diff --git a/assets/fallbackConfig.js b/assets/fallbackConfig.js index 47e23ae5..569d3ae7 100644 --- a/assets/fallbackConfig.js +++ b/assets/fallbackConfig.js @@ -29,7 +29,8 @@ var shogunApplicationConfig = { 'Application', 'Layer', 'User', - 'Group' + 'Group', + 'Role' ], dashboard: { news: { diff --git a/assets/formconfigs/role.json b/assets/formconfigs/role.json new file mode 100644 index 00000000..354fed27 --- /dev/null +++ b/assets/formconfigs/role.json @@ -0,0 +1,128 @@ +{ + "i18n": { + "de": { + "entityName": "Rolle", + "navigationTitle": "Rollen", + "labelId": "ID", + "labelEstabl": "Erstellt am", + "labelEdit": "Zuletzt editiert am", + "labelKey": "Keycloak ID", + "labelKeyProvDetail": "Keycloak Details", + "titleId": "ID", + "titleKeyId": "Keycloak ID", + "roleDoesNotExistTitle": "Die Rolle existiert nicht in Keycloak", + "titleRolename": "Rollenname", + "titleLink": "Link zur Rolle", + "titleOpen": "Öffne Rolle in Keycloak" + }, + "en": { + "entityName": "Role", + "navigationTitle": "Roles", + "labelId": "ID", + "labelEstabl": "Created at", + "labelEdit": "Last edited on", + "labelKey": "Keycloak ID", + "labelKeyProvDetail": "Keycloak details", + "titleId": "ID", + "titleKeyId": "Keycloak ID", + "roleDoesNotExistTitle": "Role does not exist in Keycloak", + "titleRolename": "Role name", + "titleLink": "Link to role", + "titleOpen": "Open role in Keycloak" + } + }, + "endpoint": "/roles", + "entityType": "role", + "entityName": "#i18n.entityName", + "navigationTitle": "#i18n.navigationTitle", + "subTitle": "", + "formConfig": { + "name": "role", + "fields": [ + { + "dataType": "number", + "dataField": "id", + "readOnly": true, + "label": "#i18n.labelId" + }, + { + "dataField": "created", + "dataType": "date", + "readOnly": "true", + "component": "DateField", + "label": "#i18n.labelEstabl", + "fieldProps": { + "dateFormat": "DD.MM.YYYY HH:mm" + } + }, + { + "dataField": "modified", + "dataType": "date", + "readOnly": "true", + "label": "#i18n.labelEdit", + "component": "DateField", + "fieldProps": { + "dateFormat": "DD.MM.YYYY HH:mm" + } + }, + { + "dataType": "string", + "dataField": "authProviderId", + "label": "#i18n.labelKey" + }, + { + "component": "JSONEditor", + "dataField": "providerDetails", + "label": "#i18n.labelKeyProvDetail", + "fieldProps": {} + } + ] + }, + "tableConfig": { + "columnDefinition": [ + { + "title": "#i18n.titleId", + "dataIndex": "id", + "sortConfig": { + "isSortable": true + } + }, + { + "title": "#i18n.titleRolename", + "dataIndex": ["providerDetails", "name"], + "key": "name", + "sortConfig": { + "isSortable": true + }, + "filterConfig": { + "isFilterable": true + } + }, + { + "title": "#i18n.titleKeyId", + "dataIndex": "authProviderId", + "key": "authProviderId", + "cellRenderComponentName": "VerifyProviderDetailsCell", + "cellRenderComponentProps": { + "title": "#i18n.roleDoesNotExistTitle" + }, + "sortConfig": { + "isSortable": true + }, + "filterConfig": { + "isFilterable": true + } + }, + { + "title": "#i18n.titleLink", + "dataIndex": "authProviderId", + "key": "link", + "cellRenderComponentName": "LinkCell", + "cellRenderComponentProps": { + "title": "#i18n.titleOpen", + "template": "/auth/admin/master/console/#/SHOGun/roles/{}/details" + } + } + ] + } +} diff --git a/package-lock.json b/package-lock.json index 571229b7..90041ee8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@monaco-editor/react": "4.6.0", "@terrestris/base-util": "2.0.0", "@terrestris/ol-util": "19.0.0", - "@terrestris/shogun-util": "8.3.2", + "@terrestris/shogun-util": "8.4.0", "@uiw/react-md-editor": "4.0.4", "antd": "5.17.2", "i18next": "23.11.5", @@ -5677,9 +5677,9 @@ } }, "node_modules/@terrestris/shogun-util": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/@terrestris/shogun-util/-/shogun-util-8.3.2.tgz", - "integrity": "sha512-i3sshjcLWZ2U7VRup8RAhSbE+plpVY8l7HLXRU4kTxDVjtm1nu/vy+oM/5VPi+++i91XEcml2Cg7j5YGhMGbnw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@terrestris/shogun-util/-/shogun-util-8.4.0.tgz", + "integrity": "sha512-JqwuY1pSYPzU+8YKX7ocacEwn739wBfv+lPvGtmohbt+vN1ONloZdhIIpJnNpF/zsiMD6zJzz7I1gYLskKzJwA==", "dependencies": { "@terrestris/base-util": "^2.0.0", "geojson": "^0.5.0", diff --git a/package.json b/package.json index 5aae2710..8cc87358 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@monaco-editor/react": "4.6.0", "@terrestris/base-util": "2.0.0", "@terrestris/ol-util": "19.0.0", - "@terrestris/shogun-util": "8.3.2", + "@terrestris/shogun-util": "8.4.0", "@uiw/react-md-editor": "4.0.4", "antd": "5.17.2", "i18next": "23.11.5", 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..a9a86d93 --- /dev/null +++ b/src/Component/FormField/Permission/GroupPermissionGrid/GroupPermissionGrid.tsx @@ -0,0 +1,121 @@ +import React, { + useCallback +} from 'react'; + +import { + useTranslation +} from 'react-i18next'; + +import BaseEntity from '@terrestris/shogun-util/dist/model/BaseEntity'; +import PermissionCollectionType from '@terrestris/shogun-util/dist/model/enum/PermissionCollectionType'; +import Group from '@terrestris/shogun-util/dist/model/Group'; +import GroupInstancePermission from '@terrestris/shogun-util/dist/model/security/GroupInstancePermission'; +import GenericEntityService from '@terrestris/shogun-util/dist/service/GenericEntityService'; +import { PageOpts } from '@terrestris/shogun-util/dist/service/GenericService'; + +import useSHOGunAPIClient from '../../../../Hooks/useSHOGunAPIClient'; +import InstancePermissionGrid, { + DataType, + InstancePermissionGridProps +} from '../InstancePermissionGrid/InstancePermissionGrid'; + +export interface GroupPermissionGridProps extends Omit, + 'getInstancePermissions' | 'setInstancePermission' | 'deleteInstancePermission' | 'toDataType' | + 'nameColumnDefinition' | 'getReferences' | 'toTag' | 'modalProps'> { }; + +const GroupPermissionGrid: React.FC = ({ + entityType, + ...passThroughProps +}) => { + + const { t } = useTranslation(); + const client = useSHOGunAPIClient(); + + const service = useCallback(() => { + return (client?.[entityType] as () => GenericEntityService)(); + }, [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 (pageOpts?: PageOpts) => { + return await client?.group().findAll(pageOpts); + }; + + 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) => { + const aName = a.reference?.providerDetails?.name; + const bName = b.reference?.providerDetails?.name; + + if (!aName || !bName) { + return 0; + } + + return bName.localeCompare(aName); + } + }; + }; + + 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 83% rename from src/Component/FormField/UserPermissionGrid/UserPermissionGrid.less rename to src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.less index b719fd01..6b945eff 100644 --- a/src/Component/FormField/UserPermissionGrid/UserPermissionGrid.less +++ b/src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.less @@ -1,5 +1,5 @@ -.permission-grid { - .operation-column { +.instance-permission-grid { + .ant-table-cell.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/Permission/InstancePermissionGrid/InstancePermissionGrid.tsx b/src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.tsx new file mode 100644 index 00000000..89567fbe --- /dev/null +++ b/src/Component/FormField/Permission/InstancePermissionGrid/InstancePermissionGrid.tsx @@ -0,0 +1,300 @@ +import React, { + useEffect, + useState, + useRef, + useCallback +} from 'react'; + +import { + DeleteOutlined, + SearchOutlined +} from '@ant-design/icons'; + +import { + Button, + Input, + InputRef, + message, + Space, + Table, + Tooltip +} from 'antd'; +import type { + ColumnsType, + ColumnType, + TableProps +} from 'antd/es/table'; +import Logger from 'js-logger'; +import _cloneDeep from 'lodash/cloneDeep'; +import _get from 'lodash/get'; +import _isFunction from 'lodash/isFunction'; +import _isNil from 'lodash/isNil'; +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 Role from '@terrestris/shogun-util/dist/model/Role'; +import InstancePermission from '@terrestris/shogun-util/dist/model/security/InstancePermission'; +import User from '@terrestris/shogun-util/dist/model/User'; +import SHOGunAPIClient from '@terrestris/shogun-util/dist/service/SHOGunAPIClient'; + +import useSHOGunAPIClient from '../../../../Hooks/useSHOGunAPIClient'; +import PermissionModal, { + PermissionModalProps +} from '../PermissionModal/PermissionModal'; +import PermissionSelect from '../PermissionSelect/PermissionSelect'; + +import './InstancePermissionGrid.less'; + +type NonBaseEntityCLientKeys = 'cache' | 'info' | 'auth' | 'graphql' | 'openapi' | + 'getBasePath' | 'setBasePath' | 'getKeycloak' | 'setKeycloak'; + +export type EntityType = Exclude; + +export interface DataType { + key?: number; + reference: T; + name?: string; + permission: PermissionCollectionType; +} + +export interface InstancePermissionGridProps extends TableProps { + entityId: number; + entityType: EntityType; + getInstancePermissions: (entityId: number) => Promise; + setInstancePermission: (entityId: number, referenceId: number, permission: PermissionCollectionType) => Promise; + deleteInstancePermission: (entityId: number, referenceId: number) => Promise; + toDataType: (permission: E) => DataType; + nameColumnDefinition: ColumnType; + modalProps: Omit; +}; + +export const InstancePermissionGrid = ({ + entityId, + getInstancePermissions, + setInstancePermission, + deleteInstancePermission, + toDataType, + modalProps, + nameColumnDefinition, + ...passThroughProps +}: InstancePermissionGridProps): React.ReactElement | null => { + const [permissionsLoading, setPermissionsLoading] = useState(false); + const [permissions, setPermissions] = useState([]); + const [data, setData] = useState([]); + + const searchInput = useRef(null); + + const { t } = useTranslation(); + const client = useSHOGunAPIClient(); + + const getPermissions = useCallback(async () => { + setPermissionsLoading(true); + + 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(() => { + getPermissions(); + }, [getPermissions]); + + useEffect(() => { + if (client && Array.isArray(permissions)) { + setData(permissions.map(toDataType)); + } + }, [client, permissions, toDataType]); + + const getColumnSearchProps = (dataIndex: string): ColumnType => ({ + filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => ( +
+ setSelectedKeys(e.target.value ? [e.target.value] : [])} + onPressEnter={() => confirm()} + /> + + + + +
+ ), + filterIcon: (filtered: boolean) => ( + + ), + onFilter: (value, record) => { + const val = _get(record, dataIndex); + if (_isNil(val)) { + return false; + } + return val + .toLowerCase() + .includes((value as string).toLowerCase()); + }, + onFilterDropdownOpenChange: visible => { + if (visible) { + setTimeout(() => searchInput.current?.select(), 100); + } + } + }); + + const onPermissionSelect = async (permission: PermissionCollectionType, record: DataType) => { + setPermissionsLoading(true); + + if (!record.reference?.id) { + return; + } + + try { + setInstancePermission(entityId, record.reference.id, permission); + + let dataClone = _cloneDeep(data); + + let match = dataClone.find(entry => entry.reference?.id === record.reference?.id); + + if (match) { + match.permission = permission; + } + + setData(dataClone); + } catch (error) { + message.error(t('InstancePermissionGrid.updateErrorMsg')); + Logger.error(error); + } finally { + setPermissionsLoading(false); + } + }; + + const onDeleteClick = async (record: DataType) => { + setPermissionsLoading(true); + + if (!record.reference?.id) { + return; + } + + try { + deleteInstancePermission(entityId, record.reference.id); + + let dataClone = data.filter(entry => entry.reference?.id !== record.reference?.id); + + setData(dataClone); + } catch (error) { + message.error(t('InstancePermissionGrid.deleteErrorMsg')); + 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 = [getNameColumnDef(), { + title: t('InstancePermissionGrid.permissionColumnTitle'), + dataIndex: 'permission', + key: 'permission', + sorter: (a, b) => { + const aName = a.name; + const bName = b.name; + + if (!aName || !bName) { + return 0; + } + + return bName?.localeCompare(aName); + }, + render: (_: any, record: DataType) => ( + { + onPermissionSelect(val, record); + }} + /> + ) + }, { + title: ( + + ), + key: 'operation', + className: 'operation-column', + width: 100, + fixed: 'right', + render: (_: any, record: DataType) => { + return ( + + onDeleteClick(record)} + /> + + ); + }, + }]; + + return ( + + ); +}; + +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..3ec400c2 --- /dev/null +++ b/src/Component/FormField/Permission/PermissionModal/PermissionModal.less @@ -0,0 +1,19 @@ +.permission-modal { + .ant-modal-body { + .description { + padding-bottom: 20px; + } + } +} + +.permission-modal-reference-dropdown { + .ant-divider { + margin: 5px 0; + } + + .ant-pagination { + display: flex; + justify-content: flex-end; + padding: 5px; + } +} 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/Permission/PermissionModal/PermissionModal.tsx b/src/Component/FormField/Permission/PermissionModal/PermissionModal.tsx new file mode 100644 index 00000000..d7929bcd --- /dev/null +++ b/src/Component/FormField/Permission/PermissionModal/PermissionModal.tsx @@ -0,0 +1,272 @@ +import React, { + useEffect, + useState +} from 'react'; + +import { + PlusOutlined +} from '@ant-design/icons'; + +import { + Button, + ModalProps, + Modal, + Select, + Form, + message, + Tooltip, + Divider, + Pagination +} 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 { Page } from '@terrestris/shogun-util/dist/model/Page'; +import Role from '@terrestris/shogun-util/dist/model/Role'; +import User from '@terrestris/shogun-util/dist/model/User'; +import { PageOpts } from '@terrestris/shogun-util/dist/service/GenericService'; + +import PermissionSelect from '../PermissionSelect/PermissionSelect'; + +import './PermissionModal.less'; + +type FormData = { + referenceIds: number[]; + permission: PermissionCollectionType; +}; + +export interface PermissionModalProps extends ModalProps { + entityId: number; + setInstancePermission: (entityId: number, referenceId: number, permission: PermissionCollectionType) => Promise; + getReferences?: (pageOpts?: PageOpts) => Promise | undefined>; + toTag: (reference: User | Group | Role) => 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 PermissionModal: React.FC = ({ + entityId, + setInstancePermission, + getReferences, + toTag, + tagRenderer, + onSave = () => {}, + descriptionText = '', + referenceLabelText = '', + referenceExtraText = '', + referenceSelectPlaceholderText = '', + permissionSelectLabel = '', + permissionSelectExtra = '', + saveErrorMsg = () => '', + ...passThroughProps +}) => { + const [loading, setLoading] = useState(false); + const [isSaving, setIsSaving] = useState(false); + const [references, setReferences] = useState<(User | Group | Role)[]>([]); + const [options, setOptions] = useState(); + const [visible, setVisible] = useState(false); + + const [referencePage, setReferencePage] = useState(1); + const [referenceTotal, setReferenceTotal] = useState(); + + const { t } = useTranslation(); + const [form] = Form.useForm(); + + const referencePageSize = 20; + + useEffect(() => { + if (!getReferences) { + return; + } + + (async () => { + setLoading(true); + + try { + const refs = await getReferences({ + page: referencePage - 1, + size: referencePageSize + }); + + if (!refs) { + throw new Error('Failed to load references'); + } + + setReferenceTotal(refs.totalElements); + setReferencePage(refs.number + 1); + setReferences(refs.content); + } catch (error) { + message.error(t('PermissionModal.loadErrorMsg')); + Logger.error(error); + } finally { + setLoading(false); + } + })(); + }, [getReferences, t, referencePage]); + + useEffect(() => { + if (Array.isArray(references)) { + setOptions(references.map(toTag)); + } + }, [references, toTag]); + + const onClick = () => { + setVisible(!visible); + }; + + const onCancel = () => { + form.resetFields(); + setVisible(false); + }; + + const onOk = async () => { + try { + await form.validateFields(); + } catch (error) { + return; + } + + const { + referenceIds, + permission + } = form.getFieldsValue(); + + let erroneousRequestReferenceIds = []; + + setIsSaving(true); + + for (const referenceId of referenceIds) { + try { + await setInstancePermission(entityId, referenceId, permission); + } catch (error) { + erroneousRequestReferenceIds.push(referenceId); + Logger.error(error); + } + } + + if (erroneousRequestReferenceIds.length > 0) { + message.error(saveErrorMsg(erroneousRequestReferenceIds.join(', '))); + } + + form.resetFields(); + setIsSaving(false); + setVisible(false); + onSave(); + }; + + const onPaginationChange = (page: number) => { + setReferencePage(page); + }; + + return ( + <> + + - - - - ), - filterIcon: (filtered: boolean) => ( - - ), - onFilter: (value, record) => { - const val = _get(record, dataIndex); - if (_isNil(val)) { - return false; - } - return val - .toLowerCase() - .includes((value as string).toLowerCase()); - }, - onFilterDropdownOpenChange: visible => { - if (visible) { - setTimeout(() => searchInput.current?.select(), 100); - } - } - }); - - const onPermissionSelect = async (permission: PermissionCollectionType, record: DataType) => { - setPermissionsLoading(true); - - try { - await (client as any)[entityType]().setUserInstancePermission(entityId, record.user?.id, permission); - - const dataClone = _cloneDeep(data); - const match = dataClone.find(entry => entry.user?.id === record.user?.id); - if (!_isNil(match)) { - match.permission = permission; - } - - setData(dataClone); - } catch (error) { - message.error(t('UserPermissionGrid.updateErrorMsg')); - Logger.error(error); - } finally { - setPermissionsLoading(false); - } - }; - - const onDeleteClick = async (record: DataType) => { - setPermissionsLoading(true); - - try { - await (client as any)[entityType]().deleteUserInstancePermission(entityId, record.user?.id); - - let dataClone = data.filter(entry => entry.user?.id !== record.user?.id); - - setData(dataClone); - } catch (error) { - message.error(t('UserPermissionGrid.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 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'), - dataIndex: 'permission', - key: 'permission', - sorter: (a, b) => b.name?.localeCompare(a.name), - render: (value: any, record: DataType) => ( - { - onPermissionSelect(val, record); - }} - /> - ) - }, { - title: ( - - ), - key: 'operation', - className: 'operation-column', - width: 100, - fixed: 'right', - render: (value: any, record: DataType) => { - return ( - - onDeleteClick(record)} - /> - - ); - }, - }]; - - return ( -
- ); -}; - -export default UserPermissionGrid; diff --git a/src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.less b/src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.less deleted file mode 100644 index 8145dd2f..00000000 --- a/src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.less +++ /dev/null @@ -1,38 +0,0 @@ -.user-modal { - .ant-modal-body { - .description { - padding-bottom: 20px; - } - - .ant-select-selector .user-avatar-tag { - display: flex; - align-items: center; - - .user-avatar { - display: flex; - align-items: center; - - .user-avatar-image { - margin-right: 10px; - height: 25px; - width: 25px; - } - - div.user-avatar-name { - display: flex; - flex-direction: column; - - > span:first-child { - margin-right: 5px; - font-weight: unset; - } - - > span:nth-of-type(2) { - display: none; - } - } - } - } - } -} - diff --git a/src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.spec.tsx b/src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.spec.tsx deleted file mode 100644 index 3836b782..00000000 --- a/src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.spec.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import UserPermissionModal from './UserPermissionModal'; - -describe('', () => { - - it('is defined', () => { - expect(UserPermissionModal).not.toBeUndefined(); - }); - -}); diff --git a/src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.tsx b/src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.tsx deleted file mode 100644 index a743b11d..00000000 --- a/src/Component/FormField/UserPermissionGrid/UserPermissionModal/UserPermissionModal.tsx +++ /dev/null @@ -1,238 +0,0 @@ -import './UserPermissionModal.less'; - -import React, { - useEffect, - useState -} from 'react'; - -import { - PlusOutlined -} from '@ant-design/icons'; - -import { - Button, - Form, - message, - Modal, - ModalProps, - Select, - Tag, - 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 User from '@terrestris/shogun-util/dist/model/User'; - -import useSHOGunAPIClient from '../../../../Hooks/useSHOGunAPIClient'; -import PermissionSelect from '../PermissionSelect/PermissionSelect'; -import UserAvatar from '../UserAvatar/UserAvatar'; - -type FormData = { - userIds: number[]; - permission: PermissionCollectionType; -}; - -export interface UserPermissionModalProps extends ModalProps { - entityId: number; - entityType: string; - onSave?: () => void; -} - -const UserPermissionModal: React.FC = ({ - entityId, - entityType, - onSave = () => {}, - ...passThroughProps -}) => { - const [usersLoading, setUsersLoading] = useState(false); - const [isSaving, setIsSaving] = useState(false); - const [users, setUsers] = useState([]); - const [options, setOptions] = useState(); - const [visible, setVisible] = useState(false); - - const { t } = useTranslation(); - const client = useSHOGunAPIClient(); - const [form] = Form.useForm(); - - useEffect(() => { - (async () => { - setUsersLoading(true); - - try { - setUsers((await (client as any).user().findAll()).content); - } catch (error) { - message.error(t('UserPermissionModal.loadErrorMsg')); - Logger.error(error); - } finally { - setUsersLoading(false); - } - })(); - }, [client, 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); - } - }, [users]); - - const onClick = () => { - setVisible(!visible); - }; - - const onCancel = () => { - form.resetFields(); - setVisible(false); - }; - - const onOk = async () => { - try { - await form.validateFields(); - } catch (error) { - return; - } - - const { - userIds, - permission - } = form.getFieldsValue(); - - let erroneousRequestUserIds = []; - - setIsSaving(true); - - for (const userId of userIds) { - try { - await (client as any)[entityType]().setUserInstancePermission(entityId, userId, permission); - } catch (error) { - erroneousRequestUserIds.push(userId); - Logger.error(error); - } - } - - if (erroneousRequestUserIds.length > 0) { - message.error(t('UserPermissionModal.saveErrorMsg', { - userIds: erroneousRequestUserIds.join(', ') - })); - } - - form.resetFields(); - setIsSaving(false); - setVisible(false); - onSave(); - }; - - const tagRender = (props: CustomTagProps) => { - const { - label, - ...passProps - } = props; - - const onPreventMouseDown = (event: React.MouseEvent) => { - event.preventDefault(); - event.stopPropagation(); - }; - - return ( - - {label} - - ); - }; - - return ( - <> - -