@strapi/content-manager 0.0.0-experimental.e60ec1829240dae21c1e1d29076681c322288813 → 0.0.0-experimental.e8d8fc824d0f6a695b2a9ebaa4680ed21c3645ca

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 (257) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/{CardDragPreview-DSVYodBX.js → CardDragPreview-C0QyJgRA.js} +10 -14
  3. package/dist/_chunks/CardDragPreview-C0QyJgRA.js.map +1 -0
  4. package/dist/_chunks/{CardDragPreview-ikSG4M46.mjs → CardDragPreview-DOxamsuj.mjs} +7 -9
  5. package/dist/_chunks/CardDragPreview-DOxamsuj.mjs.map +1 -0
  6. package/dist/_chunks/{ComponentConfigurationPage-DjWJdz6Y.js → ComponentConfigurationPage-BlzvDpbX.js} +5 -6
  7. package/dist/_chunks/{ComponentConfigurationPage-DjWJdz6Y.js.map → ComponentConfigurationPage-BlzvDpbX.js.map} +1 -1
  8. package/dist/_chunks/{ComponentConfigurationPage-BPvzFjM7.mjs → ComponentConfigurationPage-DaPOlQaD.mjs} +4 -4
  9. package/dist/_chunks/{ComponentConfigurationPage-BPvzFjM7.mjs.map → ComponentConfigurationPage-DaPOlQaD.mjs.map} +1 -1
  10. package/dist/_chunks/{ComponentIcon-BBQsYCVn.js → ComponentIcon-CRbtQEUV.js} +9 -4
  11. package/dist/_chunks/ComponentIcon-CRbtQEUV.js.map +1 -0
  12. package/dist/_chunks/{ComponentIcon-BOFnK76n.mjs → ComponentIcon-u4bIXTFY.mjs} +9 -3
  13. package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -0
  14. package/dist/_chunks/{EditConfigurationPage-DacbqQ_f.mjs → EditConfigurationPage-BZPXItXo.mjs} +4 -4
  15. package/dist/_chunks/{EditConfigurationPage-DacbqQ_f.mjs.map → EditConfigurationPage-BZPXItXo.mjs.map} +1 -1
  16. package/dist/_chunks/{EditConfigurationPage-Dmv83RlS.js → EditConfigurationPage-uy-v43AR.js} +5 -6
  17. package/dist/_chunks/{EditConfigurationPage-Dmv83RlS.js.map → EditConfigurationPage-uy-v43AR.js.map} +1 -1
  18. package/dist/_chunks/{EditViewPage-DvNpQkam.js → EditViewPage-DT6A4ayX.js} +102 -54
  19. package/dist/_chunks/EditViewPage-DT6A4ayX.js.map +1 -0
  20. package/dist/_chunks/EditViewPage-oOLeTySr.mjs +254 -0
  21. package/dist/_chunks/EditViewPage-oOLeTySr.mjs.map +1 -0
  22. package/dist/_chunks/{Field-6gvGdPBV.mjs → Field-D7dv2aUX.mjs} +1192 -896
  23. package/dist/_chunks/Field-D7dv2aUX.mjs.map +1 -0
  24. package/dist/_chunks/{Field-DmVKIAOo.js → Field-kYFVIGiP.js} +1238 -943
  25. package/dist/_chunks/Field-kYFVIGiP.js.map +1 -0
  26. package/dist/_chunks/FieldTypeIcon-CMlNO8PE.mjs.map +1 -1
  27. package/dist/_chunks/FieldTypeIcon-Dnwq_IRF.js.map +1 -1
  28. package/dist/_chunks/{Form-DW6K1IH-.mjs → Form-BxR6sc29.mjs} +67 -46
  29. package/dist/_chunks/Form-BxR6sc29.mjs.map +1 -0
  30. package/dist/_chunks/{Form-CPZC9vWa.js → Form-CCijSg3V.js} +68 -49
  31. package/dist/_chunks/Form-CCijSg3V.js.map +1 -0
  32. package/dist/_chunks/{History-DeAPlvtv.js → History-BMndx49M.js} +182 -147
  33. package/dist/_chunks/History-BMndx49M.js.map +1 -0
  34. package/dist/_chunks/{History-Dmr9fmUA.mjs → History-D8F7aYQU.mjs} +181 -144
  35. package/dist/_chunks/History-D8F7aYQU.mjs.map +1 -0
  36. package/dist/_chunks/{ListConfigurationPage-DPCwW5Vr.js → ListConfigurationPage-DouY1EWM.js} +72 -63
  37. package/dist/_chunks/ListConfigurationPage-DouY1EWM.js.map +1 -0
  38. package/dist/_chunks/{ListConfigurationPage-DhwvYcNv.mjs → ListConfigurationPage-DqAdSPwC.mjs} +68 -57
  39. package/dist/_chunks/ListConfigurationPage-DqAdSPwC.mjs.map +1 -0
  40. package/dist/_chunks/{ListViewPage-5ySZ-VUs.js → ListViewPage-BPVmh9pq.js} +179 -164
  41. package/dist/_chunks/ListViewPage-BPVmh9pq.js.map +1 -0
  42. package/dist/_chunks/{ListViewPage-BtAwuYLE.mjs → ListViewPage-C73F0jPh.mjs} +175 -159
  43. package/dist/_chunks/ListViewPage-C73F0jPh.mjs.map +1 -0
  44. package/dist/_chunks/{NoContentTypePage-DSPxnxxp.mjs → NoContentTypePage-B5w7iJOF.mjs} +3 -3
  45. package/dist/_chunks/NoContentTypePage-B5w7iJOF.mjs.map +1 -0
  46. package/dist/_chunks/{NoContentTypePage-DOC_yWOf.js → NoContentTypePage-BwcL--4H.js} +3 -3
  47. package/dist/_chunks/NoContentTypePage-BwcL--4H.js.map +1 -0
  48. package/dist/_chunks/{NoPermissionsPage-Dwu8rRJu.js → NoPermissionsPage-BMFKVcwJ.js} +2 -2
  49. package/dist/_chunks/{NoPermissionsPage-Dwu8rRJu.js.map → NoPermissionsPage-BMFKVcwJ.js.map} +1 -1
  50. package/dist/_chunks/{NoPermissionsPage-UWDC-1Tw.mjs → NoPermissionsPage-UnEgMGK4.mjs} +2 -2
  51. package/dist/_chunks/{NoPermissionsPage-UWDC-1Tw.mjs.map → NoPermissionsPage-UnEgMGK4.mjs.map} +1 -1
  52. package/dist/_chunks/Preview-B7PR3Ok_.js +312 -0
  53. package/dist/_chunks/Preview-B7PR3Ok_.js.map +1 -0
  54. package/dist/_chunks/Preview-DECOhK0D.mjs +294 -0
  55. package/dist/_chunks/Preview-DECOhK0D.mjs.map +1 -0
  56. package/dist/_chunks/{Relations-J8cscLlR.mjs → Relations-DinMQJ4B.mjs} +138 -94
  57. package/dist/_chunks/Relations-DinMQJ4B.mjs.map +1 -0
  58. package/dist/_chunks/{Relations-CgWtgnPe.js → Relations-lndx3aQk.js} +142 -100
  59. package/dist/_chunks/Relations-lndx3aQk.js.map +1 -0
  60. package/dist/_chunks/{en-C-V1_90f.js → en-BK8Xyl5I.js} +38 -18
  61. package/dist/_chunks/{en-C-V1_90f.js.map → en-BK8Xyl5I.js.map} +1 -1
  62. package/dist/_chunks/{en-MBPul9Su.mjs → en-Dtk_ot79.mjs} +38 -18
  63. package/dist/_chunks/{en-MBPul9Su.mjs.map → en-Dtk_ot79.mjs.map} +1 -1
  64. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  65. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  66. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  67. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  68. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  69. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  70. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  71. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  72. package/dist/_chunks/hooks-BAaaKPS_.js.map +1 -1
  73. package/dist/_chunks/{index-CwRRo1V9.mjs → index-C2SagWVW.mjs} +2070 -988
  74. package/dist/_chunks/index-C2SagWVW.mjs.map +1 -0
  75. package/dist/_chunks/{index-C6AH2hEl.js → index-Cnw4gqee.js} +2047 -966
  76. package/dist/_chunks/index-Cnw4gqee.js.map +1 -0
  77. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  78. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  79. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  80. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  81. package/dist/_chunks/{layout-jIDzX0Fp.mjs → layout-DY_D9MGA.mjs} +46 -28
  82. package/dist/_chunks/layout-DY_D9MGA.mjs.map +1 -0
  83. package/dist/_chunks/{layout-B_SXLhqf.js → layout-ivwIVPnV.js} +47 -32
  84. package/dist/_chunks/layout-ivwIVPnV.js.map +1 -0
  85. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  86. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  87. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  88. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  89. package/dist/_chunks/{relations-iBMa_OFG.js → relations-B7C7O_Pv.js} +6 -7
  90. package/dist/_chunks/relations-B7C7O_Pv.js.map +1 -0
  91. package/dist/_chunks/{relations-CuvIgCqI.mjs → relations-Boc5Y9kX.mjs} +6 -7
  92. package/dist/_chunks/relations-Boc5Y9kX.mjs.map +1 -0
  93. package/dist/_chunks/useDebounce-CtcjDB3L.js +28 -0
  94. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  95. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  96. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  97. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js → useDragAndDrop-BMtgCYzL.js} +5 -9
  98. package/dist/_chunks/useDragAndDrop-BMtgCYzL.js.map +1 -0
  99. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs → useDragAndDrop-DJ6jqvZN.mjs} +4 -7
  100. package/dist/_chunks/useDragAndDrop-DJ6jqvZN.mjs.map +1 -0
  101. package/dist/admin/index.js +3 -1
  102. package/dist/admin/index.js.map +1 -1
  103. package/dist/admin/index.mjs +9 -7
  104. package/dist/admin/src/components/ComponentIcon.d.ts +6 -3
  105. package/dist/admin/src/content-manager.d.ts +6 -5
  106. package/dist/admin/src/exports.d.ts +2 -1
  107. package/dist/admin/src/history/components/VersionInputRenderer.d.ts +1 -1
  108. package/dist/admin/src/history/index.d.ts +3 -0
  109. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  110. package/dist/admin/src/hooks/useDocument.d.ts +37 -9
  111. package/dist/admin/src/hooks/useDocumentActions.d.ts +24 -3
  112. package/dist/admin/src/hooks/useDocumentLayout.d.ts +2 -2
  113. package/dist/admin/src/hooks/useDragAndDrop.d.ts +4 -4
  114. package/dist/admin/src/hooks/useKeyboardDragAndDrop.d.ts +1 -1
  115. package/dist/admin/src/index.d.ts +1 -0
  116. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  117. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +12 -5
  118. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +2 -2
  119. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Code.d.ts +7 -0
  120. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/utils/prismLanguages.d.ts +49 -0
  121. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksInput.d.ts +3 -3
  122. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +5 -0
  123. package/dist/admin/src/pages/EditView/components/FormInputs/Component/Input.d.ts +2 -2
  124. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.d.ts +3 -5
  125. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +1 -1
  126. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +30 -18
  127. package/dist/admin/src/pages/EditView/components/FormInputs/UID.d.ts +2 -2
  128. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +3 -49
  129. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/Field.d.ts +2 -2
  130. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  131. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +16 -53
  132. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  133. package/dist/admin/src/pages/EditView/components/InputRenderer.d.ts +2 -10
  134. package/dist/admin/src/pages/ListView/components/BulkActions/Actions.d.ts +3 -30
  135. package/dist/admin/src/pages/ListView/components/BulkActions/ConfirmBulkActionDialog.d.ts +2 -2
  136. package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +9 -26
  137. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  138. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  139. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  140. package/dist/admin/src/preview/index.d.ts +4 -0
  141. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  142. package/dist/admin/src/preview/routes.d.ts +3 -0
  143. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  144. package/dist/admin/src/router.d.ts +1 -1
  145. package/dist/admin/src/services/api.d.ts +2 -3
  146. package/dist/admin/src/services/components.d.ts +2 -2
  147. package/dist/admin/src/services/contentTypes.d.ts +5 -5
  148. package/dist/admin/src/services/documents.d.ts +31 -20
  149. package/dist/admin/src/services/init.d.ts +2 -2
  150. package/dist/admin/src/services/relations.d.ts +3 -3
  151. package/dist/admin/src/services/uid.d.ts +3 -3
  152. package/dist/admin/src/utils/api.d.ts +4 -18
  153. package/dist/admin/src/utils/validation.d.ts +5 -7
  154. package/dist/server/index.js +886 -480
  155. package/dist/server/index.js.map +1 -1
  156. package/dist/server/index.mjs +892 -485
  157. package/dist/server/index.mjs.map +1 -1
  158. package/dist/server/src/bootstrap.d.ts.map +1 -1
  159. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  160. package/dist/server/src/controllers/index.d.ts.map +1 -1
  161. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  162. package/dist/server/src/controllers/single-types.d.ts.map +1 -1
  163. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  164. package/dist/server/src/controllers/utils/metadata.d.ts +23 -0
  165. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -0
  166. package/dist/server/src/controllers/validation/dimensions.d.ts +11 -0
  167. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -0
  168. package/dist/server/src/controllers/validation/index.d.ts +1 -1
  169. package/dist/server/src/history/services/history.d.ts.map +1 -1
  170. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  171. package/dist/server/src/history/services/utils.d.ts +4 -4
  172. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  173. package/dist/server/src/index.d.ts +22 -42
  174. package/dist/server/src/index.d.ts.map +1 -1
  175. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  176. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  177. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  178. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  179. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  180. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  181. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  182. package/dist/server/src/preview/index.d.ts +4 -0
  183. package/dist/server/src/preview/index.d.ts.map +1 -0
  184. package/dist/server/src/preview/routes/index.d.ts +8 -0
  185. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  186. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  187. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  188. package/dist/server/src/preview/services/index.d.ts +16 -0
  189. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  190. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  191. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  192. package/dist/server/src/preview/services/preview.d.ts +12 -0
  193. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  194. package/dist/server/src/preview/utils.d.ts +19 -0
  195. package/dist/server/src/preview/utils.d.ts.map +1 -0
  196. package/dist/server/src/register.d.ts.map +1 -1
  197. package/dist/server/src/routes/index.d.ts.map +1 -1
  198. package/dist/server/src/services/document-manager.d.ts +13 -12
  199. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  200. package/dist/server/src/services/document-metadata.d.ts +16 -35
  201. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  202. package/dist/server/src/services/index.d.ts +22 -42
  203. package/dist/server/src/services/index.d.ts.map +1 -1
  204. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  205. package/dist/server/src/services/utils/populate.d.ts +8 -1
  206. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  207. package/dist/server/src/utils/index.d.ts +2 -0
  208. package/dist/server/src/utils/index.d.ts.map +1 -1
  209. package/dist/shared/contracts/collection-types.d.ts +17 -7
  210. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  211. package/dist/shared/contracts/index.d.ts +1 -0
  212. package/dist/shared/contracts/index.d.ts.map +1 -1
  213. package/dist/shared/contracts/preview.d.ts +27 -0
  214. package/dist/shared/contracts/preview.d.ts.map +1 -0
  215. package/dist/shared/contracts/relations.d.ts +2 -2
  216. package/dist/shared/contracts/relations.d.ts.map +1 -1
  217. package/dist/shared/index.js +4 -0
  218. package/dist/shared/index.js.map +1 -1
  219. package/dist/shared/index.mjs +4 -0
  220. package/dist/shared/index.mjs.map +1 -1
  221. package/package.json +22 -21
  222. package/dist/_chunks/CardDragPreview-DSVYodBX.js.map +0 -1
  223. package/dist/_chunks/CardDragPreview-ikSG4M46.mjs.map +0 -1
  224. package/dist/_chunks/ComponentIcon-BBQsYCVn.js.map +0 -1
  225. package/dist/_chunks/ComponentIcon-BOFnK76n.mjs.map +0 -1
  226. package/dist/_chunks/EditViewPage-DDS6H9HO.mjs +0 -203
  227. package/dist/_chunks/EditViewPage-DDS6H9HO.mjs.map +0 -1
  228. package/dist/_chunks/EditViewPage-DvNpQkam.js.map +0 -1
  229. package/dist/_chunks/Field-6gvGdPBV.mjs.map +0 -1
  230. package/dist/_chunks/Field-DmVKIAOo.js.map +0 -1
  231. package/dist/_chunks/Form-CPZC9vWa.js.map +0 -1
  232. package/dist/_chunks/Form-DW6K1IH-.mjs.map +0 -1
  233. package/dist/_chunks/History-DeAPlvtv.js.map +0 -1
  234. package/dist/_chunks/History-Dmr9fmUA.mjs.map +0 -1
  235. package/dist/_chunks/ListConfigurationPage-DPCwW5Vr.js.map +0 -1
  236. package/dist/_chunks/ListConfigurationPage-DhwvYcNv.mjs.map +0 -1
  237. package/dist/_chunks/ListViewPage-5ySZ-VUs.js.map +0 -1
  238. package/dist/_chunks/ListViewPage-BtAwuYLE.mjs.map +0 -1
  239. package/dist/_chunks/NoContentTypePage-DOC_yWOf.js.map +0 -1
  240. package/dist/_chunks/NoContentTypePage-DSPxnxxp.mjs.map +0 -1
  241. package/dist/_chunks/Relations-CgWtgnPe.js.map +0 -1
  242. package/dist/_chunks/Relations-J8cscLlR.mjs.map +0 -1
  243. package/dist/_chunks/index-C6AH2hEl.js.map +0 -1
  244. package/dist/_chunks/index-CwRRo1V9.mjs.map +0 -1
  245. package/dist/_chunks/layout-B_SXLhqf.js.map +0 -1
  246. package/dist/_chunks/layout-jIDzX0Fp.mjs.map +0 -1
  247. package/dist/_chunks/relations-CuvIgCqI.mjs.map +0 -1
  248. package/dist/_chunks/relations-iBMa_OFG.js.map +0 -1
  249. package/dist/_chunks/urls-CbOsUOoW.mjs +0 -7
  250. package/dist/_chunks/urls-CbOsUOoW.mjs.map +0 -1
  251. package/dist/_chunks/urls-DzZya_gm.js +0 -6
  252. package/dist/_chunks/urls-DzZya_gm.js.map +0 -1
  253. package/dist/_chunks/useDragAndDrop-DdHgKsqq.mjs.map +0 -1
  254. package/dist/_chunks/useDragAndDrop-J0TUUbR6.js.map +0 -1
  255. package/dist/server/src/controllers/utils/dimensions.d.ts +0 -5
  256. package/dist/server/src/controllers/utils/dimensions.d.ts.map +0 -1
  257. package/strapi-server.js +0 -3
@@ -2,22 +2,20 @@
2
2
  const Icons = require("@strapi/icons");
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const strapiAdmin = require("@strapi/admin/strapi-admin");
5
- const qs = require("qs");
6
- const reactIntl = require("react-intl");
7
- const reactRouterDom = require("react-router-dom");
8
5
  const React = require("react");
9
6
  const designSystem = require("@strapi/design-system");
10
- const styled = require("styled-components");
7
+ const mapValues = require("lodash/fp/mapValues");
8
+ const reactIntl = require("react-intl");
9
+ const reactRouterDom = require("react-router-dom");
10
+ const styledComponents = require("styled-components");
11
11
  const yup = require("yup");
12
- const react = require("@reduxjs/toolkit/query/react");
13
- const axios = require("axios");
12
+ const qs = require("qs");
14
13
  const pipe = require("lodash/fp/pipe");
15
14
  const dateFns = require("date-fns");
16
15
  const toolkit = require("@reduxjs/toolkit");
