@strapi/upload 5.51.2 → 5.52.0

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.
Files changed (193) hide show
  1. package/dist/admin/future/App.js +3 -3
  2. package/dist/admin/future/App.js.map +1 -1
  3. package/dist/admin/future/App.mjs +4 -4
  4. package/dist/admin/future/App.mjs.map +1 -1
  5. package/dist/admin/future/components/BetaNotice.js +72 -0
  6. package/dist/admin/future/components/BetaNotice.js.map +1 -0
  7. package/dist/admin/future/components/BetaNotice.mjs +70 -0
  8. package/dist/admin/future/components/BetaNotice.mjs.map +1 -0
  9. package/dist/admin/future/components/Drawer.js +16 -3
  10. package/dist/admin/future/components/Drawer.js.map +1 -1
  11. package/dist/admin/future/components/Drawer.mjs +16 -3
  12. package/dist/admin/future/components/Drawer.mjs.map +1 -1
  13. package/dist/admin/future/hooks/useAIMetadataEnabled.js +39 -0
  14. package/dist/admin/future/hooks/useAIMetadataEnabled.js.map +1 -0
  15. package/dist/admin/future/hooks/useAIMetadataEnabled.mjs +37 -0
  16. package/dist/admin/future/hooks/useAIMetadataEnabled.mjs.map +1 -0
  17. package/dist/admin/future/pages/Assets/AssetsPage.js +297 -139
  18. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  19. package/dist/admin/future/pages/Assets/AssetsPage.mjs +301 -143
  20. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  21. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js +46 -0
  22. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js.map +1 -0
  23. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs +44 -0
  24. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs.map +1 -0
  25. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +325 -0
  26. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -0
  27. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +323 -0
  28. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -0
  29. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +35 -14
  30. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
  31. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +35 -14
  32. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
  33. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +54 -53
  34. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  35. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +54 -54
  36. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  37. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +89 -24
  38. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +91 -26
  40. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +15 -3
  42. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -1
  43. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +16 -4
  44. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -1
  45. package/dist/admin/future/pages/Assets/components/AssetsTable.js +168 -58
  46. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  47. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +171 -61
  48. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  49. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +64 -132
  50. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
  51. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +64 -132
  52. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  53. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +123 -76
  54. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -1
  55. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +126 -79
  56. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -1
  57. package/dist/admin/future/pages/Assets/components/BusyOverlay.js +36 -0
  58. package/dist/admin/future/pages/Assets/components/BusyOverlay.js.map +1 -0
  59. package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs +34 -0
  60. package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs.map +1 -0
  61. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js +131 -0
  62. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js.map +1 -0
  63. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs +129 -0
  64. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs.map +1 -0
  65. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +27 -17
  66. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  67. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +28 -18
  68. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  69. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +28 -31
  70. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -1
  71. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +28 -31
  72. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -1
  73. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +10 -15
  74. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
  75. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +10 -15
  76. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
  77. package/dist/admin/future/pages/Assets/components/FilterBadges.js +13 -4
  78. package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -1
  79. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +13 -4
  80. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -1
  81. package/dist/admin/future/pages/Assets/components/FilterMenu.js +241 -61
  82. package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -1
  83. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +244 -64
  84. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -1
  85. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js +144 -0
  86. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js.map +1 -0
  87. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs +142 -0
  88. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs.map +1 -0
  89. package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.js → FolderFormDialog.js} +50 -16
  90. package/dist/admin/future/pages/Assets/components/FolderFormDialog.js.map +1 -0
  91. package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.mjs → FolderFormDialog.mjs} +52 -18
  92. package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs.map +1 -0
  93. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +111 -191
  94. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  95. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +114 -194
  96. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  97. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js +47 -0
  98. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js.map +1 -0
  99. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs +44 -0
  100. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs.map +1 -0
  101. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +203 -62
  102. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
  103. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +204 -63
  104. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
  105. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js +75 -0
  106. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js.map +1 -0
  107. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs +73 -0
  108. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs.map +1 -0
  109. package/dist/admin/future/services/api.js +24 -0
  110. package/dist/admin/future/services/api.js.map +1 -1
  111. package/dist/admin/future/services/api.mjs +24 -0
  112. package/dist/admin/future/services/api.mjs.map +1 -1
  113. package/dist/admin/future/services/assets.js +11 -0
  114. package/dist/admin/future/services/assets.js.map +1 -1
  115. package/dist/admin/future/services/assets.mjs +11 -0
  116. package/dist/admin/future/services/assets.mjs.map +1 -1
  117. package/dist/admin/future/services/folders.js +37 -2
  118. package/dist/admin/future/services/folders.js.map +1 -1
  119. package/dist/admin/future/services/folders.mjs +37 -3
  120. package/dist/admin/future/services/folders.mjs.map +1 -1
  121. package/dist/admin/future/utils/buildDragSetFromSelection.js +7 -5
  122. package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -1
  123. package/dist/admin/future/utils/buildDragSetFromSelection.mjs +7 -5
  124. package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -1
  125. package/dist/admin/future/utils/formatMoveSuccessMessage.js +12 -1
  126. package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -1
  127. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +12 -1
  128. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -1
  129. package/dist/admin/future/utils/getRelationId.js +24 -0
  130. package/dist/admin/future/utils/getRelationId.js.map +1 -0
  131. package/dist/admin/future/utils/getRelationId.mjs +22 -0
  132. package/dist/admin/future/utils/getRelationId.mjs.map +1 -0
  133. package/dist/admin/future/utils/isEventFromWithin.js +14 -0
  134. package/dist/admin/future/utils/isEventFromWithin.js.map +1 -0
  135. package/dist/admin/future/utils/isEventFromWithin.mjs +12 -0
  136. package/dist/admin/future/utils/isEventFromWithin.mjs.map +1 -0
  137. package/dist/admin/future/utils/itemLocations.js +40 -0
  138. package/dist/admin/future/utils/itemLocations.js.map +1 -0
  139. package/dist/admin/future/utils/itemLocations.mjs +36 -0
  140. package/dist/admin/future/utils/itemLocations.mjs.map +1 -0
  141. package/dist/admin/future/utils/itemSource.js +36 -0
  142. package/dist/admin/future/utils/itemSource.js.map +1 -0
  143. package/dist/admin/future/utils/itemSource.mjs +32 -0
  144. package/dist/admin/future/utils/itemSource.mjs.map +1 -0
  145. package/dist/admin/index.js +11 -17
  146. package/dist/admin/index.js.map +1 -1
  147. package/dist/admin/index.mjs +12 -18
  148. package/dist/admin/index.mjs.map +1 -1
  149. package/dist/admin/src/future/App.d.ts +1 -1
  150. package/dist/admin/src/future/components/BetaNotice.d.ts +11 -0
  151. package/dist/admin/src/future/components/Drawer.d.ts +5 -1
  152. package/dist/admin/src/future/hooks/useAIMetadataEnabled.d.ts +26 -0
  153. package/dist/admin/src/future/pages/Assets/components/ActionsMenuContent.d.ts +33 -0
  154. package/dist/admin/src/future/pages/Assets/components/AssetActionsMenu.d.ts +24 -0
  155. package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +16 -0
  156. package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +8 -1
  157. package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +17 -6
  158. package/dist/admin/src/future/pages/Assets/components/BusyOverlay.d.ts +9 -0
  159. package/dist/admin/src/future/pages/Assets/components/DeleteItemsDialog.d.ts +27 -0
  160. package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +8 -1
  161. package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +18 -3
  162. package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +2 -1
  163. package/dist/admin/src/future/pages/Assets/components/FilterBadges.d.ts +3 -1
  164. package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +4 -0
  165. package/dist/admin/src/future/pages/Assets/components/FolderActionsMenu.d.ts +20 -0
  166. package/dist/admin/src/future/pages/Assets/components/FolderFormDialog.d.ts +16 -0
  167. package/dist/admin/src/future/pages/Assets/hooks/useBusyAssets.d.ts +38 -0
  168. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +20 -6
  169. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteScrollSentinel.d.ts +41 -0
  170. package/dist/admin/src/future/services/folders.d.ts +6 -1
  171. package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +4 -3
  172. package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +7 -1
  173. package/dist/admin/src/future/utils/getRelationId.d.ts +11 -0
  174. package/dist/admin/src/future/utils/isEventFromWithin.d.ts +11 -0
  175. package/dist/admin/src/future/utils/itemLocations.d.ts +31 -0
  176. package/dist/admin/src/future/utils/itemSource.d.ts +22 -0
  177. package/dist/admin/translations/en.json.js +25 -4
  178. package/dist/admin/translations/en.json.js.map +1 -1
  179. package/dist/admin/translations/en.json.mjs +25 -4
  180. package/dist/admin/translations/en.json.mjs.map +1 -1
  181. package/dist/server/services/image-manipulation.js.map +1 -1
  182. package/dist/server/services/image-manipulation.mjs.map +1 -1
  183. package/dist/server/src/services/image-manipulation.d.ts +0 -5
  184. package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
  185. package/package.json +9 -9
  186. package/dist/admin/future/pages/Assets/components/CreateFolderDialog.js.map +0 -1
  187. package/dist/admin/future/pages/Assets/components/CreateFolderDialog.mjs.map +0 -1
  188. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js +0 -33
  189. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js.map +0 -1
  190. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs +0 -31
  191. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs.map +0 -1
  192. package/dist/admin/src/future/pages/Assets/components/CreateFolderDialog.d.ts +0 -8
  193. package/dist/admin/src/future/pages/Assets/components/FolderTree/filterFolderTree.d.ts +0 -16
