@strapi/upload 5.52.0 → 5.52.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.
Files changed (198) hide show
  1. package/dist/admin/future/components/TruncatedText.js +76 -0
  2. package/dist/admin/future/components/TruncatedText.js.map +1 -0
  3. package/dist/admin/future/components/TruncatedText.mjs +74 -0
  4. package/dist/admin/future/components/TruncatedText.mjs.map +1 -0
  5. package/dist/admin/future/components/UploadProgressDialog.js +36 -1
  6. package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
  7. package/dist/admin/future/components/UploadProgressDialog.mjs +37 -2
  8. package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
  9. package/dist/admin/future/hooks/useApiErrorMessage.js +61 -0
  10. package/dist/admin/future/hooks/useApiErrorMessage.js.map +1 -0
  11. package/dist/admin/future/hooks/useApiErrorMessage.mjs +39 -0
  12. package/dist/admin/future/hooks/useApiErrorMessage.mjs.map +1 -0
  13. package/dist/admin/future/hooks/useTracking.js +57 -0
  14. package/dist/admin/future/hooks/useTracking.js.map +1 -0
  15. package/dist/admin/future/hooks/useTracking.mjs +53 -0
  16. package/dist/admin/future/hooks/useTracking.mjs.map +1 -0
  17. package/dist/admin/future/pages/Assets/AssetsPage.js +31 -0
  18. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  19. package/dist/admin/future/pages/Assets/AssetsPage.mjs +31 -0
  20. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  21. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +13 -11
  22. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -1
  23. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +13 -11
  24. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
  25. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +62 -51
  26. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  27. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +62 -51
  28. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  29. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +3 -4
  30. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  31. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +4 -5
  32. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  33. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +11 -1
  34. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -1
  35. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +11 -1
  36. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -1
  37. package/dist/admin/future/pages/Assets/components/AssetsTable.js +6 -4
  38. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +6 -4
  40. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +10 -1
  42. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
  43. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +10 -1
  44. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  45. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +3 -2
  46. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -1
  47. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +3 -2
  48. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -1
  49. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +4 -2
  50. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  51. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +4 -2
  52. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  53. package/dist/admin/future/pages/Assets/components/FilterMenu.js +18 -2
  54. package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -1
  55. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +18 -2
  56. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -1
  57. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js +9 -7
  58. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js.map +1 -1
  59. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs +9 -7
  60. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs.map +1 -1
  61. package/dist/admin/future/pages/Assets/components/FolderFormDialog.js +12 -0
  62. package/dist/admin/future/pages/Assets/components/FolderFormDialog.js.map +1 -1
  63. package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs +12 -0
  64. package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs.map +1 -1
  65. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +21 -38
  66. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  67. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +23 -40
  68. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  69. package/dist/admin/future/pages/Assets/components/SortMenu.js +14 -0
  70. package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -1
  71. package/dist/admin/future/pages/Assets/components/SortMenu.mjs +14 -0
  72. package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -1
  73. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +5 -0
  74. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -1
  75. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +6 -1
  76. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
  77. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.js +30 -0
  78. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.js.map +1 -0
  79. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.mjs +26 -0
  80. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.mjs.map +1 -0
  81. package/dist/admin/future/pages/Assets/utils/selection.js +17 -0
  82. package/dist/admin/future/pages/Assets/utils/selection.js.map +1 -1
  83. package/dist/admin/future/pages/Assets/utils/selection.mjs +17 -1
  84. package/dist/admin/future/pages/Assets/utils/selection.mjs.map +1 -1
  85. package/dist/admin/future/services/api.js +123 -19
  86. package/dist/admin/future/services/api.js.map +1 -1
  87. package/dist/admin/future/services/api.mjs +125 -21
  88. package/dist/admin/future/services/api.mjs.map +1 -1
  89. package/dist/admin/future/services/assets.js +11 -23
  90. package/dist/admin/future/services/assets.js.map +1 -1
  91. package/dist/admin/future/services/assets.mjs +11 -23
  92. package/dist/admin/future/services/assets.mjs.map +1 -1
  93. package/dist/admin/future/services/uploadFileViaXHR.js +9 -3
  94. package/dist/admin/future/services/uploadFileViaXHR.js.map +1 -1
  95. package/dist/admin/future/services/uploadFileViaXHR.mjs +9 -3
  96. package/dist/admin/future/services/uploadFileViaXHR.mjs.map +1 -1
  97. package/dist/admin/future/store/uploadProgress.js +44 -5
  98. package/dist/admin/future/store/uploadProgress.js.map +1 -1
  99. package/dist/admin/future/store/uploadProgress.mjs +43 -6
  100. package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
  101. package/dist/admin/future/utils/getApiErrorMessage.js +20 -0
  102. package/dist/admin/future/utils/getApiErrorMessage.js.map +1 -0
  103. package/dist/admin/future/utils/getApiErrorMessage.mjs +18 -0
  104. package/dist/admin/future/utils/getApiErrorMessage.mjs.map +1 -0
  105. package/dist/admin/hooks/useAIMetadataJob.js +1 -1
  106. package/dist/admin/hooks/useAIMetadataJob.js.map +1 -1
  107. package/dist/admin/hooks/useAIMetadataJob.mjs +1 -1
  108. package/dist/admin/hooks/useAIMetadataJob.mjs.map +1 -1
  109. package/dist/admin/pages/SettingsPage/SettingsPage.js +2 -2
  110. package/dist/admin/pages/SettingsPage/SettingsPage.js.map +1 -1
  111. package/dist/admin/pages/SettingsPage/SettingsPage.mjs +2 -2
  112. package/dist/admin/pages/SettingsPage/SettingsPage.mjs.map +1 -1
  113. package/dist/admin/src/future/components/TruncatedText.d.ts +32 -0
  114. package/dist/admin/src/future/hooks/useApiErrorMessage.d.ts +17 -0
  115. package/dist/admin/src/future/hooks/useTracking.d.ts +29 -0
  116. package/dist/admin/src/future/pages/Assets/components/AssetActionsMenu.d.ts +5 -3
  117. package/dist/admin/src/future/pages/Assets/components/FolderActionsMenu.d.ts +5 -4
  118. package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +2 -0
  119. package/dist/admin/src/future/pages/Assets/hooks/useIsAssetDetailsOpen.d.ts +18 -0
  120. package/dist/admin/src/future/pages/Assets/utils/selection.d.ts +10 -0
  121. package/dist/admin/src/future/services/api.d.ts +6 -7
  122. package/dist/admin/src/future/services/uploadFileViaXHR.d.ts +6 -1
  123. package/dist/admin/src/future/store/uploadProgress.d.ts +16 -1
  124. package/dist/admin/src/future/utils/getApiErrorMessage.d.ts +1 -0
  125. package/dist/admin/translations/ca.json.js +1 -1
  126. package/dist/admin/translations/ca.json.mjs +1 -1
  127. package/dist/admin/translations/de.json.js +1 -1
  128. package/dist/admin/translations/de.json.mjs +1 -1
  129. package/dist/admin/translations/en.json.js +2 -1
  130. package/dist/admin/translations/en.json.js.map +1 -1
  131. package/dist/admin/translations/en.json.mjs +2 -1
  132. package/dist/admin/translations/en.json.mjs.map +1 -1
  133. package/dist/admin/translations/es.json.js +1 -1
  134. package/dist/admin/translations/es.json.mjs +1 -1
  135. package/dist/admin/translations/fr.json.js +1 -1
  136. package/dist/admin/translations/fr.json.mjs +1 -1
  137. package/dist/admin/translations/ja.json.js +1 -1
  138. package/dist/admin/translations/ja.json.mjs +1 -1
  139. package/dist/admin/translations/ko.json.js +1 -1
  140. package/dist/admin/translations/ko.json.mjs +1 -1
  141. package/dist/admin/translations/nl.json.js +1 -1
  142. package/dist/admin/translations/nl.json.mjs +1 -1
  143. package/dist/admin/translations/pl.json.js +1 -1
  144. package/dist/admin/translations/pl.json.mjs +1 -1
  145. package/dist/admin/translations/pt-BR.json.js +1 -1
  146. package/dist/admin/translations/pt-BR.json.mjs +1 -1
  147. package/dist/admin/translations/ru.json.js +1 -1
  148. package/dist/admin/translations/ru.json.mjs +1 -1
  149. package/dist/admin/translations/sk.json.js +1 -1
  150. package/dist/admin/translations/sk.json.mjs +1 -1
  151. package/dist/admin/translations/tr.json.js +1 -1
  152. package/dist/admin/translations/tr.json.mjs +1 -1
  153. package/dist/admin/translations/uk.json.js +1 -1
  154. package/dist/admin/translations/uk.json.mjs +1 -1
  155. package/dist/admin/translations/zh-Hans.json.js +1 -1
  156. package/dist/admin/translations/zh-Hans.json.mjs +1 -1
  157. package/dist/admin/translations/zh.json.js +1 -1
  158. package/dist/admin/translations/zh.json.mjs +1 -1
  159. package/dist/server/controllers/admin-file.js +27 -9
  160. package/dist/server/controllers/admin-file.js.map +1 -1
  161. package/dist/server/controllers/admin-file.mjs +27 -9
  162. package/dist/server/controllers/admin-file.mjs.map +1 -1
  163. package/dist/server/controllers/admin-upload.js +24 -8
  164. package/dist/server/controllers/admin-upload.js.map +1 -1
  165. package/dist/server/controllers/admin-upload.mjs +24 -8
  166. package/dist/server/controllers/admin-upload.mjs.map +1 -1
  167. package/dist/server/routes/admin.js +41 -21
  168. package/dist/server/routes/admin.js.map +1 -1
  169. package/dist/server/routes/admin.mjs +41 -21
  170. package/dist/server/routes/admin.mjs.map +1 -1
  171. package/dist/server/services/ai-metadata.js +25 -5
  172. package/dist/server/services/ai-metadata.js.map +1 -1
  173. package/dist/server/services/ai-metadata.mjs +1 -1
  174. package/dist/server/services/ai-metadata.mjs.map +1 -1
  175. package/dist/server/services/upload.js +4 -0
  176. package/dist/server/services/upload.js.map +1 -1
  177. package/dist/server/services/upload.mjs +4 -0
  178. package/dist/server/services/upload.mjs.map +1 -1
  179. package/dist/server/src/controllers/admin-file.d.ts +29 -8
  180. package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
  181. package/dist/server/src/controllers/admin-upload.d.ts +20 -4
  182. package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
  183. package/dist/server/src/controllers/index.d.ts +4 -4
  184. package/dist/server/src/index.d.ts +4 -4
  185. package/dist/server/src/routes/admin.d.ts.map +1 -1
  186. package/dist/server/src/services/ai-metadata.d.ts +2 -2
  187. package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
  188. package/dist/server/src/services/upload.d.ts.map +1 -1
  189. package/dist/shared/contracts/ai-metadata-jobs.d.ts +10 -32
  190. package/dist/shared/contracts/ai-metadata-jobs.d.ts.map +1 -1
  191. package/dist/shared/contracts/files.d.ts +10 -10
  192. package/dist/shared/contracts/files.d.ts.map +1 -1
  193. package/package.json +7 -7
  194. package/dist/admin/future/utils/getBulkMoveErrorMessage.js +0 -23
  195. package/dist/admin/future/utils/getBulkMoveErrorMessage.js.map +0 -1
  196. package/dist/admin/future/utils/getBulkMoveErrorMessage.mjs +0 -21
  197. package/dist/admin/future/utils/getBulkMoveErrorMessage.mjs.map +0 -1
  198. package/dist/admin/src/future/utils/getBulkMoveErrorMessage.d.ts +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AssetActionsMenu.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/AssetActionsMenu.tsx"],"sourcesContent":["import { useMemo, useRef, useState } from 'react';\n\nimport { useClipboard, useNotification } from '@strapi/admin/strapi-admin';\nimport {\n Button,\n Dialog,\n Flex,\n IconButton,\n Menu,\n Typography,\n VisuallyHidden,\n} from '@strapi/design-system';\nimport { ArrowRight, ArrowsCounterClockwise, Download, Link, More, Trash } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\n\nimport { useAIMetadataEnabled } from '../../../hooks/useAIMetadataEnabled';\nimport { useMediaLibraryPermissions } from '../../../hooks/useMediaLibraryPermissions';\nimport { useReplaceAssetMutation } from '../../../services/assets';\nimport { downloadFile } from '../../../utils/downloadFile';\nimport { prefixFileUrlWithBackendUrl } from '../../../utils/files';\nimport { getTranslationKey } from '../../../utils/translations';\nimport { useAssetSelection } from '../hooks/useAssetSelection';\nimport { useBusyAssetsOptional } from '../hooks/useBusyAssets';\n\nimport { ActionsMenuContent } from './ActionsMenuContent';\nimport { BulkMoveDialog } from './BulkMoveDialog';\nimport { DeleteItemsDialog } from './DeleteItemsDialog';\n\nimport type { File } from '../../../../../../shared/contracts/files';\nimport type { DragFileData } from '../../../types/dnd';\n\ninterface AssetActionsMenuProps {\n asset: File;\n /** Drag data for this asset, so the move dialog validates against its real folder. */\n dragData: DragFileData;\n}\n\n/**\n * The \"...\" menu on an asset row/card. Always acts on that one asset, whatever\n * the current multi-selection is (the bulk actions bar is the selection-scoped\n * affordance) — the file mirror of `FolderActionsMenu`.\n *\n * Replace / copy link / download duplicate what the details drawer offers as\n * icon buttons, so the same action is one click away without opening the asset\n * first. They are re-implemented rather than lifted out of the drawer: the\n * drawer versions render as `IconButton`s and report through its in-drawer toast\n * slot, neither of which fits a menu item on a row.\n *\n * Any successful move or delete clears the whole selection: a delete only\n * invalidates RTK tags, so without it an `asset:<id>` key for a file that no\n * longer exists would linger in the selection.\n */\nexport const AssetActionsMenu = ({ asset, dragData }: AssetActionsMenuProps) => {\n const { formatMessage } = useIntl();\n const { copy } = useClipboard();\n const { toggleNotification } = useNotification();\n const { clear } = useAssetSelection();\n // Absent in the asset picker and in unit tests: the replace still runs, it\n // just renders no row-level overlay.\n const markBusy = useBusyAssetsOptional()?.markBusy ?? (() => () => {});\n const {\n canUpdate,\n canDownload,\n canCopyLink,\n isLoading: isLoadingPermissions,\n } = useMediaLibraryPermissions();\n const [replaceAsset, { isLoading: isReplacing }] = useReplaceAssetMutation();\n const aiEnabled = useAIMetadataEnabled({ mime: asset.mime });\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [isReplaceOpen, setIsReplaceOpen] = useState(false);\n const [isMoveOpen, setIsMoveOpen] = useState(false);\n const [isDeleteOpen, setIsDeleteOpen] = useState(false);\n const [isDownloading, setIsDownloading] = useState(false);\n\n // Stable identity: the move dialog memoizes its destination walk on it.\n const moveItems = useMemo(() => [dragData], [dragData]);\n\n // Confirm first, then open the native picker, so the user only commits to\n // replacing after acknowledging the warning (same order as the drawer).\n const handleReplaceContinue = () => {\n setIsReplaceOpen(false);\n fileInputRef.current?.click();\n };\n\n const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n // Reset the native input so the same file can be picked again later.\n event.target.value = '';\n\n if (!file) {\n return;\n }\n\n // The menu has closed by the time the picker returns, so `isReplacing` has\n // nothing left to disable and the row would otherwise sit inert for the\n // whole upload. Marking the asset busy puts the same overlay the drawer\n // uses on the row/card, cleared when the mutation settles either way.\n const releaseBusy = markBusy(\n asset.id,\n formatMessage({\n id: getTranslationKey('asset-details.replace.loading'),\n defaultMessage: 'Replacing the file…',\n })\n );\n\n let res;\n try {\n res = await replaceAsset({ id: asset.id, file });\n } finally {\n releaseBusy();\n }\n\n if ('error' in res) {\n // `fetchBaseQuery` already unwraps the API envelope, so a server-sent\n // reason (file too large, unsupported type) lands directly on `message`.\n const { message } = res.error as { message?: string };\n toggleNotification({\n type: 'danger',\n message:\n message ??\n formatMessage({\n id: getTranslationKey('asset-details.replace.error'),\n defaultMessage: 'Failed to replace the file.',\n }),\n });\n return;\n }\n\n toggleNotification({\n type: 'success',\n message: formatMessage({\n id: getTranslationKey('asset-details.replace.success'),\n defaultMessage: 'File replaced.',\n }),\n });\n };\n\n const handleCopyLink = async () => {\n const url = prefixFileUrlWithBackendUrl(asset.url);\n\n if (!url) {\n return;\n }\n\n const didCopy = await copy(url);\n\n toggleNotification({\n type: didCopy ? 'success' : 'danger',\n message: formatMessage(\n didCopy\n ? {\n id: getTranslationKey('asset-details.copy-link.success'),\n defaultMessage: 'Link copied.',\n }\n : {\n id: getTranslationKey('asset-details.copy-link.error'),\n defaultMessage: 'Failed to copy the link.',\n }\n ),\n });\n };\n\n const handleDownload = async () => {\n const url = prefixFileUrlWithBackendUrl(asset.url);\n\n if (!url) {\n return;\n }\n\n setIsDownloading(true);\n\n try {\n await downloadFile(url, asset.name);\n } catch {\n toggleNotification({\n type: 'danger',\n message: formatMessage({\n id: getTranslationKey('asset-details.download.error'),\n defaultMessage: 'Failed to download the file.',\n }),\n });\n } finally {\n setIsDownloading(false);\n }\n };\n\n // Two permission-gated groups: replace/copy-link/download above, move/delete\n // below. The separator earns its place only when both survive the gate —\n // `canUpdate` alone puts Replace in the top group, which is not enough.\n const hasTopGroup = canUpdate || canCopyLink || canDownload;\n const hasBottomGroup = canUpdate;\n const showSeparator = (canCopyLink || canDownload) && hasBottomGroup;\n\n // A role can read the library while holding none of these, and an empty\n // popup is worse than no trigger at all. Every flag is `false` until the\n // RBAC check settles, so wait for it — otherwise the trigger unmounts and\n // remounts on first paint for everyone.\n if (!isLoadingPermissions && !hasTopGroup && !hasBottomGroup) {\n return null;\n }\n\n return (\n <>\n <VisuallyHidden>\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple={false}\n onChange={handleFileChange}\n aria-hidden\n tabIndex={-1}\n />\n </VisuallyHidden>\n {/* Non-modal: Radix's default `modal` marks the rest of the document\n `aria-hidden` and blocks pointer events while the menu is open, so\n clicking a sibling row's trigger only dismissed this one — leaving\n every menu you touched to be closed one click at a time. Non-modal\n dismissal treats that click as both \"close this\" and \"open that\". */}\n <Menu.Root modal={false}>\n <Menu.Trigger\n tag={IconButton}\n icon={<More />}\n variant=\"ghost\"\n label={formatMessage({\n id: getTranslationKey('control-card.more-actions'),\n defaultMessage: 'More actions',\n })}\n />\n {/* `ActionsMenuContent` replaces the design system's flat 15rem clamp\n with the height Radix actually measured, and un-hides the scrollbar\n it would otherwise overflow into. See that file for why. */}\n <ActionsMenuContent popoverPlacement=\"bottom-end\" zIndex={2} minWidth=\"22rem\">\n {canUpdate && (\n <Menu.Item\n startIcon={<ArrowsCounterClockwise />}\n disabled={isReplacing}\n onSelect={() => setIsReplaceOpen(true)}\n >\n {formatMessage({\n id: getTranslationKey('list.assets.actions.replace'),\n defaultMessage: 'Replace media',\n })}\n </Menu.Item>\n )}\n {canCopyLink && (\n <Menu.Item startIcon={<Link />} onSelect={handleCopyLink}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.copy-link'),\n defaultMessage: 'Copy link to media',\n })}\n </Menu.Item>\n )}\n {canDownload && (\n <Menu.Item startIcon={<Download />} disabled={isDownloading} onSelect={handleDownload}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.download'),\n defaultMessage: 'Download media',\n })}\n </Menu.Item>\n )}\n {showSeparator && <Menu.Separator />}\n {canUpdate && (\n <>\n <Menu.Item startIcon={<ArrowRight />} onSelect={() => setIsMoveOpen(true)}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.move'),\n defaultMessage: 'Move to folder',\n })}\n </Menu.Item>\n <Menu.Item\n startIcon={<Trash />}\n variant=\"danger\"\n onSelect={() => setIsDeleteOpen(true)}\n >\n {formatMessage({\n id: getTranslationKey('list.assets.actions.delete'),\n defaultMessage: 'Delete',\n })}\n </Menu.Item>\n </>\n )}\n </ActionsMenuContent>\n </Menu.Root>\n <Dialog.Root open={isReplaceOpen} onOpenChange={setIsReplaceOpen}>\n <Dialog.Content>\n <Dialog.Header>\n {formatMessage({\n id: getTranslationKey('asset-details.replace.title'),\n defaultMessage: 'Replace this media file?',\n })}\n </Dialog.Header>\n <Dialog.Body textAlign=\"center\">\n <Flex direction=\"column\" textAlign=\"center\">\n <Typography variant=\"omega\">\n {formatMessage({\n id: getTranslationKey('asset-details.replace.description'),\n defaultMessage: 'Current content will be permanently replaced.',\n })}\n </Typography>\n {aiEnabled ? (\n <Typography variant=\"omega\">\n {formatMessage({\n id: getTranslationKey('asset-details.replace.description.ai'),\n defaultMessage: 'AI will generate new metadata after upload.',\n })}\n </Typography>\n ) : null}\n </Flex>\n </Dialog.Body>\n <Dialog.Footer>\n <Dialog.Cancel>\n <Button variant=\"tertiary\" fullWidth>\n {formatMessage({ id: 'app.components.Button.cancel', defaultMessage: 'Cancel' })}\n </Button>\n </Dialog.Cancel>\n <Dialog.Action>\n <Button variant=\"secondary\" onClick={handleReplaceContinue} fullWidth>\n {formatMessage({\n id: getTranslationKey('asset-details.replace.continue'),\n defaultMessage: 'Continue',\n })}\n </Button>\n </Dialog.Action>\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog.Root>\n {isMoveOpen && (\n <BulkMoveDialog\n open\n onClose={() => setIsMoveOpen(false)}\n items={moveItems}\n onSuccess={clear}\n />\n )}\n {/* Both dialogs live inside the row, so a background refetch that drops the\n row would take an open dialog with it. Nothing invalidates until the\n mutation resolves, and the dialog closes in the same tick, so the\n flows themselves can't trigger it. */}\n {isDeleteOpen && (\n <DeleteItemsDialog\n open\n onClose={() => setIsDeleteOpen(false)}\n target={{ fileIds: [asset.id], folderIds: [] }}\n onSuccess={clear}\n />\n )}\n </>\n );\n};\n"],"names":["AssetActionsMenu","asset","dragData","formatMessage","useIntl","copy","useClipboard","toggleNotification","useNotification","clear","useAssetSelection","markBusy","useBusyAssetsOptional","canUpdate","canDownload","canCopyLink","isLoading","isLoadingPermissions","useMediaLibraryPermissions","replaceAsset","isReplacing","useReplaceAssetMutation","aiEnabled","useAIMetadataEnabled","mime","fileInputRef","useRef","isReplaceOpen","setIsReplaceOpen","useState","isMoveOpen","setIsMoveOpen","isDeleteOpen","setIsDeleteOpen","isDownloading","setIsDownloading","moveItems","useMemo","handleReplaceContinue","current","click","handleFileChange","event","file","target","files","value","releaseBusy","id","getTranslationKey","defaultMessage","res","message","error","type","handleCopyLink","url","prefixFileUrlWithBackendUrl","didCopy","handleDownload","downloadFile","name","hasTopGroup","hasBottomGroup","showSeparator","_jsxs","_Fragment","_jsx","VisuallyHidden","input","ref","multiple","onChange","aria-hidden","tabIndex","Menu","Root","modal","Trigger","tag","IconButton","icon","More","variant","label","ActionsMenuContent","popoverPlacement","zIndex","minWidth","Item","startIcon","ArrowsCounterClockwise","disabled","onSelect","Link","Download","Separator","ArrowRight","Trash","Dialog","open","onOpenChange","Content","Header","Body","textAlign","Flex","direction","Typography","Footer","Cancel","Button","fullWidth","Action","onClick","BulkMoveDialog","onClose","items","onSuccess","DeleteItemsDialog","fileIds","folderIds"],"mappings":"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"}
1
+ {"version":3,"file":"AssetActionsMenu.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/AssetActionsMenu.tsx"],"sourcesContent":["import { useMemo, useRef, useState } from 'react';\n\nimport { useClipboard, useNotification } from '@strapi/admin/strapi-admin';\nimport {\n Button,\n Dialog,\n Flex,\n IconButton,\n Menu,\n Typography,\n VisuallyHidden,\n} from '@strapi/design-system';\nimport { ArrowRight, ArrowsCounterClockwise, Download, Link, More, Trash } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\n\nimport { useAIMetadataEnabled } from '../../../hooks/useAIMetadataEnabled';\nimport { useApiErrorMessage } from '../../../hooks/useApiErrorMessage';\nimport { useMediaLibraryPermissions } from '../../../hooks/useMediaLibraryPermissions';\nimport { useReplaceAssetMutation } from '../../../services/assets';\nimport { downloadFile } from '../../../utils/downloadFile';\nimport { prefixFileUrlWithBackendUrl } from '../../../utils/files';\nimport { getTranslationKey } from '../../../utils/translations';\nimport { useAssetSelection } from '../hooks/useAssetSelection';\nimport { useBusyAssetsOptional } from '../hooks/useBusyAssets';\nimport { assetKey } from '../utils/selection';\n\nimport { ActionsMenuContent } from './ActionsMenuContent';\nimport { BulkMoveDialog } from './BulkMoveDialog';\nimport { DeleteItemsDialog } from './DeleteItemsDialog';\n\nimport type { File } from '../../../../../../shared/contracts/files';\nimport type { DragFileData } from '../../../types/dnd';\n\ninterface AssetActionsMenuProps {\n asset: File;\n /** Drag data for this asset, so the move dialog validates against its real folder. */\n dragData: DragFileData;\n}\n\n/**\n * The \"...\" menu on an asset row/card. Always acts on that one asset, whatever\n * the current multi-selection is (the bulk actions bar is the selection-scoped\n * affordance) — the file mirror of `FolderActionsMenu`.\n *\n * Replace / copy link / download duplicate what the details drawer offers as\n * icon buttons, so the same action is one click away without opening the asset\n * first. They are re-implemented rather than lifted out of the drawer: the\n * drawer versions render as `IconButton`s and report through its in-drawer toast\n * slot, neither of which fits a menu item on a row.\n *\n * A successful move or delete deselects this one asset and leaves the rest of\n * the selection intact: the mutations only invalidate RTK tags, so without it an\n * `asset:<id>` key for a file that has been deleted — or has moved out of this\n * list — would linger. The other selected rows are untouched, which is the whole\n * point of the menu being a single-item affordance.\n */\nexport const AssetActionsMenu = ({ asset, dragData }: AssetActionsMenuProps) => {\n const { formatMessage } = useIntl();\n const getErrorMessage = useApiErrorMessage();\n const { copy } = useClipboard();\n const { toggleNotification } = useNotification();\n const { deselect } = useAssetSelection();\n // Absent in the asset picker and in unit tests: the replace still runs, it\n // just renders no row-level overlay.\n const markBusy = useBusyAssetsOptional()?.markBusy ?? (() => () => {});\n const {\n canUpdate,\n canDownload,\n canCopyLink,\n isLoading: isLoadingPermissions,\n } = useMediaLibraryPermissions();\n const [replaceAsset, { isLoading: isReplacing }] = useReplaceAssetMutation();\n const aiEnabled = useAIMetadataEnabled({ mime: asset.mime });\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [isReplaceOpen, setIsReplaceOpen] = useState(false);\n const [isMoveOpen, setIsMoveOpen] = useState(false);\n const [isDeleteOpen, setIsDeleteOpen] = useState(false);\n const [isDownloading, setIsDownloading] = useState(false);\n\n // Stable identity: the move dialog memoizes its destination walk on it.\n const moveItems = useMemo(() => [dragData], [dragData]);\n\n // Confirm first, then open the native picker, so the user only commits to\n // replacing after acknowledging the warning (same order as the drawer).\n const handleReplaceContinue = () => {\n setIsReplaceOpen(false);\n fileInputRef.current?.click();\n };\n\n const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n // Reset the native input so the same file can be picked again later.\n event.target.value = '';\n\n if (!file) {\n return;\n }\n\n // The menu has closed by the time the picker returns, so `isReplacing` has\n // nothing left to disable and the row would otherwise sit inert for the\n // whole upload. Marking the asset busy puts the same overlay the drawer\n // uses on the row/card, cleared when the mutation settles either way.\n const releaseBusy = markBusy(\n asset.id,\n formatMessage({\n id: getTranslationKey('asset-details.replace.loading'),\n defaultMessage: 'Replacing the file…',\n })\n );\n\n let res;\n try {\n res = await replaceAsset({ id: asset.id, file });\n } finally {\n releaseBusy();\n }\n\n if ('error' in res) {\n toggleNotification({\n type: 'danger',\n message: getErrorMessage(\n res.error,\n formatMessage({\n id: getTranslationKey('asset-details.replace.error'),\n defaultMessage: 'Failed to replace the file.',\n })\n ),\n });\n return;\n }\n\n toggleNotification({\n type: 'success',\n message: formatMessage({\n id: getTranslationKey('asset-details.replace.success'),\n defaultMessage: 'File replaced.',\n }),\n });\n };\n\n const handleCopyLink = async () => {\n const url = prefixFileUrlWithBackendUrl(asset.url);\n\n if (!url) {\n return;\n }\n\n const didCopy = await copy(url);\n\n toggleNotification({\n type: didCopy ? 'success' : 'danger',\n message: formatMessage(\n didCopy\n ? {\n id: getTranslationKey('asset-details.copy-link.success'),\n defaultMessage: 'Link copied.',\n }\n : {\n id: getTranslationKey('asset-details.copy-link.error'),\n defaultMessage: 'Failed to copy the link.',\n }\n ),\n });\n };\n\n const handleDownload = async () => {\n const url = prefixFileUrlWithBackendUrl(asset.url);\n\n if (!url) {\n return;\n }\n\n setIsDownloading(true);\n\n try {\n await downloadFile(url, asset.name);\n } catch {\n toggleNotification({\n type: 'danger',\n message: formatMessage({\n id: getTranslationKey('asset-details.download.error'),\n defaultMessage: 'Failed to download the file.',\n }),\n });\n } finally {\n setIsDownloading(false);\n }\n };\n\n // Two permission-gated groups: replace/copy-link/download above, move/delete\n // below. The separator earns its place only when both survive the gate —\n // `canUpdate` alone puts Replace in the top group, which is not enough.\n const hasTopGroup = canUpdate || canCopyLink || canDownload;\n const hasBottomGroup = canUpdate;\n const showSeparator = (canCopyLink || canDownload) && hasBottomGroup;\n\n // A role can read the library while holding none of these, and an empty\n // popup is worse than no trigger at all. Every flag is `false` until the\n // RBAC check settles, so wait for it — otherwise the trigger unmounts and\n // remounts on first paint for everyone.\n if (!isLoadingPermissions && !hasTopGroup && !hasBottomGroup) {\n return null;\n }\n\n return (\n <>\n <VisuallyHidden>\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple={false}\n onChange={handleFileChange}\n aria-hidden\n tabIndex={-1}\n />\n </VisuallyHidden>\n {/* Non-modal: Radix's default `modal` marks the rest of the document\n `aria-hidden` and blocks pointer events while the menu is open, so\n clicking a sibling row's trigger only dismissed this one — leaving\n every menu you touched to be closed one click at a time. Non-modal\n dismissal treats that click as both \"close this\" and \"open that\". */}\n <Menu.Root modal={false}>\n <Menu.Trigger\n tag={IconButton}\n icon={<More />}\n variant=\"ghost\"\n label={formatMessage({\n id: getTranslationKey('control-card.more-actions'),\n defaultMessage: 'More actions',\n })}\n />\n {/* `ActionsMenuContent` replaces the design system's flat 15rem clamp\n with the height Radix actually measured, and un-hides the scrollbar\n it would otherwise overflow into. See that file for why. */}\n <ActionsMenuContent popoverPlacement=\"bottom-end\" zIndex={2} minWidth=\"22rem\">\n {canUpdate && (\n <Menu.Item\n startIcon={<ArrowsCounterClockwise />}\n disabled={isReplacing}\n onSelect={() => setIsReplaceOpen(true)}\n >\n {formatMessage({\n id: getTranslationKey('list.assets.actions.replace'),\n defaultMessage: 'Replace media',\n })}\n </Menu.Item>\n )}\n {canCopyLink && (\n <Menu.Item startIcon={<Link />} onSelect={handleCopyLink}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.copy-link'),\n defaultMessage: 'Copy link to media',\n })}\n </Menu.Item>\n )}\n {canDownload && (\n <Menu.Item startIcon={<Download />} disabled={isDownloading} onSelect={handleDownload}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.download'),\n defaultMessage: 'Download media',\n })}\n </Menu.Item>\n )}\n {showSeparator && <Menu.Separator />}\n {canUpdate && (\n <>\n <Menu.Item startIcon={<ArrowRight />} onSelect={() => setIsMoveOpen(true)}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.move'),\n defaultMessage: 'Move to folder',\n })}\n </Menu.Item>\n <Menu.Item\n startIcon={<Trash />}\n variant=\"danger\"\n onSelect={() => setIsDeleteOpen(true)}\n >\n {formatMessage({\n id: getTranslationKey('list.assets.actions.delete'),\n defaultMessage: 'Delete',\n })}\n </Menu.Item>\n </>\n )}\n </ActionsMenuContent>\n </Menu.Root>\n <Dialog.Root open={isReplaceOpen} onOpenChange={setIsReplaceOpen}>\n <Dialog.Content>\n <Dialog.Header>\n {formatMessage({\n id: getTranslationKey('asset-details.replace.title'),\n defaultMessage: 'Replace this media file?',\n })}\n </Dialog.Header>\n <Dialog.Body textAlign=\"center\">\n <Flex direction=\"column\" textAlign=\"center\">\n <Typography variant=\"omega\">\n {formatMessage({\n id: getTranslationKey('asset-details.replace.description'),\n defaultMessage: 'Current content will be permanently replaced.',\n })}\n </Typography>\n {aiEnabled ? (\n <Typography variant=\"omega\">\n {formatMessage({\n id: getTranslationKey('asset-details.replace.description.ai'),\n defaultMessage: 'AI will generate new metadata after upload.',\n })}\n </Typography>\n ) : null}\n </Flex>\n </Dialog.Body>\n <Dialog.Footer>\n <Dialog.Cancel>\n <Button variant=\"tertiary\" fullWidth>\n {formatMessage({ id: 'app.components.Button.cancel', defaultMessage: 'Cancel' })}\n </Button>\n </Dialog.Cancel>\n <Dialog.Action>\n <Button variant=\"secondary\" onClick={handleReplaceContinue} fullWidth>\n {formatMessage({\n id: getTranslationKey('asset-details.replace.continue'),\n defaultMessage: 'Continue',\n })}\n </Button>\n </Dialog.Action>\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog.Root>\n {isMoveOpen && (\n <BulkMoveDialog\n open\n onClose={() => setIsMoveOpen(false)}\n items={moveItems}\n onSuccess={() => deselect(assetKey(asset.id))}\n />\n )}\n {/* Both dialogs live inside the row, so a background refetch that drops the\n row would take an open dialog with it. Nothing invalidates until the\n mutation resolves, and the dialog closes in the same tick, so the\n flows themselves can't trigger it. */}\n {isDeleteOpen && (\n <DeleteItemsDialog\n open\n onClose={() => setIsDeleteOpen(false)}\n target={{ fileIds: [asset.id], folderIds: [] }}\n onSuccess={() => deselect(assetKey(asset.id))}\n />\n )}\n </>\n );\n};\n"],"names":["AssetActionsMenu","asset","dragData","formatMessage","useIntl","getErrorMessage","useApiErrorMessage","copy","useClipboard","toggleNotification","useNotification","deselect","useAssetSelection","markBusy","useBusyAssetsOptional","canUpdate","canDownload","canCopyLink","isLoading","isLoadingPermissions","useMediaLibraryPermissions","replaceAsset","isReplacing","useReplaceAssetMutation","aiEnabled","useAIMetadataEnabled","mime","fileInputRef","useRef","isReplaceOpen","setIsReplaceOpen","useState","isMoveOpen","setIsMoveOpen","isDeleteOpen","setIsDeleteOpen","isDownloading","setIsDownloading","moveItems","useMemo","handleReplaceContinue","current","click","handleFileChange","event","file","target","files","value","releaseBusy","id","getTranslationKey","defaultMessage","res","type","message","error","handleCopyLink","url","prefixFileUrlWithBackendUrl","didCopy","handleDownload","downloadFile","name","hasTopGroup","hasBottomGroup","showSeparator","_jsxs","_Fragment","_jsx","VisuallyHidden","input","ref","multiple","onChange","aria-hidden","tabIndex","Menu","Root","modal","Trigger","tag","IconButton","icon","More","variant","label","ActionsMenuContent","popoverPlacement","zIndex","minWidth","Item","startIcon","ArrowsCounterClockwise","disabled","onSelect","Link","Download","Separator","ArrowRight","Trash","Dialog","open","onOpenChange","Content","Header","Body","textAlign","Flex","direction","Typography","Footer","Cancel","Button","fullWidth","Action","onClick","BulkMoveDialog","onClose","items","onSuccess","assetKey","DeleteItemsDialog","fileIds","folderIds"],"mappings":"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"}
@@ -10,7 +10,9 @@ var styledComponents = require('styled-components');
10
10
  var enums = require('../../../../../enums.js');