17
16
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
18
17
  function _interopNamespace(e) {
19
- if (e && e.__esModule)
20
- return e;
18
+ if (e && e.__esModule) return e;
21
19
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
22
20
  if (e) {
23
21
  for (const k in e) {
@@ -34,16 +32,23 @@ function _interopNamespace(e) {
34
32
  return Object.freeze(n);
35
33
  }
36
34
  const React__namespace = /* @__PURE__ */ _interopNamespace(React);
37
- const styled__default = /* @__PURE__ */ _interopDefault(styled);
35
+ const mapValues__default = /* @__PURE__ */ _interopDefault(mapValues);
38
36
  const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
39
37
  const pipe__default = /* @__PURE__ */ _interopDefault(pipe);
40
- const __variableDynamicImportRuntimeHelper = (glob, path) => {
38
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
41
39
  const v = glob[path];
42
40
  if (v) {
43
41
  return typeof v === "function" ? v() : Promise.resolve(v);
44
42
  }
45
43
  return new Promise((_, reject) => {
46
- (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(reject.bind(null, new Error("Unknown variable dynamic import: " + path)));
44
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
45
+ reject.bind(
46
+ null,
47
+ new Error(
48
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
49
+ )
50
+ )
51
+ );
47
52
  });
48
53
  };
49
54
  const PLUGIN_ID = "content-manager";
@@ -73,42 +78,6 @@ const useInjectionZone = (area) => {
73
78
  const [page, position] = area.split(".");
74
79
  return contentManagerPlugin.getInjectedComponents(page, position);
75
80
  };
76
- const HistoryAction = ({ model, document }) => {
77
- const { formatMessage } = reactIntl.useIntl();
78
- const [{ query }] = strapiAdmin.useQueryParams();
79
- const navigate = reactRouterDom.useNavigate();
80
- const pluginsQueryParams = qs.stringify({ plugins: query.plugins }, { encode: false });
81
- if (!window.strapi.features.isEnabled("cms-content-history")) {
82
- return null;
83
- }
84
- return {
85
- icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ClockCounterClockwise, {}),
86
- label: formatMessage({
87
- id: "content-manager.history.document-action",
88
- defaultMessage: "Content History"
89
- }),
90
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
91
- disabled: (
92
- /**
93
- * The user is creating a new document.
94
- * It hasn't been saved yet, so there's no history to go to
95
- */
96
- !document || /**
97
- * The document has been created but the current dimension has never been saved.
98
- * For example, the user is creating a new locale in an existing document,
99
- * so there's no history for the document in that locale
100
- */
101
- !document.id || /**
102
- * History is only available for content types created by the user.
103
- * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
104
- * which start with `admin::` or `plugin::`
105
- */
106
- !model.startsWith("api::")
107
- ),
108
- position: "header"
109
- };
110
- };
111
- HistoryAction.type = "history";
112
81
  const ID = "id";
113
82
  const CREATED_BY_ATTRIBUTE_NAME = "createdBy";
114
83
  const UPDATED_BY_ATTRIBUTE_NAME = "updatedBy";
@@ -160,6 +129,7 @@ const DocumentRBAC = ({ children, permissions }) => {
160
129
  if (!slug) {
161
130
  throw new Error("Cannot find the slug param in the URL");
162
131
  }
132
+ const [{ rawQuery }] = strapiAdmin.useQueryParams();
163
133
  const userPermissions = strapiAdmin.useAuth("DocumentRBAC", (state) => state.permissions);
164
134
  const contentTypePermissions = React__namespace.useMemo(() => {
165
135
  const contentTypePermissions2 = userPermissions.filter(
@@ -170,7 +140,14 @@ const DocumentRBAC = ({ children, permissions }) => {
170
140
  return { ...acc, [action]: [permission] };
171
141
  }, {});
172
142
  }, [slug, userPermissions]);
173
- const { isLoading, allowedActions } = strapiAdmin.useRBAC(contentTypePermissions, permissions ?? void 0);
143
+ const { isLoading, allowedActions } = strapiAdmin.useRBAC(
144
+ contentTypePermissions,
145
+ permissions ?? void 0,
146
+ // TODO: useRBAC context should be typed and built differently
147
+ // We are passing raw query as context to the hook so that it can
148
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
149
+ rawQuery
150
+ );
174
151
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
175
152
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
176
153
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -179,9 +156,8 @@ const DocumentRBAC = ({ children, permissions }) => {
179
156
  const name = removeNumericalStrings(fieldName.split("."));
180
157
  const componentFieldNames = fieldsUserCanAction.filter((field) => field.split(".").length > 1);
181
158
  if (fieldType === "component") {
182
- const componentOrDynamicZoneFields = componentFieldNames.map((field) => field.split("."));
183
- return componentOrDynamicZoneFields.some((field) => {
184
- return field.includes(fieldName);
159
+ return componentFieldNames.some((field) => {
160
+ return field.includes(name.join("."));
185
161
  });
186
162
  }
187
163
  if (name.length > 1) {
@@ -211,89 +187,20 @@ const extractAndDedupeFields = (permissions = []) => permissions.flatMap((permis
211
187
  (field, index2, arr) => arr.indexOf(field) === index2 && typeof field === "string"
212
188
  );
213
189
  const removeNumericalStrings = (arr) => arr.filter((item) => isNaN(Number(item)));
214
- const buildValidParams = (query) => {
215
- if (!query)
216
- return query;
217
- const { plugins: _, ...validQueryParams } = {
218
- ...query,
219
- ...Object.values(query?.plugins ?? {}).reduce(
220
- (acc, current) => Object.assign(acc, current),
221
- {}
222
- )
223
- };
224
- if ("_q" in validQueryParams) {
225
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
226
- }
227
- return validQueryParams;
228
- };
229
- const axiosBaseQuery = () => async (query, { signal }) => {
230
- try {
231
- const { get, post, del, put } = strapiAdmin.getFetchClient();
232
- if (typeof query === "string") {
233
- const result = await get(query, { signal });
234
- return { data: result.data };
235
- } else {
236
- const { url, method = "GET", data, config } = query;
237
- if (method === "POST") {
238
- const result2 = await post(url, data, { ...config, signal });
239
- return { data: result2.data };
240
- }
241
- if (method === "DELETE") {
242
- const result2 = await del(url, { ...config, signal });
243
- return { data: result2.data };
244
- }
245
- if (method === "PUT") {
246
- const result2 = await put(url, data, { ...config, signal });
247
- return { data: result2.data };
248
- }
249
- const result = await get(url, { ...config, signal });
250
- return { data: result.data };
251
- }
252
- } catch (err) {
253
- if (axios.isAxiosError(err)) {
254
- if (typeof err.response?.data === "object" && err.response?.data !== null && "error" in err.response?.data) {
255
- return { data: void 0, error: err.response?.data.error };
256
- } else {
257
- return {
258
- data: void 0,
259
- error: {
260
- name: "UnknownError",
261
- message: "There was an unknown error response from the API",
262
- details: err.response?.data,
263
- status: err.response?.status
264
- }
265
- };
266
- }
267
- }
268
- const error = err;
269
- return {
270
- data: void 0,
271
- error: {
272
- name: error.name,
273
- message: error.message,
274
- stack: error.stack
275
- }
276
- };
277
- }
278
- };
279
- const isBaseQueryError = (error) => {
280
- return error.name !== void 0;
281
- };
282
- const contentManagerApi = react.createApi({
283
- reducerPath: "contentManagerApi",
284
- baseQuery: axiosBaseQuery(),
285
- tagTypes: [
190
+ const contentManagerApi = strapiAdmin.adminApi.enhanceEndpoints({
191
+ addTagTypes: [
286
192
  "ComponentConfiguration",
287
193
  "ContentTypesConfiguration",
288
194
  "ContentTypeSettings",
289
195
  "Document",
290
196
  "InitialData",
291
197
  "HistoryVersion",
292
- "Relations"
293
- ],
294
- endpoints: () => ({})
198
+ "Relations",
199
+ "UidAvailability"
200
+ ]
295
201
  });
296
202
  const documentApi = contentManagerApi.injectEndpoints({
203
+ overrideExisting: true,
297
204
  endpoints: (builder) => ({
298
205
  autoCloneDocument: builder.mutation({
299
206
  query: ({ model, sourceId, query }) => ({
@@ -303,7 +210,12 @@ const documentApi = contentManagerApi.injectEndpoints({
303
210
  params: query
304
211
  }
305
212
  }),
306
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
213
+ invalidatesTags: (_result, error, { model }) => {
214
+ if (error) {
215
+ return [];
216
+ }
217
+ return [{ type: "Document", id: `${model}_LIST` }];
218
+ }
307
219
  }),
308
220
  cloneDocument: builder.mutation({
309
221
  query: ({ model, sourceId, data, params }) => ({
@@ -314,7 +226,10 @@ const documentApi = contentManagerApi.injectEndpoints({
314
226
  params
315
227
  }
316
228
  }),
317
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
229
+ invalidatesTags: (_result, _error, { model }) => [
230
+ { type: "Document", id: `${model}_LIST` },
231
+ { type: "UidAvailability", id: model }
232
+ ]
318
233
  }),
319
234
  /**
320
235
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -331,7 +246,8 @@ const documentApi = contentManagerApi.injectEndpoints({
331
246
  }),
332
247
  invalidatesTags: (result, _error, { model }) => [
333
248
  { type: "Document", id: `${model}_LIST` },
334
- "Relations"
249
+ "Relations",
250
+ { type: "UidAvailability", id: model }
335
251
  ]
336
252
  }),
337
253
  deleteDocument: builder.mutation({
@@ -347,12 +263,15 @@ const documentApi = contentManagerApi.injectEndpoints({
347
263
  ]
348
264
  }),
349
265
  deleteManyDocuments: builder.mutation({
350
- query: ({ model, ...body }) => ({
266
+ query: ({ model, params, ...body }) => ({
351
267
  url: `/content-manager/collection-types/${model}/actions/bulkDelete`,
352
268
  method: "POST",
353
- data: body
269
+ data: body,
270
+ config: {
271
+ params
272
+ }
354
273
  }),
355
- invalidatesTags: (_res, _error, { model, documentIds }) => documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` }))
274
+ invalidatesTags: (_res, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
356
275
  }),
357
276
  discardDocument: builder.mutation({
358
277
  query: ({ collectionType, model, documentId, params }) => ({
@@ -369,7 +288,8 @@ const documentApi = contentManagerApi.injectEndpoints({
369
288
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
370
289
  },
371
290
  { type: "Document", id: `${model}_LIST` },
372
- "Relations"
291
+ "Relations",
292
+ { type: "UidAvailability", id: model }
373
293
  ];
374
294
  }
375
295
  }),
@@ -382,11 +302,12 @@ const documentApi = contentManagerApi.injectEndpoints({
382
302
  url: `/content-manager/collection-types/${model}`,
383
303
  method: "GET",
384
304
  config: {
385
- params
305
+ params: qs.stringify(params, { encode: true })
386
306
  }
387
307
  }),
388
308
  providesTags: (result, _error, arg) => {
389
309
  return [
310
+ { type: "Document", id: `ALL_LIST` },
390
311
  { type: "Document", id: `${arg.model}_LIST` },
391
312
  ...result?.results.map(({ documentId }) => ({
392
313
  type: "Document",
@@ -425,6 +346,11 @@ const documentApi = contentManagerApi.injectEndpoints({
425
346
  {
426
347
  type: "Document",
427
348
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
349
+ },
350
+ // Make it easy to invalidate all individual documents queries for a model
351
+ {
352
+ type: "Document",
353
+ id: `${model}_ALL_ITEMS`
428
354
  }
429
355
  ];
430
356
  }
@@ -463,10 +389,13 @@ const documentApi = contentManagerApi.injectEndpoints({
463
389
  }
464
390
  }),
465
391
  publishManyDocuments: builder.mutation({
466
- query: ({ model, ...body }) => ({
392
+ query: ({ model, params, ...body }) => ({
467
393
  url: `/content-manager/collection-types/${model}/actions/bulkPublish`,
468
394
  method: "POST",
469
- data: body
395
+ data: body,
396
+ config: {
397
+ params
398
+ }
470
399
  }),
471
400
  invalidatesTags: (_res, _error, { model, documentIds }) => documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` }))
472
401
  }),
@@ -485,8 +414,21 @@ const documentApi = contentManagerApi.injectEndpoints({
485
414
  type: "Document",
486
415
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
487
416
  },
488
- "Relations"
417
+ "Relations",
418
+ { type: "UidAvailability", id: model }
489
419
  ];
420
+ },
421
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
422
+ const patchResult = dispatch(
423
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
424
+ Object.assign(draft.data, data);
425
+ })
426
+ );
427
+ try {
428
+ await queryFulfilled;
429
+ } catch {
430
+ patchResult.undo();
431
+ }
490
432
  }
491
433
  }),
492
434
  unpublishDocument: builder.mutation({
@@ -508,10 +450,13 @@ const documentApi = contentManagerApi.injectEndpoints({
508
450
  }
509
451
  }),
510
452
  unpublishManyDocuments: builder.mutation({
511
- query: ({ model, ...body }) => ({
453
+ query: ({ model, params, ...body }) => ({
512
454
  url: `/content-manager/collection-types/${model}/actions/bulkUnpublish`,
513
455
  method: "POST",
514
- data: body
456
+ data: body,
457
+ config: {
458
+ params
459
+ }
515
460
  }),
516
461
  invalidatesTags: (_res, _error, { model, documentIds }) => documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` }))
517
462
  })
@@ -535,20 +480,53 @@ const {
535
480
  useUnpublishDocumentMutation,
536
481
  useUnpublishManyDocumentsMutation
537
482
  } = documentApi;
538
- const createYupSchema = (attributes = {}, components = {}) => {
483
+ const buildValidParams = (query) => {
484
+ if (!query) return query;
485
+ const { plugins: _, ...validQueryParams } = {
486
+ ...query,
487
+ ...Object.values(query?.plugins ?? {}).reduce(
488
+ (acc, current) => Object.assign(acc, current),
489
+ {}
490
+ )
491
+ };
492
+ return validQueryParams;
493
+ };
494
+ const isBaseQueryError = (error) => {
495
+ return error.name !== void 0;
496
+ };
497
+ const arrayValidator = (attribute, options) => ({
498
+ message: strapiAdmin.translatedErrors.required,
499
+ test(value) {
500
+ if (options.status === "draft") {
501
+ return true;
502
+ }
503
+ if (!attribute.required) {
504
+ return true;
505
+ }
506
+ if (!value) {
507
+ return false;
508
+ }
509
+ if (Array.isArray(value) && value.length === 0) {
510
+ return false;
511
+ }
512
+ return true;
513
+ }
514
+ });
515
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
539
516
  const createModelSchema = (attributes2) => yup__namespace.object().shape(
540
517
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
541
518
  if (DOCUMENT_META_FIELDS.includes(name)) {
542
519
  return acc;
543
520
  }
544
521
  const validations = [
522
+ addNullableValidation,
545
523
  addRequiredValidation,
546
524
  addMinLengthValidation,
547
525
  addMaxLengthValidation,
548
526
  addMinValidation,
549
527
  addMaxValidation,
550
528
  addRegexValidation
551
- ].map((fn) => fn(attribute));
529
+ ].map((fn) => fn(attribute, options));
552
530
  const transformSchema = pipe__default.default(...validations);
553
531
  switch (attribute.type) {
554
532
  case "component": {
@@ -558,12 +536,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
558
536
  ...acc,
559
537
  [name]: transformSchema(
560
538
  yup__namespace.array().of(createModelSchema(attributes3).nullable(false))
561
- )
539
+ ).test(arrayValidator(attribute, options))
562
540
  };
563
541
  } else {
564
542
  return {
565
543
  ...acc,
566
- [name]: transformSchema(createModelSchema(attributes3))
544
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
567
545
  };
568
546
  }
569
547
  }
@@ -574,24 +552,42 @@ const createYupSchema = (attributes = {}, components = {}) => {
574
552
  yup__namespace.array().of(
575
553
  yup__namespace.lazy(
576
554
  (data) => {
577
- const { attributes: attributes3 } = components[data.__component];
578
- return yup__namespace.object().shape({
555
+ const attributes3 = components?.[data?.__component]?.attributes;
556
+ const validation = yup__namespace.object().shape({
579
557
  __component: yup__namespace.string().required().oneOf(Object.keys(components))
580
- }).nullable(false).concat(createModelSchema(attributes3));
558
+ }).nullable(false);
559
+ if (!attributes3) {
560
+ return validation;
561
+ }
562
+ return validation.concat(createModelSchema(attributes3));
581
563
  }
582
564
  )
583
565
  )
584
- )
566
+ ).test(arrayValidator(attribute, options))
585
567
  };
586
568
  case "relation":
587
569
  return {
588
570
  ...acc,
589
571
  [name]: transformSchema(
590
- yup__namespace.array().of(
591
- yup__namespace.object().shape({
592
- id: yup__namespace.string().required()
593
- })
594
- )
572
+ yup__namespace.lazy((value) => {
573
+ if (!value) {
574
+ return yup__namespace.mixed().nullable(true);
575
+ } else if (Array.isArray(value)) {
576
+ return yup__namespace.array().of(
577
+ yup__namespace.object().shape({
578
+ id: yup__namespace.number().required()
579
+ })
580
+ );
581
+ } else if (typeof value === "object") {
582
+ return yup__namespace.object();
583
+ } else {
584
+ return yup__namespace.mixed().test(
585
+ "type-error",
586
+ "Relation values must be either null, an array of objects with {id} or an object.",
587
+ () => false
588
+ );
589
+ }
590
+ })
595
591
  )
596
592
  };
597
593
  default:
@@ -631,6 +627,14 @@ const createAttributeSchema = (attribute) => {
631
627
  if (!value || typeof value === "string" && value.length === 0) {
632
628
  return true;
633
629
  }
630
+ if (typeof value === "object") {
631
+ try {
632
+ JSON.stringify(value);
633
+ return true;
634
+ } catch (err) {
635
+ return false;
636
+ }
637
+ }
634
638
  try {
635
639
  JSON.parse(value);
636
640
  return true;
@@ -649,16 +653,30 @@ const createAttributeSchema = (attribute) => {
649
653
  return yup__namespace.mixed();
650
654
  }
651
655
  };
652
- const addRequiredValidation = (attribute) => (schema) => {
653
- if (attribute.required) {
654
- return schema.required({
655
- id: strapiAdmin.translatedErrors.required.id,
656
- defaultMessage: "This field is required."
657
- });
656
+ const nullableSchema = (schema) => {
657
+ return schema?.nullable ? schema.nullable() : (
658
+ // In some cases '.nullable' will not be available on the schema.
659
+ // e.g. when the schema has been built using yup.lazy (e.g. for relations).
660
+ // In these cases we should just return the schema as it is.
661
+ schema
662
+ );
663
+ };
664
+ const addNullableValidation = () => (schema) => {
665
+ return nullableSchema(schema);
666
+ };
667
+ const addRequiredValidation = (attribute, options) => (schema) => {
668
+ if (options.status === "draft" || !attribute.required) {
669
+ return schema;
658
670
  }
659
- return schema.nullable();
671
+ if (attribute.required && "required" in schema) {
672
+ return schema.required(strapiAdmin.translatedErrors.required);
673
+ }
674
+ return schema;
660
675
  };
661
- const addMinLengthValidation = (attribute) => (schema) => {
676
+ const addMinLengthValidation = (attribute, options) => (schema) => {
677
+ if (options.status === "draft") {
678
+ return schema;
679
+ }
662
680
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
663
681
  return schema.min(attribute.minLength, {
664
682
  ...strapiAdmin.translatedErrors.minLength,
@@ -680,10 +698,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
680
698
  }
681
699
  return schema;
682
700
  };
683
- const addMinValidation = (attribute) => (schema) => {
684
- if ("min" in attribute) {
701
+ const addMinValidation = (attribute, options) => (schema) => {
702
+ if (options.status === "draft") {
703
+ return schema;
704
+ }
705
+ if ("min" in attribute && "min" in schema) {
685
706
  const min = toInteger(attribute.min);
686
- if ("min" in schema && min) {
707
+ if (min) {
687
708
  return schema.min(min, {
688
709
  ...strapiAdmin.translatedErrors.min,
689
710
  values: {
@@ -728,24 +749,6 @@ const addRegexValidation = (attribute) => (schema) => {
728
749
  }
729
750
  return schema;
730
751
  };
731
- const extractValuesFromYupError = (errorType, errorParams) => {
732
- if (!errorType || !errorParams) {
733
- return {};
734
- }
735
- return {
736
- [errorType]: errorParams[errorType]
737
- };
738
- };
739
- const getInnerErrors = (error) => (error?.inner || []).reduce((acc, currentError) => {
740
- if (currentError.path) {
741
- acc[currentError.path.split("[").join(".").split("]").join("")] = {
742
- id: currentError.message,
743
- defaultMessage: currentError.message,
744
- values: extractValuesFromYupError(currentError?.type, currentError?.params)
745
- };
746
- }
747
- return acc;
748
- }, {});
749
752
  const initApi = contentManagerApi.injectEndpoints({
750
753
  endpoints: (builder) => ({
751
754
  getInitialData: builder.query({
@@ -759,27 +762,20 @@ const { useGetInitialDataQuery } = initApi;
759
762
  const useContentTypeSchema = (model) => {
760
763
  const { toggleNotification } = strapiAdmin.useNotification();
761
764
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
762
- const { components, contentType, contentTypes, error, isLoading, isFetching } = useGetInitialDataQuery(void 0, {
763
- selectFromResult: (res) => {
764
- const contentType2 = res.data?.contentTypes.find((ct) => ct.uid === model);
765
- const componentsByKey = res.data?.components.reduce(
766
- (acc, component) => {
767
- acc[component.uid] = component;
768
- return acc;
769
- },
770
- {}
771
- );
772
- const components2 = extractContentTypeComponents(contentType2?.attributes, componentsByKey);
773
- return {
774
- isLoading: res.isLoading,
775
- isFetching: res.isFetching,
776
- error: res.error,
777
- components: Object.keys(components2).length === 0 ? void 0 : components2,
778
- contentType: contentType2,
779
- contentTypes: res.data?.contentTypes ?? []
780
- };
781
- }
782
- });
765
+ const { data, error, isLoading, isFetching } = useGetInitialDataQuery(void 0);
766
+ const { components, contentType, contentTypes } = React__namespace.useMemo(() => {
767
+ const contentType2 = data?.contentTypes.find((ct) => ct.uid === model);
768
+ const componentsByKey = data?.components.reduce((acc, component) => {
769
+ acc[component.uid] = component;
770
+ return acc;
771
+ }, {});
772
+ const components2 = extractContentTypeComponents(contentType2?.attributes, componentsByKey);
773
+ return {
774
+ components: Object.keys(components2).length === 0 ? void 0 : components2,
775
+ contentType: contentType2,
776
+ contentTypes: data?.contentTypes ?? []
777
+ };
778
+ }, [model, data]);
783
779
  React__namespace.useEffect(() => {
784
780
  if (error) {
785
781
  toggleNotification({
@@ -826,16 +822,328 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
826
822
  }, {});
827
823
  return componentsByKey;
828
824
  };
829
- const useDocument = (args, opts) => {
825
+ const HOOKS = {
826
+ /**
827
+ * Hook that allows to mutate the displayed headers of the list view table
828
+ * @constant
829
+ * @type {string}
830
+ */
831
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
832
+ /**
833
+ * Hook that allows to mutate the CM's collection types links pre-set filters
834
+ * @constant
835
+ * @type {string}
836
+ */
837
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
838
+ /**
839
+ * Hook that allows to mutate the CM's edit view layout
840
+ * @constant
841
+ * @type {string}
842
+ */
843
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
844
+ /**
845
+ * Hook that allows to mutate the CM's single types links pre-set filters
846
+ * @constant
847
+ * @type {string}
848
+ */
849
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
850
+ };
851
+ const contentTypesApi = contentManagerApi.injectEndpoints({
852
+ endpoints: (builder) => ({
853
+ getContentTypeConfiguration: builder.query({
854
+ query: (uid) => ({
855
+ url: `/content-manager/content-types/${uid}/configuration`,
856
+ method: "GET"
857
+ }),
858
+ transformResponse: (response) => response.data,
859
+ providesTags: (_result, _error, uid) => [
860
+ { type: "ContentTypesConfiguration", id: uid },
861
+ { type: "ContentTypeSettings", id: "LIST" }
862
+ ]
863
+ }),
864
+ getAllContentTypeSettings: builder.query({
865
+ query: () => "/content-manager/content-types-settings",
866
+ transformResponse: (response) => response.data,
867
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
868
+ }),
869
+ updateContentTypeConfiguration: builder.mutation({
870
+ query: ({ uid, ...body }) => ({
871
+ url: `/content-manager/content-types/${uid}/configuration`,
872
+ method: "PUT",
873
+ data: body
874
+ }),
875
+ transformResponse: (response) => response.data,
876
+ invalidatesTags: (_result, _error, { uid }) => [
877
+ { type: "ContentTypesConfiguration", id: uid },
878
+ { type: "ContentTypeSettings", id: "LIST" },
879
+ // Is this necessary?
880
+ { type: "InitialData" }
881
+ ]
882
+ })
883
+ })
884
+ });
885
+ const {
886
+ useGetContentTypeConfigurationQuery,
887
+ useGetAllContentTypeSettingsQuery,
888
+ useUpdateContentTypeConfigurationMutation
889
+ } = contentTypesApi;
890
+ const checkIfAttributeIsDisplayable = (attribute) => {
891
+ const { type } = attribute;
892
+ if (type === "relation") {
893
+ return !attribute.relation.toLowerCase().includes("morph");
894
+ }
895
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
896
+ };
897
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
898
+ if (!mainFieldName) {
899
+ return void 0;
900
+ }
901
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
902
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
903
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
904
+ );
905
+ return {
906
+ name: mainFieldName,
907
+ type: mainFieldType ?? "string"
908
+ };
909
+ };
910
+ const DEFAULT_SETTINGS = {
911
+ bulkable: false,
912
+ filterable: false,
913
+ searchable: false,
914
+ pagination: false,
915
+ defaultSortBy: "",
916
+ defaultSortOrder: "asc",
917
+ mainField: "id",
918
+ pageSize: 10
919
+ };
920
+ const useDocumentLayout = (model) => {
921
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
922
+ const [{ query }] = strapiAdmin.useQueryParams();
923
+ const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
830
924
  const { toggleNotification } = strapiAdmin.useNotification();
831
925
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
926
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
832
927
  const {
833
- currentData: data,
834
- isLoading: isLoadingDocument,
835
- isFetching: isFetchingDocument,
928
+ data,
929
+ isLoading: isLoadingConfigs,
930
+ error,
931
+ isFetching: isFetchingConfigs
932
+ } = useGetContentTypeConfigurationQuery(model);
933
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
934
+ React__namespace.useEffect(() => {
935
+ if (error) {
936
+ toggleNotification({
937
+ type: "danger",
938
+ message: formatAPIError(error)
939
+ });
940
+ }
941
+ }, [error, formatAPIError, toggleNotification]);
942
+ const editLayout = React__namespace.useMemo(
943
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
944
+ layout: [],
945
+ components: {},
946
+ metadatas: {},
947
+ options: {},
948
+ settings: DEFAULT_SETTINGS
949
+ },
950
+ [data, isLoading, schemas, schema, components]
951
+ );
952
+ const listLayout = React__namespace.useMemo(() => {
953
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
954
+ layout: [],
955
+ metadatas: {},
956
+ options: {},
957
+ settings: DEFAULT_SETTINGS
958
+ };
959
+ }, [data, isLoading, schemas, schema, components]);
960
+ const { layout: edit } = React__namespace.useMemo(
961
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
962
+ layout: editLayout,
963
+ query
964
+ }),
965
+ [editLayout, query, runHookWaterfall]
966
+ );
967
+ return {
968
+ error,
969
+ isLoading,
970
+ edit,
971
+ list: listLayout
972
+ };
973
+ };
974
+ const useDocLayout = () => {
975
+ const { model } = useDoc();
976
+ return useDocumentLayout(model);
977
+ };
978
+ const formatEditLayout = (data, {
979
+ schemas,
980
+ schema,
981
+ components
982
+ }) => {
983
+ let currentPanelIndex = 0;
984
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
985
+ data.contentType.layouts.edit,
986
+ schema?.attributes,
987
+ data.contentType.metadatas,
988
+ { configurations: data.components, schemas: components },
989
+ schemas
990
+ ).reduce((panels, row) => {
991
+ if (row.some((field) => field.type === "dynamiczone")) {
992
+ panels.push([row]);
993
+ currentPanelIndex += 2;
994
+ } else {
995
+ if (!panels[currentPanelIndex]) {
996
+ panels.push([row]);
997
+ } else {
998
+ panels[currentPanelIndex].push(row);
999
+ }
1000
+ }
1001
+ return panels;
1002
+ }, []);
1003
+ const componentEditAttributes = Object.entries(data.components).reduce(
1004
+ (acc, [uid, configuration]) => {
1005
+ acc[uid] = {
1006
+ layout: convertEditLayoutToFieldLayouts(
1007
+ configuration.layouts.edit,
1008
+ components[uid].attributes,
1009
+ configuration.metadatas,
1010
+ { configurations: data.components, schemas: components }
1011
+ ),
1012
+ settings: {
1013
+ ...configuration.settings,
1014
+ icon: components[uid].info.icon,
1015
+ displayName: components[uid].info.displayName
1016
+ }
1017
+ };
1018
+ return acc;
1019
+ },
1020
+ {}
1021
+ );
1022
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1023
+ (acc, [attribute, metadata]) => {
1024
+ return {
1025
+ ...acc,
1026
+ [attribute]: metadata.edit
1027
+ };
1028
+ },
1029
+ {}
1030
+ );
1031
+ return {
1032
+ layout: panelledEditAttributes,
1033
+ components: componentEditAttributes,
1034
+ metadatas: editMetadatas,
1035
+ settings: {
1036
+ ...data.contentType.settings,
1037
+ displayName: schema?.info.displayName
1038
+ },
1039
+ options: {
1040
+ ...schema?.options,
1041
+ ...schema?.pluginOptions,
1042
+ ...data.contentType.options
1043
+ }
1044
+ };
1045
+ };
1046
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1047
+ return rows.map(
1048
+ (row) => row.map((field) => {
1049
+ const attribute = attributes[field.name];
1050
+ if (!attribute) {
1051
+ return null;
1052
+ }
1053
+ const { edit: metadata } = metadatas[field.name];
1054
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1055
+ return {
1056
+ attribute,
1057
+ disabled: !metadata.editable,
1058
+ hint: metadata.description,
1059
+ label: metadata.label ?? "",
1060
+ name: field.name,
1061
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1062
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1063
+ schemas,
1064
+ components: components?.schemas ?? {}
1065
+ }),
1066
+ placeholder: metadata.placeholder ?? "",
1067
+ required: attribute.required ?? false,
1068
+ size: field.size,
1069
+ unique: "unique" in attribute ? attribute.unique : false,
1070
+ visible: metadata.visible ?? true,
1071
+ type: attribute.type
1072
+ };
1073
+ }).filter((field) => field !== null)
1074
+ );
1075
+ };
1076
+ const formatListLayout = (data, {
1077
+ schemas,
1078
+ schema,
1079
+ components
1080
+ }) => {
1081
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1082
+ (acc, [attribute, metadata]) => {
1083
+ return {
1084
+ ...acc,
1085
+ [attribute]: metadata.list
1086
+ };
1087
+ },
1088
+ {}
1089
+ );
1090
+ const listAttributes = convertListLayoutToFieldLayouts(
1091
+ data.contentType.layouts.list,
1092
+ schema?.attributes,
1093
+ listMetadatas,
1094
+ { configurations: data.components, schemas: components },
1095
+ schemas
1096
+ );
1097
+ return {
1098
+ layout: listAttributes,
1099
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1100
+ metadatas: listMetadatas,
1101
+ options: {
1102
+ ...schema?.options,
1103
+ ...schema?.pluginOptions,
1104
+ ...data.contentType.options
1105
+ }
1106
+ };
1107
+ };
1108
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1109
+ return columns.map((name) => {
1110
+ const attribute = attributes[name];
1111
+ if (!attribute) {
1112
+ return null;
1113
+ }
1114
+ const metadata = metadatas[name];
1115
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1116
+ return {
1117
+ attribute,
1118
+ label: metadata.label ?? "",
1119
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1120
+ schemas,
1121
+ components: components?.schemas ?? {}
1122
+ }),
1123
+ name,
1124
+ searchable: metadata.searchable ?? true,
1125
+ sortable: metadata.sortable ?? true
1126
+ };
1127
+ }).filter((field) => field !== null);
1128
+ };
1129
+ const useDocument = (args, opts) => {
1130
+ const { toggleNotification } = strapiAdmin.useNotification();
1131
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1132
+ const {
1133
+ currentData: data,
1134
+ isLoading: isLoadingDocument,
1135
+ isFetching: isFetchingDocument,
836
1136
  error
837
- } = useGetDocumentQuery(args, opts);
838
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
1137
+ } = useGetDocumentQuery(args, {
1138
+ ...opts,
1139
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1140
+ });
1141
+ const {
1142
+ components,
1143
+ schema,
1144
+ schemas,
1145
+ isLoading: isLoadingSchema
1146
+ } = useContentTypeSchema(args.model);
839
1147
  React__namespace.useEffect(() => {
840
1148
  if (error) {
841
1149
  toggleNotification({
@@ -862,7 +1170,7 @@ const useDocument = (args, opts) => {
862
1170
  return null;
863
1171
  } catch (error2) {
864
1172
  if (error2 instanceof yup.ValidationError) {
865
- return getInnerErrors(error2);
1173
+ return strapiAdmin.getYupValidationErrors(error2);
866
1174
  }
867
1175
  throw error2;
868
1176
  }
@@ -870,12 +1178,15 @@ const useDocument = (args, opts) => {
870
1178
  [validationSchema]
871
1179
  );
872
1180
  const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1181
+ const hasError = !!error;
873
1182
  return {
874
1183
  components,
875
1184
  document: data?.data,
876
1185
  meta: data?.meta,
877
1186
  isLoading,
1187
+ hasError,
878
1188
  schema,
1189
+ schemas,
879
1190
  validate
880
1191
  };
881
1192
  };
@@ -889,22 +1200,60 @@ const useDoc = () => {
889
1200
  if (!slug) {
890
1201
  throw new Error("Could not find model in url params");
891
1202
  }
1203
+ const document = useDocument(
1204
+ { documentId: origin || id, model: slug, collectionType, params },
1205
+ {
1206
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1207
+ }
1208
+ );
1209
+ const returnId = origin || id === "create" ? void 0 : id;
892
1210
  return {
893
1211
  collectionType,
894
1212
  model: slug,
895
- id: origin || id === "create" ? void 0 : id,
896
- ...useDocument(
897
- { documentId: origin || id, model: slug, collectionType, params },
898
- {
899
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
900
- }
901
- )
1213
+ id: returnId,
1214
+ ...document
1215
+ };
1216
+ };
1217
+ const useContentManagerContext = () => {
1218
+ const {
1219
+ collectionType,
1220
+ model,
1221
+ id,
1222
+ components,
1223
+ isLoading: isLoadingDoc,
1224
+ schema,
1225
+ schemas
1226
+ } = useDoc();
1227
+ const layout = useDocumentLayout(model);
1228
+ const form = strapiAdmin.useForm("useContentManagerContext", (state) => state);
1229
+ const isSingleType = collectionType === SINGLE_TYPES;
1230
+ const slug = model;
1231
+ const isCreatingEntry = id === "create";
1232
+ useContentTypeSchema();
1233
+ const isLoading = isLoadingDoc || layout.isLoading;
1234
+ const error = layout.error;
1235
+ return {
1236
+ error,
1237
+ isLoading,
1238
+ // Base metadata
1239
+ model,
1240
+ collectionType,
1241
+ id,
1242
+ slug,
1243
+ isCreatingEntry,
1244
+ isSingleType,
1245
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1246
+ // All schema infos
1247
+ components,
1248
+ contentType: schema,
1249
+ contentTypes: schemas,
1250
+ // Form state
1251
+ form,
1252
+ // layout infos
1253
+ layout
902
1254
  };
903
1255
  };
904
1256
  const prefixPluginTranslations = (trad, pluginId) => {
905
- if (!pluginId) {
906
- throw new TypeError("pluginId can't be empty");
907
- }
908
1257
  return Object.keys(trad).reduce((acc, current) => {
909
1258
  acc[`${pluginId}.${current}`] = trad[current];
910
1259
  return acc;
@@ -920,6 +1269,8 @@ const useDocumentActions = () => {
920
1269
  const { formatMessage } = reactIntl.useIntl();
921
1270
  const { trackUsage } = strapiAdmin.useTracking();
922
1271
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1272
+ const navigate = reactRouterDom.useNavigate();
1273
+ const setCurrentStep = strapiAdmin.useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
923
1274
  const [deleteDocument] = useDeleteDocumentMutation();
924
1275
  const _delete = React__namespace.useCallback(
925
1276
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -958,14 +1309,15 @@ const useDocumentActions = () => {
958
1309
  },
959
1310
  [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
960
1311
  );
961
- const [discardDocument] = useDiscardDocumentMutation();
962
- const discard = React__namespace.useCallback(
963
- async ({ collectionType, model, documentId }) => {
1312
+ const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
1313
+ const deleteMany = React__namespace.useCallback(
1314
+ async ({ model, documentIds, params }) => {
964
1315
  try {
965
- const res = await discardDocument({
966
- collectionType,
1316
+ trackUsage("willBulkDeleteEntries");
1317
+ const res = await deleteManyDocuments({
967
1318
  model,
968
- documentId
1319
+ documentIds,
1320
+ params
969
1321
  });
970
1322
  if ("error" in res) {
971
1323
  toggleNotification({
@@ -976,39 +1328,77 @@ const useDocumentActions = () => {
976
1328
  }
977
1329
  toggleNotification({
978
1330
  type: "success",
979
- message: formatMessage({
980
- id: "content-manager.success.record.discard",
981
- defaultMessage: "Changes discarded"
982
- })
1331
+ title: formatMessage({
1332
+ id: getTranslation("success.records.delete"),
1333
+ defaultMessage: "Successfully deleted."
1334
+ }),
1335
+ message: ""
983
1336
  });
1337
+ trackUsage("didBulkDeleteEntries");
984
1338
  return res.data;
985
1339
  } catch (err) {
986
1340
  toggleNotification({
987
1341
  type: "danger",
988
1342
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
989
1343
  });
1344
+ trackUsage("didNotBulkDeleteEntries");
990
1345
  throw err;
991
1346
  }
992
1347
  },
993
- [discardDocument, formatAPIError, formatMessage, toggleNotification]
1348
+ [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
994
1349
  );
995
- const [publishDocument] = usePublishDocumentMutation();
996
- const publish = React__namespace.useCallback(
997
- async ({ collectionType, model, documentId, params }, data) => {
1350
+ const [discardDocument] = useDiscardDocumentMutation();
1351
+ const discard = React__namespace.useCallback(
1352
+ async ({ collectionType, model, documentId, params }) => {
998
1353
  try {
999
- trackUsage("willPublishEntry");
1000
- const res = await publishDocument({
1354
+ const res = await discardDocument({
1001
1355
  collectionType,
1002
1356
  model,
1003
1357
  documentId,
1004
- data,
1005
1358
  params
1006
1359
  });
1007
1360
  if ("error" in res) {
1008
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1361
+ toggleNotification({
1362
+ type: "danger",
1363
+ message: formatAPIError(res.error)
1364
+ });
1009
1365
  return { error: res.error };
1010
1366
  }
1011
- trackUsage("didPublishEntry");
1367
+ toggleNotification({
1368
+ type: "success",
1369
+ message: formatMessage({
1370
+ id: "content-manager.success.record.discard",
1371
+ defaultMessage: "Changes discarded"
1372
+ })
1373
+ });
1374
+ return res.data;
1375
+ } catch (err) {
1376
+ toggleNotification({
1377
+ type: "danger",
1378
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1379
+ });
1380
+ throw err;
1381
+ }
1382
+ },
1383
+ [discardDocument, formatAPIError, formatMessage, toggleNotification]
1384
+ );
1385
+ const [publishDocument] = usePublishDocumentMutation();
1386
+ const publish = React__namespace.useCallback(
1387
+ async ({ collectionType, model, documentId, params }, data) => {
1388
+ try {
1389
+ trackUsage("willPublishEntry");
1390
+ const res = await publishDocument({
1391
+ collectionType,
1392
+ model,
1393
+ documentId,
1394
+ data,
1395
+ params
1396
+ });
1397
+ if ("error" in res) {
1398
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1399
+ return { error: res.error };
1400
+ }
1401
+ trackUsage("didPublishEntry");
1012
1402
  toggleNotification({
1013
1403
  type: "success",
1014
1404
  message: formatMessage({
@@ -1027,6 +1417,43 @@ const useDocumentActions = () => {
1027
1417
  },
1028
1418
  [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1029
1419
  );
1420
+ const [publishManyDocuments] = usePublishManyDocumentsMutation();
1421
+ const publishMany = React__namespace.useCallback(
1422
+ async ({ model, documentIds, params }) => {
1423
+ try {
1424
+ const res = await publishManyDocuments({
1425
+ model,
1426
+ documentIds,
1427
+ params
1428
+ });
1429
+ if ("error" in res) {
1430
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1431
+ return { error: res.error };
1432
+ }
1433
+ toggleNotification({
1434
+ type: "success",
1435
+ message: formatMessage({
1436
+ id: getTranslation("success.record.publish"),
1437
+ defaultMessage: "Published document"
1438
+ })
1439
+ });
1440
+ return res.data;
1441
+ } catch (err) {
1442
+ toggleNotification({
1443
+ type: "danger",
1444
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1445
+ });
1446
+ throw err;
1447
+ }
1448
+ },
1449
+ [
1450
+ // trackUsage,
1451
+ publishManyDocuments,
1452
+ toggleNotification,
1453
+ formatMessage,
1454
+ formatAPIError
1455
+ ]
1456
+ );
1030
1457
  const [updateDocument] = useUpdateDocumentMutation();
1031
1458
  const update = React__namespace.useCallback(
1032
1459
  async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
@@ -1101,6 +1528,41 @@ const useDocumentActions = () => {
1101
1528
  },
1102
1529
  [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1103
1530
  );
1531
+ const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1532
+ const unpublishMany = React__namespace.useCallback(
1533
+ async ({ model, documentIds, params }) => {
1534
+ try {
1535
+ trackUsage("willBulkUnpublishEntries");
1536
+ const res = await unpublishManyDocuments({
1537
+ model,
1538
+ documentIds,
1539
+ params
1540
+ });
1541
+ if ("error" in res) {
1542
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1543
+ return { error: res.error };
1544
+ }
1545
+ trackUsage("didBulkUnpublishEntries");
1546
+ toggleNotification({
1547
+ type: "success",
1548
+ title: formatMessage({
1549
+ id: getTranslation("success.records.unpublish"),
1550
+ defaultMessage: "Successfully unpublished."
1551
+ }),
1552
+ message: ""
1553
+ });
1554
+ return res.data;
1555
+ } catch (err) {
1556
+ toggleNotification({
1557
+ type: "danger",
1558
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1559
+ });
1560
+ trackUsage("didNotBulkUnpublishEntries");
1561
+ throw err;
1562
+ }
1563
+ },
1564
+ [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1565
+ );
1104
1566
  const [createDocument] = useCreateDocumentMutation();
1105
1567
  const create = React__namespace.useCallback(
1106
1568
  async ({ model, params }, data, trackerProperty) => {
@@ -1123,6 +1585,7 @@ const useDocumentActions = () => {
1123
1585
  defaultMessage: "Saved document"
1124
1586
  })
1125
1587
  });
1588
+ setCurrentStep("contentManager.success");
1126
1589
  return res.data;
1127
1590
  } catch (err) {
1128
1591
  toggleNotification({
@@ -1144,7 +1607,6 @@ const useDocumentActions = () => {
1144
1607
  sourceId
1145
1608
  });
1146
1609
  if ("error" in res) {
1147
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1148
1610
  return { error: res.error };
1149
1611
  }
1150
1612
  toggleNotification({
@@ -1163,7 +1625,7 @@ const useDocumentActions = () => {
1163
1625
  throw err;
1164
1626
  }
1165
1627
  },
1166
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1628
+ [autoCloneDocument, formatMessage, toggleNotification]
1167
1629
  );
1168
1630
  const [cloneDocument] = useCloneDocumentMutation();
1169
1631
  const clone = React__namespace.useCallback(
@@ -1189,6 +1651,7 @@ const useDocumentActions = () => {
1189
1651
  defaultMessage: "Cloned document"
1190
1652
  })
1191
1653
  });
1654
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1192
1655
  return res.data;
1193
1656
  } catch (err) {
1194
1657
  toggleNotification({
@@ -1199,7 +1662,7 @@ const useDocumentActions = () => {
1199
1662
  throw err;
1200
1663
  }
1201
1664
  },
1202
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1665
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1203
1666
  );
1204
1667
  const [getDoc] = useLazyGetDocumentQuery();
1205
1668
  const getDocument = React__namespace.useCallback(
@@ -1214,17 +1677,20 @@ const useDocumentActions = () => {
1214
1677
  clone,
1215
1678
  create,
1216
1679
  delete: _delete,
1680
+ deleteMany,
1217
1681
  discard,
1218
1682
  getDocument,
1219
1683
  publish,
1684
+ publishMany,
1220
1685
  unpublish,
1686
+ unpublishMany,
1221
1687
  update
1222
1688
  };
1223
1689
  };
1224
- const ProtectedHistoryPage = React.lazy(
1225
- () => Promise.resolve().then(() => require("./History-DeAPlvtv.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1690
+ const ProtectedHistoryPage = React__namespace.lazy(
1691
+ () => Promise.resolve().then(() => require("./History-BMndx49M.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1226
1692
  );
1227
- const routes$1 = [
1693
+ const routes$2 = [
1228
1694
  {
1229
1695
  path: ":collectionType/:slug/:id/history",
1230
1696
  Component: ProtectedHistoryPage
@@ -1234,32 +1700,45 @@ const routes$1 = [
1234
1700
  Component: ProtectedHistoryPage
1235
1701
  }
1236
1702
  ];
1703
+ const ProtectedPreviewPage = React__namespace.lazy(
1704
+ () => Promise.resolve().then(() => require("./Preview-B7PR3Ok_.js")).then((mod) => ({ default: mod.ProtectedPreviewPage }))
1705
+ );
1706
+ const routes$1 = [
1707
+ {
1708
+ path: ":collectionType/:slug/:id/preview",
1709
+ Component: ProtectedPreviewPage
1710
+ },
1711
+ {
1712
+ path: ":collectionType/:slug/preview",
1713
+ Component: ProtectedPreviewPage
1714
+ }
1715
+ ];
1237
1716
  const ProtectedEditViewPage = React.lazy(
1238
- () => Promise.resolve().then(() => require("./EditViewPage-DvNpQkam.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1717
+ () => Promise.resolve().then(() => require("./EditViewPage-DT6A4ayX.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1239
1718
  );
1240
1719
  const ProtectedListViewPage = React.lazy(
1241
- () => Promise.resolve().then(() => require("./ListViewPage-5ySZ-VUs.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1720
+ () => Promise.resolve().then(() => require("./ListViewPage-BPVmh9pq.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1242
1721
  );
1243
1722
  const ProtectedListConfiguration = React.lazy(
1244
- () => Promise.resolve().then(() => require("./ListConfigurationPage-DPCwW5Vr.js")).then((mod) => ({
1723
+ () => Promise.resolve().then(() => require("./ListConfigurationPage-DouY1EWM.js")).then((mod) => ({
1245
1724
  default: mod.ProtectedListConfiguration
1246
1725
  }))
1247
1726
  );
1248
1727
  const ProtectedEditConfigurationPage = React.lazy(
1249
- () => Promise.resolve().then(() => require("./EditConfigurationPage-Dmv83RlS.js")).then((mod) => ({
1728
+ () => Promise.resolve().then(() => require("./EditConfigurationPage-uy-v43AR.js")).then((mod) => ({
1250
1729
  default: mod.ProtectedEditConfigurationPage
1251
1730
  }))
1252
1731
  );
1253
1732
  const ProtectedComponentConfigurationPage = React.lazy(
1254
- () => Promise.resolve().then(() => require("./ComponentConfigurationPage-DjWJdz6Y.js")).then((mod) => ({
1733
+ () => Promise.resolve().then(() => require("./ComponentConfigurationPage-BlzvDpbX.js")).then((mod) => ({
1255
1734
  default: mod.ProtectedComponentConfigurationPage
1256
1735
  }))
1257
1736
  );
1258
1737
  const NoPermissions = React.lazy(
1259
- () => Promise.resolve().then(() => require("./NoPermissionsPage-Dwu8rRJu.js")).then((mod) => ({ default: mod.NoPermissions }))
1738
+ () => Promise.resolve().then(() => require("./NoPermissionsPage-BMFKVcwJ.js")).then((mod) => ({ default: mod.NoPermissions }))
1260
1739
  );
1261
1740
  const NoContentType = React.lazy(
1262
- () => Promise.resolve().then(() => require("./NoContentTypePage-DOC_yWOf.js")).then((mod) => ({ default: mod.NoContentType }))
1741
+ () => Promise.resolve().then(() => require("./NoContentTypePage-BwcL--4H.js")).then((mod) => ({ default: mod.NoContentType }))
1263
1742
  );
1264
1743
  const CollectionTypePages = () => {
1265
1744
  const { collectionType } = reactRouterDom.useParams();
@@ -1271,7 +1750,7 @@ const CollectionTypePages = () => {
1271
1750
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1272
1751
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1273
1752
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1274
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1753
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1275
1754
  const routes = [
1276
1755
  {
1277
1756
  path: LIST_RELATIVE_PATH,
@@ -1305,6 +1784,7 @@ const routes = [
1305
1784
  path: "no-content-types",
1306
1785
  Component: NoContentType
1307
1786
  },
1787
+ ...routes$2,
1308
1788
  ...routes$1
1309
1789
  ];
1310
1790
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1373,12 +1853,14 @@ const DocumentActionButton = (action) => {
1373
1853
  /* @__PURE__ */ jsxRuntime.jsx(
1374
1854
  designSystem.Button,
1375
1855
  {
1376
- flex: 1,
1856
+ flex: "auto",
1377
1857
  startIcon: action.icon,
1378
1858
  disabled: action.disabled,
1379
1859
  onClick: handleClick(action),
1380
1860
  justifyContent: "center",
1381
1861
  variant: action.variant || "default",
1862
+ paddingTop: "7px",
1863
+ paddingBottom: "7px",
1382
1864
  children: action.label
1383
1865
  }
1384
1866
  ),
@@ -1386,7 +1868,7 @@ const DocumentActionButton = (action) => {
1386
1868
  DocumentActionConfirmDialog,
1387
1869
  {
1388
1870
  ...action.dialog,
1389
- variant: action.variant,
1871
+ variant: action.dialog?.variant ?? action.variant,
1390
1872
  isOpen: dialogId === action.id,
1391
1873
  onClose: handleClose
1392
1874
  }
@@ -1401,6 +1883,11 @@ const DocumentActionButton = (action) => {
1401
1883
  ) : null
1402
1884
  ] });
1403
1885
  };
1886
+ const MenuItem = styledComponents.styled(designSystem.Menu.Item)`
1887
+ &:hover {
1888
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
1889
+ }
1890
+ `;
1404
1891
  const DocumentActionsMenu = ({
1405
1892
  actions: actions2,
1406
1893
  children,
@@ -1443,49 +1930,48 @@ const DocumentActionsMenu = ({
1443
1930
  disabled: isDisabled,
1444
1931
  size: "S",
1445
1932
  endIcon: null,
1446
- paddingTop: "7px",
1447
- paddingLeft: "9px",
1448
- paddingRight: "9px",
1933
+ paddingTop: "4px",
1934
+ paddingLeft: "7px",
1935
+ paddingRight: "7px",
1449
1936
  variant,
1450
1937
  children: [
1451
1938
  /* @__PURE__ */ jsxRuntime.jsx(Icons.More, { "aria-hidden": true, focusable: false }),
1452
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { as: "span", children: label || formatMessage({
1939
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { tag: "span", children: label || formatMessage({
1453
1940
  id: "content-manager.containers.edit.panels.default.more-actions",
1454
1941
  defaultMessage: "More document actions"
1455
1942
  }) })
1456
1943
  ]
1457
1944
  }
1458
1945
  ),
1459
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1946
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1460
1947
  actions2.map((action) => {
1461
1948
  return /* @__PURE__ */ jsxRuntime.jsx(
1462
- designSystem.Menu.Item,
1949
+ MenuItem,
1463
1950
  {
1464
1951
  disabled: action.disabled,
1465
1952
  onSelect: handleClick(action),
1466
1953
  display: "block",
1467
- children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", gap: 4, children: [
1468
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { color: convertActionVariantToColor(action.variant), gap: 2, as: "span", children: [
1469
- action.icon,
1470
- action.label
1471
- ] }),
1472
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsxRuntime.jsx(
1473
- designSystem.Flex,
1474
- {
1475
- alignItems: "center",
1476
- background: "alternative100",
1477
- borderStyle: "solid",
1478
- borderColor: "alternative200",
1479
- borderWidth: "1px",
1480
- height: 5,
1481
- paddingLeft: 2,
1482
- paddingRight: 2,
1483
- hasRadius: true,
1484
- color: "alternative600",
1485
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1486
- }
1487
- )
1488
- ] })
1954
+ isVariantDanger: action.variant === "danger",
1955
+ isDisabled: action.disabled,
1956
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(
1957
+ designSystem.Flex,
1958
+ {
1959
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1960
+ gap: 2,
1961
+ tag: "span",
1962
+ children: [
1963
+ /* @__PURE__ */ jsxRuntime.jsx(
1964
+ designSystem.Flex,
1965
+ {
1966
+ tag: "span",
1967
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1968
+ children: action.icon
1969
+ }
1970
+ ),
1971
+ action.label
1972
+ ]
1973
+ }
1974
+ ) })
1489
1975
  },
1490
1976
  action.id
1491
1977
  );
@@ -1527,6 +2013,18 @@ const convertActionVariantToColor = (variant = "secondary") => {
1527
2013
  return "primary600";
1528
2014
  }
1529
2015
  };
2016
+ const convertActionVariantToIconColor = (variant = "secondary") => {
2017
+ switch (variant) {
2018
+ case "danger":
2019
+ return "danger600";
2020
+ case "secondary":
2021
+ return "neutral500";
2022
+ case "success":
2023
+ return "success600";
2024
+ default:
2025
+ return "primary600";
2026
+ }
2027
+ };
1530
2028
  const DocumentActionConfirmDialog = ({
1531
2029
  onClose,
1532
2030
  onCancel,
@@ -1549,61 +2047,54 @@ const DocumentActionConfirmDialog = ({
1549
2047
  }
1550
2048
  onClose();
1551
2049
  };
1552
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog, { isOpen, title, onClose: handleClose, children: [
1553
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.DialogBody, { children: content }),
1554
- /* @__PURE__ */ jsxRuntime.jsx(
1555
- designSystem.DialogFooter,
1556
- {
1557
- startAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleClose, variant: "tertiary", children: formatMessage({
1558
- id: "app.components.Button.cancel",
1559
- defaultMessage: "Cancel"
1560
- }) }),
1561
- endAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, children: formatMessage({
1562
- id: "app.components.Button.confirm",
1563
- defaultMessage: "Confirm"
1564
- }) })
1565
- }
1566
- )
1567
- ] });
2050
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2051
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2052
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: content }),
2053
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
2054
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
2055
+ id: "app.components.Button.cancel",
2056
+ defaultMessage: "Cancel"
2057
+ }) }) }),
2058
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
2059
+ id: "app.components.Button.confirm",
2060
+ defaultMessage: "Confirm"
2061
+ }) })
2062
+ ] })
2063
+ ] }) });
1568
2064
  };
1569
2065
  const DocumentActionModal = ({
1570
2066
  isOpen,
1571
2067
  title,
1572
2068
  onClose,
1573
2069
  footer: Footer,
1574
- content,
2070
+ content: Content,
1575
2071
  onModalClose
1576
2072
  }) => {
1577
- const id = React__namespace.useId();
1578
- if (!isOpen) {
1579
- return null;
1580
- }
1581
2073
  const handleClose = () => {
1582
2074
  if (onClose) {
1583
2075
  onClose();
1584
2076
  }
1585
2077
  onModalClose();
1586
2078
  };
1587
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ModalLayout, { borderRadius: "4px", overflow: "hidden", onClose: handleClose, labelledBy: id, children: [
1588
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: "neutral800", as: "h2", id, children: title }) }),
1589
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalBody, { children: content }),
1590
- /* @__PURE__ */ jsxRuntime.jsx(
1591
- designSystem.Box,
1592
- {
1593
- paddingTop: 4,
1594
- paddingBottom: 4,
1595
- paddingLeft: 5,
1596
- paddingRight: 5,
1597
- borderWidth: "1px 0 0 0",
1598
- borderStyle: "solid",
1599
- borderColor: "neutral150",
1600
- background: "neutral100",
1601
- children: typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer
1602
- }
1603
- )
1604
- ] });
2079
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
2080
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: title }) }),
2081
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: Content }),
2082
+ typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer
2083
+ ] }) });
2084
+ };
2085
+ const transformData = (data) => {
2086
+ if (Array.isArray(data)) {
2087
+ return data.map(transformData);
2088
+ }
2089
+ if (typeof data === "object" && data !== null) {
2090
+ if ("apiData" in data) {
2091
+ return data.apiData;
2092
+ }
2093
+ return mapValues__default.default(transformData)(data);
2094
+ }
2095
+ return data;
1605
2096
  };
1606
- const PublishAction = ({
2097
+ const PublishAction$1 = ({
1607
2098
  activeTab,
1608
2099
  documentId,
1609
2100
  model,
@@ -1615,13 +2106,18 @@ const PublishAction = ({
1615
2106
  const navigate = reactRouterDom.useNavigate();
1616
2107
  const { toggleNotification } = strapiAdmin.useNotification();
1617
2108
  const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
2109
+ const isListView = reactRouterDom.useMatch(LIST_PATH) !== null;
1618
2110
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2111
+ const { id } = reactRouterDom.useParams();
1619
2112
  const { formatMessage } = reactIntl.useIntl();
1620
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1621
- "PublishAction",
1622
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1623
- );
2113
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1624
2114
  const { publish } = useDocumentActions();
2115
+ const [
2116
+ countDraftRelations,
2117
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2118
+ ] = useLazyGetDraftRelationCountQuery();
2119
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React__namespace.useState(0);
2120
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React__namespace.useState(0);
1625
2121
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1626
2122
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1627
2123
  const modified = strapiAdmin.useForm("PublishAction", ({ modified: modified2 }) => modified2);
@@ -1630,10 +2126,107 @@ const PublishAction = ({
1630
2126
  const validate = strapiAdmin.useForm("PublishAction", (state) => state.validate);
1631
2127
  const setErrors = strapiAdmin.useForm("PublishAction", (state) => state.setErrors);
1632
2128
  const formValues = strapiAdmin.useForm("PublishAction", ({ values }) => values);
2129
+ React__namespace.useEffect(() => {
2130
+ if (isErrorDraftRelations) {
2131
+ toggleNotification({
2132
+ type: "danger",
2133
+ message: formatMessage({
2134
+ id: getTranslation("error.records.fetch-draft-relatons"),
2135
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2136
+ })
2137
+ });
2138
+ }
2139
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2140
+ React__namespace.useEffect(() => {
2141
+ const localDraftRelations = /* @__PURE__ */ new Set();
2142
+ const extractDraftRelations = (data) => {
2143
+ const relations = data.connect || [];
2144
+ relations.forEach((relation) => {
2145
+ if (relation.status === "draft") {
2146
+ localDraftRelations.add(relation.id);
2147
+ }
2148
+ });
2149
+ };
2150
+ const traverseAndExtract = (data) => {
2151
+ Object.entries(data).forEach(([key, value]) => {
2152
+ if (key === "connect" && Array.isArray(value)) {
2153
+ extractDraftRelations({ connect: value });
2154
+ } else if (typeof value === "object" && value !== null) {
2155
+ traverseAndExtract(value);
2156
+ }
2157
+ });
2158
+ };
2159
+ if (!documentId || modified) {
2160
+ traverseAndExtract(formValues);
2161
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2162
+ }
2163
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2164
+ React__namespace.useEffect(() => {
2165
+ if (!document || !document.documentId || isListView) {
2166
+ return;
2167
+ }
2168
+ const fetchDraftRelationsCount = async () => {
2169
+ const { data, error } = await countDraftRelations({
2170
+ collectionType,
2171
+ model,
2172
+ documentId,
2173
+ params
2174
+ });
2175
+ if (error) {
2176
+ throw error;
2177
+ }
2178
+ if (data) {
2179
+ setServerCountOfDraftRelations(data.data);
2180
+ }
2181
+ };
2182
+ fetchDraftRelationsCount();
2183
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1633
2184
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1634
2185
  if (!schema?.options?.draftAndPublish) {
1635
2186
  return null;
1636
2187
  }
2188
+ const performPublish = async () => {
2189
+ setSubmitting(true);
2190
+ try {
2191
+ const { errors } = await validate(true, {
2192
+ status: "published"
2193
+ });
2194
+ if (errors) {
2195
+ toggleNotification({
2196
+ type: "danger",
2197
+ message: formatMessage({
2198
+ id: "content-manager.validation.error",
2199
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2200
+ })
2201
+ });
2202
+ return;
2203
+ }
2204
+ const res = await publish(
2205
+ {
2206
+ collectionType,
2207
+ model,
2208
+ documentId,
2209
+ params
2210
+ },
2211
+ transformData(formValues)
2212
+ );
2213
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2214
+ if (id === "create") {
2215
+ navigate({
2216
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2217
+ search: rawQuery
2218
+ });
2219
+ }
2220
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2221
+ setErrors(formatValidationErrors(res.error));
2222
+ }
2223
+ } finally {
2224
+ setSubmitting(false);
2225
+ }
2226
+ };
2227
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2228
+ const enableDraftRelationsCount = false;
2229
+ const hasDraftRelations = enableDraftRelationsCount;
1637
2230
  return {
1638
2231
  /**
1639
2232
  * Disabled when:
@@ -1643,52 +2236,40 @@ const PublishAction = ({
1643
2236
  * - the document is already published & not modified
1644
2237
  * - the document is being created & not modified
1645
2238
  * - the user doesn't have the permission to publish
1646
- * - the user doesn't have the permission to create a new document
1647
- * - the user doesn't have the permission to update the document
1648
2239
  */
1649
- disabled: isCloning || isSubmitting || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2240
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1650
2241
  label: formatMessage({
1651
2242
  id: "app.utils.publish",
1652
2243
  defaultMessage: "Publish"
1653
2244
  }),
1654
2245
  onClick: async () => {
1655
- setSubmitting(true);
1656
- try {
1657
- const { errors } = await validate();
1658
- if (errors) {
1659
- toggleNotification({
1660
- type: "danger",
1661
- message: formatMessage({
1662
- id: "content-manager.validation.error",
1663
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1664
- })
1665
- });
1666
- return;
1667
- }
1668
- const res = await publish(
1669
- {
1670
- collectionType,
1671
- model,
1672
- documentId,
1673
- params
1674
- },
1675
- formValues
1676
- );
1677
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1678
- navigate({
1679
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1680
- search: rawQuery
1681
- });
1682
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1683
- setErrors(formatValidationErrors(res.error));
2246
+ await performPublish();
2247
+ },
2248
+ dialog: hasDraftRelations ? {
2249
+ type: "dialog",
2250
+ variant: "danger",
2251
+ footer: null,
2252
+ title: formatMessage({
2253
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2254
+ defaultMessage: "Confirmation"
2255
+ }),
2256
+ content: formatMessage(
2257
+ {
2258
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2259
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2260
+ },
2261
+ {
2262
+ count: totalDraftRelations
1684
2263
  }
1685
- } finally {
1686
- setSubmitting(false);
2264
+ ),
2265
+ onConfirm: async () => {
2266
+ await performPublish();
1687
2267
  }
1688
- }
2268
+ } : void 0
1689
2269
  };
1690
2270
  };
1691
- PublishAction.type = "publish";
2271
+ PublishAction$1.type = "publish";
2272
+ PublishAction$1.position = "panel";
1692
2273
  const UpdateAction = ({
1693
2274
  activeTab,
1694
2275
  documentId,
@@ -1701,10 +2282,6 @@ const UpdateAction = ({
1701
2282
  const cloneMatch = reactRouterDom.useMatch(CLONE_PATH);
1702
2283
  const isCloning = cloneMatch !== null;
1703
2284
  const { formatMessage } = reactIntl.useIntl();
1704
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1705
- canCreate: canCreate2,
1706
- canUpdate: canUpdate2
1707
- }));
1708
2285
  const { create, update, clone } = useDocumentActions();
1709
2286
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1710
2287
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
@@ -1715,95 +2292,139 @@ const UpdateAction = ({
1715
2292
  const validate = strapiAdmin.useForm("UpdateAction", (state) => state.validate);
1716
2293
  const setErrors = strapiAdmin.useForm("UpdateAction", (state) => state.setErrors);
1717
2294
  const resetForm = strapiAdmin.useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
1718
- return {
1719
- /**
1720
- * Disabled when:
1721
- * - the form is submitting
1722
- * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1723
- * - the active tab is the published tab
1724
- * - the user doesn't have the permission to create a new document
1725
- * - the user doesn't have the permission to update the document
1726
- */
1727
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
1728
- label: formatMessage({
1729
- id: "content-manager.containers.Edit.save",
1730
- defaultMessage: "Save"
1731
- }),
1732
- onClick: async () => {
1733
- setSubmitting(true);
1734
- try {
1735
- const { errors } = await validate();
1736
- if (errors) {
1737
- toggleNotification({
1738
- type: "danger",
1739
- message: formatMessage({
1740
- id: "content-manager.validation.error",
1741
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1742
- })
1743
- });
1744
- return;
1745
- }
1746
- if (isCloning) {
1747
- const res = await clone(
2295
+ const handleUpdate = React__namespace.useCallback(async () => {
2296
+ setSubmitting(true);
2297
+ try {
2298
+ if (!modified) {
2299
+ return;
2300
+ }
2301
+ const { errors } = await validate(true, {
2302
+ status: "draft"
2303
+ });
2304
+ if (errors) {
2305
+ toggleNotification({
2306
+ type: "danger",
2307
+ message: formatMessage({
2308
+ id: "content-manager.validation.error",
2309
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2310
+ })
2311
+ });
2312
+ return;
2313
+ }
2314
+ if (isCloning) {
2315
+ const res = await clone(
2316
+ {
2317
+ model,
2318
+ documentId: cloneMatch.params.origin,
2319
+ params
2320
+ },
2321
+ transformData(document)
2322
+ );
2323
+ if ("data" in res) {
2324
+ navigate(
1748
2325
  {
1749
- model,
1750
- documentId: cloneMatch.params.origin,
1751
- params
1752
- },
1753
- document
1754
- );
1755
- if ("data" in res) {
1756
- navigate({
1757
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2326
+ pathname: `../${res.data.documentId}`,
1758
2327
  search: rawQuery
1759
- });
1760
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1761
- setErrors(formatValidationErrors(res.error));
1762
- }
1763
- } else if (documentId || collectionType === SINGLE_TYPES) {
1764
- const res = await update(
1765
- {
1766
- collectionType,
1767
- model,
1768
- documentId,
1769
- params
1770
- },
1771
- document
1772
- );
1773
- if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1774
- setErrors(formatValidationErrors(res.error));
1775
- } else {
1776
- resetForm();
1777
- }
1778
- } else {
1779
- const res = await create(
1780
- {
1781
- model,
1782
- params
1783
2328
  },
1784
- document
2329
+ { relative: "path" }
1785
2330
  );
1786
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1787
- navigate({
1788
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1789
- search: rawQuery
1790
- });
1791
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1792
- setErrors(formatValidationErrors(res.error));
1793
- }
1794
- }
1795
- } finally {
1796
- setSubmitting(false);
2331
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2332
+ setErrors(formatValidationErrors(res.error));
2333
+ }
2334
+ } else if (documentId || collectionType === SINGLE_TYPES) {
2335
+ const res = await update(
2336
+ {
2337
+ collectionType,
2338
+ model,
2339
+ documentId,
2340
+ params
2341
+ },
2342
+ transformData(document)
2343
+ );
2344
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2345
+ setErrors(formatValidationErrors(res.error));
2346
+ } else {
2347
+ resetForm();
2348
+ }
2349
+ } else {
2350
+ const res = await create(
2351
+ {
2352
+ model,
2353
+ params
2354
+ },
2355
+ transformData(document)
2356
+ );
2357
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2358
+ navigate(
2359
+ {
2360
+ pathname: `../${res.data.documentId}`,
2361
+ search: rawQuery
2362
+ },
2363
+ { replace: true, relative: "path" }
2364
+ );
2365
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2366
+ setErrors(formatValidationErrors(res.error));
2367
+ }
1797
2368
  }
2369
+ } finally {
2370
+ setSubmitting(false);
1798
2371
  }
2372
+ }, [
2373
+ clone,
2374
+ cloneMatch?.params.origin,
2375
+ collectionType,
2376
+ create,
2377
+ document,
2378
+ documentId,
2379
+ formatMessage,
2380
+ formatValidationErrors,
2381
+ isCloning,
2382
+ model,
2383
+ modified,
2384
+ navigate,
2385
+ params,
2386
+ rawQuery,
2387
+ resetForm,
2388
+ setErrors,
2389
+ setSubmitting,
2390
+ toggleNotification,
2391
+ update,
2392
+ validate
2393
+ ]);
2394
+ React__namespace.useEffect(() => {
2395
+ const handleKeyDown = (e) => {
2396
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
2397
+ e.preventDefault();
2398
+ handleUpdate();
2399
+ }
2400
+ };
2401
+ window.addEventListener("keydown", handleKeyDown);
2402
+ return () => {
2403
+ window.removeEventListener("keydown", handleKeyDown);
2404
+ };
2405
+ }, [handleUpdate]);
2406
+ return {
2407
+ /**
2408
+ * Disabled when:
2409
+ * - the form is submitting
2410
+ * - the document is not modified & we're not cloning (you can save a clone entity straight away)
2411
+ * - the active tab is the published tab
2412
+ */
2413
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
2414
+ label: formatMessage({
2415
+ id: "global.save",
2416
+ defaultMessage: "Save"
2417
+ }),
2418
+ onClick: handleUpdate
1799
2419
  };
1800
2420
  };
1801
2421
  UpdateAction.type = "update";
2422
+ UpdateAction.position = "panel";
1802
2423
  const UNPUBLISH_DRAFT_OPTIONS = {
1803
2424
  KEEP: "keep",
1804
2425
  DISCARD: "discard"
1805
2426
  };
1806
- const UnpublishAction = ({
2427
+ const UnpublishAction$1 = ({
1807
2428
  activeTab,
1808
2429
  documentId,
1809
2430
  model,
@@ -1819,10 +2440,8 @@ const UnpublishAction = ({
1819
2440
  const { toggleNotification } = strapiAdmin.useNotification();
1820
2441
  const [shouldKeepDraft, setShouldKeepDraft] = React__namespace.useState(true);
1821
2442
  const isDocumentModified = document?.status === "modified";
1822
- const handleChange = (e) => {
1823
- if ("value" in e.target) {
1824
- setShouldKeepDraft(e.target.value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
1825
- }
2443
+ const handleChange = (value) => {
2444
+ setShouldKeepDraft(value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
1826
2445
  };
1827
2446
  if (!schema?.options?.draftAndPublish) {
1828
2447
  return null;
@@ -1833,7 +2452,7 @@ const UnpublishAction = ({
1833
2452
  id: "app.utils.unpublish",
1834
2453
  defaultMessage: "Unpublish"
1835
2454
  }),
1836
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2455
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
1837
2456
  onClick: async () => {
1838
2457
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1839
2458
  if (!documentId) {
@@ -1866,45 +2485,30 @@ const UnpublishAction = ({
1866
2485
  content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "flex-start", direction: "column", gap: 6, children: [
1867
2486
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", direction: "column", gap: 2, children: [
1868
2487
  /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
1869
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "p", variant: "omega", textAlign: "center", children: formatMessage({
2488
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
1870
2489
  id: "content-manager.actions.unpublish.dialog.body",
1871
2490
  defaultMessage: "Are you sure?"
1872
2491
  }) })
1873
2492
  ] }),
1874
2493
  /* @__PURE__ */ jsxRuntime.jsxs(
1875
- designSystem.Flex,
2494
+ designSystem.Radio.Group,
1876
2495
  {
1877
- onChange: handleChange,
1878
- direction: "column",
1879
- alignItems: "flex-start",
1880
- as: "fieldset",
1881
- gap: 3,
2496
+ defaultValue: UNPUBLISH_DRAFT_OPTIONS.KEEP,
2497
+ name: "discard-options",
2498
+ "aria-label": formatMessage({
2499
+ id: "content-manager.actions.unpublish.dialog.radio-label",
2500
+ defaultMessage: "Choose an option to unpublish the document."
2501
+ }),
2502
+ onValueChange: handleChange,
1882
2503
  children: [
1883
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { as: "legend" }),
1884
- /* @__PURE__ */ jsxRuntime.jsx(
1885
- designSystem.Radio,
1886
- {
1887
- checked: shouldKeepDraft,
1888
- value: UNPUBLISH_DRAFT_OPTIONS.KEEP,
1889
- name: "discard-options",
1890
- children: formatMessage({
1891
- id: "content-manager.actions.unpublish.dialog.option.keep-draft",
1892
- defaultMessage: "Keep draft"
1893
- })
1894
- }
1895
- ),
1896
- /* @__PURE__ */ jsxRuntime.jsx(
1897
- designSystem.Radio,
1898
- {
1899
- checked: !shouldKeepDraft,
1900
- value: UNPUBLISH_DRAFT_OPTIONS.DISCARD,
1901
- name: "discard-options",
1902
- children: formatMessage({
1903
- id: "content-manager.actions.unpublish.dialog.option.replace-draft",
1904
- defaultMessage: "Replace draft"
1905
- })
1906
- }
1907
- )
2504
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { checked: shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.KEEP, children: formatMessage({
2505
+ id: "content-manager.actions.unpublish.dialog.option.keep-draft",
2506
+ defaultMessage: "Keep draft"
2507
+ }) }),
2508
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { checked: !shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.DISCARD, children: formatMessage({
2509
+ id: "content-manager.actions.unpublish.dialog.option.replace-draft",
2510
+ defaultMessage: "Replace draft"
2511
+ }) })
1908
2512
  ]
1909
2513
  }
1910
2514
  )
@@ -1937,7 +2541,8 @@ const UnpublishAction = ({
1937
2541
  position: ["panel", "table-row"]
1938
2542
  };
1939
2543
  };
1940
- UnpublishAction.type = "unpublish";
2544
+ UnpublishAction$1.type = "unpublish";
2545
+ UnpublishAction$1.position = "panel";
1941
2546
  const DiscardAction = ({
1942
2547
  activeTab,
1943
2548
  documentId,
@@ -1960,7 +2565,7 @@ const DiscardAction = ({
1960
2565
  id: "content-manager.actions.discard.label",
1961
2566
  defaultMessage: "Discard changes"
1962
2567
  }),
1963
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2568
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
1964
2569
  position: ["panel", "table-row"],
1965
2570
  variant: "danger",
1966
2571
  dialog: {
@@ -1971,7 +2576,7 @@ const DiscardAction = ({
1971
2576
  }),
1972
2577
  content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
1973
2578
  /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
1974
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "p", variant: "omega", textAlign: "center", children: formatMessage({
2579
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
1975
2580
  id: "content-manager.actions.discard.dialog.body",
1976
2581
  defaultMessage: "Are you sure?"
1977
2582
  }) })
@@ -1988,12 +2593,8 @@ const DiscardAction = ({
1988
2593
  };
1989
2594
  };
1990
2595
  DiscardAction.type = "discard";
1991
- const StyledCrossCircle = styled__default.default(Icons.CrossCircle)`
1992
- path {
1993
- fill: currentColor;
1994
- }
1995
- `;
1996
- const DEFAULT_ACTIONS = [PublishAction, UpdateAction, UnpublishAction, DiscardAction];
2596
+ DiscardAction.position = "panel";
2597
+ const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
1997
2598
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
1998
2599
  const RelativeTime = React__namespace.forwardRef(
1999
2600
  ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
@@ -2005,7 +2606,7 @@ const RelativeTime = React__namespace.forwardRef(
2005
2606
  });
2006
2607
  const unit = intervals.find((intervalUnit) => {
2007
2608
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2008
- });
2609
+ }) ?? "seconds";
2009
2610
  const relativeTime = dateFns.isPast(timestamp) ? -interval[unit] : interval[unit];
2010
2611
  const customInterval = customIntervals.find(
2011
2612
  (custom) => interval[custom.unit] < custom.threshold
@@ -2039,34 +2640,34 @@ const getDisplayName = ({
2039
2640
  return email ?? "";
2040
2641
  };
2041
2642
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2042
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2043
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2044
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2643
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2644
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2645
+ const { formatMessage } = reactIntl.useIntl();
2646
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2647
+ id: `content-manager.containers.List.${status}`,
2648
+ defaultMessage: capitalise(status)
2649
+ }) }) });
2045
2650
  };
2046
2651
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2047
2652
  const { formatMessage } = reactIntl.useIntl();
2048
2653
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2654
+ const params = reactRouterDom.useParams();
2049
2655
  const title = isCreating ? formatMessage({
2050
2656
  id: "content-manager.containers.edit.title.new",
2051
2657
  defaultMessage: "Create an entry"
2052
2658
  }) : documentTitle;
2053
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 8, paddingBottom: 4, gap: 3, children: [
2054
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.BackButton, {}),
2055
- /* @__PURE__ */ jsxRuntime.jsxs(
2056
- designSystem.Flex,
2659
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2660
+ /* @__PURE__ */ jsxRuntime.jsx(
2661
+ strapiAdmin.BackButton,
2057
2662
  {
2058
- width: "100%",
2059
- justifyContent: "space-between",
2060
- paddingTop: 1,
2061
- gap: "80px",
2062
- alignItems: "flex-start",
2063
- children: [
2064
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", as: "h1", children: title }),
2065
- /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2066
- ]
2663
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2067
2664
  }
2068
2665
  ),
2069
- status ? /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) : null
2666
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2667
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2668
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2669
+ ] }),
2670
+ status ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2070
2671
  ] });
2071
2672
  };
2072
2673
  const HeaderToolbar = () => {
@@ -2112,7 +2713,7 @@ const HeaderToolbar = () => {
2112
2713
  meta: isCloning ? void 0 : meta,
2113
2714
  collectionType
2114
2715
  },
2115
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2716
+ descriptions: plugins["content-manager"].apis.getDocumentActions("header"),
2116
2717
  children: (actions2) => {
2117
2718
  const headerActions = actions2.filter((action) => {
2118
2719
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2149,12 +2750,12 @@ const Information = ({ activeTab }) => {
2149
2750
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2150
2751
  label: formatMessage({
2151
2752
  id: "content-manager.containers.edit.information.last-published.label",
2152
- defaultMessage: "Last published"
2753
+ defaultMessage: "Published"
2153
2754
  }),
2154
2755
  value: formatMessage(
2155
2756
  {
2156
2757
  id: "content-manager.containers.edit.information.last-published.value",
2157
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2758
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2158
2759
  },
2159
2760
  {
2160
2761
  time: /* @__PURE__ */ jsxRuntime.jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2167,12 +2768,12 @@ const Information = ({ activeTab }) => {
2167
2768
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2168
2769
  label: formatMessage({
2169
2770
  id: "content-manager.containers.edit.information.last-draft.label",
2170
- defaultMessage: "Last draft"
2771
+ defaultMessage: "Updated"
2171
2772
  }),
2172
2773
  value: formatMessage(
2173
2774
  {
2174
2775
  id: "content-manager.containers.edit.information.last-draft.value",
2175
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2776
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2176
2777
  },
2177
2778
  {
2178
2779
  time: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2190,12 +2791,12 @@ const Information = ({ activeTab }) => {
2190
2791
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2191
2792
  label: formatMessage({
2192
2793
  id: "content-manager.containers.edit.information.document.label",
2193
- defaultMessage: "Document"
2794
+ defaultMessage: "Created"
2194
2795
  }),
2195
2796
  value: formatMessage(
2196
2797
  {
2197
2798
  id: "content-manager.containers.edit.information.document.value",
2198
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2799
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2199
2800
  },
2200
2801
  {
2201
2802
  time: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2218,7 +2819,7 @@ const Information = ({ activeTab }) => {
2218
2819
  borderColor: "neutral150",
2219
2820
  direction: "column",
2220
2821
  marginTop: 2,
2221
- as: "dl",
2822
+ tag: "dl",
2222
2823
  padding: 5,
2223
2824
  gap: 3,
2224
2825
  alignItems: "flex-start",
@@ -2226,32 +2827,84 @@ const Information = ({ activeTab }) => {
2226
2827
  marginRight: "-0.4rem",
2227
2828
  width: "calc(100% + 8px)",
2228
2829
  children: information.map((info) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, direction: "column", alignItems: "flex-start", children: [
2229
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "dt", variant: "pi", fontWeight: "bold", children: info.label }),
2230
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "dd", variant: "pi", textColor: "neutral600", children: info.value })
2830
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "dt", variant: "pi", fontWeight: "bold", children: info.label }),
2831
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "dd", variant: "pi", textColor: "neutral600", children: info.value })
2231
2832
  ] }, info.label))
2232
2833
  }
2233
2834
  );
2234
2835
  };
2235
2836
  const HeaderActions = ({ actions: actions2 }) => {
2236
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: actions2.map((action) => {
2237
- if ("options" in action) {
2837
+ const [dialogId, setDialogId] = React__namespace.useState(null);
2838
+ const handleClick = (action) => async (e) => {
2839
+ if (!("options" in action)) {
2840
+ const { onClick = () => false, dialog, id } = action;
2841
+ const muteDialog = await onClick(e);
2842
+ if (dialog && !muteDialog) {
2843
+ e.preventDefault();
2844
+ setDialogId(id);
2845
+ }
2846
+ }
2847
+ };
2848
+ const handleClose = () => {
2849
+ setDialogId(null);
2850
+ };
2851
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, children: actions2.map((action) => {
2852
+ if (action.options) {
2238
2853
  return /* @__PURE__ */ jsxRuntime.jsx(
2239
2854
  designSystem.SingleSelect,
2240
2855
  {
2241
2856
  size: "S",
2242
- disabled: action.disabled,
2243
- "aria-label": action.label,
2244
2857
  onChange: action.onSelect,
2245
- value: action.value,
2858
+ "aria-label": action.label,
2859
+ ...action,
2246
2860
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { ...option, children: label }, option.value))
2247
2861
  },
2248
2862
  action.id
2249
2863
  );
2250
2864
  } else {
2251
- return null;
2865
+ if (action.type === "icon") {
2866
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
2867
+ /* @__PURE__ */ jsxRuntime.jsx(
2868
+ designSystem.IconButton,
2869
+ {
2870
+ disabled: action.disabled,
2871
+ label: action.label,
2872
+ size: "S",
2873
+ onClick: handleClick(action),
2874
+ children: action.icon
2875
+ }
2876
+ ),
2877
+ action.dialog ? /* @__PURE__ */ jsxRuntime.jsx(
2878
+ HeaderActionDialog,
2879
+ {
2880
+ ...action.dialog,
2881
+ isOpen: dialogId === action.id,
2882
+ onClose: handleClose
2883
+ }
2884
+ ) : null
2885
+ ] }, action.id);
2886
+ }
2252
2887
  }
2253
2888
  }) });
2254
2889
  };
2890
+ const HeaderActionDialog = ({
2891
+ onClose,
2892
+ onCancel,
2893
+ title,
2894
+ content: Content,
2895
+ isOpen
2896
+ }) => {
2897
+ const handleClose = async () => {
2898
+ if (onCancel) {
2899
+ await onCancel();
2900
+ }
2901
+ onClose();
2902
+ };
2903
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2904
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2905
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content
2906
+ ] }) });
2907
+ };
2255
2908
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2256
2909
  const navigate = reactRouterDom.useNavigate();
2257
2910
  const { formatMessage } = reactIntl.useIntl();
@@ -2260,7 +2913,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2260
2913
  id: "app.links.configure-view",
2261
2914
  defaultMessage: "Configure the view"
2262
2915
  }),
2263
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCog, {}),
2916
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ListPlus, {}),
2264
2917
  onClick: () => {
2265
2918
  navigate(`../${collectionType}/${model}/configurations/edit`);
2266
2919
  },
@@ -2268,11 +2921,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2268
2921
  };
2269
2922
  };
2270
2923
  ConfigureTheViewAction.type = "configure-the-view";
2271
- const StyledCog = styled__default.default(Icons.Cog)`
2272
- path {
2273
- fill: currentColor;
2274
- }
2275
- `;
2924
+ ConfigureTheViewAction.position = "header";
2276
2925
  const EditTheModelAction = ({ model }) => {
2277
2926
  const navigate = reactRouterDom.useNavigate();
2278
2927
  const { formatMessage } = reactIntl.useIntl();
@@ -2281,7 +2930,7 @@ const EditTheModelAction = ({ model }) => {
2281
2930
  id: "content-manager.link-to-ctb",
2282
2931
  defaultMessage: "Edit the model"
2283
2932
  }),
2284
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledPencil$1, {}),
2933
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {}),
2285
2934
  onClick: () => {
2286
2935
  navigate(`/plugins/content-type-builder/content-types/${model}`);
2287
2936
  },
@@ -2289,12 +2938,8 @@ const EditTheModelAction = ({ model }) => {
2289
2938
  };
2290
2939
  };