@@ -107,6 +107,12 @@ const assetsApi = uploadApi.injectEndpoints({
107
107
  {
108
108
  type: 'Asset',
109
109
  id: 'LIST'
110
+ },
111
+ // The Location select routes a folder move through this mutation, which
112
+ // changes both folders' counts — refresh the folder header count.
113
+ {
114
+ type: 'Folder',
115
+ id: 'LIST'
110
116
  }
111
117
  ]
112
118
  }),
@@ -161,6 +167,11 @@ const assetsApi = uploadApi.injectEndpoints({
161
167
  {
162
168
  type: 'Asset',
163
169
  id: 'LIST'
170
+ },
171
+ // Refresh the folder header count — deleting an asset changes it.
172
+ {
173
+ type: 'Folder',
174
+ id: 'LIST'
164
175
  }
165
176
  ]
166
177
  }),
@@ -1 +1 @@
1
- {"version":3,"file":"assets.mjs","sources":["../../../../admin/src/future/services/assets.ts"],"sourcesContent":["import { encodeSearchQuery } from '../utils/searchQueryParam';\n\nimport { 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 search?: string;\n /** Extra `filters[$and]` entries (list filters), AND-ed with the folder/search scope. */\n filters?: Record<string, unknown>[];\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 // `search` is destructured out so it never reaches the server as-is —\n // it is re-added below as the `_q` the API actually understands.\n const { folder, search, filters = [], ...rest } = params as GetAssetsParams;\n\n const queryParams: Record<string, unknown> = { ...rest };\n\n // List filters apply in BOTH modes: search composes with them (a\n // filtered search), only the folder scope is dropped while searching.\n if (search) {\n // Search is global: folder scoping is intentionally dropped so results span the whole library.\n queryParams['_q'] = encodeSearchQuery(search);\n\n if (filters.length > 0) {\n queryParams['filters'] = { $and: [...filters] };\n }\n } else {\n const folderScope =\n folder != null ? { folder: { id: folder } } : { folder: { id: { $null: true } } };\n\n queryParams['filters'] = {\n $and: [folderScope, ...filters],\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\n/**\n * `generateAiMetadata` is defined in `./api` (the upload flows dispatch it directly\n * after each file completes, and this module imports from there) — re-exported here\n * so asset components keep a single import site for asset-related hooks.\n */\nexport { useGenerateAiMetadataMutation } from './api';\n"],"names":["assetsApi","uploadApi","injectEndpoints","endpoints","builder","getAssets","query","params","folder","search","filters","rest","queryParams","encodeSearchQuery","length","$and","folderScope","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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"}
1
+ {"version":3,"file":"assets.mjs","sources":["../../../../admin/src/future/services/assets.ts"],"sourcesContent":["import { encodeSearchQuery } from '../utils/searchQueryParam';\n\nimport { 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 search?: string;\n /** Extra `filters[$and]` entries (list filters), AND-ed with the folder/search scope. */\n filters?: Record<string, unknown>[];\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 // `search` is destructured out so it never reaches the server as-is —\n // it is re-added below as the `_q` the API actually understands.\n const { folder, search, filters = [], ...rest } = params as GetAssetsParams;\n\n const queryParams: Record<string, unknown> = { ...rest };\n\n // List filters apply in BOTH modes: search composes with them (a\n // filtered search), only the folder scope is dropped while searching.\n if (search) {\n // Search is global: folder scoping is intentionally dropped so results span the whole library.\n queryParams['_q'] = encodeSearchQuery(search);\n\n if (filters.length > 0) {\n queryParams['filters'] = { $and: [...filters] };\n }\n } else {\n const folderScope =\n folder != null ? { folder: { id: folder } } : { folder: { id: { $null: true } } };\n\n queryParams['filters'] = {\n $and: [folderScope, ...filters],\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 // The Location select routes a folder move through this mutation, which\n // changes both folders' counts — refresh the folder header count.\n { type: 'Folder' 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 // Refresh the folder header count — deleting an asset changes it.\n { type: 'Folder' 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\n/**\n * `generateAiMetadata` is defined in `./api` (the upload flows dispatch it directly\n * after each file completes, and this module imports from there) — re-exported here\n * so asset components keep a single import site for asset-related hooks.\n */\nexport { useGenerateAiMetadataMutation } from './api';\n"],"names":["assetsApi","uploadApi","injectEndpoints","endpoints","builder","getAssets","query","params","folder","search","filters","rest","queryParams","encodeSearchQuery","length","$and","folderScope","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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"}
@@ -12,7 +12,10 @@ const foldersApi = api.uploadApi.injectEndpoints({
12
12
  const { parentId, sort, search, filters = [] } = params;
13
13
  const queryParams = {
14
14
  // Default matches sidebar FolderTree order (server getStructure uses sortBy('name')).
15
- sort: sort ?? 'name:ASC'
15
+ sort: sort ?? 'name:ASC',
16
+ populate: {
17
+ parent: true
18
+ }
16
19
  };
17
20
  // List filters (dates) apply in BOTH modes — search composes with them,
18
21
  // only the parent scope is dropped while searching.
@@ -96,6 +99,28 @@ const foldersApi = api.uploadApi.injectEndpoints({
96
99
  }
97
100
  ]
98
101
  }),
102
+ updateFolder: builder.mutation({
103
+ query: ({ id, ...body })=>({
104
+ url: `/upload/folders/${id}`,
105
+ method: 'PUT',
106
+ data: body
107
+ }),
108
+ transformResponse: (response)=>response.data,
109
+ invalidatesTags: (_result, _error, { id })=>[
110
+ {
111
+ type: 'Folder',
112
+ id
113
+ },
114
+ {
115
+ type: 'Folder',
116
+ id: 'LIST'
117
+ },
118
+ {
119
+ type: 'Folder',
120
+ id: 'STRUCTURE'
121
+ }
122
+ ]
123
+ }),
99
124
  /**
100
125
  * Hierarchical folder tree used by the FolderTree sidebar. Returned as a
101
126
  * single nested array — the server walks the folders table once and
@@ -172,10 +197,19 @@ const foldersApi = api.uploadApi.injectEndpoints({
172
197
  }
173
198
  }),
174
199
  transformResponse: (response)=>response.data,
200
+ // Also carries the LIST tag so a mutation that changes a folder's file
201
+ // count (upload / delete into it) can refresh the header count by
202
+ // invalidating `{ Folder, LIST }`, without needing the folder id at the
203
+ // mutation site (it's buried in the upload FormData / not passed to the
204
+ // delete).
175
205
  providesTags: (_result, _error, { id })=>[
176
206
  {
177
207
  type: 'Folder',
178
208
  id
209
+ },
210
+ {
211
+ type: 'Folder',
212
+ id: 'LIST'
179
213
  }
180
214
  ]
181
215
  }),
@@ -207,7 +241,7 @@ const foldersApi = api.uploadApi.injectEndpoints({
207
241
  })
208
242
  })
209
243
  });
210
- const { useCreateFolderMutation, useGetFoldersQuery, useGetFolderQuery, useGetAllFoldersQuery, useGetFolderStructureQuery, useBulkMoveMutation } = foldersApi;
244
+ const { useCreateFolderMutation, useUpdateFolderMutation, useGetFoldersQuery, useGetFolderQuery, useGetAllFoldersQuery, useGetFolderStructureQuery, useBulkMoveMutation } = foldersApi;
211
245
 
212
246
  exports.useBulkMoveMutation = useBulkMoveMutation;
213
247
  exports.useCreateFolderMutation = useCreateFolderMutation;
@@ -215,4 +249,5 @@ exports.useGetAllFoldersQuery = useGetAllFoldersQuery;
215
249
  exports.useGetFolderQuery = useGetFolderQuery;
216
250
  exports.useGetFolderStructureQuery = useGetFolderStructureQuery;
217
251
  exports.useGetFoldersQuery = useGetFoldersQuery;
252
+ exports.useUpdateFolderMutation = useUpdateFolderMutation;
218
253
  //# sourceMappingURL=folders.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"folders.js","sources":["../../../../admin/src/future/services/folders.ts"],"sourcesContent":["import { encodeSearchQuery } from '../utils/searchQueryParam';\n\nimport { 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 search?: string;\n /** Extra `filters[$and]` entries (list filters), AND-ed with the parent/search scope. */\n filters?: Record<string, unknown>[];\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, search, filters = [] } = 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 // List filters (dates) apply in BOTH modes — search composes with them,\n // only the parent scope is dropped while searching.\n if (search) {\n // Search is global: the parent filter is dropped so matching folders\n // anywhere in the library surface. The endpoint is unpaginated — it\n // returns every match — so callers can treat the array length as the\n // true total. Bounding it is a separate decision.\n queryParams['_q'] = encodeSearchQuery(search);\n\n if (filters.length > 0) {\n queryParams['filters'] = { $and: [...filters] };\n }\n } else {\n const parentScope =\n parentId != null ? { parent: { id: parentId } } : { parent: { id: { $null: true } } };\n\n queryParams['filters'] = {\n $and: [parentScope, ...filters],\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","search","filters","queryParams","encodeSearchQuery","length","$and","parentScope","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.js","sources":["../../../../admin/src/future/services/folders.ts"],"sourcesContent":["import { encodeSearchQuery } from '../utils/searchQueryParam';\n\nimport { uploadApi } from './api';\n\nimport type {\n Folder,\n FolderNode,\n CreateFolders,\n GetFolder,\n GetFolders,\n GetFolderStructure,\n BulkMoveFolders,\n UpdateFolder,\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 search?: string;\n /** Extra `filters[$and]` entries (list filters), AND-ed with the parent/search scope. */\n filters?: Record<string, unknown>[];\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, search, filters = [] } = 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 populate: { parent: true },\n };\n\n // List filters (dates) apply in BOTH modes — search composes with them,\n // only the parent scope is dropped while searching.\n if (search) {\n // Search is global: the parent filter is dropped so matching folders\n // anywhere in the library surface. The endpoint is unpaginated — it\n // returns every match — so callers can treat the array length as the\n // true total. Bounding it is a separate decision.\n queryParams['_q'] = encodeSearchQuery(search);\n\n if (filters.length > 0) {\n queryParams['filters'] = { $and: [...filters] };\n }\n } else {\n const parentScope =\n parentId != null ? { parent: { id: parentId } } : { parent: { id: { $null: true } } };\n\n queryParams['filters'] = {\n $and: [parentScope, ...filters],\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 updateFolder: builder.mutation<\n UpdateFolder.Response['data'],\n { id: number } & UpdateFolder.Request['body']\n >({\n query: ({ id, ...body }) => ({\n url: `/upload/folders/${id}`,\n method: 'PUT',\n data: body,\n }),\n transformResponse: (response: UpdateFolder.Response) => response.data,\n invalidatesTags: (_result, _error, { id }) => [\n { type: 'Folder', id },\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 // Also carries the LIST tag so a mutation that changes a folder's file\n // count (upload / delete into it) can refresh the header count by\n // invalidating `{ Folder, LIST }`, without needing the folder id at the\n // mutation site (it's buried in the upload FormData / not passed to the\n // delete).\n providesTags: (_result, _error, { id }) => [\n { type: 'Folder', id },\n { type: 'Folder', id: 'LIST' },\n ],\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 useUpdateFolderMutation,\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","search","filters","queryParams","populate","parent","encodeSearchQuery","length","$and","parentScope","id","$null","url","method","config","transformResponse","providesTags","results","map","type","createFolder","mutation","body","invalidatesTags","updateFolder","_result","_error","getFolderStructure","getAllFolders","getFolder","children","count","files","bulkMove","fileIds","folderIds","destinationFolderId","useCreateFolderMutation","useUpdateFolderMutation","useGetFoldersQuery","useGetFolderQuery","useGetAllFoldersQuery","useGetFolderStructureQuery","useBulkMoveMutation"],"mappings":"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"}
@@ -10,7 +10,10 @@ const foldersApi = uploadApi.injectEndpoints({
10
10
  const { parentId, sort, search, filters = [] } = params;
11
11
  const queryParams = {
12
12
  // Default matches sidebar FolderTree order (server getStructure uses sortBy('name')).
13
- sort: sort ?? 'name:ASC'
13
+ sort: sort ?? 'name:ASC',
14
+ populate: {
15
+ parent: true
16
+ }
14
17
  };
15
18
  // List filters (dates) apply in BOTH modes — search composes with them,
16
19
  // only the parent scope is dropped while searching.
@@ -94,6 +97,28 @@ const foldersApi = uploadApi.injectEndpoints({
94
97
  }
95
98
  ]
96
99
  }),
100
+ updateFolder: builder.mutation({
101
+ query: ({ id, ...body })=>({
102
+ url: `/upload/folders/${id}`,
103
+ method: 'PUT',
104
+ data: body
105
+ }),
106
+ transformResponse: (response)=>response.data,
107
+ invalidatesTags: (_result, _error, { id })=>[
108
+ {
109
+ type: 'Folder',
110
+ id
111
+ },
112
+ {
113
+ type: 'Folder',
114
+ id: 'LIST'
115
+ },
116
+ {
117
+ type: 'Folder',
118
+ id: 'STRUCTURE'
119
+ }
120
+ ]
121
+ }),
97
122
  /**
98
123
  * Hierarchical folder tree used by the FolderTree sidebar. Returned as a
99
124
  * single nested array — the server walks the folders table once and
@@ -170,10 +195,19 @@ const foldersApi = uploadApi.injectEndpoints({
170
195
  }
171
196
  }),
172
197
  transformResponse: (response)=>response.data,
198
+ // Also carries the LIST tag so a mutation that changes a folder's file
199
+ // count (upload / delete into it) can refresh the header count by
200
+ // invalidating `{ Folder, LIST }`, without needing the folder id at the
201
+ // mutation site (it's buried in the upload FormData / not passed to the
202
+ // delete).
173
203
  providesTags: (_result, _error, { id })=>[
174
204
  {
175
205
  type: 'Folder',
176
206
  id
207
+ },
208
+ {
209
+ type: 'Folder',
210
+ id: 'LIST'
177
211
  }
178
212
  ]
179
213
  }),
@@ -205,7 +239,7 @@ const foldersApi = uploadApi.injectEndpoints({
205
239
  })
206
240
  })
207
241
  });
208
- const { useCreateFolderMutation, useGetFoldersQuery, useGetFolderQuery, useGetAllFoldersQuery, useGetFolderStructureQuery, useBulkMoveMutation } = foldersApi;
242
+ const { useCreateFolderMutation, useUpdateFolderMutation, useGetFoldersQuery, useGetFolderQuery, useGetAllFoldersQuery, useGetFolderStructureQuery, useBulkMoveMutation } = foldersApi;
209
243
 
210
- export { useBulkMoveMutation, useCreateFolderMutation, useGetAllFoldersQuery, useGetFolderQuery, useGetFolderStructureQuery, useGetFoldersQuery };
244
+ export { useBulkMoveMutation, useCreateFolderMutation, useGetAllFoldersQuery, useGetFolderQuery, useGetFolderStructureQuery, useGetFoldersQuery, useUpdateFolderMutation };
211
245
  //# sourceMappingURL=folders.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"folders.mjs","sources":["../../../../admin/src/future/services/folders.ts"],"sourcesContent":["import { encodeSearchQuery } from '../utils/searchQueryParam';\n\nimport { 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 search?: string;\n /** Extra `filters[$and]` entries (list filters), AND-ed with the parent/search scope. */\n filters?: Record<string, unknown>[];\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, search, filters = [] } = 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 // List filters (dates) apply in BOTH modes — search composes with them,\n // only the parent scope is dropped while searching.\n if (search) {\n // Search is global: the parent filter is dropped so matching folders\n // anywhere in the library surface. The endpoint is unpaginated — it\n // returns every match — so callers can treat the array length as the\n // true total. Bounding it is a separate decision.\n queryParams['_q'] = encodeSearchQuery(search);\n\n if (filters.length > 0) {\n queryParams['filters'] = { $and: [...filters] };\n }\n } else {\n const parentScope =\n parentId != null ? { parent: { id: parentId } } : { parent: { id: { $null: true } } };\n\n queryParams['filters'] = {\n $and: [parentScope, ...filters],\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","search","filters","queryParams","encodeSearchQuery","length","$and","parentScope","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 { encodeSearchQuery } from '../utils/searchQueryParam';\n\nimport { uploadApi } from './api';\n\nimport type {\n Folder,\n FolderNode,\n CreateFolders,\n GetFolder,\n GetFolders,\n GetFolderStructure,\n BulkMoveFolders,\n UpdateFolder,\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 search?: string;\n /** Extra `filters[$and]` entries (list filters), AND-ed with the parent/search scope. */\n filters?: Record<string, unknown>[];\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, search, filters = [] } = 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 populate: { parent: true },\n };\n\n // List filters (dates) apply in BOTH modes — search composes with them,\n // only the parent scope is dropped while searching.\n if (search) {\n // Search is global: the parent filter is dropped so matching folders\n // anywhere in the library surface. The endpoint is unpaginated — it\n // returns every match — so callers can treat the array length as the\n // true total. Bounding it is a separate decision.\n queryParams['_q'] = encodeSearchQuery(search);\n\n if (filters.length > 0) {\n queryParams['filters'] = { $and: [...filters] };\n }\n } else {\n const parentScope =\n parentId != null ? { parent: { id: parentId } } : { parent: { id: { $null: true } } };\n\n queryParams['filters'] = {\n $and: [parentScope, ...filters],\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 updateFolder: builder.mutation<\n UpdateFolder.Response['data'],\n { id: number } & UpdateFolder.Request['body']\n >({\n query: ({ id, ...body }) => ({\n url: `/upload/folders/${id}`,\n method: 'PUT',\n data: body,\n }),\n transformResponse: (response: UpdateFolder.Response) => response.data,\n invalidatesTags: (_result, _error, { id }) => [\n { type: 'Folder', id },\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 // Also carries the LIST tag so a mutation that changes a folder's file\n // count (upload / delete into it) can refresh the header count by\n // invalidating `{ Folder, LIST }`, without needing the folder id at the\n // mutation site (it's buried in the upload FormData / not passed to the\n // delete).\n providesTags: (_result, _error, { id }) => [\n { type: 'Folder', id },\n { type: 'Folder', id: 'LIST' },\n ],\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 useUpdateFolderMutation,\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","search","filters","queryParams","populate","parent","encodeSearchQuery","length","$and","parentScope","id","$null","url","method","config","transformResponse","providesTags","results","map","type","createFolder","mutation","body","invalidatesTags","updateFolder","_result","_error","getFolderStructure","getAllFolders","getFolder","children","count","files","bulkMove","fileIds","folderIds","destinationFolderId","useCreateFolderMutation","useUpdateFolderMutation","useGetFoldersQuery","useGetFolderQuery","useGetAllFoldersQuery","useGetFolderStructureQuery","useBulkMoveMutation"],"mappings":"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"}
