diff --git a/src/api/document.ts b/src/api/document.ts index acd58ad9d..11a216829 100644 --- a/src/api/document.ts +++ b/src/api/document.ts @@ -141,6 +141,7 @@ export interface TaskableDocumentMapping extends AssessableDataMapping { } export interface TypeDataMapping extends TaskableDocumentMapping, ContainerTypeDataMapping { + ['_unknown_']: { name: string }; ['code']: CodeData; // TODO: rename to `code_version` ['script_version']: ScriptVersionData; @@ -187,6 +188,7 @@ null as unknown as EnsureAllTaskable; export interface TypeModelMapping extends TaskableTypeModelMapping, ContainerTypeModelMapping, AssessableTypeModelMapping { + ['_unknown_']: any; ['code']: Code; // TODO: rename to `code_version`? ['script_version']: ScriptVersion; diff --git a/src/api/documentRoot.ts b/src/api/documentRoot.ts index 887adb2c7..70ed629a0 100644 --- a/src/api/documentRoot.ts +++ b/src/api/documentRoot.ts @@ -43,6 +43,12 @@ export function find(id: string, signal: AbortSignal): AxiosPromise { - const params = new URLSearchParams(); - if (ignoreMissingRoots) { - params.append('ignoreMissingRoots', '1'); - } - ids.forEach((id) => params.append('ids', id)); - if (documentType) { - params.append('type', documentType); - } - const data: { - documentRootIds: string[]; - ignoreMissingRoots?: boolean; - type?: string; - } = { + const data: FindManyForData = { documentRootIds: ids }; if (ignoreMissingRoots) { diff --git a/src/api/permission.ts b/src/api/permission.ts index e7f8f7688..ba43f76c7 100644 --- a/src/api/permission.ts +++ b/src/api/permission.ts @@ -24,6 +24,8 @@ export interface UserPermission extends UserPermissionBase { export type Permissions = { id: string; + access: Access; + sharedAccess: Access; userPermissions: UserPermissionBase[]; groupPermissions: GroupPermissionBase[]; }; @@ -69,3 +71,13 @@ export function deleteGroupPermission(id: string, signal: AbortSignal): AxiosPro export function permissionsFor(documentRootId: string, signal: AbortSignal): AxiosPromise { return api.get(`/documentRoots/${documentRootId}/permissions`, { signal }); } + +export function documentRootPermissions( + documentRootIds: string[], + signal: AbortSignal +): AxiosPromise { + const data = { + documentRootIds: documentRootIds + }; + return api.post(`/documentRoots/permissions`, data, { signal }); +} diff --git a/src/components/Admin/AdminPanel/index.tsx b/src/components/Admin/AdminPanel/index.tsx index 3e2cef3d8..1ba322d56 100644 --- a/src/components/Admin/AdminPanel/index.tsx +++ b/src/components/Admin/AdminPanel/index.tsx @@ -9,6 +9,7 @@ import StudentGroupPanel from '@tdev-components/Admin/StudentGroupPanel'; import UserTable from '@tdev-components/Admin/UserTable'; import AllowedActions from '../AllowedActions'; import CreateUser from '../CreateUser'; +import PermissionsControl from '../PermissionsControl'; const AdminPanel = observer(() => { const userStore = useStore('userStore'); @@ -24,13 +25,16 @@ const AdminPanel = observer(() => { } return (
- + + + + diff --git a/src/components/Admin/PermissionsControl/AccessOverview.tsx b/src/components/Admin/PermissionsControl/AccessOverview.tsx new file mode 100644 index 000000000..a11fa8891 --- /dev/null +++ b/src/components/Admin/PermissionsControl/AccessOverview.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.scss'; +import { observer } from 'mobx-react-lite'; +import { useStore } from '@tdev-hooks/useStore'; +import Badge from '@tdev-components/shared/Badge'; +import CopyBadge from '@tdev-components/shared/CopyBadge'; +import { AccessColor, AccessIcon } from '@tdev-components/PermissionsPanel/AccessBadge'; +import Icon from '@mdi/react'; +import { SIZE_XS } from '@tdev-components/shared/iconSizes'; +import { mdiAccount, mdiAccountGroup, mdiFileMultipleOutline, mdiShareVariantOutline } from '@mdi/js'; +import { DocumentType } from '@tdev-api/document'; + +interface Props { + doc: { id: string; type: DocumentType }; +} + +const AccessOverview = observer((props: Props) => { + const { doc } = props; + const docRootStore = useStore('documentRootStore'); + const viewStore = useStore('viewStore'); + const view = viewStore.permissionControl; + const root = docRootStore.find(doc.id); + return ( +
+ + {doc.type} + + + + │ + + + + + │ + + + + {root?.groupPermissions?.length} + + + {root?.userPermissions?.length} + + +
+ ); +}); + +export default AccessOverview; diff --git a/src/components/Admin/PermissionsControl/DocumentTypeSelector.tsx b/src/components/Admin/PermissionsControl/DocumentTypeSelector.tsx new file mode 100644 index 000000000..c55d41274 --- /dev/null +++ b/src/components/Admin/PermissionsControl/DocumentTypeSelector.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.scss'; +import { observer } from 'mobx-react-lite'; +import { useStore } from '@tdev-hooks/useStore'; +import Select, { StylesConfig } from 'react-select'; +import { DocumentType, TypeModelMapping } from '@tdev-api/document'; +import Button from '@tdev-components/shared/Button'; + +interface Props {} +const colourStyles: StylesConfig<{ value: DocumentType; hslDeg: number; label: DocumentType }, true> = { + control: (styles) => ({ ...styles, backgroundColor: 'white' }), + option: (styles, { data, isFocused, isSelected }) => { + return { + ...styles, + backgroundColor: isFocused ? `hsl(${data.hslDeg}, 70%, 70%)` : undefined, + color: `hsl(${data.hslDeg}, 100%, 25%)` + }; + }, + multiValue: (styles, { data }) => { + return { + ...styles, + backgroundColor: `hsl(${data.hslDeg}, 90%, 70%)` + }; + }, + multiValueLabel: (styles, { data }) => ({ + ...styles, + color: `hsl(${data.hslDeg}, 100%, 30%)` + }), + multiValueRemove: (styles, { data }) => ({ + ...styles, + color: `hsl(${data.hslDeg}, 100%, 30%)`, + ':hover': { + backgroundColor: `hsl(${data.hslDeg}, 100%, 50%)`, + color: 'white' + } + }), + menuPortal: (base) => ({ ...base, zIndex: 'var(--ifm-z-index-overlay)' }), + container: (base) => ({ ...base, minWidth: '15em' }) +}; + +const DocumentTypeSelector = observer((props: Props) => { + const viewStore = useStore('viewStore'); + const view = viewStore.permissionControl; + + return ( +
+