11
11
  var Drawer = require('../../../../components/Drawer.js');
12
12
  var useAIMetadataEnabled = require('../../../../hooks/useAIMetadataEnabled.js');
13
+ var useApiErrorMessage = require('../../../../hooks/useApiErrorMessage.js');
13
14
  var useMediaLibraryPermissions = require('../../../../hooks/useMediaLibraryPermissions.js');
15
+ var useTracking = require('../../../../hooks/useTracking.js');
14
16
  var api = require('../../../../services/api.js');
15
17
  var assets = require('../../../../services/assets.js');
16
18
  var folders = require('../../../../services/folders.js');
@@ -19,6 +21,7 @@ var files = require('../../../../utils/files.js');
19
21
  var getAssetIcon = require('../../../../utils/getAssetIcon.js');
20
22
  var translations = require('../../../../utils/translations.js');
21
23
  var useFolderInfo = require('../../hooks/useFolderInfo.js');
24
+ var useIsAssetDetailsOpen = require('../../hooks/useIsAssetDetailsOpen.js');
22
25
  var BusyOverlay = require('../BusyOverlay.js');
23
26
  var AssetCropEditor = require('./AssetCropEditor.js');
24
27
  var AssetPreview = require('./AssetPreview.js');
@@ -43,8 +46,6 @@ function _interopNamespace(e) {
43
46
 
44
47
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
45
48
 
46
- // Name of the parameter to look for in the URL to open the drawer
47
- const URL_PARAM = 'assetId';
48
49
  const DrawerNotifyContext = /*#__PURE__*/ React__namespace.createContext(null);
49
50
  const useDrawerNotify = ()=>{
50
51
  const ctx = React__namespace.useContext(DrawerNotifyContext);
@@ -62,12 +63,11 @@ const useAssetOperation = ()=>{
62
63
  return ctx;
63
64
  };
64
65
  /* -------------------------------------------------------------------------------------------------
65
- * useAssetDetailsParam - sync drawer visibility with URL ?{URL_PARAM}={id}
66
+ * useAssetDetailsParam - sync drawer visibility with URL ?{ASSET_DETAILS_URL_PARAM}={id}
66
67
  * -----------------------------------------------------------------------------------------------*/ const useAssetDetailsParam = ()=>{
67
68
  const [{ query }, setQuery] = strapiAdmin.useQueryParams();
68
- const detailsId = query?.[URL_PARAM];
69
- const assetId = detailsId ? parseInt(detailsId, 10) : null;
70
- const hasValidId = assetId !== null && !Number.isNaN(assetId);
69
+ const assetId = useIsAssetDetailsOpen.parseAssetDetailsId(query?.[useIsAssetDetailsOpen.ASSET_DETAILS_URL_PARAM]);
70
+ const hasValidId = assetId !== null;
71
71
  // Closing is driven by removing the URL param (a navigation), so navigation
72
72
  // guards like <Blocker> can intercept it. `isMounted` keeps the drawer in the
73
73
  // tree through the slide-out: it stays true once opened and only flips false
@@ -94,14 +94,14 @@ const useAssetOperation = ()=>{
94
94
  ]);
95
95
  const openDetails = React__namespace.useCallback((id)=>{
96
96
  setQuery({
97
- [URL_PARAM]: String(id)
97
+ [useIsAssetDetailsOpen.ASSET_DETAILS_URL_PARAM]: String(id)
98
98
  }, 'push', true);
99
99
  }, [
100
100
  setQuery
101
101
  ]);
102
102
  const closeDetails = React__namespace.useCallback(()=>{
103
103
  setQuery({
104
- [URL_PARAM]: undefined
104
+ [useIsAssetDetailsOpen.ASSET_DETAILS_URL_PARAM]: undefined
105
105
  }, 'remove', true);
106
106
  }, [
107
107
  setQuery
@@ -307,7 +307,6 @@ const LocationField = ({ label, rootLabel, folders, disabled })=>{
307
307
  children: [
308
308
  /*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Trigger, {
309
309
  children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
310
- withTooltip: false,
311
310
  label: triggerLabel,
312
311
  variant: "danger-light",
313
312
  children: /*#__PURE__*/ jsxRuntime.jsx(icons.Trash, {})
@@ -385,7 +384,6 @@ const CopyLinkButton = ({ asset })=>{
385
384
  });
386
385
  };
387
386
  return /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
388
- withTooltip: false,
389
387
  label: formatMessage({
390
388
  id: translations.getTranslationKey('asset-details.copy-link.trigger'),
391
389
  defaultMessage: 'Copy link'
@@ -418,7 +416,6 @@ const DownloadAssetButton = ({ asset })=>{
418
416
  }
419
417
  };
420
418
  return /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
421
- withTooltip: false,
422
419
  label: formatMessage({
423
420
  id: translations.getTranslationKey('asset-details.download.trigger'),
424
421
  defaultMessage: 'Download'
@@ -462,6 +459,7 @@ const ReplaceAssetButton = ({ mime })=>{
462
459
  children: /*#__PURE__*/ jsxRuntime.jsx("input", {
463
460
  ref: fileInputRef,
464
461
  type: "file",
462
+ accept: mime ?? '',
465
463
  multiple: false,
466
464
  onChange: handleFileChange,
467
465
  "aria-hidden": true,
@@ -469,7 +467,6 @@ const ReplaceAssetButton = ({ mime })=>{
469
467
  })
470
468
  }),
471
469
  /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
472
- withTooltip: false,
473
470
  label: formatMessage({
474
471
  id: translations.getTranslationKey('asset-details.replace.trigger'),
475
472
  defaultMessage: 'Replace this file'
@@ -544,36 +541,32 @@ const ReplaceAssetButton = ({ mime })=>{
544
541
  ]
545
542
  });
546
543
  };
547
- const AssetImageActions = ({ onCrop, mime })=>{
544
+ const AssetImageActions = ({ onCrop })=>{
548
545
  const { formatMessage } = reactIntl.useIntl();
549
546
  const isSubmitting = strapiAdmin.useForm('AssetImageActions', (state)=>state.isSubmitting);
550
- return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
547
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
551
548
  direction: "column",
552
549
  gap: 2,
553
- children: [
554
- /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
555
- withTooltip: false,
556
- label: formatMessage({
557
- id: translations.getTranslationKey('asset-details.crop.trigger'),
558
- defaultMessage: 'Crop'
559
- }),
560
- variant: "tertiary",
561
- onClick: onCrop,
562
- disabled: isSubmitting || !onCrop,
563
- children: /*#__PURE__*/ jsxRuntime.jsx(icons.Crop, {})
550
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
551
+ label: formatMessage({
552
+ id: translations.getTranslationKey('asset-details.crop.trigger'),
553
+ defaultMessage: 'Crop'
564
554
  }),
565
- /*#__PURE__*/ jsxRuntime.jsx(ReplaceAssetButton, {
566
- mime: mime
567
- })
568
- ]
555
+ variant: "tertiary",
556
+ onClick: onCrop,
557
+ disabled: isSubmitting || !onCrop,
558
+ children: /*#__PURE__*/ jsxRuntime.jsx(icons.Crop, {})
559
+ })
569
560
  });
570
561
  };
571
562
  const AssetDetails = ({ asset, closeDetails })=>{
572
563
  const { formatMessage, formatDate } = reactIntl.useIntl();
564
+ const getErrorMessage = useApiErrorMessage.useApiErrorMessage();
573
565
  const { canCreate, canUpdate, canDownload, canCopyLink } = useMediaLibraryPermissions.useMediaLibraryPermissions();
574
566
  const { data: folders$1 = [] } = folders.useGetAllFoldersQuery();
575
567
  const { toggleNotification } = strapiAdmin.useNotification();
576
568
  const [updateAsset] = assets.useUpdateAssetMutation();
569
+ const { trackUsage } = useTracking.useTracking();
577
570
  const [replaceMutation, { isLoading: isReplacing }] = assets.useReplaceAssetMutation();
578
571
  const [deleteMutation, { isLoading: isDeleting }] = assets.useDeleteAssetMutation();
579
572
  const [uploadCroppedCopy, { isLoading: isCropCopying }] = api.useUploadFileSilentlyMutation();
@@ -611,13 +604,19 @@ const AssetDetails = ({ asset, closeDetails })=>{
611
604
  if ('error' in res) {
612
605
  notify({
613
606
  type: 'danger',
614
- message: formatMessage({
607
+ message: getErrorMessage(res.error, formatMessage({
615
608
  id: translations.getTranslationKey('asset-details.update.error'),
616
609
  defaultMessage: 'Failed to update the file.'
617
- })
610
+ }))
618
611
  });
619
612
  return;
620
613
  }
614
+ trackUsage('didEditMediaLibraryElements', {
615
+ location: useTracking.MEDIA_LIBRARY_LOCATION,
616
+ // Legacy sends the mime top-level type (image/video/audio/application…).
617
+ type: asset.mime?.split('/')[0],
618
+ changeLocation: values.folder !== initialValues.folder
619
+ });
621
620
  notify({
622
621
  type: 'success',
623
622
  message: formatMessage({
@@ -634,17 +633,18 @@ const AssetDetails = ({ asset, closeDetails })=>{
634
633
  file
635
634
  });
636
635
  if ('error' in res) {
637
- const error = res.error;
638
- const message = error?.data?.error?.message ?? error?.data?.message ?? formatMessage({
639
- id: translations.getTranslationKey('asset-details.replace.error'),
640
- defaultMessage: 'Failed to replace the file.'
641
- });
642
636
  notify({
643
637
  type: 'danger',
644
- message
638
+ message: getErrorMessage(res.error, formatMessage({
639
+ id: translations.getTranslationKey('asset-details.replace.error'),
640
+ defaultMessage: 'Failed to replace the file.'
641
+ }))
645
642
  });
646
643
  return;
647
644
  }
645
+ trackUsage('didReplaceMedia', {
646
+ location: useTracking.MEDIA_LIBRARY_LOCATION
647
+ });
648
648
  notify({
649
649
  type: 'success',
650
650
  message: formatMessage({
@@ -655,22 +655,22 @@ const AssetDetails = ({ asset, closeDetails })=>{
655
655
  }, [
656
656
  asset.id,
657
657
  formatMessage,
658
+ getErrorMessage,
658
659
  notify,
659
- replaceMutation
660
+ replaceMutation,
661
+ trackUsage
660
662
  ]);
661
663
  // Owns the delete: on error notify in-drawer (drawer stays), on success fire
662
664
  // a persistent global notification then close the drawer.
663
665
  const handleDelete = React__namespace.useCallback(async ()=>{
664
666
  const res = await deleteMutation(asset.id);
665
667
  if ('error' in res) {
666
- const error = res.error;
667
- const message = error?.data?.error?.message ?? error?.data?.message ?? formatMessage({
668
- id: translations.getTranslationKey('asset-details.delete.error'),
669
- defaultMessage: 'Failed to delete the asset.'
670
- });
671
668
  notify({
672
669
  type: 'danger',
673
- message
670
+ message: getErrorMessage(res.error, formatMessage({
671
+ id: translations.getTranslationKey('asset-details.delete.error'),
672
+ defaultMessage: 'Failed to delete the asset.'
673
+ }))
674
674
  });
675
675
  return;
676
676
  }
@@ -690,16 +690,17 @@ const AssetDetails = ({ asset, closeDetails })=>{
690
690
  deleteMutation,
691
691
  folderName,
692
692
  formatMessage,
693
+ getErrorMessage,
693
694
  notify,
694
695
  toggleNotification
695
696
  ]);
696
- const notifyCropError = ()=>{
697
+ const notifyCropError = (error)=>{
697
698
  notify({
698
699
  type: 'danger',
699
- message: formatMessage({
700
+ message: getErrorMessage(error, formatMessage({
700
701
  id: translations.getTranslationKey('asset-details.crop.error'),
701
702
  defaultMessage: 'Failed to crop the file.'
702
- })
703
+ }))
703
704
  });
704
705
  };
705
706
  // Apply: replace the original binary with the cropped file + focal point.
@@ -715,9 +716,13 @@ const AssetDetails = ({ asset, closeDetails })=>{
715
716
  }
716
717
  });
717
718
  if ('error' in res) {
718
- notifyCropError();
719
+ notifyCropError(res.error);
719
720
  return;
720
721
  }
722
+ trackUsage('didCropFile', {
723
+ location: useTracking.MEDIA_LIBRARY_LOCATION,
724
+ duplicatedFile: false
725
+ });
721
726
  notify({
722
727
  type: 'success',
723
728
  message: formatMessage({
@@ -742,9 +747,13 @@ const AssetDetails = ({ asset, closeDetails })=>{
742
747
  }
743
748
  });
744
749
  if ('error' in res) {
745
- notifyCropError();
750
+ notifyCropError(res.error);
746
751
  return;
747
752
  }
753
+ trackUsage('didCropFile', {
754
+ location: useTracking.MEDIA_LIBRARY_LOCATION,
755
+ duplicatedFile: true
756
+ });
748
757
  notify({
749
758
  type: 'success',
750
759
  message: formatMessage({
@@ -813,8 +822,7 @@ const AssetDetails = ({ asset, closeDetails })=>{
813
822
  /*#__PURE__*/ jsxRuntime.jsx(AssetPreview.AssetPreview, {
814
823
  asset: asset,
815
824
  actions: isImage && canUpdate ? /*#__PURE__*/ jsxRuntime.jsx(AssetImageActions, {
816
- onCrop: ()=>setIsCropOpen(true),
817
- mime: asset.mime
825
+ onCrop: ()=>setIsCropOpen(true)
818
826
  }) : null
819
827
  }),
820
828
  /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
@@ -967,6 +975,9 @@ const AssetDetails = ({ asset, closeDetails })=>{
967
975
  }),
968
976
  canDownload && /*#__PURE__*/ jsxRuntime.jsx(DownloadAssetButton, {
969
977
  asset: asset
978
+ }),
979
+ canUpdate && /*#__PURE__*/ jsxRuntime.jsx(ReplaceAssetButton, {
980
+ mime: asset.mime
970
981
  })
971
982
  ]
972
983
  }),