@@ -1,21 +1,23 @@
1
1
  'use strict';
2
2
 
3
+ var itemLocations = require('./itemLocations.js');
4
+
3
5
  /**
4
6
  * Reconstruct the drag set from a bulk-actions selection so the dialog can feed
5
7
  * the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical
6
8
  * `canDropItemOnFolder` predicate that pointer drag uses.
7
9
  *
8
- * Selection is page-scoped to the current folder, so every selected item shares
9
- * `currentFolderId` as its location (files `folderId`, folders `parentId`).
10
+ * Each item's location comes from the loaded row, not from the folder currently
11
+ * open: search results are global, so a selected item can live anywhere.
10
12
  * Names are irrelevant to drop validation, so they are left empty.
11
- */ const buildDragSetFromSelection = (selectedIds, selectedFolderIds, currentFolderId)=>{
13
+ */ const buildDragSetFromSelection = (selectedIds, selectedFolderIds, locations, fallbackFolderId)=>{
12
14
  const items = [];
13
15
  selectedIds.forEach((id)=>{
14
16
  items.push({
15
17
  kind: 'file',
16
18
  id,
17
19
  name: '',
18
- folderId: currentFolderId
20
+ folderId: itemLocations.locateItem(locations, 'file', id, fallbackFolderId)
19
21
  });
20
22
  });
21
23
  selectedFolderIds.forEach((id)=>{
@@ -23,7 +25,7 @@
23
25
  kind: 'folder',
24
26
  id,
25
27
  name: '',
26
- parentId: currentFolderId
28
+ parentId: itemLocations.locateItem(locations, 'folder', id, fallbackFolderId)
27
29
  });
28
30
  });
