@strapi/upload 5.50.2 → 5.51.1
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.
- package/dist/admin/future/pages/Assets/AssetsPage.js +201 -137
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +202 -138
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +161 -30
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +163 -32
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +222 -59
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +223 -60
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +252 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +250 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +209 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +207 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +111 -36
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +113 -38
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js +120 -13
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs +120 -13
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +71 -0
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +69 -0
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.js +34 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.mjs +32 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.js +15 -0
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.mjs +13 -1
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +19 -25
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +19 -25
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.js +35 -0
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.mjs +33 -0
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/EmptyState.js +63 -0
- package/dist/admin/future/pages/Assets/components/EmptyState.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/EmptyState.mjs +61 -0
- package/dist/admin/future/pages/Assets/components/EmptyState.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +120 -22
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +121 -23
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.js +22 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.mjs +19 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.js +165 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs +163 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +59 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +55 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.js +124 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.mjs +120 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.js +29 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs +27 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.js +84 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.mjs +81 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/selection.js +104 -0
- package/dist/admin/future/pages/Assets/utils/selection.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/selection.mjs +94 -0
- package/dist/admin/future/pages/Assets/utils/selection.mjs.map +1 -0
- package/dist/admin/future/services/assets.js +33 -1
- package/dist/admin/future/services/assets.js.map +1 -1
- package/dist/admin/future/services/assets.mjs +33 -2
- package/dist/admin/future/services/assets.mjs.map +1 -1
- package/dist/admin/future/services/folders.js +3 -3
- package/dist/admin/future/services/folders.js.map +1 -1
- package/dist/admin/future/services/folders.mjs +3 -3
- package/dist/admin/future/services/folders.mjs.map +1 -1
- package/dist/admin/future/services/uploadFileViaXHR.js +23 -2
- package/dist/admin/future/services/uploadFileViaXHR.js.map +1 -1
- package/dist/admin/future/services/uploadFileViaXHR.mjs +23 -2
- package/dist/admin/future/services/uploadFileViaXHR.mjs.map +1 -1
- package/dist/admin/future/utils/buildDragSetFromSelection.js +33 -0
- package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -0
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs +31 -0
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -0
- package/dist/admin/future/utils/canDropItemOnFolder.js +4 -0
- package/dist/admin/future/utils/canDropItemOnFolder.js.map +1 -1
- package/dist/admin/future/utils/canDropItemOnFolder.mjs +4 -0
- package/dist/admin/future/utils/canDropItemOnFolder.mjs.map +1 -1
- package/dist/admin/future/utils/flattenFolderStructure.js +28 -0
- package/dist/admin/future/utils/flattenFolderStructure.js.map +1 -0
- package/dist/admin/future/utils/flattenFolderStructure.mjs +26 -0
- package/dist/admin/future/utils/flattenFolderStructure.mjs.map +1 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.js +19 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +17 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -0
- package/dist/admin/future/utils/getFolderLabel.js +31 -0
- package/dist/admin/future/utils/getFolderLabel.js.map +1 -0
- package/dist/admin/future/utils/getFolderLabel.mjs +29 -0
- package/dist/admin/future/utils/getFolderLabel.mjs.map +1 -0
- package/dist/admin/hooks/useUpload.js +15 -9
- package/dist/admin/hooks/useUpload.js.map +1 -1
- package/dist/admin/hooks/useUpload.mjs +17 -11
- package/dist/admin/hooks/useUpload.mjs.map +1 -1
- package/dist/admin/src/future/pages/Assets/components/AssetsGrid.d.ts +1 -1
- package/dist/admin/src/future/pages/Assets/components/AssetsTable.d.ts +7 -1
- package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +1 -0
- package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +13 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +2 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/DragOverlayChip.d.ts +2 -2
- package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +17 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/computeValidDropTargets.d.ts +7 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/dndIds.d.ts +3 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +3 -1
- package/dist/admin/src/future/pages/Assets/components/Dnd/useFolderTreeDroppable.d.ts +19 -0
- package/dist/admin/src/future/pages/Assets/components/EmptyState.d.ts +11 -0
- package/dist/admin/src/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.d.ts +6 -0
- package/dist/admin/src/future/pages/Assets/components/SortMenu.d.ts +17 -0
- package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +39 -0
- package/dist/admin/src/future/pages/Assets/hooks/useListSort.d.ts +47 -0
- package/dist/admin/src/future/pages/Assets/utils/listQueryKey.d.ts +30 -0
- package/dist/admin/src/future/pages/Assets/utils/mergeMixedList.d.ts +40 -0
- package/dist/admin/src/future/pages/Assets/utils/selection.d.ts +50 -0
- package/dist/admin/src/future/services/assets.d.ts +4 -1
- package/dist/admin/src/future/services/folders.d.ts +4 -1
- package/dist/admin/src/future/services/uploadFileViaXHR.d.ts +1 -17
- package/dist/admin/src/future/types/dnd.d.ts +5 -0
- package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +11 -0
- package/dist/admin/src/future/utils/canDropItemOnFolder.d.ts +1 -1
- package/dist/admin/src/future/utils/flattenFolderStructure.d.ts +18 -0
- package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +14 -0
- package/dist/admin/src/future/utils/getFolderLabel.d.ts +11 -0
- package/dist/admin/translations/en.json.js +36 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +36 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/shared/contracts/folders.d.ts +1 -1
- package/package.json +8 -8
|
@@ -151,10 +151,41 @@ const assetsApi = uploadApi.injectEndpoints({
|
|
|
151
151
|
id: 'LIST'
|
|
152
152
|
}
|
|
153
153
|
]
|
|
154
|
+
}),
|
|
155
|
+
/**
|
|
156
|
+
* Permanently delete several assets and/or folders in one request. Same
|
|
157
|
+
* endpoint as the legacy bulk remove (`POST /upload/actions/bulk-delete`,
|
|
158
|
+
* handled by `admin-folder-file.deleteMany`) so server behaviour is
|
|
159
|
+
* unchanged — folder deletion cascades to everything inside.
|
|
160
|
+
*/ bulkDeleteItems: builder.mutation({
|
|
161
|
+
query: ({ fileIds, folderIds })=>({
|
|
162
|
+
url: '/upload/actions/bulk-delete',
|
|
163
|
+
method: 'POST',
|
|
164
|
+
data: {
|
|
165
|
+
fileIds,
|
|
166
|
+
folderIds
|
|
167
|
+
}
|
|
168
|
+
}),
|
|
169
|
+
invalidatesTags: [
|
|
170
|
+
{
|
|
171
|
+
type: 'Asset',
|
|
172
|
+
id: 'LIST'
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
type: 'Folder',
|
|
176
|
+
id: 'LIST'
|
|
177
|
+
},
|
|
178
|
+
// The sidebar FolderTree reads /upload/folder-structure, which carries
|
|
179
|
+
// its own tag — without it, deleted folders linger in the tree.
|
|
180
|
+
{
|
|
181
|
+
type: 'Folder',
|
|
182
|
+
id: 'STRUCTURE'
|
|
183
|
+
}
|
|
184
|
+
]
|
|
154
185
|
})
|
|
155
186
|
})
|
|
156
187
|
});
|
|
157
|
-
const { useGetAssetsQuery, useGetAssetQuery, useUpdateAssetMutation, useReplaceAssetMutation, useDeleteAssetMutation } = assetsApi;
|
|
188
|
+
const { useGetAssetsQuery, useGetAssetQuery, useUpdateAssetMutation, useReplaceAssetMutation, useDeleteAssetMutation, useBulkDeleteItemsMutation } = assetsApi;
|
|
158
189
|
|
|
159
|
-
export { useDeleteAssetMutation, useGetAssetQuery, useGetAssetsQuery, useReplaceAssetMutation, useUpdateAssetMutation };
|
|
190
|
+
export { useBulkDeleteItemsMutation, useDeleteAssetMutation, useGetAssetQuery, useGetAssetsQuery, useReplaceAssetMutation, useUpdateAssetMutation };
|
|
160
191
|
//# sourceMappingURL=assets.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets.mjs","sources":["../../../../admin/src/future/services/assets.ts"],"sourcesContent":["import { uploadApi } from './api';\n\nimport type {\n GetFiles,\n File,\n Pagination,\n UploadFileInfo,\n AssetWithPopulatedCreatedBy,\n} from '../../../../shared/contracts/files';\n\ninterface GetAssetsParams {\n page?: number;\n pageSize?: number;\n folder?: number | null;\n sort?: string;\n}\n\ninterface GetAssetsResponse {\n results: File[];\n pagination: Pagination;\n}\n\ninterface UpdateAssetArgs {\n id: number;\n fileInfo: Partial<UploadFileInfo>;\n}\n\ninterface ReplaceAssetArgs {\n id: number;\n // `File` is shadowed in this module by the asset-file contract type; the\n // global browser File is what we need for FormData.\n file: globalThis.File;\n fileInfo?: Partial<UploadFileInfo>;\n}\n\nconst assetsApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n getAssets: builder.query<GetAssetsResponse, GetAssetsParams | void>({\n query: (params = {}) => {\n const { folder, ...rest } = params as GetAssetsParams;\n\n const queryParams: Record<string, unknown> = { ...rest };\n\n if (folder != null) {\n queryParams['filters'] = {\n $and: [{ folder: { id: folder } }],\n };\n } else {\n queryParams['filters'] = {\n $and: [{ folder: { id: { $null: true } } }],\n };\n }\n\n return {\n url: '/upload/files',\n method: 'GET',\n config: { params: queryParams },\n };\n },\n transformResponse: (response: GetFiles.Response['data']) => response,\n providesTags: (result) =>\n result\n ? [\n ...result.results.map(({ id }) => ({ type: 'Asset' as const, id })),\n { type: 'Asset', id: 'LIST' },\n ]\n : [{ type: 'Asset', id: 'LIST' }],\n }),\n getAsset: builder.query<AssetWithPopulatedCreatedBy, number>({\n query: (id) => ({\n url: `/upload/files/${id}`,\n method: 'GET',\n }),\n providesTags: (_result, _error, id) => [{ type: 'Asset' as const, id }],\n }),\n /**\n * Update the editable metadata of an existing asset.\n * Hits the legacy `POST /upload?id=<id>` endpoint which dispatches to\n * `admin-upload.updateFileInfo`.\n */\n updateAsset: builder.mutation<AssetWithPopulatedCreatedBy, UpdateAssetArgs>({\n query: ({ id, fileInfo }) => {\n const formData = new FormData();\n formData.append('fileInfo', JSON.stringify(fileInfo));\n\n return {\n url: '/upload',\n method: 'POST',\n data: formData,\n config: { params: { id } },\n };\n },\n invalidatesTags: (_result, _error, { id }) => [\n { type: 'Asset' as const, id },\n { type: 'Asset' as const, id: 'LIST' },\n ],\n }),\n /**\n * Replace the binary content of an existing asset.\n * Hits `POST /upload?id=<id>` with a multipart body — the controller\n * dispatches to `admin-upload.replaceFile` when a `files` part is present.\n * Uses the standard axios baseQuery (no streaming) since we only ever\n * replace one file at a time and don't need per-byte progress here.\n */\n replaceAsset: builder.mutation<AssetWithPopulatedCreatedBy, ReplaceAssetArgs>({\n query: ({ id, file, fileInfo }) => {\n const formData = new FormData();\n formData.append('files', file);\n if (fileInfo) {\n formData.append('fileInfo', JSON.stringify(fileInfo));\n }\n return {\n url: '/upload',\n method: 'POST',\n data: formData,\n config: { params: { id } },\n };\n },\n invalidatesTags: (_result, _error, { id }) => [\n { type: 'Asset' as const, id },\n { type: 'Asset' as const, id: 'LIST' },\n ],\n }),\n /**\n * Permanently delete an asset by id. Hits the same endpoint as the legacy\n * `useRemoveAsset` hook so server behaviour is unchanged.\n */\n deleteAsset: builder.mutation<unknown, number>({\n query: (id) => ({\n url: `/upload/files/${id}`,\n method: 'DELETE',\n }),\n invalidatesTags: (_result, _error, id) => [\n { type: 'Asset' as const, id },\n { type: 'Asset' as const, id: 'LIST' },\n ],\n }),\n }),\n});\n\nexport const {\n useGetAssetsQuery,\n useGetAssetQuery,\n useUpdateAssetMutation,\n useReplaceAssetMutation,\n useDeleteAssetMutation,\n} = assetsApi;\n"],"names":["assetsApi","uploadApi","injectEndpoints","endpoints","builder","getAssets","query","params","folder","rest","queryParams","$and","id","$null","url","method","config","transformResponse","response","providesTags","result","results","map","type","getAsset","_result","_error","updateAsset","mutation","fileInfo","formData","FormData","append","JSON","stringify","data","invalidatesTags","replaceAsset","file","deleteAsset","useGetAssetsQuery","useGetAssetQuery","useUpdateAssetMutation","useReplaceAssetMutation","useDeleteAssetMutation"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"assets.mjs","sources":["../../../../admin/src/future/services/assets.ts"],"sourcesContent":["import { uploadApi } from './api';\n\nimport type {\n GetFiles,\n File,\n Pagination,\n UploadFileInfo,\n AssetWithPopulatedCreatedBy,\n} from '../../../../shared/contracts/files';\n\ninterface GetAssetsParams {\n page?: number;\n pageSize?: number;\n folder?: number | null;\n sort?: string;\n}\n\ninterface GetAssetsResponse {\n results: File[];\n pagination: Pagination;\n}\n\ninterface UpdateAssetArgs {\n id: number;\n fileInfo: Partial<UploadFileInfo>;\n}\n\ninterface ReplaceAssetArgs {\n id: number;\n // `File` is shadowed in this module by the asset-file contract type; the\n // global browser File is what we need for FormData.\n file: globalThis.File;\n fileInfo?: Partial<UploadFileInfo>;\n}\n\nconst assetsApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n getAssets: builder.query<GetAssetsResponse, GetAssetsParams | void>({\n query: (params = {}) => {\n const { folder, ...rest } = params as GetAssetsParams;\n\n const queryParams: Record<string, unknown> = { ...rest };\n\n if (folder != null) {\n queryParams['filters'] = {\n $and: [{ folder: { id: folder } }],\n };\n } else {\n queryParams['filters'] = {\n $and: [{ folder: { id: { $null: true } } }],\n };\n }\n\n return {\n url: '/upload/files',\n method: 'GET',\n config: { params: queryParams },\n };\n },\n transformResponse: (response: GetFiles.Response['data']) => response,\n providesTags: (result) =>\n result\n ? [\n ...result.results.map(({ id }) => ({ type: 'Asset' as const, id })),\n { type: 'Asset', id: 'LIST' },\n ]\n : [{ type: 'Asset', id: 'LIST' }],\n }),\n getAsset: builder.query<AssetWithPopulatedCreatedBy, number>({\n query: (id) => ({\n url: `/upload/files/${id}`,\n method: 'GET',\n }),\n providesTags: (_result, _error, id) => [{ type: 'Asset' as const, id }],\n }),\n /**\n * Update the editable metadata of an existing asset.\n * Hits the legacy `POST /upload?id=<id>` endpoint which dispatches to\n * `admin-upload.updateFileInfo`.\n */\n updateAsset: builder.mutation<AssetWithPopulatedCreatedBy, UpdateAssetArgs>({\n query: ({ id, fileInfo }) => {\n const formData = new FormData();\n formData.append('fileInfo', JSON.stringify(fileInfo));\n\n return {\n url: '/upload',\n method: 'POST',\n data: formData,\n config: { params: { id } },\n };\n },\n invalidatesTags: (_result, _error, { id }) => [\n { type: 'Asset' as const, id },\n { type: 'Asset' as const, id: 'LIST' },\n ],\n }),\n /**\n * Replace the binary content of an existing asset.\n * Hits `POST /upload?id=<id>` with a multipart body — the controller\n * dispatches to `admin-upload.replaceFile` when a `files` part is present.\n * Uses the standard axios baseQuery (no streaming) since we only ever\n * replace one file at a time and don't need per-byte progress here.\n */\n replaceAsset: builder.mutation<AssetWithPopulatedCreatedBy, ReplaceAssetArgs>({\n query: ({ id, file, fileInfo }) => {\n const formData = new FormData();\n formData.append('files', file);\n if (fileInfo) {\n formData.append('fileInfo', JSON.stringify(fileInfo));\n }\n return {\n url: '/upload',\n method: 'POST',\n data: formData,\n config: { params: { id } },\n };\n },\n invalidatesTags: (_result, _error, { id }) => [\n { type: 'Asset' as const, id },\n { type: 'Asset' as const, id: 'LIST' },\n ],\n }),\n /**\n * Permanently delete an asset by id. Hits the same endpoint as the legacy\n * `useRemoveAsset` hook so server behaviour is unchanged.\n */\n deleteAsset: builder.mutation<unknown, number>({\n query: (id) => ({\n url: `/upload/files/${id}`,\n method: 'DELETE',\n }),\n invalidatesTags: (_result, _error, id) => [\n { type: 'Asset' as const, id },\n { type: 'Asset' as const, id: 'LIST' },\n ],\n }),\n /**\n * Permanently delete several assets and/or folders in one request. Same\n * endpoint as the legacy bulk remove (`POST /upload/actions/bulk-delete`,\n * handled by `admin-folder-file.deleteMany`) so server behaviour is\n * unchanged — folder deletion cascades to everything inside.\n */\n bulkDeleteItems: builder.mutation<unknown, { fileIds: number[]; folderIds: number[] }>({\n query: ({ fileIds, folderIds }) => ({\n url: '/upload/actions/bulk-delete',\n method: 'POST',\n data: { fileIds, folderIds },\n }),\n invalidatesTags: [\n { type: 'Asset' as const, id: 'LIST' },\n { type: 'Folder' as const, id: 'LIST' },\n // The sidebar FolderTree reads /upload/folder-structure, which carries\n // its own tag — without it, deleted folders linger in the tree.\n { type: 'Folder' as const, id: 'STRUCTURE' },\n ],\n }),\n }),\n});\n\nexport const {\n useGetAssetsQuery,\n useGetAssetQuery,\n useUpdateAssetMutation,\n useReplaceAssetMutation,\n useDeleteAssetMutation,\n useBulkDeleteItemsMutation,\n} = assetsApi;\n"],"names":["assetsApi","uploadApi","injectEndpoints","endpoints","builder","getAssets","query","params","folder","rest","queryParams","$and","id","$null","url","method","config","transformResponse","response","providesTags","result","results","map","type","getAsset","_result","_error","updateAsset","mutation","fileInfo","formData","FormData","append","JSON","stringify","data","invalidatesTags","replaceAsset","file","deleteAsset","bulkDeleteItems","fileIds","folderIds","useGetAssetsQuery","useGetAssetQuery","useUpdateAssetMutation","useReplaceAssetMutation","useDeleteAssetMutation","useBulkDeleteItemsMutation"],"mappings":"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"}
|
|
@@ -8,10 +8,10 @@ const foldersApi = api.uploadApi.injectEndpoints({
|
|
|
8
8
|
endpoints: (builder)=>({
|
|
9
9
|
getFolders: builder.query({
|
|
10
10
|
query: (params = {})=>{
|
|
11
|
-
const { parentId } = params;
|
|
11
|
+
const { parentId, sort } = params;
|
|
12
12
|
const queryParams = {
|
|
13
|
-
//
|
|
14
|
-
sort: 'name:ASC'
|
|
13
|
+
// Default matches sidebar FolderTree order (server getStructure uses sortBy('name')).
|
|
14
|
+
sort: sort ?? 'name:ASC'
|
|
15
15
|
};
|
|
16
16
|
if (parentId != null) {
|
|
17
17
|
queryParams['filters'] = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"folders.js","sources":["../../../../admin/src/future/services/folders.ts"],"sourcesContent":["import { uploadApi } from './api';\n\nimport type {\n Folder,\n FolderNode,\n CreateFolders,\n GetFolder,\n GetFolders,\n GetFolderStructure,\n BulkMoveFolders,\n} from '../../../../shared/contracts/folders';\n\nexport type FolderWithCounts = Omit<Folder, 'children' | 'files'> & {\n children?: { count: number };\n files?: { count: number };\n};\n\ninterface GetFoldersParams {\n parentId?: number | null;\n}\n\ninterface BulkMoveParams {\n fileIds?: number[];\n folderIds?: number[];\n destinationFolderId: number;\n}\n\ntype DataEnvelope<T> = {\n data: T;\n};\n\nconst isDataEnvelope = <T>(response: T | DataEnvelope<T>): response is DataEnvelope<T> =>\n typeof response === 'object' && response !== null && 'data' in response;\n\nconst unwrapData = <T>(response: T | DataEnvelope<T>): T =>\n isDataEnvelope(response) ? response.data : response;\n\nconst foldersApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n getFolders: builder.query<Folder[], GetFoldersParams | void>({\n query: (params = {}) => {\n const { parentId } = params as GetFoldersParams;\n\n const queryParams: Record<string, unknown> = {\n // Match sidebar FolderTree order (server getStructure uses sortBy('name')).\n sort: 'name:ASC',\n };\n\n if (parentId != null) {\n queryParams['filters'] = {\n $and: [{ parent: { id: parentId } }],\n };\n } else {\n queryParams['filters'] = {\n $and: [{ parent: { id: { $null: true } } }],\n };\n }\n\n return {\n url: '/upload/folders',\n method: 'GET',\n config: { params: queryParams },\n };\n },\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder', id: 'LIST' },\n ];\n }\n return [{ type: 'Folder', id: 'LIST' }];\n },\n }),\n createFolder: builder.mutation<CreateFolders.Response['data'], CreateFolders.Request['body']>({\n query: (body) => ({\n url: '/upload/folders',\n method: 'POST',\n data: body,\n }),\n transformResponse: (response: CreateFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n /**\n * Hierarchical folder tree used by the FolderTree sidebar. Returned as a\n * single nested array — the server walks the folders table once and\n * assembles parent/child relationships.\n *\n * TODO: filter results like `admin-folder.find` for folder-scoped roles\n * (v1 returns all folders; relies on global `plugin::upload.read`).\n */\n getFolderStructure: builder.query<FolderNode[], void>({\n query: () => ({\n url: '/upload/folder-structure',\n method: 'GET',\n }),\n // TODO: align GetFolderStructure contract with the real /upload/folder-structure\n // envelope and drop this defensive unwrap (data[] vs { data: [] }).\n transformResponse: (response: GetFolderStructure.Response['data']) =>\n ((response as unknown as { data: FolderNode[] })?.data ??\n (response as unknown as FolderNode[]) ??\n []) as FolderNode[],\n providesTags: [{ type: 'Folder', id: 'STRUCTURE' }],\n }),\n /**\n * Flat list of every folder, used to populate the \"Location\" select in the\n * asset details drawer. No parent filter — we want the entire tree in one\n * query so the select can render any destination folder.\n */\n getAllFolders: builder.query<Folder[], void>({\n query: () => ({\n url: '/upload/folders',\n method: 'GET',\n }),\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response ?? []),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder' as const, id: 'LIST' },\n ];\n }\n return [{ type: 'Folder' as const, id: 'LIST' }];\n },\n }),\n getFolder: builder.query<FolderWithCounts, { id: number }>({\n query: ({ id }) => ({\n url: `/upload/folders/${id}`,\n method: 'GET',\n config: {\n params: {\n populate: {\n parent: {\n populate: {\n parent: '*',\n },\n },\n children: { count: true },\n files: { count: true },\n },\n },\n },\n }),\n transformResponse: (response: GetFolder.Response) =>\n response.data as unknown as FolderWithCounts,\n providesTags: (_result, _error, { id }) => [{ type: 'Folder', id }],\n }),\n bulkMove: builder.mutation<BulkMoveFolders.Response['data'], BulkMoveParams>({\n query: ({ fileIds = [], folderIds = [], destinationFolderId }) => ({\n url: '/upload/actions/bulk-move',\n method: 'POST',\n data: { fileIds, folderIds, destinationFolderId },\n }),\n transformResponse: (response: BulkMoveFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Asset', id: 'LIST' },\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n }),\n});\n\nexport const {\n useCreateFolderMutation,\n useGetFoldersQuery,\n useGetFolderQuery,\n useGetAllFoldersQuery,\n useGetFolderStructureQuery,\n useBulkMoveMutation,\n} = foldersApi;\n"],"names":["isDataEnvelope","response","unwrapData","data","foldersApi","uploadApi","injectEndpoints","endpoints","builder","getFolders","query","params","parentId","queryParams","sort","$and","parent","id","$null","url","method","config","transformResponse","providesTags","results","map","type","createFolder","mutation","body","invalidatesTags","getFolderStructure","getAllFolders","getFolder","populate","children","count","files","_result","_error","bulkMove","fileIds","folderIds","destinationFolderId","useCreateFolderMutation","useGetFoldersQuery","useGetFolderQuery","useGetAllFoldersQuery","useGetFolderStructureQuery","useBulkMoveMutation"],"mappings":";;;;AA+BA,MAAMA,cAAAA,GAAiB,CAAIC,QAAAA,GACzB,OAAOA,aAAa,QAAA,IAAYA,QAAAA,KAAa,QAAQ,MAAA,IAAUA,QAAAA;AAEjE,MAAMC,aAAa,CAAID,QAAAA,GACrBD,eAAeC,QAAAA,CAAAA,GAAYA,QAAAA,CAASE,IAAI,GAAGF,QAAAA;AAE7C,MAAMG,UAAAA,GAAaC,aAAAA,CAAUC,eAAe,CAAC;IAC3CC,SAAAA,EAAW,CAACC,WAAa;YACvBC,UAAAA,EAAYD,OAAAA,CAAQE,KAAK,CAAoC;gBAC3DA,KAAAA,EAAO,CAACC,MAAAA,GAAS,EAAE,GAAA;oBACjB,MAAM,EAAEC,QAAQ,EAAE,GAAGD,MAAAA;AAErB,oBAAA,MAAME,WAAAA,GAAuC;;wBAE3CC,IAAAA,EAAM;AACR,qBAAA;AAEA,oBAAA,IAAIF,YAAY,IAAA,EAAM;wBACpBC,WAAW,CAAC,UAAU,GAAG;4BACvBE,IAAAA,EAAM;AAAC,gCAAA;oCAAEC,MAAAA,EAAQ;wCAAEC,EAAAA,EAAIL;AAAS;AAAE;AAAE;AACtC,yBAAA;oBACF,CAAA,MAAO;wBACLC,WAAW,CAAC,UAAU,GAAG;4BACvBE,IAAAA,EAAM;AAAC,gCAAA;oCAAEC,MAAAA,EAAQ;wCAAEC,EAAAA,EAAI;4CAAEC,KAAAA,EAAO;AAAK;AAAE;AAAE;AAAE;AAC7C,yBAAA;AACF,oBAAA;oBAEA,OAAO;wBACLC,GAAAA,EAAK,iBAAA;wBACLC,MAAAA,EAAQ,KAAA;wBACRC,MAAAA,EAAQ;4BAAEV,MAAAA,EAAQE;AAAY;AAChC,qBAAA;AACF,gBAAA,CAAA;gBACAS,iBAAAA,EAAmB,CAACrB,WAClBC,UAAAA,CAAwCD,QAAAA,CAAAA;AAC1CsB,gBAAAA,YAAAA,EAAc,CAACC,OAAAA,GAAAA;AACb,oBAAA,IAAIA,OAAAA,EAAS;wBACX,OAAO;AACFA,4BAAAA,GAAAA,OAAAA,CAAQC,GAAG,CAAC,CAAC,EAAER,EAAE,EAAE,IAAM;oCAAES,IAAAA,EAAM,QAAA;AAAmBT,oCAAAA;iCAAG,CAAA,CAAA;AAC1D,4BAAA;gCAAES,IAAAA,EAAM,QAAA;gCAAUT,EAAAA,EAAI;AAAO;AAC9B,yBAAA;AACH,oBAAA;oBACA,OAAO;AAAC,wBAAA;4BAAES,IAAAA,EAAM,QAAA;4BAAUT,EAAAA,EAAI;AAAO;AAAE,qBAAA;AACzC,gBAAA;AACF,aAAA,CAAA;YACAU,YAAAA,EAAcnB,OAAAA,CAAQoB,QAAQ,CAAgE;gBAC5FlB,KAAAA,EAAO,CAACmB,QAAU;wBAChBV,GAAAA,EAAK,iBAAA;wBACLC,MAAAA,EAAQ,MAAA;wBACRjB,IAAAA,EAAM0B;qBACR,CAAA;gBACAP,iBAAAA,EAAmB,CAACrB,QAAAA,GAAqCA,QAAAA,CAASE,IAAI;gBACtE2B,eAAAA,EAAiB;AACf,oBAAA;wBAAEJ,IAAAA,EAAM,QAAA;wBAAUT,EAAAA,EAAI;AAAO,qBAAA;AAC7B,oBAAA;wBAAES,IAAAA,EAAM,QAAA;wBAAUT,EAAAA,EAAI;AAAY;AACnC;AACH,aAAA,CAAA;AACA;;;;;;;QAQAc,kBAAAA,EAAoBvB,OAAAA,CAAQE,KAAK,CAAqB;AACpDA,gBAAAA,KAAAA,EAAO,KAAO;wBACZS,GAAAA,EAAK,0BAAA;wBACLC,MAAAA,EAAQ;qBACV,CAAA;;;AAGAE,gBAAAA,iBAAAA,EAAmB,CAACrB,QAAAA,GAChBA,QAAAA,EAAgDE,IAAAA,IAC/CF,YACD,EAAE;gBACNsB,YAAAA,EAAc;AAAC,oBAAA;wBAAEG,IAAAA,EAAM,QAAA;wBAAUT,EAAAA,EAAI;AAAY;AAAE;AACrD,aAAA,CAAA;AACA;;;;QAKAe,aAAAA,EAAexB,OAAAA,CAAQE,KAAK,CAAiB;AAC3CA,gBAAAA,KAAAA,EAAO,KAAO;wBACZS,GAAAA,EAAK,iBAAA;wBACLC,MAAAA,EAAQ;qBACV,CAAA;AACAE,gBAAAA,iBAAAA,EAAmB,CAACrB,QAAAA,GAClBC,UAAAA,CAAwCD,QAAAA,IAAY,EAAE,CAAA;AACxDsB,gBAAAA,YAAAA,EAAc,CAACC,OAAAA,GAAAA;AACb,oBAAA,IAAIA,OAAAA,EAAS;wBACX,OAAO;AACFA,4BAAAA,GAAAA,OAAAA,CAAQC,GAAG,CAAC,CAAC,EAAER,EAAE,EAAE,IAAM;oCAAES,IAAAA,EAAM,QAAA;AAAmBT,oCAAAA;iCAAG,CAAA,CAAA;AAC1D,4BAAA;gCAAES,IAAAA,EAAM,QAAA;gCAAmBT,EAAAA,EAAI;AAAO;AACvC,yBAAA;AACH,oBAAA;oBACA,OAAO;AAAC,wBAAA;4BAAES,IAAAA,EAAM,QAAA;4BAAmBT,EAAAA,EAAI;AAAO;AAAE,qBAAA;AAClD,gBAAA;AACF,aAAA,CAAA;YACAgB,SAAAA,EAAWzB,OAAAA,CAAQE,KAAK,CAAmC;AACzDA,gBAAAA,KAAAA,EAAO,CAAC,EAAEO,EAAE,EAAE,IAAM;wBAClBE,GAAAA,EAAK,CAAC,gBAAgB,EAAEF,EAAAA,CAAAA,CAAI;wBAC5BG,MAAAA,EAAQ,KAAA;wBACRC,MAAAA,EAAQ;4BACNV,MAAAA,EAAQ;gCACNuB,QAAAA,EAAU;oCACRlB,MAAAA,EAAQ;wCACNkB,QAAAA,EAAU;4CACRlB,MAAAA,EAAQ;AACV;AACF,qCAAA;oCACAmB,QAAAA,EAAU;wCAAEC,KAAAA,EAAO;AAAK,qCAAA;oCACxBC,KAAAA,EAAO;wCAAED,KAAAA,EAAO;AAAK;AACvB;AACF;AACF;qBACF,CAAA;gBACAd,iBAAAA,EAAmB,CAACrB,QAAAA,GAClBA,QAAAA,CAASE,IAAI;AACfoB,gBAAAA,YAAAA,EAAc,CAACe,OAAAA,EAASC,MAAAA,EAAQ,EAAEtB,EAAE,EAAE,GAAK;AAAC,wBAAA;4BAAES,IAAAA,EAAM,QAAA;AAAUT,4BAAAA;AAAG;AAAE;AACrE,aAAA,CAAA;YACAuB,QAAAA,EAAUhC,OAAAA,CAAQoB,QAAQ,CAAmD;gBAC3ElB,KAAAA,EAAO,CAAC,EAAE+B,OAAAA,GAAU,EAAE,EAAEC,SAAAA,GAAY,EAAE,EAAEC,mBAAmB,EAAE,IAAM;wBACjExB,GAAAA,EAAK,2BAAA;wBACLC,MAAAA,EAAQ,MAAA;wBACRjB,IAAAA,EAAM;AAAEsC,4BAAAA,OAAAA;AAASC,4BAAAA,SAAAA;AAAWC,4BAAAA;AAAoB;qBAClD,CAAA;gBACArB,iBAAAA,EAAmB,CAACrB,QAAAA,GAAuCA,QAAAA,CAASE,IAAI;gBACxE2B,eAAAA,EAAiB;AACf,oBAAA;wBAAEJ,IAAAA,EAAM,OAAA;wBAAST,EAAAA,EAAI;AAAO,qBAAA;AAC5B,oBAAA;wBAAES,IAAAA,EAAM,QAAA;wBAAUT,EAAAA,EAAI;AAAO,qBAAA;AAC7B,oBAAA;wBAAES,IAAAA,EAAM,QAAA;wBAAUT,EAAAA,EAAI;AAAY;AACnC;AACH,aAAA;SACF;AACF,CAAA,CAAA;AAEO,MAAM,EACX2B,uBAAuB,EACvBC,kBAAkB,EAClBC,iBAAiB,EACjBC,qBAAqB,EACrBC,0BAA0B,EAC1BC,mBAAmB,EACpB,GAAG7C;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"folders.js","sources":["../../../../admin/src/future/services/folders.ts"],"sourcesContent":["import { uploadApi } from './api';\n\nimport type {\n Folder,\n FolderNode,\n CreateFolders,\n GetFolder,\n GetFolders,\n GetFolderStructure,\n BulkMoveFolders,\n} from '../../../../shared/contracts/folders';\n\nexport type FolderWithCounts = Omit<Folder, 'children' | 'files'> & {\n children?: { count: number };\n files?: { count: number };\n};\n\ninterface GetFoldersParams {\n parentId?: number | null;\n /** Comma-separated rules, e.g. `updatedAt:DESC,name:ASC`. Defaults to alphabetical. */\n sort?: string;\n}\n\ninterface BulkMoveParams {\n fileIds?: number[];\n folderIds?: number[];\n /** `null` moves the items to the root of the Media Library. */\n destinationFolderId: number | null;\n}\n\ntype DataEnvelope<T> = {\n data: T;\n};\n\nconst isDataEnvelope = <T>(response: T | DataEnvelope<T>): response is DataEnvelope<T> =>\n typeof response === 'object' && response !== null && 'data' in response;\n\nconst unwrapData = <T>(response: T | DataEnvelope<T>): T =>\n isDataEnvelope(response) ? response.data : response;\n\nconst foldersApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n getFolders: builder.query<Folder[], GetFoldersParams | void>({\n query: (params = {}) => {\n const { parentId, sort } = params as GetFoldersParams;\n\n const queryParams: Record<string, unknown> = {\n // Default matches sidebar FolderTree order (server getStructure uses sortBy('name')).\n sort: sort ?? 'name:ASC',\n };\n\n if (parentId != null) {\n queryParams['filters'] = {\n $and: [{ parent: { id: parentId } }],\n };\n } else {\n queryParams['filters'] = {\n $and: [{ parent: { id: { $null: true } } }],\n };\n }\n\n return {\n url: '/upload/folders',\n method: 'GET',\n config: { params: queryParams },\n };\n },\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder', id: 'LIST' },\n ];\n }\n return [{ type: 'Folder', id: 'LIST' }];\n },\n }),\n createFolder: builder.mutation<CreateFolders.Response['data'], CreateFolders.Request['body']>({\n query: (body) => ({\n url: '/upload/folders',\n method: 'POST',\n data: body,\n }),\n transformResponse: (response: CreateFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n /**\n * Hierarchical folder tree used by the FolderTree sidebar. Returned as a\n * single nested array — the server walks the folders table once and\n * assembles parent/child relationships.\n *\n * TODO: filter results like `admin-folder.find` for folder-scoped roles\n * (v1 returns all folders; relies on global `plugin::upload.read`).\n */\n getFolderStructure: builder.query<FolderNode[], void>({\n query: () => ({\n url: '/upload/folder-structure',\n method: 'GET',\n }),\n // TODO: align GetFolderStructure contract with the real /upload/folder-structure\n // envelope and drop this defensive unwrap (data[] vs { data: [] }).\n transformResponse: (response: GetFolderStructure.Response['data']) =>\n ((response as unknown as { data: FolderNode[] })?.data ??\n (response as unknown as FolderNode[]) ??\n []) as FolderNode[],\n providesTags: [{ type: 'Folder', id: 'STRUCTURE' }],\n }),\n /**\n * Flat list of every folder, used to populate the \"Location\" select in the\n * asset details drawer. No parent filter — we want the entire tree in one\n * query so the select can render any destination folder.\n */\n getAllFolders: builder.query<Folder[], void>({\n query: () => ({\n url: '/upload/folders',\n method: 'GET',\n }),\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response ?? []),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder' as const, id: 'LIST' },\n ];\n }\n return [{ type: 'Folder' as const, id: 'LIST' }];\n },\n }),\n getFolder: builder.query<FolderWithCounts, { id: number }>({\n query: ({ id }) => ({\n url: `/upload/folders/${id}`,\n method: 'GET',\n config: {\n params: {\n populate: {\n parent: {\n populate: {\n parent: '*',\n },\n },\n children: { count: true },\n files: { count: true },\n },\n },\n },\n }),\n transformResponse: (response: GetFolder.Response) =>\n response.data as unknown as FolderWithCounts,\n providesTags: (_result, _error, { id }) => [{ type: 'Folder', id }],\n }),\n bulkMove: builder.mutation<BulkMoveFolders.Response['data'], BulkMoveParams>({\n query: ({ fileIds = [], folderIds = [], destinationFolderId }) => ({\n url: '/upload/actions/bulk-move',\n method: 'POST',\n data: { fileIds, folderIds, destinationFolderId },\n }),\n transformResponse: (response: BulkMoveFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Asset', id: 'LIST' },\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n }),\n});\n\nexport const {\n useCreateFolderMutation,\n useGetFoldersQuery,\n useGetFolderQuery,\n useGetAllFoldersQuery,\n useGetFolderStructureQuery,\n useBulkMoveMutation,\n} = foldersApi;\n"],"names":["isDataEnvelope","response","unwrapData","data","foldersApi","uploadApi","injectEndpoints","endpoints","builder","getFolders","query","params","parentId","sort","queryParams","$and","parent","id","$null","url","method","config","transformResponse","providesTags","results","map","type","createFolder","mutation","body","invalidatesTags","getFolderStructure","getAllFolders","getFolder","populate","children","count","files","_result","_error","bulkMove","fileIds","folderIds","destinationFolderId","useCreateFolderMutation","useGetFoldersQuery","useGetFolderQuery","useGetAllFoldersQuery","useGetFolderStructureQuery","useBulkMoveMutation"],"mappings":"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"}
|
|
@@ -6,10 +6,10 @@ const foldersApi = uploadApi.injectEndpoints({
|
|
|
6
6
|
endpoints: (builder)=>({
|
|
7
7
|
getFolders: builder.query({
|
|
8
8
|
query: (params = {})=>{
|
|
9
|
-
const { parentId } = params;
|
|
9
|
+
const { parentId, sort } = params;
|
|
10
10
|
const queryParams = {
|
|
11
|
-
//
|
|
12
|
-
sort: 'name:ASC'
|
|
11
|
+
// Default matches sidebar FolderTree order (server getStructure uses sortBy('name')).
|
|
12
|
+
sort: sort ?? 'name:ASC'
|
|
13
13
|
};
|
|
14
14
|
if (parentId != null) {
|
|
15
15
|
queryParams['filters'] = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"folders.mjs","sources":["../../../../admin/src/future/services/folders.ts"],"sourcesContent":["import { uploadApi } from './api';\n\nimport type {\n Folder,\n FolderNode,\n CreateFolders,\n GetFolder,\n GetFolders,\n GetFolderStructure,\n BulkMoveFolders,\n} from '../../../../shared/contracts/folders';\n\nexport type FolderWithCounts = Omit<Folder, 'children' | 'files'> & {\n children?: { count: number };\n files?: { count: number };\n};\n\ninterface GetFoldersParams {\n parentId?: number | null;\n}\n\ninterface BulkMoveParams {\n fileIds?: number[];\n folderIds?: number[];\n destinationFolderId: number;\n}\n\ntype DataEnvelope<T> = {\n data: T;\n};\n\nconst isDataEnvelope = <T>(response: T | DataEnvelope<T>): response is DataEnvelope<T> =>\n typeof response === 'object' && response !== null && 'data' in response;\n\nconst unwrapData = <T>(response: T | DataEnvelope<T>): T =>\n isDataEnvelope(response) ? response.data : response;\n\nconst foldersApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n getFolders: builder.query<Folder[], GetFoldersParams | void>({\n query: (params = {}) => {\n const { parentId } = params as GetFoldersParams;\n\n const queryParams: Record<string, unknown> = {\n // Match sidebar FolderTree order (server getStructure uses sortBy('name')).\n sort: 'name:ASC',\n };\n\n if (parentId != null) {\n queryParams['filters'] = {\n $and: [{ parent: { id: parentId } }],\n };\n } else {\n queryParams['filters'] = {\n $and: [{ parent: { id: { $null: true } } }],\n };\n }\n\n return {\n url: '/upload/folders',\n method: 'GET',\n config: { params: queryParams },\n };\n },\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder', id: 'LIST' },\n ];\n }\n return [{ type: 'Folder', id: 'LIST' }];\n },\n }),\n createFolder: builder.mutation<CreateFolders.Response['data'], CreateFolders.Request['body']>({\n query: (body) => ({\n url: '/upload/folders',\n method: 'POST',\n data: body,\n }),\n transformResponse: (response: CreateFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n /**\n * Hierarchical folder tree used by the FolderTree sidebar. Returned as a\n * single nested array — the server walks the folders table once and\n * assembles parent/child relationships.\n *\n * TODO: filter results like `admin-folder.find` for folder-scoped roles\n * (v1 returns all folders; relies on global `plugin::upload.read`).\n */\n getFolderStructure: builder.query<FolderNode[], void>({\n query: () => ({\n url: '/upload/folder-structure',\n method: 'GET',\n }),\n // TODO: align GetFolderStructure contract with the real /upload/folder-structure\n // envelope and drop this defensive unwrap (data[] vs { data: [] }).\n transformResponse: (response: GetFolderStructure.Response['data']) =>\n ((response as unknown as { data: FolderNode[] })?.data ??\n (response as unknown as FolderNode[]) ??\n []) as FolderNode[],\n providesTags: [{ type: 'Folder', id: 'STRUCTURE' }],\n }),\n /**\n * Flat list of every folder, used to populate the \"Location\" select in the\n * asset details drawer. No parent filter — we want the entire tree in one\n * query so the select can render any destination folder.\n */\n getAllFolders: builder.query<Folder[], void>({\n query: () => ({\n url: '/upload/folders',\n method: 'GET',\n }),\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response ?? []),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder' as const, id: 'LIST' },\n ];\n }\n return [{ type: 'Folder' as const, id: 'LIST' }];\n },\n }),\n getFolder: builder.query<FolderWithCounts, { id: number }>({\n query: ({ id }) => ({\n url: `/upload/folders/${id}`,\n method: 'GET',\n config: {\n params: {\n populate: {\n parent: {\n populate: {\n parent: '*',\n },\n },\n children: { count: true },\n files: { count: true },\n },\n },\n },\n }),\n transformResponse: (response: GetFolder.Response) =>\n response.data as unknown as FolderWithCounts,\n providesTags: (_result, _error, { id }) => [{ type: 'Folder', id }],\n }),\n bulkMove: builder.mutation<BulkMoveFolders.Response['data'], BulkMoveParams>({\n query: ({ fileIds = [], folderIds = [], destinationFolderId }) => ({\n url: '/upload/actions/bulk-move',\n method: 'POST',\n data: { fileIds, folderIds, destinationFolderId },\n }),\n transformResponse: (response: BulkMoveFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Asset', id: 'LIST' },\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n }),\n});\n\nexport const {\n useCreateFolderMutation,\n useGetFoldersQuery,\n useGetFolderQuery,\n useGetAllFoldersQuery,\n useGetFolderStructureQuery,\n useBulkMoveMutation,\n} = foldersApi;\n"],"names":["isDataEnvelope","response","unwrapData","data","foldersApi","uploadApi","injectEndpoints","endpoints","builder","getFolders","query","params","parentId","queryParams","sort","$and","parent","id","$null","url","method","config","transformResponse","providesTags","results","map","type","createFolder","mutation","body","invalidatesTags","getFolderStructure","getAllFolders","getFolder","populate","children","count","files","_result","_error","bulkMove","fileIds","folderIds","destinationFolderId","useCreateFolderMutation","useGetFoldersQuery","useGetFolderQuery","useGetAllFoldersQuery","useGetFolderStructureQuery","useBulkMoveMutation"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"folders.mjs","sources":["../../../../admin/src/future/services/folders.ts"],"sourcesContent":["import { uploadApi } from './api';\n\nimport type {\n Folder,\n FolderNode,\n CreateFolders,\n GetFolder,\n GetFolders,\n GetFolderStructure,\n BulkMoveFolders,\n} from '../../../../shared/contracts/folders';\n\nexport type FolderWithCounts = Omit<Folder, 'children' | 'files'> & {\n children?: { count: number };\n files?: { count: number };\n};\n\ninterface GetFoldersParams {\n parentId?: number | null;\n /** Comma-separated rules, e.g. `updatedAt:DESC,name:ASC`. Defaults to alphabetical. */\n sort?: string;\n}\n\ninterface BulkMoveParams {\n fileIds?: number[];\n folderIds?: number[];\n /** `null` moves the items to the root of the Media Library. */\n destinationFolderId: number | null;\n}\n\ntype DataEnvelope<T> = {\n data: T;\n};\n\nconst isDataEnvelope = <T>(response: T | DataEnvelope<T>): response is DataEnvelope<T> =>\n typeof response === 'object' && response !== null && 'data' in response;\n\nconst unwrapData = <T>(response: T | DataEnvelope<T>): T =>\n isDataEnvelope(response) ? response.data : response;\n\nconst foldersApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n getFolders: builder.query<Folder[], GetFoldersParams | void>({\n query: (params = {}) => {\n const { parentId, sort } = params as GetFoldersParams;\n\n const queryParams: Record<string, unknown> = {\n // Default matches sidebar FolderTree order (server getStructure uses sortBy('name')).\n sort: sort ?? 'name:ASC',\n };\n\n if (parentId != null) {\n queryParams['filters'] = {\n $and: [{ parent: { id: parentId } }],\n };\n } else {\n queryParams['filters'] = {\n $and: [{ parent: { id: { $null: true } } }],\n };\n }\n\n return {\n url: '/upload/folders',\n method: 'GET',\n config: { params: queryParams },\n };\n },\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder', id: 'LIST' },\n ];\n }\n return [{ type: 'Folder', id: 'LIST' }];\n },\n }),\n createFolder: builder.mutation<CreateFolders.Response['data'], CreateFolders.Request['body']>({\n query: (body) => ({\n url: '/upload/folders',\n method: 'POST',\n data: body,\n }),\n transformResponse: (response: CreateFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n /**\n * Hierarchical folder tree used by the FolderTree sidebar. Returned as a\n * single nested array — the server walks the folders table once and\n * assembles parent/child relationships.\n *\n * TODO: filter results like `admin-folder.find` for folder-scoped roles\n * (v1 returns all folders; relies on global `plugin::upload.read`).\n */\n getFolderStructure: builder.query<FolderNode[], void>({\n query: () => ({\n url: '/upload/folder-structure',\n method: 'GET',\n }),\n // TODO: align GetFolderStructure contract with the real /upload/folder-structure\n // envelope and drop this defensive unwrap (data[] vs { data: [] }).\n transformResponse: (response: GetFolderStructure.Response['data']) =>\n ((response as unknown as { data: FolderNode[] })?.data ??\n (response as unknown as FolderNode[]) ??\n []) as FolderNode[],\n providesTags: [{ type: 'Folder', id: 'STRUCTURE' }],\n }),\n /**\n * Flat list of every folder, used to populate the \"Location\" select in the\n * asset details drawer. No parent filter — we want the entire tree in one\n * query so the select can render any destination folder.\n */\n getAllFolders: builder.query<Folder[], void>({\n query: () => ({\n url: '/upload/folders',\n method: 'GET',\n }),\n transformResponse: (response: GetFolders.Response['data']) =>\n unwrapData<GetFolders.Response['data']>(response ?? []),\n providesTags: (results) => {\n if (results) {\n return [\n ...results.map(({ id }) => ({ type: 'Folder' as const, id })),\n { type: 'Folder' as const, id: 'LIST' },\n ];\n }\n return [{ type: 'Folder' as const, id: 'LIST' }];\n },\n }),\n getFolder: builder.query<FolderWithCounts, { id: number }>({\n query: ({ id }) => ({\n url: `/upload/folders/${id}`,\n method: 'GET',\n config: {\n params: {\n populate: {\n parent: {\n populate: {\n parent: '*',\n },\n },\n children: { count: true },\n files: { count: true },\n },\n },\n },\n }),\n transformResponse: (response: GetFolder.Response) =>\n response.data as unknown as FolderWithCounts,\n providesTags: (_result, _error, { id }) => [{ type: 'Folder', id }],\n }),\n bulkMove: builder.mutation<BulkMoveFolders.Response['data'], BulkMoveParams>({\n query: ({ fileIds = [], folderIds = [], destinationFolderId }) => ({\n url: '/upload/actions/bulk-move',\n method: 'POST',\n data: { fileIds, folderIds, destinationFolderId },\n }),\n transformResponse: (response: BulkMoveFolders.Response) => response.data,\n invalidatesTags: [\n { type: 'Asset', id: 'LIST' },\n { type: 'Folder', id: 'LIST' },\n { type: 'Folder', id: 'STRUCTURE' },\n ],\n }),\n }),\n});\n\nexport const {\n useCreateFolderMutation,\n useGetFoldersQuery,\n useGetFolderQuery,\n useGetAllFoldersQuery,\n useGetFolderStructureQuery,\n useBulkMoveMutation,\n} = foldersApi;\n"],"names":["isDataEnvelope","response","unwrapData","data","foldersApi","uploadApi","injectEndpoints","endpoints","builder","getFolders","query","params","parentId","sort","queryParams","$and","parent","id","$null","url","method","config","transformResponse","providesTags","results","map","type","createFolder","mutation","body","invalidatesTags","getFolderStructure","getAllFolders","getFolder","populate","children","count","files","_result","_error","bulkMove","fileIds","folderIds","destinationFolderId","useCreateFolderMutation","useGetFoldersQuery","useGetFolderQuery","useGetAllFoldersQuery","useGetFolderStructureQuery","useBulkMoveMutation"],"mappings":";;AAkCA,MAAMA,cAAAA,GAAiB,CAAIC,QAAAA,GACzB,OAAOA,aAAa,QAAA,IAAYA,QAAAA,KAAa,QAAQ,MAAA,IAAUA,QAAAA;AAEjE,MAAMC,aAAa,CAAID,QAAAA,GACrBD,eAAeC,QAAAA,CAAAA,GAAYA,QAAAA,CAASE,IAAI,GAAGF,QAAAA;AAE7C,MAAMG,UAAAA,GAAaC,SAAAA,CAAUC,eAAe,CAAC;IAC3CC,SAAAA,EAAW,CAACC,WAAa;YACvBC,UAAAA,EAAYD,OAAAA,CAAQE,KAAK,CAAoC;gBAC3DA,KAAAA,EAAO,CAACC,MAAAA,GAAS,EAAE,GAAA;AACjB,oBAAA,MAAM,EAAEC,QAAQ,EAAEC,IAAI,EAAE,GAAGF,MAAAA;AAE3B,oBAAA,MAAMG,WAAAA,GAAuC;;AAE3CD,wBAAAA,IAAAA,EAAMA,IAAAA,IAAQ;AAChB,qBAAA;AAEA,oBAAA,IAAID,YAAY,IAAA,EAAM;wBACpBE,WAAW,CAAC,UAAU,GAAG;4BACvBC,IAAAA,EAAM;AAAC,gCAAA;oCAAEC,MAAAA,EAAQ;wCAAEC,EAAAA,EAAIL;AAAS;AAAE;AAAE;AACtC,yBAAA;oBACF,CAAA,MAAO;wBACLE,WAAW,CAAC,UAAU,GAAG;4BACvBC,IAAAA,EAAM;AAAC,gCAAA;oCAAEC,MAAAA,EAAQ;wCAAEC,EAAAA,EAAI;4CAAEC,KAAAA,EAAO;AAAK;AAAE;AAAE;AAAE;AAC7C,yBAAA;AACF,oBAAA;oBAEA,OAAO;wBACLC,GAAAA,EAAK,iBAAA;wBACLC,MAAAA,EAAQ,KAAA;wBACRC,MAAAA,EAAQ;4BAAEV,MAAAA,EAAQG;AAAY;AAChC,qBAAA;AACF,gBAAA,CAAA;gBACAQ,iBAAAA,EAAmB,CAACrB,WAClBC,UAAAA,CAAwCD,QAAAA,CAAAA;AAC1CsB,gBAAAA,YAAAA,EAAc,CAACC,OAAAA,GAAAA;AACb,oBAAA,IAAIA,OAAAA,EAAS;wBACX,OAAO;AACFA,4BAAAA,GAAAA,OAAAA,CAAQC,GAAG,CAAC,CAAC,EAAER,EAAE,EAAE,IAAM;oCAAES,IAAAA,EAAM,QAAA;AAAmBT,oCAAAA;iCAAG,CAAA,CAAA;AAC1D,4BAAA;gCAAES,IAAAA,EAAM,QAAA;gCAAUT,EAAAA,EAAI;AAAO;AAC9B,yBAAA;AACH,oBAAA;oBACA,OAAO;AAAC,wBAAA;4BAAES,IAAAA,EAAM,QAAA;4BAAUT,EAAAA,EAAI;AAAO;AAAE,qBAAA;AACzC,gBAAA;AACF,aAAA,CAAA;YACAU,YAAAA,EAAcnB,OAAAA,CAAQoB,QAAQ,CAAgE;gBAC5FlB,KAAAA,EAAO,CAACmB,QAAU;wBAChBV,GAAAA,EAAK,iBAAA;wBACLC,MAAAA,EAAQ,MAAA;wBACRjB,IAAAA,EAAM0B;qBACR,CAAA;gBACAP,iBAAAA,EAAmB,CAACrB,QAAAA,GAAqCA,QAAAA,CAASE,IAAI;gBACtE2B,eAAAA,EAAiB;AACf,oBAAA;wBAAEJ,IAAAA,EAAM,QAAA;wBAAUT,EAAAA,EAAI;AAAO,qBAAA;AAC7B,oBAAA;wBAAES,IAAAA,EAAM,QAAA;wBAAUT,EAAAA,EAAI;AAAY;AACnC;AACH,aAAA,CAAA;AACA;;;;;;;QAQAc,kBAAAA,EAAoBvB,OAAAA,CAAQE,KAAK,CAAqB;AACpDA,gBAAAA,KAAAA,EAAO,KAAO;wBACZS,GAAAA,EAAK,0BAAA;wBACLC,MAAAA,EAAQ;qBACV,CAAA;;;AAGAE,gBAAAA,iBAAAA,EAAmB,CAACrB,QAAAA,GAChBA,QAAAA,EAAgDE,IAAAA,IAC/CF,YACD,EAAE;gBACNsB,YAAAA,EAAc;AAAC,oBAAA;wBAAEG,IAAAA,EAAM,QAAA;wBAAUT,EAAAA,EAAI;AAAY;AAAE;AACrD,aAAA,CAAA;AACA;;;;QAKAe,aAAAA,EAAexB,OAAAA,CAAQE,KAAK,CAAiB;AAC3CA,gBAAAA,KAAAA,EAAO,KAAO;wBACZS,GAAAA,EAAK,iBAAA;wBACLC,MAAAA,EAAQ;qBACV,CAAA;AACAE,gBAAAA,iBAAAA,EAAmB,CAACrB,QAAAA,GAClBC,UAAAA,CAAwCD,QAAAA,IAAY,EAAE,CAAA;AACxDsB,gBAAAA,YAAAA,EAAc,CAACC,OAAAA,GAAAA;AACb,oBAAA,IAAIA,OAAAA,EAAS;wBACX,OAAO;AACFA,4BAAAA,GAAAA,OAAAA,CAAQC,GAAG,CAAC,CAAC,EAAER,EAAE,EAAE,IAAM;oCAAES,IAAAA,EAAM,QAAA;AAAmBT,oCAAAA;iCAAG,CAAA,CAAA;AAC1D,4BAAA;gCAAES,IAAAA,EAAM,QAAA;gCAAmBT,EAAAA,EAAI;AAAO;AACvC,yBAAA;AACH,oBAAA;oBACA,OAAO;AAAC,wBAAA;4BAAES,IAAAA,EAAM,QAAA;4BAAmBT,EAAAA,EAAI;AAAO;AAAE,qBAAA;AAClD,gBAAA;AACF,aAAA,CAAA;YACAgB,SAAAA,EAAWzB,OAAAA,CAAQE,KAAK,CAAmC;AACzDA,gBAAAA,KAAAA,EAAO,CAAC,EAAEO,EAAE,EAAE,IAAM;wBAClBE,GAAAA,EAAK,CAAC,gBAAgB,EAAEF,EAAAA,CAAAA,CAAI;wBAC5BG,MAAAA,EAAQ,KAAA;wBACRC,MAAAA,EAAQ;4BACNV,MAAAA,EAAQ;gCACNuB,QAAAA,EAAU;oCACRlB,MAAAA,EAAQ;wCACNkB,QAAAA,EAAU;4CACRlB,MAAAA,EAAQ;AACV;AACF,qCAAA;oCACAmB,QAAAA,EAAU;wCAAEC,KAAAA,EAAO;AAAK,qCAAA;oCACxBC,KAAAA,EAAO;wCAAED,KAAAA,EAAO;AAAK;AACvB;AACF;AACF;qBACF,CAAA;gBACAd,iBAAAA,EAAmB,CAACrB,QAAAA,GAClBA,QAAAA,CAASE,IAAI;AACfoB,gBAAAA,YAAAA,EAAc,CAACe,OAAAA,EAASC,MAAAA,EAAQ,EAAEtB,EAAE,EAAE,GAAK;AAAC,wBAAA;4BAAES,IAAAA,EAAM,QAAA;AAAUT,4BAAAA;AAAG;AAAE;AACrE,aAAA,CAAA;YACAuB,QAAAA,EAAUhC,OAAAA,CAAQoB,QAAQ,CAAmD;gBAC3ElB,KAAAA,EAAO,CAAC,EAAE+B,OAAAA,GAAU,EAAE,EAAEC,SAAAA,GAAY,EAAE,EAAEC,mBAAmB,EAAE,IAAM;wBACjExB,GAAAA,EAAK,2BAAA;wBACLC,MAAAA,EAAQ,MAAA;wBACRjB,IAAAA,EAAM;AAAEsC,4BAAAA,OAAAA;AAASC,4BAAAA,SAAAA;AAAWC,4BAAAA;AAAoB;qBAClD,CAAA;gBACArB,iBAAAA,EAAmB,CAACrB,QAAAA,GAAuCA,QAAAA,CAASE,IAAI;gBACxE2B,eAAAA,EAAiB;AACf,oBAAA;wBAAEJ,IAAAA,EAAM,OAAA;wBAAST,EAAAA,EAAI;AAAO,qBAAA;AAC5B,oBAAA;wBAAES,IAAAA,EAAM,QAAA;wBAAUT,EAAAA,EAAI;AAAO,qBAAA;AAC7B,oBAAA;wBAAES,IAAAA,EAAM,QAAA;wBAAUT,EAAAA,EAAI;AAAY;AACnC;AACH,aAAA;SACF;AACF,CAAA,CAAA;AAEO,MAAM,EACX2B,uBAAuB,EACvBC,kBAAkB,EAClBC,iBAAiB,EACjBC,qBAAqB,EACrBC,0BAA0B,EAC1BC,mBAAmB,EACpB,GAAG7C;;;;"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var strapiAdmin = require('@strapi/admin/strapi-admin');
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Thrown when an upload is aborted via its `AbortSignal`.
|
|
5
7
|
* Distinct from {@link UploadFileError} so callers can tell cancellation apart
|
|
@@ -31,10 +33,11 @@
|
|
|
31
33
|
* @param formData - Prebuilt multipart body containing the single file and its `fileInfo`.
|
|
32
34
|
* @param signal - Aborts the in-flight request when triggered.
|
|
33
35
|
* @param onProgress - Called with `(loaded, total)` bytes as the upload progresses.
|
|
34
|
-
* @returns The parsed
|
|
36
|
+
* @returns The parsed response body on a 2xx response — a signed `File` by default,
|
|
37
|
+
* or `T` when the endpoint returns a different shape (e.g. an array of files).
|
|
35
38
|
* @throws {UploadAbortedError} When the signal aborts.
|
|
36
39
|
* @throws {UploadFileError} On a non-2xx response or network error.
|
|
37
|
-
*/ const
|
|
40
|
+
*/ const sendUploadViaXHR = (url, token, formData, signal, onProgress)=>{
|
|
38
41
|
return new Promise((resolve, reject)=>{
|
|
39
42
|
if (signal.aborted) {
|
|
40
43
|
reject(new UploadAbortedError());
|
|
@@ -85,6 +88,24 @@
|
|
|
85
88
|
xhr.send(formData);
|
|
86
89
|
});
|
|
87
90
|
};
|
|
91
|
+
const uploadFileViaXHR = async (url, token, formData, signal, onProgress)=>{
|
|
92
|
+
try {
|
|
93
|
+
return await sendUploadViaXHR(url, token, formData, signal, onProgress);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (!(error instanceof UploadFileError) || error.status !== 401) {
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const refreshedToken = await strapiAdmin.attemptTokenRefresh();
|
|
100
|
+
return await sendUploadViaXHR(url, refreshedToken, formData, signal, onProgress);
|
|
101
|
+
} catch (refreshError) {
|
|
102
|
+
if (refreshError instanceof UploadFileError) {
|
|
103
|
+
throw refreshError;
|
|
104
|
+
}
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
88
109
|
|
|
89
110
|
exports.UploadAbortedError = UploadAbortedError;
|
|
90
111
|
exports.UploadFileError = UploadFileError;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploadFileViaXHR.js","sources":["../../../../admin/src/future/services/uploadFileViaXHR.ts"],"sourcesContent":["import type { File } from '../../../../shared/contracts/files';\n\n/**\n * Thrown when an upload is aborted via its `AbortSignal`.\n * Distinct from {@link UploadFileError} so callers can tell cancellation apart\n * from a genuine failure.\n */\nexport class UploadAbortedError extends Error {\n constructor(message = 'Upload aborted') {\n super(message);\n this.name = 'UploadAbortedError';\n }\n}\n\n/**\n * Thrown when an upload fails (non-2xx response, network error, or unparseable body).\n */\nexport class UploadFileError extends Error {\n status?: number;\n\n constructor(message: string, status?: number) {\n super(message);\n this.name = 'UploadFileError';\n this.status = status;\n }\n}\n\nexport type UploadProgressCallback = (bytes: number, total: number) => void;\n\n/**\n * Uploads a single file via `XMLHttpRequest`, exposing real byte-level upload\n * progress through {@link XMLHttpRequest.upload}'s `progress` event.\n *\n * This is the only place raw XHR lives. `fetch()` is intentionally avoided here\n * because it does not surface upload progress.\n *\n * @param url - The full endpoint URL to POST to.\n * @param token - Admin auth token; the `Authorization` header is only set when present.\n * @param formData - Prebuilt multipart body containing the single file and its `fileInfo`.\n * @param signal - Aborts the in-flight request when triggered.\n * @param onProgress - Called with `(loaded, total)` bytes as the upload progresses.\n * @returns The parsed
|
|
1
|
+
{"version":3,"file":"uploadFileViaXHR.js","sources":["../../../../admin/src/future/services/uploadFileViaXHR.ts"],"sourcesContent":["import { attemptTokenRefresh } from '@strapi/admin/strapi-admin';\n\nimport type { File } from '../../../../shared/contracts/files';\n\n/**\n * Thrown when an upload is aborted via its `AbortSignal`.\n * Distinct from {@link UploadFileError} so callers can tell cancellation apart\n * from a genuine failure.\n */\nexport class UploadAbortedError extends Error {\n constructor(message = 'Upload aborted') {\n super(message);\n this.name = 'UploadAbortedError';\n }\n}\n\n/**\n * Thrown when an upload fails (non-2xx response, network error, or unparseable body).\n */\nexport class UploadFileError extends Error {\n status?: number;\n\n constructor(message: string, status?: number) {\n super(message);\n this.name = 'UploadFileError';\n this.status = status;\n }\n}\n\nexport type UploadProgressCallback = (bytes: number, total: number) => void;\n\n/**\n * Uploads a single file via `XMLHttpRequest`, exposing real byte-level upload\n * progress through {@link XMLHttpRequest.upload}'s `progress` event.\n *\n * This is the only place raw XHR lives. `fetch()` is intentionally avoided here\n * because it does not surface upload progress.\n *\n * @param url - The full endpoint URL to POST to.\n * @param token - Admin auth token; the `Authorization` header is only set when present.\n * @param formData - Prebuilt multipart body containing the single file and its `fileInfo`.\n * @param signal - Aborts the in-flight request when triggered.\n * @param onProgress - Called with `(loaded, total)` bytes as the upload progresses.\n * @returns The parsed response body on a 2xx response — a signed `File` by default,\n * or `T` when the endpoint returns a different shape (e.g. an array of files).\n * @throws {UploadAbortedError} When the signal aborts.\n * @throws {UploadFileError} On a non-2xx response or network error.\n */\nconst sendUploadViaXHR = <T>(\n url: string,\n token: string | null | undefined,\n formData: FormData,\n signal: AbortSignal,\n onProgress?: UploadProgressCallback\n): Promise<T> => {\n return new Promise<T>((resolve, reject) => {\n if (signal.aborted) {\n reject(new UploadAbortedError());\n return;\n }\n\n const xhr = new XMLHttpRequest();\n xhr.open('POST', url);\n\n if (token) {\n xhr.setRequestHeader('Authorization', `Bearer ${token}`);\n }\n\n const handleAbort = () => xhr.abort();\n signal.addEventListener('abort', handleAbort);\n\n const cleanup = () => signal.removeEventListener('abort', handleAbort);\n\n if (onProgress) {\n xhr.upload.onprogress = (event: ProgressEvent) => {\n if (event.lengthComputable) {\n onProgress(event.loaded, event.total);\n }\n };\n }\n\n xhr.onload = () => {\n cleanup();\n\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n resolve(JSON.parse(xhr.responseText) as T);\n } catch {\n reject(new UploadFileError('Failed to parse upload response'));\n }\n return;\n }\n\n let message = `Upload failed with status ${xhr.status}`;\n try {\n const parsed = JSON.parse(xhr.responseText);\n message = parsed?.error?.message || parsed?.message || message;\n } catch {\n // Keep the default status-based message.\n }\n reject(new UploadFileError(message, xhr.status));\n };\n\n xhr.onerror = () => {\n cleanup();\n reject(new UploadFileError('Network error occurred'));\n };\n\n xhr.onabort = () => {\n cleanup();\n reject(new UploadAbortedError());\n };\n\n xhr.send(formData);\n });\n};\n\nexport const uploadFileViaXHR = async <T = File>(\n url: string,\n token: string | null | undefined,\n formData: FormData,\n signal: AbortSignal,\n onProgress?: UploadProgressCallback\n): Promise<T> => {\n try {\n return await sendUploadViaXHR<T>(url, token, formData, signal, onProgress);\n } catch (error) {\n if (!(error instanceof UploadFileError) || error.status !== 401) {\n throw error;\n }\n\n try {\n const refreshedToken = await attemptTokenRefresh();\n\n return await sendUploadViaXHR<T>(url, refreshedToken, formData, signal, onProgress);\n } catch (refreshError) {\n if (refreshError instanceof UploadFileError) {\n throw refreshError;\n }\n\n throw error;\n }\n }\n};\n"],"names":["UploadAbortedError","Error","message","name","UploadFileError","status","sendUploadViaXHR","url","token","formData","signal","onProgress","Promise","resolve","reject","aborted","xhr","XMLHttpRequest","open","setRequestHeader","handleAbort","abort","addEventListener","cleanup","removeEventListener","upload","onprogress","event","lengthComputable","loaded","total","onload","JSON","parse","responseText","parsed","error","onerror","onabort","send","uploadFileViaXHR","refreshedToken","attemptTokenRefresh","refreshError"],"mappings":"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"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { attemptTokenRefresh } from '@strapi/admin/strapi-admin';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Thrown when an upload is aborted via its `AbortSignal`.
|
|
3
5
|
* Distinct from {@link UploadFileError} so callers can tell cancellation apart
|
|
@@ -29,10 +31,11 @@
|
|
|
29
31
|
* @param formData - Prebuilt multipart body containing the single file and its `fileInfo`.
|
|
30
32
|
* @param signal - Aborts the in-flight request when triggered.
|
|
31
33
|
* @param onProgress - Called with `(loaded, total)` bytes as the upload progresses.
|
|
32
|
-
* @returns The parsed
|
|
34
|
+
* @returns The parsed response body on a 2xx response — a signed `File` by default,
|
|
35
|
+
* or `T` when the endpoint returns a different shape (e.g. an array of files).
|
|
33
36
|
* @throws {UploadAbortedError} When the signal aborts.
|
|
34
37
|
* @throws {UploadFileError} On a non-2xx response or network error.
|
|
35
|
-
*/ const
|
|
38
|
+
*/ const sendUploadViaXHR = (url, token, formData, signal, onProgress)=>{
|
|
36
39
|
return new Promise((resolve, reject)=>{
|
|
37
40
|
if (signal.aborted) {
|
|
38
41
|
reject(new UploadAbortedError());
|
|
@@ -83,6 +86,24 @@
|
|
|
83
86
|
xhr.send(formData);
|
|
84
87
|
});
|
|
85
88
|
};
|
|
89
|
+
const uploadFileViaXHR = async (url, token, formData, signal, onProgress)=>{
|
|
90
|
+
try {
|
|
91
|
+
return await sendUploadViaXHR(url, token, formData, signal, onProgress);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (!(error instanceof UploadFileError) || error.status !== 401) {
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const refreshedToken = await attemptTokenRefresh();
|
|
98
|
+
return await sendUploadViaXHR(url, refreshedToken, formData, signal, onProgress);
|
|
99
|
+
} catch (refreshError) {
|
|
100
|
+
if (refreshError instanceof UploadFileError) {
|
|
101
|
+
throw refreshError;
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
86
107
|
|
|
87
108
|
export { UploadAbortedError, UploadFileError, uploadFileViaXHR };
|
|
88
109
|
//# sourceMappingURL=uploadFileViaXHR.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploadFileViaXHR.mjs","sources":["../../../../admin/src/future/services/uploadFileViaXHR.ts"],"sourcesContent":["import type { File } from '../../../../shared/contracts/files';\n\n/**\n * Thrown when an upload is aborted via its `AbortSignal`.\n * Distinct from {@link UploadFileError} so callers can tell cancellation apart\n * from a genuine failure.\n */\nexport class UploadAbortedError extends Error {\n constructor(message = 'Upload aborted') {\n super(message);\n this.name = 'UploadAbortedError';\n }\n}\n\n/**\n * Thrown when an upload fails (non-2xx response, network error, or unparseable body).\n */\nexport class UploadFileError extends Error {\n status?: number;\n\n constructor(message: string, status?: number) {\n super(message);\n this.name = 'UploadFileError';\n this.status = status;\n }\n}\n\nexport type UploadProgressCallback = (bytes: number, total: number) => void;\n\n/**\n * Uploads a single file via `XMLHttpRequest`, exposing real byte-level upload\n * progress through {@link XMLHttpRequest.upload}'s `progress` event.\n *\n * This is the only place raw XHR lives. `fetch()` is intentionally avoided here\n * because it does not surface upload progress.\n *\n * @param url - The full endpoint URL to POST to.\n * @param token - Admin auth token; the `Authorization` header is only set when present.\n * @param formData - Prebuilt multipart body containing the single file and its `fileInfo`.\n * @param signal - Aborts the in-flight request when triggered.\n * @param onProgress - Called with `(loaded, total)` bytes as the upload progresses.\n * @returns The parsed
|
|
1
|
+
{"version":3,"file":"uploadFileViaXHR.mjs","sources":["../../../../admin/src/future/services/uploadFileViaXHR.ts"],"sourcesContent":["import { attemptTokenRefresh } from '@strapi/admin/strapi-admin';\n\nimport type { File } from '../../../../shared/contracts/files';\n\n/**\n * Thrown when an upload is aborted via its `AbortSignal`.\n * Distinct from {@link UploadFileError} so callers can tell cancellation apart\n * from a genuine failure.\n */\nexport class UploadAbortedError extends Error {\n constructor(message = 'Upload aborted') {\n super(message);\n this.name = 'UploadAbortedError';\n }\n}\n\n/**\n * Thrown when an upload fails (non-2xx response, network error, or unparseable body).\n */\nexport class UploadFileError extends Error {\n status?: number;\n\n constructor(message: string, status?: number) {\n super(message);\n this.name = 'UploadFileError';\n this.status = status;\n }\n}\n\nexport type UploadProgressCallback = (bytes: number, total: number) => void;\n\n/**\n * Uploads a single file via `XMLHttpRequest`, exposing real byte-level upload\n * progress through {@link XMLHttpRequest.upload}'s `progress` event.\n *\n * This is the only place raw XHR lives. `fetch()` is intentionally avoided here\n * because it does not surface upload progress.\n *\n * @param url - The full endpoint URL to POST to.\n * @param token - Admin auth token; the `Authorization` header is only set when present.\n * @param formData - Prebuilt multipart body containing the single file and its `fileInfo`.\n * @param signal - Aborts the in-flight request when triggered.\n * @param onProgress - Called with `(loaded, total)` bytes as the upload progresses.\n * @returns The parsed response body on a 2xx response — a signed `File` by default,\n * or `T` when the endpoint returns a different shape (e.g. an array of files).\n * @throws {UploadAbortedError} When the signal aborts.\n * @throws {UploadFileError} On a non-2xx response or network error.\n */\nconst sendUploadViaXHR = <T>(\n url: string,\n token: string | null | undefined,\n formData: FormData,\n signal: AbortSignal,\n onProgress?: UploadProgressCallback\n): Promise<T> => {\n return new Promise<T>((resolve, reject) => {\n if (signal.aborted) {\n reject(new UploadAbortedError());\n return;\n }\n\n const xhr = new XMLHttpRequest();\n xhr.open('POST', url);\n\n if (token) {\n xhr.setRequestHeader('Authorization', `Bearer ${token}`);\n }\n\n const handleAbort = () => xhr.abort();\n signal.addEventListener('abort', handleAbort);\n\n const cleanup = () => signal.removeEventListener('abort', handleAbort);\n\n if (onProgress) {\n xhr.upload.onprogress = (event: ProgressEvent) => {\n if (event.lengthComputable) {\n onProgress(event.loaded, event.total);\n }\n };\n }\n\n xhr.onload = () => {\n cleanup();\n\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n resolve(JSON.parse(xhr.responseText) as T);\n } catch {\n reject(new UploadFileError('Failed to parse upload response'));\n }\n return;\n }\n\n let message = `Upload failed with status ${xhr.status}`;\n try {\n const parsed = JSON.parse(xhr.responseText);\n message = parsed?.error?.message || parsed?.message || message;\n } catch {\n // Keep the default status-based message.\n }\n reject(new UploadFileError(message, xhr.status));\n };\n\n xhr.onerror = () => {\n cleanup();\n reject(new UploadFileError('Network error occurred'));\n };\n\n xhr.onabort = () => {\n cleanup();\n reject(new UploadAbortedError());\n };\n\n xhr.send(formData);\n });\n};\n\nexport const uploadFileViaXHR = async <T = File>(\n url: string,\n token: string | null | undefined,\n formData: FormData,\n signal: AbortSignal,\n onProgress?: UploadProgressCallback\n): Promise<T> => {\n try {\n return await sendUploadViaXHR<T>(url, token, formData, signal, onProgress);\n } catch (error) {\n if (!(error instanceof UploadFileError) || error.status !== 401) {\n throw error;\n }\n\n try {\n const refreshedToken = await attemptTokenRefresh();\n\n return await sendUploadViaXHR<T>(url, refreshedToken, formData, signal, onProgress);\n } catch (refreshError) {\n if (refreshError instanceof UploadFileError) {\n throw refreshError;\n }\n\n throw error;\n }\n }\n};\n"],"names":["UploadAbortedError","Error","message","name","UploadFileError","status","sendUploadViaXHR","url","token","formData","signal","onProgress","Promise","resolve","reject","aborted","xhr","XMLHttpRequest","open","setRequestHeader","handleAbort","abort","addEventListener","cleanup","removeEventListener","upload","onprogress","event","lengthComputable","loaded","total","onload","JSON","parse","responseText","parsed","error","onerror","onabort","send","uploadFileViaXHR","refreshedToken","attemptTokenRefresh","refreshError"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reconstruct the drag set from a bulk-actions selection so the dialog can feed
|
|
5
|
+
* the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical
|
|
6
|
+
* `canDropItemOnFolder` predicate that pointer drag uses.
|
|
7
|
+
*
|
|
8
|
+
* Selection is page-scoped to the current folder, so every selected item shares
|
|
9
|
+
* `currentFolderId` as its location (files → `folderId`, folders → `parentId`).
|
|
10
|
+
* Names are irrelevant to drop validation, so they are left empty.
|
|
11
|
+
*/ const buildDragSetFromSelection = (selectedIds, selectedFolderIds, currentFolderId)=>{
|
|
12
|
+
const items = [];
|
|
13
|
+
selectedIds.forEach((id)=>{
|
|
14
|
+
items.push({
|
|
15
|
+
kind: 'file',
|
|
16
|
+
id,
|
|
17
|
+
name: '',
|
|
18
|
+
folderId: currentFolderId
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
selectedFolderIds.forEach((id)=>{
|
|
22
|
+
items.push({
|
|
23
|
+
kind: 'folder',
|
|
24
|
+
id,
|
|
25
|
+
name: '',
|
|
26
|
+
parentId: currentFolderId
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
return items;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
exports.buildDragSetFromSelection = buildDragSetFromSelection;
|
|
33
|
+
//# sourceMappingURL=buildDragSetFromSelection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildDragSetFromSelection.js","sources":["../../../../admin/src/future/utils/buildDragSetFromSelection.ts"],"sourcesContent":["import type { DragItemData } from '../types/dnd';\n\n/**\n * Reconstruct the drag set from a bulk-actions selection so the dialog can feed\n * the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical\n * `canDropItemOnFolder` predicate that pointer drag uses.\n *\n * Selection is page-scoped to the current folder, so every selected item shares\n * `currentFolderId` as its location (files → `folderId`, folders → `parentId`).\n * Names are irrelevant to drop validation, so they are left empty.\n */\nexport const buildDragSetFromSelection = (\n selectedIds: ReadonlySet<number>,\n selectedFolderIds: ReadonlySet<number>,\n currentFolderId: number | null\n): DragItemData[] => {\n const items: DragItemData[] = [];\n\n selectedIds.forEach((id) => {\n items.push({ kind: 'file', id, name: '', folderId: currentFolderId });\n });\n\n selectedFolderIds.forEach((id) => {\n items.push({ kind: 'folder', id, name: '', parentId: currentFolderId });\n });\n\n return items;\n};\n"],"names":["buildDragSetFromSelection","selectedIds","selectedFolderIds","currentFolderId","items","forEach","id","push","kind","name","folderId","parentId"],"mappings":";;AAEA;;;;;;;;AAQC,IACM,MAAMA,yBAAAA,GAA4B,CACvCC,aACAC,iBAAAA,EACAC,eAAAA,GAAAA;AAEA,IAAA,MAAMC,QAAwB,EAAE;IAEhCH,WAAAA,CAAYI,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACnBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YAAEC,IAAAA,EAAM,MAAA;AAAQF,YAAAA,EAAAA;YAAIG,IAAAA,EAAM,EAAA;YAAIC,QAAAA,EAAUP;AAAgB,SAAA,CAAA;AACrE,IAAA,CAAA,CAAA;IAEAD,iBAAAA,CAAkBG,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACzBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YAAEC,IAAAA,EAAM,QAAA;AAAUF,YAAAA,EAAAA;YAAIG,IAAAA,EAAM,EAAA;YAAIE,QAAAA,EAAUR;AAAgB,SAAA,CAAA;AACvE,IAAA,CAAA,CAAA;IAEA,OAAOC,KAAAA;AACT;;;;"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reconstruct the drag set from a bulk-actions selection so the dialog can feed
|
|
3
|
+
* the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical
|
|
4
|
+
* `canDropItemOnFolder` predicate that pointer drag uses.
|
|
5
|
+
*
|
|
6
|
+
* Selection is page-scoped to the current folder, so every selected item shares
|
|
7
|
+
* `currentFolderId` as its location (files → `folderId`, folders → `parentId`).
|
|
8
|
+
* Names are irrelevant to drop validation, so they are left empty.
|
|
9
|
+
*/ const buildDragSetFromSelection = (selectedIds, selectedFolderIds, currentFolderId)=>{
|
|
10
|
+
const items = [];
|
|
11
|
+
selectedIds.forEach((id)=>{
|
|
12
|
+
items.push({
|
|
13
|
+
kind: 'file',
|
|
14
|
+
id,
|
|
15
|
+
name: '',
|
|
16
|
+
folderId: currentFolderId
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
selectedFolderIds.forEach((id)=>{
|
|
20
|
+
items.push({
|
|
21
|
+
kind: 'folder',
|
|
22
|
+
id,
|
|
23
|
+
name: '',
|
|
24
|
+
parentId: currentFolderId
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
return items;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export { buildDragSetFromSelection };
|
|
31
|
+
//# sourceMappingURL=buildDragSetFromSelection.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildDragSetFromSelection.mjs","sources":["../../../../admin/src/future/utils/buildDragSetFromSelection.ts"],"sourcesContent":["import type { DragItemData } from '../types/dnd';\n\n/**\n * Reconstruct the drag set from a bulk-actions selection so the dialog can feed\n * the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical\n * `canDropItemOnFolder` predicate that pointer drag uses.\n *\n * Selection is page-scoped to the current folder, so every selected item shares\n * `currentFolderId` as its location (files → `folderId`, folders → `parentId`).\n * Names are irrelevant to drop validation, so they are left empty.\n */\nexport const buildDragSetFromSelection = (\n selectedIds: ReadonlySet<number>,\n selectedFolderIds: ReadonlySet<number>,\n currentFolderId: number | null\n): DragItemData[] => {\n const items: DragItemData[] = [];\n\n selectedIds.forEach((id) => {\n items.push({ kind: 'file', id, name: '', folderId: currentFolderId });\n });\n\n selectedFolderIds.forEach((id) => {\n items.push({ kind: 'folder', id, name: '', parentId: currentFolderId });\n });\n\n return items;\n};\n"],"names":["buildDragSetFromSelection","selectedIds","selectedFolderIds","currentFolderId","items","forEach","id","push","kind","name","folderId","parentId"],"mappings":"AAEA;;;;;;;;AAQC,IACM,MAAMA,yBAAAA,GAA4B,CACvCC,aACAC,iBAAAA,EACAC,eAAAA,GAAAA;AAEA,IAAA,MAAMC,QAAwB,EAAE;IAEhCH,WAAAA,CAAYI,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACnBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YAAEC,IAAAA,EAAM,MAAA;AAAQF,YAAAA,EAAAA;YAAIG,IAAAA,EAAM,EAAA;YAAIC,QAAAA,EAAUP;AAAgB,SAAA,CAAA;AACrE,IAAA,CAAA,CAAA;IAEAD,iBAAAA,CAAkBG,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACzBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YAAEC,IAAAA,EAAM,QAAA;AAAUF,YAAAA,EAAAA;YAAIG,IAAAA,EAAM,EAAA;YAAIE,QAAAA,EAAUR;AAAgB,SAAA,CAAA;AACvE,IAAA,CAAA,CAAA;IAEA,OAAOC,KAAAA;AACT;;;;"}
|
|
@@ -35,10 +35,14 @@ const isFolderDescendantOf = (folderStructure, ancestorId, candidateId)=>{
|
|
|
35
35
|
}
|
|
36
36
|
return collectDescendantIds(ancestorNode).has(candidateId);
|
|
37
37
|
};
|
|
38
|
+
const isItemAtRoot = (item)=>item.kind === 'file' ? item.folderId == null : item.parentId == null;
|
|
38
39
|
const canDropItemOnFolder = ({ items, targetFolderId, folderStructure })=>{
|
|
39
40
|
if (items.length === 0) {
|
|
40
41
|
return false;
|
|
41
42
|
}
|
|
43
|
+
if (targetFolderId === null) {
|
|
44
|
+
return items.some((item)=>!isItemAtRoot(item));
|
|
45
|
+
}
|
|
42
46
|
const draggedFolderIds = new Set(items.filter((item)=>item.kind === 'folder').map((item)=>item.id));
|
|
43
47
|
if (draggedFolderIds.has(targetFolderId)) {
|
|
44
48
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canDropItemOnFolder.js","sources":["../../../../admin/src/future/utils/canDropItemOnFolder.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\nimport type { DragItemData } from '../types/dnd';\n\n// TODO: permission-aware droppables — server validates in v1.\n\nexport interface CanDropItemOnFolderParams {\n items: DragItemData[];\n targetFolderId: number;\n folderStructure: FolderNode[];\n}\n\nconst findNode = (nodes: FolderNode[], id: number): FolderNode | null => {\n for (const node of nodes) {\n if (node.id === id) {\n return node;\n }\n\n const found = findNode(node.children, id);\n if (found) {\n return found;\n }\n }\n\n return null;\n};\n\nconst collectDescendantIds = (node: FolderNode): Set<number> => {\n const ids = new Set<number>();\n\n const walk = (current: FolderNode) => {\n for (const child of current.children) {\n if (child.id != null) {\n ids.add(child.id);\n }\n walk(child);\n }\n };\n\n walk(node);\n return ids;\n};\n\nexport const isFolderDescendantOf = (\n folderStructure: FolderNode[],\n ancestorId: number,\n candidateId: number\n): boolean => {\n if (ancestorId === candidateId) {\n return true;\n }\n\n const ancestorNode = findNode(folderStructure, ancestorId);\n if (!ancestorNode) {\n return false;\n }\n\n return collectDescendantIds(ancestorNode).has(candidateId);\n};\n\nexport const canDropItemOnFolder = ({\n items,\n targetFolderId,\n folderStructure,\n}: CanDropItemOnFolderParams): boolean => {\n if (items.length === 0) {\n return false;\n }\n\n const draggedFolderIds = new Set(\n items\n .filter((item): item is Extract<DragItemData, { kind: 'folder' }> => item.kind === 'folder')\n .map((item) => item.id)\n );\n\n if (draggedFolderIds.has(targetFolderId)) {\n return false;\n }\n\n for (const folderId of draggedFolderIds) {\n if (isFolderDescendantOf(folderStructure, folderId, targetFolderId)) {\n return false;\n }\n }\n\n for (const item of items) {\n if (item.kind === 'file' && item.folderId === targetFolderId) {\n return false;\n }\n\n if (item.kind === 'folder' && item.parentId === targetFolderId) {\n return false;\n }\n }\n\n return true;\n};\n"],"names":["findNode","nodes","id","node","found","children","collectDescendantIds","ids","Set","walk","current","child","add","isFolderDescendantOf","folderStructure","ancestorId","candidateId","ancestorNode","has","
|
|
1
|
+
{"version":3,"file":"canDropItemOnFolder.js","sources":["../../../../admin/src/future/utils/canDropItemOnFolder.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\nimport type { DragItemData } from '../types/dnd';\n\n// TODO: permission-aware droppables — server validates in v1.\n\nexport interface CanDropItemOnFolderParams {\n items: DragItemData[];\n targetFolderId: number | null;\n folderStructure: FolderNode[];\n}\n\nconst findNode = (nodes: FolderNode[], id: number): FolderNode | null => {\n for (const node of nodes) {\n if (node.id === id) {\n return node;\n }\n\n const found = findNode(node.children, id);\n if (found) {\n return found;\n }\n }\n\n return null;\n};\n\nconst collectDescendantIds = (node: FolderNode): Set<number> => {\n const ids = new Set<number>();\n\n const walk = (current: FolderNode) => {\n for (const child of current.children) {\n if (child.id != null) {\n ids.add(child.id);\n }\n walk(child);\n }\n };\n\n walk(node);\n return ids;\n};\n\nexport const isFolderDescendantOf = (\n folderStructure: FolderNode[],\n ancestorId: number,\n candidateId: number\n): boolean => {\n if (ancestorId === candidateId) {\n return true;\n }\n\n const ancestorNode = findNode(folderStructure, ancestorId);\n if (!ancestorNode) {\n return false;\n }\n\n return collectDescendantIds(ancestorNode).has(candidateId);\n};\n\nconst isItemAtRoot = (item: DragItemData): boolean =>\n item.kind === 'file' ? item.folderId == null : item.parentId == null;\n\nexport const canDropItemOnFolder = ({\n items,\n targetFolderId,\n folderStructure,\n}: CanDropItemOnFolderParams): boolean => {\n if (items.length === 0) {\n return false;\n }\n\n if (targetFolderId === null) {\n return items.some((item) => !isItemAtRoot(item));\n }\n\n const draggedFolderIds = new Set(\n items\n .filter((item): item is Extract<DragItemData, { kind: 'folder' }> => item.kind === 'folder')\n .map((item) => item.id)\n );\n\n if (draggedFolderIds.has(targetFolderId)) {\n return false;\n }\n\n for (const folderId of draggedFolderIds) {\n if (isFolderDescendantOf(folderStructure, folderId, targetFolderId)) {\n return false;\n }\n }\n\n for (const item of items) {\n if (item.kind === 'file' && item.folderId === targetFolderId) {\n return false;\n }\n\n if (item.kind === 'folder' && item.parentId === targetFolderId) {\n return false;\n }\n }\n\n return true;\n};\n"],"names":["findNode","nodes","id","node","found","children","collectDescendantIds","ids","Set","walk","current","child","add","isFolderDescendantOf","folderStructure","ancestorId","candidateId","ancestorNode","has","isItemAtRoot","item","kind","folderId","parentId","canDropItemOnFolder","items","targetFolderId","length","some","draggedFolderIds","filter","map"],"mappings":"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"}
|
|
@@ -33,10 +33,14 @@ const isFolderDescendantOf = (folderStructure, ancestorId, candidateId)=>{
|
|
|
33
33
|
}
|
|
34
34
|
return collectDescendantIds(ancestorNode).has(candidateId);
|
|
35
35
|
};
|
|
36
|
+
const isItemAtRoot = (item)=>item.kind === 'file' ? item.folderId == null : item.parentId == null;
|
|
36
37
|
const canDropItemOnFolder = ({ items, targetFolderId, folderStructure })=>{
|
|
37
38
|
if (items.length === 0) {
|
|
38
39
|
return false;
|
|
39
40
|
}
|
|
41
|
+
if (targetFolderId === null) {
|
|
42
|
+
return items.some((item)=>!isItemAtRoot(item));
|
|
43
|
+
}
|
|
40
44
|
const draggedFolderIds = new Set(items.filter((item)=>item.kind === 'folder').map((item)=>item.id));
|
|
41
45
|
if (draggedFolderIds.has(targetFolderId)) {
|
|
42
46
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canDropItemOnFolder.mjs","sources":["../../../../admin/src/future/utils/canDropItemOnFolder.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\nimport type { DragItemData } from '../types/dnd';\n\n// TODO: permission-aware droppables — server validates in v1.\n\nexport interface CanDropItemOnFolderParams {\n items: DragItemData[];\n targetFolderId: number;\n folderStructure: FolderNode[];\n}\n\nconst findNode = (nodes: FolderNode[], id: number): FolderNode | null => {\n for (const node of nodes) {\n if (node.id === id) {\n return node;\n }\n\n const found = findNode(node.children, id);\n if (found) {\n return found;\n }\n }\n\n return null;\n};\n\nconst collectDescendantIds = (node: FolderNode): Set<number> => {\n const ids = new Set<number>();\n\n const walk = (current: FolderNode) => {\n for (const child of current.children) {\n if (child.id != null) {\n ids.add(child.id);\n }\n walk(child);\n }\n };\n\n walk(node);\n return ids;\n};\n\nexport const isFolderDescendantOf = (\n folderStructure: FolderNode[],\n ancestorId: number,\n candidateId: number\n): boolean => {\n if (ancestorId === candidateId) {\n return true;\n }\n\n const ancestorNode = findNode(folderStructure, ancestorId);\n if (!ancestorNode) {\n return false;\n }\n\n return collectDescendantIds(ancestorNode).has(candidateId);\n};\n\nexport const canDropItemOnFolder = ({\n items,\n targetFolderId,\n folderStructure,\n}: CanDropItemOnFolderParams): boolean => {\n if (items.length === 0) {\n return false;\n }\n\n const draggedFolderIds = new Set(\n items\n .filter((item): item is Extract<DragItemData, { kind: 'folder' }> => item.kind === 'folder')\n .map((item) => item.id)\n );\n\n if (draggedFolderIds.has(targetFolderId)) {\n return false;\n }\n\n for (const folderId of draggedFolderIds) {\n if (isFolderDescendantOf(folderStructure, folderId, targetFolderId)) {\n return false;\n }\n }\n\n for (const item of items) {\n if (item.kind === 'file' && item.folderId === targetFolderId) {\n return false;\n }\n\n if (item.kind === 'folder' && item.parentId === targetFolderId) {\n return false;\n }\n }\n\n return true;\n};\n"],"names":["findNode","nodes","id","node","found","children","collectDescendantIds","ids","Set","walk","current","child","add","isFolderDescendantOf","folderStructure","ancestorId","candidateId","ancestorNode","has","
|
|
1
|
+
{"version":3,"file":"canDropItemOnFolder.mjs","sources":["../../../../admin/src/future/utils/canDropItemOnFolder.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\nimport type { DragItemData } from '../types/dnd';\n\n// TODO: permission-aware droppables — server validates in v1.\n\nexport interface CanDropItemOnFolderParams {\n items: DragItemData[];\n targetFolderId: number | null;\n folderStructure: FolderNode[];\n}\n\nconst findNode = (nodes: FolderNode[], id: number): FolderNode | null => {\n for (const node of nodes) {\n if (node.id === id) {\n return node;\n }\n\n const found = findNode(node.children, id);\n if (found) {\n return found;\n }\n }\n\n return null;\n};\n\nconst collectDescendantIds = (node: FolderNode): Set<number> => {\n const ids = new Set<number>();\n\n const walk = (current: FolderNode) => {\n for (const child of current.children) {\n if (child.id != null) {\n ids.add(child.id);\n }\n walk(child);\n }\n };\n\n walk(node);\n return ids;\n};\n\nexport const isFolderDescendantOf = (\n folderStructure: FolderNode[],\n ancestorId: number,\n candidateId: number\n): boolean => {\n if (ancestorId === candidateId) {\n return true;\n }\n\n const ancestorNode = findNode(folderStructure, ancestorId);\n if (!ancestorNode) {\n return false;\n }\n\n return collectDescendantIds(ancestorNode).has(candidateId);\n};\n\nconst isItemAtRoot = (item: DragItemData): boolean =>\n item.kind === 'file' ? item.folderId == null : item.parentId == null;\n\nexport const canDropItemOnFolder = ({\n items,\n targetFolderId,\n folderStructure,\n}: CanDropItemOnFolderParams): boolean => {\n if (items.length === 0) {\n return false;\n }\n\n if (targetFolderId === null) {\n return items.some((item) => !isItemAtRoot(item));\n }\n\n const draggedFolderIds = new Set(\n items\n .filter((item): item is Extract<DragItemData, { kind: 'folder' }> => item.kind === 'folder')\n .map((item) => item.id)\n );\n\n if (draggedFolderIds.has(targetFolderId)) {\n return false;\n }\n\n for (const folderId of draggedFolderIds) {\n if (isFolderDescendantOf(folderStructure, folderId, targetFolderId)) {\n return false;\n }\n }\n\n for (const item of items) {\n if (item.kind === 'file' && item.folderId === targetFolderId) {\n return false;\n }\n\n if (item.kind === 'folder' && item.parentId === targetFolderId) {\n return false;\n }\n }\n\n return true;\n};\n"],"names":["findNode","nodes","id","node","found","children","collectDescendantIds","ids","Set","walk","current","child","add","isFolderDescendantOf","folderStructure","ancestorId","candidateId","ancestorNode","has","isItemAtRoot","item","kind","folderId","parentId","canDropItemOnFolder","items","targetFolderId","length","some","draggedFolderIds","filter","map"],"mappings":"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"}
|