2291
2940
  EditTheModelAction.type = "edit-the-model";
2292
- const StyledPencil$1 = styled__default.default(Icons.Pencil)`
2293
- path {
2294
- fill: currentColor;
2295
- }
2296
- `;
2297
- const DeleteAction = ({ documentId, model, collectionType, document }) => {
2941
+ EditTheModelAction.position = "header";
2942
+ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2298
2943
  const navigate = reactRouterDom.useNavigate();
2299
2944
  const { formatMessage } = reactIntl.useIntl();
2300
2945
  const listViewPathMatch = reactRouterDom.useMatch(LIST_PATH);
@@ -2302,13 +2947,17 @@ const DeleteAction = ({ documentId, model, collectionType, document }) => {
2302
2947
  const { delete: deleteAction } = useDocumentActions();
2303
2948
  const { toggleNotification } = strapiAdmin.useNotification();
2304
2949
  const setSubmitting = strapiAdmin.useForm("DeleteAction", (state) => state.setSubmitting);
2950
+ const isLocalized = document?.locale != null;
2305
2951
  return {
2306
2952
  disabled: !canDelete || !document,
2307
- label: formatMessage({
2308
- id: "content-manager.actions.delete.label",
2309
- defaultMessage: "Delete document"
2310
- }),
2311
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledTrash, {}),
2953
+ label: formatMessage(
2954
+ {
2955
+ id: "content-manager.actions.delete.label",
2956
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2957
+ },
2958
+ { isLocalized }
2959
+ ),
2960
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2312
2961
  dialog: {
2313
2962
  type: "dialog",
2314
2963
  title: formatMessage({
@@ -2317,7 +2966,7 @@ const DeleteAction = ({ documentId, model, collectionType, document }) => {
2317
2966
  }),
2318
2967
  content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
2319
2968
  /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2320
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "p", variant: "omega", textAlign: "center", children: formatMessage({
2969
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2321
2970
  id: "content-manager.actions.delete.dialog.body",
2322
2971
  defaultMessage: "Are you sure?"
2323
2972
  }) })
@@ -2362,13 +3011,9 @@ const DeleteAction = ({ documentId, model, collectionType, document }) => {
2362
3011
  position: ["header", "table-row"]
2363
3012
  };
2364
3013
  };
2365
- DeleteAction.type = "delete";
2366
- const StyledTrash = styled__default.default(Icons.Trash)`
2367
- path {
2368
- fill: currentColor;
2369
- }
2370
- `;
2371
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction];
3014
+ DeleteAction$1.type = "delete";
3015
+ DeleteAction$1.position = ["header", "table-row"];
3016
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2372
3017
  const Panels = () => {
2373
3018
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2374
3019
  const [
@@ -2402,7 +3047,7 @@ const ActionsPanel = () => {
2402
3047
  return {
2403
3048
  title: formatMessage({
2404
3049
  id: "content-manager.containers.edit.panels.default.title",
2405
- defaultMessage: "Document"
3050
+ defaultMessage: "Entry"
2406
3051
  }),
2407
3052
  content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2408
3053
  };
@@ -2430,7 +3075,7 @@ const ActionsPanelContent = () => {
2430
3075
  strapiAdmin.DescriptionComponentRenderer,
2431
3076
  {
2432
3077
  props,
2433
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3078
+ descriptions: plugins["content-manager"].apis.getDocumentActions("panel"),
2434
3079
  children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
2435
3080
  }
2436
3081
  ),
@@ -2442,7 +3087,7 @@ const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
2442
3087
  designSystem.Flex,
2443
3088
  {
2444
3089
  ref,
2445
- as: "aside",
3090
+ tag: "aside",
2446
3091
  "aria-labelledby": "additional-information",
2447
3092
  background: "neutral0",
2448
3093
  borderColor: "neutral150",
@@ -2457,133 +3102,707 @@ const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
2457
3102
  justifyContent: "stretch",
2458
3103
  alignItems: "flex-start",
2459
3104
  children: [
2460
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
3105
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
2461
3106
  children
2462
3107
  ]
2463
3108
  }
2464
3109
  );
2465
3110
  });