29
31
  return items;
@@ -1 +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;;;;"}
1
+ {"version":3,"file":"buildDragSetFromSelection.js","sources":["../../../../admin/src/future/utils/buildDragSetFromSelection.ts"],"sourcesContent":["import { locateItem, type ItemLocations } from './itemLocations';\n\nimport 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 * Each item's location comes from the loaded row, not from the folder currently\n * open: search results are global, so a selected item can live anywhere.\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 locations: ItemLocations,\n fallbackFolderId: number | null\n): DragItemData[] => {\n const items: DragItemData[] = [];\n\n selectedIds.forEach((id) => {\n items.push({\n kind: 'file',\n id,\n name: '',\n folderId: locateItem(locations, 'file', id, fallbackFolderId),\n });\n });\n\n selectedFolderIds.forEach((id) => {\n items.push({\n kind: 'folder',\n id,\n name: '',\n parentId: locateItem(locations, 'folder', id, fallbackFolderId),\n });\n });\n\n return items;\n};\n"],"names":["buildDragSetFromSelection","selectedIds","selectedFolderIds","locations","fallbackFolderId","items","forEach","id","push","kind","name","folderId","locateItem","parentId"],"mappings":";;;;AAIA;;;;;;;;AAQC,IACM,MAAMA,yBAAAA,GAA4B,CACvCC,WAAAA,EACAC,mBACAC,SAAAA,EACAC,gBAAAA,GAAAA;AAEA,IAAA,MAAMC,QAAwB,EAAE;IAEhCJ,WAAAA,CAAYK,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACnBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YACTC,IAAAA,EAAM,MAAA;AACNF,YAAAA,EAAAA;YACAG,IAAAA,EAAM,EAAA;YACNC,QAAAA,EAAUC,wBAAAA,CAAWT,SAAAA,EAAW,MAAA,EAAQI,EAAAA,EAAIH,gBAAAA;AAC9C,SAAA,CAAA;AACF,IAAA,CAAA,CAAA;IAEAF,iBAAAA,CAAkBI,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACzBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YACTC,IAAAA,EAAM,QAAA;AACNF,YAAAA,EAAAA;YACAG,IAAAA,EAAM,EAAA;YACNG,QAAAA,EAAUD,wBAAAA,CAAWT,SAAAA,EAAW,QAAA,EAAUI,EAAAA,EAAIH,gBAAAA;AAChD,SAAA,CAAA;AACF,IAAA,CAAA,CAAA;IAEA,OAAOC,KAAAA;AACT;;;;"}
@@ -1,19 +1,21 @@
1
+ import { locateItem } from './itemLocations.mjs';
2
+
1
3
  /**
2
4
  * Reconstruct the drag set from a bulk-actions selection so the dialog can feed
3
5
  * the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical
4
6
  * `canDropItemOnFolder` predicate that pointer drag uses.
5
7
  *
6
- * Selection is page-scoped to the current folder, so every selected item shares
7
- * `currentFolderId` as its location (files `folderId`, folders `parentId`).
8
+ * Each item's location comes from the loaded row, not from the folder currently
9
+ * open: search results are global, so a selected item can live anywhere.
8
10
  * Names are irrelevant to drop validation, so they are left empty.
9
- */ const buildDragSetFromSelection = (selectedIds, selectedFolderIds, currentFolderId)=>{
11
+ */ const buildDragSetFromSelection = (selectedIds, selectedFolderIds, locations, fallbackFolderId)=>{
10
12
  const items = [];
11
13
  selectedIds.forEach((id)=>{
12
14
  items.push({
13
15
  kind: 'file',
14
16
  id,
15
17
  name: '',
16
- folderId: currentFolderId
18
+ folderId: locateItem(locations, 'file', id, fallbackFolderId)
17
19
  });
18
20
  });
19
21
  selectedFolderIds.forEach((id)=>{
@@ -21,7 +23,7 @@
21
23
  kind: 'folder',
22
24
  id,
23
25
  name: '',
24
- parentId: currentFolderId
26
+ parentId: locateItem(locations, 'folder', id, fallbackFolderId)
25
27
  });
26
28
  });
