@strapi/content-manager 0.0.0-experimental.17b4116f461a49b8ce5386f7c8d79c511d40fb3b → 0.0.0-experimental.19d775295eb622de3e659110caf22fcd2cd5d10d

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 (264) 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-DjQBdcKF.mjs → ComponentConfigurationPage-B_99pmC0.mjs} +4 -4
  7. package/dist/_chunks/{ComponentConfigurationPage-DjQBdcKF.mjs.map → ComponentConfigurationPage-B_99pmC0.mjs.map} +1 -1
  8. package/dist/_chunks/{ComponentConfigurationPage-2iOVVhqV.js → ComponentConfigurationPage-NeMPjY5M.js} +5 -6
  9. package/dist/_chunks/{ComponentConfigurationPage-2iOVVhqV.js.map → ComponentConfigurationPage-NeMPjY5M.js.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-BoBb-DLH.mjs → EditConfigurationPage-B0kNlNoj.mjs} +4 -4
  15. package/dist/_chunks/{EditConfigurationPage-BoBb-DLH.mjs.map → EditConfigurationPage-B0kNlNoj.mjs.map} +1 -1
  16. package/dist/_chunks/{EditConfigurationPage-B7dw5_cS.js → EditConfigurationPage-n7_xHayb.js} +5 -6
  17. package/dist/_chunks/{EditConfigurationPage-B7dw5_cS.js.map → EditConfigurationPage-n7_xHayb.js.map} +1 -1
  18. package/dist/_chunks/EditViewPage-BT7Achc-.js +209 -0
  19. package/dist/_chunks/EditViewPage-BT7Achc-.js.map +1 -0
  20. package/dist/_chunks/EditViewPage-DYXZs4_2.mjs +191 -0
  21. package/dist/_chunks/EditViewPage-DYXZs4_2.mjs.map +1 -0
  22. package/dist/_chunks/FieldTypeIcon-CMlNO8PE.mjs.map +1 -1
  23. package/dist/_chunks/FieldTypeIcon-Dnwq_IRF.js.map +1 -1
  24. package/dist/_chunks/{Form-CQ67ZifP.js → Form-BRmk2Dp3.js} +68 -49
  25. package/dist/_chunks/Form-BRmk2Dp3.js.map +1 -0
  26. package/dist/_chunks/{Form-Jgh5hGTu.mjs → Form-D3paRF1F.mjs} +67 -46
  27. package/dist/_chunks/Form-D3paRF1F.mjs.map +1 -0
  28. package/dist/_chunks/{History-BLEnudTX.js → History-BQpDoOu8.js} +211 -148
  29. package/dist/_chunks/History-BQpDoOu8.js.map +1 -0
  30. package/dist/_chunks/{History-DKhZAPcK.mjs → History-CzQbTOwa.mjs} +204 -139
  31. package/dist/_chunks/History-CzQbTOwa.mjs.map +1 -0
  32. package/dist/_chunks/{Field-kVFO4ZKB.mjs → Input-ww3KFYZr.mjs} +1996 -1730
  33. package/dist/_chunks/Input-ww3KFYZr.mjs.map +1 -0
  34. package/dist/_chunks/{Field-kq1c2TF1.js → Input-yM6HnyQa.js} +2035 -1770
  35. package/dist/_chunks/Input-yM6HnyQa.js.map +1 -0
  36. package/dist/_chunks/{ListConfigurationPage-Zso_LUjn.js → ListConfigurationPage-B6NsS-0m.js} +72 -63
  37. package/dist/_chunks/ListConfigurationPage-B6NsS-0m.js.map +1 -0
  38. package/dist/_chunks/{ListConfigurationPage-nrXcxNYi.mjs → ListConfigurationPage-Bbw8w5cS.mjs} +68 -57
  39. package/dist/_chunks/ListConfigurationPage-Bbw8w5cS.mjs.map +1 -0
  40. package/dist/_chunks/{ListViewPage-ChhYmA-L.mjs → ListViewPage-DnOP55pM.mjs} +179 -160
  41. package/dist/_chunks/ListViewPage-DnOP55pM.mjs.map +1 -0
  42. package/dist/_chunks/{ListViewPage-DsaOakWQ.js → ListViewPage-Dt8OUTwO.js} +183 -165
  43. package/dist/_chunks/ListViewPage-Dt8OUTwO.js.map +1 -0
  44. package/dist/_chunks/{NoContentTypePage-BrdFcN33.mjs → NoContentTypePage-CXKXHNMa.mjs} +3 -3
  45. package/dist/_chunks/NoContentTypePage-CXKXHNMa.mjs.map +1 -0
  46. package/dist/_chunks/{NoContentTypePage-DPCuS9Y1.js → NoContentTypePage-Dgm-uj-6.js} +3 -3
  47. package/dist/_chunks/NoContentTypePage-Dgm-uj-6.js.map +1 -0
  48. package/dist/_chunks/{NoPermissionsPage-DdyOfdKb.js → NoPermissionsPage-CLbU5SOt.js} +2 -2
  49. package/dist/_chunks/{NoPermissionsPage-DdyOfdKb.js.map → NoPermissionsPage-CLbU5SOt.js.map} +1 -1
  50. package/dist/_chunks/{NoPermissionsPage-B9dqrtTy.mjs → NoPermissionsPage-kaj1rPiW.mjs} +2 -2
  51. package/dist/_chunks/{NoPermissionsPage-B9dqrtTy.mjs.map → NoPermissionsPage-kaj1rPiW.mjs.map} +1 -1
  52. package/dist/_chunks/Preview-Bieh13Ro.mjs +287 -0
  53. package/dist/_chunks/Preview-Bieh13Ro.mjs.map +1 -0
  54. package/dist/_chunks/Preview-CbXHXqBg.js +305 -0
  55. package/dist/_chunks/Preview-CbXHXqBg.js.map +1 -0
  56. package/dist/_chunks/{Relations-DjFiYd7-.mjs → Relations-7rWJcZ3_.mjs} +138 -94
  57. package/dist/_chunks/Relations-7rWJcZ3_.mjs.map +1 -0
  58. package/dist/_chunks/{Relations-CY8Isqdu.js → Relations-CvifV6Y6.js} +142 -100
  59. package/dist/_chunks/Relations-CvifV6Y6.js.map +1 -0
  60. package/dist/_chunks/{en-C-V1_90f.js → en-BR48D_RH.js} +45 -18
  61. package/dist/_chunks/{en-C-V1_90f.js.map → en-BR48D_RH.js.map} +1 -1
  62. package/dist/_chunks/{en-MBPul9Su.mjs → en-D65uIF6Y.mjs} +45 -18
  63. package/dist/_chunks/{en-MBPul9Su.mjs.map → en-D65uIF6Y.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-B7kGGg3E.js → fr-C43IbhA_.js} +16 -3
  69. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-C43IbhA_.js.map} +1 -1
  70. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr-DBseuRuB.mjs} +16 -3
  71. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr-DBseuRuB.mjs.map} +1 -1
  72. package/dist/_chunks/hooks-BAaaKPS_.js.map +1 -1
  73. package/dist/_chunks/{index-CAc9yTnx.mjs → index-BH2JnYpF.mjs} +2302 -995
  74. package/dist/_chunks/index-BH2JnYpF.mjs.map +1 -0
  75. package/dist/_chunks/{index-DNa1J4HE.js → index-DkJQjlak.js} +2273 -967
  76. package/dist/_chunks/index-DkJQjlak.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-BqtLA6Lb.js → layout-4BqLFW_b.js} +47 -32
  82. package/dist/_chunks/layout-4BqLFW_b.js.map +1 -0
  83. package/dist/_chunks/{layout-CXsHbc3E.mjs → layout-bbOlPwLA.mjs} +46 -28
  84. package/dist/_chunks/layout-bbOlPwLA.mjs.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-mMFEcZRq.mjs → relations-HsflnFpO.mjs} +6 -7
  90. package/dist/_chunks/relations-HsflnFpO.mjs.map +1 -0
  91. package/dist/_chunks/{relations-BHY_KDJ_.js → relations-Yc0Z6A20.js} +6 -7
  92. package/dist/_chunks/relations-Yc0Z6A20.js.map +1 -0
  93. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js → useDragAndDrop-BMtgCYzL.js} +5 -9
  94. package/dist/_chunks/useDragAndDrop-BMtgCYzL.js.map +1 -0
  95. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs → useDragAndDrop-DJ6jqvZN.mjs} +4 -7
  96. package/dist/_chunks/useDragAndDrop-DJ6jqvZN.mjs.map +1 -0
  97. package/dist/_chunks/usePrev-CZGy2Vjf.mjs +29 -0
  98. package/dist/_chunks/usePrev-CZGy2Vjf.mjs.map +1 -0
  99. package/dist/_chunks/usePrev-D5J_2fEu.js +28 -0
  100. package/dist/_chunks/usePrev-D5J_2fEu.js.map +1 -0
  101. package/dist/admin/index.js +4 -1
  102. package/dist/admin/index.js.map +1 -1
  103. package/dist/admin/index.mjs +10 -7
  104. package/dist/admin/src/components/ComponentIcon.d.ts +6 -3
  105. package/dist/admin/src/content-manager.d.ts +7 -5
  106. package/dist/admin/src/exports.d.ts +3 -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 +54 -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 +3 -3
  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/DynamicComponent.d.ts +4 -1
  126. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +4 -1
  127. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +30 -18
  128. package/dist/admin/src/pages/EditView/components/FormInputs/UID.d.ts +2 -2
  129. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +3 -49
  130. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/Field.d.ts +2 -2
  131. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  132. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +16 -53
  133. package/dist/admin/src/pages/EditView/components/FormLayout.d.ts +27 -0
  134. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  135. package/dist/admin/src/pages/EditView/components/InputRenderer.d.ts +2 -10
  136. package/dist/admin/src/pages/EditView/utils/data.d.ts +1 -0
  137. package/dist/admin/src/pages/ListView/components/BulkActions/Actions.d.ts +3 -30
  138. package/dist/admin/src/pages/ListView/components/BulkActions/ConfirmBulkActionDialog.d.ts +2 -2
  139. package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +9 -26
  140. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  141. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  142. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  143. package/dist/admin/src/preview/index.d.ts +4 -0
  144. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  145. package/dist/admin/src/preview/routes.d.ts +3 -0
  146. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  147. package/dist/admin/src/router.d.ts +1 -1
  148. package/dist/admin/src/services/api.d.ts +2 -3
  149. package/dist/admin/src/services/components.d.ts +2 -2
  150. package/dist/admin/src/services/contentTypes.d.ts +5 -5
  151. package/dist/admin/src/services/documents.d.ts +31 -20
  152. package/dist/admin/src/services/init.d.ts +2 -2
  153. package/dist/admin/src/services/relations.d.ts +3 -3
  154. package/dist/admin/src/services/uid.d.ts +3 -3
  155. package/dist/admin/src/utils/api.d.ts +4 -18
  156. package/dist/admin/src/utils/validation.d.ts +5 -7
  157. package/dist/server/index.js +959 -543
  158. package/dist/server/index.js.map +1 -1
  159. package/dist/server/index.mjs +960 -543
  160. package/dist/server/index.mjs.map +1 -1
  161. package/dist/server/src/bootstrap.d.ts.map +1 -1
  162. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  163. package/dist/server/src/controllers/index.d.ts.map +1 -1
  164. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  165. package/dist/server/src/controllers/single-types.d.ts.map +1 -1
  166. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  167. package/dist/server/src/controllers/utils/metadata.d.ts +23 -0
  168. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -0
  169. package/dist/server/src/controllers/validation/dimensions.d.ts +11 -0
  170. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -0
  171. package/dist/server/src/controllers/validation/index.d.ts +1 -1
  172. package/dist/server/src/history/controllers/history-version.d.ts +1 -1
  173. package/dist/server/src/history/controllers/history-version.d.ts.map +1 -1
  174. package/dist/server/src/history/services/history.d.ts +3 -3
  175. package/dist/server/src/history/services/history.d.ts.map +1 -1
  176. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  177. package/dist/server/src/history/services/utils.d.ts +8 -12
  178. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  179. package/dist/server/src/index.d.ts +21 -36
  180. package/dist/server/src/index.d.ts.map +1 -1
  181. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  182. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  183. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  184. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  185. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  186. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  187. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  188. package/dist/server/src/preview/index.d.ts +4 -0
  189. package/dist/server/src/preview/index.d.ts.map +1 -0
  190. package/dist/server/src/preview/routes/index.d.ts +8 -0
  191. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  192. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  193. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  194. package/dist/server/src/preview/services/index.d.ts +16 -0
  195. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  196. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  197. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  198. package/dist/server/src/preview/services/preview.d.ts +12 -0
  199. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  200. package/dist/server/src/preview/utils.d.ts +19 -0
  201. package/dist/server/src/preview/utils.d.ts.map +1 -0
  202. package/dist/server/src/register.d.ts.map +1 -1
  203. package/dist/server/src/routes/index.d.ts.map +1 -1
  204. package/dist/server/src/services/document-manager.d.ts +11 -6
  205. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  206. package/dist/server/src/services/document-metadata.d.ts +16 -35
  207. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  208. package/dist/server/src/services/index.d.ts +21 -36
  209. package/dist/server/src/services/index.d.ts.map +1 -1
  210. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  211. package/dist/server/src/services/utils/populate.d.ts +8 -1
  212. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  213. package/dist/server/src/utils/index.d.ts +2 -0
  214. package/dist/server/src/utils/index.d.ts.map +1 -1
  215. package/dist/shared/contracts/collection-types.d.ts +17 -7
  216. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  217. package/dist/shared/contracts/index.d.ts +1 -0
  218. package/dist/shared/contracts/index.d.ts.map +1 -1
  219. package/dist/shared/contracts/preview.d.ts +27 -0
  220. package/dist/shared/contracts/preview.d.ts.map +1 -0
  221. package/dist/shared/contracts/relations.d.ts +2 -2
  222. package/dist/shared/contracts/relations.d.ts.map +1 -1
  223. package/dist/shared/index.js +4 -0
  224. package/dist/shared/index.js.map +1 -1
  225. package/dist/shared/index.mjs +4 -0
  226. package/dist/shared/index.mjs.map +1 -1
  227. package/package.json +22 -22
  228. package/dist/_chunks/CardDragPreview-DSVYodBX.js.map +0 -1
  229. package/dist/_chunks/CardDragPreview-ikSG4M46.mjs.map +0 -1
  230. package/dist/_chunks/ComponentIcon-BBQsYCVn.js.map +0 -1
  231. package/dist/_chunks/ComponentIcon-BOFnK76n.mjs.map +0 -1
  232. package/dist/_chunks/EditViewPage-KRG56aCq.js +0 -224
  233. package/dist/_chunks/EditViewPage-KRG56aCq.js.map +0 -1
  234. package/dist/_chunks/EditViewPage-aUnqL-63.mjs +0 -203
  235. package/dist/_chunks/EditViewPage-aUnqL-63.mjs.map +0 -1
  236. package/dist/_chunks/Field-kVFO4ZKB.mjs.map +0 -1
  237. package/dist/_chunks/Field-kq1c2TF1.js.map +0 -1
  238. package/dist/_chunks/Form-CQ67ZifP.js.map +0 -1
  239. package/dist/_chunks/Form-Jgh5hGTu.mjs.map +0 -1
  240. package/dist/_chunks/History-BLEnudTX.js.map +0 -1
  241. package/dist/_chunks/History-DKhZAPcK.mjs.map +0 -1
  242. package/dist/_chunks/ListConfigurationPage-Zso_LUjn.js.map +0 -1
  243. package/dist/_chunks/ListConfigurationPage-nrXcxNYi.mjs.map +0 -1
  244. package/dist/_chunks/ListViewPage-ChhYmA-L.mjs.map +0 -1
  245. package/dist/_chunks/ListViewPage-DsaOakWQ.js.map +0 -1
  246. package/dist/_chunks/NoContentTypePage-BrdFcN33.mjs.map +0 -1
  247. package/dist/_chunks/NoContentTypePage-DPCuS9Y1.js.map +0 -1
  248. package/dist/_chunks/Relations-CY8Isqdu.js.map +0 -1
  249. package/dist/_chunks/Relations-DjFiYd7-.mjs.map +0 -1
  250. package/dist/_chunks/index-CAc9yTnx.mjs.map +0 -1
  251. package/dist/_chunks/index-DNa1J4HE.js.map +0 -1
  252. package/dist/_chunks/layout-BqtLA6Lb.js.map +0 -1
  253. package/dist/_chunks/layout-CXsHbc3E.mjs.map +0 -1
  254. package/dist/_chunks/relations-BHY_KDJ_.js.map +0 -1
  255. package/dist/_chunks/relations-mMFEcZRq.mjs.map +0 -1
  256. package/dist/_chunks/urls-CbOsUOoW.mjs +0 -7
  257. package/dist/_chunks/urls-CbOsUOoW.mjs.map +0 -1
  258. package/dist/_chunks/urls-DzZya_gm.js +0 -6
  259. package/dist/_chunks/urls-DzZya_gm.js.map +0 -1
  260. package/dist/_chunks/useDragAndDrop-DdHgKsqq.mjs.map +0 -1
  261. package/dist/_chunks/useDragAndDrop-J0TUUbR6.js.map +0 -1
  262. package/dist/server/src/controllers/utils/dimensions.d.ts +0 -5
  263. package/dist/server/src/controllers/utils/dimensions.d.ts.map +0 -1
  264. package/strapi-server.js +0 -3
