From 13557dab9cb9a848e236ebd48875c7ed04e1929e Mon Sep 17 00:00:00 2001 From: Daniel Koch Date: Thu, 2 Mar 2023 17:14:17 +0100 Subject: [PATCH 1/5] feat: init the GroupPermissionGrid --- .../GroupPermissionGrid.spec.tsx | 9 + .../GroupPermissionGrid.tsx | 114 +++++++++++ .../InstancePermissionGrid.less} | 2 +- .../InstancePermissionGrid.spec.tsx | 9 + .../InstancePermissionGrid.tsx} | 182 +++++++++--------- .../PermissionModal/PermissionModal.less | 8 + .../PermissionModal/PermissionModal.spec.tsx | 9 + .../PermissionModal/PermissionModal.tsx} | 145 +++++++------- .../PermissionSelect.spec.tsx | 0 .../PermissionSelect/PermissionSelect.tsx | 0 .../UserPermissionGrid.less | 31 +++ .../UserPermissionGrid.spec.tsx | 0 .../UserPermissionGrid/UserPermissionGrid.tsx | 156 +++++++++++++++ .../UserPermissionModal.less | 38 ---- .../UserPermissionModal.spec.tsx | 9 - .../GeneralEntityForm/GeneralEntityForm.tsx | 18 +- .../UserAvatar/UserAvatar.less | 0 .../UserAvatar/UserAvatar.spec.tsx | 0 .../UserAvatar/UserAvatar.tsx | 2 +- src/i18n/translations.ts | 78 +++++--- 20 files changed, 564 insertions(+), 246 deletions(-) create mode 100644 src/Component/FormField/Permission/GroupPermissionGrid/GroupPermissionGrid.spec.tsx create mode 100644 src/Component/FormField/Permission/GroupPermissionGrid/GroupPermissionGrid.tsx rename src/Component/FormField/{UserPermissionGrid/UserPermissionGrid.less => Permission/InstancePermissionGrid/InstancePermissionGrid.less} (92%) create mode 100644 src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.spec.tsx rename src/Component/FormField/{UserPermissionGrid/UserPermissionGrid.tsx => Permission/InstancePermissionGrid/InstancePermissionGrid.tsx} (53%) create mode 100644 src/Component/FormField/Permission/PermissionModal/PermissionModal.less create mode 100644 src/Component/FormField/Permission/PermissionModal/PermissionModal.spec.tsx rename src/Component/FormField/{UserPermissionGrid/UserPermissionModal/UserPermissionModal.tsx => Permission/PermissionModal/PermissionModal.tsx} (53%) rename src/Component/FormField/{UserPermissionGrid => Permission}/PermissionSelect/PermissionSelect.spec.tsx (100%) rename src/Component/FormField/{UserPermissionGrid => Permission}/PermissionSelect/PermissionSelect.tsx (100%) create mode 100644 src/Component/FormField/Permission/UserPermissionGrid/UserPermissionGrid.less rename src/Component/FormField/{ => Permission}/UserPermissionGrid/UserPermissionGrid.spec.tsx (100%) create mode 100644 src/Component/FormField/Permission/UserPermissionGrid/UserPermissionGrid.tsx delete mode 100644 src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.less delete mode 100644 src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.spec.tsx rename src/Component/{FormField/UserPermissionGrid => }/UserAvatar/UserAvatar.less (100%) rename src/Component/{FormField/UserPermissionGrid => }/UserAvatar/UserAvatar.spec.tsx (100%) rename src/Component/{FormField/UserPermissionGrid => }/UserAvatar/UserAvatar.tsx (95%) diff --git a/src/Component/FormField/Permission/GroupPermissionGrid/GroupPermissionGrid.spec.tsx b/src/Component/FormField/Permission/GroupPermissionGrid/GroupPermissionGrid.spec.tsx new file mode 100644 index 00000000..b436baf4 --- /dev/null +++ b/src/Component/FormField/Permission/GroupPermissionGrid/GroupPermissionGrid.spec.tsx @@ -0,0 +1,9 @@ +import GroupPermissionGrid from './GroupPermissionGrid'; + +describe('', () => { + + it('is defined', () => { + expect(GroupPermissionGrid).not.toBeUndefined(); + }); + +}); diff --git a/src/Component/FormField/Permission/GroupPermissionGrid/GroupPermissionGrid.tsx b/src/Component/FormField/Permission/GroupPermissionGrid/GroupPermissionGrid.tsx new file mode 100644 index 00000000..5e41b45c --- /dev/null +++ b/src/Component/FormField/Permission/GroupPermissionGrid/GroupPermissionGrid.tsx @@ -0,0 +1,114 @@ +import React, { + useCallback +} from 'react'; + +import { + useTranslation +} from 'react-i18next'; + +import PermissionCollectionType from '@terrestris/shogun-util/dist/model/enum/PermissionCollectionType'; +import GroupInstancePermission from '@terrestris/shogun-util/dist/model/security/GroupInstancePermission'; +import Group from '@terrestris/shogun-util/dist/model/Group'; +import BaseEntity from '@terrestris/shogun-util/dist/model/BaseEntity'; +import GenericService from '@terrestris/shogun-util/dist/service/GenericService'; + +import InstancePermissionGrid, { + DataType, + InstancePermissionGridProps +} from '../InstancePermissionGrid/InstancePermissionGrid'; + +import useSHOGunAPIClient from '../../../../Hooks/useSHOGunAPIClient'; + +export interface GroupPermissionGridProps extends Omit { }; + +const GroupPermissionGrid: React.FC = ({ + entityType, + ...passThroughProps +}) => { + + const { t } = useTranslation(); + const client = useSHOGunAPIClient(); + + const service = useCallback(() => { + return client[entityType]() as GenericService; + }, [client, entityType]); + + const getGroupInstancePermissions = async (id: number) => { + return await service().getGroupInstancePermissions(id); + }; + + const setGroupInstancePermission = async (id: number, referenceId: number, permission: PermissionCollectionType) => { + await service().setGroupInstancePermission(id, referenceId, permission); + }; + + const deleteGroupInstancePermission = async (id: number, referenceId: number) => { + await service().deleteGroupInstancePermission(id, referenceId); + }; + + const getGroups = async () => { + return await client.group().findAll(); + }; + + const toGroupDataType = (permission: GroupInstancePermission): DataType => { + return { + key: permission.group?.id, + reference: permission.group, + name: permission.group?.providerDetails?.name, + permission: permission.permission?.name + }; + }; + + const toTag = (group: Group) => { + return { + value: group.id, + filterValues: [ + group.id, + group.authProviderId, + group.providerDetails?.name + ], + label: ( + {group.providerDetails.name} + ) + }; + }; + + const colDefinition = () => { + return { + sorter: (a: DataType, b: DataType) => { + return a.reference?.providerDetails?.name?.localeCompare(b.reference?.providerDetails?.name); + } + }; + }; + + return ( + { + return t('GroupPermissionGrid.modal.saveErrorMsg', { + referenceIds: placeholder + }); + } + }} + {...passThroughProps} + /> + ); +}; + +export default GroupPermissionGrid; diff --git a/src/Component/FormField/UserPermissionGrid/UserPermissionGrid.less b/src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.less similarity index 92% rename from src/Component/FormField/UserPermissionGrid/UserPermissionGrid.less rename to src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.less index b719fd01..89645e64 100644 --- a/src/Component/FormField/UserPermissionGrid/UserPermissionGrid.less +++ b/src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.less @@ -1,4 +1,4 @@ -.permission-grid { +.instance-permission-grid { .operation-column { font-size: 1.5em; text-align: center; diff --git a/src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.spec.tsx b/src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.spec.tsx new file mode 100644 index 00000000..ab777db6 --- /dev/null +++ b/src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.spec.tsx @@ -0,0 +1,9 @@ +import InstancePermissionGrid from './InstancePermissionGrid'; + +describe('', () => { + + it('is defined', () => { + expect(InstancePermissionGrid).not.toBeUndefined(); + }); + +}); diff --git a/src/Component/FormField/UserPermissionGrid/UserPermissionGrid.tsx b/src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.tsx similarity index 53% rename from src/Component/FormField/UserPermissionGrid/UserPermissionGrid.tsx rename to src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.tsx index cd7c2752..0ae13654 100644 --- a/src/Component/FormField/UserPermissionGrid/UserPermissionGrid.tsx +++ b/src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.tsx @@ -1,8 +1,9 @@ -import './UserPermissionGrid.less'; - -import React, {useEffect, +import React, { + useEffect, + useState, useRef, - useState} from 'react'; + useCallback +} from 'react'; import { DeleteOutlined, @@ -18,9 +19,11 @@ import { Table, Tooltip } from 'antd'; -import type {ColumnsType, +import type { + ColumnsType, ColumnType, - TableProps} from 'antd/es/table'; + TableProps +} from 'antd/es/table'; import Logger from 'js-logger'; import _cloneDeep from 'lodash/cloneDeep'; import _get from 'lodash/get'; @@ -31,34 +34,49 @@ import { } from 'react-i18next'; import PermissionCollectionType from '@terrestris/shogun-util/dist/model/enum/PermissionCollectionType'; -import UserInstancePermission from '@terrestris/shogun-util/dist/model/security/UserInstancePermission'; +import Group from '@terrestris/shogun-util/dist/model/Group'; +import InstancePermission from '@terrestris/shogun-util/dist/model/security/InstancePermission'; import User from '@terrestris/shogun-util/dist/model/User'; -import useSHOGunAPIClient from '../../../Hooks/useSHOGunAPIClient'; +import useSHOGunAPIClient from '../../../../Hooks/useSHOGunAPIClient'; +import PermissionModal, { + PermissionModalProps +} from '../PermissionModal/PermissionModal'; +import PermissionSelect from '../PermissionSelect/PermissionSelect'; -import PermissionSelect from './PermissionSelect/PermissionSelect'; -import UserAvatar from './UserAvatar/UserAvatar'; -import UserPermissionModal from './UserPermissionModal/UserPermissionModal'; +import './InstancePermissionGrid.less'; -interface DataType { - key: number | undefined; - user: User; +export interface DataType { + key: number; + reference: T; name: string; permission: PermissionCollectionType; } -export interface UserPermissionGridProps extends TableProps { +export interface InstancePermissionGridProps extends TableProps { entityType: string; entityId: number; -} + getInstancePermissions: (entityId: number) => Promise; + setInstancePermission: (entityId: number, referenceId: number, permission: PermissionCollectionType) => Promise; + deleteInstancePermission: (entityId: number, referenceId: number) => Promise; + toDataType: (permission: InstancePermission) => DataType; + nameColumnDefinition: ColumnType; + modalProps: Omit; +}; -const UserPermissionGrid: React.FC = ({ +const InstancePermissionGrid: React.FC = ({ entityType, entityId, + getInstancePermissions, + setInstancePermission, + deleteInstancePermission, + toDataType, + modalProps, + nameColumnDefinition, ...passThroughProps }) => { const [permissionsLoading, setPermissionsLoading] = useState(false); - const [permissions, setPermissions] = useState([]); + const [permissions, setPermissions] = useState([]); const [data, setData] = useState([]); const searchInput = useRef(null); @@ -66,35 +84,32 @@ const UserPermissionGrid: React.FC = ({ const { t } = useTranslation(); const client = useSHOGunAPIClient(); - useEffect(() => { - (async () => { - setPermissionsLoading(true); + const getPermissions = useCallback(async () => { + setPermissionsLoading(true); - if (Number.isFinite(entityId)) { - try { - setPermissions(await (client as any)[entityType]().getUserInstancePermissions(entityId)); - } catch (error) { - message.error(t('UserPermissionGrid.loadErrorMsg')); - Logger.error(error); - } finally { - setPermissionsLoading(false); - } - } - })(); - }, [entityId, entityType, client, t]); + if (!Number.isFinite(entityId)) { + return; + } + + try { + setPermissions(await getInstancePermissions(entityId)); + } catch (error) { + message.error(t('InstancePermissionGrid.loadErrorMsg')); + Logger.error(error); + } finally { + setPermissionsLoading(false); + } + }, [entityId, getInstancePermissions, t]); useEffect(() => { - if (client && Array.isArray(permissions)) { - const userData: DataType[] = permissions.map((permission: UserInstancePermission): DataType => ({ - key: permission.user?.id, - user: permission.user, - name: `${permission.user?.providerDetails?.username} (${permission.user?.authProviderId})`, - permission: permission.permission?.name - })); + getPermissions(); + }, [getPermissions]); - setData(userData); + useEffect(() => { + if (client && Array.isArray(permissions)) { + setData(permissions.map(toDataType)); } - }, [client, permissions]); + }, [client, permissions, toDataType]); const getColumnSearchProps = (dataIndex: string): ColumnType => ({ filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => ( @@ -103,8 +118,8 @@ const UserPermissionGrid: React.FC = ({ > setSelectedKeys(e.target.value ? [e.target.value] : [])} onPressEnter={() => confirm()} /> @@ -115,7 +130,7 @@ const UserPermissionGrid: React.FC = ({ icon={} size="small" > - {t('UserPermissionGrid.filterSearchButtonText')} + {t('InstancePermissionGrid.filterSearchButtonText')} @@ -158,17 +173,16 @@ const UserPermissionGrid: React.FC = ({ setPermissionsLoading(true); try { - await (client as any)[entityType]().setUserInstancePermission(entityId, record.user?.id, permission); + setInstancePermission(entityId, record.reference?.id, permission); - const dataClone = _cloneDeep(data); - const match = dataClone.find(entry => entry.user?.id === record.user?.id); - if (!_isNil(match)) { - match.permission = permission; - } + let dataClone = _cloneDeep(data); + + let match = dataClone.find(entry => entry.reference?.id === record.reference?.id); + match.permission = permission; setData(dataClone); } catch (error) { - message.error(t('UserPermissionGrid.updateErrorMsg')); + message.error(t('InstancePermissionGrid.updateErrorMsg')); Logger.error(error); } finally { setPermissionsLoading(false); @@ -179,53 +193,34 @@ const UserPermissionGrid: React.FC = ({ setPermissionsLoading(true); try { - await (client as any)[entityType]().deleteUserInstancePermission(entityId, record.user?.id); + deleteInstancePermission(entityId, record.reference?.id); - let dataClone = data.filter(entry => entry.user?.id !== record.user?.id); + let dataClone = data.filter(entry => entry.reference?.id !== record.reference?.id); setData(dataClone); } catch (error) { - message.error(t('UserPermissionGrid.deleteErrorMsg')); + message.error(t('InstancePermissionGrid.deleteErrorMsg')); Logger.error(error); } finally { setPermissionsLoading(false); } }; - const onSave = async () => { - setPermissionsLoading(true); - - try { - setPermissions(await (client as any)[entityType]().getUserInstancePermissions(entityId)); - } catch (error) { - message.error(t('UserPermissionGrid.loadErrorMsg')); - Logger.error(error); - } finally { - setPermissionsLoading(false); - } + const getNameColumnDef = (): ColumnType => { + return { + ...{ + title: t('InstancePermissionGrid.nameColumnTitle'), + dataIndex: 'name', + key: 'name', + defaultSortOrder: 'ascend', + }, + ...getColumnSearchProps('name'), + ...nameColumnDefinition, + }; }; - const columns: ColumnsType = [{ - title: t('UserPermissionGrid.userColumnTitle'), - dataIndex: 'name', - key: 'name', - defaultSortOrder: 'ascend', - sorter: (a, b) => { - let usernameA = a?.user.providerDetails?.username; - let usernameB = b?.user.providerDetails?.username; - if (_isNil(usernameA) || _isNil(usernameB)) { - return 0; - } - return usernameA.localeCompare(usernameB); - }, - render: (value: any, record: DataType) => ( - - ), - ...getColumnSearchProps('name') - }, { - title: t('UserPermissionGrid.permissionColumnTitle'), + const columns: ColumnsType = [getNameColumnDef(), { + title: t('InstancePermissionGrid.permissionColumnTitle'), dataIndex: 'permission', key: 'permission', sorter: (a, b) => b.name?.localeCompare(a.name), @@ -239,10 +234,11 @@ const UserPermissionGrid: React.FC = ({ ) }, { title: ( - ), key: 'operation', @@ -252,7 +248,7 @@ const UserPermissionGrid: React.FC = ({ render: (value: any, record: DataType) => { return ( onDeleteClick(record)} @@ -264,7 +260,7 @@ const UserPermissionGrid: React.FC = ({ return ( = ({ ); }; -export default UserPermissionGrid; +export default InstancePermissionGrid; diff --git a/src/Component/FormField/Permission/PermissionModal/PermissionModal.less b/src/Component/FormField/Permission/PermissionModal/PermissionModal.less new file mode 100644 index 00000000..d6c23df7 --- /dev/null +++ b/src/Component/FormField/Permission/PermissionModal/PermissionModal.less @@ -0,0 +1,8 @@ +.permission-modal { + .ant-modal-body { + .description { + padding-bottom: 20px; + } + } +} + diff --git a/src/Component/FormField/Permission/PermissionModal/PermissionModal.spec.tsx b/src/Component/FormField/Permission/PermissionModal/PermissionModal.spec.tsx new file mode 100644 index 00000000..417334f1 --- /dev/null +++ b/src/Component/FormField/Permission/PermissionModal/PermissionModal.spec.tsx @@ -0,0 +1,9 @@ +import PermissionModal from './PermissionModal'; + +describe('', () => { + + it('is defined', () => { + expect(PermissionModal).not.toBeUndefined(); + }); + +}); diff --git a/src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.tsx b/src/Component/FormField/Permission/PermissionModal/PermissionModal.tsx similarity index 53% rename from src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.tsx rename to src/Component/FormField/Permission/PermissionModal/PermissionModal.tsx index a743b11d..1b11e2da 100644 --- a/src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.tsx +++ b/src/Component/FormField/Permission/PermissionModal/PermissionModal.tsx @@ -11,93 +11,104 @@ import { import { Button, - Form, - message, - Modal, ModalProps, + Modal, Select, - Tag, + Form, + message, Tooltip } from 'antd'; + import { DefaultOptionType } from 'antd/lib/select'; + import Logger from 'js-logger'; + import { CustomTagProps } from 'rc-select/lib/BaseSelect'; + import { useTranslation } from 'react-i18next'; import PermissionCollectionType from '@terrestris/shogun-util/dist/model/enum/PermissionCollectionType'; +import Group from '@terrestris/shogun-util/dist/model/Group'; import User from '@terrestris/shogun-util/dist/model/User'; -import useSHOGunAPIClient from '../../../../Hooks/useSHOGunAPIClient'; import PermissionSelect from '../PermissionSelect/PermissionSelect'; -import UserAvatar from '../UserAvatar/UserAvatar'; + +import './PermissionModal.less'; type FormData = { - userIds: number[]; + referenceIds: number[]; permission: PermissionCollectionType; }; -export interface UserPermissionModalProps extends ModalProps { +export interface PermissionModalProps extends ModalProps { entityId: number; entityType: string; + setInstancePermission: (entityId: number, referenceId: number, permission: PermissionCollectionType) => Promise; + getReferences: () => Promise<(User | Group)[]>; + toTag: (reference: User | Group) => DefaultOptionType; + tagRenderer?: (props: CustomTagProps) => JSX.Element; onSave?: () => void; -} + descriptionText?: string; + referenceLabelText?: string; + referenceExtraText?: string; + referenceSelectPlaceholderText?: string; + permissionSelectLabel?: string; + permissionSelectExtra?: string; + saveErrorMsg?: (placeholder: string) => string; +}; -const UserPermissionModal: React.FC = ({ +const PermissionModal: React.FC = ({ entityId, entityType, + setInstancePermission, + getReferences, + toTag, + tagRenderer, onSave = () => {}, + descriptionText = '', + referenceLabelText = '', + referenceExtraText = '', + referenceSelectPlaceholderText = '', + permissionSelectLabel = '', + permissionSelectExtra = '', + saveErrorMsg, ...passThroughProps }) => { - const [usersLoading, setUsersLoading] = useState(false); + const [loading, setLoading] = useState(false); const [isSaving, setIsSaving] = useState(false); - const [users, setUsers] = useState([]); + const [references, setReferences] = useState<(User | Group)[]>([]); const [options, setOptions] = useState(); const [visible, setVisible] = useState(false); const { t } = useTranslation(); - const client = useSHOGunAPIClient(); const [form] = Form.useForm(); useEffect(() => { (async () => { - setUsersLoading(true); + setLoading(true); try { - setUsers((await (client as any).user().findAll()).content); + setReferences(await getReferences()); } catch (error) { - message.error(t('UserPermissionModal.loadErrorMsg')); + message.error(t('PermissionModal.loadErrorMsg')); Logger.error(error); } finally { - setUsersLoading(false); + setLoading(false); } })(); - }, [client, t]); + }, [getReferences, t]); useEffect(() => { - if (Array.isArray(users)) { - const opts: DefaultOptionType[] = users.map(user => ({ - value: user.id, - filterValues: [ - user.providerDetails?.firstName, - user.providerDetails?.lastName, - user.providerDetails?.username, - user.providerDetails?.email - ], - label: ( - - ) - })); - setOptions(opts); + if (Array.isArray(references)) { + setOptions(references.map(toTag)); } - }, [users]); + }, [references, toTag]); const onClick = () => { setVisible(!visible); @@ -116,27 +127,25 @@ const UserPermissionModal: React.FC = ({ } const { - userIds, + referenceIds, permission } = form.getFieldsValue(); - let erroneousRequestUserIds = []; + let erroneousRequestReferenceIds = []; setIsSaving(true); - for (const userId of userIds) { + for (const referenceId of referenceIds) { try { - await (client as any)[entityType]().setUserInstancePermission(entityId, userId, permission); + await setInstancePermission(entityId, referenceId, permission); } catch (error) { - erroneousRequestUserIds.push(userId); + erroneousRequestReferenceIds.push(referenceId); Logger.error(error); } } - if (erroneousRequestUserIds.length > 0) { - message.error(t('UserPermissionModal.saveErrorMsg', { - userIds: erroneousRequestUserIds.join(', ') - })); + if (erroneousRequestReferenceIds.length > 0) { + message.error(saveErrorMsg(erroneousRequestReferenceIds.join(', '))); } form.resetFields(); @@ -145,32 +154,10 @@ const UserPermissionModal: React.FC = ({ onSave(); }; - const tagRender = (props: CustomTagProps) => { - const { - label, - ...passProps - } = props; - - const onPreventMouseDown = (event: React.MouseEvent) => { - event.preventDefault(); - event.stopPropagation(); - }; - - return ( - - {label} - - ); - }; - return ( <>