@strapi/upload 5.51.0 → 5.51.2
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/components/AssetCard/VideoPreview.js +1 -1
- package/dist/admin/components/AssetCard/VideoPreview.js.map +1 -1
- package/dist/admin/components/AssetCard/VideoPreview.mjs +1 -1
- package/dist/admin/components/AssetCard/VideoPreview.mjs.map +1 -1
- package/dist/admin/components/TableList/TableList.js +1 -1
- package/dist/admin/components/TableList/TableList.js.map +1 -1
- package/dist/admin/components/TableList/TableList.mjs +1 -1
- package/dist/admin/components/TableList/TableList.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js +21 -4
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs +21 -4
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs.map +1 -1
- package/dist/admin/future/components/Drawer.js +3 -1
- package/dist/admin/future/components/Drawer.js.map +1 -1
- package/dist/admin/future/components/Drawer.mjs +3 -1
- package/dist/admin/future/components/Drawer.mjs.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.js +200 -27
- package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.mjs +203 -30
- package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
- package/dist/admin/future/hooks/useMediaLibraryPermissions.js +35 -0
- package/dist/admin/future/hooks/useMediaLibraryPermissions.js.map +1 -0
- package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs +33 -0
- package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/AssetsPage.js +344 -150
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +343 -149
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +75 -32
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +75 -32
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +46 -19
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +47 -20
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +161 -19
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +162 -20
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +100 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +98 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +218 -37
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +219 -38
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +370 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +368 -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/DateRangeCalendar.js +193 -0
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs +191 -0
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +129 -37
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +131 -39
- 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/DropZone/UploadDropZoneContext.js +13 -3
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs +13 -3
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/EmptyState.js +62 -3
- package/dist/admin/future/pages/Assets/components/EmptyState.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/EmptyState.mjs +62 -4
- package/dist/admin/future/pages/Assets/components/EmptyState.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterBadges.js +408 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +406 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.js +273 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +267 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +195 -40
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +198 -43
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js +33 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs +31 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs.map +1 -0
- 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 +154 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs +152 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js +40 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs +38 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +75 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +71 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js +33 -12
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs +33 -12
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +40 -4
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +40 -4
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useListFilters.js +168 -0
- package/dist/admin/future/pages/Assets/hooks/useListFilters.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs +162 -0
- package/dist/admin/future/pages/Assets/hooks/useListFilters.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/buildAssetFilters.js +201 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.mjs +198 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.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/api.js +163 -28
- package/dist/admin/future/services/api.js.map +1 -1
- package/dist/admin/future/services/api.mjs +163 -30
- package/dist/admin/future/services/api.mjs.map +1 -1
- package/dist/admin/future/services/assets.js +62 -18
- package/dist/admin/future/services/assets.js.map +1 -1
- package/dist/admin/future/services/assets.mjs +62 -19
- package/dist/admin/future/services/assets.mjs.map +1 -1
- package/dist/admin/future/services/folders.js +31 -19
- package/dist/admin/future/services/folders.js.map +1 -1
- package/dist/admin/future/services/folders.mjs +31 -19
- package/dist/admin/future/services/folders.mjs.map +1 -1
- package/dist/admin/future/services/settings.js +10 -3
- package/dist/admin/future/services/settings.js.map +1 -1
- package/dist/admin/future/services/settings.mjs +10 -3
- package/dist/admin/future/services/settings.mjs.map +1 -1
- package/dist/admin/future/store/uploadProgress.js +109 -2
- package/dist/admin/future/store/uploadProgress.js.map +1 -1
- package/dist/admin/future/store/uploadProgress.mjs +105 -3
- package/dist/admin/future/store/uploadProgress.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/future/utils/searchQueryParam.js +11 -0
- package/dist/admin/future/utils/searchQueryParam.js.map +1 -0
- package/dist/admin/future/utils/searchQueryParam.mjs +9 -0
- package/dist/admin/future/utils/searchQueryParam.mjs.map +1 -0
- package/dist/admin/hooks/useSelectionState.js +2 -4
- package/dist/admin/hooks/useSelectionState.js.map +1 -1
- package/dist/admin/hooks/useSelectionState.mjs +2 -4
- package/dist/admin/hooks/useSelectionState.mjs.map +1 -1
- package/dist/admin/shared/constants.js +43 -0
- package/dist/admin/shared/constants.js.map +1 -0
- package/dist/admin/shared/constants.mjs +38 -0
- package/dist/admin/shared/constants.mjs.map +1 -0
- package/dist/admin/src/future/hooks/useMediaLibraryPermissions.d.ts +21 -0
- package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetCropEditor.d.ts +7 -1
- package/dist/admin/src/future/pages/Assets/components/AssetsSearchInput.d.ts +5 -0
- package/dist/admin/src/future/pages/Assets/components/AssetsTable.d.ts +7 -1
- package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +14 -0
- package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +13 -0
- package/dist/admin/src/future/pages/Assets/components/DateRangeCalendar.d.ts +8 -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/DropZone/UploadDropZoneContext.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/components/EmptyState.d.ts +18 -1
- package/dist/admin/src/future/pages/Assets/components/FilterBadges.d.ts +6 -0
- package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +22 -0
- package/dist/admin/src/future/pages/Assets/components/FolderTree/FolderTree.d.ts +13 -4
- package/dist/admin/src/future/pages/Assets/components/FolderTree/filterFolderTree.d.ts +16 -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 +23 -0
- package/dist/admin/src/future/pages/Assets/hooks/useAssetSearch.d.ts +12 -0
- package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +46 -0
- package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +13 -3
- package/dist/admin/src/future/pages/Assets/hooks/useListFilters.d.ts +69 -0
- package/dist/admin/src/future/pages/Assets/hooks/useListSort.d.ts +47 -0
- package/dist/admin/src/future/pages/Assets/utils/buildAssetFilters.d.ts +46 -0
- package/dist/admin/src/future/pages/Assets/utils/listQueryKey.d.ts +29 -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/api.d.ts +53 -5
- package/dist/admin/src/future/services/assets.d.ts +13 -2
- package/dist/admin/src/future/services/folders.d.ts +7 -1
- package/dist/admin/src/future/services/settings.d.ts +2 -2
- package/dist/admin/src/future/store/uploadProgress.d.ts +83 -1
- 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/src/future/utils/searchQueryParam.d.ts +7 -0
- package/dist/admin/src/hooks/useAiAvailability.d.ts +1 -1
- package/dist/admin/src/hooks/useEditAsset.d.ts +4 -4
- package/dist/admin/src/hooks/useEditFolder.d.ts +4 -4
- package/dist/admin/src/hooks/useSettings.d.ts +1 -0
- package/dist/admin/src/hooks/useTracking.d.ts +1 -1
- package/dist/admin/src/hooks/useUpload.d.ts +1 -1
- package/dist/admin/translations/en.json.js +85 -2
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +85 -2
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/urlYupSchema.js +33 -9
- package/dist/admin/utils/urlYupSchema.js.map +1 -1
- package/dist/admin/utils/urlYupSchema.mjs +33 -9
- package/dist/admin/utils/urlYupSchema.mjs.map +1 -1
- package/dist/server/config.js +13 -5
- package/dist/server/config.js.map +1 -1
- package/dist/server/config.mjs +13 -5
- package/dist/server/config.mjs.map +1 -1
- package/dist/server/constants.js.map +1 -1
- package/dist/server/constants.mjs.map +1 -1
- package/dist/server/controllers/admin-file.js +30 -0
- package/dist/server/controllers/admin-file.js.map +1 -1
- package/dist/server/controllers/admin-file.mjs +30 -0
- package/dist/server/controllers/admin-file.mjs.map +1 -1
- package/dist/server/controllers/admin-settings.js +20 -2
- package/dist/server/controllers/admin-settings.js.map +1 -1
- package/dist/server/controllers/admin-settings.mjs +21 -3
- package/dist/server/controllers/admin-settings.mjs.map +1 -1
- package/dist/server/controllers/content-api.js +1 -2
- package/dist/server/controllers/content-api.js.map +1 -1
- package/dist/server/controllers/content-api.mjs +2 -2
- package/dist/server/controllers/content-api.mjs.map +1 -1
- package/dist/server/controllers/validation/admin/ai-metadata.js +12 -0
- package/dist/server/controllers/validation/admin/ai-metadata.js.map +1 -0
- package/dist/server/controllers/validation/admin/ai-metadata.mjs +10 -0
- package/dist/server/controllers/validation/admin/ai-metadata.mjs.map +1 -0
- package/dist/server/controllers/validation/admin/settings.js +4 -1
- package/dist/server/controllers/validation/admin/settings.js.map +1 -1
- package/dist/server/controllers/validation/admin/settings.mjs +1 -1
- package/dist/server/controllers/validation/admin/settings.mjs.map +1 -1
- package/dist/server/routes/admin.js +18 -0
- package/dist/server/routes/admin.js.map +1 -1
- package/dist/server/routes/admin.mjs +18 -0
- package/dist/server/routes/admin.mjs.map +1 -1
- package/dist/server/services/ai-metadata.js +108 -11
- package/dist/server/services/ai-metadata.js.map +1 -1
- package/dist/server/services/ai-metadata.mjs +108 -11
- package/dist/server/services/ai-metadata.mjs.map +1 -1
- package/dist/server/services/file.js +86 -13
- package/dist/server/services/file.js.map +1 -1
- package/dist/server/services/file.mjs +87 -14
- package/dist/server/services/file.mjs.map +1 -1
- package/dist/server/services/image-manipulation.js +4 -4
- package/dist/server/services/image-manipulation.js.map +1 -1
- package/dist/server/services/image-manipulation.mjs +4 -4
- package/dist/server/services/image-manipulation.mjs.map +1 -1
- package/dist/server/shared/constants.js +43 -0
- package/dist/server/shared/constants.js.map +1 -0
- package/dist/server/shared/constants.mjs +38 -0
- package/dist/server/shared/constants.mjs.map +1 -0
- package/dist/server/src/config.d.ts +2 -0
- package/dist/server/src/config.d.ts.map +1 -1
- package/dist/server/src/constants.d.ts +2 -1
- package/dist/server/src/constants.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-file.d.ts +10 -0
- package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-settings.d.ts.map +1 -1
- package/dist/server/src/controllers/index.d.ts +1 -0
- package/dist/server/src/controllers/index.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts +2 -0
- package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/admin/settings.d.ts +1 -1
- package/dist/server/src/controllers/validation/admin/settings.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +7 -1
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/routes/admin.d.ts.map +1 -1
- package/dist/server/src/services/ai-metadata.d.ts +19 -0
- package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
- package/dist/server/src/services/file.d.ts +12 -2
- package/dist/server/src/services/file.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +4 -1
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/types.d.ts +3 -0
- package/dist/server/src/types.d.ts.map +1 -1
- package/dist/shared/constants.d.ts +35 -0
- package/dist/shared/constants.d.ts.map +1 -0
- package/dist/shared/contracts/files.d.ts +25 -0
- package/dist/shared/contracts/files.d.ts.map +1 -1
- package/dist/shared/contracts/folders.d.ts +1 -1
- package/dist/shared/contracts/settings.d.ts +7 -0
- package/package.json +8 -8
|
@@ -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":"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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} 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,3 +1,4 @@
|
|
|
1
|
+
import { encodeSearchQuery } from '../utils/searchQueryParam.mjs';
|
|
1
2
|
import { uploadApi } from './api.mjs';
|
|
2
3
|
|
|
3
4
|
const isDataEnvelope = (response)=>typeof response === 'object' && response !== null && 'data' in response;
|
|
@@ -6,31 +7,42 @@ const foldersApi = uploadApi.injectEndpoints({
|
|
|
6
7
|
endpoints: (builder)=>({
|
|
7
8
|
getFolders: builder.query({
|
|
8
9
|
query: (params = {})=>{
|
|
9
|
-
const { parentId } = params;
|
|
10
|
+
const { parentId, sort, search, filters = [] } = params;
|
|
10
11
|
const queryParams = {
|
|
11
|
-
//
|
|
12
|
-
sort: 'name:ASC'
|
|
12
|
+
// Default matches sidebar FolderTree order (server getStructure uses sortBy('name')).
|
|
13
|
+
sort: sort ?? 'name:ASC'
|
|
13
14
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
// List filters (dates) apply in BOTH modes — search composes with them,
|
|
16
|
+
// only the parent scope is dropped while searching.
|
|
17
|
+
if (search) {
|
|
18
|
+
// Search is global: the parent filter is dropped so matching folders
|
|
19
|
+
// anywhere in the library surface. The endpoint is unpaginated — it
|
|
20
|
+
// returns every match — so callers can treat the array length as the
|
|
21
|
+
// true total. Bounding it is a separate decision.
|
|
22
|
+
queryParams['_q'] = encodeSearchQuery(search);
|
|
23
|
+
if (filters.length > 0) {
|
|
24
|
+
queryParams['filters'] = {
|
|
25
|
+
$and: [
|
|
26
|
+
...filters
|
|
27
|
+
]
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
const parentScope = parentId != null ? {
|
|
32
|
+
parent: {
|
|
33
|
+
id: parentId
|
|
34
|
+
}
|
|
35
|
+
} : {
|
|
36
|
+
parent: {
|
|
37
|
+
id: {
|
|
38
|
+
$null: true
|
|
21
39
|
}
|
|
22
|
-
|
|
40
|
+
}
|
|
23
41
|
};
|
|
24
|
-
} else {
|
|
25
42
|
queryParams['filters'] = {
|
|
26
43
|
$and: [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
id: {
|
|
30
|
-
$null: true
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
44
|
+
parentScope,
|
|
45
|
+
...filters
|
|
34
46
|
]
|
|
35
47
|
};
|
|
36
48
|
}
|
|
@@ -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 { 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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"}
|
|
@@ -4,7 +4,14 @@ var api = require('./api.js');
|
|
|
4
4
|
|
|
5
5
|
const settingsApi = api.uploadApi.injectEndpoints({
|
|
6
6
|
endpoints: (builder)=>({
|
|
7
|
-
getSettings
|
|
7
|
+
// Deliberately NOT named `getSettings`. Every plugin's admin API is built on
|
|
8
|
+
// the same shared `adminApi` instance — one endpoint registry keyed by name,
|
|
9
|
+
// under `reducerPath: 'adminApi'` — and `injectEndpoints` silently keeps the
|
|
10
|
+
// FIRST registration of a duplicate name and discards later ones. The i18n
|
|
11
|
+
// plugin already registers a `getSettings`, so a generic name here would,
|
|
12
|
+
// depending on module load order, resolve this hook to i18n's cache entry
|
|
13
|
+
// (`/i18n/settings`) instead of `/upload/settings` — non-deterministically.
|
|
14
|
+
getUploadSettings: builder.query({
|
|
8
15
|
query: ()=>({
|
|
9
16
|
url: '/upload/settings',
|
|
10
17
|
method: 'GET'
|
|
@@ -12,7 +19,7 @@ const settingsApi = api.uploadApi.injectEndpoints({
|
|
|
12
19
|
})
|
|
13
20
|
})
|
|
14
21
|
});
|
|
15
|
-
const {
|
|
22
|
+
const { useGetUploadSettingsQuery } = settingsApi;
|
|
16
23
|
|
|
17
|
-
exports.
|
|
24
|
+
exports.useGetUploadSettingsQuery = useGetUploadSettingsQuery;
|
|
18
25
|
//# sourceMappingURL=settings.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.js","sources":["../../../../admin/src/future/services/settings.ts"],"sourcesContent":["import { uploadApi } from './api';\n\nimport type { GetSettings } from '../../../../shared/contracts/settings';\n\nconst settingsApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n getSettings: builder.query<GetSettings.Response['data'], void>({\n query: () => ({\n url: '/upload/settings',\n method: 'GET',\n }),\n }),\n }),\n});\n\nconst {
|
|
1
|
+
{"version":3,"file":"settings.js","sources":["../../../../admin/src/future/services/settings.ts"],"sourcesContent":["import { uploadApi } from './api';\n\nimport type { GetSettings } from '../../../../shared/contracts/settings';\n\nconst settingsApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n // Deliberately NOT named `getSettings`. Every plugin's admin API is built on\n // the same shared `adminApi` instance — one endpoint registry keyed by name,\n // under `reducerPath: 'adminApi'` — and `injectEndpoints` silently keeps the\n // FIRST registration of a duplicate name and discards later ones. The i18n\n // plugin already registers a `getSettings`, so a generic name here would,\n // depending on module load order, resolve this hook to i18n's cache entry\n // (`/i18n/settings`) instead of `/upload/settings` — non-deterministically.\n getUploadSettings: builder.query<GetSettings.Response['data'], void>({\n query: () => ({\n url: '/upload/settings',\n method: 'GET',\n }),\n }),\n }),\n});\n\nconst { useGetUploadSettingsQuery } = settingsApi;\n\nexport { useGetUploadSettingsQuery };\n"],"names":["settingsApi","uploadApi","injectEndpoints","endpoints","builder","getUploadSettings","query","url","method","useGetUploadSettingsQuery"],"mappings":";;;;AAIA,MAAMA,WAAAA,GAAcC,aAAAA,CAAUC,eAAe,CAAC;IAC5CC,SAAAA,EAAW,CAACC,WAAa;;;;;;;;YAQvBC,iBAAAA,EAAmBD,OAAAA,CAAQE,KAAK,CAAqC;AACnEA,gBAAAA,KAAAA,EAAO,KAAO;wBACZC,GAAAA,EAAK,kBAAA;wBACLC,MAAAA,EAAQ;qBACV;AACF,aAAA;SACF;AACF,CAAA,CAAA;AAEA,MAAM,EAAEC,yBAAyB,EAAE,GAAGT;;;;"}
|
|
@@ -2,7 +2,14 @@ import { uploadApi } from './api.mjs';
|
|
|
2
2
|
|
|
3
3
|
const settingsApi = uploadApi.injectEndpoints({
|
|
4
4
|
endpoints: (builder)=>({
|
|
5
|
-
getSettings
|
|
5
|
+
// Deliberately NOT named `getSettings`. Every plugin's admin API is built on
|
|
6
|
+
// the same shared `adminApi` instance — one endpoint registry keyed by name,
|
|
7
|
+
// under `reducerPath: 'adminApi'` — and `injectEndpoints` silently keeps the
|
|
8
|
+
// FIRST registration of a duplicate name and discards later ones. The i18n
|
|
9
|
+
// plugin already registers a `getSettings`, so a generic name here would,
|
|
10
|
+
// depending on module load order, resolve this hook to i18n's cache entry
|
|
11
|
+
// (`/i18n/settings`) instead of `/upload/settings` — non-deterministically.
|
|
12
|
+
getUploadSettings: builder.query({
|
|
6
13
|
query: ()=>({
|
|
7
14
|
url: '/upload/settings',
|
|
8
15
|
method: 'GET'
|
|
@@ -10,7 +17,7 @@ const settingsApi = uploadApi.injectEndpoints({
|
|
|
10
17
|
})
|
|
11
18
|
})
|
|
12
19
|
});
|
|
13
|
-
const {
|
|
20
|
+
const { useGetUploadSettingsQuery } = settingsApi;
|
|
14
21
|
|
|
15
|
-
export {
|
|
22
|
+
export { useGetUploadSettingsQuery };
|
|
16
23
|
//# sourceMappingURL=settings.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.mjs","sources":["../../../../admin/src/future/services/settings.ts"],"sourcesContent":["import { uploadApi } from './api';\n\nimport type { GetSettings } from '../../../../shared/contracts/settings';\n\nconst settingsApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n getSettings: builder.query<GetSettings.Response['data'], void>({\n query: () => ({\n url: '/upload/settings',\n method: 'GET',\n }),\n }),\n }),\n});\n\nconst {
|
|
1
|
+
{"version":3,"file":"settings.mjs","sources":["../../../../admin/src/future/services/settings.ts"],"sourcesContent":["import { uploadApi } from './api';\n\nimport type { GetSettings } from '../../../../shared/contracts/settings';\n\nconst settingsApi = uploadApi.injectEndpoints({\n endpoints: (builder) => ({\n // Deliberately NOT named `getSettings`. Every plugin's admin API is built on\n // the same shared `adminApi` instance — one endpoint registry keyed by name,\n // under `reducerPath: 'adminApi'` — and `injectEndpoints` silently keeps the\n // FIRST registration of a duplicate name and discards later ones. The i18n\n // plugin already registers a `getSettings`, so a generic name here would,\n // depending on module load order, resolve this hook to i18n's cache entry\n // (`/i18n/settings`) instead of `/upload/settings` — non-deterministically.\n getUploadSettings: builder.query<GetSettings.Response['data'], void>({\n query: () => ({\n url: '/upload/settings',\n method: 'GET',\n }),\n }),\n }),\n});\n\nconst { useGetUploadSettingsQuery } = settingsApi;\n\nexport { useGetUploadSettingsQuery };\n"],"names":["settingsApi","uploadApi","injectEndpoints","endpoints","builder","getUploadSettings","query","url","method","useGetUploadSettingsQuery"],"mappings":";;AAIA,MAAMA,WAAAA,GAAcC,SAAAA,CAAUC,eAAe,CAAC;IAC5CC,SAAAA,EAAW,CAACC,WAAa;;;;;;;;YAQvBC,iBAAAA,EAAmBD,OAAAA,CAAQE,KAAK,CAAqC;AACnEA,gBAAAA,KAAAA,EAAO,KAAO;wBACZC,GAAAA,EAAK,kBAAA;wBACLC,MAAAA,EAAQ;qBACV;AACF,aAAA;SACF;AACF,CAAA,CAAA;AAEA,MAAM,EAAEC,yBAAyB,EAAE,GAAGT;;;;"}
|
|
@@ -68,6 +68,34 @@ const uploadProgressSlice = toolkit.createSlice({
|
|
|
68
68
|
}
|
|
69
69
|
];
|
|
70
70
|
},
|
|
71
|
+
/**
|
|
72
|
+
* Metadata generation started for a row.
|
|
73
|
+
*
|
|
74
|
+
* Metadata requests are fired-and-forgotten, so their callbacks can land after
|
|
75
|
+
* a new batch has replaced `files` and reused the same row indices. Every
|
|
76
|
+
* metadata payload therefore carries the `uploadId` it was fired for, and stale
|
|
77
|
+
* ones are dropped instead of writing onto another batch's row.
|
|
78
|
+
*/ setFileMetadataGenerating (state, action) {
|
|
79
|
+
const { index, uploadId } = action.payload;
|
|
80
|
+
if (uploadId !== state.uploadId) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (state.files[index]) {
|
|
84
|
+
state.files[index].metadataStatus = 'generating';
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
/**
|
|
88
|
+
* Terminal metadata outcome for a row. Never touches `status`/`error`/`errors`:
|
|
89
|
+
* the upload itself already succeeded regardless of how generation went.
|
|
90
|
+
*/ setFileMetadataResult (state, action) {
|
|
91
|
+
const { index, uploadId, status } = action.payload;
|
|
92
|
+
if (uploadId !== state.uploadId) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (state.files[index]) {
|
|
96
|
+
state.files[index].metadataStatus = status;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
71
99
|
addUploadErrors (state, action) {
|
|
72
100
|
state.errors = [
|
|
73
101
|
...state.errors,
|
|
@@ -123,7 +151,9 @@ const uploadProgressSlice = toolkit.createSlice({
|
|
|
123
151
|
return {
|
|
124
152
|
...file,
|
|
125
153
|
status: 'pending',
|
|
126
|
-
uploadedBytes: 0
|
|
154
|
+
uploadedBytes: 0,
|
|
155
|
+
// A retried row goes through the metadata phase again from scratch.
|
|
156
|
+
metadataStatus: undefined
|
|
127
157
|
};
|
|
128
158
|
}
|
|
129
159
|
return file;
|
|
@@ -146,7 +176,79 @@ const uploadProgressSlice = toolkit.createSlice({
|
|
|
146
176
|
const uploadedBytes = files.reduce((sum, f)=>sum + f.uploadedBytes, 0);
|
|
147
177
|
return Math.round(uploadedBytes / totalSize * 100);
|
|
148
178
|
});
|
|
149
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Count-based metadata progress across the whole batch: `settled / expected`.
|
|
181
|
+
*
|
|
182
|
+
* Count-based rather than byte-weighted because the generation endpoint answers in one
|
|
183
|
+
* go — there is no intermediate progress to weight.
|
|
184
|
+
*
|
|
185
|
+
* The denominator is every row *expected* to enter the phase, not just those that
|
|
186
|
+
* already have. Generation is fired per file as each upload finishes, so counting only
|
|
187
|
+
* started rows made the denominator grow mid-batch and the percentage jump backwards
|
|
188
|
+
* (1/1 = 100%, then a second row starts and it drops to 1/2 = 50%), which flickered the
|
|
189
|
+
* header subtitle on and off once per file.
|
|
190
|
+
*
|
|
191
|
+
* "Expected" is derived from upload outcome rather than from the AI-metadata flag:
|
|
192
|
+
* - a row still uploading may yet enter the phase, so it counts toward the total;
|
|
193
|
+
* - an errored or cancelled row never will, so it must not — otherwise a batch with
|
|
194
|
+
* one failure could never reach 100%.
|
|
195
|
+
*
|
|
196
|
+
* Returns `null` when nothing has entered the phase and nothing is pending — i.e. AI
|
|
197
|
+
* metadata is off, or the batch is done — so the header can hide the subtitle.
|
|
198
|
+
*/ const selectMetadataProgress = toolkit.createSelector((state)=>state.uploadProgress.files, (files)=>{
|
|
199
|
+
const started = files.filter((f)=>f.metadataStatus !== undefined);
|
|
200
|
+
// Nothing has entered the phase yet: with no started row there is no evidence AI
|
|
201
|
+
// metadata is even enabled, so stay silent rather than showing a premature 0%.
|
|
202
|
+
if (started.length === 0) return null;
|
|
203
|
+
// Rows whose upload is still in flight are counted in the denominator so the
|
|
204
|
+
// percentage only ever climbs as the batch progresses.
|
|
205
|
+
const pending = files.filter((f)=>f.metadataStatus === undefined && (f.status === 'pending' || f.status === 'uploading')).length;
|
|
206
|
+
const expected = started.length + pending;
|
|
207
|
+
const settled = started.filter((f)=>f.metadataStatus !== 'generating').length;
|
|
208
|
+
return Math.round(settled / expected * 100);
|
|
209
|
+
});
|
|
210
|
+
/**
|
|
211
|
+
* Whether the metadata phase is still ongoing for the batch as a whole.
|
|
212
|
+
*
|
|
213
|
+
* The header uses this — not `selectMetadataProgress === 100` — to decide when to drop
|
|
214
|
+
* the subtitle. Two distinct reasons the phase can be unfinished:
|
|
215
|
+
* - a row is generating right now;
|
|
216
|
+
* - no row is generating this instant, but rows are still uploading and will enter the
|
|
217
|
+
* phase when they finish.
|
|
218
|
+
*
|
|
219
|
+
* The second clause is what keeps the subtitle stable. Generation for file N typically
|
|
220
|
+
* finishes before file N+1 finishes uploading, so a naive "is anything generating?"
|
|
221
|
+
* check goes false between every pair of files and blinks the subtitle out and back in.
|
|
222
|
+
*/ const selectIsGeneratingMetadata = toolkit.createSelector((state)=>state.uploadProgress.files, (files)=>{
|
|
223
|
+
if (files.some((f)=>f.metadataStatus === 'generating')) return true;
|
|
224
|
+
// Only bridge the gap if some row has already entered the phase — otherwise a batch
|
|
225
|
+
// with AI metadata disabled would report the phase as ongoing for its whole upload.
|
|
226
|
+
const hasStarted = files.some((f)=>f.metadataStatus !== undefined);
|
|
227
|
+
return hasStarted && files.some((f)=>f.status === 'pending' || f.status === 'uploading');
|
|
228
|
+
});
|
|
229
|
+
/**
|
|
230
|
+
* How many rows reached each terminal metadata outcome, for the header's completion
|
|
231
|
+
* message. `null` while the phase is unfinished — or was never entered at all — so the
|
|
232
|
+
* caller has a single check for "there is no settled outcome to report yet".
|
|
233
|
+
*
|
|
234
|
+
* Counts are reported separately rather than collapsed into one total because they are
|
|
235
|
+
* not interchangeable: only `generated` rows actually had metadata written, so only that
|
|
236
|
+
* count can be claimed as a success. A batch of non-images settles entirely on `skipped`,
|
|
237
|
+
* where "generated on 0 files" would be worse than saying nothing.
|
|
238
|
+
*/ const selectMetadataOutcome = toolkit.createSelector((state)=>state.uploadProgress.files, (files)=>{
|
|
239
|
+
const started = files.filter((f)=>f.metadataStatus !== undefined);
|
|
240
|
+
if (started.length === 0) return null;
|
|
241
|
+
// Mirrors `selectIsGeneratingMetadata`: the phase isn't over while a row is
|
|
242
|
+
// generating, nor while a row is still uploading and has yet to enter it.
|
|
243
|
+
const isOngoing = started.some((f)=>f.metadataStatus === 'generating') || files.some((f)=>f.status === 'pending' || f.status === 'uploading');
|
|
244
|
+
if (isOngoing) return null;
|
|
245
|
+
return {
|
|
246
|
+
generated: started.filter((f)=>f.metadataStatus === 'generated').length,
|
|
247
|
+
skipped: started.filter((f)=>f.metadataStatus === 'skipped').length,
|
|
248
|
+
failed: started.filter((f)=>f.metadataStatus === 'failed').length
|
|
249
|
+
};
|
|
250
|
+
});
|
|
251
|
+
const { openUploadProgress, setFileUploading, setFileProgress, setFileComplete, setFileError, setFileMetadataGenerating, setFileMetadataResult, addUploadErrors, closeUploadProgress, toggleMinimize, cancelUpload, setUploadFailed, retryCancelledFiles } = uploadProgressSlice.actions;
|
|
150
252
|
const uploadProgressReducer = uploadProgressSlice.reducer;
|
|
151
253
|
|
|
152
254
|
exports.addUploadErrors = addUploadErrors;
|
|
@@ -155,8 +257,13 @@ exports.closeUploadProgress = closeUploadProgress;
|
|
|
155
257
|
exports.openUploadProgress = openUploadProgress;
|
|
156
258
|
exports.retryCancelledFiles = retryCancelledFiles;
|
|
157
259
|
exports.selectAggregateProgress = selectAggregateProgress;
|
|
260
|
+
exports.selectIsGeneratingMetadata = selectIsGeneratingMetadata;
|
|
261
|
+
exports.selectMetadataOutcome = selectMetadataOutcome;
|
|
262
|
+
exports.selectMetadataProgress = selectMetadataProgress;
|
|
158
263
|
exports.setFileComplete = setFileComplete;
|
|
159
264
|
exports.setFileError = setFileError;
|
|
265
|
+
exports.setFileMetadataGenerating = setFileMetadataGenerating;
|
|
266
|
+
exports.setFileMetadataResult = setFileMetadataResult;
|
|
160
267
|
exports.setFileProgress = setFileProgress;
|
|
161
268
|
exports.setFileUploading = setFileUploading;
|
|
162
269
|
exports.setUploadFailed = setUploadFailed;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploadProgress.js","sources":["../../../../admin/src/future/store/uploadProgress.ts"],"sourcesContent":["import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit';\n\nimport type { File } from '../../../../shared/contracts/files';\n\nexport interface FileUploadError {\n name: string;\n message: string;\n}\n\nexport type FileProgressStatus = 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n\nexport interface FileProgress {\n name: string;\n index: number;\n status: FileProgressStatus;\n size: number;\n uploadedBytes: number;\n file?: File;\n error?: string;\n}\n\nexport interface UploadProgressState {\n isVisible: boolean;\n isMinimized: boolean;\n totalFiles: number;\n files: FileProgress[];\n errors: FileUploadError[];\n uploadId: number;\n}\n\nexport interface RootState {\n uploadProgress: UploadProgressState;\n}\n\nconst initialState: UploadProgressState = {\n isVisible: false,\n isMinimized: false,\n totalFiles: 0,\n files: [],\n errors: [],\n uploadId: 0,\n};\n\nconst uploadProgressSlice = createSlice({\n name: 'uploadProgress',\n initialState,\n reducers: {\n openUploadProgress(\n state,\n action: PayloadAction<{\n totalFiles: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n state.isVisible = true;\n state.isMinimized = false;\n\n // Create pending files for upload\n const pendingFiles: FileProgress[] = action.payload.fileNames.map((name, index) => ({\n name,\n index,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[index] ?? 0,\n uploadedBytes: 0,\n }));\n\n state.files = pendingFiles;\n state.totalFiles = action.payload.totalFiles;\n state.errors = [];\n state.uploadId += 1;\n },\n setFileUploading(state, action: PayloadAction<{ name: string; index: number; size: number }>) {\n const { index, size } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'uploading';\n state.files[index].size = size;\n }\n },\n setFileProgress(state, action: PayloadAction<{ index: number; bytes: number }>) {\n const { index, bytes } = action.payload;\n const file = state.files[index];\n if (file) {\n // Clamp to the known file size so the aggregate can never exceed 100%.\n file.uploadedBytes = Math.min(bytes, file.size);\n }\n },\n setFileComplete(state, action: PayloadAction<{ index: number; file: File }>) {\n const { index, file } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'complete';\n state.files[index].file = file;\n // Reflect completion in the aggregate even if the final progress event was throttled.\n state.files[index].uploadedBytes = state.files[index].size;\n }\n },\n setFileError(state, action: PayloadAction<{ index: number; name: string; message: string }>) {\n const { index, name, message } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'error';\n state.files[index].error = message;\n }\n state.errors = [...state.errors, { name, message }];\n },\n addUploadErrors(state, action: PayloadAction<FileUploadError[]>) {\n state.errors = [...state.errors, ...action.payload];\n },\n closeUploadProgress(state) {\n state.isVisible = false;\n state.isMinimized = false;\n state.totalFiles = 0;\n state.files = [];\n state.errors = [];\n },\n toggleMinimize(state) {\n state.isMinimized = !state.isMinimized;\n },\n cancelUpload(state) {\n // Mark all pending and uploading files as cancelled\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return { ...file, status: 'cancelled' as FileProgressStatus };\n }\n return file;\n });\n },\n setUploadFailed(state, action: PayloadAction<{ message: string }>) {\n // Mark all pending and uploading files as errored when a catastrophic failure occurs\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return {\n ...file,\n status: 'error' as FileProgressStatus,\n error: action.payload.message,\n };\n }\n return file;\n });\n state.errors = [...state.errors, { name: 'Upload Error', message: action.payload.message }];\n },\n retryCancelledFiles(state) {\n // Reset all cancelled files back to pending for retry\n state.files = state.files.map((file) => {\n if (file.status === 'cancelled') {\n return {\n ...file,\n status: 'pending' as FileProgressStatus,\n uploadedBytes: 0,\n };\n }\n return file;\n });\n },\n },\n});\n\n/**\n * Byte-weighted aggregate progress across the whole batch: `sum(uploadedBytes) / sum(size)`.\n *\n * Falls back to count-based progress (settled files / total files) when all sizes are\n * zero — e.g. URL-flow rows where the file size is unknown up front.\n */\nexport const selectAggregateProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number => {\n if (files.length === 0) return 0;\n\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\n\n if (totalSize === 0) {\n const settled = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n return Math.round((settled / files.length) * 100);\n }\n\n const uploadedBytes = files.reduce((sum, f) => sum + f.uploadedBytes, 0);\n return Math.round((uploadedBytes / totalSize) * 100);\n }\n);\n\nexport const {\n openUploadProgress,\n setFileUploading,\n setFileProgress,\n setFileComplete,\n setFileError,\n addUploadErrors,\n closeUploadProgress,\n toggleMinimize,\n cancelUpload,\n setUploadFailed,\n retryCancelledFiles,\n} = uploadProgressSlice.actions;\n\nexport const uploadProgressReducer = uploadProgressSlice.reducer;\n"],"names":["initialState","isVisible","isMinimized","totalFiles","files","errors","uploadId","uploadProgressSlice","createSlice","name","reducers","openUploadProgress","state","action","pendingFiles","payload","fileNames","map","index","status","size","fileSizes","uploadedBytes","setFileUploading","setFileProgress","bytes","file","Math","min","setFileComplete","setFileError","message","error","addUploadErrors","closeUploadProgress","toggleMinimize","cancelUpload","setUploadFailed","retryCancelledFiles","selectAggregateProgress","createSelector","uploadProgress","length","totalSize","reduce","sum","f","settled","filter","round","actions","uploadProgressReducer","reducer"],"mappings":";;;;AAkCA,MAAMA,YAAAA,GAAoC;IACxCC,SAAAA,EAAW,KAAA;IACXC,WAAAA,EAAa,KAAA;IACbC,UAAAA,EAAY,CAAA;AACZC,IAAAA,KAAAA,EAAO,EAAE;AACTC,IAAAA,MAAAA,EAAQ,EAAE;IACVC,QAAAA,EAAU;AACZ,CAAA;AAEA,MAAMC,sBAAsBC,mBAAAA,CAAY;IACtCC,IAAAA,EAAM,gBAAA;AACNT,IAAAA,YAAAA;IACAU,QAAAA,EAAU;QACRC,kBAAAA,CAAAA,CACEC,KAAK,EACLC,MAIE,EAAA;AAEFD,YAAAA,KAAAA,CAAMX,SAAS,GAAG,IAAA;AAClBW,YAAAA,KAAAA,CAAMV,WAAW,GAAG,KAAA;;YAGpB,MAAMY,YAAAA,GAA+BD,MAAAA,CAAOE,OAAO,CAACC,SAAS,CAACC,GAAG,CAAC,CAACR,IAAAA,EAAMS,KAAAA,IAAW;AAClFT,oBAAAA,IAAAA;AACAS,oBAAAA,KAAAA;oBACAC,MAAAA,EAAQ,SAAA;AACRC,oBAAAA,IAAAA,EAAMP,OAAOE,OAAO,CAACM,SAAS,GAAGH,MAAM,IAAI,CAAA;oBAC3CI,aAAAA,EAAe;iBACjB,CAAA,CAAA;AAEAV,YAAAA,KAAAA,CAAMR,KAAK,GAAGU,YAAAA;AACdF,YAAAA,KAAAA,CAAMT,UAAU,GAAGU,MAAAA,CAAOE,OAAO,CAACZ,UAAU;YAC5CS,KAAAA,CAAMP,MAAM,GAAG,EAAE;AACjBO,YAAAA,KAAAA,CAAMN,QAAQ,IAAI,CAAA;AACpB,QAAA,CAAA;QACAiB,gBAAAA,CAAAA,CAAiBX,KAAK,EAAEC,MAAoE,EAAA;AAC1F,YAAA,MAAM,EAAEK,KAAK,EAAEE,IAAI,EAAE,GAAGP,OAAOE,OAAO;AACtC,YAAA,IAAIH,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,EAAE;AACtBN,gBAAAA,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACC,MAAM,GAAG,WAAA;AAC5BP,gBAAAA,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACE,IAAI,GAAGA,IAAAA;AAC5B,YAAA;AACF,QAAA,CAAA;QACAI,eAAAA,CAAAA,CAAgBZ,KAAK,EAAEC,MAAuD,EAAA;AAC5E,YAAA,MAAM,EAAEK,KAAK,EAAEO,KAAK,EAAE,GAAGZ,OAAOE,OAAO;AACvC,YAAA,MAAMW,IAAAA,GAAOd,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM;AAC/B,YAAA,IAAIQ,IAAAA,EAAM;;AAERA,gBAAAA,IAAAA,CAAKJ,aAAa,GAAGK,IAAAA,CAAKC,GAAG,CAACH,KAAAA,EAAOC,KAAKN,IAAI,CAAA;AAChD,YAAA;AACF,QAAA,CAAA;QACAS,eAAAA,CAAAA,CAAgBjB,KAAK,EAAEC,MAAoD,EAAA;AACzE,YAAA,MAAM,EAAEK,KAAK,EAAEQ,IAAI,EAAE,GAAGb,OAAOE,OAAO;AACtC,YAAA,IAAIH,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,EAAE;AACtBN,gBAAAA,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACC,MAAM,GAAG,UAAA;AAC5BP,gBAAAA,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACQ,IAAI,GAAGA,IAAAA;;gBAE1Bd,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACI,aAAa,GAAGV,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACE,IAAI;AAC5D,YAAA;AACF,QAAA,CAAA;QACAU,YAAAA,CAAAA,CAAalB,KAAK,EAAEC,MAAuE,EAAA;YACzF,MAAM,EAAEK,KAAK,EAAET,IAAI,EAAEsB,OAAO,EAAE,GAAGlB,MAAAA,CAAOE,OAAO;AAC/C,YAAA,IAAIH,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,EAAE;AACtBN,gBAAAA,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACC,MAAM,GAAG,OAAA;AAC5BP,gBAAAA,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACc,KAAK,GAAGD,OAAAA;AAC7B,YAAA;AACAnB,YAAAA,KAAAA,CAAMP,MAAM,GAAG;AAAIO,gBAAAA,GAAAA,KAAAA,CAAMP,MAAM;AAAE,gBAAA;AAAEI,oBAAAA,IAAAA;AAAMsB,oBAAAA;AAAQ;AAAE,aAAA;AACrD,QAAA,CAAA;QACAE,eAAAA,CAAAA,CAAgBrB,KAAK,EAAEC,MAAwC,EAAA;AAC7DD,YAAAA,KAAAA,CAAMP,MAAM,GAAG;AAAIO,gBAAAA,GAAAA,KAAAA,CAAMP,MAAM;AAAKQ,gBAAAA,GAAAA,MAAAA,CAAOE;AAAQ,aAAA;AACrD,QAAA,CAAA;AACAmB,QAAAA,mBAAAA,CAAAA,CAAoBtB,KAAK,EAAA;AACvBA,YAAAA,KAAAA,CAAMX,SAAS,GAAG,KAAA;AAClBW,YAAAA,KAAAA,CAAMV,WAAW,GAAG,KAAA;AACpBU,YAAAA,KAAAA,CAAMT,UAAU,GAAG,CAAA;YACnBS,KAAAA,CAAMR,KAAK,GAAG,EAAE;YAChBQ,KAAAA,CAAMP,MAAM,GAAG,EAAE;AACnB,QAAA,CAAA;AACA8B,QAAAA,cAAAA,CAAAA,CAAevB,KAAK,EAAA;AAClBA,YAAAA,KAAAA,CAAMV,WAAW,GAAG,CAACU,KAAAA,CAAMV,WAAW;AACxC,QAAA,CAAA;AACAkC,QAAAA,YAAAA,CAAAA,CAAaxB,KAAK,EAAA;;AAEhBA,YAAAA,KAAAA,CAAMR,KAAK,GAAGQ,KAAAA,CAAMR,KAAK,CAACa,GAAG,CAAC,CAACS,IAAAA,GAAAA;AAC7B,gBAAA,IAAIA,KAAKP,MAAM,KAAK,aAAaO,IAAAA,CAAKP,MAAM,KAAK,WAAA,EAAa;oBAC5D,OAAO;AAAE,wBAAA,GAAGO,IAAI;wBAAEP,MAAAA,EAAQ;AAAkC,qBAAA;AAC9D,gBAAA;gBACA,OAAOO,IAAAA;AACT,YAAA,CAAA,CAAA;AACF,QAAA,CAAA;QACAW,eAAAA,CAAAA,CAAgBzB,KAAK,EAAEC,MAA0C,EAAA;;AAE/DD,YAAAA,KAAAA,CAAMR,KAAK,GAAGQ,KAAAA,CAAMR,KAAK,CAACa,GAAG,CAAC,CAACS,IAAAA,GAAAA;AAC7B,gBAAA,IAAIA,KAAKP,MAAM,KAAK,aAAaO,IAAAA,CAAKP,MAAM,KAAK,WAAA,EAAa;oBAC5D,OAAO;AACL,wBAAA,GAAGO,IAAI;wBACPP,MAAAA,EAAQ,OAAA;wBACRa,KAAAA,EAAOnB,MAAAA,CAAOE,OAAO,CAACgB;AACxB,qBAAA;AACF,gBAAA;gBACA,OAAOL,IAAAA;AACT,YAAA,CAAA,CAAA;AACAd,YAAAA,KAAAA,CAAMP,MAAM,GAAG;AAAIO,gBAAAA,GAAAA,KAAAA,CAAMP,MAAM;AAAE,gBAAA;oBAAEI,IAAAA,EAAM,cAAA;oBAAgBsB,OAAAA,EAASlB,MAAAA,CAAOE,OAAO,CAACgB;AAAQ;AAAE,aAAA;AAC7F,QAAA,CAAA;AACAO,QAAAA,mBAAAA,CAAAA,CAAoB1B,KAAK,EAAA;;AAEvBA,YAAAA,KAAAA,CAAMR,KAAK,GAAGQ,KAAAA,CAAMR,KAAK,CAACa,GAAG,CAAC,CAACS,IAAAA,GAAAA;gBAC7B,IAAIA,IAAAA,CAAKP,MAAM,KAAK,WAAA,EAAa;oBAC/B,OAAO;AACL,wBAAA,GAAGO,IAAI;wBACPP,MAAAA,EAAQ,SAAA;wBACRG,aAAAA,EAAe;AACjB,qBAAA;AACF,gBAAA;gBACA,OAAOI,IAAAA;AACT,YAAA,CAAA,CAAA;AACF,QAAA;AACF;AACF,CAAA,CAAA;AAEA;;;;;IAMO,MAAMa,uBAAAA,GAA0BC,sBAAAA,CACrC,CAAC5B,KAAAA,GAAqBA,KAAAA,CAAM6B,cAAc,CAACrC,KAAK,EAChD,CAACA,KAAAA,GAAAA;AACC,IAAA,IAAIA,KAAAA,CAAMsC,MAAM,KAAK,CAAA,EAAG,OAAO,CAAA;IAE/B,MAAMC,SAAAA,GAAYvC,KAAAA,CAAMwC,MAAM,CAAC,CAACC,KAAKC,CAAAA,GAAMD,GAAAA,GAAMC,CAAAA,CAAE1B,IAAI,EAAE,CAAA,CAAA;AAEzD,IAAA,IAAIuB,cAAc,CAAA,EAAG;AACnB,QAAA,MAAMI,UAAU3C,KAAAA,CAAM4C,MAAM,CAC1B,CAACF,CAAAA,GAAMA,EAAE3B,MAAM,KAAK,UAAA,IAAc2B,CAAAA,CAAE3B,MAAM,KAAK,OAAA,IAAW2B,EAAE3B,MAAM,KAAK,aACvEuB,MAAM;AACR,QAAA,OAAOf,KAAKsB,KAAK,CAAC,OAACF,GAAU3C,KAAAA,CAAMsC,MAAM,GAAI,GAAA,CAAA;AAC/C,IAAA;IAEA,MAAMpB,aAAAA,GAAgBlB,KAAAA,CAAMwC,MAAM,CAAC,CAACC,KAAKC,CAAAA,GAAMD,GAAAA,GAAMC,CAAAA,CAAExB,aAAa,EAAE,CAAA,CAAA;AACtE,IAAA,OAAOK,IAAAA,CAAKsB,KAAK,CAAE3B,gBAAgBqB,SAAAA,GAAa,GAAA,CAAA;AAClD,CAAA;AAGK,MAAM,EACXhC,kBAAkB,EAClBY,gBAAgB,EAChBC,eAAe,EACfK,eAAe,EACfC,YAAY,EACZG,eAAe,EACfC,mBAAmB,EACnBC,cAAc,EACdC,YAAY,EACZC,eAAe,EACfC,mBAAmB,EACpB,GAAG/B,mBAAAA,CAAoB2C;AAEjB,MAAMC,qBAAAA,GAAwB5C,mBAAAA,CAAoB6C;;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"uploadProgress.js","sources":["../../../../admin/src/future/store/uploadProgress.ts"],"sourcesContent":["import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit';\n\nimport type { File } from '../../../../shared/contracts/files';\n\nexport interface FileUploadError {\n name: string;\n message: string;\n}\n\nexport type FileProgressStatus = 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n\n/**\n * AI metadata generation phase for a single row, tracked independently from the\n * upload status: a metadata failure never turns a successful upload into an error.\n * `undefined` means the row has no metadata phase at all, i.e. AI metadata is\n * disabled — non-images do get a phase and come back `skipped` from the server.\n */\nexport type FileMetadataStatus = 'generating' | 'generated' | 'skipped' | 'failed';\n\nexport type FileMetadataResultStatus = Exclude<FileMetadataStatus, 'generating'>;\n\nexport interface FileProgress {\n name: string;\n index: number;\n status: FileProgressStatus;\n size: number;\n uploadedBytes: number;\n file?: File;\n error?: string;\n metadataStatus?: FileMetadataStatus;\n}\n\nexport interface UploadProgressState {\n isVisible: boolean;\n isMinimized: boolean;\n totalFiles: number;\n files: FileProgress[];\n errors: FileUploadError[];\n uploadId: number;\n}\n\nexport interface RootState {\n uploadProgress: UploadProgressState;\n}\n\nconst initialState: UploadProgressState = {\n isVisible: false,\n isMinimized: false,\n totalFiles: 0,\n files: [],\n errors: [],\n uploadId: 0,\n};\n\nconst uploadProgressSlice = createSlice({\n name: 'uploadProgress',\n initialState,\n reducers: {\n openUploadProgress(\n state,\n action: PayloadAction<{\n totalFiles: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n state.isVisible = true;\n state.isMinimized = false;\n\n // Create pending files for upload\n const pendingFiles: FileProgress[] = action.payload.fileNames.map((name, index) => ({\n name,\n index,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[index] ?? 0,\n uploadedBytes: 0,\n }));\n\n state.files = pendingFiles;\n state.totalFiles = action.payload.totalFiles;\n state.errors = [];\n state.uploadId += 1;\n },\n setFileUploading(state, action: PayloadAction<{ name: string; index: number; size: number }>) {\n const { index, size } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'uploading';\n state.files[index].size = size;\n }\n },\n setFileProgress(state, action: PayloadAction<{ index: number; bytes: number }>) {\n const { index, bytes } = action.payload;\n const file = state.files[index];\n if (file) {\n // Clamp to the known file size so the aggregate can never exceed 100%.\n file.uploadedBytes = Math.min(bytes, file.size);\n }\n },\n setFileComplete(state, action: PayloadAction<{ index: number; file: File }>) {\n const { index, file } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'complete';\n state.files[index].file = file;\n // Reflect completion in the aggregate even if the final progress event was throttled.\n state.files[index].uploadedBytes = state.files[index].size;\n }\n },\n setFileError(state, action: PayloadAction<{ index: number; name: string; message: string }>) {\n const { index, name, message } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'error';\n state.files[index].error = message;\n }\n state.errors = [...state.errors, { name, message }];\n },\n /**\n * Metadata generation started for a row.\n *\n * Metadata requests are fired-and-forgotten, so their callbacks can land after\n * a new batch has replaced `files` and reused the same row indices. Every\n * metadata payload therefore carries the `uploadId` it was fired for, and stale\n * ones are dropped instead of writing onto another batch's row.\n */\n setFileMetadataGenerating(state, action: PayloadAction<{ index: number; uploadId: number }>) {\n const { index, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].metadataStatus = 'generating';\n }\n },\n /**\n * Terminal metadata outcome for a row. Never touches `status`/`error`/`errors`:\n * the upload itself already succeeded regardless of how generation went.\n */\n setFileMetadataResult(\n state,\n action: PayloadAction<{\n index: number;\n uploadId: number;\n status: FileMetadataResultStatus;\n }>\n ) {\n const { index, uploadId, status } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].metadataStatus = status;\n }\n },\n addUploadErrors(state, action: PayloadAction<FileUploadError[]>) {\n state.errors = [...state.errors, ...action.payload];\n },\n closeUploadProgress(state) {\n state.isVisible = false;\n state.isMinimized = false;\n state.totalFiles = 0;\n state.files = [];\n state.errors = [];\n },\n toggleMinimize(state) {\n state.isMinimized = !state.isMinimized;\n },\n cancelUpload(state) {\n // Mark all pending and uploading files as cancelled\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return { ...file, status: 'cancelled' as FileProgressStatus };\n }\n return file;\n });\n },\n setUploadFailed(state, action: PayloadAction<{ message: string }>) {\n // Mark all pending and uploading files as errored when a catastrophic failure occurs\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return {\n ...file,\n status: 'error' as FileProgressStatus,\n error: action.payload.message,\n };\n }\n return file;\n });\n state.errors = [...state.errors, { name: 'Upload Error', message: action.payload.message }];\n },\n retryCancelledFiles(state) {\n // Reset all cancelled files back to pending for retry\n state.files = state.files.map((file) => {\n if (file.status === 'cancelled') {\n return {\n ...file,\n status: 'pending' as FileProgressStatus,\n uploadedBytes: 0,\n // A retried row goes through the metadata phase again from scratch.\n metadataStatus: undefined,\n };\n }\n return file;\n });\n },\n },\n});\n\n/**\n * Byte-weighted aggregate progress across the whole batch: `sum(uploadedBytes) / sum(size)`.\n *\n * Falls back to count-based progress (settled files / total files) when all sizes are\n * zero — e.g. URL-flow rows where the file size is unknown up front.\n */\nexport const selectAggregateProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number => {\n if (files.length === 0) return 0;\n\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\n\n if (totalSize === 0) {\n const settled = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n return Math.round((settled / files.length) * 100);\n }\n\n const uploadedBytes = files.reduce((sum, f) => sum + f.uploadedBytes, 0);\n return Math.round((uploadedBytes / totalSize) * 100);\n }\n);\n\n/**\n * Count-based metadata progress across the whole batch: `settled / expected`.\n *\n * Count-based rather than byte-weighted because the generation endpoint answers in one\n * go — there is no intermediate progress to weight.\n *\n * The denominator is every row *expected* to enter the phase, not just those that\n * already have. Generation is fired per file as each upload finishes, so counting only\n * started rows made the denominator grow mid-batch and the percentage jump backwards\n * (1/1 = 100%, then a second row starts and it drops to 1/2 = 50%), which flickered the\n * header subtitle on and off once per file.\n *\n * \"Expected\" is derived from upload outcome rather than from the AI-metadata flag:\n * - a row still uploading may yet enter the phase, so it counts toward the total;\n * - an errored or cancelled row never will, so it must not — otherwise a batch with\n * one failure could never reach 100%.\n *\n * Returns `null` when nothing has entered the phase and nothing is pending — i.e. AI\n * metadata is off, or the batch is done — so the header can hide the subtitle.\n */\nexport const selectMetadataProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number | null => {\n const started = files.filter((f) => f.metadataStatus !== undefined);\n\n // Nothing has entered the phase yet: with no started row there is no evidence AI\n // metadata is even enabled, so stay silent rather than showing a premature 0%.\n if (started.length === 0) return null;\n\n // Rows whose upload is still in flight are counted in the denominator so the\n // percentage only ever climbs as the batch progresses.\n const pending = files.filter(\n (f) => f.metadataStatus === undefined && (f.status === 'pending' || f.status === 'uploading')\n ).length;\n\n const expected = started.length + pending;\n const settled = started.filter((f) => f.metadataStatus !== 'generating').length;\n\n return Math.round((settled / expected) * 100);\n }\n);\n\n/**\n * Whether the metadata phase is still ongoing for the batch as a whole.\n *\n * The header uses this — not `selectMetadataProgress === 100` — to decide when to drop\n * the subtitle. Two distinct reasons the phase can be unfinished:\n * - a row is generating right now;\n * - no row is generating this instant, but rows are still uploading and will enter the\n * phase when they finish.\n *\n * The second clause is what keeps the subtitle stable. Generation for file N typically\n * finishes before file N+1 finishes uploading, so a naive \"is anything generating?\"\n * check goes false between every pair of files and blinks the subtitle out and back in.\n */\nexport const selectIsGeneratingMetadata = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): boolean => {\n if (files.some((f) => f.metadataStatus === 'generating')) return true;\n\n // Only bridge the gap if some row has already entered the phase — otherwise a batch\n // with AI metadata disabled would report the phase as ongoing for its whole upload.\n const hasStarted = files.some((f) => f.metadataStatus !== undefined);\n\n return hasStarted && files.some((f) => f.status === 'pending' || f.status === 'uploading');\n }\n);\n\n/**\n * How many rows reached each terminal metadata outcome, for the header's completion\n * message. `null` while the phase is unfinished — or was never entered at all — so the\n * caller has a single check for \"there is no settled outcome to report yet\".\n *\n * Counts are reported separately rather than collapsed into one total because they are\n * not interchangeable: only `generated` rows actually had metadata written, so only that\n * count can be claimed as a success. A batch of non-images settles entirely on `skipped`,\n * where \"generated on 0 files\" would be worse than saying nothing.\n */\nexport const selectMetadataOutcome = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): { generated: number; skipped: number; failed: number } | null => {\n const started = files.filter((f) => f.metadataStatus !== undefined);\n\n if (started.length === 0) return null;\n\n // Mirrors `selectIsGeneratingMetadata`: the phase isn't over while a row is\n // generating, nor while a row is still uploading and has yet to enter it.\n const isOngoing =\n started.some((f) => f.metadataStatus === 'generating') ||\n files.some((f) => f.status === 'pending' || f.status === 'uploading');\n\n if (isOngoing) return null;\n\n return {\n generated: started.filter((f) => f.metadataStatus === 'generated').length,\n skipped: started.filter((f) => f.metadataStatus === 'skipped').length,\n failed: started.filter((f) => f.metadataStatus === 'failed').length,\n };\n }\n);\n\nexport const {\n openUploadProgress,\n setFileUploading,\n setFileProgress,\n setFileComplete,\n setFileError,\n setFileMetadataGenerating,\n setFileMetadataResult,\n addUploadErrors,\n closeUploadProgress,\n toggleMinimize,\n cancelUpload,\n setUploadFailed,\n retryCancelledFiles,\n} = uploadProgressSlice.actions;\n\nexport const uploadProgressReducer = uploadProgressSlice.reducer;\n"],"names":["initialState","isVisible","isMinimized","totalFiles","files","errors","uploadId","uploadProgressSlice","createSlice","name","reducers","openUploadProgress","state","action","pendingFiles","payload","fileNames","map","index","status","size","fileSizes","uploadedBytes","setFileUploading","setFileProgress","bytes","file","Math","min","setFileComplete","setFileError","message","error","setFileMetadataGenerating","metadataStatus","setFileMetadataResult","addUploadErrors","closeUploadProgress","toggleMinimize","cancelUpload","setUploadFailed","retryCancelledFiles","undefined","selectAggregateProgress","createSelector","uploadProgress","length","totalSize","reduce","sum","f","settled","filter","round","selectMetadataProgress","started","pending","expected","selectIsGeneratingMetadata","some","hasStarted","selectMetadataOutcome","isOngoing","generated","skipped","failed","actions","uploadProgressReducer","reducer"],"mappings":"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"}
|
|
@@ -66,6 +66,34 @@ const uploadProgressSlice = createSlice({
|
|
|
66
66
|
}
|
|
67
67
|
];
|
|
68
68
|
},
|
|
69
|
+
/**
|
|
70
|
+
* Metadata generation started for a row.
|
|
71
|
+
*
|
|
72
|
+
* Metadata requests are fired-and-forgotten, so their callbacks can land after
|
|
73
|
+
* a new batch has replaced `files` and reused the same row indices. Every
|
|
74
|
+
* metadata payload therefore carries the `uploadId` it was fired for, and stale
|
|
75
|
+
* ones are dropped instead of writing onto another batch's row.
|
|
76
|
+
*/ setFileMetadataGenerating (state, action) {
|
|
77
|
+
const { index, uploadId } = action.payload;
|
|
78
|
+
if (uploadId !== state.uploadId) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (state.files[index]) {
|
|
82
|
+
state.files[index].metadataStatus = 'generating';
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
/**
|
|
86
|
+
* Terminal metadata outcome for a row. Never touches `status`/`error`/`errors`:
|
|
87
|
+
* the upload itself already succeeded regardless of how generation went.
|
|
88
|
+
*/ setFileMetadataResult (state, action) {
|
|
89
|
+
const { index, uploadId, status } = action.payload;
|
|
90
|
+
if (uploadId !== state.uploadId) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (state.files[index]) {
|
|
94
|
+
state.files[index].metadataStatus = status;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
69
97
|
addUploadErrors (state, action) {
|
|
70
98
|
state.errors = [
|
|
71
99
|
...state.errors,
|
|
@@ -121,7 +149,9 @@ const uploadProgressSlice = createSlice({
|
|
|
121
149
|
return {
|
|
122
150
|
...file,
|
|
123
151
|
status: 'pending',
|
|
124
|
-
uploadedBytes: 0
|
|
152
|
+
uploadedBytes: 0,
|
|
153
|
+
// A retried row goes through the metadata phase again from scratch.
|
|
154
|
+
metadataStatus: undefined
|
|
125
155
|
};
|
|
126
156
|
}
|
|
127
157
|
return file;
|
|
@@ -144,8 +174,80 @@ const uploadProgressSlice = createSlice({
|
|
|
144
174
|
const uploadedBytes = files.reduce((sum, f)=>sum + f.uploadedBytes, 0);
|
|
145
175
|
return Math.round(uploadedBytes / totalSize * 100);
|
|
146
176
|
});
|
|
147
|
-
|
|
177
|
+
/**
|
|
178
|
+
* Count-based metadata progress across the whole batch: `settled / expected`.
|
|
179
|
+
*
|
|
180
|
+
* Count-based rather than byte-weighted because the generation endpoint answers in one
|
|
181
|
+
* go — there is no intermediate progress to weight.
|
|
182
|
+
*
|
|
183
|
+
* The denominator is every row *expected* to enter the phase, not just those that
|
|
184
|
+
* already have. Generation is fired per file as each upload finishes, so counting only
|
|
185
|
+
* started rows made the denominator grow mid-batch and the percentage jump backwards
|
|
186
|
+
* (1/1 = 100%, then a second row starts and it drops to 1/2 = 50%), which flickered the
|
|
187
|
+
* header subtitle on and off once per file.
|
|
188
|
+
*
|
|
189
|
+
* "Expected" is derived from upload outcome rather than from the AI-metadata flag:
|
|
190
|
+
* - a row still uploading may yet enter the phase, so it counts toward the total;
|
|
191
|
+
* - an errored or cancelled row never will, so it must not — otherwise a batch with
|
|
192
|
+
* one failure could never reach 100%.
|
|
193
|
+
*
|
|
194
|
+
* Returns `null` when nothing has entered the phase and nothing is pending — i.e. AI
|
|
195
|
+
* metadata is off, or the batch is done — so the header can hide the subtitle.
|
|
196
|
+
*/ const selectMetadataProgress = createSelector((state)=>state.uploadProgress.files, (files)=>{
|
|
197
|
+
const started = files.filter((f)=>f.metadataStatus !== undefined);
|
|
198
|
+
// Nothing has entered the phase yet: with no started row there is no evidence AI
|
|
199
|
+
// metadata is even enabled, so stay silent rather than showing a premature 0%.
|
|
200
|
+
if (started.length === 0) return null;
|
|
201
|
+
// Rows whose upload is still in flight are counted in the denominator so the
|
|
202
|
+
// percentage only ever climbs as the batch progresses.
|
|
203
|
+
const pending = files.filter((f)=>f.metadataStatus === undefined && (f.status === 'pending' || f.status === 'uploading')).length;
|
|
204
|
+
const expected = started.length + pending;
|
|
205
|
+
const settled = started.filter((f)=>f.metadataStatus !== 'generating').length;
|
|
206
|
+
return Math.round(settled / expected * 100);
|
|
207
|
+
});
|
|
208
|
+
/**
|
|
209
|
+
* Whether the metadata phase is still ongoing for the batch as a whole.
|
|
210
|
+
*
|
|
211
|
+
* The header uses this — not `selectMetadataProgress === 100` — to decide when to drop
|
|
212
|
+
* the subtitle. Two distinct reasons the phase can be unfinished:
|
|
213
|
+
* - a row is generating right now;
|
|
214
|
+
* - no row is generating this instant, but rows are still uploading and will enter the
|
|
215
|
+
* phase when they finish.
|
|
216
|
+
*
|
|
217
|
+
* The second clause is what keeps the subtitle stable. Generation for file N typically
|
|
218
|
+
* finishes before file N+1 finishes uploading, so a naive "is anything generating?"
|
|
219
|
+
* check goes false between every pair of files and blinks the subtitle out and back in.
|
|
220
|
+
*/ const selectIsGeneratingMetadata = createSelector((state)=>state.uploadProgress.files, (files)=>{
|
|
221
|
+
if (files.some((f)=>f.metadataStatus === 'generating')) return true;
|
|
222
|
+
// Only bridge the gap if some row has already entered the phase — otherwise a batch
|
|
223
|
+
// with AI metadata disabled would report the phase as ongoing for its whole upload.
|
|
224
|
+
const hasStarted = files.some((f)=>f.metadataStatus !== undefined);
|
|
225
|
+
return hasStarted && files.some((f)=>f.status === 'pending' || f.status === 'uploading');
|
|
226
|
+
});
|
|
227
|
+
/**
|
|
228
|
+
* How many rows reached each terminal metadata outcome, for the header's completion
|
|
229
|
+
* message. `null` while the phase is unfinished — or was never entered at all — so the
|
|
230
|
+
* caller has a single check for "there is no settled outcome to report yet".
|
|
231
|
+
*
|
|
232
|
+
* Counts are reported separately rather than collapsed into one total because they are
|
|
233
|
+
* not interchangeable: only `generated` rows actually had metadata written, so only that
|
|
234
|
+
* count can be claimed as a success. A batch of non-images settles entirely on `skipped`,
|
|
235
|
+
* where "generated on 0 files" would be worse than saying nothing.
|
|
236
|
+
*/ const selectMetadataOutcome = createSelector((state)=>state.uploadProgress.files, (files)=>{
|
|
237
|
+
const started = files.filter((f)=>f.metadataStatus !== undefined);
|
|
238
|
+
if (started.length === 0) return null;
|
|
239
|
+
// Mirrors `selectIsGeneratingMetadata`: the phase isn't over while a row is
|
|
240
|
+
// generating, nor while a row is still uploading and has yet to enter it.
|
|
241
|
+
const isOngoing = started.some((f)=>f.metadataStatus === 'generating') || files.some((f)=>f.status === 'pending' || f.status === 'uploading');
|
|
242
|
+
if (isOngoing) return null;
|
|
243
|
+
return {
|
|
244
|
+
generated: started.filter((f)=>f.metadataStatus === 'generated').length,
|
|
245
|
+
skipped: started.filter((f)=>f.metadataStatus === 'skipped').length,
|
|
246
|
+
failed: started.filter((f)=>f.metadataStatus === 'failed').length
|
|
247
|
+
};
|
|
248
|
+
});
|
|
249
|
+
const { openUploadProgress, setFileUploading, setFileProgress, setFileComplete, setFileError, setFileMetadataGenerating, setFileMetadataResult, addUploadErrors, closeUploadProgress, toggleMinimize, cancelUpload, setUploadFailed, retryCancelledFiles } = uploadProgressSlice.actions;
|
|
148
250
|
const uploadProgressReducer = uploadProgressSlice.reducer;
|
|
149
251
|
|
|
150
|
-
export { addUploadErrors, cancelUpload, closeUploadProgress, openUploadProgress, retryCancelledFiles, selectAggregateProgress, setFileComplete, setFileError, setFileProgress, setFileUploading, setUploadFailed, toggleMinimize, uploadProgressReducer };
|
|
252
|
+
export { addUploadErrors, cancelUpload, closeUploadProgress, openUploadProgress, retryCancelledFiles, selectAggregateProgress, selectIsGeneratingMetadata, selectMetadataOutcome, selectMetadataProgress, setFileComplete, setFileError, setFileMetadataGenerating, setFileMetadataResult, setFileProgress, setFileUploading, setUploadFailed, toggleMinimize, uploadProgressReducer };
|
|
151
253
|
//# sourceMappingURL=uploadProgress.mjs.map
|