2466
- const DEFAULT_BULK_ACTIONS = [];
2467
- const AutoCloneFailureModalBody = ({ prohibitedFields }) => {
3111
+ const ConfirmBulkActionDialog = ({
3112
+ onToggleDialog,
3113
+ isOpen = false,
3114
+ dialogBody,
3115
+ endAction
3116
+ }) => {
2468
3117
  const { formatMessage } = reactIntl.useIntl();
2469
- const getDefaultErrorMessage = (reason) => {
2470
- switch (reason) {
2471
- case "relation":
2472
- return "Duplicating the relation could remove it from the original entry.";
2473
- case "unique":
2474
- return "Identical values in a unique field are not allowed";
2475
- default:
2476
- return reason;
2477
- }
2478
- };
2479
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2480
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", children: formatMessage({
2481
- id: getTranslation("containers.list.autoCloneModal.title"),
2482
- defaultMessage: "This entry can't be duplicated directly."
3118
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
3119
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage({
3120
+ id: "app.components.ConfirmDialog.title",
3121
+ defaultMessage: "Confirmation"
2483
3122
  }) }),
2484
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: formatMessage({
2485
- id: getTranslation("containers.list.autoCloneModal.description"),
2486
- defaultMessage: "A new entry will be created with the same content, but you'll have to change the following fields to save it."
2487
- }) }) }),
2488
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { marginTop: 6, gap: 2, direction: "column", alignItems: "stretch", children: prohibitedFields.map(([fieldPath, reason]) => /* @__PURE__ */ jsxRuntime.jsxs(
2489
- designSystem.Flex,
2490
- {
2491
- direction: "column",
2492
- gap: 2,
2493
- alignItems: "flex-start",
2494
- borderColor: "neutral200",
2495
- hasRadius: true,
2496
- padding: 6,
2497
- children: [
2498
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "row", as: "ol", children: fieldPath.map((pathSegment, index2) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { fontWeight: "semiBold", as: "li", children: [
2499
- pathSegment,
2500
- index2 !== fieldPath.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
2501
- Icons.ChevronRight,
2502
- {
2503
- fill: "neutral500",
2504
- height: "0.8rem",
2505
- width: "0.8rem",
2506
- style: { margin: "0 0.8rem" }
2507
- }
2508
- )
2509
- ] }, index2)) }),
2510
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "p", textColor: "neutral600", children: formatMessage({
2511
- id: getTranslation(`containers.list.autoCloneModal.error.${reason}`),
2512
- defaultMessage: getDefaultErrorMessage(reason)
2513
- }) })
2514
- ]
2515
- },
2516
- fieldPath.join()
2517
- )) })
2518
- ] });
3123
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3124
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
3125
+ dialogBody
3126
+ ] }) }),
3127
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
3128
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { fullWidth: true, onClick: onToggleDialog, variant: "tertiary", children: formatMessage({
3129
+ id: "app.components.Button.cancel",
3130
+ defaultMessage: "Cancel"
3131
+ }) }) }),
3132
+ endAction
3133
+ ] })
3134
+ ] }) });
2519
3135
  };
