0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-04-14 23:11:31 -05:00

feat(console): add permissions page for org role details page (#5631)

This commit is contained in:
Xiao Yijun 2024-04-07 10:25:44 +08:00 committed by GitHub
parent 6ed7ab5ef9
commit 11c974cfdd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 219 additions and 5 deletions

View file

@ -26,7 +26,7 @@ type FormData = Pick<OrganizationScope, 'name' | 'description'>;
const organizationScopesPath = 'api/organization-scopes';
/** A modal that allows users to create or edit an organization permission. */
function OrganizationPermissionModal({ data, onClose }: Props) {
function ManageOrganizationPermissionModal({ data, onClose }: Props) {
const isCreateMode = data === null;
const { t } = useTranslation(undefined, {
@ -111,4 +111,4 @@ function OrganizationPermissionModal({ data, onClose }: Props) {
);
}
export default OrganizationPermissionModal;
export default ManageOrganizationPermissionModal;

View file

@ -0,0 +1,11 @@
@use '@/scss/underscore' as _;
.filter {
display: flex;
justify-content: space-between;
align-items: center;
.assignButton {
margin-left: _.unit(2);
}
}

View file

@ -1,9 +1,155 @@
import { type OrganizationScope } from '@logto/schemas';
import { useCallback, useMemo, useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import useSWR from 'swr';
import Plus from '@/assets/icons/plus.svg';
import ActionsButton from '@/components/ActionsButton';
import Breakable from '@/components/Breakable';
import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder';
import ManageOrganizationPermissionModal from '@/components/ManageOrganizationPermissionModal';
import Button from '@/ds-components/Button';
import DynamicT from '@/ds-components/DynamicT';
import Search from '@/ds-components/Search';
import Table from '@/ds-components/Table';
import Tag from '@/ds-components/Tag';
import useApi, { type RequestError } from '@/hooks/use-api';
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
import * as styles from './index.module.scss';
const organizationRolesPath = 'api/organization-roles';
type Props = {
organizationRoleId: string;
};
function Permissions({ organizationRoleId }: Props) {
return <div>TBD</div>;
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const api = useApi();
const { data, error, isLoading, mutate } = useSWR<OrganizationScope[], RequestError>(
`${organizationRolesPath}/${organizationRoleId}/scopes`
);
const [{ keyword }, updateSearchParameters] = useSearchParametersWatcher({
keyword: '',
});
const filteredData = useMemo(() => {
if (keyword) {
return data?.filter((roleScope) => roleScope.name.includes(keyword));
}
return data;
}, [data, keyword]);
const [editPermission, setEditPermission] = useState<OrganizationScope>();
const scopeRemoveHandler = useCallback(
(scopeToRemove: OrganizationScope) => async () => {
await api.put(`${organizationRolesPath}/${organizationRoleId}/scopes`, {
json: {
organizationScopeIds:
data?.filter((scope) => scope.id !== scopeToRemove.id).map(({ id }) => id) ?? [],
},
});
toast.success(
t('organization_role_details.permissions.removed', { name: scopeToRemove.name })
);
void mutate();
},
[api, data, mutate, organizationRoleId, t]
);
return (
<>
<Table
rowGroups={[{ key: 'organizationRolePermissions', data: filteredData }]}
rowIndexKey="id"
columns={[
{
title: <DynamicT forKey="organization_role_details.permissions.name_column" />,
dataIndex: 'name',
colSpan: 7,
render: ({ name }) => {
return (
<Tag variant="cell">
<Breakable>{name}</Breakable>
</Tag>
);
},
},
{
title: <DynamicT forKey="organization_role_details.permissions.description_column" />,
dataIndex: 'description',
colSpan: 8,
render: ({ description }) => <Breakable>{description ?? '-'}</Breakable>,
},
{
title: null,
dataIndex: 'action',
colSpan: 1,
render: (scope) => (
<ActionsButton
fieldName="organization_role_details.permissions.name_column"
deleteConfirmation="organization_role_details.permissions.remove_confirmation"
textOverrides={{
delete: 'organization_role_details.permissions.remove_permission',
deleteConfirmation: 'general.remove',
}}
onEdit={() => {
setEditPermission(scope);
}}
onDelete={async () => {
await scopeRemoveHandler(scope)();
}}
/>
),
},
]}
filter={
<div className={styles.filter}>
<Search
isClearable={Boolean(keyword)}
placeholder={t('organization_template.permissions.search_placeholder')}
defaultValue={keyword}
onSearch={(keyword) => {
if (keyword) {
updateSearchParameters({ keyword });
}
}}
onClearSearch={() => {
updateSearchParameters({ keyword: '' });
}}
/>
<Button
title="organization_role_details.permissions.assign_permissions"
className={styles.assignButton}
type="primary"
icon={<Plus />}
onClick={() => {
// Todo @xiaoyijun Assign permissions to org role
}}
/>
</div>
}
placeholder={<EmptyDataPlaceholder />}
isLoading={isLoading}
errorMessage={error?.body?.message ?? error?.message}
onRetry={async () => mutate(undefined, true)}
/>
{editPermission && (
<ManageOrganizationPermissionModal
data={editPermission}
onClose={() => {
setEditPermission(undefined);
void mutate();
}}
/>
)}
</>
);
}
export default Permissions;

View file

@ -10,6 +10,7 @@ import PermissionsEmpty from '@/assets/images/permissions-empty.svg';
import ActionsButton from '@/components/ActionsButton';
import Breakable from '@/components/Breakable';
import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder';
import ManageOrganizationPermissionModal from '@/components/ManageOrganizationPermissionModal';
import { organizationPermissionLink } from '@/consts';
import Button from '@/ds-components/Button';
import DynamicT from '@/ds-components/DynamicT';
@ -22,7 +23,6 @@ import useDocumentationUrl from '@/hooks/use-documentation-url';
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
import { buildUrl, formatSearchKeyword } from '@/utils/url';
import OrganizationPermissionModal from './OrganizationPermissionModal';
import * as styles from './index.module.scss';
function OrganizationPermissions() {
@ -141,7 +141,7 @@ function OrganizationPermissions() {
onRetry={async () => mutate(undefined, true)}
/>
{permissionModalData !== undefined && (
<OrganizationPermissionModal
<ManageOrganizationPermissionModal
data={permissionModalData}
onClose={() => {
setPermissionModalData(undefined);

View file

@ -15,6 +15,10 @@ const organization_role_details = {
org: 'Org-Berechtigung',
},
assign_permissions: 'Berechtigungen zuweisen',
remove_permission: 'Berechtigung entfernen',
remove_confirmation:
'Wenn diese Berechtigung entfernt wird, verliert der Benutzer mit dieser Organisationsrolle den Zugriff, der durch diese Berechtigung gewährt wurde.',
removed: 'Die Berechtigung {{name}} wurde erfolgreich aus dieser Organisationsrolle entfernt',
},
general: {
tab: 'Allgemein',

View file

@ -15,6 +15,10 @@ const organization_role_details = {
org: 'Org permission',
},
assign_permissions: 'Assign permissions',
remove_permission: 'Remove permission',
remove_confirmation:
'If this permission is removed, the user with this organization role will lose the access granted by this permission.',
removed: 'The permission {{name}} was successfully removed from this organization role',
},
general: {
tab: 'General',

View file

@ -15,6 +15,10 @@ const organization_role_details = {
org: 'Permiso de organización',
},
assign_permissions: 'Asignar permisos',
remove_permission: 'Eliminar permiso',
remove_confirmation:
'Si este permiso se elimina, el usuario con este rol de organización perderá el acceso otorgado por este permiso.',
removed: 'El permiso {{name}} se eliminó correctamente de este rol de organización',
},
general: {
tab: 'General',

View file

@ -15,6 +15,10 @@ const organization_role_details = {
org: "Autorisation d'organisation",
},
assign_permissions: 'Attribuer des autorisations',
remove_permission: 'Supprimer la permission',
remove_confirmation:
"Si cette permission est supprimée, l'utilisateur avec ce rôle d'organisation perdra l'accès accordé par cette permission.",
removed: "La permission {{name}} a été supprimée avec succès de ce rôle d'organisation",
},
general: {
tab: 'Général',

View file

@ -15,6 +15,10 @@ const organization_role_details = {
org: 'Autorizzazione organizzazione',
},
assign_permissions: 'Assegna autorizzazioni',
remove_permission: 'Rimuovi permesso',
remove_confirmation:
"Se questo permesso viene rimosso, l'utente con questo ruolo organizzativo perderà l'accesso concessogli da questo permesso.",
removed: 'Il permesso {{name}} è stato rimosso con successo da questo ruolo organizzativo',
},
general: {
tab: 'Generale',

View file

@ -15,6 +15,10 @@ const organization_role_details = {
org: '組織許可',
},
assign_permissions: '許可を割り当てる',
remove_permission: '権限を削除',
remove_confirmation:
'この権限を削除すると、この組織の役割を持つユーザーはこの権限によって付与されたアクセスを失います。',
removed: 'この組織の役割から権限 {{name}} が正常に削除されました',
},
general: {
tab: '一般',

View file

@ -15,6 +15,10 @@ const organization_role_details = {
org: '조직 권한',
},
assign_permissions: '권한 할당',
remove_permission: '권한 삭제',
remove_confirmation:
'이 권한을 제거하면이 조직 역할을하는 사용자는이 권한으로 부여된 액세스를 잃게됩니다.',
removed: '권한 {{name}}이(가)이 조직 역할에서 성공적으로 제거되었습니다',
},
general: {
tab: '일반',

View file

@ -15,6 +15,10 @@ const organization_role_details = {
org: 'Uprawnienie organizacji',
},
assign_permissions: 'Przypisz uprawnienia',
remove_permission: 'Usuń uprawnienie',
remove_confirmation:
'Jeśli to uprawnienie zostanie usunięte, użytkownik z tą rolą organizacyjną utraci dostęp udzielony przez to uprawnienie.',
removed: 'Uprawnienie {{name}} zostało pomyślnie usunięte z tej roli organizacyjnej',
},
general: {
tab: 'Ogólne',

View file

@ -15,6 +15,10 @@ const organization_role_details = {
org: 'Permissão da org',
},
assign_permissions: 'Atribuir permissões',
remove_permission: 'Remover permissão',
remove_confirmation:
'Se esta permissão for removida, o usuário com essa função organizacional perderá o acesso concedido por esta permissão.',
removed: 'A permissão {{name}} foi removida com sucesso desta função organizacional',
},
general: {
tab: 'Geral',

View file

@ -15,6 +15,10 @@ const organization_role_details = {
org: 'Permissão da org',
},
assign_permissions: 'Atribuir permissões',
remove_permission: 'Remover permissão',
remove_confirmation:
'Se esta permissão for removida, o utilizador com esta função organizacional perderá o acesso concedido por esta permissão.',
removed: 'A permissão {{name}} foi removida com sucesso desta função organizacional',
},
general: {
tab: 'Geral',

View file

@ -15,6 +15,10 @@ const organization_role_details = {
org: 'Разрешение организации',
},
assign_permissions: 'Назначить разрешения',
remove_permission: 'Удалить разрешение',
remove_confirmation:
'Если это разрешение будет удалено, пользователь с этой организационной ролью потеряет доступ, предоставленный этим разрешением.',
removed: 'Разрешение {{name}} успешно удалено из этой организационной роли',
},
general: {
tab: 'Общее',

View file

@ -15,6 +15,10 @@ const organization_role_details = {
org: 'Kuruluş izni',
},
assign_permissions: 'İzinleri atama',
remove_permission: 'İzni kaldır',
remove_confirmation:
'Bu izin kaldırılırsa, bu organizasyon rolüne sahip kullanıcı bu izin tarafından verilen erişimi kaybeder.',
removed: '{{name}} izni bu organizasyon rolünden başarıyla kaldırıldı',
},
general: {
tab: 'Genel',

View file

@ -15,6 +15,9 @@ const organization_role_details = {
org: '组织权限',
},
assign_permissions: '分配权限',
remove_permission: '移除权限',
remove_confirmation: '如果移除此权限,拥有此组织角色的用户将失去此权限授予的访问权限。',
removed: '权限 {{name}} 已成功从此组织角色中移除',
},
general: {
tab: '常规',

View file

@ -15,6 +15,9 @@ const organization_role_details = {
org: '組織權限',
},
assign_permissions: '分配權限',
remove_permission: '移除權限',
remove_confirmation: '如果移除此權限,擁有此組織角色的使用者將失去此權限所授予的存取權。',
removed: '權限 {{name}} 已成功從此組織角色中移除',
},
general: {
tab: '一般',

View file

@ -15,6 +15,9 @@ const organization_role_details = {
org: '組織權限',
},
assign_permissions: '分配權限',
remove_permission: '移除權限',
remove_confirmation: '如果移除此權限,擁有此組織角色的使用者將失去此權限所授予的存取權。',
removed: '權限 {{name}} 已成功從此組織角色中移除',
},
general: {
tab: '一般',