=> ({
+ 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 (
+ <>
+
+ }
+ />
+
+
+
+ {descriptionText}
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default PermissionModal;
diff --git a/src/Component/FormField/UserPermissionGrid/PermissionSelect/PermissionSelect.spec.tsx b/src/Component/FormField/Permission/PermissionSelect/PermissionSelect.spec.tsx
similarity index 100%
rename from src/Component/FormField/UserPermissionGrid/PermissionSelect/PermissionSelect.spec.tsx
rename to src/Component/FormField/Permission/PermissionSelect/PermissionSelect.spec.tsx
diff --git a/src/Component/FormField/UserPermissionGrid/PermissionSelect/PermissionSelect.tsx b/src/Component/FormField/Permission/PermissionSelect/PermissionSelect.tsx
similarity index 100%
rename from src/Component/FormField/UserPermissionGrid/PermissionSelect/PermissionSelect.tsx
rename to src/Component/FormField/Permission/PermissionSelect/PermissionSelect.tsx
diff --git a/src/Component/FormField/Permission/RolePermissionGrid/RolePermissionGrid.spec.tsx b/src/Component/FormField/Permission/RolePermissionGrid/RolePermissionGrid.spec.tsx
new file mode 100644
index 00000000..1425790c
--- /dev/null
+++ b/src/Component/FormField/Permission/RolePermissionGrid/RolePermissionGrid.spec.tsx
@@ -0,0 +1,9 @@
+import RolePermissionGrid from './RolePermissionGrid';
+
+describe('', () => {
+
+ it('is defined', () => {
+ expect(RolePermissionGrid).not.toBeUndefined();
+ });
+
+});
diff --git a/src/Component/FormField/Permission/RolePermissionGrid/RolePermissionGrid.tsx b/src/Component/FormField/Permission/RolePermissionGrid/RolePermissionGrid.tsx
new file mode 100644
index 00000000..888bbc93
--- /dev/null
+++ b/src/Component/FormField/Permission/RolePermissionGrid/RolePermissionGrid.tsx
@@ -0,0 +1,150 @@
+import React, {
+ useMemo
+} from 'react';
+
+import {
+ Tag
+} from 'antd';
+
+import {
+ CustomTagProps
+} from 'rc-select/lib/BaseSelect';
+
+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 Role from '@terrestris/shogun-util/dist/model/Role';
+import RoleInstancePermission from '@terrestris/shogun-util/dist/model/security/RoleInstancePermission';
+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 RolePermissionGridProps extends Omit,
+ 'getInstancePermissions' | 'setInstancePermission' | 'deleteInstancePermission' | 'toDataType' |
+ 'nameColumnDefinition' | 'getReferences' | 'toTag' | 'modalProps'> { };
+
+const RolePermissionGrid: React.FC = ({
+ entityType,
+ ...passThroughProps
+}) => {
+
+ const { t } = useTranslation();
+ const client = useSHOGunAPIClient();
+
+ const service = useMemo(() => {
+ return (client?.[entityType] as () => GenericEntityService)();
+ }, [client, entityType]);
+
+ const getRoleInstancePermissions = async (id: number) => {
+ return await service?.getRoleInstancePermissions(id);
+ };
+
+ const setRoleInstancePermission = async (id: number, referenceId: number, permission: PermissionCollectionType) => {
+ await service?.setRoleInstancePermission(id, referenceId, permission);
+ };
+
+ const deleteRoleInstancePermission = async (id: number, referenceId: number) => {
+ await service?.deleteRoleInstancePermission(id, referenceId);
+ };
+
+ const getRoles = async (pageOpts?: PageOpts) => {
+ return await client?.role().findAll(pageOpts);
+ };
+
+ const toRoleDataType = (permission: RoleInstancePermission): DataType => {
+ return {
+ key: permission.role?.id,
+ reference: permission.role,
+ name: permission.role?.providerDetails?.name,
+ permission: permission.permission?.name
+ };
+ };
+
+ const toTag = (role: Role) => {
+ return {
+ value: role.id,
+ filterValues: [
+ role.providerDetails?.name
+ ],
+ label: (
+ {role.providerDetails?.name}
+ )
+ };
+ };
+
+ const tagRenderer = (props: CustomTagProps) => {
+ const {
+ label,
+ ...passProps
+ } = props;
+
+ const onPreventMouseDown = (event: React.MouseEvent) => {
+ event.preventDefault();
+ event.stopPropagation();
+ };
+
+ return (
+
+ {label}
+
+ );
+ };
+
+ 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 aName.localeCompare(bName);
+ }
+ };
+ };
+
+ return (
+ {
+ return t('RolePermissionGrid.modal.saveErrorMsg', {
+ referenceIds: placeholder
+ });
+ }
+ }}
+ {...passThroughProps}
+ />
+ );
+};
+
+export default RolePermissionGrid;
diff --git a/src/Component/FormField/Permission/UserPermissionGrid/UserPermissionGrid.less b/src/Component/FormField/Permission/UserPermissionGrid/UserPermissionGrid.less
new file mode 100644
index 00000000..e6144e9b
--- /dev/null
+++ b/src/Component/FormField/Permission/UserPermissionGrid/UserPermissionGrid.less
@@ -0,0 +1,31 @@
+.permission-modal {
+ .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/UserPermissionGrid.spec.tsx b/src/Component/FormField/Permission/UserPermissionGrid/UserPermissionGrid.spec.tsx
similarity index 100%
rename from src/Component/FormField/UserPermissionGrid/UserPermissionGrid.spec.tsx
rename to src/Component/FormField/Permission/UserPermissionGrid/UserPermissionGrid.spec.tsx
diff --git a/src/Component/FormField/Permission/UserPermissionGrid/UserPermissionGrid.tsx b/src/Component/FormField/Permission/UserPermissionGrid/UserPermissionGrid.tsx
new file mode 100644
index 00000000..45ea9a09
--- /dev/null
+++ b/src/Component/FormField/Permission/UserPermissionGrid/UserPermissionGrid.tsx
@@ -0,0 +1,163 @@
+import React, {
+ useCallback
+} from 'react';
+
+import {
+ Tag
+} from 'antd';
+
+import {
+ CustomTagProps
+} from 'rc-select/lib/BaseSelect';
+
+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 UserInstancePermission from '@terrestris/shogun-util/dist/model/security/UserInstancePermission';
+import User from '@terrestris/shogun-util/dist/model/User';
+import GenericEntityService from '@terrestris/shogun-util/dist/service/GenericEntityService';
+import { PageOpts } from '@terrestris/shogun-util/dist/service/GenericService';
+
+import useSHOGunAPIClient from '../../../../Hooks/useSHOGunAPIClient';
+import UserAvatar from '../../../UserAvatar/UserAvatar';
+import InstancePermissionGrid, {
+ DataType,
+ InstancePermissionGridProps
+} from '../InstancePermissionGrid/InstancePermissionGrid';
+
+import './UserPermissionGrid.less';
+
+export interface UserPermissionGridProps extends Omit,
+ 'getInstancePermissions' | 'setInstancePermission' | 'deleteInstancePermission' | 'toDataType' |
+ 'nameColumnDefinition' | 'getReferences' | 'toTag' | 'modalProps'> { };
+
+const UserPermissionGrid: React.FC = ({
+ entityType,
+ ...passThroughProps
+}) => {
+
+ const { t } = useTranslation();
+ const client = useSHOGunAPIClient();
+
+ const service = useCallback(() => {
+ return (client?.[entityType] as () => GenericEntityService)();
+ }, [client, entityType]);
+
+ const getUserInstancePermissions = async (id: number) => {
+ return await service()?.getUserInstancePermissions(id);
+ };
+
+ const setUserInstancePermission = async (id: number, referenceId: number, permission: PermissionCollectionType) => {
+ await service()?.setUserInstancePermission(id, referenceId, permission);
+ };
+
+ const deleteUserInstancePermission = async (id: number, referenceId: number) => {
+ await service()?.deleteUserInstancePermission(id, referenceId);
+ };
+
+ const getUsers = async (pageOpts?: PageOpts) => {
+ return await client?.user().findAll(pageOpts);
+ };
+
+ const toUserDataType = (permission: UserInstancePermission): DataType => {
+ return {
+ key: permission.user?.id,
+ reference: permission.user,
+ name: permission.user?.providerDetails?.username,
+ permission: permission.permission?.name
+ };
+ };
+
+ const toTag = (user: User) => {
+ return {
+ value: user.id,
+ filterValues: [
+ user.providerDetails?.firstName,
+ user.providerDetails?.lastName,
+ user.providerDetails?.username,
+ user.providerDetails?.email
+ ],
+ label: (
+
+ )
+ };
+ };
+
+ const tagRenderer = (props: CustomTagProps) => {
+ const {
+ label,
+ ...passProps
+ } = props;
+
+ const onPreventMouseDown = (event: React.MouseEvent) => {
+ event.preventDefault();
+ event.stopPropagation();
+ };
+
+ return (
+
+ {label}
+
+ );
+ };
+
+ const colDefinition = () => {
+ return {
+ sorter: (a: DataType, b: DataType) => {
+ const aName = a.reference?.providerDetails?.username;
+ const bName = b.reference?.providerDetails?.username;
+
+ if (!aName || !bName) {
+ return 0;
+ }
+
+ return aName.localeCompare(bName);
+ },
+ render: (_: any, record: DataType) => (
+
+ )
+ };
+ };
+
+ return (
+ {
+ return t('UserPermissionGrid.modal.saveErrorMsg', {
+ referenceIds: placeholder
+ });
+ }
+ }}
+ {...passThroughProps}
+ />
+ );
+};
+
+export default UserPermissionGrid;
diff --git a/src/Component/FormField/UserPermissionGrid/UserPermissionGrid.tsx b/src/Component/FormField/UserPermissionGrid/UserPermissionGrid.tsx
deleted file mode 100644
index cd7c2752..00000000
--- a/src/Component/FormField/UserPermissionGrid/UserPermissionGrid.tsx
+++ /dev/null
@@ -1,278 +0,0 @@
-import './UserPermissionGrid.less';
-
-import React, {useEffect,
- useRef,
- useState} 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 UserInstancePermission from '@terrestris/shogun-util/dist/model/security/UserInstancePermission';
-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 UserPermissionModal from './UserPermissionModal/UserPermissionModal';
-
-interface DataType {
- key: number | undefined;
- user: User;
- name: string;
- permission: PermissionCollectionType;
-}
-
-export interface UserPermissionGridProps extends TableProps {
- entityType: string;
- entityId: number;
-}
-
-const UserPermissionGrid: React.FC = ({
- entityType,
- entityId,
- ...passThroughProps
-}) => {
- const [permissionsLoading, setPermissionsLoading] = useState(false);
- const [permissions, setPermissions] = useState([]);
- const [data, setData] = useState([]);
-
- const searchInput = useRef(null);
-
- const { t } = useTranslation();
- const client = useSHOGunAPIClient();
-
- useEffect(() => {
- (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]);
-
- 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
- }));
-
- setData(userData);
- }
- }, [client, permissions]);
-
- 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);
-
- 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 (
- <>
-
- }
- />
-
-
-
- {t('UserPermissionModal.description')}
-
-
-
-
-
-
-
-
-
- >
- );
-};
-
-export default UserPermissionModal;
diff --git a/src/Component/GeneralEntity/GeneralEntityForm/GeneralEntityForm.tsx b/src/Component/GeneralEntity/GeneralEntityForm/GeneralEntityForm.tsx
index 8f39bfda..94dfa428 100644
--- a/src/Component/GeneralEntity/GeneralEntityForm/GeneralEntityForm.tsx
+++ b/src/Component/GeneralEntity/GeneralEntityForm/GeneralEntityForm.tsx
@@ -26,7 +26,10 @@ import TranslationUtil from '../../../Util/TranslationUtil';
import DisplayField from '../../FormField/DisplayField/DisplayField';
import JSONEditor from '../../FormField/JSONEditor/JSONEditor';
import MarkdownEditor from '../../FormField/MarkdownEditor/MarkdownEditor';
-import UserPermissionGrid from '../../FormField/UserPermissionGrid/UserPermissionGrid';
+import GroupPermissionGrid from '../../FormField/Permission/GroupPermissionGrid/GroupPermissionGrid';
+import { EntityType } from '../../FormField/Permission/InstancePermissionGrid/InstancePermissionGrid';
+import RolePermissionGrid from '../../FormField/Permission/RolePermissionGrid/RolePermissionGrid';
+import UserPermissionGrid from '../../FormField/Permission/UserPermissionGrid/UserPermissionGrid';
import YesOrNoField from '../../FormField/YesOrNoField/YesOrNoField';
import LayerTypeSelect from '../../Layer/LayerTypeSelect/LayerTypeSelect';
@@ -196,7 +199,31 @@ export const GeneralEntityForm: React.FC = ({
return (
+ );
+ case 'GroupPermissionGrid':
+ if (entityId !== form.getFieldValue('id')) {
+ return undefined;
+ }
+
+ return (
+
+ );
+ case 'RolePermissionGrid':
+ if (entityId !== form.getFieldValue('id')) {
+ return undefined;
+ }
+
+ return (
+
);
@@ -275,12 +302,13 @@ export const GeneralEntityForm: React.FC = ({
}
const {
+ component,
dataField
} = copyFieldCfg;
return (
= () => {
const avatarSource = !_isNil(userInfo.providerDetails?.email) ? UserUtil.getGravatarUrl({
email: userInfo.providerDetails?.email || '',
- size: 28
+ size: 38
}) : '';
const onMenuClick = (evt: any) => {
diff --git a/src/Component/FormField/UserPermissionGrid/UserAvatar/UserAvatar.less b/src/Component/UserAvatar/UserAvatar.less
similarity index 100%
rename from src/Component/FormField/UserPermissionGrid/UserAvatar/UserAvatar.less
rename to src/Component/UserAvatar/UserAvatar.less
diff --git a/src/Component/FormField/UserPermissionGrid/UserAvatar/UserAvatar.spec.tsx b/src/Component/UserAvatar/UserAvatar.spec.tsx
similarity index 100%
rename from src/Component/FormField/UserPermissionGrid/UserAvatar/UserAvatar.spec.tsx
rename to src/Component/UserAvatar/UserAvatar.spec.tsx
diff --git a/src/Component/FormField/UserPermissionGrid/UserAvatar/UserAvatar.tsx b/src/Component/UserAvatar/UserAvatar.tsx
similarity index 93%
rename from src/Component/FormField/UserPermissionGrid/UserAvatar/UserAvatar.tsx
rename to src/Component/UserAvatar/UserAvatar.tsx
index 7be1e892..7e74aca9 100644
--- a/src/Component/FormField/UserPermissionGrid/UserAvatar/UserAvatar.tsx
+++ b/src/Component/UserAvatar/UserAvatar.tsx
@@ -9,7 +9,7 @@ import {
import User from '@terrestris/shogun-util/dist/model/User';
-import UserUtil from '../../../../Util/UserUtil';
+import UserUtil from '../../Util/UserUtil';
export interface UserAvatarProps extends AvatarProps {
user: User;
@@ -22,7 +22,7 @@ const UserAvatar: React.FC = ({
const avatarSrc = UserUtil.getGravatarUrl({
email: user.providerDetails?.email || '',
- size: 32
+ size: 30
});
return (
diff --git a/src/Controller/GenericEntityController.ts b/src/Controller/GenericEntityController.ts
index 38e278d5..174539ff 100644
--- a/src/Controller/GenericEntityController.ts
+++ b/src/Controller/GenericEntityController.ts
@@ -146,12 +146,14 @@ export class GenericEntityController {
public async saveOrUpdate(): Promise {
const isUpdate = _isNumber(this.entity?.id);
+ const publicKey = this.formConfig?.publicKey as keyof T;
+
// omit constant fields and read only fields
let entityUpdateObject: Partial = _omit(this.entity, [
'created',
'modified',
...this.formConfig?.fields
- .filter(field => field.dataField === this.formConfig.publicKey || field.readOnly)
+ .filter(field => field.dataField === publicKey || field.readOnly)
.map(field => field.dataField)
]);
@@ -163,16 +165,19 @@ export class GenericEntityController {
};
}
- const isPublic = this.entity?.[this.formConfig.publicKey as keyof T] as boolean;
-
this.entity = isUpdate ?
await this.service.update(entityUpdateObject as T) :
await this.service.add(entityUpdateObject as T);
- if (isPublic) {
- await this.service.setPublic(this.entity.id!);
- } else {
- await this.service.revokePublic(this.entity.id!);
+
+ if (publicKey) {
+ const isPublic = this.entity?.[publicKey] as boolean;
+
+ if (isPublic) {
+ await this.service.setPublic(this.entity.id!);
+ } else {
+ await this.service.revokePublic(this.entity.id!);
+ }
}
return this.entity;
diff --git a/src/i18n/translations.ts b/src/i18n/translations.ts
index e91a1505..b34346e8 100644
--- a/src/i18n/translations.ts
+++ b/src/i18n/translations.ts
@@ -165,14 +165,14 @@ export default {
yes: 'Ja',
no: 'Nein'
},
- UserPermissionGrid: {
+ InstancePermissionGrid: {
loadErrorMsg: 'Fehler beim Laden der Berechtigungen',
updateErrorMsg: 'Fehler beim Aktualisieren der Berechtigung',
deleteErrorMsg: 'Fehler beim Löschen der Berechtigung',
filterInputPlaceholder: 'Suche…',
filterSearchButtonText: 'Suche',
filterResetButtonText: 'Zurücksetzen',
- userColumnTitle: 'Name',
+ nameColumnTitle: 'Name',
permissionColumnTitle: 'Berechtigung',
deletePermissionButtonTooltip: 'Berechtigung löschen'
},
@@ -183,18 +183,49 @@ export default {
readUpdateDeleteLabel: 'Aktualisieren & Löschen',
adminLabel: 'Besitzer'
},
- UserPermissionModal: {
+ PermissionModal: {
loadErrorMsg: 'Fehler beim Laden der Berechtigungen',
- saveErrorMsg: 'Fehler beim Speichern der Berechtigung für die Nutzer mit den IDs: {{userIds}}',
openModalButtonTooltipTitle: 'Berechtigung hinzufügen',
title: 'Berechtigung hinzufügen',
- description: 'Wählen Sie einen oder mehrere Nutzer sowie die zugehörige Berechtigung aus.',
- userSelectLabel: 'Nutzername oder Email Adresse',
- userSelectExtra: 'Wählen Sie die Nutzer aus der Liste aus oder geben Sie einen ' +
- 'Suchbegriff (Nutzername oder Email Adresse) ein',
- userSelectPlaceholder: 'Nutzer auswählen…',
- permissionSelectLabel: 'Berechtigung',
- permissionSelectExtra: 'Wählen Sie die Berechtigung aus, die die Nutzer erhalten sollen.'
+ paginationTotal: 'Total',
+ paginationNextPage: 'Nächste Seite',
+ paginationPrevPage: 'Vorherige Seite'
+ },
+ GroupPermissionGrid: {
+ modal: {
+ description: 'Wählen Sie eine oder mehrere Gruppen sowie die zugehörige Berechtigung aus.',
+ referenceSelectLabel: 'Gruppenname',
+ referenceSelectExtra: 'Wählen Sie die Gruppe aus der Liste aus oder geben Sie einen ' +
+ 'Suchbegriff (Gruppenname) ein',
+ referenceSelectPlaceholder: 'Gruppe auswählen…',
+ permissionSelectLabel: 'Berechtigung',
+ permissionSelectExtra: 'Wählen Sie die Berechtigung aus, die die Gruppe erhalten sollen.',
+ saveErrorMsg: 'Fehler beim Speichern der Berechtigung für die Gruppen mit den IDs: {{referenceIds}}'
+ }
+ },
+ UserPermissionGrid: {
+ modal: {
+ description: 'Wählen Sie einen oder mehrere Nutzer sowie die zugehörige Berechtigung aus.',
+ referenceSelectLabel: 'Nutzername oder Email Adresse',
+ referenceSelectExtra: 'Wählen Sie die Nutzer aus der Liste aus oder geben Sie einen ' +
+ 'Suchbegriff (Gruppenname oder Email Adresse) ein',
+ referenceSelectPlaceholder: 'Nutzer auswählen…',
+ permissionSelectLabel: 'Berechtigung',
+ permissionSelectExtra: 'Wählen Sie die Berechtigung aus, die die Nutzer erhalten sollen.',
+ saveErrorMsg: 'Fehler beim Speichern der Berechtigung für die Nutzer mit den IDs: {{referenceIds}}'
+ }
+ },
+ RolePermissionGrid: {
+ modal: {
+ description: 'Wählen Sie einen oder mehrere Rolle sowie die zugehörige Berechtigung aus.',
+ referenceSelectLabel: 'Rollenname',
+ referenceSelectExtra: 'Wählen Sie die Rolle aus der Liste aus oder geben Sie einen ' +
+ 'Suchbegriff (Rollenname) ein',
+ referenceSelectPlaceholder: 'Rolle auswählen…',
+ permissionSelectLabel: 'Berechtigung',
+ permissionSelectExtra: 'Wählen Sie die Berechtigung aus, die die Rolle erhalten sollen.',
+ saveErrorMsg: 'Fehler beim Speichern der Berechtigung für die Rollen mit den IDs: {{referenceIds}}'
+ }
},
ApplicationInfoModal: {
clientAbout: 'Über',
@@ -398,14 +429,15 @@ export default {
deleteFailDescript: 'The file "{{record}}" could not be deleted!',
reloadTooltip: 'Reload'
},
- UserPermissionGrid: {
+
+ InstancePermissionGrid: {
loadErrorMsg: 'Error while loading the permissions',
updateErrorMsg: 'Error while updating the permission',
deleteErrorMsg: 'Error while deleting the permission',
filterInputPlaceholder: 'Search…',
filterSearchButtonText: 'Search',
filterResetButtonText: 'Reset',
- userColumnTitle: 'Name',
+ nameColumnTitle: 'Name',
permissionColumnTitle: 'Permission',
deletePermissionButtonTooltip: 'Delete permission'
},
@@ -416,17 +448,46 @@ export default {
readUpdateDeleteLabel: 'Update & Delete',
adminLabel: 'Owner'
},
- UserPermissionModal: {
+ PermissionModal: {
loadErrorMsg: 'Error while loading the permissions',
- saveErrorMsg: 'Error while setting the permission for users with IDs: {{userIds}}',
openModalButtonTooltipTitle: 'Add permission',
title: 'Add permission',
- description: 'Select one or more users and the respective permission.',
- userSelectLabel: 'Username or email address',
- userSelectExtra: 'Select users from the list or search via username or email address.',
- userSelectPlaceholder: 'Select user(s)…',
- permissionSelectLabel: 'Permission',
- permissionSelectExtra: 'Select the permission the users should be granted.'
+ paginationTotal: 'Total',
+ paginationNextPage: 'Next page',
+ paginationPrevPage: 'Previous page'
+ },
+ GroupPermissionGrid: {
+ modal: {
+ description: 'Select one or more groups and the respective permission.',
+ referenceSelectLabel: 'Group name',
+ referenceSelectExtra: 'Select groups from the list or search via name.',
+ referenceSelectPlaceholder: 'Select groups(s)…',
+ permissionSelectLabel: 'Permission',
+ permissionSelectExtra: 'Select the permission the groups should be granted.',
+ saveErrorMsg: 'Error while setting the permission for groups with IDs: {{referenceIds}}'
+ }
+ },
+ UserPermissionGrid: {
+ modal: {
+ description: 'Select one or more users and the respective permission.',
+ referenceSelectLabel: 'Username or email address',
+ referenceSelectExtra: 'Select users from the list or search via username or email address.',
+ referenceSelectPlaceholder: 'Select user(s)…',
+ permissionSelectLabel: 'Permission',
+ permissionSelectExtra: 'Select the permission the users should be granted.',
+ saveErrorMsg: 'Error while setting the permission for users with IDs: {{referenceIds}}'
+ }
+ },
+ RolePermissionGrid: {
+ modal: {
+ description: 'Select one or more roles and the respective permission.',
+ referenceSelectLabel: 'Role name',
+ referenceSelectExtra: 'Select roles from the list or search via role name.',
+ referenceSelectPlaceholder: 'Select role(s)…',
+ permissionSelectLabel: 'Permission',
+ permissionSelectExtra: 'Select the permission the roles should be granted.',
+ saveErrorMsg: 'Error while setting the permission for roles with IDs: {{referenceIds}}'
+ }
},
ApplicationInfoModal: {
clientAbout: 'About',