0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-10 22:22:45 -05:00

refactor(console): fixing code review to-dos

This commit is contained in:
Gao Sun 2023-10-25 12:40:14 +08:00
parent 63d6a3b7db
commit 52d8e3ad37
No known key found for this signature in database
GPG key ID: 13EBE123E4773688
47 changed files with 263 additions and 176 deletions

View file

@ -11,13 +11,17 @@ type Props = {
};
function OrganizationRolesSelect({ value, onChange, keyword, setKeyword }: Props) {
const { data: scopes } = useSearchValues<OrganizationScope>('api/organization-roles', keyword);
const { data: scopes, isLoading } = useSearchValues<OrganizationScope>(
'api/organization-roles',
keyword
);
return (
<MultiSelect
value={value}
options={scopes.map(({ id, name }) => ({ value: id, title: name }))}
placeholder="organizations.search_permission_placeholder"
isOptionsLoading={isLoading}
onChange={onChange}
onSearch={setKeyword}
/>

View file

@ -11,13 +11,17 @@ type Props = {
};
function OrganizationScopesSelect({ value, onChange, keyword, setKeyword }: Props) {
const { data: scopes } = useSearchValues<OrganizationScope>('api/organization-scopes', keyword);
const { data: scopes, isLoading } = useSearchValues<OrganizationScope>(
'api/organization-scopes',
keyword
);
return (
<MultiSelect
value={value}
options={scopes.map(({ id, name }) => ({ value: id, title: name }))}
placeholder="organizations.search_permission_placeholder"
isOptionsLoading={isLoading}
onChange={onChange}
onSearch={setKeyword}
/>

View file

@ -4,6 +4,7 @@ import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import Close from '@/assets/icons/close.svg';
import { Ring as Spinner } from '@/ds-components/Spinner';
import { onKeyDownHandler } from '@/utils/a11y';
import Dropdown, { DropdownItem } from '../Dropdown';
@ -26,6 +27,7 @@ type Props<T> = {
isReadOnly?: boolean;
error?: string | boolean;
placeholder?: AdminConsoleKey;
isOptionsLoading?: boolean;
};
function MultiSelect<T extends string>({
@ -37,6 +39,7 @@ function MultiSelect<T extends string>({
isReadOnly,
error,
placeholder,
isOptionsLoading,
}: Props<T>) {
const inputRef = useRef<HTMLInputElement>(null);
const selectRef = useRef<HTMLDivElement>(null);
@ -145,18 +148,25 @@ function MultiSelect<T extends string>({
className={styles.dropdown}
anchorRef={selectRef}
>
{filteredOptions.length === 0 && <div className={styles.noResult}>{t('errors.empty')}</div>}
{filteredOptions.map(({ value, title }) => (
<DropdownItem
key={value}
onClick={(event) => {
event.preventDefault();
handleSelect({ value, title });
}}
>
{title ?? value}
</DropdownItem>
))}
{isOptionsLoading && <Spinner className={styles.spinner} />}
{!isOptionsLoading && (
<>
{filteredOptions.length === 0 && (
<div className={styles.noResult}>{t('errors.empty')}</div>
)}
{filteredOptions.map(({ value, title }) => (
<DropdownItem
key={value}
onClick={(event) => {
event.preventDefault();
handleSelect({ value, title });
}}
>
{title ?? value}
</DropdownItem>
))}
</>
)}
</Dropdown>
</div>
);

View file

@ -119,6 +119,11 @@
max-height: 288px;
}
.spinner {
color: var(--color-placeholder);
margin: _.unit(2);
}
.noResult {
color: var(--color-placeholder);
font: var(--font-body-2);

View file

@ -9,7 +9,7 @@ import { type RequestError } from './use-api';
const useSearchValues = <T>(pathname: string, keyword: string) => {
const {
data: response,
error, // TODO: handle error
error,
mutate,
} = useSWR<[T[], number], RequestError>(
buildUrl(pathname, {
@ -23,11 +23,12 @@ const useSearchValues = <T>(pathname: string, keyword: string) => {
return useMemo(
() => ({
isLoading: !response && !error,
data,
mutate,
error,
}),
[data, error, mutate]
[data, error, mutate, response]
);
};

View file

@ -27,37 +27,36 @@ function AddMembersToOrganization({ organization, isOpen, onClose }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const tAction = useActionTranslation();
const api = useApi();
const { reset, control, handleSubmit } = useForm<{
const {
reset,
control,
handleSubmit,
formState: { isSubmitting },
} = useForm<{
users: User[];
scopes: Array<Option<string>>;
}>({
defaultValues: { users: [], scopes: [] },
});
const [isLoading, setIsLoading] = useState(false);
const [keyword, setKeyword] = useState('');
const onSubmit = handleSubmit(
trySubmitSafe(async (data) => {
setIsLoading(true);
try {
await api.post(`api/organizations/${organization.id}/users`, {
await api.post(`api/organizations/${organization.id}/users`, {
json: {
userIds: data.users.map(({ id }) => id),
},
});
if (data.scopes.length > 0) {
await api.post(`api/organizations/${organization.id}/users/roles`, {
json: {
userIds: data.users.map(({ id }) => id),
organizationRoleIds: data.scopes.map(({ value }) => value),
},
});
if (data.scopes.length > 0) {
await api.post(`api/organizations/${organization.id}/users/roles`, {
json: {
userIds: data.users.map(({ id }) => id),
organizationRoleIds: data.scopes.map(({ value }) => value),
},
});
}
onClose();
} finally {
setIsLoading(false);
}
onClose();
})
);
@ -86,7 +85,7 @@ function AddMembersToOrganization({ organization, isOpen, onClose }: Props) {
subtitle="organization_details.add_members_to_organization_description"
footer={
<Button
isLoading={isLoading}
isLoading={isSubmitting}
size="large"
type="primary"
title={<>{tAction('add', 'organization_details.member_other')}</>}

View file

@ -1,4 +1,4 @@
import { type OrganizationRole, type UserWithOrganizationRoles } from '@logto/schemas';
import { type UserWithOrganizationRoles } from '@logto/schemas';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import ReactModal from 'react-modal';
@ -9,7 +9,6 @@ import FormField from '@/ds-components/FormField';
import ModalLayout from '@/ds-components/ModalLayout';
import { type Option } from '@/ds-components/Select/MultiSelect';
import useApi from '@/hooks/use-api';
import useSearchValues from '@/hooks/use-search-values';
import * as modalStyles from '@/scss/modal.module.scss';
import { decapitalize } from '@/utils/string';
@ -26,7 +25,6 @@ function EditOrganizationRolesModal({ organizationId, user, isOpen, onClose }: P
const [roles, setRoles] = useState<Array<Option<string>>>(
user.organizationRoles.map(({ id, name }) => ({ value: id, title: name }))
);
const { data } = useSearchValues<OrganizationRole>('api/organization-roles', keyword);
const name = user.name ?? decapitalize(t('organization_details.user'));
const [isLoading, setIsLoading] = useState(false);
const api = useApi();
@ -60,11 +58,7 @@ function EditOrganizationRolesModal({ organizationId, user, isOpen, onClose }: P
})}
</>
}
subtitle={
<>
Authorize <b>{name}</b> to access the following roles
</>
}
subtitle={<>{t('organization_details.authorize_to_roles', { name })}</>}
footer={
<Button
size="large"

View file

@ -1,13 +1,14 @@
import { type Organization } from '@logto/schemas';
import { useCallback, useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import { Navigate, Route, Routes, useParams } from 'react-router-dom';
import useSWR from 'swr';
import OrganizationIcon from '@/assets/icons/organization-preview.svg';
import ActionsButton from '@/components/ActionsButton';
import AppError from '@/components/AppError';
import DetailsPage from '@/components/DetailsPage';
import Skeleton from '@/components/DetailsPage/Skeleton';
import PageMeta from '@/components/PageMeta';
import ThemedIcon from '@/components/ThemedIcon';
import Card from '@/ds-components/Card';
@ -45,63 +46,65 @@ function OrganizationDetails() {
try {
await api.delete(`api/organizations/${id}`);
navigate(pathname);
} catch (error) {
toast.error(String(error));
} finally {
setIsDeleting(false);
}
}, [api, id, isDeleting, navigate]);
if (!id || error) {
return null;
}
if (!data) {
return <div>Loading...</div>;
}
const isLoading = !data && !error;
return (
<DetailsPage backLink={pathname} backLinkTitle="organizations.title" className={styles.page}>
<PageMeta titleKey="organization_details.page_title" />
<Card className={styles.header}>
<div className={styles.metadata}>
<ThemedIcon for={OrganizationIcon} size={60} />
<div>
<div className={styles.name}>{data.name}</div>
<div className={styles.row}>
<span className={styles.label}>{t('organization_details.organization_id')} </span>
<CopyToClipboard size="default" value={data.id} />
{isLoading && <Skeleton />}
{error && <AppError errorCode={error.body?.code} errorMessage={error.body?.message} />}
{data && (
<>
<Card className={styles.header}>
<div className={styles.metadata}>
<ThemedIcon for={OrganizationIcon} size={60} />
<div>
<div className={styles.name}>{data.name}</div>
<div className={styles.row}>
<span className={styles.label}>{t('organization_details.organization_id')} </span>
<CopyToClipboard size="default" value={data.id} />
</div>
</div>
</div>
</div>
</div>
<ActionsButton
buttonProps={{
type: 'default',
size: 'large',
}}
deleteConfirmation="organization_details.delete_confirmation"
fieldName="organizations.title"
onDelete={deleteOrganization}
/>
</Card>
<TabNav>
<TabNavItem href={`${pathname}/${id}/${tabs.settings}`}>Settings</TabNavItem>
<TabNavItem href={`${pathname}/${id}/${tabs.members}`}>Members</TabNavItem>
</TabNav>
<Routes>
<Route index element={<Navigate replace to={tabs.settings} />} />
<Route
path={tabs.settings}
element={
<Settings
isDeleting={isDeleting}
data={data}
onUpdated={async (data) => mutate(data)}
<ActionsButton
buttonProps={{
type: 'default',
size: 'large',
}}
deleteConfirmation="organization_details.delete_confirmation"
fieldName="organizations.title"
onDelete={deleteOrganization}
/>
}
/>
<Route path={tabs.members} element={<Members organization={data} />} />
</Routes>
</Card>
<TabNav>
<TabNavItem href={`${pathname}/${data.id}/${tabs.settings}`}>
{t('general.settings_nav')}
</TabNavItem>
<TabNavItem href={`${pathname}/${data.id}/${tabs.members}`}>
{t('organizations.members')}
</TabNavItem>
</TabNav>
<Routes>
<Route index element={<Navigate replace to={tabs.settings} />} />
<Route
path={tabs.settings}
element={
<Settings
isDeleting={isDeleting}
data={data}
onUpdated={async (data) => mutate(data)}
/>
}
/>
<Route path={tabs.members} element={<Members organization={data} />} />
</Routes>
</>
)}
</DetailsPage>
);
}

View file

@ -1,5 +1,5 @@
import { type Organization, type CreateOrganization } from '@logto/schemas';
import { useEffect, useState } from 'react';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import ReactModal from 'react-modal';
@ -19,27 +19,21 @@ type Props = {
function CreateOrganizationModal({ isOpen, onClose }: Props) {
const api = useApi();
const [isLoading, setIsLoading] = useState(false);
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const {
reset,
register,
handleSubmit,
formState: { errors },
formState: { errors, isSubmitting },
} = useForm<Partial<CreateOrganization>>();
const submit = handleSubmit(
trySubmitSafe(async (json) => {
setIsLoading(true);
try {
const { id } = await api
.post('api/organizations', {
json,
})
.json<Organization>();
onClose(id);
} finally {
setIsLoading(false);
}
const { id } = await api
.post('api/organizations', {
json,
})
.json<Organization>();
onClose(id);
})
);
@ -62,7 +56,7 @@ function CreateOrganizationModal({ isOpen, onClose }: Props) {
<ModalLayout
title="organizations.create_organization"
footer={
<Button type="primary" title="general.create" isLoading={isLoading} onClick={submit} />
<Button type="primary" title="general.create" isLoading={isSubmitting} onClick={submit} />
}
onClose={onClose}
>

View file

@ -1,6 +1,7 @@
import { type Organization } from '@logto/schemas';
import { joinPath } from '@silverhand/essentials';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import useSWR from 'swr';
import OrganizationIcon from '@/assets/icons/organization-preview.svg';
@ -24,19 +25,17 @@ function OrganizationsTable() {
page_size: String(pageSize),
})
);
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const isLoading = !response && !error;
const [data, totalCount] = response ?? [[], 0];
if (isLoading) {
return <div>Loading...</div>; // TODO: Add loading skeleton
}
return (
<Table
isLoading={isLoading}
rowGroups={[{ key: 'data', data }]}
columns={[
{
title: 'Name',
title: t('general.name'),
dataIndex: 'name',
render: ({ name, id }) => (
<ItemPreview
@ -47,14 +46,14 @@ function OrganizationsTable() {
),
},
{
title: 'Organization ID',
title: t('organizations.organization_id'),
dataIndex: 'id',
render: ({ id }) => <CopyToClipboard value={id} variant="text" />,
},
{
title: 'Members',
title: t('organizations.members'),
dataIndex: 'members',
render: () => 'members',
render: () => 'members', // TODO: render members
},
]}
rowIndexKey="id"

View file

@ -1,6 +1,6 @@
import { type OrganizationScope } from '@logto/schemas';
import { type Nullable } from '@silverhand/essentials';
import { useEffect, useState } from 'react';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import ReactModal from 'react-modal';
@ -13,6 +13,7 @@ import TextInput from '@/ds-components/TextInput';
import useActionTranslation from '@/hooks/use-action-translation';
import useApi from '@/hooks/use-api';
import * as modalStyles from '@/scss/modal.module.scss';
import { trySubmitSafe } from '@/utils/form';
const organizationScopesPath = 'api/organization-scopes';
@ -25,12 +26,11 @@ type Props = {
/** A modal that allows users to create or edit an organization scope. */
function PermissionModal({ isOpen, editData, onClose }: Props) {
const api = useApi();
const [isLoading, setIsLoading] = useState(false);
const {
reset,
register,
handleSubmit,
formState: { errors },
formState: { errors, isSubmitting },
} = useForm<Partial<OrganizationScope>>();
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const tAction = useActionTranslation();
@ -39,9 +39,8 @@ function PermissionModal({ isOpen, editData, onClose }: Props) {
: tAction('create', 'organizations.organization_permission');
const action = editData ? t('general.save') : tAction('create', 'organizations.permission');
const submit = handleSubmit(async (json) => {
setIsLoading(true);
try {
const submit = handleSubmit(
trySubmitSafe(async (json) => {
await (editData
? api.patch(`${organizationScopesPath}/${editData.id}`, {
json,
@ -50,10 +49,8 @@ function PermissionModal({ isOpen, editData, onClose }: Props) {
json,
}));
onClose();
} finally {
setIsLoading(false);
}
});
})
);
// Reset form on open
useEffect(() => {
@ -75,7 +72,7 @@ function PermissionModal({ isOpen, editData, onClose }: Props) {
<Button
type="primary"
title={<DangerousRaw>{action}</DangerousRaw>}
isLoading={isLoading}
isLoading={isSubmitting}
onClick={submit}
/>
}

View file

@ -37,10 +37,6 @@ function PermissionsField() {
const isLoading = !response && !error;
if (isLoading) {
return <>loading</>; // TODO: loading state
}
return (
<FormField title="organizations.organization_permission_other">
<PermissionModal
@ -53,6 +49,7 @@ function PermissionsField() {
/>
<TemplateTable
rowIndexKey="id"
isLoading={isLoading}
page={page}
totalCount={totalCount}
data={data}

View file

@ -15,6 +15,7 @@ import TextInput from '@/ds-components/TextInput';
import useActionTranslation from '@/hooks/use-action-translation';
import useApi from '@/hooks/use-api';
import * as modalStyles from '@/scss/modal.module.scss';
import { trySubmitSafe } from '@/utils/form';
const organizationRolePath = 'api/organization-roles';
@ -27,13 +28,12 @@ type Props = {
/** A modal that allows users to create or edit an organization role. */
function RoleModal({ isOpen, editData, onClose }: Props) {
const api = useApi();
const [isLoading, setIsLoading] = useState(false);
const {
reset,
register,
control,
handleSubmit,
formState: { errors },
formState: { errors, isSubmitting },
} = useForm<Partial<OrganizationRole> & { scopes: Array<Option<string>> }>({
defaultValues: { scopes: [] },
});
@ -45,10 +45,9 @@ function RoleModal({ isOpen, editData, onClose }: Props) {
const action = editData ? t('general.save') : tAction('create', 'organizations.role');
const [keyword, setKeyword] = useState('');
const submit = handleSubmit(async ({ scopes, ...json }) => {
setIsLoading(true);
try {
// Create or update role
const submit = handleSubmit(
trySubmitSafe(async ({ scopes, ...json }) => {
// Create or update rol e
const { id } = await (editData
? api.patch(`${organizationRolePath}/${editData.id}`, {
json,
@ -63,10 +62,8 @@ function RoleModal({ isOpen, editData, onClose }: Props) {
json: { organizationScopeIds: scopes.map(({ value }) => value) },
});
onClose();
} finally {
setIsLoading(false);
}
});
})
);
// Reset form on open
useEffect(() => {
@ -96,7 +93,7 @@ function RoleModal({ isOpen, editData, onClose }: Props) {
<Button
type="primary"
title={<DangerousRaw>{action}</DangerousRaw>}
isLoading={isLoading}
isLoading={isSubmitting}
onClick={submit}
/>
}

View file

@ -40,10 +40,6 @@ function RolesField() {
const isLoading = !response && !error;
if (isLoading) {
return <>loading</>; // TODO: loading state
}
return (
<FormField title="organizations.organization_role_other">
<RoleModal
@ -56,6 +52,7 @@ function RolesField() {
/>
<TemplateTable
rowIndexKey="id"
isLoading={isLoading}
page={page}
totalCount={totalCount}
data={data}

View file

@ -14,6 +14,7 @@ type Props<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValue
columns: Array<Column<TFieldValues>>;
totalCount: number;
page: number;
isLoading?: boolean;
onPageChange: (page: number) => void;
onAdd?: () => void;
};
@ -34,42 +35,42 @@ function TemplateTable<
onAdd,
totalCount,
page,
isLoading,
onPageChange,
}: Props<TFieldValues, TName>) {
const hasData = data.length > 0;
return (
<>
{hasData && (
<Table
hasBorder
className={styles.table}
rowGroups={[
{
key: 'data',
data,
},
]}
columns={columns}
rowIndexKey={rowIndexKey}
pagination={{
page,
totalCount,
pageSize,
onChange: onPageChange,
}}
footer={
<Button
size="small"
type="text"
className={styles.addButton}
icon={<CirclePlus />}
title="general.create_another"
onClick={onAdd}
/>
}
/>
)}
<Table
hasBorder
isLoading={isLoading}
className={styles.table}
rowGroups={[
{
key: 'data',
data,
},
]}
columns={columns}
rowIndexKey={rowIndexKey}
pagination={{
page,
totalCount,
pageSize,
onChange: onPageChange,
}}
footer={
<Button
size="small"
type="text"
className={styles.addButton}
icon={<CirclePlus />}
title="general.create_another"
onClick={onAdd}
/>
}
/>
{onAdd && !hasData && (
<Button
className={styles.addButton}

View file

@ -12,7 +12,7 @@ export type SearchOptions<Keys extends string> = {
/**
* Build the SQL for searching for a string within a set of fields (case-insensitive) of a
* schema.
* schema. Fields are joined by `or`.
*
* - If `search` is `undefined`, it will return an empty SQL.
* - If `search` is defined, it will return a SQL that is wrapped in a pair of parentheses.

View file

@ -21,9 +21,7 @@ export class OrganizationRoleApi extends ApiFactory<
super('organization-roles');
}
override async getList(
params?: URLSearchParams | undefined
): Promise<OrganizationRoleWithScopes[]> {
override async getList(params?: URLSearchParams): Promise<OrganizationRoleWithScopes[]> {
// eslint-disable-next-line no-restricted-syntax
return super.getList(params) as Promise<OrganizationRoleWithScopes[]>;
}

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',

View file

@ -14,6 +14,7 @@ const organization_details = {
'Find appropriate users by searching name, email, phone, or user ID. Existing members are not shown in the search results.',
add_with_organization_role: 'Add with organization role(s)',
user: 'User',
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
edit_organization_roles: 'Edit organization roles',
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',
remove_user_from_organization: 'Remove user from organization',

View file

@ -3,6 +3,8 @@ const organization = {
title: 'Organizations',
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
organization_id: 'Organization ID',
members: 'Members',
create_organization: 'Create organization',
organization_name_placeholder: 'My organization',
organization_description_placeholder: 'A brief description of the organization.',

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',

View file

@ -27,6 +27,8 @@ const organization_details = {
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
authorize_to_roles: 'Authorize {{name}} to access the following roles:',
/** UNTRANSLATED */
edit_organization_roles: 'Edit organization roles',
/** UNTRANSLATED */
edit_organization_roles_of_user: 'Edit organization roles of {{name}}',

View file

@ -7,6 +7,10 @@ const organizations = {
subtitle:
'Represent the teams, business customers, and partner companies that access your applications as organizations.',
/** UNTRANSLATED */
organization_id: 'Organization ID',
/** UNTRANSLATED */
members: 'Members',
/** UNTRANSLATED */
create_organization: 'Create organization',
/** UNTRANSLATED */
organization_name_placeholder: 'My organization',