27
29
  return items;
@@ -1 +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;;;;"}
1
+ {"version":3,"file":"buildDragSetFromSelection.mjs","sources":["../../../../admin/src/future/utils/buildDragSetFromSelection.ts"],"sourcesContent":["import { locateItem, type ItemLocations } from './itemLocations';\n\nimport 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 * Each item's location comes from the loaded row, not from the folder currently\n * open: search results are global, so a selected item can live anywhere.\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 locations: ItemLocations,\n fallbackFolderId: number | null\n): DragItemData[] => {\n const items: DragItemData[] = [];\n\n selectedIds.forEach((id) => {\n items.push({\n kind: 'file',\n id,\n name: '',\n folderId: locateItem(locations, 'file', id, fallbackFolderId),\n });\n });\n\n selectedFolderIds.forEach((id) => {\n items.push({\n kind: 'folder',\n id,\n name: '',\n parentId: locateItem(locations, 'folder', id, fallbackFolderId),\n });\n });\n\n return items;\n};\n"],"names":["buildDragSetFromSelection","selectedIds","selectedFolderIds","locations","fallbackFolderId","items","forEach","id","push","kind","name","folderId","locateItem","parentId"],"mappings":";;AAIA;;;;;;;;AAQC,IACM,MAAMA,yBAAAA,GAA4B,CACvCC,WAAAA,EACAC,mBACAC,SAAAA,EACAC,gBAAAA,GAAAA;AAEA,IAAA,MAAMC,QAAwB,EAAE;IAEhCJ,WAAAA,CAAYK,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACnBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YACTC,IAAAA,EAAM,MAAA;AACNF,YAAAA,EAAAA;YACAG,IAAAA,EAAM,EAAA;YACNC,QAAAA,EAAUC,UAAAA,CAAWT,SAAAA,EAAW,MAAA,EAAQI,EAAAA,EAAIH,gBAAAA;AAC9C,SAAA,CAAA;AACF,IAAA,CAAA,CAAA;IAEAF,iBAAAA,CAAkBI,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACzBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YACTC,IAAAA,EAAM,QAAA;AACNF,YAAAA,EAAAA;YACAG,IAAAA,EAAM,EAAA;YACNG,QAAAA,EAAUD,UAAAA,CAAWT,SAAAA,EAAW,QAAA,EAAUI,EAAAA,EAAIH,gBAAAA;AAChD,SAAA,CAAA;AACF,IAAA,CAAA,CAAA;IAEA,OAAOC,KAAAA;AACT;;;;"}
@@ -6,7 +6,17 @@ var translations = require('./translations.js');
6
6
  * Single source of truth for the bulk-move success wording. Both the DnD drop