@@ -1,27 +1,34 @@
1
- import { ClockCounterClockwise, CrossCircle, More, WarningCircle, Cog, Pencil, Trash, ChevronRight, Duplicate, Feather } from "@strapi/icons";
1
+ import { More, Cross, WarningCircle, ListPlus, Pencil, Trash, Check, CheckCircle, ArrowsCounterClockwise, CrossCircle, 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, RawTable, Loader, Tbody, Tr, Td, 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 { generateNKeysBetween } from "fractional-indexing";
15
14
  import pipe from "lodash/fp/pipe";
15
+ import { stringify } from "qs";
16
16
  import { intervalToDuration, isPast } from "date-fns";
17
17
  import { createSlice, combineReducers } from "@reduxjs/toolkit";
18
- const __variableDynamicImportRuntimeHelper = (glob, path) => {
18
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
19
19
  const v = glob[path];
20
20
  if (v) {
21
21
  return typeof v === "function" ? v() : Promise.resolve(v);
22
22
  }
23
23
  return new Promise((_, reject) => {
24
- (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(reject.bind(null, new Error("Unknown variable dynamic import: " + path)));
24
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
25
+ reject.bind(
26
+ null,
27
+ new Error(
28
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
29
+ )
30
+ )
31
+ );
25
32
  });
26
33
  };
27
34
  const PLUGIN_ID = "content-manager";
@@ -51,42 +58,6 @@ const useInjectionZone = (area) => {
51
58
  const [page, position] = area.split(".");
52
59
  return contentManagerPlugin.getInjectedComponents(page, position);
53
60
  };
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
61
  const ID = "id";
91
62
  const CREATED_BY_ATTRIBUTE_NAME = "createdBy";
92
63
  const UPDATED_BY_ATTRIBUTE_NAME = "updatedBy";
@@ -138,6 +109,7 @@ const DocumentRBAC = ({ children, permissions }) => {
138
109
  if (!slug) {
139
110
  throw new Error("Cannot find the slug param in the URL");
140
111
  }
112
+ const [{ rawQuery }] = useQueryParams();
141
113
  const userPermissions = useAuth("DocumentRBAC", (state) => state.permissions);
142
114
  const contentTypePermissions = React.useMemo(() => {
143
115
  const contentTypePermissions2 = userPermissions.filter(
@@ -148,7 +120,14 @@ const DocumentRBAC = ({ children, permissions }) => {
148
120
  return { ...acc, [action]: [permission] };
149
121
  }, {});
150
122
  }, [slug, userPermissions]);
151
- const { isLoading, allowedActions } = useRBAC(contentTypePermissions, permissions ?? void 0);
123
+ const { isLoading, allowedActions } = useRBAC(
124
+ contentTypePermissions,
125
+ permissions ?? void 0,
126
+ // TODO: useRBAC context should be typed and built differently
127
+ // We are passing raw query as context to the hook so that it can
128
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
129
+ rawQuery
130
+ );
152
131
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
153
132
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
154
133
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -157,9 +136,8 @@ const DocumentRBAC = ({ children, permissions }) => {
157
136
  const name = removeNumericalStrings(fieldName.split("."));
158
137
  const componentFieldNames = fieldsUserCanAction.filter((field) => field.split(".").length > 1);
159
138
  if (fieldType === "component") {
160
- const componentOrDynamicZoneFields = componentFieldNames.map((field) => field.split("."));
161
- return componentOrDynamicZoneFields.some((field) => {
162
- return field.includes(fieldName);
139
+ return componentFieldNames.some((field) => {
140
+ return field.includes(name.join("."));
163
141
  });
164
142
  }
165
143
  if (name.length > 1) {
@@ -189,89 +167,128 @@ const extractAndDedupeFields = (permissions = []) => permissions.flatMap((permis
189
167
  (field, index2, arr) => arr.indexOf(field) === index2 && typeof field === "string"
190
168
  );
191
169
  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 };
170
+ const BLOCK_LIST_ATTRIBUTE_KEYS = ["__component", "__temp_key__"];
171
+ const traverseData = (predicate, transform) => (schema, components = {}) => (data = {}) => {
172
+ const traverse = (datum, attributes) => {
173
+ return Object.entries(datum).reduce((acc, [key, value]) => {
174
+ const attribute = attributes[key];
175
+ if (BLOCK_LIST_ATTRIBUTE_KEYS.includes(key) || value === null || value === void 0) {
176
+ acc[key] = value;
177
+ return acc;
178
+ }
179
+ if (attribute.type === "component") {
180
+ if (attribute.repeatable) {
181
+ const componentValue = predicate(attribute, value) ? transform(value, attribute) : value;
182
+ acc[key] = componentValue.map(
183
+ (componentData) => traverse(componentData, components[attribute.component]?.attributes ?? {})
184
+ );
185
+ } else {
186
+ const componentValue = predicate(attribute, value) ? transform(value, attribute) : value;
187
+ acc[key] = traverse(componentValue, components[attribute.component]?.attributes ?? {});
188
+ }
189
+ } else if (attribute.type === "dynamiczone") {
190
+ const dynamicZoneValue = predicate(attribute, value) ? transform(value, attribute) : value;
191
+ acc[key] = dynamicZoneValue.map(
192
+ (componentData) => traverse(componentData, components[componentData.__component]?.attributes ?? {})
193
+ );
194
+ } else if (predicate(attribute, value)) {
195
+ acc[key] = transform(value, attribute);
234
196
  } 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
- };
197
+ acc[key] = value;
244
198
  }
199
+ return acc;
200
+ }, {});
201
+ };
202
+ return traverse(data, schema.attributes);
203
+ };
204
+ const removeProhibitedFields = (prohibitedFields) => traverseData(
205
+ (attribute) => prohibitedFields.includes(attribute.type),
206
+ () => ""
207
+ );
208
+ const prepareRelations = traverseData(
209
+ (attribute) => attribute.type === "relation",
210
+ () => ({
211
+ connect: [],
212
+ disconnect: []
213
+ })
214
+ );
215
+ const prepareTempKeys = traverseData(
216
+ (attribute) => attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone",
217
+ (data) => {
218
+ if (Array.isArray(data) && data.length > 0) {
219
+ const keys = generateNKeysBetween(void 0, void 0, data.length);
220
+ return data.map((datum, index2) => ({
221
+ ...datum,
222
+ __temp_key__: keys[index2]
223
+ }));
245
224
  }
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
- };
225
+ return data;
255
226
  }
227
+ );
228
+ const removeFieldsThatDontExistOnSchema = (schema) => (data) => {
229
+ const schemaKeys = Object.keys(schema.attributes);
230
+ const dataKeys = Object.keys(data);
231
+ const keysToRemove = dataKeys.filter((key) => !schemaKeys.includes(key));
232
+ const revisedData = [...keysToRemove, ...DOCUMENT_META_FIELDS].reduce((acc, key) => {
233
+ delete acc[key];
234
+ return acc;
235
+ }, structuredClone(data));
236
+ return revisedData;
256
237
  };
257
- const isBaseQueryError = (error) => {
258
- return error.name !== void 0;
238
+ const removeNullValues = (data) => {
239
+ return Object.entries(data).reduce((acc, [key, value]) => {
240
+ if (value === null) {
241
+ return acc;
242
+ }
243
+ acc[key] = value;
244
+ return acc;
245
+ }, {});
246
+ };
247
+ const transformDocument = (schema, components = {}) => (document) => {
248
+ const transformations = pipe(
249
+ removeFieldsThatDontExistOnSchema(schema),
250
+ removeProhibitedFields(["password"])(schema, components),
251
+ removeNullValues,
252
+ prepareRelations(schema, components),
253
+ prepareTempKeys(schema, components)
254
+ );
255
+ return transformations(document);
256
+ };
257
+ const createDefaultForm = (contentType, components = {}) => {
258
+ const traverseSchema = (attributes) => {
259
+ return Object.entries(attributes).reduce((acc, [key, attribute]) => {
260
+ if ("default" in attribute) {
261
+ acc[key] = attribute.default;
262
+ } else if (attribute.type === "component" && attribute.required) {
263
+ const defaultComponentForm = traverseSchema(components[attribute.component].attributes);
264
+ if (attribute.repeatable) {
265
+ acc[key] = attribute.min ? [...Array(attribute.min).fill(defaultComponentForm)] : [];
266
+ } else {
267
+ acc[key] = defaultComponentForm;
268
+ }
269
+ } else if (attribute.type === "dynamiczone" && attribute.required) {
270
+ acc[key] = [];
271
+ }
272
+ return acc;
273
+ }, {});
274
+ };
275
+ return traverseSchema(contentType.attributes);
259
276
  };
260
- const contentManagerApi = createApi({
261
- reducerPath: "contentManagerApi",
262
- baseQuery: axiosBaseQuery(),
263
- tagTypes: [
277
+ const contentManagerApi = adminApi.enhanceEndpoints({
278
+ addTagTypes: [
264
279
  "ComponentConfiguration",
265
280
  "ContentTypesConfiguration",
266
281
  "ContentTypeSettings",
267
282
  "Document",
268
283
  "InitialData",
269
284
  "HistoryVersion",
270
- "Relations"
271
- ],
272
- endpoints: () => ({})
285
+ "Relations",
286
+ "UidAvailability",
287
+ "RecentDocumentList"
288
+ ]
273
289
  });
274
290
  const documentApi = contentManagerApi.injectEndpoints({
291
+ overrideExisting: true,
275
292
  endpoints: (builder) => ({
276
293
  autoCloneDocument: builder.mutation({
277
294
  query: ({ model, sourceId, query }) => ({
@@ -281,7 +298,12 @@ const documentApi = contentManagerApi.injectEndpoints({
281
298
  params: query
282
299
  }
283
300
  }),
284
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
301
+ invalidatesTags: (_result, error, { model }) => {
302
+ if (error) {
303
+ return [];
304
+ }
305
+ return [{ type: "Document", id: `${model}_LIST` }, "RecentDocumentList"];
306
+ }
285
307
  }),
286
308
  cloneDocument: builder.mutation({
287
309
  query: ({ model, sourceId, data, params }) => ({
@@ -292,7 +314,11 @@ const documentApi = contentManagerApi.injectEndpoints({
292
314
  params
293
315
  }
294
316
  }),
295
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
317
+ invalidatesTags: (_result, _error, { model }) => [
318
+ { type: "Document", id: `${model}_LIST` },
319
+ { type: "UidAvailability", id: model },
320
+ "RecentDocumentList"
321
+ ]
296
322
  }),
297
323
  /**
298
324
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -309,8 +335,22 @@ const documentApi = contentManagerApi.injectEndpoints({
309
335
  }),
310
336
  invalidatesTags: (result, _error, { model }) => [
311
337
  { type: "Document", id: `${model}_LIST` },
312
- "Relations"
313
- ]
338
+ "Relations",
339
+ { type: "UidAvailability", id: model },
340
+ "RecentDocumentList"
341
+ ],
342
+ transformResponse: (response, meta, arg) => {
343
+ if (!("data" in response) && arg.model === "plugin::users-permissions.user") {
344
+ return {
345
+ data: response,
346
+ meta: {
347
+ availableStatus: [],
348
+ availableLocales: []
349
+ }
350
+ };
351
+ }
352
+ return response;
353
+ }
314
354
  }),
315
355
  deleteDocument: builder.mutation({
316
356
  query: ({ collectionType, model, documentId, params }) => ({
@@ -321,16 +361,23 @@ const documentApi = contentManagerApi.injectEndpoints({
321
361
  }
322
362
  }),
323
363
  invalidatesTags: (_result, _error, { collectionType, model }) => [
324
- { type: "Document", id: collectionType !== SINGLE_TYPES ? `${model}_LIST` : model }
364
+ { type: "Document", id: collectionType !== SINGLE_TYPES ? `${model}_LIST` : model },
365
+ "RecentDocumentList"
325
366
  ]
326
367
  }),
327
368
  deleteManyDocuments: builder.mutation({
328
- query: ({ model, ...body }) => ({
369
+ query: ({ model, params, ...body }) => ({
329
370
  url: `/content-manager/collection-types/${model}/actions/bulkDelete`,
330
371
  method: "POST",
331
- data: body
372
+ data: body,
373
+ config: {
374
+ params
375
+ }
332
376
  }),
333
- invalidatesTags: (_res, _error, { model, documentIds }) => documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` }))
377
+ invalidatesTags: (_res, _error, { model }) => [
378
+ { type: "Document", id: `${model}_LIST` },
379
+ "RecentDocumentList"
380
+ ]
334
381
  }),
335
382
  discardDocument: builder.mutation({
336
383
  query: ({ collectionType, model, documentId, params }) => ({
@@ -347,7 +394,9 @@ const documentApi = contentManagerApi.injectEndpoints({
347
394
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
348
395
  },
349
396
  { type: "Document", id: `${model}_LIST` },
350
- "Relations"
397
+ "Relations",
398
+ { type: "UidAvailability", id: model },
399
+ "RecentDocumentList"
351
400
  ];
352
401
  }
353
402
  }),
@@ -360,11 +409,12 @@ const documentApi = contentManagerApi.injectEndpoints({
360
409
  url: `/content-manager/collection-types/${model}`,
361
410
  method: "GET",
362
411
  config: {
363
- params
412
+ params: stringify(params, { encode: true })
364
413
  }
365
414
  }),
366
415
  providesTags: (result, _error, arg) => {
367
416
  return [
417
+ { type: "Document", id: `ALL_LIST` },
368
418
  { type: "Document", id: `${arg.model}_LIST` },
369
419
  ...result?.results.map(({ documentId }) => ({
370
420
  type: "Document",
@@ -403,6 +453,11 @@ const documentApi = contentManagerApi.injectEndpoints({
403
453
  {
404
454
  type: "Document",
405
455
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
456
+ },
457
+ // Make it easy to invalidate all individual documents queries for a model
458
+ {
459
+ type: "Document",
460
+ id: `${model}_ALL_ITEMS`
406
461
  }
407
462
  ];
408
463
  }
@@ -436,15 +491,19 @@ const documentApi = contentManagerApi.injectEndpoints({
436
491
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
437
492
  },
438
493
  { type: "Document", id: `${model}_LIST` },
439
- "Relations"
494
+ "Relations",
495
+ "RecentDocumentList"
440
496
  ];
441
497
  }
442
498
  }),
443
499
  publishManyDocuments: builder.mutation({
444
- query: ({ model, ...body }) => ({
500
+ query: ({ model, params, ...body }) => ({
445
501
  url: `/content-manager/collection-types/${model}/actions/bulkPublish`,
446
502
  method: "POST",
447
- data: body
503
+ data: body,
504
+ config: {
505
+ params
506
+ }
448
507
  }),
449
508
  invalidatesTags: (_res, _error, { model, documentIds }) => documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` }))
450
509
  }),
@@ -463,8 +522,23 @@ const documentApi = contentManagerApi.injectEndpoints({
463
522
  type: "Document",
464
523
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
465
524
  },
466
- "Relations"
525
+ "Relations",
526
+ { type: "UidAvailability", id: model },
527
+ "RecentDocumentList",
528
+ "RecentDocumentList"
467
529
  ];
530
+ },
531
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
532
+ const patchResult = dispatch(
533
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
534
+ Object.assign(draft.data, data);
535
+ })
536
+ );
537
+ try {
538
+ await queryFulfilled;
539
+ } catch {
540
+ patchResult.undo();
541
+ }
468
542
  }
469
543
  }),
470
544
  unpublishDocument: builder.mutation({
@@ -481,17 +555,24 @@ const documentApi = contentManagerApi.injectEndpoints({
481
555
  {
482
556
  type: "Document",
483
557
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
484
- }
558
+ },
559
+ "RecentDocumentList"
485
560
  ];
486
561
  }
487
562
  }),
488
563
  unpublishManyDocuments: builder.mutation({
489
- query: ({ model, ...body }) => ({
564
+ query: ({ model, params, ...body }) => ({
490
565
  url: `/content-manager/collection-types/${model}/actions/bulkUnpublish`,
491
566
  method: "POST",
492
- data: body
567
+ data: body,
568
+ config: {
569
+ params
570
+ }
493
571
  }),
494
- invalidatesTags: (_res, _error, { model, documentIds }) => documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` }))
572
+ invalidatesTags: (_res, _error, { model, documentIds }) => [
573
+ ...documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` })),
574
+ "RecentDocumentList"
575
+ ]
495
576
  })
496
577
  })
497
578
  });
@@ -513,20 +594,53 @@ const {
513
594
  useUnpublishDocumentMutation,
514
595
  useUnpublishManyDocumentsMutation
515
596
  } = documentApi;
516
- const createYupSchema = (attributes = {}, components = {}) => {
597
+ const buildValidParams = (query) => {
598
+ if (!query) return query;
599
+ const { plugins: _, ...validQueryParams } = {
600
+ ...query,
601
+ ...Object.values(query?.plugins ?? {}).reduce(
602
+ (acc, current) => Object.assign(acc, current),
603
+ {}
604
+ )
605
+ };
606
+ return validQueryParams;
607
+ };
608
+ const isBaseQueryError = (error) => {
609
+ return error.name !== void 0;
610
+ };
611
+ const arrayValidator = (attribute, options) => ({
612
+ message: translatedErrors.required,
613
+ test(value) {
614
+ if (options.status === "draft") {
615
+ return true;
616
+ }
617
+ if (!attribute.required) {
618
+ return true;
619
+ }
620
+ if (!value) {
621
+ return false;
622
+ }
623
+ if (Array.isArray(value) && value.length === 0) {
624
+ return false;
625
+ }
626
+ return true;
627
+ }
628
+ });
629
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
517
630
  const createModelSchema = (attributes2) => yup.object().shape(
518
631
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
519
632
  if (DOCUMENT_META_FIELDS.includes(name)) {
520
633
  return acc;
521
634
  }
522
635
  const validations = [
636
+ addNullableValidation,
523
637
  addRequiredValidation,
524
638
  addMinLengthValidation,
525
639
  addMaxLengthValidation,
526
640
  addMinValidation,
527
641
  addMaxValidation,
528
642
  addRegexValidation
529
- ].map((fn) => fn(attribute));
643
+ ].map((fn) => fn(attribute, options));
530
644
  const transformSchema = pipe(...validations);
531
645
  switch (attribute.type) {
532
646
  case "component": {
@@ -536,12 +650,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
536
650
  ...acc,
537
651
  [name]: transformSchema(
538
652
  yup.array().of(createModelSchema(attributes3).nullable(false))
539
- )
653
+ ).test(arrayValidator(attribute, options))
540
654
  };
541
655
  } else {
542
656
  return {
543
657
  ...acc,
544
- [name]: transformSchema(createModelSchema(attributes3))
658
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
545
659
  };
546
660
  }
547
661
  }
@@ -552,24 +666,42 @@ const createYupSchema = (attributes = {}, components = {}) => {
552
666
  yup.array().of(
553
667
  yup.lazy(
554
668
  (data) => {
555
- const { attributes: attributes3 } = components[data.__component];
556
- return yup.object().shape({
669
+ const attributes3 = components?.[data?.__component]?.attributes;
670
+ const validation = yup.object().shape({
557
671
  __component: yup.string().required().oneOf(Object.keys(components))
558
- }).nullable(false).concat(createModelSchema(attributes3));
672
+ }).nullable(false);
673
+ if (!attributes3) {
674
+ return validation;
675
+ }
676
+ return validation.concat(createModelSchema(attributes3));
559
677
  }
560
678
  )
561
679
  )
562
- )
680
+ ).test(arrayValidator(attribute, options))
563
681
  };
564
682
  case "relation":
565
683
  return {
566
684
  ...acc,
567
685
  [name]: transformSchema(
568
- yup.array().of(
569
- yup.object().shape({
570
- id: yup.string().required()
571
- })
572
- )
686
+ yup.lazy((value) => {
687
+ if (!value) {
688
+ return yup.mixed().nullable(true);
689
+ } else if (Array.isArray(value)) {
690
+ return yup.array().of(
691
+ yup.object().shape({
692
+ id: yup.number().required()
693
+ })
694
+ );
695
+ } else if (typeof value === "object") {
696
+ return yup.object();
697
+ } else {
698
+ return yup.mixed().test(
699
+ "type-error",
700
+ "Relation values must be either null, an array of objects with {id} or an object.",
701
+ () => false
702
+ );
703
+ }
704
+ })
573
705
  )
574
706
  };
575
707
  default:
@@ -609,6 +741,14 @@ const createAttributeSchema = (attribute) => {
609
741
  if (!value || typeof value === "string" && value.length === 0) {
610
742
  return true;
611
743
  }
744
+ if (typeof value === "object") {
745
+ try {
746
+ JSON.stringify(value);
747
+ return true;
748
+ } catch (err) {
749
+ return false;
750
+ }
751
+ }
612
752
  try {
613
753
  JSON.parse(value);
614
754
  return true;
@@ -627,16 +767,30 @@ const createAttributeSchema = (attribute) => {
627
767
  return yup.mixed();
628
768
  }
629
769
  };
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
- });
770
+ const nullableSchema = (schema) => {
771
+ return schema?.nullable ? schema.nullable() : (
772
+ // In some cases '.nullable' will not be available on the schema.
773
+ // e.g. when the schema has been built using yup.lazy (e.g. for relations).
774
+ // In these cases we should just return the schema as it is.
775
+ schema
776
+ );
777
+ };
778
+ const addNullableValidation = () => (schema) => {
779
+ return nullableSchema(schema);
780
+ };
781
+ const addRequiredValidation = (attribute, options) => (schema) => {
782
+ if (options.status === "draft" || !attribute.required) {
783
+ return schema;
636
784
  }
637
- return schema.nullable();
785
+ if (attribute.required && "required" in schema) {
786
+ return schema.required(translatedErrors.required);
787
+ }
788
+ return schema;
638
789
  };
639
- const addMinLengthValidation = (attribute) => (schema) => {
790
+ const addMinLengthValidation = (attribute, options) => (schema) => {
791
+ if (options.status === "draft") {
792
+ return schema;
793
+ }
640
794
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
641
795
  return schema.min(attribute.minLength, {
642
796
  ...translatedErrors.minLength,
@@ -658,10 +812,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
658
812
  }
659
813
  return schema;
660
814
  };
661
- const addMinValidation = (attribute) => (schema) => {
662
- if ("min" in attribute) {
815
+ const addMinValidation = (attribute, options) => (schema) => {
816
+ if (options.status === "draft") {
817
+ return schema;
818
+ }
819
+ if ("min" in attribute && "min" in schema) {
663
820
  const min = toInteger(attribute.min);
664
- if ("min" in schema && min) {
821
+ if (min) {
665
822
  return schema.min(min, {
666
823
  ...translatedErrors.min,
667
824
  values: {
@@ -706,24 +863,6 @@ const addRegexValidation = (attribute) => (schema) => {
706
863
  }
707
864
  return schema;
708
865
  };
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
866
  const initApi = contentManagerApi.injectEndpoints({
728
867
  endpoints: (builder) => ({
729
868
  getInitialData: builder.query({
@@ -737,27 +876,20 @@ const { useGetInitialDataQuery } = initApi;
737
876
  const useContentTypeSchema = (model) => {
738
877
  const { toggleNotification } = useNotification();
739
878
  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
- });
879
+ const { data, error, isLoading, isFetching } = useGetInitialDataQuery(void 0);
880
+ const { components, contentType, contentTypes } = React.useMemo(() => {
881
+ const contentType2 = data?.contentTypes.find((ct) => ct.uid === model);
882
+ const componentsByKey = data?.components.reduce((acc, component) => {
883
+ acc[component.uid] = component;
884
+ return acc;
885
+ }, {});
886
+ const components2 = extractContentTypeComponents(contentType2?.attributes, componentsByKey);
887
+ return {
888
+ components: Object.keys(components2).length === 0 ? void 0 : components2,
889
+ contentType: contentType2,
890
+ contentTypes: data?.contentTypes ?? []
891
+ };
892
+ }, [model, data]);
761
893
  React.useEffect(() => {
762
894
  if (error) {
763
895
  toggleNotification({
@@ -804,61 +936,404 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
804
936
  }, {});
805
937
  return componentsByKey;
806
938
  };
807
- const useDocument = (args, opts) => {
808
- const { toggleNotification } = useNotification();
809
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
810
- 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(() => {
818
- if (error) {
819
- toggleNotification({
820
- type: "danger",
821
- message: formatAPIError(error)
822
- });
823
- }
824
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
825
- const validationSchema = React.useMemo(() => {
826
- if (!schema) {
827
- return null;
828
- }
829
- return createYupSchema(schema.attributes, components);
830
- }, [schema, components]);
831
- const validate = React.useCallback(
832
- (document) => {
833
- if (!validationSchema) {
834
- throw new Error(
835
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
836
- );
837
- }
838
- try {
839
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
840
- return null;
841
- } catch (error2) {
842
- if (error2 instanceof ValidationError) {
843
- return getInnerErrors(error2);
844
- }
845
- throw error2;
846
- }
847
- },
848
- [validationSchema]
849
- );
850
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
851
- return {
852
- components,
853
- document: data?.data,
854
- meta: data?.meta,
855
- isLoading,
856
- schema,
857
- validate
858
- };
939
+ const HOOKS = {
940
+ /**
941
+ * Hook that allows to mutate the displayed headers of the list view table
942
+ * @constant
943
+ * @type {string}
944
+ */
945
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
946
+ /**
947
+ * Hook that allows to mutate the CM's collection types links pre-set filters
948
+ * @constant
949
+ * @type {string}
950
+ */
951
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
952
+ /**
953
+ * Hook that allows to mutate the CM's edit view layout
954
+ * @constant
955
+ * @type {string}
956
+ */
957
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
958
+ /**
959
+ * Hook that allows to mutate the CM's single types links pre-set filters
960
+ * @constant
961
+ * @type {string}
962
+ */
963
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
859
964
  };
860
- const useDoc = () => {
861
- const { id, slug, collectionType, origin } = useParams();
965
+ const contentTypesApi = contentManagerApi.injectEndpoints({
966
+ endpoints: (builder) => ({
967
+ getContentTypeConfiguration: builder.query({
968
+ query: (uid) => ({
969
+ url: `/content-manager/content-types/${uid}/configuration`,
970
+ method: "GET"
971
+ }),
972
+ transformResponse: (response) => response.data,
973
+ providesTags: (_result, _error, uid) => [
974
+ { type: "ContentTypesConfiguration", id: uid },
975
+ { type: "ContentTypeSettings", id: "LIST" }
976
+ ]
977
+ }),
978
+ getAllContentTypeSettings: builder.query({
979
+ query: () => "/content-manager/content-types-settings",
980
+ transformResponse: (response) => response.data,
981
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
982
+ }),
983
+ updateContentTypeConfiguration: builder.mutation({
984
+ query: ({ uid, ...body }) => ({
985
+ url: `/content-manager/content-types/${uid}/configuration`,
986
+ method: "PUT",
987
+ data: body
988
+ }),
989
+ transformResponse: (response) => response.data,
990
+ invalidatesTags: (_result, _error, { uid }) => [
991
+ { type: "ContentTypesConfiguration", id: uid },
992
+ { type: "ContentTypeSettings", id: "LIST" },
993
+ // Is this necessary?
994
+ { type: "InitialData" }
995
+ ]
996
+ })
997
+ })
998
+ });
999
+ const {
1000
+ useGetContentTypeConfigurationQuery,
1001
+ useGetAllContentTypeSettingsQuery,
1002
+ useUpdateContentTypeConfigurationMutation
1003
+ } = contentTypesApi;
1004
+ const checkIfAttributeIsDisplayable = (attribute) => {
1005
+ const { type } = attribute;
1006
+ if (type === "relation") {
1007
+ return !attribute.relation.toLowerCase().includes("morph");
1008
+ }
1009
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
1010
+ };
1011
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
1012
+ if (!mainFieldName) {
1013
+ return void 0;
1014
+ }
1015
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
1016
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
1017
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
1018
+ );
1019
+ return {
1020
+ name: mainFieldName,
1021
+ type: mainFieldType ?? "string"
1022
+ };
1023
+ };
1024
+ const DEFAULT_SETTINGS = {
1025
+ bulkable: false,
1026
+ filterable: false,
1027
+ searchable: false,
1028
+ pagination: false,
1029
+ defaultSortBy: "",
1030
+ defaultSortOrder: "asc",
1031
+ mainField: "id",
1032
+ pageSize: 10
1033
+ };
1034
+ const useDocumentLayout = (model) => {
1035
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
1036
+ const [{ query }] = useQueryParams();
1037
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
1038
+ const { toggleNotification } = useNotification();
1039
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1040
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
1041
+ const {
1042
+ data,
1043
+ isLoading: isLoadingConfigs,
1044
+ error,
1045
+ isFetching: isFetchingConfigs
1046
+ } = useGetContentTypeConfigurationQuery(model);
1047
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
1048
+ React.useEffect(() => {
1049
+ if (error) {
1050
+ toggleNotification({
1051
+ type: "danger",
1052
+ message: formatAPIError(error)
1053
+ });
1054
+ }
1055
+ }, [error, formatAPIError, toggleNotification]);
1056
+ const editLayout = React.useMemo(
1057
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
1058
+ layout: [],
1059
+ components: {},
1060
+ metadatas: {},
1061
+ options: {},
1062
+ settings: DEFAULT_SETTINGS
1063
+ },
1064
+ [data, isLoading, schemas, schema, components]
1065
+ );
1066
+ const listLayout = React.useMemo(() => {
1067
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
1068
+ layout: [],
1069
+ metadatas: {},
1070
+ options: {},
1071
+ settings: DEFAULT_SETTINGS
1072
+ };
1073
+ }, [data, isLoading, schemas, schema, components]);
1074
+ const { layout: edit } = React.useMemo(
1075
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
1076
+ layout: editLayout,
1077
+ query
1078
+ }),
1079
+ [editLayout, query, runHookWaterfall]
1080
+ );
1081
+ return {
1082
+ error,
1083
+ isLoading,
1084
+ edit,
1085
+ list: listLayout
1086
+ };
1087
+ };
1088
+ const useDocLayout = () => {
1089
+ const { model } = useDoc();
1090
+ return useDocumentLayout(model);
1091
+ };
1092
+ const formatEditLayout = (data, {
1093
+ schemas,
1094
+ schema,
1095
+ components
1096
+ }) => {
1097
+ let currentPanelIndex = 0;
1098
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
1099
+ data.contentType.layouts.edit,
1100
+ schema?.attributes,
1101
+ data.contentType.metadatas,
1102
+ { configurations: data.components, schemas: components },
1103
+ schemas
1104
+ ).reduce((panels, row) => {
1105
+ if (row.some((field) => field.type === "dynamiczone")) {
1106
+ panels.push([row]);
1107
+ currentPanelIndex += 2;
1108
+ } else {
1109
+ if (!panels[currentPanelIndex]) {
1110
+ panels.push([row]);
1111
+ } else {
1112
+ panels[currentPanelIndex].push(row);
1113
+ }
1114
+ }
1115
+ return panels;
1116
+ }, []);
1117
+ const componentEditAttributes = Object.entries(data.components).reduce(
1118
+ (acc, [uid, configuration]) => {
1119
+ acc[uid] = {
1120
+ layout: convertEditLayoutToFieldLayouts(
1121
+ configuration.layouts.edit,
1122
+ components[uid].attributes,
1123
+ configuration.metadatas,
1124
+ { configurations: data.components, schemas: components }
1125
+ ),
1126
+ settings: {
1127
+ ...configuration.settings,
1128
+ icon: components[uid].info.icon,
1129
+ displayName: components[uid].info.displayName
1130
+ }
1131
+ };
1132
+ return acc;
1133
+ },
1134
+ {}
1135
+ );
1136
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1137
+ (acc, [attribute, metadata]) => {
1138
+ return {
1139
+ ...acc,
1140
+ [attribute]: metadata.edit
1141
+ };
1142
+ },
1143
+ {}
1144
+ );
1145
+ return {
1146
+ layout: panelledEditAttributes,
1147
+ components: componentEditAttributes,
1148
+ metadatas: editMetadatas,
1149
+ settings: {
1150
+ ...data.contentType.settings,
1151
+ displayName: schema?.info.displayName
1152
+ },
1153
+ options: {
1154
+ ...schema?.options,
1155
+ ...schema?.pluginOptions,
1156
+ ...data.contentType.options
1157
+ }
1158
+ };
1159
+ };
1160
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1161
+ return rows.map(
1162
+ (row) => row.map((field) => {
1163
+ const attribute = attributes[field.name];
1164
+ if (!attribute) {
1165
+ return null;
1166
+ }
1167
+ const { edit: metadata } = metadatas[field.name];
1168
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1169
+ return {
1170
+ attribute,
1171
+ disabled: !metadata.editable,
1172
+ hint: metadata.description,
1173
+ label: metadata.label ?? "",
1174
+ name: field.name,
1175
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1176
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1177
+ schemas,
1178
+ components: components?.schemas ?? {}
1179
+ }),
1180
+ placeholder: metadata.placeholder ?? "",
1181
+ required: attribute.required ?? false,
1182
+ size: field.size,
1183
+ unique: "unique" in attribute ? attribute.unique : false,
1184
+ visible: metadata.visible ?? true,
1185
+ type: attribute.type
1186
+ };
1187
+ }).filter((field) => field !== null)
1188
+ );
1189
+ };
1190
+ const formatListLayout = (data, {
1191
+ schemas,
1192
+ schema,
1193
+ components
1194
+ }) => {
1195
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1196
+ (acc, [attribute, metadata]) => {
1197
+ return {
1198
+ ...acc,
1199
+ [attribute]: metadata.list
1200
+ };
1201
+ },
1202
+ {}
1203
+ );
1204
+ const listAttributes = convertListLayoutToFieldLayouts(
1205
+ data.contentType.layouts.list,
1206
+ schema?.attributes,
1207
+ listMetadatas,
1208
+ { configurations: data.components, schemas: components },
1209
+ schemas
1210
+ );
1211
+ return {
1212
+ layout: listAttributes,
1213
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1214
+ metadatas: listMetadatas,
1215
+ options: {
1216
+ ...schema?.options,
1217
+ ...schema?.pluginOptions,
1218
+ ...data.contentType.options
1219
+ }
1220
+ };
1221
+ };
1222
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1223
+ return columns.map((name) => {
1224
+ const attribute = attributes[name];
1225
+ if (!attribute) {
1226
+ return null;
1227
+ }
1228
+ const metadata = metadatas[name];
1229
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1230
+ return {
1231
+ attribute,
1232
+ label: metadata.label ?? "",
1233
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1234
+ schemas,
1235
+ components: components?.schemas ?? {}
1236
+ }),
1237
+ name,
1238
+ searchable: metadata.searchable ?? true,
1239
+ sortable: metadata.sortable ?? true
1240
+ };
1241
+ }).filter((field) => field !== null);
1242
+ };
1243
+ const useDocument = (args, opts) => {
1244
+ const { toggleNotification } = useNotification();
1245
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1246
+ const { formatMessage } = useIntl();
1247
+ const {
1248
+ currentData: data,
1249
+ isLoading: isLoadingDocument,
1250
+ isFetching: isFetchingDocument,
1251
+ error
1252
+ } = useGetDocumentQuery(args, {
1253
+ ...opts,
1254
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1255
+ });
1256
+ const document = data?.data;
1257
+ const meta = data?.meta;
1258
+ const {
1259
+ components,
1260
+ schema,
1261
+ schemas,
1262
+ isLoading: isLoadingSchema
1263
+ } = useContentTypeSchema(args.model);
1264
+ const isSingleType = schema?.kind === "singleType";
1265
+ const getTitle = (mainField) => {
1266
+ if (mainField !== "id" && document?.[mainField]) {
1267
+ return document[mainField];
1268
+ }
1269
+ if (isSingleType && schema?.info.displayName) {
1270
+ return schema.info.displayName;
1271
+ }
1272
+ return formatMessage({
1273
+ id: "content-manager.containers.untitled",
1274
+ defaultMessage: "Untitled"
1275
+ });
1276
+ };
1277
+ React.useEffect(() => {
1278
+ if (error) {
1279
+ toggleNotification({
1280
+ type: "danger",
1281
+ message: formatAPIError(error)
1282
+ });
1283
+ }
1284
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1285
+ const validationSchema = React.useMemo(() => {
1286
+ if (!schema) {
1287
+ return null;
1288
+ }
1289
+ return createYupSchema(schema.attributes, components);
1290
+ }, [schema, components]);
1291
+ const validate = React.useCallback(
1292
+ (document2) => {
1293
+ if (!validationSchema) {
1294
+ throw new Error(
1295
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1296
+ );
1297
+ }
1298
+ try {
1299
+ validationSchema.validateSync(document2, { abortEarly: false, strict: true });
1300
+ return null;
1301
+ } catch (error2) {
1302
+ if (error2 instanceof ValidationError) {
1303
+ return getYupValidationErrors(error2);
1304
+ }
1305
+ throw error2;
1306
+ }
1307
+ },
1308
+ [validationSchema]
1309
+ );
1310
+ const getInitialFormValues = React.useCallback(
1311
+ (isCreatingDocument = false) => {
1312
+ if (!document && !isCreatingDocument && !isSingleType || !schema) {
1313
+ return void 0;
1314
+ }
1315
+ const form = document?.id ? document : createDefaultForm(schema, components);
1316
+ return transformDocument(schema, components)(form);
1317
+ },
1318
+ [document, isSingleType, schema, components]
1319
+ );
1320
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1321
+ const hasError = !!error;
1322
+ return {
1323
+ components,
1324
+ document,
1325
+ meta,
1326
+ isLoading,
1327
+ hasError,
1328
+ schema,
1329
+ schemas,
1330
+ validate,
1331
+ getTitle,
1332
+ getInitialFormValues
1333
+ };
1334
+ };
1335
+ const useDoc = () => {
1336
+ const { id, slug, collectionType, origin } = useParams();
862
1337
  const [{ query }] = useQueryParams();
863
1338
  const params = React.useMemo(() => buildValidParams(query), [query]);
864
1339
  if (!collectionType) {
@@ -867,22 +1342,60 @@ const useDoc = () => {
867
1342
  if (!slug) {
868
1343
  throw new Error("Could not find model in url params");
869
1344
  }
1345
+ const document = useDocument(
1346
+ { documentId: origin || id, model: slug, collectionType, params },
1347
+ {
1348
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1349
+ }
1350
+ );
1351
+ const returnId = origin || id === "create" ? void 0 : id;
870
1352
  return {
871
1353
  collectionType,
872
1354
  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
- )
1355
+ id: returnId,
1356
+ ...document
1357
+ };
1358
+ };
1359
+ const useContentManagerContext = () => {
1360
+ const {
1361
+ collectionType,
1362
+ model,
1363
+ id,
1364
+ components,
1365
+ isLoading: isLoadingDoc,
1366
+ schema,
1367
+ schemas
1368
+ } = useDoc();
1369
+ const layout = useDocumentLayout(model);
1370
+ const form = useForm("useContentManagerContext", (state) => state);
1371
+ const isSingleType = collectionType === SINGLE_TYPES;
1372
+ const slug = model;
1373
+ const isCreatingEntry = id === "create";
1374
+ useContentTypeSchema();
1375
+ const isLoading = isLoadingDoc || layout.isLoading;
1376
+ const error = layout.error;
1377
+ return {
1378
+ error,
1379
+ isLoading,
1380
+ // Base metadata
1381
+ model,
1382
+ collectionType,
1383
+ id,
1384
+ slug,
1385
+ isCreatingEntry,
1386
+ isSingleType,
1387
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1388
+ // All schema infos
1389
+ components,
1390
+ contentType: schema,
1391
+ contentTypes: schemas,
1392
+ // Form state
1393
+ form,
1394
+ // layout infos
1395
+ layout
880
1396
  };
881
1397
  };
882
1398
  const prefixPluginTranslations = (trad, pluginId) => {
883
- if (!pluginId) {
884
- throw new TypeError("pluginId can't be empty");
885
- }
886
1399
  return Object.keys(trad).reduce((acc, current) => {
887
1400
  acc[`${pluginId}.${current}`] = trad[current];
888
1401
  return acc;
@@ -898,6 +1411,8 @@ const useDocumentActions = () => {
898
1411
  const { formatMessage } = useIntl();
899
1412
  const { trackUsage } = useTracking();
900
1413
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1414
+ const navigate = useNavigate();
1415
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
901
1416
  const [deleteDocument] = useDeleteDocumentMutation();
902
1417
  const _delete = React.useCallback(
903
1418
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -936,14 +1451,53 @@ const useDocumentActions = () => {
936
1451
  },
937
1452
  [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
938
1453
  );
1454
+ const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
1455
+ const deleteMany = React.useCallback(
1456
+ async ({ model, documentIds, params }) => {
1457
+ try {
1458
+ trackUsage("willBulkDeleteEntries");
1459
+ const res = await deleteManyDocuments({
1460
+ model,
1461
+ documentIds,
1462
+ params
1463
+ });
1464
+ if ("error" in res) {
1465
+ toggleNotification({
1466
+ type: "danger",
1467
+ message: formatAPIError(res.error)
1468
+ });
1469
+ return { error: res.error };
1470
+ }
1471
+ toggleNotification({
1472
+ type: "success",
1473
+ title: formatMessage({
1474
+ id: getTranslation("success.records.delete"),
1475
+ defaultMessage: "Successfully deleted."
1476
+ }),
1477
+ message: ""
1478
+ });
1479
+ trackUsage("didBulkDeleteEntries");
1480
+ return res.data;
1481
+ } catch (err) {
1482
+ toggleNotification({
1483
+ type: "danger",
1484
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1485
+ });
1486
+ trackUsage("didNotBulkDeleteEntries");
1487
+ throw err;
1488
+ }
1489
+ },
1490
+ [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1491
+ );
939
1492
  const [discardDocument] = useDiscardDocumentMutation();
940
1493
  const discard = React.useCallback(
941
- async ({ collectionType, model, documentId }) => {
1494
+ async ({ collectionType, model, documentId, params }) => {
942
1495
  try {
943
1496
  const res = await discardDocument({
944
1497
  collectionType,
945
1498
  model,
946
- documentId
1499
+ documentId,
1500
+ params
947
1501
  });
948
1502
  if ("error" in res) {
949
1503
  toggleNotification({
@@ -1005,6 +1559,43 @@ const useDocumentActions = () => {
1005
1559
  },
1006
1560
  [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1007
1561
  );
1562
+ const [publishManyDocuments] = usePublishManyDocumentsMutation();
1563
+ const publishMany = React.useCallback(
1564
+ async ({ model, documentIds, params }) => {
1565
+ try {
1566
+ const res = await publishManyDocuments({
1567
+ model,
1568
+ documentIds,
1569
+ params
1570
+ });
1571
+ if ("error" in res) {
1572
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1573
+ return { error: res.error };
1574
+ }
1575
+ toggleNotification({
1576
+ type: "success",
1577
+ message: formatMessage({
1578
+ id: getTranslation("success.record.publish"),
1579
+ defaultMessage: "Published document"
1580
+ })
1581
+ });
1582
+ return res.data;
1583
+ } catch (err) {
1584
+ toggleNotification({
1585
+ type: "danger",
1586
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1587
+ });
1588
+ throw err;
1589
+ }
1590
+ },
1591
+ [
1592
+ // trackUsage,
1593
+ publishManyDocuments,
1594
+ toggleNotification,
1595
+ formatMessage,
1596
+ formatAPIError
1597
+ ]
1598
+ );
1008
1599
  const [updateDocument] = useUpdateDocumentMutation();
1009
1600
  const update = React.useCallback(
1010
1601
  async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
@@ -1077,7 +1668,42 @@ const useDocumentActions = () => {
1077
1668
  throw err;
1078
1669
  }
1079
1670
  },
1080
- [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1671
+ [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1672
+ );
1673
+ const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1674
+ const unpublishMany = React.useCallback(
1675
+ async ({ model, documentIds, params }) => {
1676
+ try {
1677
+ trackUsage("willBulkUnpublishEntries");
1678
+ const res = await unpublishManyDocuments({
1679
+ model,
1680
+ documentIds,
1681
+ params
1682
+ });
1683
+ if ("error" in res) {
1684
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1685
+ return { error: res.error };
1686
+ }
1687
+ trackUsage("didBulkUnpublishEntries");
1688
+ toggleNotification({
1689
+ type: "success",
1690
+ title: formatMessage({
1691
+ id: getTranslation("success.records.unpublish"),
1692
+ defaultMessage: "Successfully unpublished."
1693
+ }),
1694
+ message: ""
1695
+ });
1696
+ return res.data;
1697
+ } catch (err) {
1698
+ toggleNotification({
1699
+ type: "danger",
1700
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1701
+ });
1702
+ trackUsage("didNotBulkUnpublishEntries");
1703
+ throw err;
1704
+ }
1705
+ },
1706
+ [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1081
1707
  );
1082
1708
  const [createDocument] = useCreateDocumentMutation();
1083
1709
  const create = React.useCallback(
@@ -1101,6 +1727,7 @@ const useDocumentActions = () => {
1101
1727
  defaultMessage: "Saved document"
1102
1728
  })
1103
1729
  });
1730
+ setCurrentStep("contentManager.success");
1104
1731
  return res.data;
1105
1732
  } catch (err) {
1106
1733
  toggleNotification({
@@ -1122,7 +1749,6 @@ const useDocumentActions = () => {
1122
1749
  sourceId
1123
1750
  });
1124
1751
  if ("error" in res) {
1125
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1126
1752
  return { error: res.error };
1127
1753
  }
1128
1754
  toggleNotification({
@@ -1141,7 +1767,7 @@ const useDocumentActions = () => {
1141
1767
  throw err;
1142
1768
  }
1143
1769
  },
1144
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1770
+ [autoCloneDocument, formatMessage, toggleNotification]
1145
1771
  );
1146
1772
  const [cloneDocument] = useCloneDocumentMutation();
1147
1773
  const clone = React.useCallback(
@@ -1167,6 +1793,7 @@ const useDocumentActions = () => {
1167
1793
  defaultMessage: "Cloned document"
1168
1794
  })
1169
1795
  });
1796
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1170
1797
  return res.data;
1171
1798
  } catch (err) {
1172
1799
  toggleNotification({
@@ -1177,7 +1804,7 @@ const useDocumentActions = () => {
1177
1804
  throw err;
1178
1805
  }
1179
1806
  },
1180
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1807
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1181
1808
  );
1182
1809
  const [getDoc] = useLazyGetDocumentQuery();
1183
1810
  const getDocument = React.useCallback(
@@ -1192,17 +1819,20 @@ const useDocumentActions = () => {
1192
1819
  clone,
1193
1820
  create,
1194
1821
  delete: _delete,
1822
+ deleteMany,
1195
1823
  discard,
1196
1824
  getDocument,
1197
1825
  publish,
1826
+ publishMany,
1198
1827
  unpublish,
1828
+ unpublishMany,
1199
1829
  update
1200
1830
  };
1201
1831
  };
1202
- const ProtectedHistoryPage = lazy(
1203
- () => import("./History-DKhZAPcK.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1832
+ const ProtectedHistoryPage = React.lazy(
1833
+ () => import("./History-CzQbTOwa.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1204
1834
  );
1205
- const routes$1 = [
1835
+ const routes$2 = [
1206
1836
  {
1207
1837
  path: ":collectionType/:slug/:id/history",
1208
1838
  Component: ProtectedHistoryPage
@@ -1212,32 +1842,45 @@ const routes$1 = [
1212
1842
  Component: ProtectedHistoryPage
1213
1843
  }
1214
1844
  ];
1845
+ const ProtectedPreviewPage = React.lazy(
1846
+ () => import("./Preview-Bieh13Ro.mjs").then((mod) => ({ default: mod.ProtectedPreviewPage }))
1847
+ );
1848
+ const routes$1 = [
1849
+ {
1850
+ path: ":collectionType/:slug/:id/preview",
1851
+ Component: ProtectedPreviewPage
1852
+ },
1853
+ {
1854
+ path: ":collectionType/:slug/preview",
1855
+ Component: ProtectedPreviewPage
1856
+ }
1857
+ ];
1215
1858
  const ProtectedEditViewPage = lazy(
1216
- () => import("./EditViewPage-aUnqL-63.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1859
+ () => import("./EditViewPage-DYXZs4_2.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1217
1860
  );
1218
1861
  const ProtectedListViewPage = lazy(
1219
- () => import("./ListViewPage-ChhYmA-L.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1862
+ () => import("./ListViewPage-DnOP55pM.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1220
1863
  );
1221
1864
  const ProtectedListConfiguration = lazy(
1222
- () => import("./ListConfigurationPage-nrXcxNYi.mjs").then((mod) => ({
1865
+ () => import("./ListConfigurationPage-Bbw8w5cS.mjs").then((mod) => ({
1223
1866
  default: mod.ProtectedListConfiguration
1224
1867
  }))
1225
1868
  );
1226
1869
  const ProtectedEditConfigurationPage = lazy(
1227
- () => import("./EditConfigurationPage-BoBb-DLH.mjs").then((mod) => ({
1870
+ () => import("./EditConfigurationPage-B0kNlNoj.mjs").then((mod) => ({
1228
1871
  default: mod.ProtectedEditConfigurationPage
1229
1872
  }))
1230
1873
  );
1231
1874
  const ProtectedComponentConfigurationPage = lazy(
1232
- () => import("./ComponentConfigurationPage-DjQBdcKF.mjs").then((mod) => ({
1875
+ () => import("./ComponentConfigurationPage-B_99pmC0.mjs").then((mod) => ({
1233
1876
  default: mod.ProtectedComponentConfigurationPage
1234
1877
  }))
1235
1878
  );
1236
1879
  const NoPermissions = lazy(
1237
- () => import("./NoPermissionsPage-B9dqrtTy.mjs").then((mod) => ({ default: mod.NoPermissions }))
1880
+ () => import("./NoPermissionsPage-kaj1rPiW.mjs").then((mod) => ({ default: mod.NoPermissions }))
1238
1881
  );
1239
1882
  const NoContentType = lazy(
1240
- () => import("./NoContentTypePage-BrdFcN33.mjs").then((mod) => ({ default: mod.NoContentType }))
1883
+ () => import("./NoContentTypePage-CXKXHNMa.mjs").then((mod) => ({ default: mod.NoContentType }))
1241
1884
  );
1242
1885
  const CollectionTypePages = () => {
1243
1886
  const { collectionType } = useParams();
@@ -1249,7 +1892,7 @@ const CollectionTypePages = () => {
1249
1892
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1250
1893
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1251
1894
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1252
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1895
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1253
1896
  const routes = [
1254
1897
  {
1255
1898
  path: LIST_RELATIVE_PATH,
@@ -1283,6 +1926,7 @@ const routes = [
1283
1926
  path: "no-content-types",
1284
1927
  Component: NoContentType
1285
1928
  },
1929
+ ...routes$2,
1286
1930
  ...routes$1
1287
1931
  ];
1288
1932
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1351,12 +1995,14 @@ const DocumentActionButton = (action) => {
1351
1995
  /* @__PURE__ */ jsx(
1352
1996
  Button,
1353
1997
  {
1354
- flex: 1,
1998
+ flex: "auto",
1355
1999
  startIcon: action.icon,
1356
2000
  disabled: action.disabled,
1357
2001
  onClick: handleClick(action),
1358
2002
  justifyContent: "center",
1359
2003
  variant: action.variant || "default",
2004
+ paddingTop: "7px",
2005
+ paddingBottom: "7px",
1360
2006
  children: action.label
1361
2007
  }
1362
2008
  ),
@@ -1364,7 +2010,7 @@ const DocumentActionButton = (action) => {
1364
2010
  DocumentActionConfirmDialog,
1365
2011
  {
1366
2012
  ...action.dialog,
1367
- variant: action.variant,
2013
+ variant: action.dialog?.variant ?? action.variant,
1368
2014
  isOpen: dialogId === action.id,
1369
2015
  onClose: handleClose
1370
2016
  }
@@ -1379,6 +2025,11 @@ const DocumentActionButton = (action) => {
1379
2025
  ) : null
1380
2026
  ] });
1381
2027
  };
2028
+ const MenuItem = styled(Menu.Item)`
2029
+ &:hover {
2030
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
2031
+ }
2032
+ `;
1382
2033
  const DocumentActionsMenu = ({
1383
2034
  actions: actions2,
1384
2035
  children,
@@ -1421,49 +2072,48 @@ const DocumentActionsMenu = ({
1421
2072
  disabled: isDisabled,
1422
2073
  size: "S",
1423
2074
  endIcon: null,
1424
- paddingTop: "7px",
1425
- paddingLeft: "9px",
1426
- paddingRight: "9px",
2075
+ paddingTop: "4px",
2076
+ paddingLeft: "7px",
2077
+ paddingRight: "7px",
1427
2078
  variant,
1428
2079
  children: [
1429
2080
  /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
1430
- /* @__PURE__ */ jsx(VisuallyHidden, { as: "span", children: label || formatMessage({
2081
+ /* @__PURE__ */ jsx(VisuallyHidden, { tag: "span", children: label || formatMessage({
1431
2082
  id: "content-manager.containers.edit.panels.default.more-actions",
1432
2083
  defaultMessage: "More document actions"
1433
2084
  }) })
1434
2085
  ]
1435
2086
  }
1436
2087
  ),
1437
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
2088
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1438
2089
  actions2.map((action) => {
1439
2090
  return /* @__PURE__ */ jsx(
1440
- Menu.Item,
2091
+ MenuItem,
1441
2092
  {
1442
2093
  disabled: action.disabled,
1443
2094
  onSelect: handleClick(action),
1444
2095
  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
- ] })
2096
+ isVariantDanger: action.variant === "danger",
2097
+ isDisabled: action.disabled,
2098
+ children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxs(
2099
+ Flex,
2100
+ {
2101
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
2102
+ gap: 2,
2103
+ tag: "span",
2104
+ children: [
2105
+ /* @__PURE__ */ jsx(
2106
+ Flex,
2107
+ {
2108
+ tag: "span",
2109
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
2110
+ children: action.icon
2111
+ }
2112
+ ),
2113
+ action.label
2114
+ ]
2115
+ }
2116
+ ) })
1467
2117
  },
1468
2118
  action.id
1469
2119
  );
@@ -1505,6 +2155,18 @@ const convertActionVariantToColor = (variant = "secondary") => {
1505
2155
  return "primary600";
1506
2156
  }
1507
2157
  };
2158
+ const convertActionVariantToIconColor = (variant = "secondary") => {
2159
+ switch (variant) {
2160
+ case "danger":
2161
+ return "danger600";
2162
+ case "secondary":
2163
+ return "neutral500";
2164
+ case "success":
2165
+ return "success600";
2166
+ default:
2167
+ return "primary600";
2168
+ }
2169
+ };
1508
2170
  const DocumentActionConfirmDialog = ({
1509
2171
  onClose,
1510
2172
  onCancel,
@@ -1527,61 +2189,54 @@ const DocumentActionConfirmDialog = ({
1527
2189
  }
1528
2190
  onClose();
1529
2191
  };
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
- ] });
2192
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2193
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2194
+ /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
2195
+ /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
2196
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
2197
+ id: "app.components.Button.cancel",
2198
+ defaultMessage: "Cancel"
2199
+ }) }) }),
2200
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
2201
+ id: "app.components.Button.confirm",
2202
+ defaultMessage: "Confirm"
2203
+ }) })
2204
+ ] })
2205
+ ] }) });
1546
2206
  };
1547
2207
  const DocumentActionModal = ({
1548
2208
  isOpen,
1549
2209
  title,
1550
2210
  onClose,
1551
2211
  footer: Footer,
1552
- content,
2212
+ content: Content,
1553
2213
  onModalClose
1554
2214
  }) => {
1555
- const id = React.useId();
1556
- if (!isOpen) {
1557
- return null;
1558
- }
1559
2215
  const handleClose = () => {
1560
2216
  if (onClose) {
1561
2217
  onClose();
1562
2218
  }
1563
2219
  onModalClose();
1564
2220
  };
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
- ] });
2221
+ return /* @__PURE__ */ jsx(Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
2222
+ /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: title }) }),
2223
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsx(Modal.Body, { children: Content }),
2224
+ typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
2225
+ ] }) });
2226
+ };
2227
+ const transformData = (data) => {
2228
+ if (Array.isArray(data)) {
2229
+ return data.map(transformData);
2230
+ }
2231
+ if (typeof data === "object" && data !== null) {
2232
+ if ("apiData" in data) {
2233
+ return data.apiData;
2234
+ }
2235
+ return mapValues(transformData)(data);
2236
+ }
2237
+ return data;
1583
2238
  };
1584
- const PublishAction = ({
2239
+ const PublishAction$1 = ({
1585
2240
  activeTab,
1586
2241
  documentId,
1587
2242
  model,
@@ -1593,13 +2248,18 @@ const PublishAction = ({
1593
2248
  const navigate = useNavigate();
1594
2249
  const { toggleNotification } = useNotification();
1595
2250
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2251
+ const isListView = useMatch(LIST_PATH) !== null;
1596
2252
  const isCloning = useMatch(CLONE_PATH) !== null;
2253
+ const { id } = useParams();
1597
2254
  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
- );
2255
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1602
2256
  const { publish } = useDocumentActions();
2257
+ const [
2258
+ countDraftRelations,
2259
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2260
+ ] = useLazyGetDraftRelationCountQuery();
2261
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React.useState(0);
2262
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React.useState(0);
1603
2263
  const [{ query, rawQuery }] = useQueryParams();
1604
2264
  const params = React.useMemo(() => buildValidParams(query), [query]);
1605
2265
  const modified = useForm("PublishAction", ({ modified: modified2 }) => modified2);
@@ -1608,10 +2268,107 @@ const PublishAction = ({
1608
2268
  const validate = useForm("PublishAction", (state) => state.validate);
1609
2269
  const setErrors = useForm("PublishAction", (state) => state.setErrors);
1610
2270
  const formValues = useForm("PublishAction", ({ values }) => values);
2271
+ React.useEffect(() => {
2272
+ if (isErrorDraftRelations) {
2273
+ toggleNotification({
2274
+ type: "danger",
2275
+ message: formatMessage({
2276
+ id: getTranslation("error.records.fetch-draft-relatons"),
2277
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2278
+ })
2279
+ });
2280
+ }
2281
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2282
+ React.useEffect(() => {
2283
+ const localDraftRelations = /* @__PURE__ */ new Set();
2284
+ const extractDraftRelations = (data) => {
2285
+ const relations = data.connect || [];
2286
+ relations.forEach((relation) => {
2287
+ if (relation.status === "draft") {
2288
+ localDraftRelations.add(relation.id);
2289
+ }
2290
+ });
2291
+ };
2292
+ const traverseAndExtract = (data) => {
2293
+ Object.entries(data).forEach(([key, value]) => {
2294
+ if (key === "connect" && Array.isArray(value)) {
2295
+ extractDraftRelations({ connect: value });
2296
+ } else if (typeof value === "object" && value !== null) {
2297
+ traverseAndExtract(value);
2298
+ }
2299
+ });
2300
+ };
2301
+ if (!documentId || modified) {
2302
+ traverseAndExtract(formValues);
2303
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2304
+ }
2305
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2306
+ React.useEffect(() => {
2307
+ if (!document || !document.documentId || isListView) {
2308
+ return;
2309
+ }
2310
+ const fetchDraftRelationsCount = async () => {
2311
+ const { data, error } = await countDraftRelations({
2312
+ collectionType,
2313
+ model,
2314
+ documentId,
2315
+ params
2316
+ });
2317
+ if (error) {
2318
+ throw error;
2319
+ }
2320
+ if (data) {
2321
+ setServerCountOfDraftRelations(data.data);
2322
+ }
2323
+ };
2324
+ fetchDraftRelationsCount();
2325
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1611
2326
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1612
2327
  if (!schema?.options?.draftAndPublish) {
1613
2328
  return null;
1614
2329
  }
2330
+ const performPublish = async () => {
2331
+ setSubmitting(true);
2332
+ try {
2333
+ const { errors } = await validate(true, {
2334
+ status: "published"
2335
+ });
2336
+ if (errors) {
2337
+ toggleNotification({
2338
+ type: "danger",
2339
+ message: formatMessage({
2340
+ id: "content-manager.validation.error",
2341
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2342
+ })
2343
+ });
2344
+ return;
2345
+ }
2346
+ const res = await publish(
2347
+ {
2348
+ collectionType,
2349
+ model,
2350
+ documentId,
2351
+ params
2352
+ },
2353
+ transformData(formValues)
2354
+ );
2355
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2356
+ if (id === "create") {
2357
+ navigate({
2358
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2359
+ search: rawQuery
2360
+ });
2361
+ }
2362
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2363
+ setErrors(formatValidationErrors(res.error));
2364
+ }
2365
+ } finally {
2366
+ setSubmitting(false);
2367
+ }
2368
+ };
2369
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2370
+ const enableDraftRelationsCount = false;
2371
+ const hasDraftRelations = enableDraftRelationsCount;
1615
2372
  return {
1616
2373
  /**
1617
2374
  * Disabled when:
@@ -1621,52 +2378,40 @@ const PublishAction = ({
1621
2378
  * - the document is already published & not modified
1622
2379
  * - the document is being created & not modified
1623
2380
  * - 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
2381
  */
1627
- disabled: isCloning || isSubmitting || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2382
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1628
2383
  label: formatMessage({
1629
2384
  id: "app.utils.publish",
1630
2385
  defaultMessage: "Publish"
1631
2386
  }),
1632
2387
  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));
2388
+ await performPublish();
2389
+ },
2390
+ dialog: hasDraftRelations ? {
2391
+ type: "dialog",
2392
+ variant: "danger",
2393
+ footer: null,
2394
+ title: formatMessage({
2395
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2396
+ defaultMessage: "Confirmation"
2397
+ }),
2398
+ content: formatMessage(
2399
+ {
2400
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2401
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2402
+ },
2403
+ {
2404
+ count: totalDraftRelations
1662
2405
  }
1663
- } finally {
1664
- setSubmitting(false);
2406
+ ),
2407
+ onConfirm: async () => {
2408
+ await performPublish();
1665
2409
  }
1666
- }
2410
+ } : void 0
1667
2411
  };
1668
2412
  };
1669
- PublishAction.type = "publish";
2413
+ PublishAction$1.type = "publish";
2414
+ PublishAction$1.position = "panel";
1670
2415
  const UpdateAction = ({
1671
2416
  activeTab,
1672
2417
  documentId,
@@ -1679,10 +2424,6 @@ const UpdateAction = ({
1679
2424
  const cloneMatch = useMatch(CLONE_PATH);
1680
2425
  const isCloning = cloneMatch !== null;
1681
2426
  const { formatMessage } = useIntl();
1682
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1683
- canCreate: canCreate2,
1684
- canUpdate: canUpdate2
1685
- }));
1686
2427
  const { create, update, clone } = useDocumentActions();
1687
2428
  const [{ query, rawQuery }] = useQueryParams();
1688
2429
  const params = React.useMemo(() => buildValidParams(query), [query]);
@@ -1693,95 +2434,139 @@ const UpdateAction = ({
1693
2434
  const validate = useForm("UpdateAction", (state) => state.validate);
1694
2435
  const setErrors = useForm("UpdateAction", (state) => state.setErrors);
1695
2436
  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(
2437
+ const handleUpdate = React.useCallback(async () => {
2438
+ setSubmitting(true);
2439
+ try {
2440
+ if (!modified) {
2441
+ return;
2442
+ }
2443
+ const { errors } = await validate(true, {
2444
+ status: "draft"
2445
+ });
2446
+ if (errors) {
2447
+ toggleNotification({
2448
+ type: "danger",
2449
+ message: formatMessage({
2450
+ id: "content-manager.validation.error",
2451
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2452
+ })
2453
+ });
2454
+ return;
2455
+ }
2456
+ if (isCloning) {
2457
+ const res = await clone(
2458
+ {
2459
+ model,
2460
+ documentId: cloneMatch.params.origin,
2461
+ params
2462
+ },
2463
+ transformData(document)
2464
+ );
2465
+ if ("data" in res) {
2466
+ navigate(
1726
2467
  {
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}`,
2468
+ pathname: `../${res.data.documentId}`,
1736
2469
  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
2470
  },
1749
- document
2471
+ { relative: "path" }
1750
2472
  );
1751
- if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1752
- setErrors(formatValidationErrors(res.error));
1753
- } else {
1754
- resetForm();
1755
- }
2473
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2474
+ setErrors(formatValidationErrors(res.error));
2475
+ }
2476
+ } else if (documentId || collectionType === SINGLE_TYPES) {
2477
+ const res = await update(
2478
+ {
2479
+ collectionType,
2480
+ model,
2481
+ documentId,
2482
+ params
2483
+ },
2484
+ transformData(document)
2485
+ );
2486
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2487
+ setErrors(formatValidationErrors(res.error));
1756
2488
  } else {
1757
- const res = await create(
2489
+ resetForm();
2490
+ }
2491
+ } else {
2492
+ const res = await create(
2493
+ {
2494
+ model,
2495
+ params
2496
+ },
2497
+ transformData(document)
2498
+ );
2499
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2500
+ navigate(
1758
2501
  {
1759
- model,
1760
- params
2502
+ pathname: `../${res.data.documentId}`,
2503
+ search: rawQuery
1761
2504
  },
1762
- document
2505
+ { replace: true, relative: "path" }
1763
2506
  );
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
- }
2507
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2508
+ setErrors(formatValidationErrors(res.error));
1772
2509
  }
1773
- } finally {
1774
- setSubmitting(false);
1775
2510
  }
2511
+ } finally {
2512
+ setSubmitting(false);
1776
2513
  }
2514
+ }, [
2515
+ clone,
2516
+ cloneMatch?.params.origin,
2517
+ collectionType,
2518
+ create,
2519
+ document,
2520
+ documentId,
2521
+ formatMessage,
2522
+ formatValidationErrors,
2523
+ isCloning,
2524
+ model,
2525
+ modified,
2526
+ navigate,
2527
+ params,
2528
+ rawQuery,
2529
+ resetForm,
2530
+ setErrors,
2531
+ setSubmitting,
2532
+ toggleNotification,
2533
+ update,
2534
+ validate
2535
+ ]);
2536
+ React.useEffect(() => {
2537
+ const handleKeyDown = (e) => {
2538
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
2539
+ e.preventDefault();
2540
+ handleUpdate();
2541
+ }
2542
+ };
2543
+ window.addEventListener("keydown", handleKeyDown);
2544
+ return () => {
2545
+ window.removeEventListener("keydown", handleKeyDown);
2546
+ };
2547
+ }, [handleUpdate]);
2548
+ return {
2549
+ /**
2550
+ * Disabled when:
2551
+ * - the form is submitting
2552
+ * - the document is not modified & we're not cloning (you can save a clone entity straight away)
2553
+ * - the active tab is the published tab
2554
+ */
2555
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
2556
+ label: formatMessage({
2557
+ id: "global.save",
2558
+ defaultMessage: "Save"
2559
+ }),
2560
+ onClick: handleUpdate
1777
2561
  };
1778
2562
  };
1779
2563
  UpdateAction.type = "update";
2564
+ UpdateAction.position = "panel";
1780
2565
  const UNPUBLISH_DRAFT_OPTIONS = {
1781
2566
  KEEP: "keep",
1782
2567
  DISCARD: "discard"
1783
2568
  };
1784
- const UnpublishAction = ({
2569
+ const UnpublishAction$1 = ({
1785
2570
  activeTab,
1786
2571
  documentId,
1787
2572
  model,
@@ -1797,10 +2582,8 @@ const UnpublishAction = ({
1797
2582
  const { toggleNotification } = useNotification();
1798
2583
  const [shouldKeepDraft, setShouldKeepDraft] = React.useState(true);
1799
2584
  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
- }
2585
+ const handleChange = (value) => {
2586
+ setShouldKeepDraft(value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
1804
2587
  };
1805
2588
  if (!schema?.options?.draftAndPublish) {
1806
2589
  return null;
@@ -1811,7 +2594,7 @@ const UnpublishAction = ({
1811
2594
  id: "app.utils.unpublish",
1812
2595
  defaultMessage: "Unpublish"
1813
2596
  }),
1814
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2597
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1815
2598
  onClick: async () => {
1816
2599
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1817
2600
  if (!documentId) {
@@ -1844,45 +2627,30 @@ const UnpublishAction = ({
1844
2627
  content: /* @__PURE__ */ jsxs(Flex, { alignItems: "flex-start", direction: "column", gap: 6, children: [
1845
2628
  /* @__PURE__ */ jsxs(Flex, { width: "100%", direction: "column", gap: 2, children: [
1846
2629
  /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
1847
- /* @__PURE__ */ jsx(Typography, { as: "p", variant: "omega", textAlign: "center", children: formatMessage({
2630
+ /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
1848
2631
  id: "content-manager.actions.unpublish.dialog.body",
1849
2632
  defaultMessage: "Are you sure?"
1850
2633
  }) })
1851
2634
  ] }),
1852
2635
  /* @__PURE__ */ jsxs(
1853
- Flex,
2636
+ Radio.Group,
1854
2637
  {
1855
- onChange: handleChange,
1856
- direction: "column",
1857
- alignItems: "flex-start",
1858
- as: "fieldset",
1859
- gap: 3,
2638
+ defaultValue: UNPUBLISH_DRAFT_OPTIONS.KEEP,
2639
+ name: "discard-options",
2640
+ "aria-label": formatMessage({
2641
+ id: "content-manager.actions.unpublish.dialog.radio-label",
2642
+ defaultMessage: "Choose an option to unpublish the document."
2643
+ }),
2644
+ onValueChange: handleChange,
1860
2645
  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
- )
2646
+ /* @__PURE__ */ jsx(Radio.Item, { checked: shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.KEEP, children: formatMessage({
2647
+ id: "content-manager.actions.unpublish.dialog.option.keep-draft",
2648
+ defaultMessage: "Keep draft"
2649
+ }) }),
2650
+ /* @__PURE__ */ jsx(Radio.Item, { checked: !shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.DISCARD, children: formatMessage({
2651
+ id: "content-manager.actions.unpublish.dialog.option.replace-draft",
2652
+ defaultMessage: "Replace draft"
2653
+ }) })
1886
2654
  ]
1887
2655
  }
1888
2656
  )
@@ -1915,7 +2683,8 @@ const UnpublishAction = ({
1915
2683
  position: ["panel", "table-row"]
1916
2684
  };
1917
2685
  };
1918
- UnpublishAction.type = "unpublish";
2686
+ UnpublishAction$1.type = "unpublish";
2687
+ UnpublishAction$1.position = "panel";
1919
2688
  const DiscardAction = ({
1920
2689
  activeTab,
1921
2690
  documentId,
@@ -1938,7 +2707,7 @@ const DiscardAction = ({
1938
2707
  id: "content-manager.actions.discard.label",
1939
2708
  defaultMessage: "Discard changes"
1940
2709
  }),
1941
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2710
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1942
2711
  position: ["panel", "table-row"],
1943
2712
  variant: "danger",
1944
2713
  dialog: {
@@ -1949,7 +2718,7 @@ const DiscardAction = ({
1949
2718
  }),
1950
2719
  content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
1951
2720
  /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
1952
- /* @__PURE__ */ jsx(Typography, { as: "p", variant: "omega", textAlign: "center", children: formatMessage({
2721
+ /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
1953
2722
  id: "content-manager.actions.discard.dialog.body",
1954
2723
  defaultMessage: "Are you sure?"
1955
2724
  }) })
@@ -1966,12 +2735,8 @@ const DiscardAction = ({
1966
2735
  };
1967
2736
  };
1968
2737
  DiscardAction.type = "discard";
1969
- const StyledCrossCircle = styled(CrossCircle)`
1970
- path {
1971
- fill: currentColor;
1972
- }
1973
- `;
1974
- const DEFAULT_ACTIONS = [PublishAction, UpdateAction, UnpublishAction, DiscardAction];
2738
+ DiscardAction.position = "panel";
2739
+ const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
1975
2740
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
1976
2741
  const RelativeTime = React.forwardRef(
1977
2742
  ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
@@ -1983,7 +2748,7 @@ const RelativeTime = React.forwardRef(
1983
2748
  });
1984
2749
  const unit = intervals.find((intervalUnit) => {
1985
2750
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
1986
- });
2751
+ }) ?? "seconds";
1987
2752
  const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
1988
2753
  const customInterval = customIntervals.find(
1989
2754
  (custom) => interval[custom.unit] < custom.threshold
@@ -2017,34 +2782,34 @@ const getDisplayName = ({
2017
2782
  return email ?? "";
2018
2783
  };
2019
2784
  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) }) });
2785
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2786
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2787
+ const { formatMessage } = useIntl();
2788
+ return /* @__PURE__ */ jsx(Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2789
+ id: `content-manager.containers.List.${status}`,
2790
+ defaultMessage: capitalise(status)
2791
+ }) }) });
2023
2792
  };
2024
2793
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2025
2794
  const { formatMessage } = useIntl();
2026
2795
  const isCloning = useMatch(CLONE_PATH) !== null;
2796
+ const params = useParams();
2027
2797
  const title = isCreating ? formatMessage({
2028
2798
  id: "content-manager.containers.edit.title.new",
2029
2799
  defaultMessage: "Create an entry"
2030
2800
  }) : 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,
2801
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2802
+ /* @__PURE__ */ jsx(
2803
+ BackButton,
2035
2804
  {
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
- ]
2805
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2045
2806
  }
2046
2807
  ),
2047
- status ? /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) : null
2808
+ /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2809
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2810
+ /* @__PURE__ */ jsx(HeaderToolbar, {})
2811
+ ] }),
2812
+ status ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2048
2813
  ] });
2049
2814
  };
2050
2815
  const HeaderToolbar = () => {
@@ -2090,7 +2855,7 @@ const HeaderToolbar = () => {
2090
2855
  meta: isCloning ? void 0 : meta,
2091
2856
  collectionType
2092
2857
  },
2093
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2858
+ descriptions: plugins["content-manager"].apis.getDocumentActions("header"),
2094
2859
  children: (actions2) => {
2095
2860
  const headerActions = actions2.filter((action) => {
2096
2861
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2127,12 +2892,12 @@ const Information = ({ activeTab }) => {
2127
2892
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2128
2893
  label: formatMessage({
2129
2894
  id: "content-manager.containers.edit.information.last-published.label",
2130
- defaultMessage: "Last published"
2895
+ defaultMessage: "Published"
2131
2896
  }),
2132
2897
  value: formatMessage(
2133
2898
  {
2134
2899
  id: "content-manager.containers.edit.information.last-published.value",
2135
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2900
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2136
2901
  },
2137
2902
  {
2138
2903
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2145,12 +2910,12 @@ const Information = ({ activeTab }) => {
2145
2910
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2146
2911
  label: formatMessage({
2147
2912
  id: "content-manager.containers.edit.information.last-draft.label",
2148
- defaultMessage: "Last draft"
2913
+ defaultMessage: "Updated"
2149
2914
  }),
2150
2915
  value: formatMessage(
2151
2916
  {
2152
2917
  id: "content-manager.containers.edit.information.last-draft.value",
2153
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2918
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2154
2919
  },
2155
2920
  {
2156
2921
  time: /* @__PURE__ */ jsx(
@@ -2168,12 +2933,12 @@ const Information = ({ activeTab }) => {
2168
2933
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2169
2934
  label: formatMessage({
2170
2935
  id: "content-manager.containers.edit.information.document.label",
2171
- defaultMessage: "Document"
2936
+ defaultMessage: "Created"
2172
2937
  }),
2173
2938
  value: formatMessage(
2174
2939
  {
2175
2940
  id: "content-manager.containers.edit.information.document.value",
2176
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2941
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2177
2942
  },
2178
2943
  {
2179
2944
  time: /* @__PURE__ */ jsx(
@@ -2196,7 +2961,7 @@ const Information = ({ activeTab }) => {
2196
2961
  borderColor: "neutral150",
2197
2962
  direction: "column",
2198
2963
  marginTop: 2,
2199
- as: "dl",
2964
+ tag: "dl",
2200
2965
  padding: 5,
2201
2966
  gap: 3,
2202
2967
  alignItems: "flex-start",
@@ -2204,31 +2969,83 @@ const Information = ({ activeTab }) => {
2204
2969
  marginRight: "-0.4rem",
2205
2970
  width: "calc(100% + 8px)",
2206
2971
  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 })
2972
+ /* @__PURE__ */ jsx(Typography, { tag: "dt", variant: "pi", fontWeight: "bold", children: info.label }),
2973
+ /* @__PURE__ */ jsx(Typography, { tag: "dd", variant: "pi", textColor: "neutral600", children: info.value })
2209
2974
  ] }, info.label))
2210
2975
  }
2211
2976
  );
2212
2977
  };
2213
2978
  const HeaderActions = ({ actions: actions2 }) => {
2214
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2215
- if ("options" in action) {
2979
+ const [dialogId, setDialogId] = React.useState(null);
2980
+ const handleClick = (action) => async (e) => {
2981
+ if (!("options" in action)) {
2982
+ const { onClick = () => false, dialog, id } = action;
2983
+ const muteDialog = await onClick(e);
2984
+ if (dialog && !muteDialog) {
2985
+ e.preventDefault();
2986
+ setDialogId(id);
2987
+ }
2988
+ }
2989
+ };
2990
+ const handleClose = () => {
2991
+ setDialogId(null);
2992
+ };
2993
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2994
+ if (action.options) {
2216
2995
  return /* @__PURE__ */ jsx(
2217
2996
  SingleSelect,
2218
2997
  {
2219
2998
  size: "S",
2220
- disabled: action.disabled,
2221
- "aria-label": action.label,
2222
2999
  onChange: action.onSelect,
2223
- value: action.value,
3000
+ "aria-label": action.label,
3001
+ ...action,
2224
3002
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2225
3003
  },
2226
3004
  action.id
2227
3005
  );
2228
3006
  } else {
2229
- return null;
3007
+ if (action.type === "icon") {
3008
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
3009
+ /* @__PURE__ */ jsx(
3010
+ IconButton,
3011
+ {
3012
+ disabled: action.disabled,
3013
+ label: action.label,
3014
+ size: "S",
3015
+ onClick: handleClick(action),
3016
+ children: action.icon
3017
+ }
3018
+ ),
3019
+ action.dialog ? /* @__PURE__ */ jsx(
3020
+ HeaderActionDialog,
3021
+ {
3022
+ ...action.dialog,
3023
+ isOpen: dialogId === action.id,
3024
+ onClose: handleClose
3025
+ }
3026
+ ) : null
3027
+ ] }, action.id);
3028
+ }
3029
+ }
3030
+ }) });
3031
+ };
3032
+ const HeaderActionDialog = ({
3033
+ onClose,
3034
+ onCancel,
3035
+ title,
3036
+ content: Content,
3037
+ isOpen
3038
+ }) => {
3039
+ const handleClose = async () => {
3040
+ if (onCancel) {
3041
+ await onCancel();
2230
3042
  }
2231
- }) });
3043
+ onClose();
3044
+ };
3045
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
3046
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
3047
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
3048
+ ] }) });
2232
3049
  };
2233
3050
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2234
3051
  const navigate = useNavigate();
@@ -2238,7 +3055,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2238
3055
  id: "app.links.configure-view",
2239
3056
  defaultMessage: "Configure the view"
2240
3057
  }),
2241
- icon: /* @__PURE__ */ jsx(StyledCog, {}),
3058
+ icon: /* @__PURE__ */ jsx(ListPlus, {}),
2242
3059
  onClick: () => {
2243
3060
  navigate(`../${collectionType}/${model}/configurations/edit`);
2244
3061
  },
@@ -2246,11 +3063,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2246
3063
  };
2247
3064
  };
2248
3065
  ConfigureTheViewAction.type = "configure-the-view";
2249
- const StyledCog = styled(Cog)`
2250
- path {
2251
- fill: currentColor;
2252
- }
2253
- `;
3066
+ ConfigureTheViewAction.position = "header";
2254
3067
  const EditTheModelAction = ({ model }) => {
2255
3068
  const navigate = useNavigate();
2256
3069
  const { formatMessage } = useIntl();
@@ -2259,7 +3072,7 @@ const EditTheModelAction = ({ model }) => {
2259
3072
  id: "content-manager.link-to-ctb",
2260
3073
  defaultMessage: "Edit the model"
2261
3074
  }),
2262
- icon: /* @__PURE__ */ jsx(StyledPencil$1, {}),
3075
+ icon: /* @__PURE__ */ jsx(Pencil, {}),
2263
3076
  onClick: () => {
2264
3077
  navigate(`/plugins/content-type-builder/content-types/${model}`);
2265
3078
  },
@@ -2267,12 +3080,8 @@ const EditTheModelAction = ({ model }) => {
2267
3080
  };
2268
3081
  };
2269
3082
  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 }) => {
3083
+ EditTheModelAction.position = "header";
3084
+ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2276
3085
  const navigate = useNavigate();
2277
3086
  const { formatMessage } = useIntl();
2278
3087
  const listViewPathMatch = useMatch(LIST_PATH);
@@ -2280,13 +3089,17 @@ const DeleteAction = ({ documentId, model, collectionType, document }) => {
2280
3089
  const { delete: deleteAction } = useDocumentActions();
2281
3090
  const { toggleNotification } = useNotification();
2282
3091
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
3092
+ const isLocalized = document?.locale != null;
2283
3093
  return {
2284
3094
  disabled: !canDelete || !document,
2285
- label: formatMessage({
2286
- id: "content-manager.actions.delete.label",
2287
- defaultMessage: "Delete document"
2288
- }),
2289
- icon: /* @__PURE__ */ jsx(StyledTrash, {}),
3095
+ label: formatMessage(
3096
+ {
3097
+ id: "content-manager.actions.delete.label",
3098
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
3099
+ },
3100
+ { isLocalized }
3101
+ ),
3102
+ icon: /* @__PURE__ */ jsx(Trash, {}),
2290
3103
  dialog: {
2291
3104
  type: "dialog",
2292
3105
  title: formatMessage({
@@ -2295,7 +3108,7 @@ const DeleteAction = ({ documentId, model, collectionType, document }) => {
2295
3108
  }),
2296
3109
  content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
2297
3110
  /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2298
- /* @__PURE__ */ jsx(Typography, { as: "p", variant: "omega", textAlign: "center", children: formatMessage({
3111
+ /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2299
3112
  id: "content-manager.actions.delete.dialog.body",
2300
3113
  defaultMessage: "Are you sure?"
2301
3114
  }) })
@@ -2335,113 +3148,741 @@ const DeleteAction = ({ documentId, model, collectionType, document }) => {
2335
3148
  }
2336
3149
  }
2337
3150
  }
2338
- },
2339
- variant: "danger",
2340
- position: ["header", "table-row"]
2341
- };
3151
+ },
3152
+ variant: "danger",
3153
+ position: ["header", "table-row"]
3154
+ };
3155
+ };
3156
+ DeleteAction$1.type = "delete";
3157
+ DeleteAction$1.position = ["header", "table-row"];
3158
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
3159
+ const Panels = () => {
3160
+ const isCloning = useMatch(CLONE_PATH) !== null;
3161
+ const [
3162
+ {
3163
+ query: { status }
3164
+ }
3165
+ ] = useQueryParams({
3166
+ status: "draft"
3167
+ });
3168
+ const { model, id, document, meta, collectionType } = useDoc();
3169
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
3170
+ const props = {
3171
+ activeTab: status,
3172
+ model,
3173
+ documentId: id,
3174
+ document: isCloning ? void 0 : document,
3175
+ meta: isCloning ? void 0 : meta,
3176
+ collectionType
3177
+ };
3178
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
3179
+ DescriptionComponentRenderer,
3180
+ {
3181
+ props,
3182
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
3183
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
3184
+ }
3185
+ ) });
3186
+ };
3187
+ const ActionsPanel = () => {
3188
+ const { formatMessage } = useIntl();
3189
+ return {
3190
+ title: formatMessage({
3191
+ id: "content-manager.containers.edit.panels.default.title",
3192
+ defaultMessage: "Entry"
3193
+ }),
3194
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
3195
+ };
3196
+ };
3197
+ ActionsPanel.type = "actions";
3198
+ const ActionsPanelContent = () => {
3199
+ const isCloning = useMatch(CLONE_PATH) !== null;
3200
+ const [
3201
+ {
3202
+ query: { status = "draft" }
3203
+ }
3204
+ ] = useQueryParams();
3205
+ const { model, id, document, meta, collectionType } = useDoc();
3206
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
3207
+ const props = {
3208
+ activeTab: status,
3209
+ model,
3210
+ documentId: id,
3211
+ document: isCloning ? void 0 : document,
3212
+ meta: isCloning ? void 0 : meta,
3213
+ collectionType
3214
+ };
3215
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
3216
+ /* @__PURE__ */ jsx(
3217
+ DescriptionComponentRenderer,
3218
+ {
3219
+ props,
3220
+ descriptions: plugins["content-manager"].apis.getDocumentActions("panel"),
3221
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
3222
+ }
3223
+ ),
3224
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
3225
+ ] });
3226
+ };
3227
+ const Panel = React.forwardRef(({ children, title }, ref) => {
3228
+ return /* @__PURE__ */ jsxs(
3229
+ Flex,
3230
+ {
3231
+ ref,
3232
+ tag: "aside",
3233
+ "aria-labelledby": "additional-information",
3234
+ background: "neutral0",
3235
+ borderColor: "neutral150",
3236
+ hasRadius: true,
3237
+ paddingBottom: 4,
3238
+ paddingLeft: 4,
3239
+ paddingRight: 4,
3240
+ paddingTop: 4,
3241
+ shadow: "tableShadow",
3242
+ gap: 3,
3243
+ direction: "column",
3244
+ justifyContent: "stretch",
3245
+ alignItems: "flex-start",
3246
+ children: [
3247
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3248
+ children
3249
+ ]
3250
+ }
3251
+ );
3252
+ });
3253
+ const ConfirmBulkActionDialog = ({
3254
+ onToggleDialog,
3255
+ isOpen = false,
3256
+ dialogBody,
3257
+ endAction
3258
+ }) => {
3259
+ const { formatMessage } = useIntl();
3260
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
3261
+ /* @__PURE__ */ jsx(Dialog.Header, { children: formatMessage({
3262
+ id: "app.components.ConfirmDialog.title",
3263
+ defaultMessage: "Confirmation"
3264
+ }) }),
3265
+ /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3266
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
3267
+ dialogBody
3268
+ ] }) }),
3269
+ /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
3270
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { fullWidth: true, onClick: onToggleDialog, variant: "tertiary", children: formatMessage({
3271
+ id: "app.components.Button.cancel",
3272
+ defaultMessage: "Cancel"
3273
+ }) }) }),
3274
+ endAction
3275
+ ] })
3276
+ ] }) });
3277
+ };
3278
+ const BoldChunk = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
3279
+ const ConfirmDialogPublishAll = ({
3280
+ isOpen,
3281
+ onToggleDialog,
3282
+ isConfirmButtonLoading = false,
3283
+ onConfirm
3284
+ }) => {
3285
+ const { formatMessage } = useIntl();
3286
+ const selectedEntries = useTable("ConfirmDialogPublishAll", (state) => state.selectedRows);
3287
+ const { toggleNotification } = useNotification();
3288
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
3289
+ const { model, schema } = useDoc();
3290
+ const [{ query }] = useQueryParams();
3291
+ const enableDraftRelationsCount = false;
3292
+ const {
3293
+ data: countDraftRelations = 0,
3294
+ isLoading,
3295
+ error
3296
+ } = useGetManyDraftRelationCountQuery(
3297
+ {
3298
+ model,
3299
+ documentIds: selectedEntries.map((entry) => entry.documentId),
3300
+ locale: query?.plugins?.i18n?.locale
3301
+ },
3302
+ {
3303
+ skip: !enableDraftRelationsCount
3304
+ }
3305
+ );
3306
+ React.useEffect(() => {
3307
+ if (error) {
3308
+ toggleNotification({ type: "danger", message: formatAPIError(error) });
3309
+ }
3310
+ }, [error, formatAPIError, toggleNotification]);
3311
+ if (error) {
3312
+ return null;
3313
+ }
3314
+ return /* @__PURE__ */ jsx(
3315
+ ConfirmBulkActionDialog,
3316
+ {
3317
+ isOpen: isOpen && !isLoading,
3318
+ onToggleDialog,
3319
+ dialogBody: /* @__PURE__ */ jsxs(Fragment, { children: [
3320
+ /* @__PURE__ */ jsxs(Typography, { id: "confirm-description", textAlign: "center", children: [
3321
+ countDraftRelations > 0 && formatMessage(
3322
+ {
3323
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
3324
+ 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. "
3325
+ },
3326
+ {
3327
+ b: BoldChunk,
3328
+ count: countDraftRelations,
3329
+ entities: selectedEntries.length
3330
+ }
3331
+ ),
3332
+ formatMessage({
3333
+ id: getTranslation("popUpWarning.bodyMessage.contentType.publish.all"),
3334
+ defaultMessage: "Are you sure you want to publish these entries?"
3335
+ })
3336
+ ] }),
3337
+ schema?.pluginOptions && "i18n" in schema.pluginOptions && schema?.pluginOptions.i18n && /* @__PURE__ */ jsx(Typography, { textColor: "danger500", textAlign: "center", children: formatMessage(
3338
+ {
3339
+ id: getTranslation("Settings.list.actions.publishAdditionalInfos"),
3340
+ defaultMessage: "This will publish the active locale versions <em>(from Internationalization)</em>"
3341
+ },
3342
+ {
3343
+ em: Emphasis
3344
+ }
3345
+ ) })
3346
+ ] }),
3347
+ endAction: /* @__PURE__ */ jsx(
3348
+ Button,
3349
+ {
3350
+ onClick: onConfirm,
3351
+ variant: "secondary",
3352
+ startIcon: /* @__PURE__ */ jsx(Check, {}),
3353
+ loading: isConfirmButtonLoading,
3354
+ children: formatMessage({
3355
+ id: "app.utils.publish",
3356
+ defaultMessage: "Publish"
3357
+ })
3358
+ }
3359
+ )
3360
+ }
3361
+ );
3362
+ };
3363
+ const TypographyMaxWidth = styled(Typography)`
3364
+ max-width: 300px;
3365
+ `;
3366
+ const TableComponent = styled(RawTable)`
3367
+ width: 100%;
3368
+ table-layout: fixed;
3369
+ td:first-child {
3370
+ border-right: 1px solid ${({ theme }) => theme.colors.neutral150};
3371
+ }
3372
+ td:first-of-type {
3373
+ padding: ${({ theme }) => theme.spaces[4]};
3374
+ }
3375
+ `;
3376
+ const formatErrorMessages = (errors, parentKey, formatMessage) => {
3377
+ const messages = [];
3378
+ Object.entries(errors).forEach(([key, value]) => {
3379
+ const currentKey = parentKey ? `${parentKey}.${key}` : key;
3380
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3381
+ if ("id" in value && "defaultMessage" in value) {
3382
+ messages.push(
3383
+ formatMessage(
3384
+ {
3385
+ id: `${value.id}.withField`,
3386
+ defaultMessage: value.defaultMessage
3387
+ },
3388
+ { field: currentKey }
3389
+ )
3390
+ );
3391
+ } else {
3392
+ messages.push(
3393
+ ...formatErrorMessages(
3394
+ // @ts-expect-error TODO: check why value is not compatible with FormErrors
3395
+ value,
3396
+ currentKey,
3397
+ formatMessage
3398
+ )
3399
+ );
3400
+ }
3401
+ } else {
3402
+ messages.push(
3403
+ formatMessage(
3404
+ {
3405
+ id: `${value}.withField`,
3406
+ defaultMessage: value
3407
+ },
3408
+ { field: currentKey }
3409
+ )
3410
+ );
3411
+ }
3412
+ });
3413
+ return messages;
3414
+ };
3415
+ const EntryValidationText = ({ validationErrors, status }) => {
3416
+ const { formatMessage } = useIntl();
3417
+ if (validationErrors) {
3418
+ const validationErrorsMessages = formatErrorMessages(validationErrors, "", formatMessage).join(
3419
+ " "
3420
+ );
3421
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3422
+ /* @__PURE__ */ jsx(CrossCircle, { fill: "danger600" }),
3423
+ /* @__PURE__ */ jsx(Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
3424
+ ] });
3425
+ }
3426
+ if (status === "published") {
3427
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3428
+ /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
3429
+ /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
3430
+ id: "content-manager.bulk-publish.already-published",
3431
+ defaultMessage: "Already Published"
3432
+ }) })
3433
+ ] });
3434
+ }
3435
+ if (status === "modified") {
3436
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3437
+ /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
3438
+ /* @__PURE__ */ jsx(Typography, { children: formatMessage({
3439
+ id: "content-manager.bulk-publish.modified",
3440
+ defaultMessage: "Ready to publish changes"
3441
+ }) })
3442
+ ] });
3443
+ }
3444
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3445
+ /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
3446
+ /* @__PURE__ */ jsx(Typography, { children: formatMessage({
3447
+ id: "app.utils.ready-to-publish",
3448
+ defaultMessage: "Ready to publish"
3449
+ }) })
3450
+ ] });
3451
+ };
3452
+ const TABLE_HEADERS = [
3453
+ { name: "id", label: "id" },
3454
+ { name: "name", label: "name" },
3455
+ { name: "status", label: "status" },
3456
+ { name: "publicationStatus", label: "Publication status" }
3457
+ ];
3458
+ const SelectedEntriesTableContent = ({
3459
+ isPublishing,
3460
+ rowsToDisplay = [],
3461
+ entriesToPublish = [],
3462
+ validationErrors = {}
3463
+ }) => {
3464
+ const { pathname } = useLocation();
3465
+ const { formatMessage } = useIntl();
3466
+ const {
3467
+ list: {
3468
+ settings: { mainField }
3469
+ }
3470
+ } = useDocLayout();
3471
+ const shouldDisplayMainField = mainField != null && mainField !== "id";
3472
+ return /* @__PURE__ */ jsxs(Table.Content, { children: [
3473
+ /* @__PURE__ */ jsxs(Table.Head, { children: [
3474
+ /* @__PURE__ */ jsx(Table.HeaderCheckboxCell, {}),
3475
+ TABLE_HEADERS.filter((head) => head.name !== "name" || shouldDisplayMainField).map(
3476
+ (head) => /* @__PURE__ */ jsx(Table.HeaderCell, { ...head }, head.name)
3477
+ )
3478
+ ] }),
3479
+ /* @__PURE__ */ jsx(Table.Loading, {}),
3480
+ /* @__PURE__ */ jsx(Table.Body, { children: rowsToDisplay.map((row) => /* @__PURE__ */ jsxs(Table.Row, { children: [
3481
+ /* @__PURE__ */ jsx(Table.CheckboxCell, { id: row.id }),
3482
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Typography, { children: row.id }) }),
3483
+ shouldDisplayMainField && /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Typography, { children: row[mainField] }) }),
3484
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(DocumentStatus, { status: row.status, maxWidth: "min-content" }) }),
3485
+ /* @__PURE__ */ jsx(Table.Cell, { children: isPublishing && entriesToPublish.includes(row.documentId) ? /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3486
+ /* @__PURE__ */ jsx(Typography, { children: formatMessage({
3487
+ id: "content-manager.success.record.publishing",
3488
+ defaultMessage: "Publishing..."
3489
+ }) }),
3490
+ /* @__PURE__ */ jsx(Loader, { small: true })
3491
+ ] }) : /* @__PURE__ */ jsx(
3492
+ EntryValidationText,
3493
+ {
3494
+ validationErrors: validationErrors[row.documentId],
3495
+ status: row.status
3496
+ }
3497
+ ) }),
3498
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3499
+ IconButton,
3500
+ {
3501
+ tag: Link,
3502
+ to: {
3503
+ pathname: `${pathname}/${row.documentId}`,
3504
+ search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3505
+ },
3506
+ state: { from: pathname },
3507
+ label: formatMessage({
3508
+ id: "content-manager.bulk-publish.edit",
3509
+ defaultMessage: "Edit"
3510
+ }),
3511
+ target: "_blank",
3512
+ marginLeft: "auto",
3513
+ variant: "ghost",
3514
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3515
+ }
3516
+ ) }) })
3517
+ ] }, row.id)) })
3518
+ ] });
3519
+ };
3520
+ const PublicationStatusSummary = ({ count, icon, message }) => {
3521
+ return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", flex: 1, gap: 3, children: [
3522
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3523
+ icon,
3524
+ /* @__PURE__ */ jsx(Typography, { children: message })
3525
+ ] }),
3526
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: count })
3527
+ ] });
3528
+ };
3529
+ const PublicationStatusGrid = ({
3530
+ entriesReadyToPublishCount,
3531
+ entriesPublishedCount,
3532
+ entriesModifiedCount,
3533
+ entriesWithErrorsCount
3534
+ }) => {
3535
+ const { formatMessage } = useIntl();
3536
+ return /* @__PURE__ */ jsx(Box, { hasRadius: true, borderColor: "neutral150", children: /* @__PURE__ */ jsx(TableComponent, { colCount: 2, rowCount: 2, children: /* @__PURE__ */ jsxs(Tbody, { children: [
3537
+ /* @__PURE__ */ jsxs(Tr, { children: [
3538
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
3539
+ PublicationStatusSummary,
3540
+ {
3541
+ count: entriesReadyToPublishCount,
3542
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
3543
+ message: formatMessage({
3544
+ id: "app.utils.ready-to-publish",
3545
+ defaultMessage: "Ready to publish"
3546
+ })
3547
+ }
3548
+ ) }),
3549
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
3550
+ PublicationStatusSummary,
3551
+ {
3552
+ count: entriesPublishedCount,
3553
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
3554
+ message: formatMessage({
3555
+ id: "app.utils.already-published",
3556
+ defaultMessage: "Already published"
3557
+ })
3558
+ }
3559
+ ) })
3560
+ ] }),
3561
+ /* @__PURE__ */ jsxs(Tr, { children: [
3562
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
3563
+ PublicationStatusSummary,
3564
+ {
3565
+ count: entriesModifiedCount,
3566
+ icon: /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
3567
+ message: formatMessage({
3568
+ id: "content-manager.bulk-publish.modified",
3569
+ defaultMessage: "Ready to publish changes"
3570
+ })
3571
+ }
3572
+ ) }),
3573
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
3574
+ PublicationStatusSummary,
3575
+ {
3576
+ count: entriesWithErrorsCount,
3577
+ icon: /* @__PURE__ */ jsx(CrossCircle, { fill: "danger600" }),
3578
+ message: formatMessage({
3579
+ id: "content-manager.bulk-publish.waiting-for-action",
3580
+ defaultMessage: "Waiting for action"
3581
+ })
3582
+ }
3583
+ ) })
3584
+ ] })
3585
+ ] }) }) });
3586
+ };
3587
+ const SelectedEntriesModalContent = ({
3588
+ listViewSelectedEntries,
3589
+ toggleModal,
3590
+ setListViewSelectedDocuments,
3591
+ model
3592
+ }) => {
3593
+ const { formatMessage } = useIntl();
3594
+ const { schema, components } = useContentTypeSchema(model);
3595
+ const documentIds = listViewSelectedEntries.map(({ documentId }) => documentId);
3596
+ const [{ query }] = useQueryParams();
3597
+ const params = React.useMemo(() => buildValidParams(query), [query]);
3598
+ const { data, isLoading, isFetching, refetch } = useGetAllDocumentsQuery(
3599
+ {
3600
+ model,
3601
+ params: {
3602
+ page: "1",
3603
+ pageSize: documentIds.length.toString(),
3604
+ sort: query.sort,
3605
+ filters: {
3606
+ documentId: {
3607
+ $in: documentIds
3608
+ }
3609
+ },
3610
+ locale: query.plugins?.i18n?.locale
3611
+ }
3612
+ },
3613
+ {
3614
+ selectFromResult: ({ data: data2, ...restRes }) => ({ data: data2?.results ?? [], ...restRes })
3615
+ }
3616
+ );
3617
+ const { rows, validationErrors } = React.useMemo(() => {
3618
+ if (data.length > 0 && schema) {
3619
+ const validate = createYupSchema(
3620
+ schema.attributes,
3621
+ components,
3622
+ // Since this is the "Publish" action, the validation
3623
+ // schema must enforce the rules for published entities
3624
+ { status: "published" }
3625
+ );
3626
+ const validationErrors2 = {};
3627
+ const rows2 = data.map((entry) => {
3628
+ try {
3629
+ validate.validateSync(entry, { abortEarly: false });
3630
+ return entry;
3631
+ } catch (e) {
3632
+ if (e instanceof ValidationError) {
3633
+ validationErrors2[entry.documentId] = getYupValidationErrors(e);
3634
+ }
3635
+ return entry;
3636
+ }
3637
+ });
3638
+ return { rows: rows2, validationErrors: validationErrors2 };
3639
+ }
3640
+ return {
3641
+ rows: [],
3642
+ validationErrors: {}
3643
+ };
3644
+ }, [components, data, schema]);
3645
+ const [isDialogOpen, setIsDialogOpen] = React.useState(false);
3646
+ const { publishMany: bulkPublishAction } = useDocumentActions();
3647
+ const [, { isLoading: isSubmittingForm }] = usePublishManyDocumentsMutation();
3648
+ const selectedRows = useTable("publishAction", (state) => state.selectedRows);
3649
+ const selectedEntries = rows.filter(
3650
+ (entry) => selectedRows.some((selectedEntry) => selectedEntry.documentId === entry.documentId)
3651
+ );
3652
+ const entriesToPublish = selectedEntries.filter((entry) => !validationErrors[entry.documentId]).map((entry) => entry.documentId);
3653
+ const selectedEntriesWithErrorsCount = selectedEntries.filter(
3654
+ ({ documentId }) => validationErrors[documentId]
3655
+ ).length;
3656
+ const selectedEntriesPublishedCount = selectedEntries.filter(
3657
+ ({ status }) => status === "published"
3658
+ ).length;
3659
+ const selectedEntriesModifiedCount = selectedEntries.filter(
3660
+ ({ status, documentId }) => status === "modified" && !validationErrors[documentId]
3661
+ ).length;
3662
+ const selectedEntriesWithNoErrorsCount = selectedEntries.length - selectedEntriesWithErrorsCount - selectedEntriesPublishedCount;
3663
+ const toggleDialog = () => setIsDialogOpen((prev) => !prev);
3664
+ const handleConfirmBulkPublish = async () => {
3665
+ toggleDialog();
3666
+ const res = await bulkPublishAction({ model, documentIds: entriesToPublish, params });
3667
+ if (!("error" in res)) {
3668
+ const unpublishedEntries = rows.filter((row) => {
3669
+ return !entriesToPublish.includes(row.documentId);
3670
+ });
3671
+ setListViewSelectedDocuments(unpublishedEntries);
3672
+ }
3673
+ };
3674
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3675
+ /* @__PURE__ */ jsxs(Modal.Body, { children: [
3676
+ /* @__PURE__ */ jsx(
3677
+ PublicationStatusGrid,
3678
+ {
3679
+ entriesReadyToPublishCount: selectedEntriesWithNoErrorsCount - selectedEntriesModifiedCount,
3680
+ entriesPublishedCount: selectedEntriesPublishedCount,
3681
+ entriesModifiedCount: selectedEntriesModifiedCount,
3682
+ entriesWithErrorsCount: selectedEntriesWithErrorsCount
3683
+ }
3684
+ ),
3685
+ /* @__PURE__ */ jsx(Box, { marginTop: 7, children: /* @__PURE__ */ jsx(
3686
+ SelectedEntriesTableContent,
3687
+ {
3688
+ isPublishing: isSubmittingForm,
3689
+ rowsToDisplay: rows,
3690
+ entriesToPublish,
3691
+ validationErrors
3692
+ }
3693
+ ) })
3694
+ ] }),
3695
+ /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3696
+ /* @__PURE__ */ jsx(Button, { onClick: toggleModal, variant: "tertiary", children: formatMessage({
3697
+ id: "app.components.Button.cancel",
3698
+ defaultMessage: "Cancel"
3699
+ }) }),
3700
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3701
+ /* @__PURE__ */ jsx(Button, { onClick: refetch, variant: "tertiary", loading: isFetching, children: formatMessage({ id: "app.utils.refresh", defaultMessage: "Refresh" }) }),
3702
+ /* @__PURE__ */ jsx(
3703
+ Button,
3704
+ {
3705
+ onClick: toggleDialog,
3706
+ disabled: selectedEntries.length === 0 || selectedEntries.length === selectedEntriesWithErrorsCount || selectedEntriesPublishedCount === selectedEntries.length || isLoading,
3707
+ loading: isSubmittingForm,
3708
+ children: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" })
3709
+ }
3710
+ )
3711
+ ] })
3712
+ ] }),
3713
+ /* @__PURE__ */ jsx(
3714
+ ConfirmDialogPublishAll,
3715
+ {
3716
+ isOpen: isDialogOpen,
3717
+ onToggleDialog: toggleDialog,
3718
+ isConfirmButtonLoading: isSubmittingForm,
3719
+ onConfirm: handleConfirmBulkPublish
3720
+ }
3721
+ )
3722
+ ] });
2342
3723
  };
2343
- DeleteAction.type = "delete";
2344
- const StyledTrash = styled(Trash)`
2345
- path {
2346
- fill: currentColor;
2347
- }
2348
- `;
2349
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction];
2350
- const Panels = () => {
2351
- const isCloning = useMatch(CLONE_PATH) !== null;
2352
- const [
2353
- {
2354
- query: { status }
3724
+ const PublishAction = ({ documents, model }) => {
3725
+ const { formatMessage } = useIntl();
3726
+ const hasPublishPermission = useDocumentRBAC("unpublishAction", (state) => state.canPublish);
3727
+ const showPublishButton = hasPublishPermission && documents.some(({ status }) => status !== "published");
3728
+ const setListViewSelectedDocuments = useTable("publishAction", (state) => state.selectRow);
3729
+ const refetchList = () => {
3730
+ contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3731
+ };
3732
+ if (!showPublishButton) return null;
3733
+ return {
3734
+ actionType: "publish",
3735
+ variant: "tertiary",
3736
+ label: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" }),
3737
+ dialog: {
3738
+ type: "modal",
3739
+ title: formatMessage({
3740
+ id: getTranslation("containers.ListPage.selectedEntriesModal.title"),
3741
+ defaultMessage: "Publish entries"
3742
+ }),
3743
+ content: ({ onClose }) => {
3744
+ return /* @__PURE__ */ jsx(Table.Root, { rows: documents, defaultSelectedRows: documents, headers: TABLE_HEADERS, children: /* @__PURE__ */ jsx(
3745
+ SelectedEntriesModalContent,
3746
+ {
3747
+ listViewSelectedEntries: documents,
3748
+ toggleModal: () => {
3749
+ onClose();
3750
+ refetchList();
3751
+ },
3752
+ setListViewSelectedDocuments,
3753
+ model
3754
+ }
3755
+ ) });
3756
+ },
3757
+ onClose: () => {
3758
+ refetchList();
3759
+ }
2355
3760
  }
2356
- ] = useQueryParams({
2357
- status: "draft"
2358
- });
2359
- const { model, id, document, meta, collectionType } = useDoc();
2360
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2361
- const props = {
2362
- activeTab: status,
2363
- model,
2364
- documentId: id,
2365
- document: isCloning ? void 0 : document,
2366
- meta: isCloning ? void 0 : meta,
2367
- collectionType
2368
3761
  };
2369
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
3762
+ };
3763
+ const BulkActionsRenderer = () => {
3764
+ const plugins = useStrapiApp("BulkActionsRenderer", (state) => state.plugins);
3765
+ const { model, collectionType } = useDoc();
3766
+ const { selectedRows } = useTable("BulkActionsRenderer", (state) => state);
3767
+ return /* @__PURE__ */ jsx(Flex, { gap: 2, children: /* @__PURE__ */ jsx(
2370
3768
  DescriptionComponentRenderer,
2371
3769
  {
2372
- props,
2373
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2374
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
3770
+ props: {
3771
+ model,
3772
+ collectionType,
3773
+ documents: selectedRows
3774
+ },
3775
+ descriptions: plugins["content-manager"].apis.getBulkActions(),
3776
+ children: (actions2) => actions2.map((action) => /* @__PURE__ */ jsx(DocumentActionButton, { ...action }, action.id))
2375
3777
  }
2376
3778
  ) });
2377
3779
  };
2378
- const ActionsPanel = () => {
3780
+ const DeleteAction = ({ documents, model }) => {
2379
3781
  const { formatMessage } = useIntl();
3782
+ const { schema: contentType } = useDoc();
3783
+ const selectRow = useTable("DeleteAction", (state) => state.selectRow);
3784
+ const hasI18nEnabled = Boolean(contentType?.pluginOptions?.i18n);
3785
+ const [{ query }] = useQueryParams();
3786
+ const params = React.useMemo(() => buildValidParams(query), [query]);
3787
+ const hasDeletePermission = useDocumentRBAC("deleteAction", (state) => state.canDelete);
3788
+ const { deleteMany: bulkDeleteAction } = useDocumentActions();
3789
+ const documentIds = documents.map(({ documentId }) => documentId);
3790
+ const handleConfirmBulkDelete = async () => {
3791
+ const res = await bulkDeleteAction({
3792
+ documentIds,
3793
+ model,
3794
+ params
3795
+ });
3796
+ if (!("error" in res)) {
3797
+ selectRow([]);
3798
+ }
3799
+ };
3800
+ if (!hasDeletePermission) return null;
2380
3801
  return {
2381
- title: formatMessage({
2382
- id: "content-manager.containers.edit.panels.default.title",
2383
- defaultMessage: "Document"
2384
- }),
2385
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
3802
+ variant: "danger-light",
3803
+ label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
3804
+ dialog: {
3805
+ type: "dialog",
3806
+ title: formatMessage({
3807
+ id: "app.components.ConfirmDialog.title",
3808
+ defaultMessage: "Confirmation"
3809
+ }),
3810
+ content: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3811
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
3812
+ /* @__PURE__ */ jsx(Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
3813
+ id: "popUpWarning.bodyMessage.contentType.delete.all",
3814
+ defaultMessage: "Are you sure you want to delete these entries?"
3815
+ }) }),
3816
+ hasI18nEnabled && /* @__PURE__ */ jsx(Box, { textAlign: "center", padding: 3, children: /* @__PURE__ */ jsx(Typography, { textColor: "danger500", children: formatMessage(
3817
+ {
3818
+ id: getTranslation("Settings.list.actions.deleteAdditionalInfos"),
3819
+ defaultMessage: "This will delete the active locale versions <em>(from Internationalization)</em>"
3820
+ },
3821
+ {
3822
+ em: Emphasis
3823
+ }
3824
+ ) }) })
3825
+ ] }),
3826
+ onConfirm: handleConfirmBulkDelete
3827
+ }
2386
3828
  };
2387
3829
  };
2388
- ActionsPanel.type = "actions";
2389
- const ActionsPanelContent = () => {
2390
- const isCloning = useMatch(CLONE_PATH) !== null;
2391
- const [
2392
- {
2393
- query: { status = "draft" }
3830
+ DeleteAction.type = "delete";
3831
+ const UnpublishAction = ({ documents, model }) => {
3832
+ const { formatMessage } = useIntl();
3833
+ const { schema } = useDoc();
3834
+ const selectRow = useTable("UnpublishAction", (state) => state.selectRow);
3835
+ const hasPublishPermission = useDocumentRBAC("unpublishAction", (state) => state.canPublish);
3836
+ const hasI18nEnabled = Boolean(schema?.pluginOptions?.i18n);
3837
+ const hasDraftAndPublishEnabled = Boolean(schema?.options?.draftAndPublish);
3838
+ const { unpublishMany: bulkUnpublishAction } = useDocumentActions();
3839
+ const documentIds = documents.map(({ documentId }) => documentId);
3840
+ const [{ query }] = useQueryParams();
3841
+ const params = React.useMemo(() => buildValidParams(query), [query]);
3842
+ const handleConfirmBulkUnpublish = async () => {
3843
+ const data = await bulkUnpublishAction({ documentIds, model, params });
3844
+ if (!("error" in data)) {
3845
+ selectRow([]);
2394
3846
  }
2395
- ] = useQueryParams();
2396
- const { model, id, document, meta, collectionType } = useDoc();
2397
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2398
- const props = {
2399
- activeTab: status,
2400
- model,
2401
- documentId: id,
2402
- document: isCloning ? void 0 : document,
2403
- meta: isCloning ? void 0 : meta,
2404
- collectionType
2405
3847
  };
2406
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2407
- /* @__PURE__ */ jsx(
2408
- DescriptionComponentRenderer,
2409
- {
2410
- props,
2411
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2412
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2413
- }
2414
- ),
2415
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2416
- ] });
2417
- };
2418
- const Panel = React.forwardRef(({ children, title }, ref) => {
2419
- return /* @__PURE__ */ jsxs(
2420
- Flex,
2421
- {
2422
- ref,
2423
- as: "aside",
2424
- "aria-labelledby": "additional-information",
2425
- background: "neutral0",
2426
- borderColor: "neutral150",
2427
- hasRadius: true,
2428
- paddingBottom: 4,
2429
- paddingLeft: 4,
2430
- paddingRight: 4,
2431
- paddingTop: 4,
2432
- shadow: "tableShadow",
2433
- gap: 3,
2434
- direction: "column",
2435
- justifyContent: "stretch",
2436
- alignItems: "flex-start",
2437
- children: [
2438
- /* @__PURE__ */ jsx(Typography, { as: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2439
- children
2440
- ]
3848
+ const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3849
+ if (!showUnpublishButton) return null;
3850
+ return {
3851
+ variant: "tertiary",
3852
+ label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
3853
+ dialog: {
3854
+ type: "dialog",
3855
+ title: formatMessage({
3856
+ id: "app.components.ConfirmDialog.title",
3857
+ defaultMessage: "Confirmation"
3858
+ }),
3859
+ content: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3860
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
3861
+ /* @__PURE__ */ jsx(Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
3862
+ id: "popUpWarning.bodyMessage.contentType.unpublish.all",
3863
+ defaultMessage: "Are you sure you want to unpublish these entries?"
3864
+ }) }),
3865
+ hasI18nEnabled && /* @__PURE__ */ jsx(Box, { textAlign: "center", padding: 3, children: /* @__PURE__ */ jsx(Typography, { textColor: "danger500", children: formatMessage(
3866
+ {
3867
+ id: getTranslation("Settings.list.actions.unpublishAdditionalInfos"),
3868
+ defaultMessage: "This will unpublish the active locale versions <em>(from Internationalization)</em>"
3869
+ },
3870
+ {
3871
+ em: Emphasis
3872
+ }
3873
+ ) }) })
3874
+ ] }),
3875
+ confirmButton: formatMessage({
3876
+ id: "app.utils.unpublish",
3877
+ defaultMessage: "Unpublish"
3878
+ }),
3879
+ onConfirm: handleConfirmBulkUnpublish
2441
3880
  }
2442
- );
2443
- });
2444
- const DEFAULT_BULK_ACTIONS = [];
3881
+ };
3882
+ };
3883
+ UnpublishAction.type = "unpublish";
3884
+ const Emphasis = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", textColor: "danger500", children: chunks });
3885
+ const DEFAULT_BULK_ACTIONS = [PublishAction, UnpublishAction, DeleteAction];
2445
3886
  const AutoCloneFailureModalBody = ({ prohibitedFields }) => {
2446
3887
  const { formatMessage } = useIntl();
2447
3888
  const getDefaultErrorMessage = (reason) => {
@@ -2473,7 +3914,7 @@ const AutoCloneFailureModalBody = ({ prohibitedFields }) => {
2473
3914
  hasRadius: true,
2474
3915
  padding: 6,
2475
3916
  children: [
2476
- /* @__PURE__ */ jsx(Flex, { direction: "row", as: "ol", children: fieldPath.map((pathSegment, index2) => /* @__PURE__ */ jsxs(Typography, { fontWeight: "semiBold", as: "li", children: [
3917
+ /* @__PURE__ */ jsx(Flex, { direction: "row", tag: "ol", children: fieldPath.map((pathSegment, index2) => /* @__PURE__ */ jsxs(Typography, { fontWeight: "semiBold", tag: "li", children: [
2477
3918
  pathSegment,
2478
3919
  index2 !== fieldPath.length - 1 && /* @__PURE__ */ jsx(
2479
3920
  ChevronRight,
@@ -2485,7 +3926,7 @@ const AutoCloneFailureModalBody = ({ prohibitedFields }) => {
2485
3926
  }
2486
3927
  )
2487
3928
  ] }, index2)) }),
2488
- /* @__PURE__ */ jsx(Typography, { as: "p", textColor: "neutral600", children: formatMessage({
3929
+ /* @__PURE__ */ jsx(Typography, { tag: "p", textColor: "neutral600", children: formatMessage({
2489
3930
  id: getTranslation(`containers.list.autoCloneModal.error.${reason}`),
2490
3931
  defaultMessage: getDefaultErrorMessage(reason)
2491
3932
  }) })
@@ -2510,7 +3951,7 @@ const TableActions = ({ document }) => {
2510
3951
  DescriptionComponentRenderer,
2511
3952
  {
2512
3953
  props,
2513
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3954
+ descriptions: plugins["content-manager"].apis.getDocumentActions("table-row").filter((action) => action.name !== "PublishAction"),
2514
3955
  children: (actions2) => {
2515
3956
  const tableRowActions = actions2.filter((action) => {
2516
3957
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2569,6 +4010,7 @@ const EditAction = ({ documentId }) => {
2569
4010
  };
2570
4011
  };
2571
4012
  EditAction.type = "edit";
4013
+ EditAction.position = "table-row";
2572
4014
  const StyledPencil = styled(Pencil)`
2573
4015
  path {
2574
4016
  fill: currentColor;
@@ -2621,7 +4063,7 @@ const CloneAction = ({ model, documentId }) => {
2621
4063
  }),
2622
4064
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
2623
4065
  footer: ({ onClose }) => {
2624
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
4066
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
2625
4067
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
2626
4068
  id: "cancel",
2627
4069
  defaultMessage: "Cancel"
@@ -2629,7 +4071,7 @@ const CloneAction = ({ model, documentId }) => {
2629
4071
  /* @__PURE__ */ jsx(
2630
4072
  LinkButton,
2631
4073
  {
2632
- as: NavLink,
4074
+ tag: NavLink,
2633
4075
  to: {
2634
4076
  pathname: `clone/${documentId}`
2635
4077
  },
@@ -2645,6 +4087,7 @@ const CloneAction = ({ model, documentId }) => {
2645
4087
  };
2646
4088
  };
2647
4089
  CloneAction.type = "clone";
4090
+ CloneAction.position = "table-row";
2648
4091
  const StyledDuplicate = styled(Duplicate)`
2649
4092
  path {
2650
4093
  fill: currentColor;
@@ -2662,8 +4105,7 @@ class ContentManagerPlugin {
2662
4105
  documentActions = [
2663
4106
  ...DEFAULT_ACTIONS,
2664
4107
  ...DEFAULT_TABLE_ROW_ACTIONS,
2665
- ...DEFAULT_HEADER_ACTIONS,
2666
- HistoryAction
4108
+ ...DEFAULT_HEADER_ACTIONS
2667
4109
  ];
2668
4110
  editViewSidePanels = [ActionsPanel];
2669
4111
  headerActions = [];
@@ -2726,13 +4168,21 @@ class ContentManagerPlugin {
2726
4168
  id: PLUGIN_ID,
2727
4169
  name: "Content Manager",
2728
4170
  injectionZones: INJECTION_ZONES,
4171
+ isReady: false,
2729
4172
  apis: {
2730
4173
  addBulkAction: this.addBulkAction.bind(this),
2731
4174
  addDocumentAction: this.addDocumentAction.bind(this),
2732
4175
  addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
2733
4176
  addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
2734
4177
  getBulkActions: () => this.bulkActions,
2735
- getDocumentActions: () => this.documentActions,
4178
+ getDocumentActions: (position) => {
4179
+ if (position) {
4180
+ return this.documentActions.filter(
4181
+ (action) => action.position == void 0 || [action.position].flat().includes(position)
4182
+ );
4183
+ }
4184
+ return this.documentActions;
4185
+ },
2736
4186
  getEditViewSidePanels: () => this.editViewSidePanels,
2737
4187
  getHeaderActions: () => this.headerActions
2738
4188
  }
@@ -2742,16 +4192,71 @@ class ContentManagerPlugin {
2742
4192
  const getPrintableType = (value) => {
2743
4193
  const nativeType = typeof value;
2744
4194
  if (nativeType === "object") {
2745
- if (value === null)
2746
- return "null";
2747
- if (Array.isArray(value))
2748
- return "array";
4195
+ if (value === null) return "null";
4196
+ if (Array.isArray(value)) return "array";
2749
4197
  if (value instanceof Object && value.constructor.name !== "Object") {
2750
4198
  return value.constructor.name;
2751
4199
  }
2752
4200
  }
2753
4201
  return nativeType;
2754
4202
  };
4203
+ const HistoryAction = ({ model, document }) => {
4204
+ const { formatMessage } = useIntl();
4205
+ const [{ query }] = useQueryParams();
4206
+ const navigate = useNavigate();
4207
+ const { trackUsage } = useTracking();
4208
+ const { pathname } = useLocation();
4209
+ const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
4210
+ if (!window.strapi.features.isEnabled("cms-content-history")) {
4211
+ return null;
4212
+ }
4213
+ const handleOnClick = () => {
4214
+ const destination = { pathname: "history", search: pluginsQueryParams };
4215
+ trackUsage("willNavigate", {
4216
+ from: pathname,
4217
+ to: `${pathname}/${destination.pathname}`
4218
+ });
4219
+ navigate(destination);
4220
+ };
4221
+ return {
4222
+ icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
4223
+ label: formatMessage({
4224
+ id: "content-manager.history.document-action",
4225
+ defaultMessage: "Content History"
4226
+ }),
4227
+ onClick: handleOnClick,
4228
+ disabled: (
4229
+ /**
4230
+ * The user is creating a new document.
4231
+ * It hasn't been saved yet, so there's no history to go to
4232
+ */
4233
+ !document || /**
4234
+ * The document has been created but the current dimension has never been saved.
4235
+ * For example, the user is creating a new locale in an existing document,
4236
+ * so there's no history for the document in that locale
4237
+ */
4238
+ !document.id || /**
4239
+ * History is only available for content types created by the user.
4240
+ * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
4241
+ * which start with `admin::` or `plugin::`
4242
+ */
4243
+ !model.startsWith("api::")
4244
+ ),
4245
+ position: "header"
4246
+ };
4247
+ };
4248
+ HistoryAction.type = "history";
4249
+ HistoryAction.position = "header";
4250
+ const historyAdmin = {
4251
+ bootstrap(app) {
4252
+ const { addDocumentAction } = app.getPlugin("content-manager").apis;
4253
+ addDocumentAction((actions2) => {
4254
+ const indexOfDeleteAction = actions2.findIndex((action) => action.type === "delete");
4255
+ actions2.splice(indexOfDeleteAction, 0, HistoryAction);
4256
+ return actions2;
4257
+ });
4258
+ }
4259
+ };
2755
4260
  const initialState = {
2756
4261
  collectionTypeLinks: [],
2757
4262
  components: [],
@@ -2788,316 +4293,94 @@ const { setInitialData } = actions;
2788
4293
  const reducer = combineReducers({
2789
4294
  app: reducer$1
2790
4295
  });
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({
4296
+ const previewApi = contentManagerApi.injectEndpoints({
2818
4297
  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
- ]
4298
+ getPreviewUrl: builder.query({
4299
+ query({ query, params }) {
4300
+ return {
4301
+ url: `/content-manager/preview/url/${params.contentType}`,
4302
+ method: "GET",
4303
+ config: {
4304
+ params: query
4305
+ }
4306
+ };
4307
+ }
2848
4308
  })
2849
4309
  })
2850
4310
  });
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");
2860
- }
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;
4311
+ const { useGetPreviewUrlQuery } = previewApi;
4312
+ const ConditionalTooltip = ({ isShown, label, children }) => {
4313
+ if (isShown) {
4314
+ return /* @__PURE__ */ jsx(Tooltip, { label, children });
2866
4315
  }
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
4316
+ return children;
2885
4317
  };
2886
- const useDocumentLayout = (model) => {
2887
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
4318
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4319
+ const { formatMessage } = useIntl();
4320
+ const { trackUsage } = useTracking();
4321
+ const { pathname } = useLocation();
2888
4322
  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
- };
2992
- },
2993
- {}
2994
- );
2995
- return {
2996
- layout: panelledEditAttributes,
2997
- components: componentEditAttributes,
2998
- metadatas: editMetadatas,
2999
- settings: {
3000
- ...data.contentType.settings,
3001
- displayName: schema?.info.displayName
4323
+ const isModified = useForm("PreviewSidePanel", (state) => state.modified);
4324
+ const { data, error } = useGetPreviewUrlQuery({
4325
+ params: {
4326
+ contentType: model
3002
4327
  },
3003
- options: {
3004
- ...schema?.options,
3005
- ...schema?.pluginOptions,
3006
- ...data.contentType.options
4328
+ query: {
4329
+ documentId,
4330
+ locale: document?.locale,
4331
+ status: document?.status
3007
4332
  }
4333
+ });
4334
+ if (!data?.data?.url || error) {
4335
+ return null;
4336
+ }
4337
+ const trackNavigation = () => {
4338
+ const destinationPathname = pathname.replace(/\/$/, "") + "/preview";
4339
+ trackUsage("willNavigate", { from: pathname, to: destinationPathname });
3008
4340
  };
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
4341
  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
- }
4342
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4343
+ content: /* @__PURE__ */ jsx(
4344
+ ConditionalTooltip,
4345
+ {
4346
+ label: formatMessage({
4347
+ id: "content-manager.preview.panel.button-disabled-tooltip",
4348
+ defaultMessage: "Please save to open the preview"
4349
+ }),
4350
+ isShown: isModified,
4351
+ children: /* @__PURE__ */ jsx(Box, { cursor: "not-allowed", width: "100%", children: /* @__PURE__ */ jsx(
4352
+ Button,
4353
+ {
4354
+ variant: "tertiary",
4355
+ tag: Link,
4356
+ to: { pathname: "preview", search: stringify(query, { encode: false }) },
4357
+ onClick: trackNavigation,
4358
+ width: "100%",
4359
+ disabled: isModified,
4360
+ pointerEvents: isModified ? "none" : void 0,
4361
+ tabIndex: isModified ? -1 : void 0,
4362
+ children: formatMessage({
4363
+ id: "content-manager.preview.panel.button",
4364
+ defaultMessage: "Open preview"
4365
+ })
4366
+ }
4367
+ ) })
4368
+ }
4369
+ )
3070
4370
  };
3071
4371
  };
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);
4372
+ const previewAdmin = {
4373
+ bootstrap(app) {
4374
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4375
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4376
+ }
3092
4377
  };
3093
4378
  const index = {
3094
4379
  register(app) {
3095
4380
  const cm = new ContentManagerPlugin();
3096
4381
  app.addReducers({
3097
- [contentManagerApi.reducerPath]: contentManagerApi.reducer,
3098
4382
  [PLUGIN_ID]: reducer
3099
4383
  });
3100
- app.addMiddlewares([() => contentManagerApi.middleware]);
3101
4384
  app.addMenuLink({
3102
4385
  to: PLUGIN_ID,
3103
4386
  icon: Feather,
@@ -3106,14 +4389,32 @@ const index = {
3106
4389
  defaultMessage: "Content Manager"
3107
4390
  },
3108
4391
  permissions: [],
3109
- Component: () => import("./layout-CXsHbc3E.mjs").then((mod) => ({ default: mod.Layout }))
4392
+ position: 1
4393
+ });
4394
+ app.router.addRoute({
4395
+ path: "content-manager/*",
4396
+ lazy: async () => {
4397
+ const { Layout } = await import("./layout-bbOlPwLA.mjs");
4398
+ return {
4399
+ Component: Layout
4400
+ };
4401
+ },
4402
+ children: routes
3110
4403
  });
3111
4404
  app.registerPlugin(cm.config);
3112
4405
  },
4406
+ bootstrap(app) {
4407
+ if (typeof historyAdmin.bootstrap === "function") {
4408
+ historyAdmin.bootstrap(app);
4409
+ }
4410
+ if (typeof previewAdmin.bootstrap === "function") {
4411
+ previewAdmin.bootstrap(app);
4412
+ }
4413
+ },
3113
4414
  async registerTrads({ locales }) {
3114
4415
  const importedTrads = await Promise.all(
3115
4416
  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 }) => {
4417
+ 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-D65uIF6Y.mjs"), "./translations/es.json": () => import("./es-D34tqjMw.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-DBseuRuB.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
4418
  return {
3118
4419
  data: prefixPluginTranslations(data, PLUGIN_ID),
3119
4420
  locale
@@ -3130,46 +4431,52 @@ const index = {
3130
4431
  }
3131
4432
  };
3132
4433
  export {
3133
- ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD as A,
3134
- extractContentTypeComponents as B,
4434
+ useUpdateContentTypeConfigurationMutation as A,
4435
+ BulkActionsRenderer as B,
3135
4436
  COLLECTION_TYPES as C,
3136
4437
  DocumentStatus as D,
3137
- DEFAULT_SETTINGS as E,
3138
- convertEditLayoutToFieldLayouts as F,
3139
- useDocument as G,
4438
+ ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD as E,
4439
+ extractContentTypeComponents as F,
4440
+ DEFAULT_SETTINGS as G,
3140
4441
  HOOKS as H,
3141
4442
  InjectionZone as I,
3142
- index as J,
3143
- useDocumentActions as K,
4443
+ convertEditLayoutToFieldLayouts as J,
4444
+ removeFieldsThatDontExistOnSchema as K,
4445
+ prepareTempKeys as L,
4446
+ useDocument as M,
4447
+ useGetPreviewUrlQuery as N,
4448
+ index as O,
3144
4449
  Panels as P,
4450
+ useContentManagerContext as Q,
3145
4451
  RelativeTime as R,
3146
4452
  SINGLE_TYPES as S,
3147
4453
  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,
4454
+ useDocumentActions as U,
4455
+ useGetInitialDataQuery as a,
4456
+ useGetAllContentTypeSettingsQuery as b,
4457
+ useDoc as c,
4458
+ buildValidParams as d,
4459
+ contentManagerApi as e,
4460
+ useDocumentRBAC as f,
3154
4461
  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,
4462
+ useDocumentLayout as h,
4463
+ createYupSchema as i,
4464
+ Header as j,
4465
+ PERMISSIONS as k,
4466
+ DocumentRBAC as l,
3160
4467
  useDocLayout as m,
3161
- useContentTypeSchema as n,
3162
- useGetContentTypeConfigurationQuery as o,
3163
- CREATOR_FIELDS as p,
3164
- getMainField as q,
3165
- routes as r,
4468
+ createDefaultForm as n,
4469
+ CLONE_PATH as o,
4470
+ useGetContentTypeConfigurationQuery as p,
4471
+ CREATOR_FIELDS as q,
4472
+ getMainField as r,
3166
4473
  setInitialData as s,
3167
- getDisplayName as t,
3168
- useGetInitialDataQuery as u,
3169
- checkIfAttributeIsDisplayable as v,
3170
- useGetAllDocumentsQuery as w,
3171
- convertListLayoutToFieldLayouts as x,
3172
- capitalise as y,
3173
- useUpdateContentTypeConfigurationMutation as z
3174
- };
3175
- //# sourceMappingURL=index-CAc9yTnx.mjs.map
4474
+ transformDocument as t,
4475
+ useContentTypeSchema as u,
4476
+ getDisplayName as v,
4477
+ checkIfAttributeIsDisplayable as w,
4478
+ useGetAllDocumentsQuery as x,
4479
+ convertListLayoutToFieldLayouts as y,
4480
+ capitalise as z
4481
+ };
4482
+ //# sourceMappingURL=index-BH2JnYpF.mjs.map