diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/types.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/types.tsx index 1b852925..a1db947a 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/types.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/types.tsx @@ -2,9 +2,16 @@ import { User } from '@/features/auth'; export interface Access { id: string; + max_ancestors_role: Role; role: Role; + max_role: Role; team: string; user: User; + document: { + id: string; + path: string; + depth: number; + }; abilities: { destroy: boolean; partial_update: boolean; @@ -21,10 +28,17 @@ export enum Role { OWNER = 'owner', } +export const RoleImportance = { + [Role.READER]: 1, + [Role.EDITOR]: 2, + [Role.ADMIN]: 3, + [Role.OWNER]: 4, +}; + export enum LinkReach { RESTRICTED = 'restricted', - PUBLIC = 'public', AUTHENTICATED = 'authenticated', + PUBLIC = 'public', } export enum LinkRole { @@ -43,13 +57,19 @@ export interface Doc { created_at: string; creator: string; depth: number; + path: string; is_favorite: boolean; link_reach: LinkReach; link_role: LinkRole; nb_accesses_direct: number; nb_accesses_ancestors: number; + computed_link_reach: LinkReach; + computed_link_role?: LinkRole; + ancestors_link_reach: LinkReach; + ancestors_link_role?: LinkRole; numchild: number; updated_at: string; + user_role: Role; user_roles: Role[]; abilities: { accesses_manage: boolean; @@ -74,9 +94,16 @@ export interface Doc { versions_destroy: boolean; versions_list: boolean; versions_retrieve: boolean; + link_select_options: LinkSelectOption; }; } +export interface LinkSelectOption { + public?: LinkRole[]; + authenticated?: LinkRole[]; + restricted?: LinkRole[]; +} + export enum DocDefaultFilter { ALL_DOCS = 'all_docs', MY_DOCS = 'my_docs', diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/utils.ts b/src/frontend/apps/impress/src/features/docs/doc-management/utils.ts index 2c229128..62f0bad6 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/utils.ts +++ b/src/frontend/apps/impress/src/features/docs/doc-management/utils.ts @@ -1,6 +1,6 @@ import * as Y from 'yjs'; -import { Doc, Role } from './types'; +import { Doc, LinkReach, LinkRole, Role } from './types'; export const currentDocRole = (abilities: Doc['abilities']): Role => { return abilities.destroy @@ -22,3 +22,23 @@ export const base64ToYDoc = (base64: string) => { export const base64ToBlocknoteXmlFragment = (base64: string) => { return base64ToYDoc(base64).getXmlFragment('document-store'); }; + +export const getDocLinkReach = (doc: Doc): LinkReach => { + return doc.computed_link_reach ?? doc.link_reach; +}; + +export const getDocLinkRole = (doc: Doc): LinkRole => { + return doc.computed_link_role ?? doc.link_role; +}; + +export const docLinkIsDesync = (doc: Doc) => { + // If the document has no ancestors + if (!doc.ancestors_link_reach) { + return false; + } + + return ( + doc.computed_link_reach !== doc.ancestors_link_reach || + doc.computed_link_role !== doc.ancestors_link_role + ); +}; diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/api/index.ts b/src/frontend/apps/impress/src/features/docs/doc-tree/api/index.ts index e3ca92cb..6cdf4290 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-tree/api/index.ts +++ b/src/frontend/apps/impress/src/features/docs/doc-tree/api/index.ts @@ -1,2 +1,3 @@ export * from './useCreateChildren'; export * from './useDocChildren'; +export * from './useMove'; diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTree.tsx b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTree.tsx index 90af2319..4495d536 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTree.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTree.tsx @@ -10,12 +10,11 @@ import { css } from 'styled-components'; import { Box, StyledLink } from '@/components'; import { useCunninghamTheme } from '@/cunningham'; +import { Doc, KEY_SUB_PAGE, useDoc, useDocStore } from '@/docs/doc-management'; +import { SimpleDocItem } from '@/docs/docs-grid'; -import { Doc, KEY_SUB_PAGE, useDoc, useDocStore } from '../../doc-management'; -import { SimpleDocItem } from '../../docs-grid'; import { useDocTree } from '../api/useDocTree'; import { useMoveDoc } from '../api/useMove'; -import { canDrag, canDrop } from '../utils'; import { DocSubPageItem } from './DocSubPageItem'; import { DocTreeItemActions } from './DocTreeItemActions'; @@ -211,13 +210,13 @@ export const DocTree = ({ initialTargetId }: DocTreeProps) => { } const parentDoc = parentNode?.data.value as Doc; if (!parentDoc) { - return canDrop(rootNode); + return rootNode?.abilities.move; } - return canDrop(parentDoc); + return parentDoc?.abilities.move; }} canDrag={(node) => { const doc = node.value as Doc; - return canDrag(doc); + return doc.abilities.move; }} rootNodeId={treeContext.root?.id ?? ''} renderNode={DocSubPageItem} diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItemActions.tsx b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItemActions.tsx index ef8d5f19..37e79a81 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItemActions.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItemActions.tsx @@ -17,7 +17,6 @@ import { useCreateChildrenDoc } from '../api/useCreateChildren'; import { useDetachDoc } from '../api/useDetach'; import MoveDocIcon from '../assets/doc-extract-bold.svg'; import { useTreeUtils } from '../hooks'; -import { isOwnerOrAdmin } from '../utils'; type DocTreeItemActionsProps = { doc: Doc; @@ -36,7 +35,6 @@ export const DocTreeItemActions = ({ const deleteModal = useModal(); const { togglePanel } = useLeftPanelStore(); const copyLink = useCopyDocLink(doc.id); - const canUpdate = isOwnerOrAdmin(doc); const { isCurrentParent } = useTreeUtils(doc); const { mutate: detachDoc } = useDetachDoc(); const treeContext = useTreeContext(); @@ -70,7 +68,7 @@ export const DocTreeItemActions = ({ ? [ { label: t('Convert to doc'), - isDisabled: !canUpdate, + isDisabled: !doc.abilities.move, icon: ( , callback: deleteModal.open, }, @@ -138,7 +136,7 @@ export const DocTreeItemActions = ({ $variation="600" /> - {canUpdate && ( + {doc.abilities.children_create && ( { e.stopPropagation(); diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/utils.ts b/src/frontend/apps/impress/src/features/docs/doc-tree/utils.ts index 60d17d86..789e9211 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-tree/utils.ts +++ b/src/frontend/apps/impress/src/features/docs/doc-tree/utils.ts @@ -1,6 +1,6 @@ import { TreeViewDataType } from '@gouvfr-lasuite/ui-kit'; -import { Doc, Role } from '../doc-management'; +import { Doc } from '../doc-management'; export const subPageToTree = (children: Doc[]): TreeViewDataType[] => { children.forEach((child) => { @@ -9,17 +9,3 @@ export const subPageToTree = (children: Doc[]): TreeViewDataType[] => { }); return children; }; - -export const isOwnerOrAdmin = (doc: Doc): boolean => { - return doc.user_roles.some( - (role) => role === Role.OWNER || role === Role.ADMIN, - ); -}; - -export const canDrag = (doc: Doc): boolean => { - return isOwnerOrAdmin(doc); -}; - -export const canDrop = (doc: Doc): boolean => { - return isOwnerOrAdmin(doc); -}; diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocGridContentList.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocGridContentList.tsx index fb62a421..561d16e0 100644 --- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocGridContentList.tsx +++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocGridContentList.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; import { Box, Text } from '@/components'; import { Doc, KEY_LIST_DOC } from '@/docs/doc-management'; -import { useMoveDoc } from '@/docs/doc-tree/api/useMove'; +import { useMoveDoc } from '@/docs/doc-tree'; import { useDragAndDrop } from '../hooks/useDragAndDrop'; diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/hooks/useDragAndDrop.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/hooks/useDragAndDrop.tsx index 531708f4..f89c3ece 100644 --- a/src/frontend/apps/impress/src/features/docs/docs-grid/hooks/useDragAndDrop.tsx +++ b/src/frontend/apps/impress/src/features/docs/docs-grid/hooks/useDragAndDrop.tsx @@ -21,7 +21,7 @@ export function useDragAndDrop( const [selectedDoc, setSelectedDoc] = useState(); const [canDrop, setCanDrop] = useState(); - const canDrag = selectedDoc?.abilities.move; + const canDrag = !!selectedDoc?.abilities.move; const mouseSensor = useSensor(MouseSensor, { activationConstraint }); const touchSensor = useSensor(TouchSensor, { activationConstraint }); diff --git a/src/frontend/apps/impress/src/features/service-worker/plugins/ApiPlugin.ts b/src/frontend/apps/impress/src/features/service-worker/plugins/ApiPlugin.ts index d41da7fd..dfd5bd00 100644 --- a/src/frontend/apps/impress/src/features/service-worker/plugins/ApiPlugin.ts +++ b/src/frontend/apps/impress/src/features/service-worker/plugins/ApiPlugin.ts @@ -1,7 +1,7 @@ import { WorkboxPlugin } from 'workbox-core'; import { Doc, DocsResponse } from '@/docs/doc-management'; -import { LinkReach, LinkRole } from '@/docs/doc-management/types'; +import { LinkReach, LinkRole, Role } from '@/docs/doc-management/types'; import { DBRequest, DocsDB } from '../DocsDB'; import { RequestSerializer } from '../RequestSerializer'; @@ -201,10 +201,21 @@ export class ApiPlugin implements WorkboxPlugin { versions_destroy: true, versions_list: true, versions_retrieve: true, + link_select_options: { + public: [LinkRole.READER, LinkRole.EDITOR], + authenticated: [LinkRole.READER, LinkRole.EDITOR], + restricted: undefined, + }, }, link_reach: LinkReach.RESTRICTED, link_role: LinkRole.READER, - user_roles: [], + user_roles: [Role.OWNER], + user_role: Role.OWNER, + path: '', + computed_link_reach: LinkReach.RESTRICTED, + computed_link_role: LinkRole.READER, + ancestors_link_reach: LinkReach.RESTRICTED, + ancestors_link_role: undefined, }; await DocsDB.cacheResponse(