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