2520
- const TableActions = ({ document }) => {
3136
+ const BoldChunk$1 = (chunks) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: chunks });
3137
+ const ConfirmDialogPublishAll = ({
3138
+ isOpen,
3139
+ onToggleDialog,
3140
+ isConfirmButtonLoading = false,
3141
+ onConfirm
3142
+ }) => {
2521
3143
  const { formatMessage } = reactIntl.useIntl();
2522
- const { model, collectionType } = useDoc();
2523
- const plugins = strapiAdmin.useStrapiApp("TableActions", (state) => state.plugins);
2524
- const props = {
2525
- activeTab: null,
2526
- model,
2527
- documentId: document.documentId,
2528
- collectionType,
2529
- document
2530
- };
3144
+ const selectedEntries = strapiAdmin.useTable("ConfirmDialogPublishAll", (state) => state.selectedRows);
3145
+ const { toggleNotification } = strapiAdmin.useNotification();
3146
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler(getTranslation);
3147
+ const { model, schema } = useDoc();
3148
+ const [{ query }] = strapiAdmin.useQueryParams();
3149
+ const enableDraftRelationsCount = false;
3150
+ const {
3151
+ data: countDraftRelations = 0,
3152
+ isLoading,
3153
+ error
3154
+ } = useGetManyDraftRelationCountQuery(
3155
+ {
3156
+ model,
3157
+ documentIds: selectedEntries.map((entry) => entry.documentId),
3158
+ locale: query?.plugins?.i18n?.locale
3159
+ },
3160
+ {
3161
+ skip: !enableDraftRelationsCount
3162
+ }
3163
+ );
3164
+ React__namespace.useEffect(() => {
3165
+ if (error) {
3166
+ toggleNotification({ type: "danger", message: formatAPIError(error) });
3167
+ }
3168
+ }, [error, formatAPIError, toggleNotification]);
3169
+ if (error) {
3170
+ return null;
3171
+ }
2531
3172
  return /* @__PURE__ */ jsxRuntime.jsx(
2532
- strapiAdmin.DescriptionComponentRenderer,
3173
+ ConfirmBulkActionDialog,
2533
3174
  {
2534
- props,
2535
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2536
- children: (actions2) => {
2537
- const tableRowActions = actions2.filter((action) => {
2538
- const positions = Array.isArray(action.position) ? action.position : [action.position];
2539
- return positions.includes("table-row");
2540
- });
2541
- return /* @__PURE__ */ jsxRuntime.jsx(
2542
- DocumentActionsMenu,
3175
+ isOpen: isOpen && !isLoading,
3176
+ onToggleDialog,
3177
+ dialogBody: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3178
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { id: "confirm-description", textAlign: "center", children: [
3179
+ countDraftRelations > 0 && formatMessage(
3180
+ {
3181
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
3182
+ defaultMessage: "<b>{count} {count, plural, one { relation } other { relations } } out of {entities} { entities, plural, one { entry } other { entries } } {count, plural, one { is } other { are } }</b> not published yet and might lead to unexpected behavior. "
3183
+ },
3184
+ {
3185
+ b: BoldChunk$1,
3186
+ count: countDraftRelations,
3187
+ entities: selectedEntries.length
3188
+ }
3189
+ ),
3190
+ formatMessage({
3191
+ id: getTranslation("popUpWarning.bodyMessage.contentType.publish.all"),
3192
+ defaultMessage: "Are you sure you want to publish these entries?"
3193
+ })
3194
+ ] }),
3195
+ schema?.pluginOptions && "i18n" in schema.pluginOptions && schema?.pluginOptions.i18n && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger500", textAlign: "center", children: formatMessage(
2543
3196
  {
2544
- actions: tableRowActions,
2545
- label: formatMessage({
2546
- id: "content-manager.containers.list.table.row-actions",
2547
- defaultMessage: "Row action"
2548
- }),
2549
- variant: "ghost"
3197
+ id: getTranslation("Settings.list.actions.publishAdditionalInfos"),
3198
+ defaultMessage: "This will publish the active locale versions <em>(from Internationalization)</em>"
3199
+ },
3200
+ {
3201
+ em: Emphasis
2550
3202
  }
2551
- );
2552
- }
3203
+ ) })
3204
+ ] }),
3205
+ endAction: /* @__PURE__ */ jsxRuntime.jsx(
3206
+ designSystem.Button,
3207
+ {
3208
+ onClick: onConfirm,
3209
+ variant: "secondary",
3210
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Check, {}),
3211
+ loading: isConfirmButtonLoading,
3212
+ children: formatMessage({
3213
+ id: "app.utils.publish",
3214
+ defaultMessage: "Publish"
3215
+ })
3216
+ }
3217
+ )
2553
3218
  }