7
7
  * and the BulkMoveDialog format the toast (and DnD live-region announcement)
8
8
  * through here so the two paths stay identical for the same move.
9
- */ const formatMoveSuccessMessage = ({ formatMessage, count, source, destination })=>formatMessage({
9
+ */ const formatMoveSuccessMessage = ({ formatMessage, count, source, destination })=>{
10
+ if (source === null) {
11
+ return formatMessage({
12
+ id: translations.getTranslationKey('list.bulk-actions.move.success-multiple-sources'),
13
+ defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved to {destination}'
14
+ }, {
15
+ count,
16
+ destination
17
+ });
18
+ }
19
+ return formatMessage({
10
20
  id: translations.getTranslationKey('list.bulk-actions.move.success'),
11
21
  defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}'
12
22
  }, {
@@ -14,6 +24,7 @@ var translations = require('./translations.js');
14
24
  source,
15
25
  destination
16
26
  });
27
+ };
17
28
 
18
29
  exports.formatMoveSuccessMessage = formatMoveSuccessMessage;
19
30
  //# sourceMappingURL=formatMoveSuccessMessage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatMoveSuccessMessage.js","sources":["../../../../admin/src/future/utils/formatMoveSuccessMessage.ts"],"sourcesContent":["import { getTranslationKey } from './translations';\n\nimport type { IntlShape } from 'react-intl';\n\ninterface FormatMoveSuccessArgs {\n formatMessage: IntlShape['formatMessage'];\n count: number;\n source: string;\n destination: string;\n}\n\n/**\n * Single source of truth for the bulk-move success wording. Both the DnD drop\n * and the BulkMoveDialog format the toast (and DnD live-region announcement)\n * through here so the two paths stay identical for the same move.\n */\nexport const formatMoveSuccessMessage = ({\n formatMessage,\n count,\n source,\n destination,\n}: FormatMoveSuccessArgs): string =>\n formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}',\n },\n { count, source, destination }\n );\n"],"names":["formatMoveSuccessMessage","formatMessage","count","source","destination","id","getTranslationKey","defaultMessage"],"mappings":";;;;AAWA;;;;AAIC,IACM,MAAMA,wBAAAA,GAA2B,CAAC,EACvCC,aAAa,EACbC,KAAK,EACLC,MAAM,EACNC,WAAW,EACW,GACtBH,aAAAA,CACE;AACEI,QAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,gCAAA,CAAA;QACtBC,cAAAA,EACE;KACJ,EACA;AAAEL,QAAAA,KAAAA;AAAOC,QAAAA,MAAAA;AAAQC,QAAAA;KAAY;;;;"}
1
+ {"version":3,"file":"formatMoveSuccessMessage.js","sources":["../../../../admin/src/future/utils/formatMoveSuccessMessage.ts"],"sourcesContent":["import { getTranslationKey } from './translations';\n\nimport type { IntlShape } from 'react-intl';\n\ninterface FormatMoveSuccessArgs {\n formatMessage: IntlShape['formatMessage'];\n count: number;\n /**\n * Leaf name of the folder the items came from, or `null` when the move spans\n * several source folders and no single one can be named — a global-search\n * selection can hold items from anywhere. The wording then omits the source\n * rather than naming one folder and implying the rest came from it too.\n */\n source: string | null;\n destination: string;\n}\n\n/**\n * Single source of truth for the bulk-move success wording. Both the DnD drop\n * and the BulkMoveDialog format the toast (and DnD live-region announcement)\n * through here so the two paths stay identical for the same move.\n */\nexport const formatMoveSuccessMessage = ({\n formatMessage,\n count,\n source,\n destination,\n}: FormatMoveSuccessArgs): string => {\n if (source === null) {\n return formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success-multiple-sources'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved to {destination}',\n },\n { count, destination }\n );\n }\n\n return formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}',\n },\n { count, source, destination }\n );\n};\n"],"names":["formatMoveSuccessMessage","formatMessage","count","source","destination","id","getTranslationKey","defaultMessage"],"mappings":";;;;AAiBA;;;;AAIC,IACM,MAAMA,wBAAAA,GAA2B,CAAC,EACvCC,aAAa,EACbC,KAAK,EACLC,MAAM,EACNC,WAAW,EACW,GAAA;AACtB,IAAA,IAAID,WAAW,IAAA,EAAM;AACnB,QAAA,OAAOF,aAAAA,CACL;AACEI,YAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,iDAAA,CAAA;YACtBC,cAAAA,EACE;SACJ,EACA;AAAEL,YAAAA,KAAAA;AAAOE,YAAAA;AAAY,SAAA,CAAA;AAEzB,IAAA;AAEA,IAAA,OAAOH,aAAAA,CACL;AACEI,QAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,gCAAA,CAAA;QACtBC,cAAAA,EACE;KACJ,EACA;AAAEL,QAAAA,KAAAA;AAAOC,QAAAA,MAAAA;AAAQC,QAAAA;AAAY,KAAA,CAAA;AAEjC;;;;"}
@@ -4,7 +4,17 @@ import { getTranslationKey } from './translations.mjs';
4
4
  * Single source of truth for the bulk-move success wording. Both the DnD drop
5
5
  * and the BulkMoveDialog format the toast (and DnD live-region announcement)
6
6
  * through here so the two paths stay identical for the same move.
7
- */ const formatMoveSuccessMessage = ({ formatMessage, count, source, destination })=>formatMessage({
7
+ */ const formatMoveSuccessMessage = ({ formatMessage, count, source, destination })=>{
8
+ if (source === null) {
9
+ return formatMessage({
10
+ id: getTranslationKey('list.bulk-actions.move.success-multiple-sources'),
11
+ defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved to {destination}'
12
+ }, {
13
+ count,
14
+ destination
15
+ });
16
+ }
17
+ return formatMessage({
8
18
  id: getTranslationKey('list.bulk-actions.move.success'),
9
19
  defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}'
10
20
  }, {
@@ -12,6 +22,7 @@ import { getTranslationKey } from './translations.mjs';
12
22
  source,
13
23
  destination
14
24
  });
25
+ };
15
26
 
16
27
  export { formatMoveSuccessMessage };
17
28
  //# sourceMappingURL=formatMoveSuccessMessage.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatMoveSuccessMessage.mjs","sources":["../../../../admin/src/future/utils/formatMoveSuccessMessage.ts"],"sourcesContent":["import { getTranslationKey } from './translations';\n\nimport type { IntlShape } from 'react-intl';\n\ninterface FormatMoveSuccessArgs {\n formatMessage: IntlShape['formatMessage'];\n count: number;\n source: string;\n destination: string;\n}\n\n/**\n * Single source of truth for the bulk-move success wording. Both the DnD drop\n * and the BulkMoveDialog format the toast (and DnD live-region announcement)\n * through here so the two paths stay identical for the same move.\n */\nexport const formatMoveSuccessMessage = ({\n formatMessage,\n count,\n source,\n destination,\n}: FormatMoveSuccessArgs): string =>\n formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}',\n },\n { count, source, destination }\n );\n"],"names":["formatMoveSuccessMessage","formatMessage","count","source","destination","id","getTranslationKey","defaultMessage"],"mappings":";;AAWA;;;;AAIC,IACM,MAAMA,wBAAAA,GAA2B,CAAC,EACvCC,aAAa,EACbC,KAAK,EACLC,MAAM,EACNC,WAAW,EACW,GACtBH,aAAAA,CACE;AACEI,QAAAA,EAAAA,EAAIC,iBAAAA,CAAkB,gCAAA,CAAA;QACtBC,cAAAA,EACE;KACJ,EACA;AAAEL,QAAAA,KAAAA;AAAOC,QAAAA,MAAAA;AAAQC,QAAAA;KAAY;;;;"}
1
+ {"version":3,"file":"formatMoveSuccessMessage.mjs","sources":["../../../../admin/src/future/utils/formatMoveSuccessMessage.ts"],"sourcesContent":["import { getTranslationKey } from './translations';\n\nimport type { IntlShape } from 'react-intl';\n\ninterface FormatMoveSuccessArgs {\n formatMessage: IntlShape['formatMessage'];\n count: number;\n /**\n * Leaf name of the folder the items came from, or `null` when the move spans\n * several source folders and no single one can be named — a global-search\n * selection can hold items from anywhere. The wording then omits the source\n * rather than naming one folder and implying the rest came from it too.\n */\n source: string | null;\n destination: string;\n}\n\n/**\n * Single source of truth for the bulk-move success wording. Both the DnD drop\n * and the BulkMoveDialog format the toast (and DnD live-region announcement)\n * through here so the two paths stay identical for the same move.\n */\nexport const formatMoveSuccessMessage = ({\n formatMessage,\n count,\n source,\n destination,\n}: FormatMoveSuccessArgs): string => {\n if (source === null) {\n return formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success-multiple-sources'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved to {destination}',\n },\n { count, destination }\n );\n }\n\n return formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}',\n },\n { count, source, destination }\n );\n};\n"],"names":["formatMoveSuccessMessage","formatMessage","count","source","destination","id","getTranslationKey","defaultMessage"],"mappings":";;AAiBA;;;;AAIC,IACM,MAAMA,wBAAAA,GAA2B,CAAC,EACvCC,aAAa,EACbC,KAAK,EACLC,MAAM,EACNC,WAAW,EACW,GAAA;AACtB,IAAA,IAAID,WAAW,IAAA,EAAM;AACnB,QAAA,OAAOF,aAAAA,CACL;AACEI,YAAAA,EAAAA,EAAIC,iBAAAA,CAAkB,iDAAA,CAAA;YACtBC,cAAAA,EACE;SACJ,EACA;AAAEL,YAAAA,KAAAA;AAAOE,YAAAA;AAAY,SAAA,CAAA;AAEzB,IAAA;AAEA,IAAA,OAAOH,aAAAA,CACL;AACEI,QAAAA,EAAAA,EAAIC,iBAAAA,CAAkB,gCAAA,CAAA;QACtBC,cAAAA,EACE;KACJ,EACA;AAAEL,QAAAA,KAAAA;AAAOC,QAAAA,MAAAA;AAAQC,QAAAA;AAAY,KAAA,CAAA;AAEjC;;;;"}
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Normalise a to-one relation down to its id.
5
+ *
6
+ * The contracts type `File['folder']` and `Folder['parent']` as ids, but the
7
+ * admin populates both (`populate: { folder: true }` on `/upload/files`,
8
+ * `populate: { parent: true }` on `/upload/folders`), so the wire value is an
9
+ * object. Only the id is ever read — move validation compares locations by id.
10
+ */ const getRelationId = (relation)=>{
11
+ if (relation == null) {
12
+ return null;
13
+ }
14
+ if (typeof relation === 'object') {
15
+ return relation.id ?? null;
16
+ }
17
+ if (typeof relation === 'number') {
18
+ return relation;
19
+ }
20
+ return Number(relation) || null;
21
+ };
22
+
23
+ exports.getRelationId = getRelationId;
24
+ //# sourceMappingURL=getRelationId.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getRelationId.js","sources":["../../../../admin/src/future/utils/getRelationId.ts"],"sourcesContent":["/**\n * Normalise a to-one relation down to its id.\n *\n * The contracts type `File['folder']` and `Folder['parent']` as ids, but the\n * admin populates both (`populate: { folder: true }` on `/upload/files`,\n * `populate: { parent: true }` on `/upload/folders`), so the wire value is an\n * object. Only the id is ever read — move validation compares locations by id.\n */\nexport const getRelationId = (\n relation: number | string | { id?: number } | null | undefined\n): number | null => {\n if (relation == null) {\n return null;\n }\n\n if (typeof relation === 'object') {\n return relation.id ?? null;\n }\n\n if (typeof relation === 'number') {\n return relation;\n }\n\n return Number(relation) || null;\n};\n"],"names":["getRelationId","relation","id","Number"],"mappings":";;AAAA;;;;;;;IAQO,MAAMA,aAAAA,GAAgB,CAC3BC,QAAAA,GAAAA;AAEA,IAAA,IAAIA,YAAY,IAAA,EAAM;QACpB,OAAO,IAAA;AACT,IAAA;IAEA,IAAI,OAAOA,aAAa,QAAA,EAAU;QAChC,OAAOA,QAAAA,CAASC,EAAE,IAAI,IAAA;AACxB,IAAA;IAEA,IAAI,OAAOD,aAAa,QAAA,EAAU;QAChC,OAAOA,QAAAA;AACT,IAAA;AAEA,IAAA,OAAOE,OAAOF,QAAAA,CAAAA,IAAa,IAAA;AAC7B;;;;"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Normalise a to-one relation down to its id.
3
+ *
4
+ * The contracts type `File['folder']` and `Folder['parent']` as ids, but the
5
+ * admin populates both (`populate: { folder: true }` on `/upload/files`,
6
+ * `populate: { parent: true }` on `/upload/folders`), so the wire value is an
7
+ * object. Only the id is ever read — move validation compares locations by id.
8
+ */ const getRelationId = (relation)=>{
9
+ if (relation == null) {
10
+ return null;
11
+ }
12
+ if (typeof relation === 'object') {
13
+ return relation.id ?? null;
14
+ }
15
+ if (typeof relation === 'number') {
16
+ return relation;
17
+ }
18
+ return Number(relation) || null;
19
+ };
20
+
21
+ export { getRelationId };
22
+ //# sourceMappingURL=getRelationId.mjs.map