mirror of
https://github.com/logto-io/logto.git
synced 2025-01-06 20:40:08 -05:00
feat(console): create organization role (#5630)
This commit is contained in:
parent
d6cd67be76
commit
6ed7ab5ef9
17 changed files with 279 additions and 71 deletions
packages
console/src/pages/OrganizationTemplate/OrganizationRoles
phrases/src/locales
de/translation/admin-console
en/translation/admin-console
es/translation/admin-console
fr/translation/admin-console
it/translation/admin-console
ja/translation/admin-console
ko/translation/admin-console
pl-pl/translation/admin-console
pt-br/translation/admin-console
pt-pt/translation/admin-console
ru/translation/admin-console
tr-tr/translation/admin-console
zh-cn/translation/admin-console
zh-hk/translation/admin-console
zh-tw/translation/admin-console
|
@ -0,0 +1,86 @@
|
|||
import { type OrganizationRole } from '@logto/schemas';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ReactModal from 'react-modal';
|
||||
|
||||
import Button from '@/ds-components/Button';
|
||||
import FormField from '@/ds-components/FormField';
|
||||
import ModalLayout from '@/ds-components/ModalLayout';
|
||||
import TextInput from '@/ds-components/TextInput';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import * as modalStyles from '@/scss/modal.module.scss';
|
||||
import { trySubmitSafe } from '@/utils/form';
|
||||
|
||||
type FormData = Pick<OrganizationRole, 'name' | 'description'>;
|
||||
|
||||
type Props = {
|
||||
onClose: (createdOrganizationRole?: OrganizationRole) => void;
|
||||
};
|
||||
|
||||
function CreateOrganizationRoleModal({ onClose }: Props) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const {
|
||||
register,
|
||||
formState: { errors, isSubmitting },
|
||||
handleSubmit,
|
||||
} = useForm<FormData>();
|
||||
|
||||
const api = useApi();
|
||||
|
||||
const submit = handleSubmit(
|
||||
trySubmitSafe(async (formData) => {
|
||||
const createdData = await api
|
||||
.post('api/organization-roles', { json: formData })
|
||||
.json<OrganizationRole>();
|
||||
toast.success(
|
||||
t('organization_template.roles.create_modal.created', { name: createdData.name })
|
||||
);
|
||||
onClose(createdData);
|
||||
})
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactModal
|
||||
isOpen
|
||||
className={modalStyles.content}
|
||||
overlayClassName={modalStyles.overlay}
|
||||
onRequestClose={() => {
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<ModalLayout
|
||||
title="organization_template.roles.create_modal.title"
|
||||
footer={
|
||||
<Button
|
||||
type="primary"
|
||||
title="organization_template.roles.create_modal.create"
|
||||
isLoading={isSubmitting}
|
||||
onClick={submit}
|
||||
/>
|
||||
}
|
||||
onClose={onClose}
|
||||
>
|
||||
<FormField isRequired title="organization_template.roles.create_modal.name_field">
|
||||
<TextInput
|
||||
// eslint-disable-next-line jsx-a11y/no-autofocus
|
||||
autoFocus
|
||||
placeholder="viewer"
|
||||
error={Boolean(errors.name)}
|
||||
{...register('name', { required: true })}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField title="organization_template.permissions.description_field_name">
|
||||
<TextInput
|
||||
placeholder={t('organization_role_details.general.description_field_placeholder')}
|
||||
error={Boolean(errors.description)}
|
||||
{...register('description')}
|
||||
/>
|
||||
</FormField>
|
||||
</ModalLayout>
|
||||
</ReactModal>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreateOrganizationRoleModal;
|
|
@ -1,4 +1,5 @@
|
|||
import { type OrganizationRoleWithScopes } from '@logto/schemas';
|
||||
import { useState } from 'react';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import Plus from '@/assets/icons/plus.svg';
|
||||
|
@ -20,6 +21,7 @@ import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
|
|||
import useTenantPathname from '@/hooks/use-tenant-pathname';
|
||||
import { buildUrl } from '@/utils/url';
|
||||
|
||||
import CreateOrganizationRoleModal from './CreateOrganizationRoleModal';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
function OrganizationRoles() {
|
||||
|
@ -41,88 +43,103 @@ function OrganizationRoles() {
|
|||
|
||||
const [orgRoles, totalCount] = data ?? [];
|
||||
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Table
|
||||
rowGroups={[{ key: 'organizationRoles', data: orgRoles }]}
|
||||
rowIndexKey="id"
|
||||
columns={[
|
||||
{
|
||||
title: <DynamicT forKey="organization_template.roles.role_column" />,
|
||||
dataIndex: 'name',
|
||||
colSpan: 4,
|
||||
render: ({ id, name }) => {
|
||||
return <ItemPreview title={name} icon={<ThemedIcon for={OrgRoleIcon} />} to={id} />;
|
||||
<>
|
||||
<Table
|
||||
rowGroups={[{ key: 'organizationRoles', data: orgRoles }]}
|
||||
rowIndexKey="id"
|
||||
columns={[
|
||||
{
|
||||
title: <DynamicT forKey="organization_template.roles.role_column" />,
|
||||
dataIndex: 'name',
|
||||
colSpan: 4,
|
||||
render: ({ id, name }) => {
|
||||
return <ItemPreview title={name} icon={<ThemedIcon for={OrgRoleIcon} />} to={id} />;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: <DynamicT forKey="organization_template.roles.permissions_column" />,
|
||||
dataIndex: 'scopes',
|
||||
colSpan: 12,
|
||||
render: ({ scopes }) => {
|
||||
return scopes.length === 0 ? (
|
||||
'-'
|
||||
) : (
|
||||
<div className={styles.permissions}>
|
||||
{scopes.map(({ id, name }) => (
|
||||
<Tag key={id} variant="cell">
|
||||
<Breakable>{name}</Breakable>
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
{
|
||||
title: <DynamicT forKey="organization_template.roles.permissions_column" />,
|
||||
dataIndex: 'scopes',
|
||||
colSpan: 12,
|
||||
render: ({ scopes }) => {
|
||||
return scopes.length === 0 ? (
|
||||
'-'
|
||||
) : (
|
||||
<div className={styles.permissions}>
|
||||
{scopes.map(({ id, name }) => (
|
||||
<Tag key={id} variant="cell">
|
||||
<Breakable>{name}</Breakable>
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
]}
|
||||
rowClickHandler={({ id }) => {
|
||||
navigate(id);
|
||||
}}
|
||||
filter={
|
||||
<div className={styles.filter}>
|
||||
<Button
|
||||
title="organization_template.roles.create_title"
|
||||
type="primary"
|
||||
icon={<Plus />}
|
||||
onClick={() => {
|
||||
// Todo @xiaoyijun implment create org role
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
placeholder={
|
||||
<TablePlaceholder
|
||||
image={<RolesEmpty />}
|
||||
imageDark={<RolesEmptyDark />}
|
||||
title="organization_template.roles.placeholder_title"
|
||||
description="organization_template.roles.placeholder_description"
|
||||
learnMoreLink={{
|
||||
href: getDocumentationUrl(organizationRoleLink),
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
action={
|
||||
]}
|
||||
rowClickHandler={({ id }) => {
|
||||
navigate(id);
|
||||
}}
|
||||
filter={
|
||||
<div className={styles.filter}>
|
||||
<Button
|
||||
title="organization_template.roles.create_title"
|
||||
type="primary"
|
||||
size="large"
|
||||
icon={<Plus />}
|
||||
onClick={() => {
|
||||
// Todo @xiaoyijun implment create org role
|
||||
setIsCreateModalOpen(true);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
placeholder={
|
||||
<TablePlaceholder
|
||||
image={<RolesEmpty />}
|
||||
imageDark={<RolesEmptyDark />}
|
||||
title="organization_template.roles.placeholder_title"
|
||||
description="organization_template.roles.placeholder_description"
|
||||
learnMoreLink={{
|
||||
href: getDocumentationUrl(organizationRoleLink),
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
action={
|
||||
<Button
|
||||
title="organization_template.roles.create_title"
|
||||
type="primary"
|
||||
size="large"
|
||||
icon={<Plus />}
|
||||
onClick={() => {
|
||||
setIsCreateModalOpen(true);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
pagination={{
|
||||
page,
|
||||
totalCount,
|
||||
pageSize: defaultPageSize,
|
||||
onChange: (page) => {
|
||||
updateSearchParameters({ page });
|
||||
},
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
errorMessage={error?.body?.message ?? error?.message}
|
||||
onRetry={async () => mutate(undefined, true)}
|
||||
/>
|
||||
{isCreateModalOpen && (
|
||||
<CreateOrganizationRoleModal
|
||||
onClose={(createdRole) => {
|
||||
setIsCreateModalOpen(false);
|
||||
if (createdRole) {
|
||||
void mutate();
|
||||
navigate(createdRole.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
}
|
||||
pagination={{
|
||||
page,
|
||||
totalCount,
|
||||
pageSize: defaultPageSize,
|
||||
onChange: (page) => {
|
||||
updateSearchParameters({ page });
|
||||
},
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
errorMessage={error?.body?.message ?? error?.message}
|
||||
onRetry={async () => mutate(undefined, true)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,13 @@ const organization_template = {
|
|||
placeholder_title: 'Organisationsrolle',
|
||||
placeholder_description:
|
||||
'Eine Organisationsrolle ist eine Gruppierung von Berechtigungen, die Benutzern zugewiesen werden können. Die Berechtigungen müssen aus den vordefinierten Organisationsberechtigungen stammen.',
|
||||
create_modal: {
|
||||
title: 'Rolle der Organisation erstellen',
|
||||
create: 'Rolle erstellen',
|
||||
name_field: 'Rollenname',
|
||||
description_field: 'Beschreibung',
|
||||
created: 'Die Organisationsrolle {{name}} wurde erfolgreich erstellt.',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: 'Org Berechtigungen',
|
||||
|
|
|
@ -11,6 +11,13 @@ const organization_template = {
|
|||
placeholder_title: 'Organization role',
|
||||
placeholder_description:
|
||||
'Organization role is a grouping of permissions that can be assigned to users. The permissions must come from the predefined organization permissions.',
|
||||
create_modal: {
|
||||
title: 'Create organization role',
|
||||
create: 'Create role',
|
||||
name_field: 'Role name',
|
||||
description_field: 'Description',
|
||||
created: 'Organization role {{name}} has been successfully created.',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: 'Organization permissions',
|
||||
|
|
|
@ -11,6 +11,13 @@ const organization_template = {
|
|||
placeholder_title: 'Rol de organización',
|
||||
placeholder_description:
|
||||
'El rol de organización es un agrupamiento de permisos que se pueden asignar a los usuarios. Los permisos deben provenir de los permisos de organización predefinidos.',
|
||||
create_modal: {
|
||||
title: 'Crear rol de organización',
|
||||
create: 'Crear rol',
|
||||
name_field: 'Nombre del rol',
|
||||
description_field: 'Descripción',
|
||||
created: 'El rol de organización {{name}} se ha creado correctamente.',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: 'Permisos de org',
|
||||
|
|
|
@ -11,6 +11,13 @@ const organization_template = {
|
|||
placeholder_title: 'Rôle d’organisation',
|
||||
placeholder_description:
|
||||
'Un rôle d’organisation est un groupement de permissions qui peuvent être attribuées aux utilisateurs. Les permissions doivent provenir des permissions d’organisation prédéfinies.',
|
||||
create_modal: {
|
||||
title: "Créer un rôle d'organisation",
|
||||
create: 'Créer un rôle',
|
||||
name_field: 'Nom du rôle',
|
||||
description_field: 'Description',
|
||||
created: "Le rôle d'organisation {{name}} a été créé avec succès.",
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: 'Permissions org',
|
||||
|
|
|
@ -11,6 +11,13 @@ const organization_template = {
|
|||
placeholder_title: 'Ruolo di organizzazione',
|
||||
placeholder_description:
|
||||
'Il ruolo di organizzazione è un raggruppamento di permessi che possono essere assegnati agli utenti. I permessi devono provenire dai permessi di organizzazione predefiniti.',
|
||||
create_modal: {
|
||||
title: "Crea ruolo dell'organizzazione",
|
||||
create: 'Crea ruolo',
|
||||
name_field: 'Nome del ruolo',
|
||||
description_field: 'Descrizione',
|
||||
created: "Il ruolo dell'organizzazione {{name}} è stato creato con successo.",
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: 'Permessi org',
|
||||
|
|
|
@ -11,6 +11,13 @@ const organization_template = {
|
|||
placeholder_title: '組織の役割',
|
||||
placeholder_description:
|
||||
'組織の役割は、ユーザーに割り当てることができる権限のグループです。権限は、事前に定義された組織の権限から来なければなりません。',
|
||||
create_modal: {
|
||||
title: '組織の役割を作成する',
|
||||
create: '役割を作成する',
|
||||
name_field: '役割名',
|
||||
description_field: '説明',
|
||||
created: '組織の役割{{name}}が正常に作成されました。',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: '組織の権限',
|
||||
|
|
|
@ -11,6 +11,13 @@ const organization_template = {
|
|||
placeholder_title: '조직 역할',
|
||||
placeholder_description:
|
||||
'조직 역할은 사용자에게 할당될 수 있는 권한의 그룹입니다. 권한은 미리 정의된 조직 권한에서 와야 합니다.',
|
||||
create_modal: {
|
||||
title: '조직 역할 만들기',
|
||||
create: '역할 만들기',
|
||||
name_field: '역할 이름',
|
||||
description_field: '설명',
|
||||
created: '조직 역할 {{name}}이(가) 성공적으로 만들어졌습니다.',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: '조직 권한',
|
||||
|
|
|
@ -11,6 +11,13 @@ const organization_template = {
|
|||
placeholder_title: 'Rola organizacyjna',
|
||||
placeholder_description:
|
||||
'Rola organizacyjna to grupowanie uprawnień, które można przypisać użytkownikom. Uprawnienia muszą pochodzić z wcześniej zdefiniowanych uprawnień organizacyjnych.',
|
||||
create_modal: {
|
||||
title: 'Utwórz rolę organizacji',
|
||||
create: 'Utwórz rolę',
|
||||
name_field: 'Nazwa roli',
|
||||
description_field: 'Opis',
|
||||
created: 'Rola organizacji {{name}} została pomyślnie utworzona.',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: 'Uprawnienia org',
|
||||
|
|
|
@ -11,6 +11,13 @@ const organization_template = {
|
|||
placeholder_title: 'Papel da organização',
|
||||
placeholder_description:
|
||||
'Papel da organização é um agrupamento de permissões que podem ser atribuídas aos usuários. As permissões devem vir das permissões organizacionais predefinidas.',
|
||||
create_modal: {
|
||||
title: 'Criar função da organização',
|
||||
create: 'Criar função',
|
||||
name_field: 'Nome da função',
|
||||
description_field: 'Descrição',
|
||||
created: 'A função da organização {{name}} foi criada com sucesso.',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: 'Permissões da org',
|
||||
|
|
|
@ -11,6 +11,13 @@ const organization_template = {
|
|||
placeholder_title: 'Papel da organização',
|
||||
placeholder_description:
|
||||
'O papel da organização é um agrupamento de permissões que podem ser atribuídas a utilizadores. As permissões devem provir das permissões organizacionais predefinidas.',
|
||||
create_modal: {
|
||||
title: 'Criar função da organização',
|
||||
create: 'Criar função',
|
||||
name_field: 'Nome da função',
|
||||
description_field: 'Descrição',
|
||||
created: 'A função da organização {{name}} foi criada com sucesso.',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: 'Permissões da org',
|
||||
|
|
|
@ -11,6 +11,13 @@ const organization_template = {
|
|||
placeholder_title: 'Роль организации',
|
||||
placeholder_description:
|
||||
'Роль организации - это группировка разрешений, которые могут быть назначены пользователям. Разрешения должны происходить из предопределенных разрешений организации.',
|
||||
create_modal: {
|
||||
title: 'Создать роль организации',
|
||||
create: 'Создать роль',
|
||||
name_field: 'Название роли',
|
||||
description_field: 'Описание',
|
||||
created: 'Роль организации {{name}} успешно создана.',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: 'Разрешения орг',
|
||||
|
|
|
@ -11,6 +11,13 @@ const organization_template = {
|
|||
placeholder_title: 'Organizasyon rolü',
|
||||
placeholder_description:
|
||||
'Organizasyon rolü, kullanıcılara atanabilecek izinlerin bir gruplamasıdır. İzinler, önceden belirlenmiş organizasyon izinlerinden gelmelidir.',
|
||||
create_modal: {
|
||||
title: 'Kuruluş rolü oluştur',
|
||||
create: 'Rol oluştur',
|
||||
name_field: 'Rol adı',
|
||||
description_field: 'Açıklama',
|
||||
created: 'Kuruluş rolü {{name}} başarıyla oluşturuldu.',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: 'Org izinleri',
|
||||
|
|
|
@ -10,6 +10,13 @@ const organization_template = {
|
|||
permissions_column: '权限',
|
||||
placeholder_title: '组织角色',
|
||||
placeholder_description: '组织角色是一组可以分配给用户的权限。权限必须来自预定义的组织权限。',
|
||||
create_modal: {
|
||||
title: '创建组织角色',
|
||||
create: '创建角色',
|
||||
name_field: '角色名称',
|
||||
description_field: '描述',
|
||||
created: '成功创建组织角色 {{name}}。',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: '组织权限',
|
||||
|
|
|
@ -10,6 +10,13 @@ const organization_template = {
|
|||
permissions_column: '權限',
|
||||
placeholder_title: '組織角色',
|
||||
placeholder_description: '組織角色是一組可以分配給用戶的權限。權限必須來自預定義的組織權限。',
|
||||
create_modal: {
|
||||
title: '建立組織角色',
|
||||
create: '建立角色',
|
||||
name_field: '角色名稱',
|
||||
description_field: '描述',
|
||||
created: '成功建立組織角色 {{name}}。',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: '組織權限',
|
||||
|
|
|
@ -10,6 +10,13 @@ const organization_template = {
|
|||
permissions_column: '權限',
|
||||
placeholder_title: '組織角色',
|
||||
placeholder_description: '組織角色是一組可以分配給使用者的權限。權限必須來自預定義的組織權限。',
|
||||
create_modal: {
|
||||
title: '建立組織角色',
|
||||
create: '建立角色',
|
||||
name_field: '角色名稱',
|
||||
description_field: '描述',
|
||||
created: '成功建立組織角色 {{name}}。',
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
tab_name: '組織權限',
|
||||
|
|
Loading…
Reference in a new issue