2554
3219
  );
2555
3220
  };
2556
- const EditAction = ({ documentId }) => {
2557
- const navigate = reactRouterDom.useNavigate();
2558
- const { formatMessage } = reactIntl.useIntl();
2559
- const { canRead } = useDocumentRBAC("EditAction", ({ canRead: canRead2 }) => ({ canRead: canRead2 }));
2560
- const { toggleNotification } = strapiAdmin.useNotification();
2561
- const [{ query }] = strapiAdmin.useQueryParams();
2562
- return {
2563
- disabled: !canRead,
2564
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledPencil, {}),
2565
- label: formatMessage({
2566
- id: "content-manager.actions.edit.label",
2567
- defaultMessage: "Edit"
2568
- }),
2569
- position: "table-row",
2570
- onClick: async () => {
2571
- if (!documentId) {
2572
- console.error(
2573
- "You're trying to edit a document without an id, this is likely a bug with Strapi. Please open an issue."
3221
+ const TypographyMaxWidth = styledComponents.styled(designSystem.Typography)`
3222
+ max-width: 300px;
3223
+ `;
3224
+ const formatErrorMessages = (errors, parentKey, formatMessage) => {
3225
+ const messages = [];
3226
+ Object.entries(errors).forEach(([key, value]) => {
3227
+ const currentKey = parentKey ? `${parentKey}.${key}` : key;
3228
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3229
+ if ("id" in value && "defaultMessage" in value) {
3230
+ messages.push(
3231
+ formatMessage(
3232
+ {
3233
+ id: `${value.id}.withField`,
3234
+ defaultMessage: value.defaultMessage
3235
+ },
3236
+ { field: currentKey }
3237
+ )
3238
+ );
3239
+ } else {
3240
+ messages.push(
3241
+ ...formatErrorMessages(
3242
+ // @ts-expect-error TODO: check why value is not compatible with FormErrors
3243
+ value,
3244
+ currentKey,
3245
+ formatMessage
3246
+ )
2574
3247
  );
2575
- toggleNotification({
2576
- message: formatMessage({
2577
- id: "content-manager.actions.edit.error",
2578
- defaultMessage: "An error occurred while trying to edit the document."
2579
- }),
2580
- type: "danger"
2581
- });
2582
- return;
2583
3248
  }
2584
- navigate({
2585
- pathname: documentId,
2586
- search: qs.stringify({
3249
+ } else {
3250
+ messages.push(
3251
+ formatMessage(
3252
+ {
3253
+ id: `${value}.withField`,
3254
+ defaultMessage: value
3255
+ },
3256
+ { field: currentKey }
3257
+ )
3258
+ );
3259
+ }
3260
+ });
3261
+ return messages;
3262
+ };
3263
+ const EntryValidationText = ({ validationErrors, status }) => {
3264
+ const { formatMessage } = reactIntl.useIntl();
3265
+ if (validationErrors) {
3266
+ const validationErrorsMessages = formatErrorMessages(validationErrors, "", formatMessage).join(
3267
+ " "
3268
+ );
3269
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
3270
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.CrossCircle, { fill: "danger600" }),
3271
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsxRuntime.jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
3272
+ ] });
3273
+ }
3274
+ if (status === "published") {
3275
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
3276
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.CheckCircle, { fill: "success600" }),
3277
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
3278
+ id: "content-manager.bulk-publish.already-published",
3279
+ defaultMessage: "Already Published"
3280
+ }) })
3281
+ ] });
3282
+ }
3283
+ if (status === "modified") {
3284
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
3285
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.ArrowsCounterClockwise, { fill: "alternative600" }),
3286
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
3287
+ id: "content-manager.bulk-publish.modified",
3288
+ defaultMessage: "Ready to publish changes"
3289
+ }) })
3290
+ ] });
3291
+ }
3292
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
3293
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.CheckCircle, { fill: "success600" }),
3294
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
3295
+ id: "app.utils.ready-to-publish",
3296
+ defaultMessage: "Ready to publish"
3297
+ }) })
3298
+ ] });
3299
+ };
3300
+ const TABLE_HEADERS = [
3301
+ { name: "id", label: "id" },
3302
+ { name: "name", label: "name" },
3303
+ { name: "status", label: "status" },
3304
+ { name: "publicationStatus", label: "Publication status" }
3305
+ ];
3306
+ const SelectedEntriesTableContent = ({
3307
+ isPublishing,
3308
+ rowsToDisplay = [],
3309
+ entriesToPublish = [],
3310
+ validationErrors = {}
3311
+ }) => {
3312
+ const { pathname } = reactRouterDom.useLocation();
3313
+ const { formatMessage } = reactIntl.useIntl();
3314
+ const {
3315
+ list: {
3316
+ settings: { mainField }
3317
+ }
3318
+ } = useDocLayout();
3319
+ const shouldDisplayMainField = mainField != null && mainField !== "id";
3320
+ return /* @__PURE__ */ jsxRuntime.jsxs(strapiAdmin.Table.Content, { children: [
3321
+ /* @__PURE__ */ jsxRuntime.jsxs(strapiAdmin.Table.Head, { children: [
3322
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.HeaderCheckboxCell, {}),
3323
+ TABLE_HEADERS.filter((head) => head.name !== "name" || shouldDisplayMainField).map(
3324
+ (head) => /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.HeaderCell, { ...head }, head.name)
3325
+ )
3326
+ ] }),
3327
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Loading, {}),
3328
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Body, { children: rowsToDisplay.map((row, index2) => /* @__PURE__ */ jsxRuntime.jsxs(strapiAdmin.Table.Row, { children: [
3329
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.CheckboxCell, { id: row.id }),
3330
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: row.id }) }),
3331
+ shouldDisplayMainField && /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: row[mainField] }) }),
3332
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: row.status, maxWidth: "min-content" }) }),
3333
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: isPublishing && entriesToPublish.includes(row.documentId) ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
3334
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
3335
+ id: "content-manager.success.record.publishing",
3336
+ defaultMessage: "Publishing..."
3337
+ }) }),
3338
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true })
3339
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
3340
+ EntryValidationText,
3341
+ {
3342
+ validationErrors: validationErrors[row.documentId],
3343
+ status: row.status
3344
+ }
3345
+ ) }),
3346
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(
3347
+ designSystem.IconButton,
3348
+ {
3349
+ tag: reactRouterDom.Link,
3350
+ to: {
3351
+ pathname: `${pathname}/${row.documentId}`,
3352
+ search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3353
+ },
3354
+ state: { from: pathname },
3355
+ label: formatMessage({
3356
+ id: "content-manager.bulk-publish.edit",
3357
+ defaultMessage: "Edit"
3358
+ }),
3359
+ target: "_blank",
3360
+ marginLeft: "auto",
3361
+ variant: "ghost",
3362
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, { width: "1.6rem", height: "1.6rem" })
3363
+ }
3364
+ ) }) })
3365
+ ] }, row.id)) })
3366
+ ] });
3367
+ };
3368
+ const BoldChunk = (chunks) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: chunks });
3369
+ const SelectedEntriesModalContent = ({
3370
+ listViewSelectedEntries,
3371
+ toggleModal,
3372
+ setListViewSelectedDocuments,
3373
+ model
3374
+ }) => {
3375
+ const { formatMessage } = reactIntl.useIntl();
3376
+ const { schema, components } = useContentTypeSchema(model);
3377
+ const documentIds = listViewSelectedEntries.map(({ documentId }) => documentId);
3378
+ const [{ query }] = strapiAdmin.useQueryParams();
3379
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
3380
+ const { data, isLoading, isFetching, refetch } = useGetAllDocumentsQuery(
3381
+ {
3382
+ model,
3383
+ params: {
3384
+ page: "1",
3385
+ pageSize: documentIds.length.toString(),
3386
+ sort: query.sort,
3387
+ filters: {
3388
+ documentId: {
3389
+ $in: documentIds
3390
+ }
3391
+ },
3392
+ locale: query.plugins?.i18n?.locale
3393
+ }
3394
+ },
3395
+ {
3396
+ selectFromResult: ({ data: data2, ...restRes }) => ({ data: data2?.results ?? [], ...restRes })
3397
+ }
3398
+ );
3399
+ const { rows, validationErrors } = React__namespace.useMemo(() => {
3400
+ if (data.length > 0 && schema) {
3401
+ const validate = createYupSchema(
3402
+ schema.attributes,
3403
+ components,
3404
+ // Since this is the "Publish" action, the validation
3405
+ // schema must enforce the rules for published entities
3406
+ { status: "published" }
3407
+ );
3408
+ const validationErrors2 = {};
3409
+ const rows2 = data.map((entry) => {
3410
+ try {
3411
+ validate.validateSync(entry, { abortEarly: false });
3412
+ return entry;
3413
+ } catch (e) {
3414
+ if (e instanceof yup.ValidationError) {
3415
+ validationErrors2[entry.documentId] = strapiAdmin.getYupValidationErrors(e);
3416
+ }
3417
+ return entry;
3418
+ }
3419
+ });
3420
+ return { rows: rows2, validationErrors: validationErrors2 };
3421
+ }
3422
+ return {
3423
+ rows: [],
3424
+ validationErrors: {}
3425
+ };
3426
+ }, [components, data, schema]);
3427
+ const [publishedCount, setPublishedCount] = React__namespace.useState(0);
3428
+ const [isDialogOpen, setIsDialogOpen] = React__namespace.useState(false);
3429
+ const { publishMany: bulkPublishAction } = useDocumentActions();
3430
+ const [, { isLoading: isSubmittingForm }] = usePublishManyDocumentsMutation();
3431
+ const selectedRows = strapiAdmin.useTable("publishAction", (state) => state.selectedRows);
3432
+ const selectedEntries = rows.filter(
3433
+ (entry) => selectedRows.some((selectedEntry) => selectedEntry.documentId === entry.documentId)
3434
+ );
3435
+ const entriesToPublish = selectedEntries.filter((entry) => !validationErrors[entry.documentId]).map((entry) => entry.documentId);
3436
+ const selectedEntriesWithErrorsCount = selectedEntries.filter(
3437
+ ({ documentId }) => validationErrors[documentId]
3438
+ ).length;
3439
+ const selectedEntriesPublished = selectedEntries.filter(
3440
+ ({ status }) => status === "published"
3441
+ ).length;
3442
+ const selectedEntriesWithNoErrorsCount = selectedEntries.length - selectedEntriesWithErrorsCount - selectedEntriesPublished;
3443
+ const toggleDialog = () => setIsDialogOpen((prev) => !prev);
3444
+ const handleConfirmBulkPublish = async () => {
3445
+ toggleDialog();
3446
+ const res = await bulkPublishAction({ model, documentIds: entriesToPublish, params });
3447
+ if (!("error" in res)) {
3448
+ setPublishedCount(res.count);
3449
+ const unpublishedEntries = rows.filter((row) => {
3450
+ return !entriesToPublish.includes(row.documentId);
3451
+ });
3452
+ setListViewSelectedDocuments(unpublishedEntries);
3453
+ }
3454
+ };
3455
+ const getFormattedCountMessage = () => {
3456
+ if (publishedCount) {
3457
+ return formatMessage(
3458
+ {
3459
+ id: getTranslation("containers.list.selectedEntriesModal.publishedCount"),
3460
+ defaultMessage: "<b>{publishedCount}</b> {publishedCount, plural, =0 {entries} one {entry} other {entries}} published. <b>{withErrorsCount}</b> {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action."
3461
+ },
3462
+ {
3463
+ publishedCount,
3464
+ withErrorsCount: selectedEntriesWithErrorsCount,
3465
+ b: BoldChunk
3466
+ }
3467
+ );
3468
+ }
3469
+ return formatMessage(
3470
+ {
3471
+ id: getTranslation("containers.list.selectedEntriesModal.selectedCount"),
3472
+ defaultMessage: "<b>{alreadyPublishedCount}</b> {alreadyPublishedCount, plural, =0 {entries} one {entry} other {entries}} already published. <b>{readyToPublishCount}</b> {readyToPublishCount, plural, =0 {entries} one {entry} other {entries}} ready to publish. <b>{withErrorsCount}</b> {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action."
3473
+ },
3474
+ {
3475
+ readyToPublishCount: selectedEntriesWithNoErrorsCount,
3476
+ withErrorsCount: selectedEntriesWithErrorsCount,
3477
+ alreadyPublishedCount: selectedEntriesPublished,
3478
+ b: BoldChunk
3479
+ }
3480
+ );
3481
+ };
3482
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3483
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Body, { children: [
3484
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: getFormattedCountMessage() }),
3485
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 5, children: /* @__PURE__ */ jsxRuntime.jsx(
3486
+ SelectedEntriesTableContent,
3487
+ {
3488
+ isPublishing: isSubmittingForm,
3489
+ rowsToDisplay: rows,
3490
+ entriesToPublish,
3491
+ validationErrors
3492
+ }
3493
+ ) })
3494
+ ] }),
3495
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
3496
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: toggleModal, variant: "tertiary", children: formatMessage({
3497
+ id: "app.components.Button.cancel",
3498
+ defaultMessage: "Cancel"
3499
+ }) }),
3500
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
3501
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: refetch, variant: "tertiary", loading: isFetching, children: formatMessage({ id: "app.utils.refresh", defaultMessage: "Refresh" }) }),
3502
+ /* @__PURE__ */ jsxRuntime.jsx(
3503
+ designSystem.Button,
3504
+ {
3505
+ onClick: toggleDialog,
3506
+ disabled: selectedEntries.length === 0 || selectedEntries.length === selectedEntriesWithErrorsCount || selectedEntriesPublished === selectedEntries.length || isLoading,
3507
+ loading: isSubmittingForm,
3508
+ children: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" })
3509
+ }
3510
+ )
3511
+ ] })
3512
+ ] }),
3513
+ /* @__PURE__ */ jsxRuntime.jsx(
3514
+ ConfirmDialogPublishAll,
3515
+ {
3516
+ isOpen: isDialogOpen,
3517
+ onToggleDialog: toggleDialog,
3518
+ isConfirmButtonLoading: isSubmittingForm,
3519
+ onConfirm: handleConfirmBulkPublish
3520
+ }
3521
+ )
3522
+ ] });
3523
+ };
3524
+ const PublishAction = ({ documents, model }) => {
3525
+ const { formatMessage } = reactIntl.useIntl();
3526
+ const hasPublishPermission = useDocumentRBAC("unpublishAction", (state) => state.canPublish);
3527
+ const showPublishButton = hasPublishPermission && documents.some(({ status }) => status !== "published");
3528
+ const setListViewSelectedDocuments = strapiAdmin.useTable("publishAction", (state) => state.selectRow);
3529
+ const refetchList = () => {
3530
+ contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3531
+ };
3532
+ if (!showPublishButton) return null;
3533
+ return {
3534
+ actionType: "publish",
3535
+ variant: "tertiary",
3536
+ label: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" }),
3537
+ dialog: {
3538
+ type: "modal",
3539
+ title: formatMessage({
3540
+ id: getTranslation("containers.ListPage.selectedEntriesModal.title"),
3541
+ defaultMessage: "Publish entries"
3542
+ }),
3543
+ content: ({ onClose }) => {
3544
+ return /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Root, { rows: documents, defaultSelectedRows: documents, headers: TABLE_HEADERS, children: /* @__PURE__ */ jsxRuntime.jsx(
3545
+ SelectedEntriesModalContent,
3546
+ {
3547
+ listViewSelectedEntries: documents,
3548
+ toggleModal: () => {
3549
+ onClose();
3550
+ refetchList();
3551
+ },
3552
+ setListViewSelectedDocuments,
3553
+ model
3554
+ }
3555
+ ) });
3556
+ },
3557
+ onClose: () => {
3558
+ refetchList();
3559
+ }
3560
+ }
3561
+ };
3562
+ };
3563
+ const BulkActionsRenderer = () => {
3564
+ const plugins = strapiAdmin.useStrapiApp("BulkActionsRenderer", (state) => state.plugins);
3565
+ const { model, collectionType } = useDoc();
3566
+ const { selectedRows } = strapiAdmin.useTable("BulkActionsRenderer", (state) => state);
3567
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
3568
+ strapiAdmin.DescriptionComponentRenderer,
3569
+ {
3570
+ props: {
3571
+ model,
3572
+ collectionType,
3573
+ documents: selectedRows
3574
+ },
3575
+ descriptions: plugins["content-manager"].apis.getBulkActions(),
3576
+ children: (actions2) => actions2.map((action) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActionButton, { ...action }, action.id))
3577
+ }
3578
+ ) });
3579
+ };
3580
+ const DeleteAction = ({ documents, model }) => {
3581
+ const { formatMessage } = reactIntl.useIntl();
3582
+ const { schema: contentType } = useDoc();
3583
+ const selectRow = strapiAdmin.useTable("DeleteAction", (state) => state.selectRow);
3584
+ const hasI18nEnabled = Boolean(contentType?.pluginOptions?.i18n);
3585
+ const [{ query }] = strapiAdmin.useQueryParams();
3586
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
3587
+ const hasDeletePermission = useDocumentRBAC("deleteAction", (state) => state.canDelete);
3588
+ const { deleteMany: bulkDeleteAction } = useDocumentActions();
3589
+ const documentIds = documents.map(({ documentId }) => documentId);
3590
+ const handleConfirmBulkDelete = async () => {
3591
+ const res = await bulkDeleteAction({
3592
+ documentIds,
3593
+ model,
3594
+ params
3595
+ });
3596
+ if (!("error" in res)) {
3597
+ selectRow([]);
3598
+ }
3599
+ };
3600
+ if (!hasDeletePermission) return null;
3601
+ return {
3602
+ variant: "danger-light",
3603
+ label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
3604
+ dialog: {
3605
+ type: "dialog",
3606
+ title: formatMessage({
3607
+ id: "app.components.ConfirmDialog.title",
3608
+ defaultMessage: "Confirmation"
3609
+ }),
3610
+ content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3611
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
3612
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
3613
+ id: "popUpWarning.bodyMessage.contentType.delete.all",
3614
+ defaultMessage: "Are you sure you want to delete these entries?"
3615
+ }) }),
3616
+ hasI18nEnabled && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { textAlign: "center", padding: 3, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger500", children: formatMessage(
3617
+ {
3618
+ id: getTranslation("Settings.list.actions.deleteAdditionalInfos"),
3619
+ defaultMessage: "This will delete the active locale versions <em>(from Internationalization)</em>"
3620
+ },
3621
+ {
3622
+ em: Emphasis
3623
+ }
3624
+ ) }) })
3625
+ ] }),
3626
+ onConfirm: handleConfirmBulkDelete
3627
+ }
3628
+ };
3629
+ };
3630
+ DeleteAction.type = "delete";
3631
+ const UnpublishAction = ({ documents, model }) => {
3632
+ const { formatMessage } = reactIntl.useIntl();
3633
+ const { schema } = useDoc();
3634
+ const selectRow = strapiAdmin.useTable("UnpublishAction", (state) => state.selectRow);
3635
+ const hasPublishPermission = useDocumentRBAC("unpublishAction", (state) => state.canPublish);
3636
+ const hasI18nEnabled = Boolean(schema?.pluginOptions?.i18n);
3637
+ const hasDraftAndPublishEnabled = Boolean(schema?.options?.draftAndPublish);
3638
+ const { unpublishMany: bulkUnpublishAction } = useDocumentActions();
3639
+ const documentIds = documents.map(({ documentId }) => documentId);
3640
+ const [{ query }] = strapiAdmin.useQueryParams();
3641
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
3642
+ const handleConfirmBulkUnpublish = async () => {
3643
+ const data = await bulkUnpublishAction({ documentIds, model, params });
3644
+ if (!("error" in data)) {
3645
+ selectRow([]);
3646
+ }
3647
+ };
3648
+ const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3649
+ if (!showUnpublishButton) return null;
3650
+ return {
3651
+ variant: "tertiary",
3652
+ label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
3653
+ dialog: {
3654
+ type: "dialog",
3655
+ title: formatMessage({
3656
+ id: "app.components.ConfirmDialog.title",
3657
+ defaultMessage: "Confirmation"
3658
+ }),
3659
+ content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3660
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
3661
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
3662
+ id: "popUpWarning.bodyMessage.contentType.unpublish.all",
3663
+ defaultMessage: "Are you sure you want to unpublish these entries?"
3664
+ }) }),
3665
+ hasI18nEnabled && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { textAlign: "center", padding: 3, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger500", children: formatMessage(
3666
+ {
3667
+ id: getTranslation("Settings.list.actions.unpublishAdditionalInfos"),
3668
+ defaultMessage: "This will unpublish the active locale versions <em>(from Internationalization)</em>"
3669
+ },
3670
+ {
3671
+ em: Emphasis
3672
+ }
3673
+ ) }) })
3674
+ ] }),
3675
+ confirmButton: formatMessage({
3676
+ id: "app.utils.unpublish",
3677
+ defaultMessage: "Unpublish"
3678
+ }),
3679
+ onConfirm: handleConfirmBulkUnpublish
3680
+ }
3681
+ };
3682
+ };
3683
+ UnpublishAction.type = "unpublish";
3684
+ const Emphasis = (chunks) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", textColor: "danger500", children: chunks });
3685
+ const DEFAULT_BULK_ACTIONS = [PublishAction, UnpublishAction, DeleteAction];
3686
+ const AutoCloneFailureModalBody = ({ prohibitedFields }) => {
3687
+ const { formatMessage } = reactIntl.useIntl();
3688
+ const getDefaultErrorMessage = (reason) => {
3689
+ switch (reason) {
3690
+ case "relation":
3691
+ return "Duplicating the relation could remove it from the original entry.";
3692
+ case "unique":
3693
+ return "Identical values in a unique field are not allowed";
3694
+ default:
3695
+ return reason;
3696
+ }
3697
+ };
3698
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3699
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", children: formatMessage({
3700
+ id: getTranslation("containers.list.autoCloneModal.title"),
3701
+ defaultMessage: "This entry can't be duplicated directly."
3702
+ }) }),
3703
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: formatMessage({
3704
+ id: getTranslation("containers.list.autoCloneModal.description"),
3705
+ defaultMessage: "A new entry will be created with the same content, but you'll have to change the following fields to save it."
3706
+ }) }) }),
3707
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { marginTop: 6, gap: 2, direction: "column", alignItems: "stretch", children: prohibitedFields.map(([fieldPath, reason]) => /* @__PURE__ */ jsxRuntime.jsxs(
3708
+ designSystem.Flex,
3709
+ {
3710
+ direction: "column",
3711
+ gap: 2,
3712
+ alignItems: "flex-start",
3713
+ borderColor: "neutral200",
3714
+ hasRadius: true,
3715
+ padding: 6,
3716
+ children: [
3717
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "row", tag: "ol", children: fieldPath.map((pathSegment, index2) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { fontWeight: "semiBold", tag: "li", children: [
3718
+ pathSegment,
3719
+ index2 !== fieldPath.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
3720
+ Icons.ChevronRight,
3721
+ {
3722
+ fill: "neutral500",
3723
+ height: "0.8rem",
3724
+ width: "0.8rem",
3725
+ style: { margin: "0 0.8rem" }
3726
+ }
3727
+ )
3728
+ ] }, index2)) }),
3729
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", textColor: "neutral600", children: formatMessage({
3730
+ id: getTranslation(`containers.list.autoCloneModal.error.${reason}`),
3731
+ defaultMessage: getDefaultErrorMessage(reason)
3732
+ }) })
3733
+ ]
3734
+ },
3735
+ fieldPath.join()
3736
+ )) })
3737
+ ] });
3738
+ };
3739
+ const TableActions = ({ document }) => {
3740
+ const { formatMessage } = reactIntl.useIntl();
3741
+ const { model, collectionType } = useDoc();
3742
+ const plugins = strapiAdmin.useStrapiApp("TableActions", (state) => state.plugins);
3743
+ const props = {
3744
+ activeTab: null,
3745
+ model,
3746
+ documentId: document.documentId,
3747
+ collectionType,
3748
+ document
3749
+ };
3750
+ return /* @__PURE__ */ jsxRuntime.jsx(
3751
+ strapiAdmin.DescriptionComponentRenderer,
3752
+ {
3753
+ props,
3754
+ descriptions: plugins["content-manager"].apis.getDocumentActions("table-row").filter((action) => action.name !== "PublishAction"),
3755
+ children: (actions2) => {
3756
+ const tableRowActions = actions2.filter((action) => {
3757
+ const positions = Array.isArray(action.position) ? action.position : [action.position];
3758
+ return positions.includes("table-row");
3759
+ });
3760
+ return /* @__PURE__ */ jsxRuntime.jsx(
3761
+ DocumentActionsMenu,
3762
+ {
3763
+ actions: tableRowActions,
3764
+ label: formatMessage({
3765
+ id: "content-manager.containers.list.table.row-actions",
3766
+ defaultMessage: "Row action"
3767
+ }),
3768
+ variant: "ghost"
3769
+ }
3770
+ );
3771
+ }
3772
+ }
3773
+ );
3774
+ };
3775
+ const EditAction = ({ documentId }) => {
3776
+ const navigate = reactRouterDom.useNavigate();
3777
+ const { formatMessage } = reactIntl.useIntl();
3778
+ const { canRead } = useDocumentRBAC("EditAction", ({ canRead: canRead2 }) => ({ canRead: canRead2 }));
3779
+ const { toggleNotification } = strapiAdmin.useNotification();
3780
+ const [{ query }] = strapiAdmin.useQueryParams();
3781
+ return {
3782
+ disabled: !canRead,
3783
+ icon: /* @__PURE__ */ jsxRuntime.jsx(StyledPencil, {}),
3784
+ label: formatMessage({
3785
+ id: "content-manager.actions.edit.label",
3786
+ defaultMessage: "Edit"
3787
+ }),
3788
+ position: "table-row",
3789
+ onClick: async () => {
3790
+ if (!documentId) {
3791
+ console.error(
3792
+ "You're trying to edit a document without an id, this is likely a bug with Strapi. Please open an issue."
3793
+ );
3794
+ toggleNotification({
3795
+ message: formatMessage({
3796
+ id: "content-manager.actions.edit.error",
3797
+ defaultMessage: "An error occurred while trying to edit the document."
3798
+ }),
3799
+ type: "danger"
3800
+ });
3801
+ return;
3802
+ }
3803
+ navigate({
3804
+ pathname: documentId,
3805
+ search: qs.stringify({
2587
3806
  plugins: query.plugins
2588
3807
  })
2589
3808
  });
@@ -2591,7 +3810,8 @@ const EditAction = ({ documentId }) => {
2591
3810
  };
2592
3811
  };
2593
3812
  EditAction.type = "edit";
2594
- const StyledPencil = styled__default.default(Icons.Pencil)`
3813
+ EditAction.position = "table-row";
3814
+ const StyledPencil = styledComponents.styled(Icons.Pencil)`
2595
3815
  path {
2596
3816
  fill: currentColor;
2597
3817
  }
@@ -2643,7 +3863,7 @@ const CloneAction = ({ model, documentId }) => {
2643
3863
  }),
2644
3864
  content: /* @__PURE__ */ jsxRuntime.jsx(AutoCloneFailureModalBody, { prohibitedFields }),
2645
3865
  footer: ({ onClose }) => {
2646
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
3866
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
2647
3867
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
2648
3868
  id: "cancel",
2649
3869
  defaultMessage: "Cancel"
@@ -2651,7 +3871,7 @@ const CloneAction = ({ model, documentId }) => {
2651
3871
  /* @__PURE__ */ jsxRuntime.jsx(
2652
3872
  designSystem.LinkButton,
2653
3873
  {
2654
- as: reactRouterDom.NavLink,
3874
+ tag: reactRouterDom.NavLink,
2655
3875
  to: {
2656
3876
  pathname: `clone/${documentId}`
2657
3877
  },
@@ -2667,7 +3887,8 @@ const CloneAction = ({ model, documentId }) => {
2667
3887
  };
2668
3888
  };
2669
3889
  CloneAction.type = "clone";
2670
- const StyledDuplicate = styled__default.default(Icons.Duplicate)`
3890
+ CloneAction.position = "table-row";
3891
+ const StyledDuplicate = styledComponents.styled(Icons.Duplicate)`
2671
3892
  path {
2672
3893
  fill: currentColor;
2673
3894
  }
@@ -2684,8 +3905,7 @@ class ContentManagerPlugin {
2684
3905
  documentActions = [
2685
3906
  ...DEFAULT_ACTIONS,
2686
3907
  ...DEFAULT_TABLE_ROW_ACTIONS,
2687
- ...DEFAULT_HEADER_ACTIONS,
2688
- HistoryAction
3908
+ ...DEFAULT_HEADER_ACTIONS
2689
3909
  ];
2690
3910
  editViewSidePanels = [ActionsPanel];
2691
3911
  headerActions = [];
@@ -2754,7 +3974,14 @@ class ContentManagerPlugin {
2754
3974
  addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
2755
3975
  addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
2756
3976
  getBulkActions: () => this.bulkActions,
2757
- getDocumentActions: () => this.documentActions,
3977
+ getDocumentActions: (position) => {
3978
+ if (position) {
3979
+ return this.documentActions.filter(
3980
+ (action) => action.position == void 0 || [action.position].flat().includes(position)
3981
+ );
3982
+ }
3983
+ return this.documentActions;
3984
+ },
2758
3985
  getEditViewSidePanels: () => this.editViewSidePanels,
2759
3986
  getHeaderActions: () => this.headerActions
2760
3987
  }
@@ -2764,16 +3991,71 @@ class ContentManagerPlugin {
2764
3991
  const getPrintableType = (value) => {
2765
3992
  const nativeType = typeof value;
2766
3993
  if (nativeType === "object") {
2767
- if (value === null)
2768
- return "null";
2769
- if (Array.isArray(value))
2770
- return "array";
3994
+ if (value === null) return "null";
3995
+ if (Array.isArray(value)) return "array";
2771
3996
  if (value instanceof Object && value.constructor.name !== "Object") {
2772
3997
  return value.constructor.name;
2773
3998
  }
2774
3999
  }
2775
4000
  return nativeType;
2776
4001
  };
4002
+ const HistoryAction = ({ model, document }) => {
4003
+ const { formatMessage } = reactIntl.useIntl();
4004
+ const [{ query }] = strapiAdmin.useQueryParams();
4005
+ const navigate = reactRouterDom.useNavigate();
4006
+ const { trackUsage } = strapiAdmin.useTracking();
4007
+ const { pathname } = reactRouterDom.useLocation();
4008
+ const pluginsQueryParams = qs.stringify({ plugins: query.plugins }, { encode: false });
4009
+ if (!window.strapi.features.isEnabled("cms-content-history")) {
4010
+ return null;
4011
+ }
4012
+ const handleOnClick = () => {
4013
+ const destination = { pathname: "history", search: pluginsQueryParams };
4014
+ trackUsage("willNavigate", {
4015
+ from: pathname,
4016
+ to: `${pathname}/${destination.pathname}`
4017
+ });
4018
+ navigate(destination);
4019
+ };
4020
+ return {
4021
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ClockCounterClockwise, {}),
4022
+ label: formatMessage({
4023
+ id: "content-manager.history.document-action",
4024
+ defaultMessage: "Content History"
4025
+ }),
4026
+ onClick: handleOnClick,
4027
+ disabled: (
4028
+ /**
4029
+ * The user is creating a new document.
4030
+ * It hasn't been saved yet, so there's no history to go to
4031
+ */
4032
+ !document || /**
4033
+ * The document has been created but the current dimension has never been saved.
4034
+ * For example, the user is creating a new locale in an existing document,
4035
+ * so there's no history for the document in that locale
4036
+ */
4037
+ !document.id || /**
4038
+ * History is only available for content types created by the user.
4039
+ * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
4040
+ * which start with `admin::` or `plugin::`
4041
+ */
4042
+ !model.startsWith("api::")
4043
+ ),
4044
+ position: "header"
4045
+ };
4046
+ };
4047
+ HistoryAction.type = "history";
4048
+ HistoryAction.position = "header";
4049
+ const historyAdmin = {
4050
+ bootstrap(app) {
4051
+ const { addDocumentAction } = app.getPlugin("content-manager").apis;
4052
+ addDocumentAction((actions2) => {
4053
+ const indexOfDeleteAction = actions2.findIndex((action) => action.type === "delete");
4054
+ actions2.splice(indexOfDeleteAction, 0, HistoryAction);
4055
+ return actions2;
4056
+ });
4057
+ }
4058
+ };
2777
4059
  const initialState = {
2778
4060
  collectionTypeLinks: [],
2779
4061
  components: [],
@@ -2810,316 +4092,94 @@ const { setInitialData } = actions;
2810
4092
  const reducer = toolkit.combineReducers({
2811
4093
  app: reducer$1
2812
4094
  });
2813
- const HOOKS = {
2814
- /**
2815
- * Hook that allows to mutate the displayed headers of the list view table
2816
- * @constant
2817
- * @type {string}
2818
- */
2819
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2820
- /**
2821
- * Hook that allows to mutate the CM's collection types links pre-set filters
2822
- * @constant
2823
- * @type {string}
2824
- */
2825
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2826
- /**
2827
- * Hook that allows to mutate the CM's edit view layout
2828
- * @constant
2829
- * @type {string}
2830
- */
2831
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2832
- /**
2833
- * Hook that allows to mutate the CM's single types links pre-set filters
2834
- * @constant
2835
- * @type {string}
2836
- */
2837
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2838
- };
2839
- const contentTypesApi = contentManagerApi.injectEndpoints({
2840
- endpoints: (builder) => ({
2841
- getContentTypeConfiguration: builder.query({
2842
- query: (uid) => ({
2843
- url: `/content-manager/content-types/${uid}/configuration`,
2844
- method: "GET"
2845
- }),
2846
- transformResponse: (response) => response.data,
2847
- providesTags: (_result, _error, uid) => [
2848
- { type: "ContentTypesConfiguration", id: uid },
2849
- { type: "ContentTypeSettings", id: "LIST" }
2850
- ]
2851
- }),
2852
- getAllContentTypeSettings: builder.query({
2853
- query: () => "/content-manager/content-types-settings",
2854
- transformResponse: (response) => response.data,
2855
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2856
- }),
2857
- updateContentTypeConfiguration: builder.mutation({
2858
- query: ({ uid, ...body }) => ({
2859
- url: `/content-manager/content-types/${uid}/configuration`,
2860
- method: "PUT",
2861
- data: body
2862
- }),
2863
- transformResponse: (response) => response.data,
2864
- invalidatesTags: (_result, _error, { uid }) => [
2865
- { type: "ContentTypesConfiguration", id: uid },
2866
- { type: "ContentTypeSettings", id: "LIST" },
2867
- // Is this necessary?
2868
- { type: "InitialData" }
2869
- ]
4095
+ const previewApi = contentManagerApi.injectEndpoints({
4096
+ endpoints: (builder) => ({
4097
+ getPreviewUrl: builder.query({
4098
+ query({ query, params }) {
4099
+ return {
4100
+ url: `/content-manager/preview/url/${params.contentType}`,
4101
+ method: "GET",
4102
+ config: {
4103
+ params: query
4104
+ }
4105
+ };
4106
+ }
2870
4107
  })
2871
4108
  })
2872
4109
  });
2873
- const {
2874
- useGetContentTypeConfigurationQuery,
2875
- useGetAllContentTypeSettingsQuery,
2876
- useUpdateContentTypeConfigurationMutation
2877
- } = contentTypesApi;
2878
- const checkIfAttributeIsDisplayable = (attribute) => {
2879
- const { type } = attribute;
2880
- if (type === "relation") {
2881
- return !attribute.relation.toLowerCase().includes("morph");
4110
+ const { useGetPreviewUrlQuery } = previewApi;
4111
+ const ConditionalTooltip = ({ isShown, label, children }) => {
4112
+ if (isShown) {
4113
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label, children });
2882
4114
  }
2883
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2884
- };
2885
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2886
- if (!mainFieldName) {
2887
- return void 0;
2888
- }
2889
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2890
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2891
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2892
- );
2893
- return {
2894
- name: mainFieldName,
2895
- type: mainFieldType ?? "string"
2896
- };
2897
- };
2898
- const DEFAULT_SETTINGS = {
2899
- bulkable: false,
2900
- filterable: false,
2901
- searchable: false,
2902
- pagination: false,
2903
- defaultSortBy: "",
2904
- defaultSortOrder: "asc",
2905
- mainField: "id",
2906
- pageSize: 10
4115
+ return children;
2907
4116
  };
2908
- const useDocumentLayout = (model) => {
2909
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
4117
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4118
+ const { formatMessage } = reactIntl.useIntl();
4119
+ const { trackUsage } = strapiAdmin.useTracking();
4120
+ const { pathname } = reactRouterDom.useLocation();
2910
4121
  const [{ query }] = strapiAdmin.useQueryParams();
2911
- const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2912
- const { toggleNotification } = strapiAdmin.useNotification();
2913
- const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
2914
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2915
- const {
2916
- data,
2917
- isLoading: isLoadingConfigs,
2918
- error,
2919
- isFetching: isFetchingConfigs
2920
- } = useGetContentTypeConfigurationQuery(model);
2921
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2922
- React__namespace.useEffect(() => {
2923
- if (error) {
2924
- toggleNotification({
2925
- type: "danger",
2926
- message: formatAPIError(error)
2927
- });
2928
- }
2929
- }, [error, formatAPIError, toggleNotification]);
2930
- const editLayout = React__namespace.useMemo(
2931
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2932
- layout: [],
2933
- components: {},
2934
- metadatas: {},
2935
- options: {},
2936
- settings: DEFAULT_SETTINGS
2937
- },
2938
- [data, isLoading, schemas, schema, components]
2939
- );
2940
- const listLayout = React__namespace.useMemo(() => {
2941
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2942
- layout: [],
2943
- metadatas: {},
2944
- options: {},
2945
- settings: DEFAULT_SETTINGS
2946
- };
2947
- }, [data, isLoading, schemas, schema, components]);
2948
- const { layout: edit } = React__namespace.useMemo(
2949
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2950
- layout: editLayout,
2951
- query
2952
- }),
2953
- [editLayout, query, runHookWaterfall]
2954
- );
2955
- return {
2956
- error,
2957
- isLoading,
2958
- edit,
2959
- list: listLayout
2960
- };
2961
- };
2962
- const useDocLayout = () => {
2963
- const { model } = useDoc();
2964
- return useDocumentLayout(model);
2965
- };
2966
- const formatEditLayout = (data, {
2967
- schemas,
2968
- schema,
2969
- components
2970
- }) => {
2971
- let currentPanelIndex = 0;
2972
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2973
- data.contentType.layouts.edit,
2974
- schema?.attributes,
2975
- data.contentType.metadatas,
2976
- { configurations: data.components, schemas: components },
2977
- schemas
2978
- ).reduce((panels, row) => {
2979
- if (row.some((field) => field.type === "dynamiczone")) {
2980
- panels.push([row]);
2981
- currentPanelIndex += 2;
2982
- } else {
2983
- if (!panels[currentPanelIndex]) {
2984
- panels.push([]);
2985
- }
2986
- panels[currentPanelIndex].push(row);
2987
- }
2988
- return panels;
2989
- }, []);
2990
- const componentEditAttributes = Object.entries(data.components).reduce(
2991
- (acc, [uid, configuration]) => {
2992
- acc[uid] = {
2993
- layout: convertEditLayoutToFieldLayouts(
2994
- configuration.layouts.edit,
2995
- components[uid].attributes,
2996
- configuration.metadatas
2997
- ),
2998
- settings: {
2999
- ...configuration.settings,
3000
- icon: components[uid].info.icon,
3001
- displayName: components[uid].info.displayName
3002
- }
3003
- };
3004
- return acc;
3005
- },
3006
- {}
3007
- );
3008
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
3009
- (acc, [attribute, metadata]) => {
3010
- return {
3011
- ...acc,
3012
- [attribute]: metadata.edit
3013
- };
4122
+ const isModified = strapiAdmin.useForm("PreviewSidePanel", (state) => state.modified);
4123
+ const { data, error } = useGetPreviewUrlQuery({
4124
+ params: {
4125
+ contentType: model
3014
4126
  },
3015
- {}
3016
- );
3017
- return {
3018
- layout: panelledEditAttributes,
3019
- components: componentEditAttributes,
3020
- metadatas: editMetadatas,
3021
- settings: {
3022
- ...data.contentType.settings,
3023
- displayName: schema?.info.displayName
3024
- },
3025
- options: {
3026
- ...schema?.options,
3027
- ...schema?.pluginOptions,
3028
- ...data.contentType.options
4127
+ query: {
4128
+ documentId,
4129
+ locale: document?.locale,
4130
+ status: document?.status
3029
4131
  }
4132
+ });
4133
+ if (!data?.data?.url || error) {
4134
+ return null;
4135
+ }
4136
+ const trackNavigation = () => {
4137
+ const destinationPathname = pathname.replace(/\/$/, "") + "/preview";
4138
+ trackUsage("willNavigate", { from: pathname, to: destinationPathname });
3030
4139
  };
3031
- };
3032
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
3033
- return rows.map(
3034
- (row) => row.map((field) => {
3035
- const attribute = attributes[field.name];
3036
- if (!attribute) {
3037
- return null;
3038
- }
3039
- const { edit: metadata } = metadatas[field.name];
3040
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
3041
- return {
3042
- attribute,
3043
- disabled: !metadata.editable,
3044
- hint: metadata.description,
3045
- label: metadata.label ?? "",
3046
- name: field.name,
3047
- // @ts-expect-error – mainField does exist on the metadata for a relation.
3048
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
3049
- schemas,
3050
- components: components?.schemas ?? {}
3051
- }),
3052
- placeholder: metadata.placeholder ?? "",
3053
- required: attribute.required ?? false,
3054
- size: field.size,
3055
- unique: "unique" in attribute ? attribute.unique : false,
3056
- visible: metadata.visible ?? true,
3057
- type: attribute.type
3058
- };
3059
- }).filter((field) => field !== null)
3060
- );
3061
- };
3062
- const formatListLayout = (data, {
3063
- schemas,
3064
- schema,
3065
- components
3066
- }) => {
3067
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
3068
- (acc, [attribute, metadata]) => {
3069
- return {
3070
- ...acc,
3071
- [attribute]: metadata.list
3072
- };
3073
- },
3074
- {}
3075
- );
3076
- const listAttributes = convertListLayoutToFieldLayouts(
3077
- data.contentType.layouts.list,
3078
- schema?.attributes,
3079
- listMetadatas,
3080
- { configurations: data.components, schemas: components },
3081
- schemas
3082
- );
3083
4140
  return {
3084
- layout: listAttributes,
3085
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
3086
- metadatas: listMetadatas,
3087
- options: {
3088
- ...schema?.options,
3089
- ...schema?.pluginOptions,
3090
- ...data.contentType.options
3091
- }
4141
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4142
+ content: /* @__PURE__ */ jsxRuntime.jsx(
4143
+ ConditionalTooltip,
4144
+ {
4145
+ label: formatMessage({
4146
+ id: "content-manager.preview.panel.button-disabled-tooltip",
4147
+ defaultMessage: "Please save to open the preview"
4148
+ }),
4149
+ isShown: isModified,
4150
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { cursor: "not-allowed", width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
4151
+ designSystem.Button,
4152
+ {
4153
+ variant: "tertiary",
4154
+ tag: reactRouterDom.Link,
4155
+ to: { pathname: "preview", search: qs.stringify(query, { encode: false }) },
4156
+ onClick: trackNavigation,
4157
+ width: "100%",
4158
+ disabled: isModified,
4159
+ pointerEvents: isModified ? "none" : void 0,
4160
+ tabIndex: isModified ? -1 : void 0,
4161
+ children: formatMessage({
4162
+ id: "content-manager.preview.panel.button",
4163
+ defaultMessage: "Open preview"
4164
+ })
4165
+ }
4166
+ ) })
4167
+ }
4168
+ )
3092
4169
  };
3093
4170
  };
3094
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
3095
- return columns.map((name) => {
3096
- const attribute = attributes[name];
3097
- if (!attribute) {
3098
- return null;
3099
- }
3100
- const metadata = metadatas[name];
3101
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
3102
- return {
3103
- attribute,
3104
- label: metadata.label ?? "",
3105
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
3106
- schemas,
3107
- components: components?.schemas ?? {}
3108
- }),
3109
- name,
3110
- searchable: metadata.searchable ?? true,
3111
- sortable: metadata.sortable ?? true
3112
- };
3113
- }).filter((field) => field !== null);
4171
+ const previewAdmin = {
4172
+ bootstrap(app) {
4173
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4174
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4175
+ }
3114
4176
  };
