From 8bc56f00007a381128a86e6bea8e36f56b11afea Mon Sep 17 00:00:00 2001 From: Nathan Panchout Date: Fri, 21 Feb 2025 08:12:56 +0100 Subject: [PATCH] wip --- .../src/components/common/tree/TreeView.tsx | 10 ++- .../src/components/common/tree/treeStore.ts | 4 +- .../docs/doc-management/api/useDoc.tsx | 1 + .../components/ModalRemoveDoc.tsx | 13 ++- .../features/docs/doc-management/types.tsx | 1 + .../features/docs/doc-tree/api/useDocTree.tsx | 2 + .../docs/doc-tree/components/DocTree.tsx | 87 +++++++++++++------ .../docs/doc-tree/components/DocTreeItem.tsx | 31 ++++++- 8 files changed, 117 insertions(+), 32 deletions(-) diff --git a/src/frontend/apps/impress/src/components/common/tree/TreeView.tsx b/src/frontend/apps/impress/src/components/common/tree/TreeView.tsx index 893003868..5143de4b2 100644 --- a/src/frontend/apps/impress/src/components/common/tree/TreeView.tsx +++ b/src/frontend/apps/impress/src/components/common/tree/TreeView.tsx @@ -8,6 +8,7 @@ import { NodeRendererProps, Tree, } from 'react-arborist'; +import { OpenMap } from 'react-arborist/dist/module/state/open-slice'; import { BaseType, @@ -27,6 +28,7 @@ export type TreeViewProps = { width?: number | string; selectedNodeId?: string; rootNodeId: string; + initialOpenState?: OpenMap; renderNode: ( props: NodeRendererProps>, ) => React.ReactNode; @@ -43,6 +45,7 @@ export const TreeView = ({ renderNode, afterMove, selectedNodeId, + initialOpenState, }: TreeViewProps) => { const onMove3 = (args: { dragIds: string[]; @@ -223,6 +226,7 @@ export const TreeView = ({ height={1000} indent={20} width={width} + initialOpenState={initialOpenState} selection={selectedNodeId} disableEdit={true} rowHeight={32} @@ -326,7 +330,11 @@ export const TreeViewNode = ({ ) : ( void handleClick()} + onClick={(e) => { + e.stopPropagation(); + e.preventDefault(); + void handleClick(); + }} $variation="500" $size="16px" iconName={ diff --git a/src/frontend/apps/impress/src/components/common/tree/treeStore.ts b/src/frontend/apps/impress/src/components/common/tree/treeStore.ts index 7ee5942c7..3cae8e86d 100644 --- a/src/frontend/apps/impress/src/components/common/tree/treeStore.ts +++ b/src/frontend/apps/impress/src/components/common/tree/treeStore.ts @@ -84,9 +84,11 @@ export const createTreeStore = ( return filteredNodes.map((node) => { if (node.children) { + const children = removeNodeFromTree(node.children); return { ...node, - children: removeNodeFromTree(node.children), + children: children, + childrenCount: children.length, }; } return node; diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/useDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/api/useDoc.tsx index ebbb1d543..f9cabe91f 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/api/useDoc.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/useDoc.tsx @@ -28,6 +28,7 @@ export function useDoc( return useQuery({ queryKey: [KEY_DOC, param], queryFn: () => getDoc(param), + ...queryConfig, }); } diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalRemoveDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalRemoveDoc.tsx index a3a134aaa..53115c9d2 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalRemoveDoc.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalRemoveDoc.tsx @@ -17,16 +17,20 @@ import { Doc } from '../types'; interface ModalRemoveDocProps { onClose: () => void; doc: Doc; + afterDelete?: (doc: Doc) => void; } -export const ModalRemoveDoc = ({ onClose, doc }: ModalRemoveDocProps) => { +export const ModalRemoveDoc = ({ + onClose, + doc, + afterDelete, +}: ModalRemoveDocProps) => { const { toast } = useToastProvider(); const { push } = useRouter(); const pathname = usePathname(); const { mutate: removeDoc, - isError, error, } = useRemoveDoc({ @@ -34,6 +38,11 @@ export const ModalRemoveDoc = ({ onClose, doc }: ModalRemoveDocProps) => { toast(t('The document has been deleted.'), VariantType.SUCCESS, { duration: 4000, }); + if (afterDelete) { + afterDelete(doc); + return; + } + if (pathname === '/') { onClose(); } else { 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 f99347ecb..75054f684 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 @@ -46,6 +46,7 @@ export interface Doc { created_at: string; updated_at: string; numchild: number; + children?: Doc[]; abilities: { accesses_manage: boolean; accesses_view: boolean; diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/api/useDocTree.tsx b/src/frontend/apps/impress/src/features/docs/doc-tree/api/useDocTree.tsx index e4a289a2d..8a0e86172 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-tree/api/useDocTree.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-tree/api/useDocTree.tsx @@ -47,6 +47,8 @@ export function useDocTree( return useQuery({ queryKey: [KEY_LIST_DOC_CHILDREN, params], queryFn: () => getDocTree(params), + staleTime: 0, + gcTime: 0, ...queryConfig, }); } 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 af5a3fe46..5712f6da6 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 @@ -1,4 +1,5 @@ -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; +import { OpenMap } from 'react-arborist/dist/module/state/open-slice'; import { css } from 'styled-components'; import { fetchAPI } from '@/api'; @@ -7,7 +8,7 @@ import { TreeView } from '@/components/common/tree/TreeView'; import { useTreeStore } from '@/components/common/tree/treeStore'; import { useCunninghamTheme } from '@/cunningham'; -import { Doc, useDoc } from '../../doc-management'; +import { Doc } from '../../doc-management'; import { SimpleDocItem } from '../../docs-grid'; import { useDocTree } from '../api/useDocTree'; import { TreeViewDataType, TreeViewMoveResult } from '../types/tree'; @@ -20,9 +21,14 @@ type Props = { export type DocTreeDataType = TreeViewDataType; export const DocTree = ({ docId }: Props) => { + const [rootNode, setRootNode] = useState(null); const { spacingsTokens } = useCunninghamTheme(); const spacing = spacingsTokens(); - const { data: rootNode } = useDoc({ id: docId }); + + const [initialOpenState, setInitialOpenState] = useState( + undefined, + ); + const { selectedNode, setSelectedNode, @@ -62,18 +68,46 @@ export const DocTree = ({ docId }: Props) => { return; } - console.log(data); - const newChildren = data ?? []; - const root = newChildren.shift(); - console.log('root', root); - const newData: DocTreeDataType[] = newChildren.map((child) => ({ - ...child, - childrenCount: child.numchild, - children: [], - parentId: docId, - })); - setTreeDataStore(newData); - }, [data, setTreeDataStore, docId]); + const initialOpenState: OpenMap = {}; + const root = data[0]; + + initialOpenState[root.id] = true; + + const serialize = ( + children: Doc[], + parentId: Doc['id'], + ): DocTreeDataType[] => { + if (children.length === 0) { + return []; + } + return children.map((child) => { + if (child?.children?.length && child?.children?.length > 0) { + initialOpenState[child.id] = true; + } + + if (docId === child.id) { + setSelectedNode(child); + } + + const node = { + ...child, + childrenCount: child.numchild, + children: serialize(child.children ?? [], child.id), + parentId: parentId, + }; + if (child?.children?.length && child?.children?.length > 0) { + initialOpenState[child.id] = true; + } + return node; + }); + }; + + root.children = serialize(root.children ?? [], docId); + console.log('initialOpenState', initialOpenState); + setInitialOpenState(initialOpenState); + setRootNode(root); + setTreeDataStore(root.children ?? []); + }, [data, setTreeDataStore, docId, setSelectedNode, rootNode]); const isRootNodeSelected = !selectedNode ? true @@ -114,16 +148,19 @@ export const DocTree = ({ docId }: Props) => { - } - afterMove={(result, newTreeData) => { - void afterMove(result, newTreeData); - }} - /> + {initialOpenState && treeDataStore.length > 0 && ( + } + afterMove={(result, newTreeData) => { + void afterMove(result, newTreeData); + }} + /> + )} ); diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItem.tsx b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItem.tsx index 5c98cfcb2..1fbf7c035 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItem.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItem.tsx @@ -25,11 +25,15 @@ type DocTreeItemProps = NodeRendererProps>; export const DocTreeItem = ({ node, ...props }: DocTreeItemProps) => { const data = node.data; + const { push } = useRouter(); + const deleteModal = useModal(); + const shareModal = useModal(); const renameModal = useModal(); const [isOpen, setIsOpen] = useState(false); - const { updateNode, setSelectedNode } = useTreeStore(); + const { updateNode, setSelectedNode, removeNode, refreshNode } = + useTreeStore(); const { spacingsTokens } = useCunninghamTheme(); const { refetch } = useDocChildren( { @@ -54,9 +58,16 @@ export const DocTreeItem = ({ node, ...props }: DocTreeItemProps) => { }) .catch(console.error); } else { + const newDoc = { + ...doc, + children: [], + childrenCount: 0, + parentId: node.id, + }; updateNode(node.id, { ...node.data, - children: [...actualChildren, doc], + children: [...actualChildren, newDoc], + childrenCount: actualChildren.length + 1, }); node.open(); router.push(`/docs/${doc.id}`); @@ -84,6 +95,16 @@ export const DocTreeItem = ({ node, ...props }: DocTreeItemProps) => { return newChilds; }; + const afterDelete = () => { + console.log('afterDelete', node.data); + if (node.data.parentId) { + router.push(`/docs/${node.data.parentId}`); + refreshNode(node.data.parentId); + setSelectedNode(node.data); + } + removeNode(node.data.id); + }; + const options = [ { label: t('Rename'), @@ -140,7 +161,11 @@ export const DocTreeItem = ({ node, ...props }: DocTreeItemProps) => { /> {deleteModal.isOpen && ( - + )} {shareModal.isOpen && (