@webiny/app-aco 0.0.0-unstable.9f53ea597d → 0.0.0-unstable.a4f3e4ea8b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -73,11 +73,19 @@ const List = ({ folders, folderActions, onFolderClick, focusedFolderId, hiddenFo
73
73
  const { dragSourceId, dropTargetId } = options;
74
74
  const folder = folders.find((f)=>f.id === dragSourceId);
75
75
  const targetFolder = folders.find((f)=>f.id === dropTargetId);
76
- if (!folder || !targetFolder) return;
77
- showConfirmMoveFolderDialog({
78
- folder,
79
- targetFolder,
80
- onAccept: runDrop
76
+ if (!folder || !targetFolder) throw new Error("Folder not found");
77
+ await new Promise((resolve, reject)=>{
78
+ showConfirmMoveFolderDialog({
79
+ folder,
80
+ targetFolder,
81
+ onAccept: async ()=>{
82
+ await runDrop();
83
+ resolve();
84
+ },
85
+ onClose: ()=>{
86
+ reject(new Error("cancelled"));
87
+ }
88
+ });
81
89
  });
82
90
  } else await runDrop();
83
91
  }, [
@@ -1 +1 @@
1
- {"version":3,"file":"components/FolderTree/List/List.js","sources":["../../../../src/components/FolderTree/List/List.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n Tree,\n type NodeDto,\n type TreeProps,\n type WithDefaultNodeData,\n type DropOptions\n} from \"@webiny/admin-ui\";\nimport { useSnackbar } from \"@webiny/app-admin\";\nimport type { FolderDto } from \"~/domain/folder/FolderDto.js\";\nimport { useGetFolderLevelPermission } from \"~/features/folders/getFolderLevelPermission/index.js\";\nimport { useListFoldersByParentIds } from \"~/features/folders/listFoldersByParentIds/index.js\";\nimport { useGetFolderAncestors } from \"~/features/folders/getFolderAncestors/index.js\";\nimport { useUpdateFolder } from \"~/features/folders/updateFolder/index.js\";\nimport { Node } from \"../Node/index.js\";\nimport { createInitialOpenList, createTreeData } from \"./utils.js\";\nimport { ROOT_FOLDER } from \"~/constants.js\";\nimport { FolderProvider } from \"~/contexts/folder.js\";\nimport { useConfirmMoveFolderDialog } from \"~/dialogs/index.js\";\nimport type { FolderActionConfig } from \"~/config/AcoConfig.js\";\n\ninterface ListProps {\n folders: FolderDto[];\n folderActions: FolderActionConfig[];\n dropConfirmation?: boolean;\n focusedFolderId?: string;\n hiddenFolderIds?: string[];\n enableActions?: boolean;\n onFolderClick: (data: FolderDto) => void;\n}\n\nexport const List = ({\n folders,\n folderActions,\n onFolderClick,\n focusedFolderId,\n hiddenFolderIds,\n enableActions,\n dropConfirmation = false\n}: ListProps) => {\n const { listFoldersByParentIds, loading } = useListFoldersByParentIds();\n const { updateFolder } = useUpdateFolder();\n const { getFolderLevelPermission: canManageStructure } =\n useGetFolderLevelPermission(\"canManageStructure\");\n const { getFolderAncestors } = useGetFolderAncestors();\n const { showSnackbar } = useSnackbar();\n\n const [treeData, setTreeData] = useState<NodeDto<FolderDto>[]>([]);\n const [openFolderIds, setOpenFolderIds] = useState<string[]>([ROOT_FOLDER]);\n const { showDialog: showConfirmMoveFolderDialog } = useConfirmMoveFolderDialog();\n\n useEffect(() => {\n setTreeData(createTreeData(folders, focusedFolderId, hiddenFolderIds));\n }, [folders, focusedFolderId, hiddenFolderIds]);\n\n useEffect(() => {\n setOpenFolderIds(prev => {\n const expanded = createInitialOpenList(folders, prev, focusedFolderId);\n return [...new Set([ROOT_FOLDER, ...expanded])];\n });\n }, [focusedFolderId, folders, setOpenFolderIds]);\n\n const handleChangeOpen: TreeProps[\"onChangeOpen\"] = async nodes => {\n const folderIds = nodes.map(node => node.id);\n const updatedOpenIds = [...new Set([ROOT_FOLDER, ...folderIds])];\n setOpenFolderIds(updatedOpenIds);\n\n const fetchableIds = folderIds.filter(id => id !== ROOT_FOLDER && id !== \"0\");\n await listFoldersByParentIds(fetchableIds);\n };\n\n const handleDrop: TreeProps[\"onDrop\"] = async (_, { dragSourceId, dropTargetId }) => {\n try {\n const item = folders.find(folder => folder.id === dragSourceId);\n if (!item) {\n throw new Error(\"Folder not found!\");\n }\n\n await updateFolder({\n ...item,\n parentId: dropTargetId !== ROOT_FOLDER ? (dropTargetId as string) : null\n });\n } catch (error) {\n showSnackbar(error.message);\n }\n };\n\n const onDrop = useCallback<NonNullable<TreeProps[\"onDrop\"]>>(\n async (newTree, options: DropOptions) => {\n // Function to execute the drop logic\n const runDrop = async () => handleDrop(newTree, options);\n\n // If drop confirmation is enabled, show dialog before proceeding\n if (dropConfirmation) {\n const { dragSourceId, dropTargetId } = options;\n const folder = folders.find(f => f.id === dragSourceId);\n const targetFolder = folders.find(f => f.id === dropTargetId);\n\n // Abort if either folder is not found\n if (!folder || !targetFolder) {\n return;\n }\n\n showConfirmMoveFolderDialog({\n folder,\n targetFolder,\n onAccept: runDrop\n });\n } else {\n // Otherwise, perform the drop immediately\n await runDrop();\n }\n },\n [folders, dropConfirmation, showConfirmMoveFolderDialog]\n );\n\n const sort = useMemo(\n () => (a: NodeDto<any>, b: NodeDto<any>) => {\n if (a.id === ROOT_FOLDER || b.id === ROOT_FOLDER) {\n return 1;\n }\n return a.label.localeCompare(b.label, undefined, { numeric: true });\n },\n []\n );\n\n const canDrag: TreeProps<FolderDto>[\"canDrag\"] = useCallback(\n (node: NodeDto<FolderDto>) => node.id !== ROOT_FOLDER && canManageStructure(node.id),\n [canManageStructure]\n );\n\n const canDrop: TreeProps<FolderDto>[\"canDrop\"] = (_, options: DropOptions<FolderDto>) => {\n const { dragSourceId, dropTargetId } = options;\n const dropTagetAncestorIds = getFolderAncestors(dropTargetId).map(item => item.id);\n\n // Prevent dropping a folder into itself or its descendants\n return !(dragSourceId && dropTagetAncestorIds.includes(dragSourceId));\n };\n\n const nodeRenderer: TreeProps<FolderDto>[\"renderer\"] = node => {\n const folder = folders.find(folder => folder.id === node.id);\n return (\n <FolderProvider folder={folder}>\n <Node enableActions={enableActions} folderActions={folderActions} />\n </FolderProvider>\n );\n };\n\n const handleNodeClick = useCallback(\n (node: WithDefaultNodeData<FolderDto>) => {\n onFolderClick(node);\n },\n [onFolderClick]\n );\n\n return (\n <Tree<FolderDto>\n nodes={treeData}\n rootId={\"0\"}\n defaultOpenNodeIds={openFolderIds}\n onChangeOpen={handleChangeOpen}\n onDrop={onDrop}\n onNodeClick={handleNodeClick}\n sort={sort}\n canDrag={canDrag}\n canDrop={canDrop}\n renderer={nodeRenderer}\n defaultLockedOpenNodeIds={[ROOT_FOLDER]}\n loadingNodeIds={loading}\n />\n );\n};\n"],"names":["List","folders","folderActions","onFolderClick","focusedFolderId","hiddenFolderIds","enableActions","dropConfirmation","listFoldersByParentIds","loading","useListFoldersByParentIds","updateFolder","useUpdateFolder","canManageStructure","useGetFolderLevelPermission","getFolderAncestors","useGetFolderAncestors","showSnackbar","useSnackbar","treeData","setTreeData","useState","openFolderIds","setOpenFolderIds","ROOT_FOLDER","showConfirmMoveFolderDialog","useConfirmMoveFolderDialog","useEffect","createTreeData","prev","expanded","createInitialOpenList","Set","handleChangeOpen","nodes","folderIds","node","updatedOpenIds","fetchableIds","id","handleDrop","_","dragSourceId","dropTargetId","item","folder","Error","error","onDrop","useCallback","newTree","options","runDrop","f","targetFolder","sort","useMemo","a","b","undefined","canDrag","canDrop","dropTagetAncestorIds","nodeRenderer","FolderProvider","Node","handleNodeClick","Tree"],"mappings":"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"}
1
+ {"version":3,"file":"components/FolderTree/List/List.js","sources":["../../../../src/components/FolderTree/List/List.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n Tree,\n type NodeDto,\n type TreeProps,\n type WithDefaultNodeData,\n type DropOptions\n} from \"@webiny/admin-ui\";\nimport { useSnackbar } from \"@webiny/app-admin\";\nimport type { FolderDto } from \"~/domain/folder/FolderDto.js\";\nimport { useGetFolderLevelPermission } from \"~/features/folders/getFolderLevelPermission/index.js\";\nimport { useListFoldersByParentIds } from \"~/features/folders/listFoldersByParentIds/index.js\";\nimport { useGetFolderAncestors } from \"~/features/folders/getFolderAncestors/index.js\";\nimport { useUpdateFolder } from \"~/features/folders/updateFolder/index.js\";\nimport { Node } from \"../Node/index.js\";\nimport { createInitialOpenList, createTreeData } from \"./utils.js\";\nimport { ROOT_FOLDER } from \"~/constants.js\";\nimport { FolderProvider } from \"~/contexts/folder.js\";\nimport { useConfirmMoveFolderDialog } from \"~/dialogs/index.js\";\nimport type { FolderActionConfig } from \"~/config/AcoConfig.js\";\n\ninterface ListProps {\n folders: FolderDto[];\n folderActions: FolderActionConfig[];\n dropConfirmation?: boolean;\n focusedFolderId?: string;\n hiddenFolderIds?: string[];\n enableActions?: boolean;\n onFolderClick: (data: FolderDto) => void;\n}\n\nexport const List = ({\n folders,\n folderActions,\n onFolderClick,\n focusedFolderId,\n hiddenFolderIds,\n enableActions,\n dropConfirmation = false\n}: ListProps) => {\n const { listFoldersByParentIds, loading } = useListFoldersByParentIds();\n const { updateFolder } = useUpdateFolder();\n const { getFolderLevelPermission: canManageStructure } =\n useGetFolderLevelPermission(\"canManageStructure\");\n const { getFolderAncestors } = useGetFolderAncestors();\n const { showSnackbar } = useSnackbar();\n\n const [treeData, setTreeData] = useState<NodeDto<FolderDto>[]>([]);\n const [openFolderIds, setOpenFolderIds] = useState<string[]>([ROOT_FOLDER]);\n const { showDialog: showConfirmMoveFolderDialog } = useConfirmMoveFolderDialog();\n\n useEffect(() => {\n setTreeData(createTreeData(folders, focusedFolderId, hiddenFolderIds));\n }, [folders, focusedFolderId, hiddenFolderIds]);\n\n useEffect(() => {\n setOpenFolderIds(prev => {\n const expanded = createInitialOpenList(folders, prev, focusedFolderId);\n return [...new Set([ROOT_FOLDER, ...expanded])];\n });\n }, [focusedFolderId, folders, setOpenFolderIds]);\n\n const handleChangeOpen: TreeProps[\"onChangeOpen\"] = async nodes => {\n const folderIds = nodes.map(node => node.id);\n const updatedOpenIds = [...new Set([ROOT_FOLDER, ...folderIds])];\n setOpenFolderIds(updatedOpenIds);\n\n const fetchableIds = folderIds.filter(id => id !== ROOT_FOLDER && id !== \"0\");\n await listFoldersByParentIds(fetchableIds);\n };\n\n const handleDrop: TreeProps[\"onDrop\"] = async (_, { dragSourceId, dropTargetId }) => {\n try {\n const item = folders.find(folder => folder.id === dragSourceId);\n if (!item) {\n throw new Error(\"Folder not found!\");\n }\n\n await updateFolder({\n ...item,\n parentId: dropTargetId !== ROOT_FOLDER ? (dropTargetId as string) : null\n });\n } catch (error) {\n showSnackbar(error.message);\n }\n };\n\n const onDrop = useCallback<NonNullable<TreeProps[\"onDrop\"]>>(\n async (newTree, options: DropOptions) => {\n // Function to execute the drop logic\n const runDrop = async () => handleDrop(newTree, options);\n\n // If drop confirmation is enabled, show dialog before proceeding\n if (dropConfirmation) {\n const { dragSourceId, dropTargetId } = options;\n const folder = folders.find(f => f.id === dragSourceId);\n const targetFolder = folders.find(f => f.id === dropTargetId);\n\n // Abort if either folder is not found\n if (!folder || !targetFolder) {\n throw new Error(\"Folder not found\");\n }\n\n await new Promise<void>((resolve, reject) => {\n showConfirmMoveFolderDialog({\n folder,\n targetFolder,\n onAccept: async () => {\n await runDrop();\n resolve();\n },\n onClose: () => {\n reject(new Error(\"cancelled\"));\n }\n });\n });\n } else {\n // Otherwise, perform the drop immediately\n await runDrop();\n }\n },\n [folders, dropConfirmation, showConfirmMoveFolderDialog]\n );\n\n const sort = useMemo(\n () => (a: NodeDto<any>, b: NodeDto<any>) => {\n if (a.id === ROOT_FOLDER || b.id === ROOT_FOLDER) {\n return 1;\n }\n return a.label.localeCompare(b.label, undefined, { numeric: true });\n },\n []\n );\n\n const canDrag: TreeProps<FolderDto>[\"canDrag\"] = useCallback(\n (node: NodeDto<FolderDto>) => node.id !== ROOT_FOLDER && canManageStructure(node.id),\n [canManageStructure]\n );\n\n const canDrop: TreeProps<FolderDto>[\"canDrop\"] = (_, options: DropOptions<FolderDto>) => {\n const { dragSourceId, dropTargetId } = options;\n const dropTagetAncestorIds = getFolderAncestors(dropTargetId).map(item => item.id);\n\n // Prevent dropping a folder into itself or its descendants\n return !(dragSourceId && dropTagetAncestorIds.includes(dragSourceId));\n };\n\n const nodeRenderer: TreeProps<FolderDto>[\"renderer\"] = node => {\n const folder = folders.find(folder => folder.id === node.id);\n return (\n <FolderProvider folder={folder}>\n <Node enableActions={enableActions} folderActions={folderActions} />\n </FolderProvider>\n );\n };\n\n const handleNodeClick = useCallback(\n (node: WithDefaultNodeData<FolderDto>) => {\n onFolderClick(node);\n },\n [onFolderClick]\n );\n\n return (\n <Tree<FolderDto>\n nodes={treeData}\n rootId={\"0\"}\n defaultOpenNodeIds={openFolderIds}\n onChangeOpen={handleChangeOpen}\n onDrop={onDrop}\n onNodeClick={handleNodeClick}\n sort={sort}\n canDrag={canDrag}\n canDrop={canDrop}\n renderer={nodeRenderer}\n defaultLockedOpenNodeIds={[ROOT_FOLDER]}\n loadingNodeIds={loading}\n />\n );\n};\n"],"names":["List","folders","folderActions","onFolderClick","focusedFolderId","hiddenFolderIds","enableActions","dropConfirmation","listFoldersByParentIds","loading","useListFoldersByParentIds","updateFolder","useUpdateFolder","canManageStructure","useGetFolderLevelPermission","getFolderAncestors","useGetFolderAncestors","showSnackbar","useSnackbar","treeData","setTreeData","useState","openFolderIds","setOpenFolderIds","ROOT_FOLDER","showConfirmMoveFolderDialog","useConfirmMoveFolderDialog","useEffect","createTreeData","prev","expanded","createInitialOpenList","Set","handleChangeOpen","nodes","folderIds","node","updatedOpenIds","fetchableIds","id","handleDrop","_","dragSourceId","dropTargetId","item","folder","Error","error","onDrop","useCallback","newTree","options","runDrop","f","targetFolder","Promise","resolve","reject","sort","useMemo","a","b","undefined","canDrag","canDrop","dropTagetAncestorIds","nodeRenderer","FolderProvider","Node","handleNodeClick","Tree"],"mappings":"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"}
@@ -3,6 +3,7 @@ interface ShowDialogParams {
3
3
  folder: FolderDto;
4
4
  targetFolder: FolderDto;
5
5
  onAccept: (folder: FolderDto, targetFolder: FolderDto) => Promise<void>;
6
+ onClose?: () => void;
6
7
  }
7
8
  interface UseConfirmMoveFolderDialogResponse {
8
9
  showDialog: (params: ShowDialogParams) => void;
@@ -1,14 +1,15 @@
1
1
  import { useDialogs } from "@webiny/app-admin";
2
2
  const useConfirmMoveFolderDialog = ()=>{
3
3
  const dialogs = useDialogs();
4
- const showDialog = ({ folder, targetFolder, onAccept })=>{
4
+ const showDialog = ({ folder, targetFolder, onAccept, onClose })=>{
5
5
  dialogs.showDialog({
6
6
  title: "Move folder",
7
7
  content: `You are about to move the folder "${folder.title}" into "${targetFolder.title}"! Are you sure you want to continue?`,
8
8
  acceptLabel: "Move folder",
9
9
  cancelLabel: "Cancel",
10
10
  loadingLabel: "Moving folder...",
11
- onAccept: ()=>onAccept(folder, targetFolder)
11
+ onAccept: ()=>onAccept(folder, targetFolder),
12
+ onClose
12
13
  });
13
14
  };
14
15
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"dialogs/useConfirmMoveFolderDialog.js","sources":["../../src/dialogs/useConfirmMoveFolderDialog.tsx"],"sourcesContent":["import { useDialogs } from \"@webiny/app-admin\";\nimport type { FolderDto } from \"~/domain/folder/FolderDto.js\";\n\ninterface ShowDialogParams {\n folder: FolderDto;\n targetFolder: FolderDto;\n onAccept: (folder: FolderDto, targetFolder: FolderDto) => Promise<void>;\n}\n\ninterface UseConfirmMoveFolderDialogResponse {\n showDialog: (params: ShowDialogParams) => void;\n}\n\nexport const useConfirmMoveFolderDialog = (): UseConfirmMoveFolderDialogResponse => {\n const dialogs = useDialogs();\n\n const showDialog = ({ folder, targetFolder, onAccept }: ShowDialogParams) => {\n dialogs.showDialog({\n title: \"Move folder\",\n content: `You are about to move the folder \"${folder.title}\" into \"${targetFolder.title}\"! Are you sure you want to continue?`,\n acceptLabel: \"Move folder\",\n cancelLabel: \"Cancel\",\n loadingLabel: \"Moving folder...\",\n onAccept: () => onAccept(folder, targetFolder)\n });\n };\n\n return {\n showDialog\n };\n};\n"],"names":["useConfirmMoveFolderDialog","dialogs","useDialogs","showDialog","folder","targetFolder","onAccept"],"mappings":";AAaO,MAAMA,6BAA6B;IACtC,MAAMC,UAAUC;IAEhB,MAAMC,aAAa,CAAC,EAAEC,MAAM,EAAEC,YAAY,EAAEC,QAAQ,EAAoB;QACpEL,QAAQ,UAAU,CAAC;YACf,OAAO;YACP,SAAS,CAAC,kCAAkC,EAAEG,OAAO,KAAK,CAAC,QAAQ,EAAEC,aAAa,KAAK,CAAC,qCAAqC,CAAC;YAC9H,aAAa;YACb,aAAa;YACb,cAAc;YACd,UAAU,IAAMC,SAASF,QAAQC;QACrC;IACJ;IAEA,OAAO;QACHF;IACJ;AACJ"}
1
+ {"version":3,"file":"dialogs/useConfirmMoveFolderDialog.js","sources":["../../src/dialogs/useConfirmMoveFolderDialog.tsx"],"sourcesContent":["import { useDialogs } from \"@webiny/app-admin\";\nimport type { FolderDto } from \"~/domain/folder/FolderDto.js\";\n\ninterface ShowDialogParams {\n folder: FolderDto;\n targetFolder: FolderDto;\n onAccept: (folder: FolderDto, targetFolder: FolderDto) => Promise<void>;\n onClose?: () => void;\n}\n\ninterface UseConfirmMoveFolderDialogResponse {\n showDialog: (params: ShowDialogParams) => void;\n}\n\nexport const useConfirmMoveFolderDialog = (): UseConfirmMoveFolderDialogResponse => {\n const dialogs = useDialogs();\n\n const showDialog = ({ folder, targetFolder, onAccept, onClose }: ShowDialogParams) => {\n dialogs.showDialog({\n title: \"Move folder\",\n content: `You are about to move the folder \"${folder.title}\" into \"${targetFolder.title}\"! Are you sure you want to continue?`,\n acceptLabel: \"Move folder\",\n cancelLabel: \"Cancel\",\n loadingLabel: \"Moving folder...\",\n onAccept: () => onAccept(folder, targetFolder),\n onClose\n });\n };\n\n return {\n showDialog\n };\n};\n"],"names":["useConfirmMoveFolderDialog","dialogs","useDialogs","showDialog","folder","targetFolder","onAccept","onClose"],"mappings":";AAcO,MAAMA,6BAA6B;IACtC,MAAMC,UAAUC;IAEhB,MAAMC,aAAa,CAAC,EAAEC,MAAM,EAAEC,YAAY,EAAEC,QAAQ,EAAEC,OAAO,EAAoB;QAC7EN,QAAQ,UAAU,CAAC;YACf,OAAO;YACP,SAAS,CAAC,kCAAkC,EAAEG,OAAO,KAAK,CAAC,QAAQ,EAAEC,aAAa,KAAK,CAAC,qCAAqC,CAAC;YAC9H,aAAa;YACb,aAAa;YACb,cAAc;YACd,UAAU,IAAMC,SAASF,QAAQC;YACjCE;QACJ;IACJ;IAEA,OAAO;QACHJ;IACJ;AACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webiny/app-aco",
3
- "version": "0.0.0-unstable.9f53ea597d",
3
+ "version": "0.0.0-unstable.a4f3e4ea8b",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -15,18 +15,18 @@
15
15
  "author": "Webiny Ltd.",
16
16
  "license": "MIT",
17
17
  "dependencies": {
18
- "@webiny/admin-ui": "0.0.0-unstable.9f53ea597d",
19
- "@webiny/app": "0.0.0-unstable.9f53ea597d",
20
- "@webiny/app-admin": "0.0.0-unstable.9f53ea597d",
21
- "@webiny/app-headless-cms-common": "0.0.0-unstable.9f53ea597d",
22
- "@webiny/app-utils": "0.0.0-unstable.9f53ea597d",
23
- "@webiny/feature": "0.0.0-unstable.9f53ea597d",
24
- "@webiny/form": "0.0.0-unstable.9f53ea597d",
25
- "@webiny/icons": "0.0.0-unstable.9f53ea597d",
26
- "@webiny/react-properties": "0.0.0-unstable.9f53ea597d",
27
- "@webiny/shared-aco": "0.0.0-unstable.9f53ea597d",
28
- "@webiny/utils": "0.0.0-unstable.9f53ea597d",
29
- "@webiny/validation": "0.0.0-unstable.9f53ea597d",
18
+ "@webiny/admin-ui": "0.0.0-unstable.a4f3e4ea8b",
19
+ "@webiny/app": "0.0.0-unstable.a4f3e4ea8b",
20
+ "@webiny/app-admin": "0.0.0-unstable.a4f3e4ea8b",
21
+ "@webiny/app-headless-cms-common": "0.0.0-unstable.a4f3e4ea8b",
22
+ "@webiny/app-utils": "0.0.0-unstable.a4f3e4ea8b",
23
+ "@webiny/feature": "0.0.0-unstable.a4f3e4ea8b",
24
+ "@webiny/form": "0.0.0-unstable.a4f3e4ea8b",
25
+ "@webiny/icons": "0.0.0-unstable.a4f3e4ea8b",
26
+ "@webiny/react-properties": "0.0.0-unstable.a4f3e4ea8b",
27
+ "@webiny/shared-aco": "0.0.0-unstable.a4f3e4ea8b",
28
+ "@webiny/utils": "0.0.0-unstable.a4f3e4ea8b",
29
+ "@webiny/validation": "0.0.0-unstable.a4f3e4ea8b",
30
30
  "lodash": "4.18.1",
31
31
  "mobx": "6.16.1",
32
32
  "mobx-react-lite": "4.1.1",
@@ -37,9 +37,9 @@
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/react": "18.3.31",
40
- "@webiny/build-tools": "0.0.0-unstable.9f53ea597d",
40
+ "@webiny/build-tools": "0.0.0-unstable.a4f3e4ea8b",
41
41
  "@webiny/di": "1.0.2",
42
- "@webiny/wcp": "0.0.0-unstable.9f53ea597d",
42
+ "@webiny/wcp": "0.0.0-unstable.a4f3e4ea8b",
43
43
  "rimraf": "6.1.3",
44
44
  "typescript": "7.0.2",
45
45
  "vitest": "4.1.10"
@@ -138,10 +138,18 @@ const FolderTree = ({ vm, actions, folderActions = [], enableCreate, enableActio
138
138
  const folder = localFolders.find((f)=>f.id === dragSourceId);
139
139
  const targetFolder = localFolders.find((f)=>f.id === dropTargetId);
140
140
  if (!folder || !targetFolder) return;
141
- showConfirmMoveFolderDialog({
142
- folder,
143
- targetFolder,
144
- onAccept: runDrop
141
+ await new Promise((resolve, reject)=>{
142
+ showConfirmMoveFolderDialog({
143
+ folder,
144
+ targetFolder,
145
+ onAccept: async ()=>{
146
+ await runDrop();
147
+ resolve();
148
+ },
149
+ onClose: ()=>{
150
+ reject(new Error("cancelled"));
151
+ }
152
+ });
145
153
  });
146
154
  } else await runDrop();
147
155
  }, [
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/folderTree/FolderTree.js","sources":["../../../src/presentation/folderTree/FolderTree.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n Tree,\n Tooltip,\n Dialog,\n Text,\n OverlayLoader,\n type NodeDto,\n type TreeProps,\n type WithDefaultNodeData,\n type DropOptions\n} from \"@webiny/admin-ui\";\nimport { useFeature } from \"@webiny/app\";\nimport { observer } from \"mobx-react-lite\";\nimport { useSnackbar, FormView } from \"@webiny/app-admin\";\nimport { i18n } from \"@webiny/app/i18n/index.js\";\nimport { ROOT_FOLDER } from \"~/constants.js\";\nimport { FolderProvider } from \"~/contexts/folder.js\";\nimport { Node } from \"~/components/FolderTree/Node/Node.js\";\nimport { Loader } from \"~/components/FolderTree/Loader/Loader.js\";\nimport { ButtonCreate } from \"~/components/FolderTree/ButtonCreate/ButtonCreate.js\";\nimport { useConfirmMoveFolderDialog } from \"~/dialogs/index.js\";\nimport { createTreeData, createInitialOpenList } from \"~/components/FolderTree/List/utils.js\";\nimport { FolderTreePresenterFeature } from \"./feature.js\";\nimport type { FolderDto } from \"~/domain/folder/FolderDto.js\";\nimport type { FolderActionConfig } from \"~/config/AcoConfig.js\";\nimport type {\n IFolderTreeCallbacks,\n IFolderTreeViewModel,\n IFolderOperationState\n} from \"./abstractions.js\";\n\nconst t = i18n.ns(\"app-aco/presentation/folder-tree\");\n\nconst FolderOperationDialog = observer(function FolderOperationDialog({\n operation,\n actions\n}: {\n operation: IFolderOperationState;\n actions: IFolderTreeCallbacks;\n}) {\n const [loading, setLoading] = useState(false);\n\n if (!operation.active) {\n return null;\n }\n\n const handleClose = () => {\n if (!loading) {\n actions.cancelOperation();\n }\n };\n\n const withLoading = async (fn: () => Promise<unknown>) => {\n setLoading(true);\n try {\n await fn();\n } finally {\n setLoading(false);\n }\n };\n\n if (operation.mode === \"delete\") {\n return (\n <Dialog\n open={true}\n onClose={handleClose}\n title={t`Delete Folder`}\n actions={\n <>\n <Dialog.CancelAction onClick={handleClose} text={t`Cancel`} />\n <Dialog.ConfirmAction\n onClick={() => {\n if (operation.folderId) {\n void withLoading(() =>\n actions.deleteFolder(operation.folderId!)\n );\n }\n }}\n text={t`Delete`}\n />\n </>\n }\n >\n {loading && <OverlayLoader text={t`Deleting folder...`} />}\n <Text size={\"md\"}>\n {t`Are you sure you want to delete this folder? This action cannot be undone.`}\n </Text>\n </Dialog>\n );\n }\n\n if ((operation.mode === \"create\" || operation.mode === \"edit\") && operation.form) {\n const title = operation.mode === \"create\" ? t`Create Folder` : t`Edit Folder`;\n const confirmLabel = operation.mode === \"create\" ? t`Create` : t`Save`;\n const loadingLabel =\n operation.mode === \"create\" ? t`Creating folder...` : t`Saving folder...`;\n\n return (\n <Dialog\n open={true}\n onClose={handleClose}\n title={title}\n actions={\n <>\n <Dialog.CancelAction onClick={handleClose} text={t`Cancel`} />\n <Dialog.ConfirmAction\n onClick={() => {\n void withLoading(() => actions.submitOperation());\n }}\n text={confirmLabel}\n />\n </>\n }\n >\n {loading && <OverlayLoader text={loadingLabel} />}\n <FormView name={\"Folder Tree\"} form={operation.form.vm} />\n </Dialog>\n );\n }\n\n return null;\n});\n\nexport interface PresenterFolderTreeProps {\n vm: IFolderTreeViewModel;\n actions: IFolderTreeCallbacks;\n folderActions?: FolderActionConfig[];\n enableCreate?: boolean;\n enableActions?: boolean;\n dropConfirmation?: boolean;\n rootFolderLabel?: string;\n hiddenFolderIds?: string[];\n}\n\nexport interface UncontrolledFolderTreeProps extends Omit<\n PresenterFolderTreeProps,\n \"vm\" | \"actions\"\n> {\n focusedFolderId?: string | null;\n onFolderClick?: (folderId: string | null) => void;\n}\n\nexport const FolderTree = ({\n vm,\n actions,\n folderActions = [],\n enableCreate,\n enableActions,\n dropConfirmation = false,\n rootFolderLabel,\n hiddenFolderIds\n}: PresenterFolderTreeProps) => {\n const { showSnackbar } = useSnackbar();\n const { showDialog: showConfirmMoveFolderDialog } = useConfirmMoveFolderDialog();\n\n const focusedFolderId = vm.currentFolderId ?? ROOT_FOLDER;\n\n const folders = vm.folders;\n\n const localFolders = useMemo(() => {\n if (!folders.length) {\n return [];\n }\n return folders.map(item =>\n item.id === ROOT_FOLDER && rootFolderLabel ? { ...item, title: rootFolderLabel } : item\n );\n }, [folders, rootFolderLabel]);\n\n const [treeData, setTreeData] = useState<NodeDto<FolderDto>[]>([]);\n const [openFolderIds, setOpenFolderIds] = useState<string[]>([ROOT_FOLDER]);\n\n useEffect(() => {\n setTreeData(createTreeData(localFolders, focusedFolderId, hiddenFolderIds));\n }, [localFolders, focusedFolderId, hiddenFolderIds]);\n\n useEffect(() => {\n setOpenFolderIds(prev => {\n const expanded = createInitialOpenList(localFolders, prev, focusedFolderId);\n return [...new Set([ROOT_FOLDER, ...expanded])];\n });\n }, [focusedFolderId, localFolders]);\n\n const handleChangeOpen: TreeProps[\"onChangeOpen\"] = async nodes => {\n const folderIds = nodes.map(node => node.id);\n const updatedOpenIds = [...new Set([ROOT_FOLDER, ...folderIds])];\n const newlyOpened = updatedOpenIds.filter(id => !openFolderIds.includes(id));\n setOpenFolderIds(updatedOpenIds);\n\n if (newlyOpened.length > 0) {\n await actions.loadChildFolders(newlyOpened);\n }\n };\n\n const handleDrop: TreeProps[\"onDrop\"] = async (_, { dragSourceId, dropTargetId }) => {\n try {\n const targetParentId = dropTargetId !== ROOT_FOLDER ? (dropTargetId as string) : null;\n await actions.moveFolder(dragSourceId as string, targetParentId);\n } catch (error) {\n showSnackbar((error as Error).message);\n }\n };\n\n const onDrop = useCallback<NonNullable<TreeProps[\"onDrop\"]>>(\n async (newTree, options: DropOptions) => {\n const runDrop = async () => handleDrop(newTree, options);\n\n if (dropConfirmation) {\n const { dragSourceId, dropTargetId } = options;\n const folder = localFolders.find(f => f.id === dragSourceId);\n const targetFolder = localFolders.find(f => f.id === dropTargetId);\n\n if (!folder || !targetFolder) {\n return;\n }\n\n showConfirmMoveFolderDialog({\n folder,\n targetFolder,\n onAccept: runDrop\n });\n } else {\n await runDrop();\n }\n },\n [localFolders, dropConfirmation, showConfirmMoveFolderDialog]\n );\n\n const sort = useMemo(\n () => (a: NodeDto<any>, b: NodeDto<any>) => {\n if (a.id === ROOT_FOLDER || b.id === ROOT_FOLDER) {\n return 1;\n }\n return a.label.localeCompare(b.label, undefined, { numeric: true });\n },\n []\n );\n\n const canDrag: TreeProps<FolderDto>[\"canDrag\"] = useCallback(\n (node: NodeDto<FolderDto>) =>\n node.id !== ROOT_FOLDER && actions.canManageStructure(node.id),\n [actions]\n );\n\n const canDrop: TreeProps<FolderDto>[\"canDrop\"] = (_, options: DropOptions<FolderDto>) => {\n const { dragSourceId, dropTargetId } = options;\n const ancestorIds = actions.getAncestorIds(dropTargetId as string);\n return !(dragSourceId && ancestorIds.includes(dragSourceId as string));\n };\n\n const nodeRenderer: TreeProps<FolderDto>[\"renderer\"] = node => {\n const folder = localFolders.find(f => f.id === node.id);\n return (\n <FolderProvider folder={folder}>\n <Node enableActions={enableActions} folderActions={folderActions} />\n </FolderProvider>\n );\n };\n\n const handleNodeClick = useCallback(\n (node: WithDefaultNodeData<FolderDto>) => {\n actions.selectFolder(node.id === ROOT_FOLDER ? null : node.id);\n },\n [actions]\n );\n\n const createButton = useMemo(() => {\n if (!enableCreate) {\n return null;\n }\n\n const canCreate = actions.canManageStructure(focusedFolderId);\n const button = (\n <ButtonCreate\n disabled={!canCreate}\n onCreateFolder={() =>\n actions.createFolder(\n focusedFolderId === ROOT_FOLDER ? undefined : focusedFolderId\n )\n }\n />\n );\n\n return canCreate ? (\n button\n ) : (\n <Tooltip\n content={`Cannot create folder because you're not an owner.`}\n trigger={button}\n />\n );\n }, [enableCreate, actions, focusedFolderId, localFolders]);\n\n if (vm.loading) {\n return <Loader />;\n }\n\n return (\n <div className=\"my-xs\">\n <Tree<FolderDto>\n nodes={treeData}\n rootId={\"0\"}\n defaultOpenNodeIds={openFolderIds}\n onChangeOpen={handleChangeOpen}\n onDrop={onDrop}\n onNodeClick={handleNodeClick}\n sort={sort}\n canDrag={canDrag}\n canDrop={canDrop}\n renderer={nodeRenderer}\n defaultLockedOpenNodeIds={[ROOT_FOLDER]}\n loadingNodeIds={vm.loadingNodeIds}\n />\n {enableCreate && (\n <div className={\"m-xs-plus mt-sm-plus mb-lg pl-sm-extra\"}>{createButton}</div>\n )}\n <FolderOperationDialog operation={vm.operation} actions={actions} />\n </div>\n );\n};\n\nexport const UncontrolledFolderTree = ({\n focusedFolderId,\n onFolderClick,\n ...props\n}: UncontrolledFolderTreeProps) => {\n const { presenter } = useFeature(FolderTreePresenterFeature);\n\n const vm = useMemo(() => {\n if (focusedFolderId === undefined) {\n return presenter.vm;\n }\n return { ...presenter.vm, currentFolderId: focusedFolderId };\n }, [presenter.vm, focusedFolderId]);\n\n const actions = useMemo(() => {\n if (!onFolderClick) {\n return presenter;\n }\n return { ...presenter, selectFolder: onFolderClick };\n }, [presenter, onFolderClick]);\n\n return <FolderTree vm={vm} actions={actions} {...props} />;\n};\n"],"names":["t","i18n","FolderOperationDialog","observer","operation","actions","loading","setLoading","useState","handleClose","withLoading","fn","Dialog","OverlayLoader","Text","title","confirmLabel","loadingLabel","FormView","FolderTree","vm","folderActions","enableCreate","enableActions","dropConfirmation","rootFolderLabel","hiddenFolderIds","showSnackbar","useSnackbar","showConfirmMoveFolderDialog","useConfirmMoveFolderDialog","focusedFolderId","ROOT_FOLDER","folders","localFolders","useMemo","item","treeData","setTreeData","openFolderIds","setOpenFolderIds","useEffect","createTreeData","prev","expanded","createInitialOpenList","Set","handleChangeOpen","nodes","folderIds","node","updatedOpenIds","newlyOpened","id","handleDrop","_","dragSourceId","dropTargetId","targetParentId","error","onDrop","useCallback","newTree","options","runDrop","folder","f","targetFolder","sort","a","b","undefined","canDrag","canDrop","ancestorIds","nodeRenderer","FolderProvider","Node","handleNodeClick","createButton","canCreate","button","ButtonCreate","Tooltip","Loader","Tree","UncontrolledFolderTree","onFolderClick","props","presenter","useFeature","FolderTreePresenterFeature"],"mappings":"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"}
1
+ {"version":3,"file":"presentation/folderTree/FolderTree.js","sources":["../../../src/presentation/folderTree/FolderTree.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n Tree,\n Tooltip,\n Dialog,\n Text,\n OverlayLoader,\n type NodeDto,\n type TreeProps,\n type WithDefaultNodeData,\n type DropOptions\n} from \"@webiny/admin-ui\";\nimport { useFeature } from \"@webiny/app\";\nimport { observer } from \"mobx-react-lite\";\nimport { useSnackbar, FormView } from \"@webiny/app-admin\";\nimport { i18n } from \"@webiny/app/i18n/index.js\";\nimport { ROOT_FOLDER } from \"~/constants.js\";\nimport { FolderProvider } from \"~/contexts/folder.js\";\nimport { Node } from \"~/components/FolderTree/Node/Node.js\";\nimport { Loader } from \"~/components/FolderTree/Loader/Loader.js\";\nimport { ButtonCreate } from \"~/components/FolderTree/ButtonCreate/ButtonCreate.js\";\nimport { useConfirmMoveFolderDialog } from \"~/dialogs/index.js\";\nimport { createTreeData, createInitialOpenList } from \"~/components/FolderTree/List/utils.js\";\nimport { FolderTreePresenterFeature } from \"./feature.js\";\nimport type { FolderDto } from \"~/domain/folder/FolderDto.js\";\nimport type { FolderActionConfig } from \"~/config/AcoConfig.js\";\nimport type {\n IFolderTreeCallbacks,\n IFolderTreeViewModel,\n IFolderOperationState\n} from \"./abstractions.js\";\n\nconst t = i18n.ns(\"app-aco/presentation/folder-tree\");\n\nconst FolderOperationDialog = observer(function FolderOperationDialog({\n operation,\n actions\n}: {\n operation: IFolderOperationState;\n actions: IFolderTreeCallbacks;\n}) {\n const [loading, setLoading] = useState(false);\n\n if (!operation.active) {\n return null;\n }\n\n const handleClose = () => {\n if (!loading) {\n actions.cancelOperation();\n }\n };\n\n const withLoading = async (fn: () => Promise<unknown>) => {\n setLoading(true);\n try {\n await fn();\n } finally {\n setLoading(false);\n }\n };\n\n if (operation.mode === \"delete\") {\n return (\n <Dialog\n open={true}\n onClose={handleClose}\n title={t`Delete Folder`}\n actions={\n <>\n <Dialog.CancelAction onClick={handleClose} text={t`Cancel`} />\n <Dialog.ConfirmAction\n onClick={() => {\n if (operation.folderId) {\n void withLoading(() =>\n actions.deleteFolder(operation.folderId!)\n );\n }\n }}\n text={t`Delete`}\n />\n </>\n }\n >\n {loading && <OverlayLoader text={t`Deleting folder...`} />}\n <Text size={\"md\"}>\n {t`Are you sure you want to delete this folder? This action cannot be undone.`}\n </Text>\n </Dialog>\n );\n }\n\n if ((operation.mode === \"create\" || operation.mode === \"edit\") && operation.form) {\n const title = operation.mode === \"create\" ? t`Create Folder` : t`Edit Folder`;\n const confirmLabel = operation.mode === \"create\" ? t`Create` : t`Save`;\n const loadingLabel =\n operation.mode === \"create\" ? t`Creating folder...` : t`Saving folder...`;\n\n return (\n <Dialog\n open={true}\n onClose={handleClose}\n title={title}\n actions={\n <>\n <Dialog.CancelAction onClick={handleClose} text={t`Cancel`} />\n <Dialog.ConfirmAction\n onClick={() => {\n void withLoading(() => actions.submitOperation());\n }}\n text={confirmLabel}\n />\n </>\n }\n >\n {loading && <OverlayLoader text={loadingLabel} />}\n <FormView name={\"Folder Tree\"} form={operation.form.vm} />\n </Dialog>\n );\n }\n\n return null;\n});\n\nexport interface PresenterFolderTreeProps {\n vm: IFolderTreeViewModel;\n actions: IFolderTreeCallbacks;\n folderActions?: FolderActionConfig[];\n enableCreate?: boolean;\n enableActions?: boolean;\n dropConfirmation?: boolean;\n rootFolderLabel?: string;\n hiddenFolderIds?: string[];\n}\n\nexport interface UncontrolledFolderTreeProps extends Omit<\n PresenterFolderTreeProps,\n \"vm\" | \"actions\"\n> {\n focusedFolderId?: string | null;\n onFolderClick?: (folderId: string | null) => void;\n}\n\nexport const FolderTree = ({\n vm,\n actions,\n folderActions = [],\n enableCreate,\n enableActions,\n dropConfirmation = false,\n rootFolderLabel,\n hiddenFolderIds\n}: PresenterFolderTreeProps) => {\n const { showSnackbar } = useSnackbar();\n const { showDialog: showConfirmMoveFolderDialog } = useConfirmMoveFolderDialog();\n\n const focusedFolderId = vm.currentFolderId ?? ROOT_FOLDER;\n\n const folders = vm.folders;\n\n const localFolders = useMemo(() => {\n if (!folders.length) {\n return [];\n }\n return folders.map(item =>\n item.id === ROOT_FOLDER && rootFolderLabel ? { ...item, title: rootFolderLabel } : item\n );\n }, [folders, rootFolderLabel]);\n\n const [treeData, setTreeData] = useState<NodeDto<FolderDto>[]>([]);\n const [openFolderIds, setOpenFolderIds] = useState<string[]>([ROOT_FOLDER]);\n\n useEffect(() => {\n setTreeData(createTreeData(localFolders, focusedFolderId, hiddenFolderIds));\n }, [localFolders, focusedFolderId, hiddenFolderIds]);\n\n useEffect(() => {\n setOpenFolderIds(prev => {\n const expanded = createInitialOpenList(localFolders, prev, focusedFolderId);\n return [...new Set([ROOT_FOLDER, ...expanded])];\n });\n }, [focusedFolderId, localFolders]);\n\n const handleChangeOpen: TreeProps[\"onChangeOpen\"] = async nodes => {\n const folderIds = nodes.map(node => node.id);\n const updatedOpenIds = [...new Set([ROOT_FOLDER, ...folderIds])];\n const newlyOpened = updatedOpenIds.filter(id => !openFolderIds.includes(id));\n setOpenFolderIds(updatedOpenIds);\n\n if (newlyOpened.length > 0) {\n await actions.loadChildFolders(newlyOpened);\n }\n };\n\n const handleDrop: TreeProps[\"onDrop\"] = async (_, { dragSourceId, dropTargetId }) => {\n try {\n const targetParentId = dropTargetId !== ROOT_FOLDER ? (dropTargetId as string) : null;\n await actions.moveFolder(dragSourceId as string, targetParentId);\n } catch (error) {\n showSnackbar((error as Error).message);\n }\n };\n\n const onDrop = useCallback<NonNullable<TreeProps[\"onDrop\"]>>(\n async (newTree, options: DropOptions) => {\n const runDrop = async () => handleDrop(newTree, options);\n\n if (dropConfirmation) {\n const { dragSourceId, dropTargetId } = options;\n const folder = localFolders.find(f => f.id === dragSourceId);\n const targetFolder = localFolders.find(f => f.id === dropTargetId);\n\n if (!folder || !targetFolder) {\n return;\n }\n\n await new Promise<void>((resolve, reject) => {\n showConfirmMoveFolderDialog({\n folder,\n targetFolder,\n onAccept: async () => {\n await runDrop();\n resolve();\n },\n onClose: () => {\n reject(new Error(\"cancelled\"));\n }\n });\n });\n } else {\n await runDrop();\n }\n },\n [localFolders, dropConfirmation, showConfirmMoveFolderDialog]\n );\n\n const sort = useMemo(\n () => (a: NodeDto<any>, b: NodeDto<any>) => {\n if (a.id === ROOT_FOLDER || b.id === ROOT_FOLDER) {\n return 1;\n }\n return a.label.localeCompare(b.label, undefined, { numeric: true });\n },\n []\n );\n\n const canDrag: TreeProps<FolderDto>[\"canDrag\"] = useCallback(\n (node: NodeDto<FolderDto>) =>\n node.id !== ROOT_FOLDER && actions.canManageStructure(node.id),\n [actions]\n );\n\n const canDrop: TreeProps<FolderDto>[\"canDrop\"] = (_, options: DropOptions<FolderDto>) => {\n const { dragSourceId, dropTargetId } = options;\n const ancestorIds = actions.getAncestorIds(dropTargetId as string);\n return !(dragSourceId && ancestorIds.includes(dragSourceId as string));\n };\n\n const nodeRenderer: TreeProps<FolderDto>[\"renderer\"] = node => {\n const folder = localFolders.find(f => f.id === node.id);\n return (\n <FolderProvider folder={folder}>\n <Node enableActions={enableActions} folderActions={folderActions} />\n </FolderProvider>\n );\n };\n\n const handleNodeClick = useCallback(\n (node: WithDefaultNodeData<FolderDto>) => {\n actions.selectFolder(node.id === ROOT_FOLDER ? null : node.id);\n },\n [actions]\n );\n\n const createButton = useMemo(() => {\n if (!enableCreate) {\n return null;\n }\n\n const canCreate = actions.canManageStructure(focusedFolderId);\n const button = (\n <ButtonCreate\n disabled={!canCreate}\n onCreateFolder={() =>\n actions.createFolder(\n focusedFolderId === ROOT_FOLDER ? undefined : focusedFolderId\n )\n }\n />\n );\n\n return canCreate ? (\n button\n ) : (\n <Tooltip\n content={`Cannot create folder because you're not an owner.`}\n trigger={button}\n />\n );\n }, [enableCreate, actions, focusedFolderId, localFolders]);\n\n if (vm.loading) {\n return <Loader />;\n }\n\n return (\n <div className=\"my-xs\">\n <Tree<FolderDto>\n nodes={treeData}\n rootId={\"0\"}\n defaultOpenNodeIds={openFolderIds}\n onChangeOpen={handleChangeOpen}\n onDrop={onDrop}\n onNodeClick={handleNodeClick}\n sort={sort}\n canDrag={canDrag}\n canDrop={canDrop}\n renderer={nodeRenderer}\n defaultLockedOpenNodeIds={[ROOT_FOLDER]}\n loadingNodeIds={vm.loadingNodeIds}\n />\n {enableCreate && (\n <div className={\"m-xs-plus mt-sm-plus mb-lg pl-sm-extra\"}>{createButton}</div>\n )}\n <FolderOperationDialog operation={vm.operation} actions={actions} />\n </div>\n );\n};\n\nexport const UncontrolledFolderTree = ({\n focusedFolderId,\n onFolderClick,\n ...props\n}: UncontrolledFolderTreeProps) => {\n const { presenter } = useFeature(FolderTreePresenterFeature);\n\n const vm = useMemo(() => {\n if (focusedFolderId === undefined) {\n return presenter.vm;\n }\n return { ...presenter.vm, currentFolderId: focusedFolderId };\n }, [presenter.vm, focusedFolderId]);\n\n const actions = useMemo(() => {\n if (!onFolderClick) {\n return presenter;\n }\n return { ...presenter, selectFolder: onFolderClick };\n }, [presenter, onFolderClick]);\n\n return <FolderTree vm={vm} actions={actions} {...props} />;\n};\n"],"names":["t","i18n","FolderOperationDialog","observer","operation","actions","loading","setLoading","useState","handleClose","withLoading","fn","Dialog","OverlayLoader","Text","title","confirmLabel","loadingLabel","FormView","FolderTree","vm","folderActions","enableCreate","enableActions","dropConfirmation","rootFolderLabel","hiddenFolderIds","showSnackbar","useSnackbar","showConfirmMoveFolderDialog","useConfirmMoveFolderDialog","focusedFolderId","ROOT_FOLDER","folders","localFolders","useMemo","item","treeData","setTreeData","openFolderIds","setOpenFolderIds","useEffect","createTreeData","prev","expanded","createInitialOpenList","Set","handleChangeOpen","nodes","folderIds","node","updatedOpenIds","newlyOpened","id","handleDrop","_","dragSourceId","dropTargetId","targetParentId","error","onDrop","useCallback","newTree","options","runDrop","folder","f","targetFolder","Promise","resolve","reject","Error","sort","a","b","undefined","canDrag","canDrop","ancestorIds","nodeRenderer","FolderProvider","Node","handleNodeClick","createButton","canCreate","button","ButtonCreate","Tooltip","Loader","Tree","UncontrolledFolderTree","onFolderClick","props","presenter","useFeature","FolderTreePresenterFeature"],"mappings":"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"}
@@ -26,6 +26,7 @@ declare class FolderTreePresenterImpl implements Abstraction.Interface {
26
26
  private operation;
27
27
  private callbacks;
28
28
  private disposed;
29
+ private previousFolders;
29
30
  constructor(foldersContext: FoldersContext.Interface, foldersCache: FoldersCache.Interface, listFoldersUseCase: ListFoldersUseCase.Interface, listFoldersByParentIdsUseCase: ListFoldersByParentIdsUseCase.Interface, createFolderUseCase: CreateFolderUseCase.Interface, updateFolderUseCase: UpdateFolderUseCase.Interface, deleteFolderUseCase: DeleteFolderUseCase.Interface, getFolderAncestorsUseCase: GetFolderAncestorsUseCase.Interface, getFolderLevelPermissionUseCase: GetFolderLevelPermissionUseCase.Interface, formModelFactory: FormModelFactory.Interface);
30
31
  get vm(): IFolderTreeViewModel;
31
32
  selectFolder(folderId: string | null): void;
@@ -40,6 +41,7 @@ declare class FolderTreePresenterImpl implements Abstraction.Interface {
40
41
  deleteFolder(folderId: string): Promise<void>;
41
42
  cancelOperation(): void;
42
43
  private loadFolders;
44
+ private resolveValidFolderId;
43
45
  private buildTree;
44
46
  private buildTreeFromFolders;
45
47
  private findFolder;
@@ -33,14 +33,32 @@ class FolderTreePresenterImpl {
33
33
  };
34
34
  this.callbacks = new Set();
35
35
  this.disposed = false;
36
+ this.previousFolders = [];
36
37
  makeAutoObservable(this, {
37
- callbacks: false
38
+ callbacks: false,
39
+ previousFolders: false
38
40
  }, {
39
41
  autoBind: true
40
42
  });
41
43
  reaction(()=>this.currentFolderId, (folderId)=>{
42
44
  if (!this.disposed) this.callbacks.forEach((cb)=>cb(folderId));
43
45
  });
46
+ reaction(()=>({
47
+ folders: this.foldersCache.getItems(),
48
+ currentFolderId: this.currentFolderId
49
+ }), ({ folders, currentFolderId })=>{
50
+ if (null !== currentFolderId) {
51
+ const stillExists = folders.some((f)=>f.id === currentFolderId);
52
+ const isTransientReloadClear = 0 === folders.length && this.loading;
53
+ if (!stillExists && !isTransientReloadClear) {
54
+ const resolvedId = this.resolveValidFolderId(currentFolderId, this.previousFolders);
55
+ this.selectFolder(resolvedId);
56
+ }
57
+ }
58
+ this.previousFolders = folders;
59
+ }, {
60
+ fireImmediately: true
61
+ });
44
62
  this.loadFolders();
45
63
  }
46
64
  get vm() {
@@ -209,22 +227,18 @@ class FolderTreePresenterImpl {
209
227
  return true;
210
228
  }
211
229
  async deleteFolder(folderId) {
212
- const folders = this.foldersCache.getItems();
213
- const folder = folders.find((f)=>f.id === folderId);
214
- const parentId = folder?.parentId ?? null;
215
230
  this.operation = {
216
231
  active: true,
217
232
  mode: "delete",
218
233
  folderId
219
234
  };
220
235
  await this.deleteFolderUseCase.execute(folderId);
221
- await this.loadFolders();
222
236
  runInAction(()=>{
237
+ this.foldersCache.removeItems((f)=>f.id === folderId);
223
238
  this.operation = {
224
239
  active: false,
225
240
  mode: null
226
241
  };
227
- this.currentFolderId = parentId;
228
242
  });
229
243
  }
230
244
  cancelOperation() {
@@ -245,6 +259,18 @@ class FolderTreePresenterImpl {
245
259
  });
246
260
  }
247
261
  }
262
+ resolveValidFolderId(folderId, previousFolders) {
263
+ let candidateId = folderId;
264
+ const visited = new Set();
265
+ while(null !== candidateId){
266
+ if (visited.has(candidateId)) break;
267
+ visited.add(candidateId);
268
+ if (this.foldersCache.getItem((f)=>f.id === candidateId)) return candidateId;
269
+ const prev = previousFolders.find((f)=>f.id === candidateId);
270
+ candidateId = prev?.parentId ?? null;
271
+ }
272
+ return null;
273
+ }
248
274
  buildTree() {
249
275
  const folders = this.foldersCache.getItems();
250
276
  return this.buildTreeFromFolders(folders);
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/folderTree/FolderTreePresenter.js","sources":["../../../src/presentation/folderTree/FolderTreePresenter.ts"],"sourcesContent":["import { makeAutoObservable, reaction, runInAction } from \"mobx\";\nimport slugify from \"slugify\";\nimport {\n FolderTreePresenter as Abstraction,\n type IFolderTreeNode,\n type IFolderTreeViewModel,\n type IFolderOperationState\n} from \"./abstractions.js\";\nimport { ROOT_FOLDER } from \"~/constants.js\";\nimport { FoldersCache, FoldersContext } from \"~/features/folders/abstractions.js\";\nimport { ListFoldersUseCase } from \"~/features/folders/listFolders/abstractions.js\";\nimport { ListFoldersByParentIdsUseCase } from \"~/features/folders/listFoldersByParentIds/abstractions.js\";\nimport { CreateFolderUseCase } from \"~/features/folders/createFolder/abstractions.js\";\nimport { UpdateFolderUseCase } from \"~/features/folders/updateFolder/abstractions.js\";\nimport { DeleteFolderUseCase } from \"~/features/folders/deleteFolder/abstractions.js\";\nimport { GetFolderAncestorsUseCase } from \"~/features/folders/getFolderAncestors/abstractions.js\";\nimport { GetFolderLevelPermissionUseCase } from \"~/features/folders/getFolderLevelPermission/abstractions.js\";\nimport { FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { Folder } from \"~/domain/folder/Folder.js\";\nimport type { FolderDto } from \"~/domain/folder/FolderDto.js\";\nimport { FolderDtoMapper } from \"~/domain/folder/FolderDtoMapper.js\";\n\ntype FolderChangeCallback = (folderId: string | null) => void;\n\nclass FolderTreePresenterImpl implements Abstraction.Interface {\n private currentFolderId: string | null = null;\n private loading = false;\n private _loadingNodeIds: string[] = [];\n private operation: IFolderOperationState = { active: false, mode: null };\n private callbacks: Set<FolderChangeCallback> = new Set();\n private disposed = false;\n\n constructor(\n private foldersContext: FoldersContext.Interface,\n private foldersCache: FoldersCache.Interface,\n private listFoldersUseCase: ListFoldersUseCase.Interface,\n private listFoldersByParentIdsUseCase: ListFoldersByParentIdsUseCase.Interface,\n private createFolderUseCase: CreateFolderUseCase.Interface,\n private updateFolderUseCase: UpdateFolderUseCase.Interface,\n private deleteFolderUseCase: DeleteFolderUseCase.Interface,\n private getFolderAncestorsUseCase: GetFolderAncestorsUseCase.Interface,\n private getFolderLevelPermissionUseCase: GetFolderLevelPermissionUseCase.Interface,\n private formModelFactory: FormModelFactory.Interface\n ) {\n makeAutoObservable<FolderTreePresenterImpl, \"callbacks\">(\n this,\n { callbacks: false },\n { autoBind: true }\n );\n\n reaction(\n () => this.currentFolderId,\n folderId => {\n if (!this.disposed) {\n this.callbacks.forEach(cb => cb(folderId));\n }\n }\n );\n\n void this.loadFolders();\n }\n\n get vm(): IFolderTreeViewModel {\n const tree = this.buildTree();\n const allFolders = this.foldersCache.getItems();\n const currentNode = this.findNode(this.currentFolderId, tree);\n const currentFolder = this.findFolder(this.currentFolderId);\n const childNodes = currentNode\n ? currentNode.children\n : tree.filter(n => n.id !== ROOT_FOLDER);\n return {\n folders: allFolders.map(f => FolderDtoMapper.toDTO(f)),\n tree,\n currentFolderId: this.currentFolderId,\n currentFolder: currentFolder ? FolderDtoMapper.toDTO(currentFolder) : null,\n isRootFolder: this.currentFolderId === null,\n currentFolderTitle: currentNode?.name ?? \"All Files\",\n childFolders: childNodes\n .map(n => {\n const folder = allFolders.find(f => f.id === n.id);\n return folder ? FolderDtoMapper.toDTO(folder) : null;\n })\n .filter((f): f is FolderDto => f !== null),\n loading: this.loading,\n loadingNodeIds: this._loadingNodeIds,\n operation: this.operation\n };\n }\n\n selectFolder(folderId: string | null): void {\n const normalized = folderId === ROOT_FOLDER ? null : folderId;\n this.currentFolderId = normalized;\n if (normalized) {\n void this.loadChildFolders([normalized]);\n }\n }\n\n onFolderChange(callback: FolderChangeCallback): () => void {\n this.callbacks.add(callback);\n return () => {\n this.callbacks.delete(callback);\n };\n }\n\n async loadChildFolders(parentIds: string[]): Promise<void> {\n const fetchableIds = parentIds.filter(id => id !== ROOT_FOLDER && id !== \"0\");\n if (fetchableIds.length === 0) {\n return;\n }\n\n runInAction(() => {\n this._loadingNodeIds = fetchableIds;\n });\n\n try {\n await this.listFoldersByParentIdsUseCase.execute(fetchableIds);\n } finally {\n runInAction(() => {\n this._loadingNodeIds = [];\n });\n }\n }\n\n async moveFolder(folderId: string, targetParentId: string | null): Promise<void> {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n if (!folder) {\n return;\n }\n\n await this.updateFolderUseCase.execute({\n id: folderId,\n title: folder.title,\n slug: folder.slug,\n type: this.foldersContext.type,\n parentId: targetParentId,\n permissions: folder.permissions\n });\n\n await this.loadFolders();\n }\n\n canManageStructure(folderId: string): boolean {\n return this.getFolderLevelPermissionUseCase.execute(folderId, \"canManageStructure\");\n }\n\n getAncestorIds(folderId: string): string[] {\n return this.getFolderAncestorsUseCase.execute(folderId).map(f => f.id);\n }\n\n createFolder(parentFolderId?: string): void {\n const form = this.formModelFactory.create({\n fields: fields => ({\n title: fields.text().label(\"Title\").required(\"Title is required\"),\n slug: fields\n .text()\n .label(\"Slug\")\n .required(\"Slug is required\")\n .computedUntilDirty(({ form }) => {\n const title = form.field(\"title\").getValue();\n return slugify(String(title ?? \"\"), {\n replacement: \"-\",\n lower: true,\n remove: /[*#\\?<>_\\{\\}\\[\\]+~.()'\"!:;@]/g,\n trim: false\n });\n }),\n parentId: fields\n .text()\n .label(\"Parent folder\")\n .renderer(\"folderTree\" as any)\n }),\n layout: layout => [layout.row(\"title\"), layout.row(\"slug\"), layout.row(\"parentId\")]\n });\n\n form.setData({ parentId: parentFolderId ?? null });\n\n this.operation = {\n active: true,\n mode: \"create\",\n parentFolderId,\n form\n };\n }\n\n editFolder(folderId: string): void {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n if (!folder) {\n return;\n }\n\n const form = this.formModelFactory.create({\n fields: fields => ({\n title: fields.text().label(\"Name\").required(\"Name is required\"),\n slug: fields\n .text()\n .label(\"Slug\")\n .required(\"Slug is required\")\n .computedUntilDirty(({ form }) => {\n const title = form.field(\"title\").getValue();\n return slugify(String(title ?? \"\"), {\n replacement: \"-\",\n lower: true,\n remove: /[*#\\?<>_\\{\\}\\[\\]+~.()'\"!:;@]/g,\n trim: false\n });\n })\n }),\n layout: layout => [layout.row(\"title\"), layout.row(\"slug\")]\n });\n\n form.setData({ title: folder.title, slug: folder.slug });\n\n this.operation = {\n active: true,\n mode: \"edit\",\n folderId,\n form\n };\n }\n\n async submitOperation(): Promise<boolean> {\n const { mode, form, parentFolderId, folderId } = this.operation;\n if (!form || !mode) {\n return false;\n }\n\n const data = await form.submit<{ title: string; slug: string; parentId?: string | null }>();\n if (!data) {\n return false;\n }\n\n if (mode === \"create\") {\n await this.createFolderUseCase.execute({\n title: data.title,\n slug: data.slug,\n type: this.foldersContext.type,\n parentId: data.parentId ?? parentFolderId ?? null,\n permissions: []\n });\n } else if (mode === \"edit\" && folderId) {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n\n await this.updateFolderUseCase.execute({\n id: folderId,\n title: data.title,\n slug: data.slug,\n type: this.foldersContext.type,\n parentId: folder?.parentId ?? null,\n permissions: folder?.permissions ?? []\n });\n }\n\n runInAction(() => {\n this.operation = { active: false, mode: null };\n });\n\n return true;\n }\n\n async deleteFolder(folderId: string): Promise<void> {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n const parentId = folder?.parentId ?? null;\n\n this.operation = {\n active: true,\n mode: \"delete\",\n folderId\n };\n\n await this.deleteFolderUseCase.execute(folderId);\n await this.loadFolders();\n\n runInAction(() => {\n this.operation = { active: false, mode: null };\n this.currentFolderId = parentId;\n });\n }\n\n cancelOperation(): void {\n this.operation = { active: false, mode: null };\n }\n\n private async loadFolders(): Promise<void> {\n runInAction(() => {\n this.loading = true;\n });\n\n try {\n await this.listFoldersUseCase.execute();\n } finally {\n runInAction(() => {\n this.loading = false;\n });\n }\n }\n\n private buildTree(): IFolderTreeNode[] {\n const folders = this.foldersCache.getItems();\n return this.buildTreeFromFolders(folders);\n }\n\n private buildTreeFromFolders(folders: Folder[]): IFolderTreeNode[] {\n const nodeMap = new Map<string, IFolderTreeNode>();\n\n for (const folder of folders) {\n nodeMap.set(folder.id, {\n id: folder.id,\n name: folder.title,\n slug: folder.slug,\n parentId: folder.parentId,\n children: [],\n hasNonInheritedPermissions: folder.hasNonInheritedPermissions ?? false,\n canManagePermissions: folder.canManagePermissions ?? false,\n canManageStructure: folder.canManageStructure ?? true\n });\n }\n\n const roots: IFolderTreeNode[] = [];\n for (const folder of folders) {\n const node = nodeMap.get(folder.id)!;\n if (folder.parentId && nodeMap.has(folder.parentId)) {\n nodeMap.get(folder.parentId)!.children.push(node);\n } else {\n roots.push(node);\n }\n }\n\n return roots;\n }\n\n private findFolder(folderId: string | null): Folder | null {\n if (!folderId) {\n return null;\n }\n\n return this.foldersCache.getItem(f => f.id === folderId) ?? null;\n }\n\n private findNode(folderId: string | null, nodes: IFolderTreeNode[]): IFolderTreeNode | null {\n if (folderId === null) {\n return null;\n }\n\n for (const node of nodes) {\n if (node.id === folderId) {\n return node;\n }\n const found = this.findNode(folderId, node.children);\n if (found) {\n return found;\n }\n }\n\n return null;\n }\n}\n\nexport const FolderTreePresenter = Abstraction.createImplementation({\n implementation: FolderTreePresenterImpl,\n dependencies: [\n FoldersContext,\n FoldersCache,\n ListFoldersUseCase,\n ListFoldersByParentIdsUseCase,\n CreateFolderUseCase,\n UpdateFolderUseCase,\n DeleteFolderUseCase,\n GetFolderAncestorsUseCase,\n GetFolderLevelPermissionUseCase,\n FormModelFactory\n ]\n});\n"],"names":["FolderTreePresenterImpl","foldersContext","foldersCache","listFoldersUseCase","listFoldersByParentIdsUseCase","createFolderUseCase","updateFolderUseCase","deleteFolderUseCase","getFolderAncestorsUseCase","getFolderLevelPermissionUseCase","formModelFactory","Set","makeAutoObservable","reaction","folderId","cb","tree","allFolders","currentNode","currentFolder","childNodes","n","ROOT_FOLDER","f","FolderDtoMapper","folder","normalized","callback","parentIds","fetchableIds","id","runInAction","targetParentId","folders","parentFolderId","form","fields","title","slugify","String","layout","mode","data","parentId","nodeMap","Map","roots","node","nodes","found","FolderTreePresenter","Abstraction","FoldersContext","FoldersCache","ListFoldersUseCase","ListFoldersByParentIdsUseCase","CreateFolderUseCase","UpdateFolderUseCase","DeleteFolderUseCase","GetFolderAncestorsUseCase","GetFolderLevelPermissionUseCase","FormModelFactory"],"mappings":"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"}
1
+ {"version":3,"file":"presentation/folderTree/FolderTreePresenter.js","sources":["../../../src/presentation/folderTree/FolderTreePresenter.ts"],"sourcesContent":["import { makeAutoObservable, reaction, runInAction } from \"mobx\";\nimport slugify from \"slugify\";\nimport {\n FolderTreePresenter as Abstraction,\n type IFolderTreeNode,\n type IFolderTreeViewModel,\n type IFolderOperationState\n} from \"./abstractions.js\";\nimport { ROOT_FOLDER } from \"~/constants.js\";\nimport { FoldersCache, FoldersContext } from \"~/features/folders/abstractions.js\";\nimport { ListFoldersUseCase } from \"~/features/folders/listFolders/abstractions.js\";\nimport { ListFoldersByParentIdsUseCase } from \"~/features/folders/listFoldersByParentIds/abstractions.js\";\nimport { CreateFolderUseCase } from \"~/features/folders/createFolder/abstractions.js\";\nimport { UpdateFolderUseCase } from \"~/features/folders/updateFolder/abstractions.js\";\nimport { DeleteFolderUseCase } from \"~/features/folders/deleteFolder/abstractions.js\";\nimport { GetFolderAncestorsUseCase } from \"~/features/folders/getFolderAncestors/abstractions.js\";\nimport { GetFolderLevelPermissionUseCase } from \"~/features/folders/getFolderLevelPermission/abstractions.js\";\nimport { FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { Folder } from \"~/domain/folder/Folder.js\";\nimport type { FolderDto } from \"~/domain/folder/FolderDto.js\";\nimport { FolderDtoMapper } from \"~/domain/folder/FolderDtoMapper.js\";\n\ntype FolderChangeCallback = (folderId: string | null) => void;\n\nclass FolderTreePresenterImpl implements Abstraction.Interface {\n private currentFolderId: string | null = null;\n private loading = false;\n private _loadingNodeIds: string[] = [];\n private operation: IFolderOperationState = { active: false, mode: null };\n private callbacks: Set<FolderChangeCallback> = new Set();\n private disposed = false;\n\n private previousFolders: Folder[] = [];\n\n constructor(\n private foldersContext: FoldersContext.Interface,\n private foldersCache: FoldersCache.Interface,\n private listFoldersUseCase: ListFoldersUseCase.Interface,\n private listFoldersByParentIdsUseCase: ListFoldersByParentIdsUseCase.Interface,\n private createFolderUseCase: CreateFolderUseCase.Interface,\n private updateFolderUseCase: UpdateFolderUseCase.Interface,\n private deleteFolderUseCase: DeleteFolderUseCase.Interface,\n private getFolderAncestorsUseCase: GetFolderAncestorsUseCase.Interface,\n private getFolderLevelPermissionUseCase: GetFolderLevelPermissionUseCase.Interface,\n private formModelFactory: FormModelFactory.Interface\n ) {\n makeAutoObservable<FolderTreePresenterImpl, \"callbacks\" | \"previousFolders\">(\n this,\n { callbacks: false, previousFolders: false },\n { autoBind: true }\n );\n\n reaction(\n () => this.currentFolderId,\n folderId => {\n if (!this.disposed) {\n this.callbacks.forEach(cb => cb(folderId));\n }\n }\n );\n\n reaction(\n () => ({\n folders: this.foldersCache.getItems(),\n currentFolderId: this.currentFolderId\n }),\n ({ folders, currentFolderId }) => {\n if (currentFolderId !== null) {\n const stillExists = folders.some(f => f.id === currentFolderId);\n // Skip when folders is empty *and* a reload is in flight — the cache is\n // cleared before being repopulated, so this is a transient state, not a\n // real \"all folders were removed\" event.\n const isTransientReloadClear = folders.length === 0 && this.loading;\n if (!stillExists && !isTransientReloadClear) {\n const resolvedId = this.resolveValidFolderId(\n currentFolderId,\n this.previousFolders\n );\n this.selectFolder(resolvedId);\n }\n }\n\n this.previousFolders = folders;\n },\n { fireImmediately: true }\n );\n\n void this.loadFolders();\n }\n\n get vm(): IFolderTreeViewModel {\n const tree = this.buildTree();\n const allFolders = this.foldersCache.getItems();\n const currentNode = this.findNode(this.currentFolderId, tree);\n const currentFolder = this.findFolder(this.currentFolderId);\n const childNodes = currentNode\n ? currentNode.children\n : tree.filter(n => n.id !== ROOT_FOLDER);\n return {\n folders: allFolders.map(f => FolderDtoMapper.toDTO(f)),\n tree,\n currentFolderId: this.currentFolderId,\n currentFolder: currentFolder ? FolderDtoMapper.toDTO(currentFolder) : null,\n isRootFolder: this.currentFolderId === null,\n currentFolderTitle: currentNode?.name ?? \"All Files\",\n childFolders: childNodes\n .map(n => {\n const folder = allFolders.find(f => f.id === n.id);\n return folder ? FolderDtoMapper.toDTO(folder) : null;\n })\n .filter((f): f is FolderDto => f !== null),\n loading: this.loading,\n loadingNodeIds: this._loadingNodeIds,\n operation: this.operation\n };\n }\n\n selectFolder(folderId: string | null): void {\n const normalized = folderId === ROOT_FOLDER ? null : folderId;\n this.currentFolderId = normalized;\n if (normalized) {\n void this.loadChildFolders([normalized]);\n }\n }\n\n onFolderChange(callback: FolderChangeCallback): () => void {\n this.callbacks.add(callback);\n return () => {\n this.callbacks.delete(callback);\n };\n }\n\n async loadChildFolders(parentIds: string[]): Promise<void> {\n const fetchableIds = parentIds.filter(id => id !== ROOT_FOLDER && id !== \"0\");\n if (fetchableIds.length === 0) {\n return;\n }\n\n runInAction(() => {\n this._loadingNodeIds = fetchableIds;\n });\n\n try {\n await this.listFoldersByParentIdsUseCase.execute(fetchableIds);\n } finally {\n runInAction(() => {\n this._loadingNodeIds = [];\n });\n }\n }\n\n async moveFolder(folderId: string, targetParentId: string | null): Promise<void> {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n if (!folder) {\n return;\n }\n\n await this.updateFolderUseCase.execute({\n id: folderId,\n title: folder.title,\n slug: folder.slug,\n type: this.foldersContext.type,\n parentId: targetParentId,\n permissions: folder.permissions\n });\n\n await this.loadFolders();\n }\n\n canManageStructure(folderId: string): boolean {\n return this.getFolderLevelPermissionUseCase.execute(folderId, \"canManageStructure\");\n }\n\n getAncestorIds(folderId: string): string[] {\n return this.getFolderAncestorsUseCase.execute(folderId).map(f => f.id);\n }\n\n createFolder(parentFolderId?: string): void {\n const form = this.formModelFactory.create({\n fields: fields => ({\n title: fields.text().label(\"Title\").required(\"Title is required\"),\n slug: fields\n .text()\n .label(\"Slug\")\n .required(\"Slug is required\")\n .computedUntilDirty(({ form }) => {\n const title = form.field(\"title\").getValue();\n return slugify(String(title ?? \"\"), {\n replacement: \"-\",\n lower: true,\n remove: /[*#\\?<>_\\{\\}\\[\\]+~.()'\"!:;@]/g,\n trim: false\n });\n }),\n parentId: fields\n .text()\n .label(\"Parent folder\")\n .renderer(\"folderTree\" as any)\n }),\n layout: layout => [layout.row(\"title\"), layout.row(\"slug\"), layout.row(\"parentId\")]\n });\n\n form.setData({ parentId: parentFolderId ?? null });\n\n this.operation = {\n active: true,\n mode: \"create\",\n parentFolderId,\n form\n };\n }\n\n editFolder(folderId: string): void {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n if (!folder) {\n return;\n }\n\n const form = this.formModelFactory.create({\n fields: fields => ({\n title: fields.text().label(\"Name\").required(\"Name is required\"),\n slug: fields\n .text()\n .label(\"Slug\")\n .required(\"Slug is required\")\n .computedUntilDirty(({ form }) => {\n const title = form.field(\"title\").getValue();\n return slugify(String(title ?? \"\"), {\n replacement: \"-\",\n lower: true,\n remove: /[*#\\?<>_\\{\\}\\[\\]+~.()'\"!:;@]/g,\n trim: false\n });\n })\n }),\n layout: layout => [layout.row(\"title\"), layout.row(\"slug\")]\n });\n\n form.setData({ title: folder.title, slug: folder.slug });\n\n this.operation = {\n active: true,\n mode: \"edit\",\n folderId,\n form\n };\n }\n\n async submitOperation(): Promise<boolean> {\n const { mode, form, parentFolderId, folderId } = this.operation;\n if (!form || !mode) {\n return false;\n }\n\n const data = await form.submit<{ title: string; slug: string; parentId?: string | null }>();\n if (!data) {\n return false;\n }\n\n if (mode === \"create\") {\n await this.createFolderUseCase.execute({\n title: data.title,\n slug: data.slug,\n type: this.foldersContext.type,\n parentId: data.parentId ?? parentFolderId ?? null,\n permissions: []\n });\n } else if (mode === \"edit\" && folderId) {\n const folders = this.foldersCache.getItems();\n const folder = folders.find(f => f.id === folderId);\n\n await this.updateFolderUseCase.execute({\n id: folderId,\n title: data.title,\n slug: data.slug,\n type: this.foldersContext.type,\n parentId: folder?.parentId ?? null,\n permissions: folder?.permissions ?? []\n });\n }\n\n runInAction(() => {\n this.operation = { active: false, mode: null };\n });\n\n return true;\n }\n\n async deleteFolder(folderId: string): Promise<void> {\n this.operation = {\n active: true,\n mode: \"delete\",\n folderId\n };\n\n await this.deleteFolderUseCase.execute(folderId);\n\n runInAction(() => {\n this.foldersCache.removeItems(f => f.id === folderId);\n this.operation = { active: false, mode: null };\n });\n }\n\n cancelOperation(): void {\n this.operation = { active: false, mode: null };\n }\n\n private async loadFolders(): Promise<void> {\n runInAction(() => {\n this.loading = true;\n });\n\n try {\n await this.listFoldersUseCase.execute();\n } finally {\n runInAction(() => {\n this.loading = false;\n });\n }\n }\n\n private resolveValidFolderId(\n folderId: string | null,\n previousFolders: Folder[]\n ): string | null {\n let candidateId = folderId;\n const visited = new Set<string>();\n\n while (candidateId !== null) {\n if (visited.has(candidateId)) {\n return null;\n }\n visited.add(candidateId);\n\n if (this.foldersCache.getItem(f => f.id === candidateId)) {\n return candidateId;\n }\n\n const prev = previousFolders.find(f => f.id === candidateId);\n candidateId = prev?.parentId ?? null;\n }\n\n return null;\n }\n\n private buildTree(): IFolderTreeNode[] {\n const folders = this.foldersCache.getItems();\n return this.buildTreeFromFolders(folders);\n }\n\n private buildTreeFromFolders(folders: Folder[]): IFolderTreeNode[] {\n const nodeMap = new Map<string, IFolderTreeNode>();\n\n for (const folder of folders) {\n nodeMap.set(folder.id, {\n id: folder.id,\n name: folder.title,\n slug: folder.slug,\n parentId: folder.parentId,\n children: [],\n hasNonInheritedPermissions: folder.hasNonInheritedPermissions ?? false,\n canManagePermissions: folder.canManagePermissions ?? false,\n canManageStructure: folder.canManageStructure ?? true\n });\n }\n\n const roots: IFolderTreeNode[] = [];\n for (const folder of folders) {\n const node = nodeMap.get(folder.id)!;\n if (folder.parentId && nodeMap.has(folder.parentId)) {\n nodeMap.get(folder.parentId)!.children.push(node);\n } else {\n roots.push(node);\n }\n }\n\n return roots;\n }\n\n private findFolder(folderId: string | null): Folder | null {\n if (!folderId) {\n return null;\n }\n\n return this.foldersCache.getItem(f => f.id === folderId) ?? null;\n }\n\n private findNode(folderId: string | null, nodes: IFolderTreeNode[]): IFolderTreeNode | null {\n if (folderId === null) {\n return null;\n }\n\n for (const node of nodes) {\n if (node.id === folderId) {\n return node;\n }\n const found = this.findNode(folderId, node.children);\n if (found) {\n return found;\n }\n }\n\n return null;\n }\n}\n\nexport const FolderTreePresenter = Abstraction.createImplementation({\n implementation: FolderTreePresenterImpl,\n dependencies: [\n FoldersContext,\n FoldersCache,\n ListFoldersUseCase,\n ListFoldersByParentIdsUseCase,\n CreateFolderUseCase,\n UpdateFolderUseCase,\n DeleteFolderUseCase,\n GetFolderAncestorsUseCase,\n GetFolderLevelPermissionUseCase,\n FormModelFactory\n ]\n});\n"],"names":["FolderTreePresenterImpl","foldersContext","foldersCache","listFoldersUseCase","listFoldersByParentIdsUseCase","createFolderUseCase","updateFolderUseCase","deleteFolderUseCase","getFolderAncestorsUseCase","getFolderLevelPermissionUseCase","formModelFactory","Set","makeAutoObservable","reaction","folderId","cb","folders","currentFolderId","stillExists","f","isTransientReloadClear","resolvedId","tree","allFolders","currentNode","currentFolder","childNodes","n","ROOT_FOLDER","FolderDtoMapper","folder","normalized","callback","parentIds","fetchableIds","id","runInAction","targetParentId","parentFolderId","form","fields","title","slugify","String","layout","mode","data","previousFolders","candidateId","visited","prev","nodeMap","Map","roots","node","nodes","found","FolderTreePresenter","Abstraction","FoldersContext","FoldersCache","ListFoldersUseCase","ListFoldersByParentIdsUseCase","CreateFolderUseCase","UpdateFolderUseCase","DeleteFolderUseCase","GetFolderAncestorsUseCase","GetFolderLevelPermissionUseCase","FormModelFactory"],"mappings":"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"}
@@ -469,40 +469,137 @@ describe("FolderTreePresenter", ()=>{
469
469
  expect(presenter.vm.operation.active).toBe(false);
470
470
  expect(presenter.vm.operation.mode).toBeNull();
471
471
  });
472
- it("should navigate to parent folder after deletion", async ()=>{
472
+ it("should navigate to parent folder after deleting the currently open folder", async ()=>{
473
473
  const { presenter } = createTestPresenter(createFlatFolders());
474
474
  await vi.waitFor(()=>{
475
475
  expect(presenter.vm.loading).toBe(false);
476
476
  });
477
+ presenter.selectFolder("child-1");
477
478
  await presenter.deleteFolder("child-1");
478
479
  expect(presenter.vm.currentFolderId).toBe("root-1");
479
480
  });
480
- it("should navigate to root (null) when deleting a root folder", async ()=>{
481
+ it("should navigate to root (null) when deleting the currently open root folder", async ()=>{
481
482
  const { presenter } = createTestPresenter(createFlatFolders());
482
483
  await vi.waitFor(()=>{
483
484
  expect(presenter.vm.loading).toBe(false);
484
485
  });
486
+ presenter.selectFolder("root-1");
485
487
  await presenter.deleteFolder("root-1");
486
488
  expect(presenter.vm.currentFolderId).toBeNull();
487
489
  });
488
- it("should refresh the folder list after deletion", async ()=>{
490
+ it("should not change currentFolderId when deleting a folder that isn't open", async ()=>{
491
+ const { presenter } = createTestPresenter(createFlatFolders());
492
+ await vi.waitFor(()=>{
493
+ expect(presenter.vm.loading).toBe(false);
494
+ });
495
+ presenter.selectFolder("root-2");
496
+ await presenter.deleteFolder("child-1");
497
+ expect(presenter.vm.currentFolderId).toBe("root-2");
498
+ });
499
+ it("should remove the deleted folder from the cache without refetching from the API", async ()=>{
489
500
  const { presenter, listFolders } = createTestPresenter(createFlatFolders());
490
501
  await vi.waitFor(()=>{
491
502
  expect(presenter.vm.loading).toBe(false);
492
503
  });
493
504
  await presenter.deleteFolder("child-1");
494
- expect(listFolders.execute).toHaveBeenCalledTimes(2);
505
+ expect(listFolders.execute).toHaveBeenCalledTimes(1);
506
+ expect(presenter.vm.folders.find((f)=>"child-1" === f.id)).toBeUndefined();
495
507
  });
496
- it("should fire onFolderChange callback when navigating to parent after delete", async ()=>{
508
+ it("should fire onFolderChange callback when navigating to parent after deleting the open folder", async ()=>{
497
509
  const { presenter } = createTestPresenter(createFlatFolders());
498
510
  await vi.waitFor(()=>{
499
511
  expect(presenter.vm.loading).toBe(false);
500
512
  });
513
+ presenter.selectFolder("child-1");
501
514
  const callback = vi.fn();
502
515
  presenter.onFolderChange(callback);
503
516
  await presenter.deleteFolder("child-1");
504
517
  expect(callback).toHaveBeenCalledWith("root-1");
505
518
  });
519
+ it("should resolve the label for the parent folder after deleting the currently open folder", async ()=>{
520
+ const { presenter } = createTestPresenter(createFlatFolders());
521
+ await vi.waitFor(()=>{
522
+ expect(presenter.vm.loading).toBe(false);
523
+ });
524
+ presenter.selectFolder("grandchild-1");
525
+ await presenter.deleteFolder("grandchild-1");
526
+ expect(presenter.vm.currentFolderId).toBe("child-1");
527
+ expect(presenter.vm.currentFolderTitle).toBe("Invoices");
528
+ });
529
+ });
530
+ describe("stale currentFolderId validation", ()=>{
531
+ it("should fall back to root when the initial currentFolderId no longer exists", async ()=>{
532
+ const { presenter } = createTestPresenter(createFlatFolders());
533
+ presenter.selectFolder("does-not-exist");
534
+ await vi.waitFor(()=>{
535
+ expect(presenter.vm.loading).toBe(false);
536
+ });
537
+ expect(presenter.vm.currentFolderId).toBeNull();
538
+ });
539
+ it("should fall back to the nearest existing ancestor when a deeply nested folder was deleted in another session", async ()=>{
540
+ const { presenter, foldersCache, listFolders, updateFolderUseCase } = createTestPresenter(createFlatFolders());
541
+ await vi.waitFor(()=>{
542
+ expect(presenter.vm.loading).toBe(false);
543
+ });
544
+ presenter.selectFolder("grandchild-1");
545
+ expect(presenter.vm.currentFolderId).toBe("grandchild-1");
546
+ listFolders.execute.mockImplementationOnce(async ()=>{
547
+ foldersCache.removeItems((f)=>"grandchild-1" === f.id || "child-1" === f.id);
548
+ });
549
+ await presenter.moveFolder("root-2", "root-1");
550
+ expect(updateFolderUseCase.execute).toHaveBeenCalledTimes(1);
551
+ expect(presenter.vm.currentFolderId).toBe("root-1");
552
+ });
553
+ });
554
+ describe("external cache deletion (e.g. useDeleteFolder bypassing presenter)", ()=>{
555
+ it("should redirect to parent when the current folder is removed from cache externally", async ()=>{
556
+ const { presenter, foldersCache } = createTestPresenter(createFlatFolders());
557
+ await vi.waitFor(()=>{
558
+ expect(presenter.vm.loading).toBe(false);
559
+ });
560
+ presenter.selectFolder("child-1");
561
+ expect(presenter.vm.currentFolderId).toBe("child-1");
562
+ foldersCache.removeItems((f)=>"child-1" === f.id);
563
+ expect(presenter.vm.currentFolderId).toBe("root-1");
564
+ });
565
+ it("should fire onFolderChange callback on external cache deletion", async ()=>{
566
+ const { presenter, foldersCache } = createTestPresenter(createFlatFolders());
567
+ await vi.waitFor(()=>{
568
+ expect(presenter.vm.loading).toBe(false);
569
+ });
570
+ presenter.selectFolder("child-1");
571
+ const callback = vi.fn();
572
+ presenter.onFolderChange(callback);
573
+ foldersCache.removeItems((f)=>"child-1" === f.id);
574
+ expect(callback).toHaveBeenCalledWith("root-1");
575
+ });
576
+ it("should redirect to root when a root-level folder is removed externally", async ()=>{
577
+ const { presenter, foldersCache } = createTestPresenter(createFlatFolders());
578
+ await vi.waitFor(()=>{
579
+ expect(presenter.vm.loading).toBe(false);
580
+ });
581
+ presenter.selectFolder("root-1");
582
+ foldersCache.removeItems((f)=>"root-1" === f.id);
583
+ expect(presenter.vm.currentFolderId).toBeNull();
584
+ });
585
+ it("should redirect to root when the last remaining folder is removed externally", async ()=>{
586
+ const singleFolder = [
587
+ createFolder({
588
+ id: "root-1",
589
+ title: "Documents",
590
+ slug: "documents",
591
+ parentId: null
592
+ })
593
+ ];
594
+ const { presenter, foldersCache } = createTestPresenter(singleFolder);
595
+ await vi.waitFor(()=>{
596
+ expect(presenter.vm.loading).toBe(false);
597
+ });
598
+ presenter.selectFolder("root-1");
599
+ expect(presenter.vm.currentFolderId).toBe("root-1");
600
+ foldersCache.removeItems((f)=>"root-1" === f.id);
601
+ expect(presenter.vm.currentFolderId).toBeNull();
602
+ });
506
603
  });
507
604
  describe("operation state", ()=>{
508
605
  it("should start with inactive operation state", async ()=>{
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/folderTree/FolderTreePresenter.test.js","sources":["../../../src/presentation/folderTree/FolderTreePresenter.test.ts"],"sourcesContent":["import { describe, it, expect, vi } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { FolderTreePresenter as Abstraction } from \"./abstractions.js\";\nimport { FolderTreePresenterFeature } from \"./feature.js\";\nimport { FoldersCache, FoldersContext } from \"~/features/folders/abstractions.js\";\nimport { ListFoldersUseCase } from \"~/features/folders/listFolders/abstractions.js\";\nimport { CreateFolderUseCase } from \"~/features/folders/createFolder/abstractions.js\";\nimport { UpdateFolderUseCase } from \"~/features/folders/updateFolder/abstractions.js\";\nimport { DeleteFolderUseCase } from \"~/features/folders/deleteFolder/abstractions.js\";\nimport { ListFoldersByParentIdsUseCase } from \"~/features/folders/listFoldersByParentIds/abstractions.js\";\nimport { GetFolderAncestorsUseCase } from \"~/features/folders/getFolderAncestors/abstractions.js\";\nimport { GetFolderLevelPermissionUseCase } from \"~/features/folders/getFolderLevelPermission/abstractions.js\";\nimport { FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport { ListCache } from \"~/features/folders/cache/index.js\";\nimport { Folder } from \"~/domain/folder/Folder.js\";\nimport type {\n IFormModel,\n IFormModelConfig,\n IField\n} from \"@webiny/app-admin/features/formModel/abstractions.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst TYPE = \"FmFile\";\n\nfunction createFolder(data: {\n id: string;\n title: string;\n slug: string;\n parentId: string | null;\n}): Folder {\n return Folder.create({\n ...data,\n type: TYPE,\n permissions: []\n });\n}\n\nfunction createFlatFolders(): Folder[] {\n return [\n createFolder({ id: \"root-1\", title: \"Documents\", slug: \"documents\", parentId: null }),\n createFolder({ id: \"root-2\", title: \"Images\", slug: \"images\", parentId: null }),\n createFolder({ id: \"child-1\", title: \"Invoices\", slug: \"invoices\", parentId: \"root-1\" }),\n createFolder({\n id: \"child-2\",\n title: \"Contracts\",\n slug: \"contracts\",\n parentId: \"root-1\"\n }),\n createFolder({\n id: \"grandchild-1\",\n title: \"2024\",\n slug: \"2024\",\n parentId: \"child-1\"\n })\n ];\n}\n\n// ---------------------------------------------------------------------------\n// Mock FormModel\n// ---------------------------------------------------------------------------\n\nfunction createMockField(): IField {\n let value: unknown = undefined;\n return {\n getValue: vi.fn(() => value),\n setValue: vi.fn((v: unknown) => {\n value = v;\n }),\n setDisabled: vi.fn(),\n setVisible: vi.fn(),\n remove: vi.fn(),\n addBeforeChange: vi.fn(),\n addAfterChange: vi.fn(),\n addAfterSetValue: vi.fn(),\n addOnBlur: vi.fn(),\n blur: vi.fn(),\n as: vi.fn()\n } as unknown as IField;\n}\n\nfunction createMockFormModel(): IFormModel {\n const fields = new Map<string, IField>();\n let data: Record<string, unknown> = {};\n\n const form: IFormModel = {\n field(name: string) {\n if (!fields.has(name)) {\n fields.set(name, createMockField());\n }\n return fields.get(name)!;\n },\n fields: vi.fn(),\n layout: vi.fn() as IFormModel[\"layout\"],\n setLayout: vi.fn(),\n addRule: vi.fn(),\n getData: vi.fn(() => data),\n setData: vi.fn((d: Record<string, unknown>) => {\n data = d;\n }),\n reset: vi.fn(),\n validate: vi.fn(async () => true),\n submit: vi.fn(async () => data) as IFormModel[\"submit\"],\n evaluateRules: vi.fn(() => ({ visible: true, disabled: false })),\n focusField: vi.fn(),\n isDirty: false,\n isValid: null,\n submitted: false,\n submitCount: 0,\n errors: [],\n vm: {\n layout: [],\n isDirty: false,\n isValid: null,\n errors: [],\n hasErrors: false,\n submitCount: 0,\n focusField: vi.fn(),\n getData: vi.fn(() => ({})),\n setData: vi.fn()\n },\n getFieldBuilders: vi.fn(() => []),\n resolveChildLayout: vi.fn(() => []),\n registry: {} as IFormModel[\"registry\"],\n traverse: vi.fn()\n };\n\n return form;\n}\n\n// ---------------------------------------------------------------------------\n// Mock use cases\n// ---------------------------------------------------------------------------\n\nclass MockListFoldersUseCase implements ListFoldersUseCase.Interface {\n execute = vi.fn(async () => {});\n}\n\nclass MockCreateFolderUseCase implements CreateFolderUseCase.Interface {\n execute = vi.fn(async () => {});\n}\n\nclass MockUpdateFolderUseCase implements UpdateFolderUseCase.Interface {\n execute = vi.fn(async () => {});\n}\n\nclass MockDeleteFolderUseCase implements DeleteFolderUseCase.Interface {\n execute = vi.fn(async () => {});\n}\n\nclass MockFormModelFactory {\n create = vi.fn((_config: IFormModelConfig) => createMockFormModel());\n}\n\n// ---------------------------------------------------------------------------\n// Setup\n// ---------------------------------------------------------------------------\n\nfunction createTestPresenter(folders: Folder[] = []) {\n const container = new Container();\n const foldersCache = new ListCache<Folder>();\n const listFolders = new MockListFoldersUseCase();\n const createFolderUseCase = new MockCreateFolderUseCase();\n const updateFolderUseCase = new MockUpdateFolderUseCase();\n const deleteFolderUseCase = new MockDeleteFolderUseCase();\n const formModelFactory = new MockFormModelFactory();\n\n // Pre-populate cache.\n if (folders.length > 0) {\n foldersCache.addItems(folders);\n }\n\n container.registerInstance(FoldersContext, { type: TYPE });\n container.registerInstance(FoldersCache, foldersCache);\n container.registerInstance(ListFoldersUseCase, listFolders);\n container.registerInstance(CreateFolderUseCase, createFolderUseCase);\n container.registerInstance(UpdateFolderUseCase, updateFolderUseCase);\n container.registerInstance(DeleteFolderUseCase, deleteFolderUseCase);\n container.registerInstance(ListFoldersByParentIdsUseCase, {\n execute: vi.fn().mockResolvedValue(undefined)\n });\n container.registerInstance(GetFolderAncestorsUseCase, {\n execute: vi.fn().mockReturnValue([])\n });\n container.registerInstance(GetFolderLevelPermissionUseCase, {\n execute: vi.fn().mockReturnValue(true)\n });\n container.registerInstance(\n FormModelFactory,\n formModelFactory as unknown as FormModelFactory.Interface\n );\n\n FolderTreePresenterFeature.register(container);\n\n const presenter = container.resolve(Abstraction);\n\n return {\n presenter,\n foldersCache,\n listFolders,\n createFolderUseCase,\n updateFolderUseCase,\n deleteFolderUseCase,\n formModelFactory\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\ndescribe(\"FolderTreePresenter\", () => {\n // -------------------------------------------------------------------\n // Scoped initialization via FoldersContext\n // -------------------------------------------------------------------\n\n describe(\"scoped initialization\", () => {\n it(\"should load folders on construction via ListFoldersUseCase\", () => {\n const { listFolders } = createTestPresenter();\n expect(listFolders.execute).toHaveBeenCalledTimes(1);\n });\n\n it(\"should not require an init(type) method — type comes from FoldersContext\", () => {\n const { presenter } = createTestPresenter();\n // The presenter has no init method — it reads type from injected FoldersContext.\n expect((presenter as any).init).toBeUndefined();\n });\n\n it(\"should start with loading=true then resolve to false\", async () => {\n const { presenter } = createTestPresenter();\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n });\n });\n\n // -------------------------------------------------------------------\n // Tree computation from flat folder list\n // -------------------------------------------------------------------\n\n describe(\"tree computation\", () => {\n it(\"should build a nested tree from a flat folder list\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n const { tree } = presenter.vm;\n\n // Two root nodes.\n expect(tree).toHaveLength(2);\n expect(tree[0].name).toBe(\"Documents\");\n expect(tree[1].name).toBe(\"Images\");\n\n // Documents has 2 children.\n expect(tree[0].children).toHaveLength(2);\n expect(tree[0].children[0].name).toBe(\"Invoices\");\n expect(tree[0].children[1].name).toBe(\"Contracts\");\n\n // Invoices has 1 grandchild.\n expect(tree[0].children[0].children).toHaveLength(1);\n expect(tree[0].children[0].children[0].name).toBe(\"2024\");\n\n // Images has no children.\n expect(tree[1].children).toHaveLength(0);\n });\n\n it(\"should return an empty tree when cache is empty\", async () => {\n const { presenter } = createTestPresenter([]);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n expect(presenter.vm.tree).toEqual([]);\n });\n\n it(\"should treat folders with unknown parentId as root nodes\", async () => {\n const folders = [\n createFolder({\n id: \"orphan\",\n title: \"Orphan\",\n slug: \"orphan\",\n parentId: \"non-existent\"\n })\n ];\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n expect(presenter.vm.tree).toHaveLength(1);\n expect(presenter.vm.tree[0].name).toBe(\"Orphan\");\n });\n\n it(\"should map folder properties correctly to tree nodes\", async () => {\n const folders = [\n createFolder({\n id: \"f1\",\n title: \"My Folder\",\n slug: \"my-folder\",\n parentId: null\n })\n ];\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n const node = presenter.vm.tree[0];\n expect(node.id).toBe(\"f1\");\n expect(node.name).toBe(\"My Folder\");\n expect(node.slug).toBe(\"my-folder\");\n expect(node.parentId).toBeNull();\n expect(node.children).toEqual([]);\n });\n });\n\n // -------------------------------------------------------------------\n // selectFolder and onFolderChange\n // -------------------------------------------------------------------\n\n describe(\"selectFolder\", () => {\n it(\"should update currentFolderId\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n expect(presenter.vm.currentFolderId).toBeNull();\n\n presenter.selectFolder(\"root-1\");\n expect(presenter.vm.currentFolderId).toBe(\"root-1\");\n });\n\n it(\"should update currentFolder to the matching folder\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"child-1\");\n expect(presenter.vm.currentFolder).not.toBeNull();\n expect(presenter.vm.currentFolder!.id).toBe(\"child-1\");\n expect(presenter.vm.currentFolder!.title).toBe(\"Invoices\");\n });\n\n it(\"should set currentFolder to null when selecting root (null)\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"root-1\");\n expect(presenter.vm.currentFolder).not.toBeNull();\n\n presenter.selectFolder(null);\n expect(presenter.vm.currentFolderId).toBeNull();\n expect(presenter.vm.currentFolder).toBeNull();\n });\n\n it(\"should find deeply nested folders as currentFolder\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"grandchild-1\");\n expect(presenter.vm.currentFolder).not.toBeNull();\n expect(presenter.vm.currentFolder!.title).toBe(\"2024\");\n });\n });\n\n describe(\"onFolderChange\", () => {\n it(\"should fire callbacks when currentFolderId changes\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n const callback = vi.fn();\n presenter.onFolderChange(callback);\n\n presenter.selectFolder(\"root-1\");\n\n // MobX reaction fires synchronously for simple observable changes.\n expect(callback).toHaveBeenCalledTimes(1);\n expect(callback).toHaveBeenCalledWith(\"root-1\");\n });\n\n it(\"should fire callback with null when navigating to root\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"root-1\");\n\n const callback = vi.fn();\n presenter.onFolderChange(callback);\n\n presenter.selectFolder(null);\n expect(callback).toHaveBeenCalledWith(null);\n });\n\n it(\"should support multiple callbacks\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n const cb1 = vi.fn();\n const cb2 = vi.fn();\n presenter.onFolderChange(cb1);\n presenter.onFolderChange(cb2);\n\n presenter.selectFolder(\"root-2\");\n\n expect(cb1).toHaveBeenCalledWith(\"root-2\");\n expect(cb2).toHaveBeenCalledWith(\"root-2\");\n });\n\n it(\"should return an unsubscribe function that stops callbacks\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n const callback = vi.fn();\n const unsubscribe = presenter.onFolderChange(callback);\n\n presenter.selectFolder(\"root-1\");\n expect(callback).toHaveBeenCalledTimes(1);\n\n unsubscribe();\n\n presenter.selectFolder(\"root-2\");\n // Should not have been called again after unsubscribe.\n expect(callback).toHaveBeenCalledTimes(1);\n });\n\n it(\"should not fire callback when selecting the same folder\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"root-1\");\n\n const callback = vi.fn();\n presenter.onFolderChange(callback);\n\n // Select the same folder again.\n presenter.selectFolder(\"root-1\");\n expect(callback).not.toHaveBeenCalled();\n });\n });\n\n // -------------------------------------------------------------------\n // createFolder / editFolder — operation state with FormModel\n // -------------------------------------------------------------------\n\n describe(\"createFolder\", () => {\n it(\"should set operation state with mode 'create' and a FormModel\", async () => {\n const { presenter, formModelFactory } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.createFolder(\"root-1\");\n\n expect(presenter.vm.operation.active).toBe(true);\n expect(presenter.vm.operation.mode).toBe(\"create\");\n expect(presenter.vm.operation.parentFolderId).toBe(\"root-1\");\n expect(presenter.vm.operation.form).toBeDefined();\n expect(formModelFactory.create).toHaveBeenCalledTimes(1);\n });\n\n it(\"should set parentFolderId to undefined when creating at root\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.createFolder();\n\n expect(presenter.vm.operation.active).toBe(true);\n expect(presenter.vm.operation.mode).toBe(\"create\");\n expect(presenter.vm.operation.parentFolderId).toBeUndefined();\n });\n\n it(\"should wire submitOperation to CreateFolderUseCase\", async () => {\n const { presenter, createFolderUseCase, listFolders } =\n createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.createFolder(\"root-1\");\n\n await presenter.submitOperation();\n\n expect(createFolderUseCase.execute).toHaveBeenCalledTimes(1);\n expect(listFolders.execute).toHaveBeenCalledTimes(1);\n });\n });\n\n describe(\"editFolder\", () => {\n it(\"should set operation state with mode 'edit' and a FormModel\", async () => {\n const { presenter, formModelFactory } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.editFolder(\"root-1\");\n\n expect(presenter.vm.operation.active).toBe(true);\n expect(presenter.vm.operation.mode).toBe(\"edit\");\n expect(presenter.vm.operation.folderId).toBe(\"root-1\");\n expect(presenter.vm.operation.form).toBeDefined();\n expect(formModelFactory.create).toHaveBeenCalledTimes(1);\n });\n\n it(\"should pre-populate form with existing folder data\", async () => {\n const mockForm = createMockFormModel();\n const { presenter, formModelFactory } = createTestPresenter(createFlatFolders());\n formModelFactory.create.mockReturnValue(mockForm);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.editFolder(\"root-1\");\n\n expect(mockForm.setData).toHaveBeenCalledWith({\n title: \"Documents\",\n slug: \"documents\"\n });\n });\n\n it(\"should do nothing when editing a non-existent folder\", async () => {\n const { presenter, formModelFactory } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.editFolder(\"non-existent\");\n\n expect(presenter.vm.operation.active).toBe(false);\n expect(presenter.vm.operation.mode).toBeNull();\n expect(formModelFactory.create).not.toHaveBeenCalled();\n });\n });\n\n // -------------------------------------------------------------------\n // deleteFolder — navigates to parent and updates tree\n // -------------------------------------------------------------------\n\n describe(\"deleteFolder\", () => {\n it(\"should call DeleteFolderUseCase with the folder id\", async () => {\n const { presenter, deleteFolderUseCase } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n await presenter.deleteFolder(\"child-1\");\n\n expect(deleteFolderUseCase.execute).toHaveBeenCalledWith(\"child-1\");\n });\n\n it(\"should set operation state with mode 'delete' during deletion\", async () => {\n const { presenter, deleteFolderUseCase } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n // Make delete hang so we can inspect intermediate state.\n let resolveDelete!: () => void;\n deleteFolderUseCase.execute.mockReturnValueOnce(\n new Promise<void>(resolve => {\n resolveDelete = resolve;\n })\n );\n\n const deletePromise = presenter.deleteFolder(\"child-1\");\n\n expect(presenter.vm.operation.active).toBe(true);\n expect(presenter.vm.operation.mode).toBe(\"delete\");\n expect(presenter.vm.operation.folderId).toBe(\"child-1\");\n\n resolveDelete();\n await deletePromise;\n\n // After completion, operation should be reset.\n expect(presenter.vm.operation.active).toBe(false);\n expect(presenter.vm.operation.mode).toBeNull();\n });\n\n it(\"should navigate to parent folder after deletion\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n // child-1 has parentId \"root-1\".\n await presenter.deleteFolder(\"child-1\");\n\n expect(presenter.vm.currentFolderId).toBe(\"root-1\");\n });\n\n it(\"should navigate to root (null) when deleting a root folder\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n // root-1 has parentId null.\n await presenter.deleteFolder(\"root-1\");\n\n expect(presenter.vm.currentFolderId).toBeNull();\n });\n\n it(\"should refresh the folder list after deletion\", async () => {\n const { presenter, listFolders } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n await presenter.deleteFolder(\"child-1\");\n\n // Initial load + refresh after delete.\n expect(listFolders.execute).toHaveBeenCalledTimes(2);\n });\n\n it(\"should fire onFolderChange callback when navigating to parent after delete\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n const callback = vi.fn();\n presenter.onFolderChange(callback);\n\n // child-1 has parentId \"root-1\".\n await presenter.deleteFolder(\"child-1\");\n\n expect(callback).toHaveBeenCalledWith(\"root-1\");\n });\n });\n\n // -------------------------------------------------------------------\n // Operation state reset\n // -------------------------------------------------------------------\n\n describe(\"operation state\", () => {\n it(\"should start with inactive operation state\", async () => {\n const { presenter } = createTestPresenter();\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n expect(presenter.vm.operation.active).toBe(false);\n expect(presenter.vm.operation.mode).toBeNull();\n });\n });\n});\n"],"names":["TYPE","createFolder","data","Folder","createFlatFolders","createMockField","value","vi","v","createMockFormModel","fields","Map","form","name","d","MockListFoldersUseCase","MockCreateFolderUseCase","MockUpdateFolderUseCase","MockDeleteFolderUseCase","MockFormModelFactory","_config","createTestPresenter","folders","container","Container","foldersCache","ListCache","listFolders","createFolderUseCase","updateFolderUseCase","deleteFolderUseCase","formModelFactory","FoldersContext","FoldersCache","ListFoldersUseCase","CreateFolderUseCase","UpdateFolderUseCase","DeleteFolderUseCase","ListFoldersByParentIdsUseCase","undefined","GetFolderAncestorsUseCase","GetFolderLevelPermissionUseCase","FormModelFactory","FolderTreePresenterFeature","presenter","Abstraction","describe","it","expect","tree","node","callback","cb1","cb2","unsubscribe","mockForm","resolveDelete","Promise","resolve","deletePromise"],"mappings":"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"}
1
+ {"version":3,"file":"presentation/folderTree/FolderTreePresenter.test.js","sources":["../../../src/presentation/folderTree/FolderTreePresenter.test.ts"],"sourcesContent":["import { describe, it, expect, vi } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { FolderTreePresenter as Abstraction } from \"./abstractions.js\";\nimport { FolderTreePresenterFeature } from \"./feature.js\";\nimport { FoldersCache, FoldersContext } from \"~/features/folders/abstractions.js\";\nimport { ListFoldersUseCase } from \"~/features/folders/listFolders/abstractions.js\";\nimport { CreateFolderUseCase } from \"~/features/folders/createFolder/abstractions.js\";\nimport { UpdateFolderUseCase } from \"~/features/folders/updateFolder/abstractions.js\";\nimport { DeleteFolderUseCase } from \"~/features/folders/deleteFolder/abstractions.js\";\nimport { ListFoldersByParentIdsUseCase } from \"~/features/folders/listFoldersByParentIds/abstractions.js\";\nimport { GetFolderAncestorsUseCase } from \"~/features/folders/getFolderAncestors/abstractions.js\";\nimport { GetFolderLevelPermissionUseCase } from \"~/features/folders/getFolderLevelPermission/abstractions.js\";\nimport { FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport { ListCache } from \"~/features/folders/cache/index.js\";\nimport { Folder } from \"~/domain/folder/Folder.js\";\nimport type {\n IFormModel,\n IFormModelConfig,\n IField\n} from \"@webiny/app-admin/features/formModel/abstractions.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst TYPE = \"FmFile\";\n\nfunction createFolder(data: {\n id: string;\n title: string;\n slug: string;\n parentId: string | null;\n}): Folder {\n return Folder.create({\n ...data,\n type: TYPE,\n permissions: []\n });\n}\n\nfunction createFlatFolders(): Folder[] {\n return [\n createFolder({ id: \"root-1\", title: \"Documents\", slug: \"documents\", parentId: null }),\n createFolder({ id: \"root-2\", title: \"Images\", slug: \"images\", parentId: null }),\n createFolder({ id: \"child-1\", title: \"Invoices\", slug: \"invoices\", parentId: \"root-1\" }),\n createFolder({\n id: \"child-2\",\n title: \"Contracts\",\n slug: \"contracts\",\n parentId: \"root-1\"\n }),\n createFolder({\n id: \"grandchild-1\",\n title: \"2024\",\n slug: \"2024\",\n parentId: \"child-1\"\n })\n ];\n}\n\n// ---------------------------------------------------------------------------\n// Mock FormModel\n// ---------------------------------------------------------------------------\n\nfunction createMockField(): IField {\n let value: unknown = undefined;\n return {\n getValue: vi.fn(() => value),\n setValue: vi.fn((v: unknown) => {\n value = v;\n }),\n setDisabled: vi.fn(),\n setVisible: vi.fn(),\n remove: vi.fn(),\n addBeforeChange: vi.fn(),\n addAfterChange: vi.fn(),\n addAfterSetValue: vi.fn(),\n addOnBlur: vi.fn(),\n blur: vi.fn(),\n as: vi.fn()\n } as unknown as IField;\n}\n\nfunction createMockFormModel(): IFormModel {\n const fields = new Map<string, IField>();\n let data: Record<string, unknown> = {};\n\n const form: IFormModel = {\n field(name: string) {\n if (!fields.has(name)) {\n fields.set(name, createMockField());\n }\n return fields.get(name)!;\n },\n fields: vi.fn(),\n layout: vi.fn() as IFormModel[\"layout\"],\n setLayout: vi.fn(),\n addRule: vi.fn(),\n getData: vi.fn(() => data),\n setData: vi.fn((d: Record<string, unknown>) => {\n data = d;\n }),\n reset: vi.fn(),\n validate: vi.fn(async () => true),\n submit: vi.fn(async () => data) as IFormModel[\"submit\"],\n evaluateRules: vi.fn(() => ({ visible: true, disabled: false })),\n focusField: vi.fn(),\n isDirty: false,\n isValid: null,\n submitted: false,\n submitCount: 0,\n errors: [],\n vm: {\n layout: [],\n isDirty: false,\n isValid: null,\n errors: [],\n hasErrors: false,\n submitCount: 0,\n focusField: vi.fn(),\n getData: vi.fn(() => ({})),\n setData: vi.fn()\n },\n getFieldBuilders: vi.fn(() => []),\n resolveChildLayout: vi.fn(() => []),\n registry: {} as IFormModel[\"registry\"],\n traverse: vi.fn()\n };\n\n return form;\n}\n\n// ---------------------------------------------------------------------------\n// Mock use cases\n// ---------------------------------------------------------------------------\n\nclass MockListFoldersUseCase implements ListFoldersUseCase.Interface {\n execute = vi.fn(async () => {});\n}\n\nclass MockCreateFolderUseCase implements CreateFolderUseCase.Interface {\n execute = vi.fn(async () => {});\n}\n\nclass MockUpdateFolderUseCase implements UpdateFolderUseCase.Interface {\n execute = vi.fn(async () => {});\n}\n\nclass MockDeleteFolderUseCase implements DeleteFolderUseCase.Interface {\n execute = vi.fn(async () => {});\n}\n\nclass MockFormModelFactory {\n create = vi.fn((_config: IFormModelConfig) => createMockFormModel());\n}\n\n// ---------------------------------------------------------------------------\n// Setup\n// ---------------------------------------------------------------------------\n\nfunction createTestPresenter(folders: Folder[] = []) {\n const container = new Container();\n const foldersCache = new ListCache<Folder>();\n const listFolders = new MockListFoldersUseCase();\n const createFolderUseCase = new MockCreateFolderUseCase();\n const updateFolderUseCase = new MockUpdateFolderUseCase();\n const deleteFolderUseCase = new MockDeleteFolderUseCase();\n const formModelFactory = new MockFormModelFactory();\n\n // Pre-populate cache.\n if (folders.length > 0) {\n foldersCache.addItems(folders);\n }\n\n container.registerInstance(FoldersContext, { type: TYPE });\n container.registerInstance(FoldersCache, foldersCache);\n container.registerInstance(ListFoldersUseCase, listFolders);\n container.registerInstance(CreateFolderUseCase, createFolderUseCase);\n container.registerInstance(UpdateFolderUseCase, updateFolderUseCase);\n container.registerInstance(DeleteFolderUseCase, deleteFolderUseCase);\n container.registerInstance(ListFoldersByParentIdsUseCase, {\n execute: vi.fn().mockResolvedValue(undefined)\n });\n container.registerInstance(GetFolderAncestorsUseCase, {\n execute: vi.fn().mockReturnValue([])\n });\n container.registerInstance(GetFolderLevelPermissionUseCase, {\n execute: vi.fn().mockReturnValue(true)\n });\n container.registerInstance(\n FormModelFactory,\n formModelFactory as unknown as FormModelFactory.Interface\n );\n\n FolderTreePresenterFeature.register(container);\n\n const presenter = container.resolve(Abstraction);\n\n return {\n presenter,\n foldersCache,\n listFolders,\n createFolderUseCase,\n updateFolderUseCase,\n deleteFolderUseCase,\n formModelFactory\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\ndescribe(\"FolderTreePresenter\", () => {\n // -------------------------------------------------------------------\n // Scoped initialization via FoldersContext\n // -------------------------------------------------------------------\n\n describe(\"scoped initialization\", () => {\n it(\"should load folders on construction via ListFoldersUseCase\", () => {\n const { listFolders } = createTestPresenter();\n expect(listFolders.execute).toHaveBeenCalledTimes(1);\n });\n\n it(\"should not require an init(type) method — type comes from FoldersContext\", () => {\n const { presenter } = createTestPresenter();\n // The presenter has no init method — it reads type from injected FoldersContext.\n expect((presenter as any).init).toBeUndefined();\n });\n\n it(\"should start with loading=true then resolve to false\", async () => {\n const { presenter } = createTestPresenter();\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n });\n });\n\n // -------------------------------------------------------------------\n // Tree computation from flat folder list\n // -------------------------------------------------------------------\n\n describe(\"tree computation\", () => {\n it(\"should build a nested tree from a flat folder list\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n const { tree } = presenter.vm;\n\n // Two root nodes.\n expect(tree).toHaveLength(2);\n expect(tree[0].name).toBe(\"Documents\");\n expect(tree[1].name).toBe(\"Images\");\n\n // Documents has 2 children.\n expect(tree[0].children).toHaveLength(2);\n expect(tree[0].children[0].name).toBe(\"Invoices\");\n expect(tree[0].children[1].name).toBe(\"Contracts\");\n\n // Invoices has 1 grandchild.\n expect(tree[0].children[0].children).toHaveLength(1);\n expect(tree[0].children[0].children[0].name).toBe(\"2024\");\n\n // Images has no children.\n expect(tree[1].children).toHaveLength(0);\n });\n\n it(\"should return an empty tree when cache is empty\", async () => {\n const { presenter } = createTestPresenter([]);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n expect(presenter.vm.tree).toEqual([]);\n });\n\n it(\"should treat folders with unknown parentId as root nodes\", async () => {\n const folders = [\n createFolder({\n id: \"orphan\",\n title: \"Orphan\",\n slug: \"orphan\",\n parentId: \"non-existent\"\n })\n ];\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n expect(presenter.vm.tree).toHaveLength(1);\n expect(presenter.vm.tree[0].name).toBe(\"Orphan\");\n });\n\n it(\"should map folder properties correctly to tree nodes\", async () => {\n const folders = [\n createFolder({\n id: \"f1\",\n title: \"My Folder\",\n slug: \"my-folder\",\n parentId: null\n })\n ];\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n const node = presenter.vm.tree[0];\n expect(node.id).toBe(\"f1\");\n expect(node.name).toBe(\"My Folder\");\n expect(node.slug).toBe(\"my-folder\");\n expect(node.parentId).toBeNull();\n expect(node.children).toEqual([]);\n });\n });\n\n // -------------------------------------------------------------------\n // selectFolder and onFolderChange\n // -------------------------------------------------------------------\n\n describe(\"selectFolder\", () => {\n it(\"should update currentFolderId\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n expect(presenter.vm.currentFolderId).toBeNull();\n\n presenter.selectFolder(\"root-1\");\n expect(presenter.vm.currentFolderId).toBe(\"root-1\");\n });\n\n it(\"should update currentFolder to the matching folder\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"child-1\");\n expect(presenter.vm.currentFolder).not.toBeNull();\n expect(presenter.vm.currentFolder!.id).toBe(\"child-1\");\n expect(presenter.vm.currentFolder!.title).toBe(\"Invoices\");\n });\n\n it(\"should set currentFolder to null when selecting root (null)\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"root-1\");\n expect(presenter.vm.currentFolder).not.toBeNull();\n\n presenter.selectFolder(null);\n expect(presenter.vm.currentFolderId).toBeNull();\n expect(presenter.vm.currentFolder).toBeNull();\n });\n\n it(\"should find deeply nested folders as currentFolder\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"grandchild-1\");\n expect(presenter.vm.currentFolder).not.toBeNull();\n expect(presenter.vm.currentFolder!.title).toBe(\"2024\");\n });\n });\n\n describe(\"onFolderChange\", () => {\n it(\"should fire callbacks when currentFolderId changes\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n const callback = vi.fn();\n presenter.onFolderChange(callback);\n\n presenter.selectFolder(\"root-1\");\n\n // MobX reaction fires synchronously for simple observable changes.\n expect(callback).toHaveBeenCalledTimes(1);\n expect(callback).toHaveBeenCalledWith(\"root-1\");\n });\n\n it(\"should fire callback with null when navigating to root\", async () => {\n const folders = createFlatFolders();\n const { presenter } = createTestPresenter(folders);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"root-1\");\n\n const callback = vi.fn();\n presenter.onFolderChange(callback);\n\n presenter.selectFolder(null);\n expect(callback).toHaveBeenCalledWith(null);\n });\n\n it(\"should support multiple callbacks\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n const cb1 = vi.fn();\n const cb2 = vi.fn();\n presenter.onFolderChange(cb1);\n presenter.onFolderChange(cb2);\n\n presenter.selectFolder(\"root-2\");\n\n expect(cb1).toHaveBeenCalledWith(\"root-2\");\n expect(cb2).toHaveBeenCalledWith(\"root-2\");\n });\n\n it(\"should return an unsubscribe function that stops callbacks\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n const callback = vi.fn();\n const unsubscribe = presenter.onFolderChange(callback);\n\n presenter.selectFolder(\"root-1\");\n expect(callback).toHaveBeenCalledTimes(1);\n\n unsubscribe();\n\n presenter.selectFolder(\"root-2\");\n // Should not have been called again after unsubscribe.\n expect(callback).toHaveBeenCalledTimes(1);\n });\n\n it(\"should not fire callback when selecting the same folder\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"root-1\");\n\n const callback = vi.fn();\n presenter.onFolderChange(callback);\n\n // Select the same folder again.\n presenter.selectFolder(\"root-1\");\n expect(callback).not.toHaveBeenCalled();\n });\n });\n\n // -------------------------------------------------------------------\n // createFolder / editFolder — operation state with FormModel\n // -------------------------------------------------------------------\n\n describe(\"createFolder\", () => {\n it(\"should set operation state with mode 'create' and a FormModel\", async () => {\n const { presenter, formModelFactory } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.createFolder(\"root-1\");\n\n expect(presenter.vm.operation.active).toBe(true);\n expect(presenter.vm.operation.mode).toBe(\"create\");\n expect(presenter.vm.operation.parentFolderId).toBe(\"root-1\");\n expect(presenter.vm.operation.form).toBeDefined();\n expect(formModelFactory.create).toHaveBeenCalledTimes(1);\n });\n\n it(\"should set parentFolderId to undefined when creating at root\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.createFolder();\n\n expect(presenter.vm.operation.active).toBe(true);\n expect(presenter.vm.operation.mode).toBe(\"create\");\n expect(presenter.vm.operation.parentFolderId).toBeUndefined();\n });\n\n it(\"should wire submitOperation to CreateFolderUseCase\", async () => {\n const { presenter, createFolderUseCase, listFolders } =\n createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.createFolder(\"root-1\");\n\n await presenter.submitOperation();\n\n expect(createFolderUseCase.execute).toHaveBeenCalledTimes(1);\n expect(listFolders.execute).toHaveBeenCalledTimes(1);\n });\n });\n\n describe(\"editFolder\", () => {\n it(\"should set operation state with mode 'edit' and a FormModel\", async () => {\n const { presenter, formModelFactory } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.editFolder(\"root-1\");\n\n expect(presenter.vm.operation.active).toBe(true);\n expect(presenter.vm.operation.mode).toBe(\"edit\");\n expect(presenter.vm.operation.folderId).toBe(\"root-1\");\n expect(presenter.vm.operation.form).toBeDefined();\n expect(formModelFactory.create).toHaveBeenCalledTimes(1);\n });\n\n it(\"should pre-populate form with existing folder data\", async () => {\n const mockForm = createMockFormModel();\n const { presenter, formModelFactory } = createTestPresenter(createFlatFolders());\n formModelFactory.create.mockReturnValue(mockForm);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.editFolder(\"root-1\");\n\n expect(mockForm.setData).toHaveBeenCalledWith({\n title: \"Documents\",\n slug: \"documents\"\n });\n });\n\n it(\"should do nothing when editing a non-existent folder\", async () => {\n const { presenter, formModelFactory } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.editFolder(\"non-existent\");\n\n expect(presenter.vm.operation.active).toBe(false);\n expect(presenter.vm.operation.mode).toBeNull();\n expect(formModelFactory.create).not.toHaveBeenCalled();\n });\n });\n\n // -------------------------------------------------------------------\n // deleteFolder — navigates to parent and updates tree\n // -------------------------------------------------------------------\n\n describe(\"deleteFolder\", () => {\n it(\"should call DeleteFolderUseCase with the folder id\", async () => {\n const { presenter, deleteFolderUseCase } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n await presenter.deleteFolder(\"child-1\");\n\n expect(deleteFolderUseCase.execute).toHaveBeenCalledWith(\"child-1\");\n });\n\n it(\"should set operation state with mode 'delete' during deletion\", async () => {\n const { presenter, deleteFolderUseCase } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n // Make delete hang so we can inspect intermediate state.\n let resolveDelete!: () => void;\n deleteFolderUseCase.execute.mockReturnValueOnce(\n new Promise<void>(resolve => {\n resolveDelete = resolve;\n })\n );\n\n const deletePromise = presenter.deleteFolder(\"child-1\");\n\n expect(presenter.vm.operation.active).toBe(true);\n expect(presenter.vm.operation.mode).toBe(\"delete\");\n expect(presenter.vm.operation.folderId).toBe(\"child-1\");\n\n resolveDelete();\n await deletePromise;\n\n // After completion, operation should be reset.\n expect(presenter.vm.operation.active).toBe(false);\n expect(presenter.vm.operation.mode).toBeNull();\n });\n\n it(\"should navigate to parent folder after deleting the currently open folder\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n // child-1 has parentId \"root-1\".\n presenter.selectFolder(\"child-1\");\n await presenter.deleteFolder(\"child-1\");\n\n expect(presenter.vm.currentFolderId).toBe(\"root-1\");\n });\n\n it(\"should navigate to root (null) when deleting the currently open root folder\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n // root-1 has parentId null.\n presenter.selectFolder(\"root-1\");\n await presenter.deleteFolder(\"root-1\");\n\n expect(presenter.vm.currentFolderId).toBeNull();\n });\n\n it(\"should not change currentFolderId when deleting a folder that isn't open\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"root-2\");\n await presenter.deleteFolder(\"child-1\");\n\n expect(presenter.vm.currentFolderId).toBe(\"root-2\");\n });\n\n it(\"should remove the deleted folder from the cache without refetching from the API\", async () => {\n const { presenter, listFolders } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n await presenter.deleteFolder(\"child-1\");\n\n // Only the initial load — deletion updates the cache directly, since\n // refetching would be unreliable while storage is eventually consistent.\n expect(listFolders.execute).toHaveBeenCalledTimes(1);\n expect(presenter.vm.folders.find(f => f.id === \"child-1\")).toBeUndefined();\n });\n\n it(\"should fire onFolderChange callback when navigating to parent after deleting the open folder\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"child-1\");\n\n const callback = vi.fn();\n presenter.onFolderChange(callback);\n\n // child-1 has parentId \"root-1\".\n await presenter.deleteFolder(\"child-1\");\n\n expect(callback).toHaveBeenCalledWith(\"root-1\");\n });\n\n it(\"should resolve the label for the parent folder after deleting the currently open folder\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n // grandchild-1's parent is child-1, which must resolve (not fall back to \"All Files\").\n presenter.selectFolder(\"grandchild-1\");\n await presenter.deleteFolder(\"grandchild-1\");\n\n expect(presenter.vm.currentFolderId).toBe(\"child-1\");\n expect(presenter.vm.currentFolderTitle).toBe(\"Invoices\");\n });\n });\n\n describe(\"stale currentFolderId validation\", () => {\n it(\"should fall back to root when the initial currentFolderId no longer exists\", async () => {\n const { presenter } = createTestPresenter(createFlatFolders());\n\n // Simulate a consumer selecting a stale (already-deleted) folder before the\n // initial load resolves, e.g. from a URL param or localStorage value.\n presenter.selectFolder(\"does-not-exist\");\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n expect(presenter.vm.currentFolderId).toBeNull();\n });\n\n it(\"should fall back to the nearest existing ancestor when a deeply nested folder was deleted in another session\", async () => {\n const { presenter, foldersCache, listFolders, updateFolderUseCase } =\n createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n // Simulate having navigated into grandchild-1 in a previous session.\n presenter.selectFolder(\"grandchild-1\");\n expect(presenter.vm.currentFolderId).toBe(\"grandchild-1\");\n\n // Simulate grandchild-1 and child-1 both having been deleted server-side\n // (e.g. from another tab), and a reload only bringing back the surviving folders.\n listFolders.execute.mockImplementationOnce(async () => {\n foldersCache.removeItems(f => f.id === \"grandchild-1\" || f.id === \"child-1\");\n });\n\n // Trigger a reload via an unrelated action (moveFolder), simulating any\n // action that causes the presenter to refresh its folder list.\n await presenter.moveFolder(\"root-2\", \"root-1\");\n expect(updateFolderUseCase.execute).toHaveBeenCalledTimes(1);\n\n // Falls back past the also-deleted immediate parent (child-1) to root-1.\n expect(presenter.vm.currentFolderId).toBe(\"root-1\");\n });\n });\n\n describe(\"external cache deletion (e.g. useDeleteFolder bypassing presenter)\", () => {\n it(\"should redirect to parent when the current folder is removed from cache externally\", async () => {\n const { presenter, foldersCache } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"child-1\");\n expect(presenter.vm.currentFolderId).toBe(\"child-1\");\n\n foldersCache.removeItems(f => f.id === \"child-1\");\n\n expect(presenter.vm.currentFolderId).toBe(\"root-1\");\n });\n\n it(\"should fire onFolderChange callback on external cache deletion\", async () => {\n const { presenter, foldersCache } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"child-1\");\n\n const callback = vi.fn();\n presenter.onFolderChange(callback);\n\n foldersCache.removeItems(f => f.id === \"child-1\");\n\n expect(callback).toHaveBeenCalledWith(\"root-1\");\n });\n\n it(\"should redirect to root when a root-level folder is removed externally\", async () => {\n const { presenter, foldersCache } = createTestPresenter(createFlatFolders());\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"root-1\");\n\n foldersCache.removeItems(f => f.id === \"root-1\");\n\n expect(presenter.vm.currentFolderId).toBeNull();\n });\n\n it(\"should redirect to root when the last remaining folder is removed externally\", async () => {\n const singleFolder = [\n createFolder({\n id: \"root-1\",\n title: \"Documents\",\n slug: \"documents\",\n parentId: null\n })\n ];\n const { presenter, foldersCache } = createTestPresenter(singleFolder);\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n presenter.selectFolder(\"root-1\");\n expect(presenter.vm.currentFolderId).toBe(\"root-1\");\n\n // Cache goes from 1 item to 0 — must still redirect, not get stuck on a\n // stale currentFolderId just because the cache is now empty.\n foldersCache.removeItems(f => f.id === \"root-1\");\n\n expect(presenter.vm.currentFolderId).toBeNull();\n });\n });\n\n // -------------------------------------------------------------------\n // Operation state reset\n // -------------------------------------------------------------------\n\n describe(\"operation state\", () => {\n it(\"should start with inactive operation state\", async () => {\n const { presenter } = createTestPresenter();\n\n await vi.waitFor(() => {\n expect(presenter.vm.loading).toBe(false);\n });\n\n expect(presenter.vm.operation.active).toBe(false);\n expect(presenter.vm.operation.mode).toBeNull();\n });\n });\n});\n"],"names":["TYPE","createFolder","data","Folder","createFlatFolders","createMockField","value","vi","v","createMockFormModel","fields","Map","form","name","d","MockListFoldersUseCase","MockCreateFolderUseCase","MockUpdateFolderUseCase","MockDeleteFolderUseCase","MockFormModelFactory","_config","createTestPresenter","folders","container","Container","foldersCache","ListCache","listFolders","createFolderUseCase","updateFolderUseCase","deleteFolderUseCase","formModelFactory","FoldersContext","FoldersCache","ListFoldersUseCase","CreateFolderUseCase","UpdateFolderUseCase","DeleteFolderUseCase","ListFoldersByParentIdsUseCase","undefined","GetFolderAncestorsUseCase","GetFolderLevelPermissionUseCase","FormModelFactory","FolderTreePresenterFeature","presenter","Abstraction","describe","it","expect","tree","node","callback","cb1","cb2","unsubscribe","mockForm","resolveDelete","Promise","resolve","deletePromise","f","singleFolder"],"mappings":"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"}