3115
4177
  const index = {
3116
4178
  register(app) {
3117
4179
  const cm = new ContentManagerPlugin();
3118
4180
  app.addReducers({
3119
- [contentManagerApi.reducerPath]: contentManagerApi.reducer,
3120
4181
  [PLUGIN_ID]: reducer
3121
4182
  });
3122
- app.addMiddlewares([() => contentManagerApi.middleware]);
3123
4183
  app.addMenuLink({
3124
4184
  to: PLUGIN_ID,
3125
4185
  icon: Icons.Feather,
@@ -3128,14 +4188,32 @@ const index = {
3128
4188
  defaultMessage: "Content Manager"
3129
4189
  },
3130
4190
  permissions: [],
3131
- Component: () => Promise.resolve().then(() => require("./layout-B_SXLhqf.js")).then((mod) => ({ default: mod.Layout }))
4191
+ position: 1
4192
+ });
4193
+ app.router.addRoute({
4194
+ path: "content-manager/*",
4195
+ lazy: async () => {
4196
+ const { Layout } = await Promise.resolve().then(() => require("./layout-ivwIVPnV.js"));
4197
+ return {
4198
+ Component: Layout
4199
+ };
4200
+ },
4201
+ children: routes
3132
4202
  });
3133
4203
  app.registerPlugin(cm.config);
3134
4204
  },
4205
+ bootstrap(app) {
4206
+ if (typeof historyAdmin.bootstrap === "function") {
4207
+ historyAdmin.bootstrap(app);
4208
+ }
4209
+ if (typeof previewAdmin.bootstrap === "function") {
4210
+ previewAdmin.bootstrap(app);
4211
+ }
4212
+ },
3135
4213
  async registerTrads({ locales }) {
3136
4214
  const importedTrads = await Promise.all(
3137
4215
  locales.map((locale) => {
3138
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => Promise.resolve().then(() => require("./ar-BUUWXIYu.js")), "./translations/ca.json": () => Promise.resolve().then(() => require("./ca-Cmk45QO6.js")), "./translations/cs.json": () => Promise.resolve().then(() => require("./cs-CkJy6B2v.js")), "./translations/de.json": () => Promise.resolve().then(() => require("./de-CCEmbAah.js")), "./translations/en.json": () => Promise.resolve().then(() => require("./en-C-V1_90f.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-EUonQTon.js")), "./translations/eu.json": () => Promise.resolve().then(() => require("./eu-VDH-3ovk.js")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-B7kGGg3E.js")), "./translations/gu.json": () => Promise.resolve().then(() => require("./gu-BRmF601H.js")), "./translations/hi.json": () => Promise.resolve().then(() => require("./hi-CCJBptSq.js")), "./translations/hu.json": () => Promise.resolve().then(() => require("./hu-sNV_yLYy.js")), "./translations/id.json": () => Promise.resolve().then(() => require("./id-B5Ser98A.js")), "./translations/it.json": () => Promise.resolve().then(() => require("./it-DkBIs7vD.js")), "./translations/ja.json": () => Promise.resolve().then(() => require("./ja-CcFe8diO.js")), "./translations/ko.json": () => Promise.resolve().then(() => require("./ko-woFZPmLk.js")), "./translations/ml.json": () => Promise.resolve().then(() => require("./ml-C2W8N8k1.js")), "./translations/ms.json": () => Promise.resolve().then(() => require("./ms-BuFotyP_.js")), "./translations/nl.json": () => Promise.resolve().then(() => require("./nl-bbEOHChV.js")), "./translations/pl.json": () => Promise.resolve().then(() => require("./pl-uzwG-hk7.js")), "./translations/pt-BR.json": () => Promise.resolve().then(() => require("./pt-BR-BiOz37D9.js")), "./translations/pt.json": () => Promise.resolve().then(() => require("./pt-CeXQuq50.js")), "./translations/ru.json": () => Promise.resolve().then(() => require("./ru-BT3ybNny.js")), "./translations/sa.json": () => Promise.resolve().then(() => require("./sa-CcvkYInH.js")), "./translations/sk.json": () => Promise.resolve().then(() => require("./sk-CvY09Xjv.js")), "./translations/sv.json": () => Promise.resolve().then(() => require("./sv-MYDuzgvT.js")), "./translations/th.json": () => Promise.resolve().then(() => require("./th-D9_GfAjc.js")), "./translations/tr.json": () => Promise.resolve().then(() => require("./tr-D9UH-O_R.js")), "./translations/uk.json": () => Promise.resolve().then(() => require("./uk-C8EiqJY7.js")), "./translations/vi.json": () => Promise.resolve().then(() => require("./vi-CJlYDheJ.js")), "./translations/zh-Hans.json": () => Promise.resolve().then(() => require("./zh-Hans-9kOncHGw.js")), "./translations/zh.json": () => Promise.resolve().then(() => require("./zh-CQQfszqR.js")) }), `./translations/${locale}.json`).then(({ default: data }) => {
4216
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => Promise.resolve().then(() => require("./ar-BUUWXIYu.js")), "./translations/ca.json": () => Promise.resolve().then(() => require("./ca-Cmk45QO6.js")), "./translations/cs.json": () => Promise.resolve().then(() => require("./cs-CkJy6B2v.js")), "./translations/de.json": () => Promise.resolve().then(() => require("./de-CCEmbAah.js")), "./translations/en.json": () => Promise.resolve().then(() => require("./en-BK8Xyl5I.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-9K52xZIr.js")), "./translations/eu.json": () => Promise.resolve().then(() => require("./eu-VDH-3ovk.js")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-B2Kyv8Z9.js")), "./translations/gu.json": () => Promise.resolve().then(() => require("./gu-BRmF601H.js")), "./translations/hi.json": () => Promise.resolve().then(() => require("./hi-CCJBptSq.js")), "./translations/hu.json": () => Promise.resolve().then(() => require("./hu-sNV_yLYy.js")), "./translations/id.json": () => Promise.resolve().then(() => require("./id-B5Ser98A.js")), "./translations/it.json": () => Promise.resolve().then(() => require("./it-DkBIs7vD.js")), "./translations/ja.json": () => Promise.resolve().then(() => require("./ja-7sfIbjxE.js")), "./translations/ko.json": () => Promise.resolve().then(() => require("./ko-woFZPmLk.js")), "./translations/ml.json": () => Promise.resolve().then(() => require("./ml-C2W8N8k1.js")), "./translations/ms.json": () => Promise.resolve().then(() => require("./ms-BuFotyP_.js")), "./translations/nl.json": () => Promise.resolve().then(() => require("./nl-bbEOHChV.js")), "./translations/pl.json": () => Promise.resolve().then(() => require("./pl-uzwG-hk7.js")), "./translations/pt-BR.json": () => Promise.resolve().then(() => require("./pt-BR-BiOz37D9.js")), "./translations/pt.json": () => Promise.resolve().then(() => require("./pt-CeXQuq50.js")), "./translations/ru.json": () => Promise.resolve().then(() => require("./ru-BT3ybNny.js")), "./translations/sa.json": () => Promise.resolve().then(() => require("./sa-CcvkYInH.js")), "./translations/sk.json": () => Promise.resolve().then(() => require("./sk-CvY09Xjv.js")), "./translations/sv.json": () => Promise.resolve().then(() => require("./sv-MYDuzgvT.js")), "./translations/th.json": () => Promise.resolve().then(() => require("./th-D9_GfAjc.js")), "./translations/tr.json": () => Promise.resolve().then(() => require("./tr-D9UH-O_R.js")), "./translations/uk.json": () => Promise.resolve().then(() => require("./uk-C8EiqJY7.js")), "./translations/vi.json": () => Promise.resolve().then(() => require("./vi-CJlYDheJ.js")), "./translations/zh-Hans.json": () => Promise.resolve().then(() => require("./zh-Hans-9kOncHGw.js")), "./translations/zh.json": () => Promise.resolve().then(() => require("./zh-CQQfszqR.js")) }), `./translations/${locale}.json`, 3).then(({ default: data }) => {
3139
4217
  return {
3140
4218
  data: prefixPluginTranslations(data, PLUGIN_ID),
3141
4219
  locale
@@ -3152,6 +4230,8 @@ const index = {
3152
4230
  }
3153
4231
  };
3154
4232
  exports.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD;
4233
+ exports.BulkActionsRenderer = BulkActionsRenderer;
4234
+ exports.CLONE_PATH = CLONE_PATH;
3155
4235
  exports.COLLECTION_TYPES = COLLECTION_TYPES;
3156
4236
  exports.CREATOR_FIELDS = CREATOR_FIELDS;
3157
4237
  exports.DEFAULT_SETTINGS = DEFAULT_SETTINGS;
@@ -3178,8 +4258,8 @@ exports.getDisplayName = getDisplayName;
3178
4258
  exports.getMainField = getMainField;
3179
4259
  exports.getTranslation = getTranslation;
3180
4260
  exports.index = index;
3181
- exports.routes = routes;
3182
4261
  exports.setInitialData = setInitialData;
4262
+ exports.useContentManagerContext = useContentManagerContext;
3183
4263
  exports.useContentTypeSchema = useContentTypeSchema;
3184
4264
  exports.useDoc = useDoc;
3185
4265
  exports.useDocLayout = useDocLayout;
@@ -3191,5 +4271,6 @@ exports.useGetAllContentTypeSettingsQuery = useGetAllContentTypeSettingsQuery;
3191
4271
  exports.useGetAllDocumentsQuery = useGetAllDocumentsQuery;
3192
4272
  exports.useGetContentTypeConfigurationQuery = useGetContentTypeConfigurationQuery;
3193
4273
  exports.useGetInitialDataQuery = useGetInitialDataQuery;
4274
+ exports.useGetPreviewUrlQuery = useGetPreviewUrlQuery;
3194
4275
  exports.useUpdateContentTypeConfigurationMutation = useUpdateContentTypeConfigurationMutation;
3195
- //# sourceMappingURL=index-C6AH2hEl.js.map
4276
+ //# sourceMappingURL=index-Cnw4gqee.js.map