@strapi/content-manager 0.0.0-experimental.7afdc9b682bc83a53ce599c4fb7c9e4506b31fff → 0.0.0-experimental.7bc5339b0393e53f9f568301594621e7fb466e2f

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 (224) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/CardDragPreview-C0QyJgRA.js.map +1 -1
  3. package/dist/_chunks/CardDragPreview-DOxamsuj.mjs.map +1 -1
  4. package/dist/_chunks/{ComponentConfigurationPage-DJcn1DrO.js → ComponentConfigurationPage-BPiQrhZ6.js} +5 -6
  5. package/dist/_chunks/{ComponentConfigurationPage-DJcn1DrO.js.map → ComponentConfigurationPage-BPiQrhZ6.js.map} +1 -1
  6. package/dist/_chunks/{ComponentConfigurationPage-CR5XdR33.mjs → ComponentConfigurationPage-viOAdZM6.mjs} +4 -4
  7. package/dist/_chunks/{ComponentConfigurationPage-CR5XdR33.mjs.map → ComponentConfigurationPage-viOAdZM6.mjs.map} +1 -1
  8. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js → ComponentIcon-CRbtQEUV.js} +2 -3
  9. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js.map → ComponentIcon-CRbtQEUV.js.map} +1 -1
  10. package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -1
  11. package/dist/_chunks/{EditConfigurationPage-tDtWj7R2.js → EditConfigurationPage-CZRKq8CN.js} +5 -6
  12. package/dist/_chunks/{EditConfigurationPage-tDtWj7R2.js.map → EditConfigurationPage-CZRKq8CN.js.map} +1 -1
  13. package/dist/_chunks/{EditConfigurationPage-DmCIb4kD.mjs → EditConfigurationPage-DKtPyjs_.mjs} +4 -4
  14. package/dist/_chunks/{EditConfigurationPage-DmCIb4kD.mjs.map → EditConfigurationPage-DKtPyjs_.mjs.map} +1 -1
  15. package/dist/_chunks/{EditViewPage-CoQEnFlC.js → EditViewPage-C-TZmK5r.js} +101 -51
  16. package/dist/_chunks/EditViewPage-C-TZmK5r.js.map +1 -0
  17. package/dist/_chunks/{EditViewPage-DvaV7U9b.mjs → EditViewPage-D2FPb5Mh.mjs} +102 -51
  18. package/dist/_chunks/EditViewPage-D2FPb5Mh.mjs.map +1 -0
  19. package/dist/_chunks/{Field-ZdrmmQ4Y.js → Field-IMBnqF-k.js} +634 -283
  20. package/dist/_chunks/Field-IMBnqF-k.js.map +1 -0
  21. package/dist/_chunks/{Field-Cz_J9551.mjs → Field-hD0Y00Mt.mjs} +631 -279
  22. package/dist/_chunks/Field-hD0Y00Mt.mjs.map +1 -0
  23. package/dist/_chunks/FieldTypeIcon-CMlNO8PE.mjs.map +1 -1
  24. package/dist/_chunks/FieldTypeIcon-Dnwq_IRF.js.map +1 -1
  25. package/dist/_chunks/{Form-Bpig5rch.js → Form-BaURS01O.js} +55 -38
  26. package/dist/_chunks/Form-BaURS01O.js.map +1 -0
  27. package/dist/_chunks/{Form-Dxmihyw8.mjs → Form-ZQ9fzWkd.mjs} +55 -37
  28. package/dist/_chunks/Form-ZQ9fzWkd.mjs.map +1 -0
  29. package/dist/_chunks/{History-BfX6XmZK.js → History-BqRQm2Lc.js} +175 -129
  30. package/dist/_chunks/History-BqRQm2Lc.js.map +1 -0
  31. package/dist/_chunks/{History-BZP8n7KT.mjs → History-C4X3jiQJ.mjs} +176 -129
  32. package/dist/_chunks/History-C4X3jiQJ.mjs.map +1 -0
  33. package/dist/_chunks/{ListConfigurationPage-B3CXj8PY.js → ListConfigurationPage-BT69-TvF.js} +62 -51
  34. package/dist/_chunks/ListConfigurationPage-BT69-TvF.js.map +1 -0
  35. package/dist/_chunks/{ListConfigurationPage-DxKuVkKz.mjs → ListConfigurationPage-CwMU6tWQ.mjs} +63 -52
  36. package/dist/_chunks/ListConfigurationPage-CwMU6tWQ.mjs.map +1 -0
  37. package/dist/_chunks/{ListViewPage-Bk9VO__I.js → ListViewPage-CYkrfWsm.js} +155 -132
  38. package/dist/_chunks/ListViewPage-CYkrfWsm.js.map +1 -0
  39. package/dist/_chunks/{ListViewPage-D5D3tVPq.mjs → ListViewPage-CvxfiNTc.mjs} +152 -128
  40. package/dist/_chunks/ListViewPage-CvxfiNTc.mjs.map +1 -0
  41. package/dist/_chunks/{NoContentTypePage-DsB2F7Z1.js → NoContentTypePage-BZMfRsrg.js} +2 -2
  42. package/dist/_chunks/{NoContentTypePage-DsB2F7Z1.js.map → NoContentTypePage-BZMfRsrg.js.map} +1 -1
  43. package/dist/_chunks/{NoContentTypePage-DnMeuQCj.mjs → NoContentTypePage-Dy6sa9LT.mjs} +2 -2
  44. package/dist/_chunks/{NoContentTypePage-DnMeuQCj.mjs.map → NoContentTypePage-Dy6sa9LT.mjs.map} +1 -1
  45. package/dist/_chunks/{NoPermissionsPage-OyoME_Tf.mjs → NoPermissionsPage-DiW-TIzq.mjs} +2 -2
  46. package/dist/_chunks/{NoPermissionsPage-OyoME_Tf.mjs.map → NoPermissionsPage-DiW-TIzq.mjs.map} +1 -1
  47. package/dist/_chunks/{NoPermissionsPage-BQDM64_b.js → NoPermissionsPage-DjUdvhJ6.js} +2 -2
  48. package/dist/_chunks/{NoPermissionsPage-BQDM64_b.js.map → NoPermissionsPage-DjUdvhJ6.js.map} +1 -1
  49. package/dist/_chunks/Preview-BqRUSbFU.js +296 -0
  50. package/dist/_chunks/Preview-BqRUSbFU.js.map +1 -0
  51. package/dist/_chunks/Preview-DwWET-X7.mjs +278 -0
  52. package/dist/_chunks/Preview-DwWET-X7.mjs.map +1 -0
  53. package/dist/_chunks/{Relations-BOYZmuWy.mjs → Relations-BOTC_ZqE.mjs} +76 -42
  54. package/dist/_chunks/Relations-BOTC_ZqE.mjs.map +1 -0
  55. package/dist/_chunks/{Relations-B6B3A3mb.js → Relations-DnCdLzq_.js} +76 -43
  56. package/dist/_chunks/Relations-DnCdLzq_.js.map +1 -0
  57. package/dist/_chunks/{en-BN1bvFK7.js → en-BK8Xyl5I.js} +35 -18
  58. package/dist/_chunks/{en-BN1bvFK7.js.map → en-BK8Xyl5I.js.map} +1 -1
  59. package/dist/_chunks/{en-Dzv55oQw.mjs → en-Dtk_ot79.mjs} +35 -18
  60. package/dist/_chunks/{en-Dzv55oQw.mjs.map → en-Dtk_ot79.mjs.map} +1 -1
  61. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  62. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  63. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  64. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  65. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  66. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  67. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  68. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  69. package/dist/_chunks/hooks-BAaaKPS_.js.map +1 -1
  70. package/dist/_chunks/{index-VHviNMeW.mjs → index-BeYsz0Vi.mjs} +1203 -899
  71. package/dist/_chunks/index-BeYsz0Vi.mjs.map +1 -0
  72. package/dist/_chunks/{index-DzN3kBgx.js → index-CptTdHNy.js} +1183 -879
  73. package/dist/_chunks/index-CptTdHNy.js.map +1 -0
  74. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  75. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  76. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  77. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  78. package/dist/_chunks/{layout-CPn1PM6x.mjs → layout-CzSSEy9b.mjs} +42 -24
  79. package/dist/_chunks/layout-CzSSEy9b.mjs.map +1 -0
  80. package/dist/_chunks/{layout-b91XRlD2.js → layout-szfTCeYm.js} +41 -24
  81. package/dist/_chunks/layout-szfTCeYm.js.map +1 -0
  82. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  83. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  84. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  85. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  86. package/dist/_chunks/{relations-BsqxS6tR.mjs → relations-DvzmDbWc.mjs} +6 -7
  87. package/dist/_chunks/relations-DvzmDbWc.mjs.map +1 -0
  88. package/dist/_chunks/{relations-CA7IYmcP.js → relations-qssSbh1V.js} +6 -7
  89. package/dist/_chunks/relations-qssSbh1V.js.map +1 -0
  90. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  91. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  92. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  93. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  94. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js → useDragAndDrop-BMtgCYzL.js} +5 -9
  95. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js.map → useDragAndDrop-BMtgCYzL.js.map} +1 -1
  96. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs → useDragAndDrop-DJ6jqvZN.mjs} +4 -7
  97. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs.map → useDragAndDrop-DJ6jqvZN.mjs.map} +1 -1
  98. package/dist/admin/index.js +2 -1
  99. package/dist/admin/index.js.map +1 -1
  100. package/dist/admin/index.mjs +8 -7
  101. package/dist/admin/src/content-manager.d.ts +3 -2
  102. package/dist/admin/src/exports.d.ts +1 -1
  103. package/dist/admin/src/history/components/VersionInputRenderer.d.ts +1 -1
  104. package/dist/admin/src/history/index.d.ts +3 -0
  105. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  106. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  107. package/dist/admin/src/index.d.ts +1 -0
  108. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  109. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +9 -4
  110. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +2 -2
  111. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +4 -0
  112. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  113. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  114. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  115. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +6 -58
  116. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  117. package/dist/admin/src/pages/ListView/components/BulkActions/Actions.d.ts +3 -30
  118. package/dist/admin/src/pages/ListView/components/BulkActions/ConfirmBulkActionDialog.d.ts +2 -2
  119. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  120. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  121. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  122. package/dist/admin/src/preview/constants.d.ts +1 -0
  123. package/dist/admin/src/preview/index.d.ts +4 -0
  124. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  125. package/dist/admin/src/preview/routes.d.ts +3 -0
  126. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  127. package/dist/admin/src/router.d.ts +1 -1
  128. package/dist/admin/src/services/api.d.ts +1 -1
  129. package/dist/admin/src/services/components.d.ts +2 -2
  130. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  131. package/dist/admin/src/services/documents.d.ts +19 -20
  132. package/dist/admin/src/services/init.d.ts +1 -1
  133. package/dist/admin/src/services/relations.d.ts +2 -2
  134. package/dist/admin/src/services/uid.d.ts +3 -3
  135. package/dist/admin/src/utils/validation.d.ts +4 -1
  136. package/dist/server/index.js +614 -297
  137. package/dist/server/index.js.map +1 -1
  138. package/dist/server/index.mjs +614 -296
  139. package/dist/server/index.mjs.map +1 -1
  140. package/dist/server/src/bootstrap.d.ts.map +1 -1
  141. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  142. package/dist/server/src/controllers/index.d.ts.map +1 -1
  143. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  144. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  145. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  146. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  147. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  148. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  149. package/dist/server/src/history/services/history.d.ts.map +1 -1
  150. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  151. package/dist/server/src/history/services/utils.d.ts +4 -4
  152. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  153. package/dist/server/src/index.d.ts +4 -4
  154. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  155. package/dist/server/src/preview/constants.d.ts +2 -0
  156. package/dist/server/src/preview/constants.d.ts.map +1 -0
  157. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  158. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  159. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  160. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  161. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  162. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  163. package/dist/server/src/preview/index.d.ts +4 -0
  164. package/dist/server/src/preview/index.d.ts.map +1 -0
  165. package/dist/server/src/preview/routes/index.d.ts +8 -0
  166. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  167. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  168. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  169. package/dist/server/src/preview/services/index.d.ts +16 -0
  170. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  171. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  172. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  173. package/dist/server/src/preview/services/preview.d.ts +12 -0
  174. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  175. package/dist/server/src/preview/utils.d.ts +19 -0
  176. package/dist/server/src/preview/utils.d.ts.map +1 -0
  177. package/dist/server/src/register.d.ts.map +1 -1
  178. package/dist/server/src/routes/index.d.ts.map +1 -1
  179. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  180. package/dist/server/src/services/document-metadata.d.ts +8 -8
  181. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  182. package/dist/server/src/services/index.d.ts +4 -4
  183. package/dist/server/src/services/index.d.ts.map +1 -1
  184. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  185. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  186. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  187. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  188. package/dist/server/src/utils/index.d.ts +2 -0
  189. package/dist/server/src/utils/index.d.ts.map +1 -1
  190. package/dist/shared/contracts/collection-types.d.ts +3 -1
  191. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  192. package/dist/shared/contracts/index.d.ts +1 -0
  193. package/dist/shared/contracts/index.d.ts.map +1 -1
  194. package/dist/shared/contracts/preview.d.ts +27 -0
  195. package/dist/shared/contracts/preview.d.ts.map +1 -0
  196. package/dist/shared/index.js +4 -0
  197. package/dist/shared/index.js.map +1 -1
  198. package/dist/shared/index.mjs +4 -0
  199. package/dist/shared/index.mjs.map +1 -1
  200. package/package.json +15 -15
  201. package/dist/_chunks/EditViewPage-CoQEnFlC.js.map +0 -1
  202. package/dist/_chunks/EditViewPage-DvaV7U9b.mjs.map +0 -1
  203. package/dist/_chunks/Field-Cz_J9551.mjs.map +0 -1
  204. package/dist/_chunks/Field-ZdrmmQ4Y.js.map +0 -1
  205. package/dist/_chunks/Form-Bpig5rch.js.map +0 -1
  206. package/dist/_chunks/Form-Dxmihyw8.mjs.map +0 -1
  207. package/dist/_chunks/History-BZP8n7KT.mjs.map +0 -1
  208. package/dist/_chunks/History-BfX6XmZK.js.map +0 -1
  209. package/dist/_chunks/ListConfigurationPage-B3CXj8PY.js.map +0 -1
  210. package/dist/_chunks/ListConfigurationPage-DxKuVkKz.mjs.map +0 -1
  211. package/dist/_chunks/ListViewPage-Bk9VO__I.js.map +0 -1
  212. package/dist/_chunks/ListViewPage-D5D3tVPq.mjs.map +0 -1
  213. package/dist/_chunks/Relations-B6B3A3mb.js.map +0 -1
  214. package/dist/_chunks/Relations-BOYZmuWy.mjs.map +0 -1
  215. package/dist/_chunks/index-DzN3kBgx.js.map +0 -1
  216. package/dist/_chunks/index-VHviNMeW.mjs.map +0 -1
  217. package/dist/_chunks/layout-CPn1PM6x.mjs.map +0 -1
  218. package/dist/_chunks/layout-b91XRlD2.js.map +0 -1
  219. package/dist/_chunks/relations-BsqxS6tR.mjs.map +0 -1
  220. package/dist/_chunks/relations-CA7IYmcP.js.map +0 -1
  221. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  222. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  223. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  224. package/strapi-server.js +0 -3
@@ -2,20 +2,20 @@
2
2
  const Icons = require("@strapi/icons");
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const strapiAdmin = require("@strapi/admin/strapi-admin");
5
- const qs = require("qs");
6
- const reactIntl = require("react-intl");
7
- const reactRouterDom = require("react-router-dom");
8
5
  const React = require("react");
9
6
  const designSystem = require("@strapi/design-system");
7
+ const mapValues = require("lodash/fp/mapValues");
8
+ const reactIntl = require("react-intl");
9
+ const reactRouterDom = require("react-router-dom");
10
10
  const styledComponents = require("styled-components");
11
11
  const yup = require("yup");
12
+ const qs = require("qs");
12
13
  const pipe = require("lodash/fp/pipe");
13
14
  const dateFns = require("date-fns");
14
15
  const toolkit = require("@reduxjs/toolkit");
15
16
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
16
17
  function _interopNamespace(e) {
17
- if (e && e.__esModule)
18
- return e;
18
+ if (e && e.__esModule) return e;
19
19
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
20
20
  if (e) {
21
21
  for (const k in e) {
@@ -32,15 +32,23 @@ function _interopNamespace(e) {
32
32
  return Object.freeze(n);
33
33
  }
34
34
  const React__namespace = /* @__PURE__ */ _interopNamespace(React);
35
+ const mapValues__default = /* @__PURE__ */ _interopDefault(mapValues);
35
36
  const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
36
37
  const pipe__default = /* @__PURE__ */ _interopDefault(pipe);
37
- const __variableDynamicImportRuntimeHelper = (glob, path) => {
38
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
38
39
  const v = glob[path];
39
40
  if (v) {
40
41
  return typeof v === "function" ? v() : Promise.resolve(v);
41
42
  }
42
43
  return new Promise((_, reject) => {
43
- (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(reject.bind(null, new Error("Unknown variable dynamic import: " + path)));
44
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
45
+ reject.bind(
46
+ null,
47
+ new Error(
48
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
49
+ )
50
+ )
51
+ );
44
52
  });
45
53
  };
46
54
  const PLUGIN_ID = "content-manager";
@@ -70,42 +78,6 @@ const useInjectionZone = (area) => {
70
78
  const [page, position] = area.split(".");
71
79
  return contentManagerPlugin.getInjectedComponents(page, position);
72
80
  };
73
- const HistoryAction = ({ model, document }) => {
74
- const { formatMessage } = reactIntl.useIntl();
75
- const [{ query }] = strapiAdmin.useQueryParams();
76
- const navigate = reactRouterDom.useNavigate();
77
- const pluginsQueryParams = qs.stringify({ plugins: query.plugins }, { encode: false });
78
- if (!window.strapi.features.isEnabled("cms-content-history")) {
79
- return null;
80
- }
81
- return {
82
- icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ClockCounterClockwise, {}),
83
- label: formatMessage({
84
- id: "content-manager.history.document-action",
85
- defaultMessage: "Content History"
86
- }),
87
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
88
- disabled: (
89
- /**
90
- * The user is creating a new document.
91
- * It hasn't been saved yet, so there's no history to go to
92
- */
93
- !document || /**
94
- * The document has been created but the current dimension has never been saved.
95
- * For example, the user is creating a new locale in an existing document,
96
- * so there's no history for the document in that locale
97
- */
98
- !document.id || /**
99
- * History is only available for content types created by the user.
100
- * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
101
- * which start with `admin::` or `plugin::`
102
- */
103
- !model.startsWith("api::")
104
- ),
105
- position: "header"
106
- };
107
- };
108
- HistoryAction.type = "history";
109
81
  const ID = "id";
110
82
  const CREATED_BY_ATTRIBUTE_NAME = "createdBy";
111
83
  const UPDATED_BY_ATTRIBUTE_NAME = "updatedBy";
@@ -157,6 +129,7 @@ const DocumentRBAC = ({ children, permissions }) => {
157
129
  if (!slug) {
158
130
  throw new Error("Cannot find the slug param in the URL");
159
131
  }
132
+ const [{ rawQuery }] = strapiAdmin.useQueryParams();
160
133
  const userPermissions = strapiAdmin.useAuth("DocumentRBAC", (state) => state.permissions);
161
134
  const contentTypePermissions = React__namespace.useMemo(() => {
162
135
  const contentTypePermissions2 = userPermissions.filter(
@@ -167,7 +140,14 @@ const DocumentRBAC = ({ children, permissions }) => {
167
140
  return { ...acc, [action]: [permission] };
168
141
  }, {});
169
142
  }, [slug, userPermissions]);
170
- const { isLoading, allowedActions } = strapiAdmin.useRBAC(contentTypePermissions, permissions ?? void 0);
143
+ const { isLoading, allowedActions } = strapiAdmin.useRBAC(
144
+ contentTypePermissions,
145
+ permissions ?? void 0,
146
+ // TODO: useRBAC context should be typed and built differently
147
+ // We are passing raw query as context to the hook so that it can
148
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
149
+ rawQuery
150
+ );
171
151
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
172
152
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
173
153
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -215,10 +195,12 @@ const contentManagerApi = strapiAdmin.adminApi.enhanceEndpoints({
215
195
  "Document",
216
196
  "InitialData",
217
197
  "HistoryVersion",
218
- "Relations"
198
+ "Relations",
199
+ "UidAvailability"
219
200
  ]
220
201
  });
221
202
  const documentApi = contentManagerApi.injectEndpoints({
203
+ overrideExisting: true,
222
204
  endpoints: (builder) => ({
223
205
  autoCloneDocument: builder.mutation({
224
206
  query: ({ model, sourceId, query }) => ({
@@ -228,7 +210,12 @@ const documentApi = contentManagerApi.injectEndpoints({
228
210
  params: query
229
211
  }
230
212
  }),
231
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
213
+ invalidatesTags: (_result, error, { model }) => {
214
+ if (error) {
215
+ return [];
216
+ }
217
+ return [{ type: "Document", id: `${model}_LIST` }];
218
+ }
232
219
  }),
233
220
  cloneDocument: builder.mutation({
234
221
  query: ({ model, sourceId, data, params }) => ({
@@ -239,7 +226,10 @@ const documentApi = contentManagerApi.injectEndpoints({
239
226
  params
240
227
  }
241
228
  }),
242
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
229
+ invalidatesTags: (_result, _error, { model }) => [
230
+ { type: "Document", id: `${model}_LIST` },
231
+ { type: "UidAvailability", id: model }
232
+ ]
243
233
  }),
244
234
  /**
245
235
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -256,7 +246,8 @@ const documentApi = contentManagerApi.injectEndpoints({
256
246
  }),
257
247
  invalidatesTags: (result, _error, { model }) => [
258
248
  { type: "Document", id: `${model}_LIST` },
259
- "Relations"
249
+ "Relations",
250
+ { type: "UidAvailability", id: model }
260
251
  ]
261
252
  }),
262
253
  deleteDocument: builder.mutation({
@@ -297,7 +288,8 @@ const documentApi = contentManagerApi.injectEndpoints({
297
288
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
298
289
  },
299
290
  { type: "Document", id: `${model}_LIST` },
300
- "Relations"
291
+ "Relations",
292
+ { type: "UidAvailability", id: model }
301
293
  ];
302
294
  }
303
295
  }),
@@ -310,11 +302,12 @@ const documentApi = contentManagerApi.injectEndpoints({
310
302
  url: `/content-manager/collection-types/${model}`,
311
303
  method: "GET",
312
304
  config: {
313
- params
305
+ params: qs.stringify(params, { encode: true })
314
306
  }
315
307
  }),
316
308
  providesTags: (result, _error, arg) => {
317
309
  return [
310
+ { type: "Document", id: `ALL_LIST` },
318
311
  { type: "Document", id: `${arg.model}_LIST` },
319
312
  ...result?.results.map(({ documentId }) => ({
320
313
  type: "Document",
@@ -353,6 +346,11 @@ const documentApi = contentManagerApi.injectEndpoints({
353
346
  {
354
347
  type: "Document",
355
348
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
349
+ },
350
+ // Make it easy to invalidate all individual documents queries for a model
351
+ {
352
+ type: "Document",
353
+ id: `${model}_ALL_ITEMS`
356
354
  }
357
355
  ];
358
356
  }
@@ -416,8 +414,21 @@ const documentApi = contentManagerApi.injectEndpoints({
416
414
  type: "Document",
417
415
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
418
416
  },
419
- "Relations"
417
+ "Relations",
418
+ { type: "UidAvailability", id: model }
420
419
  ];
420
+ },
421
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
422
+ const patchResult = dispatch(
423
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
424
+ Object.assign(draft.data, data);
425
+ })
426
+ );
427
+ try {
428
+ await queryFulfilled;
429
+ } catch {
430
+ patchResult.undo();
431
+ }
421
432
  }
422
433
  }),
423
434
  unpublishDocument: builder.mutation({
@@ -470,8 +481,7 @@ const {
470
481
  useUnpublishManyDocumentsMutation
471
482
  } = documentApi;
472
483
  const buildValidParams = (query) => {
473
- if (!query)
474
- return query;
484
+ if (!query) return query;
475
485
  const { plugins: _, ...validQueryParams } = {
476
486
  ...query,
477
487
  ...Object.values(query?.plugins ?? {}).reduce(
@@ -479,28 +489,44 @@ const buildValidParams = (query) => {
479
489
  {}
480
490
  )
481
491
  };
482
- if ("_q" in validQueryParams) {
483
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
484
- }
485
492
  return validQueryParams;
486
493
  };
487
494
  const isBaseQueryError = (error) => {
488
495
  return error.name !== void 0;
489
496
  };
490
- const createYupSchema = (attributes = {}, components = {}) => {
497
+ const arrayValidator = (attribute, options) => ({
498
+ message: strapiAdmin.translatedErrors.required,
499
+ test(value) {
500
+ if (options.status === "draft") {
501
+ return true;
502
+ }
503
+ if (!attribute.required) {
504
+ return true;
505
+ }
506
+ if (!value) {
507
+ return false;
508
+ }
509
+ if (Array.isArray(value) && value.length === 0) {
510
+ return false;
511
+ }
512
+ return true;
513
+ }
514
+ });
515
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
491
516
  const createModelSchema = (attributes2) => yup__namespace.object().shape(
492
517
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
493
518
  if (DOCUMENT_META_FIELDS.includes(name)) {
494
519
  return acc;
495
520
  }
496
521
  const validations = [
522
+ addNullableValidation,
497
523
  addRequiredValidation,
498
524
  addMinLengthValidation,
499
525
  addMaxLengthValidation,
500
526
  addMinValidation,
501
527
  addMaxValidation,
502
528
  addRegexValidation
503
- ].map((fn) => fn(attribute));
529
+ ].map((fn) => fn(attribute, options));
504
530
  const transformSchema = pipe__default.default(...validations);
505
531
  switch (attribute.type) {
506
532
  case "component": {
@@ -510,12 +536,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
510
536
  ...acc,
511
537
  [name]: transformSchema(
512
538
  yup__namespace.array().of(createModelSchema(attributes3).nullable(false))
513
- )
539
+ ).test(arrayValidator(attribute, options))
514
540
  };
515
541
  } else {
516
542
  return {
517
543
  ...acc,
518
- [name]: transformSchema(createModelSchema(attributes3))
544
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
519
545
  };
520
546
  }
521
547
  }
@@ -537,7 +563,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
537
563
  }
538
564
  )
539
565
  )
540
- )
566
+ ).test(arrayValidator(attribute, options))
541
567
  };
542
568
  case "relation":
543
569
  return {
@@ -549,7 +575,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
549
575
  } else if (Array.isArray(value)) {
550
576
  return yup__namespace.array().of(
551
577
  yup__namespace.object().shape({
552
- id: yup__namespace.string().required()
578
+ id: yup__namespace.number().required()
553
579
  })
554
580
  );
555
581
  } else if (typeof value === "object") {
@@ -601,6 +627,14 @@ const createAttributeSchema = (attribute) => {
601
627
  if (!value || typeof value === "string" && value.length === 0) {
602
628
  return true;
603
629
  }
630
+ if (typeof value === "object") {
631
+ try {
632
+ JSON.stringify(value);
633
+ return true;
634
+ } catch (err) {
635
+ return false;
636
+ }
637
+ }
604
638
  try {
605
639
  JSON.parse(value);
606
640
  return true;
@@ -619,13 +653,7 @@ const createAttributeSchema = (attribute) => {
619
653
  return yup__namespace.mixed();
620
654
  }
621
655
  };
622
- const addRequiredValidation = (attribute) => (schema) => {
623
- if (attribute.required) {
624
- return schema.required({
625
- id: strapiAdmin.translatedErrors.required.id,
626
- defaultMessage: "This field is required."
627
- });
628
- }
656
+ const nullableSchema = (schema) => {
629
657
  return schema?.nullable ? schema.nullable() : (
630
658
  // In some cases '.nullable' will not be available on the schema.
631
659
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -633,7 +661,22 @@ const addRequiredValidation = (attribute) => (schema) => {
633
661
  schema
634
662
  );
635
663
  };
636
- const addMinLengthValidation = (attribute) => (schema) => {
664
+ const addNullableValidation = () => (schema) => {
665
+ return nullableSchema(schema);
666
+ };
667
+ const addRequiredValidation = (attribute, options) => (schema) => {
668
+ if (options.status === "draft" || !attribute.required) {
669
+ return schema;
670
+ }
671
+ if (attribute.required && "required" in schema) {
672
+ return schema.required(strapiAdmin.translatedErrors.required);
673
+ }
674
+ return schema;
675
+ };
676
+ const addMinLengthValidation = (attribute, options) => (schema) => {
677
+ if (options.status === "draft") {
678
+ return schema;
679
+ }
637
680
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
638
681
  return schema.min(attribute.minLength, {
639
682
  ...strapiAdmin.translatedErrors.minLength,
@@ -655,10 +698,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
655
698
  }
656
699
  return schema;
657
700
  };
658
- const addMinValidation = (attribute) => (schema) => {
659
- if ("min" in attribute) {
701
+ const addMinValidation = (attribute, options) => (schema) => {
702
+ if (options.status === "draft") {
703
+ return schema;
704
+ }
705
+ if ("min" in attribute && "min" in schema) {
660
706
  const min = toInteger(attribute.min);
661
- if ("min" in schema && min) {
707
+ if (min) {
662
708
  return schema.min(min, {
663
709
  ...strapiAdmin.translatedErrors.min,
664
710
  values: {
@@ -776,16 +822,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
776
822
  }, {});
777
823
  return componentsByKey;
778
824
  };
779
- const useDocument = (args, opts) => {
825
+ const HOOKS = {
826
+ /**
827
+ * Hook that allows to mutate the displayed headers of the list view table
828
+ * @constant
829
+ * @type {string}
830
+ */
831
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
832
+ /**
833
+ * Hook that allows to mutate the CM's collection types links pre-set filters
834
+ * @constant
835
+ * @type {string}
836
+ */
837
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
838
+ /**
839
+ * Hook that allows to mutate the CM's edit view layout
840
+ * @constant
841
+ * @type {string}
842
+ */
843
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
844
+ /**
845
+ * Hook that allows to mutate the CM's single types links pre-set filters
846
+ * @constant
847
+ * @type {string}
848
+ */
849
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
850
+ };
851
+ const contentTypesApi = contentManagerApi.injectEndpoints({
852
+ endpoints: (builder) => ({
853
+ getContentTypeConfiguration: builder.query({
854
+ query: (uid) => ({
855
+ url: `/content-manager/content-types/${uid}/configuration`,
856
+ method: "GET"
857
+ }),
858
+ transformResponse: (response) => response.data,
859
+ providesTags: (_result, _error, uid) => [
860
+ { type: "ContentTypesConfiguration", id: uid },
861
+ { type: "ContentTypeSettings", id: "LIST" }
862
+ ]
863
+ }),
864
+ getAllContentTypeSettings: builder.query({
865
+ query: () => "/content-manager/content-types-settings",
866
+ transformResponse: (response) => response.data,
867
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
868
+ }),
869
+ updateContentTypeConfiguration: builder.mutation({
870
+ query: ({ uid, ...body }) => ({
871
+ url: `/content-manager/content-types/${uid}/configuration`,
872
+ method: "PUT",
873
+ data: body
874
+ }),
875
+ transformResponse: (response) => response.data,
876
+ invalidatesTags: (_result, _error, { uid }) => [
877
+ { type: "ContentTypesConfiguration", id: uid },
878
+ { type: "ContentTypeSettings", id: "LIST" },
879
+ // Is this necessary?
880
+ { type: "InitialData" }
881
+ ]
882
+ })
883
+ })
884
+ });
885
+ const {
886
+ useGetContentTypeConfigurationQuery,
887
+ useGetAllContentTypeSettingsQuery,
888
+ useUpdateContentTypeConfigurationMutation
889
+ } = contentTypesApi;
890
+ const checkIfAttributeIsDisplayable = (attribute) => {
891
+ const { type } = attribute;
892
+ if (type === "relation") {
893
+ return !attribute.relation.toLowerCase().includes("morph");
894
+ }
895
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
896
+ };
897
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
898
+ if (!mainFieldName) {
899
+ return void 0;
900
+ }
901
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
902
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
903
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
904
+ );
905
+ return {
906
+ name: mainFieldName,
907
+ type: mainFieldType ?? "string"
908
+ };
909
+ };
910
+ const DEFAULT_SETTINGS = {
911
+ bulkable: false,
912
+ filterable: false,
913
+ searchable: false,
914
+ pagination: false,
915
+ defaultSortBy: "",
916
+ defaultSortOrder: "asc",
917
+ mainField: "id",
918
+ pageSize: 10
919
+ };
920
+ const useDocumentLayout = (model) => {
921
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
922
+ const [{ query }] = strapiAdmin.useQueryParams();
923
+ const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
780
924
  const { toggleNotification } = strapiAdmin.useNotification();
781
925
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
926
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
782
927
  const {
783
- currentData: data,
784
- isLoading: isLoadingDocument,
785
- isFetching: isFetchingDocument,
786
- error
787
- } = useGetDocumentQuery(args, opts);
788
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
928
+ data,
929
+ isLoading: isLoadingConfigs,
930
+ error,
931
+ isFetching: isFetchingConfigs
932
+ } = useGetContentTypeConfigurationQuery(model);
933
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
789
934
  React__namespace.useEffect(() => {
790
935
  if (error) {
791
936
  toggleNotification({
@@ -793,68 +938,322 @@ const useDocument = (args, opts) => {
793
938
  message: formatAPIError(error)
794
939
  });
795
940
  }
796
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
797
- const validationSchema = React__namespace.useMemo(() => {
798
- if (!schema) {
799
- return null;
800
- }
801
- return createYupSchema(schema.attributes, components);
802
- }, [schema, components]);
803
- const validate = React__namespace.useCallback(
804
- (document) => {
805
- if (!validationSchema) {
806
- throw new Error(
807
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
808
- );
809
- }
810
- try {
811
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
812
- return null;
813
- } catch (error2) {
814
- if (error2 instanceof yup.ValidationError) {
815
- return strapiAdmin.getYupValidationErrors(error2);
816
- }
817
- throw error2;
818
- }
941
+ }, [error, formatAPIError, toggleNotification]);
942
+ const editLayout = React__namespace.useMemo(
943
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
944
+ layout: [],
945
+ components: {},
946
+ metadatas: {},
947
+ options: {},
948
+ settings: DEFAULT_SETTINGS
819
949
  },
820
- [validationSchema]
950
+ [data, isLoading, schemas, schema, components]
951
+ );
952
+ const listLayout = React__namespace.useMemo(() => {
953
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
954
+ layout: [],
955
+ metadatas: {},
956
+ options: {},
957
+ settings: DEFAULT_SETTINGS
958
+ };
959
+ }, [data, isLoading, schemas, schema, components]);
960
+ const { layout: edit } = React__namespace.useMemo(
961
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
962
+ layout: editLayout,
963
+ query
964
+ }),
965
+ [editLayout, query, runHookWaterfall]
821
966
  );
822
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
823
967
  return {
824
- components,
825
- document: data?.data,
826
- meta: data?.meta,
968
+ error,
827
969
  isLoading,
828
- schema,
829
- validate
970
+ edit,
971
+ list: listLayout
830
972
  };
831
973
  };
832
- const useDoc = () => {
833
- const { id, slug, collectionType, origin } = reactRouterDom.useParams();
834
- const [{ query }] = strapiAdmin.useQueryParams();
835
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
836
- if (!collectionType) {
837
- throw new Error("Could not find collectionType in url params");
974
+ const useDocLayout = () => {
975
+ const { model } = useDoc();
976
+ return useDocumentLayout(model);
977
+ };
978
+ const formatEditLayout = (data, {
979
+ schemas,
980
+ schema,
981
+ components
982
+ }) => {
983
+ let currentPanelIndex = 0;
984
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
985
+ data.contentType.layouts.edit,
986
+ schema?.attributes,
987
+ data.contentType.metadatas,
988
+ { configurations: data.components, schemas: components },
989
+ schemas
990
+ ).reduce((panels, row) => {
991
+ if (row.some((field) => field.type === "dynamiczone")) {
992
+ panels.push([row]);
993
+ currentPanelIndex += 2;
994
+ } else {
995
+ if (!panels[currentPanelIndex]) {
996
+ panels.push([row]);
997
+ } else {
998
+ panels[currentPanelIndex].push(row);
999
+ }
1000
+ }
1001
+ return panels;
1002
+ }, []);
1003
+ const componentEditAttributes = Object.entries(data.components).reduce(
1004
+ (acc, [uid, configuration]) => {
1005
+ acc[uid] = {
1006
+ layout: convertEditLayoutToFieldLayouts(
1007
+ configuration.layouts.edit,
1008
+ components[uid].attributes,
1009
+ configuration.metadatas,
1010
+ { configurations: data.components, schemas: components }
1011
+ ),
1012
+ settings: {
1013
+ ...configuration.settings,
1014
+ icon: components[uid].info.icon,
1015
+ displayName: components[uid].info.displayName
1016
+ }
1017
+ };
1018
+ return acc;
1019
+ },
1020
+ {}
1021
+ );
1022
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1023
+ (acc, [attribute, metadata]) => {
1024
+ return {
1025
+ ...acc,
1026
+ [attribute]: metadata.edit
1027
+ };
1028
+ },
1029
+ {}
1030
+ );
1031
+ return {
1032
+ layout: panelledEditAttributes,
1033
+ components: componentEditAttributes,
1034
+ metadatas: editMetadatas,
1035
+ settings: {
1036
+ ...data.contentType.settings,
1037
+ displayName: schema?.info.displayName
1038
+ },
1039
+ options: {
1040
+ ...schema?.options,
1041
+ ...schema?.pluginOptions,
1042
+ ...data.contentType.options
1043
+ }
1044
+ };
1045
+ };
1046
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1047
+ return rows.map(
1048
+ (row) => row.map((field) => {
1049
+ const attribute = attributes[field.name];
1050
+ if (!attribute) {
1051
+ return null;
1052
+ }
1053
+ const { edit: metadata } = metadatas[field.name];
1054
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1055
+ return {
1056
+ attribute,
1057
+ disabled: !metadata.editable,
1058
+ hint: metadata.description,
1059
+ label: metadata.label ?? "",
1060
+ name: field.name,
1061
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1062
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1063
+ schemas,
1064
+ components: components?.schemas ?? {}
1065
+ }),
1066
+ placeholder: metadata.placeholder ?? "",
1067
+ required: attribute.required ?? false,
1068
+ size: field.size,
1069
+ unique: "unique" in attribute ? attribute.unique : false,
1070
+ visible: metadata.visible ?? true,
1071
+ type: attribute.type
1072
+ };
1073
+ }).filter((field) => field !== null)
1074
+ );
1075
+ };
1076
+ const formatListLayout = (data, {
1077
+ schemas,
1078
+ schema,
1079
+ components
1080
+ }) => {
1081
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1082
+ (acc, [attribute, metadata]) => {
1083
+ return {
1084
+ ...acc,
1085
+ [attribute]: metadata.list
1086
+ };
1087
+ },
1088
+ {}
1089
+ );
1090
+ const listAttributes = convertListLayoutToFieldLayouts(
1091
+ data.contentType.layouts.list,
1092
+ schema?.attributes,
1093
+ listMetadatas,
1094
+ { configurations: data.components, schemas: components },
1095
+ schemas
1096
+ );
1097
+ return {
1098
+ layout: listAttributes,
1099
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1100
+ metadatas: listMetadatas,
1101
+ options: {
1102
+ ...schema?.options,
1103
+ ...schema?.pluginOptions,
1104
+ ...data.contentType.options
1105
+ }
1106
+ };
1107
+ };
1108
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1109
+ return columns.map((name) => {
1110
+ const attribute = attributes[name];
1111
+ if (!attribute) {
1112
+ return null;
1113
+ }
1114
+ const metadata = metadatas[name];
1115
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1116
+ return {
1117
+ attribute,
1118
+ label: metadata.label ?? "",
1119
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1120
+ schemas,
1121
+ components: components?.schemas ?? {}
1122
+ }),
1123
+ name,
1124
+ searchable: metadata.searchable ?? true,
1125
+ sortable: metadata.sortable ?? true
1126
+ };
1127
+ }).filter((field) => field !== null);
1128
+ };
1129
+ const useDocument = (args, opts) => {
1130
+ const { toggleNotification } = strapiAdmin.useNotification();
1131
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1132
+ const {
1133
+ currentData: data,
1134
+ isLoading: isLoadingDocument,
1135
+ isFetching: isFetchingDocument,
1136
+ error
1137
+ } = useGetDocumentQuery(args, {
1138
+ ...opts,
1139
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1140
+ });
1141
+ const {
1142
+ components,
1143
+ schema,
1144
+ schemas,
1145
+ isLoading: isLoadingSchema
1146
+ } = useContentTypeSchema(args.model);
1147
+ React__namespace.useEffect(() => {
1148
+ if (error) {
1149
+ toggleNotification({
1150
+ type: "danger",
1151
+ message: formatAPIError(error)
1152
+ });
1153
+ }
1154
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1155
+ const validationSchema = React__namespace.useMemo(() => {
1156
+ if (!schema) {
1157
+ return null;
1158
+ }
1159
+ return createYupSchema(schema.attributes, components);
1160
+ }, [schema, components]);
1161
+ const validate = React__namespace.useCallback(
1162
+ (document) => {
1163
+ if (!validationSchema) {
1164
+ throw new Error(
1165
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1166
+ );
1167
+ }
1168
+ try {
1169
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1170
+ return null;
1171
+ } catch (error2) {
1172
+ if (error2 instanceof yup.ValidationError) {
1173
+ return strapiAdmin.getYupValidationErrors(error2);
1174
+ }
1175
+ throw error2;
1176
+ }
1177
+ },
1178
+ [validationSchema]
1179
+ );
1180
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1181
+ const hasError = !!error;
1182
+ return {
1183
+ components,
1184
+ document: data?.data,
1185
+ meta: data?.meta,
1186
+ isLoading,
1187
+ hasError,
1188
+ schema,
1189
+ schemas,
1190
+ validate
1191
+ };
1192
+ };
1193
+ const useDoc = () => {
1194
+ const { id, slug, collectionType, origin } = reactRouterDom.useParams();
1195
+ const [{ query }] = strapiAdmin.useQueryParams();
1196
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1197
+ if (!collectionType) {
1198
+ throw new Error("Could not find collectionType in url params");
838
1199
  }
839
1200
  if (!slug) {
840
1201
  throw new Error("Could not find model in url params");
841
1202
  }
1203
+ const document = useDocument(
1204
+ { documentId: origin || id, model: slug, collectionType, params },
1205
+ {
1206
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1207
+ }
1208
+ );
1209
+ const returnId = origin || id === "create" ? void 0 : id;
842
1210
  return {
843
1211
  collectionType,
844
1212
  model: slug,
845
- id: origin || id === "create" ? void 0 : id,
846
- ...useDocument(
847
- { documentId: origin || id, model: slug, collectionType, params },
848
- {
849
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
850
- }
851
- )
1213
+ id: returnId,
1214
+ ...document
1215
+ };
1216
+ };
1217
+ const useContentManagerContext = () => {
1218
+ const {
1219
+ collectionType,
1220
+ model,
1221
+ id,
1222
+ components,
1223
+ isLoading: isLoadingDoc,
1224
+ schema,
1225
+ schemas
1226
+ } = useDoc();
1227
+ const layout = useDocumentLayout(model);
1228
+ const form = strapiAdmin.useForm("useContentManagerContext", (state) => state);
1229
+ const isSingleType = collectionType === SINGLE_TYPES;
1230
+ const slug = model;
1231
+ const isCreatingEntry = id === "create";
1232
+ useContentTypeSchema();
1233
+ const isLoading = isLoadingDoc || layout.isLoading;
1234
+ const error = layout.error;
1235
+ return {
1236
+ error,
1237
+ isLoading,
1238
+ // Base metadata
1239
+ model,
1240
+ collectionType,
1241
+ id,
1242
+ slug,
1243
+ isCreatingEntry,
1244
+ isSingleType,
1245
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1246
+ // All schema infos
1247
+ components,
1248
+ contentType: schema,
1249
+ contentTypes: schemas,
1250
+ // Form state
1251
+ form,
1252
+ // layout infos
1253
+ layout
852
1254
  };
853
1255
  };
854
1256
  const prefixPluginTranslations = (trad, pluginId) => {
855
- if (!pluginId) {
856
- throw new TypeError("pluginId can't be empty");
857
- }
858
1257
  return Object.keys(trad).reduce((acc, current) => {
859
1258
  acc[`${pluginId}.${current}`] = trad[current];
860
1259
  return acc;
@@ -870,6 +1269,8 @@ const useDocumentActions = () => {
870
1269
  const { formatMessage } = reactIntl.useIntl();
871
1270
  const { trackUsage } = strapiAdmin.useTracking();
872
1271
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1272
+ const navigate = reactRouterDom.useNavigate();
1273
+ const setCurrentStep = strapiAdmin.useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
873
1274
  const [deleteDocument] = useDeleteDocumentMutation();
874
1275
  const _delete = React__namespace.useCallback(
875
1276
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -948,12 +1349,13 @@ const useDocumentActions = () => {
948
1349
  );
949
1350
  const [discardDocument] = useDiscardDocumentMutation();
950
1351
  const discard = React__namespace.useCallback(
951
- async ({ collectionType, model, documentId }) => {
1352
+ async ({ collectionType, model, documentId, params }) => {
952
1353
  try {
953
1354
  const res = await discardDocument({
954
1355
  collectionType,
955
1356
  model,
956
- documentId
1357
+ documentId,
1358
+ params
957
1359
  });
958
1360
  if ("error" in res) {
959
1361
  toggleNotification({
@@ -1183,6 +1585,7 @@ const useDocumentActions = () => {
1183
1585
  defaultMessage: "Saved document"
1184
1586
  })
1185
1587
  });
1588
+ setCurrentStep("contentManager.success");
1186
1589
  return res.data;
1187
1590
  } catch (err) {
1188
1591
  toggleNotification({
@@ -1204,7 +1607,6 @@ const useDocumentActions = () => {
1204
1607
  sourceId
1205
1608
  });
1206
1609
  if ("error" in res) {
1207
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1208
1610
  return { error: res.error };
1209
1611
  }
1210
1612
  toggleNotification({
@@ -1223,7 +1625,7 @@ const useDocumentActions = () => {
1223
1625
  throw err;
1224
1626
  }
1225
1627
  },
1226
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1628
+ [autoCloneDocument, formatMessage, toggleNotification]
1227
1629
  );
1228
1630
  const [cloneDocument] = useCloneDocumentMutation();
1229
1631
  const clone = React__namespace.useCallback(
@@ -1249,6 +1651,7 @@ const useDocumentActions = () => {
1249
1651
  defaultMessage: "Cloned document"
1250
1652
  })
1251
1653
  });
1654
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1252
1655
  return res.data;
1253
1656
  } catch (err) {
1254
1657
  toggleNotification({
@@ -1259,7 +1662,7 @@ const useDocumentActions = () => {
1259
1662
  throw err;
1260
1663
  }
1261
1664
  },
1262
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1665
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1263
1666
  );
1264
1667
  const [getDoc] = useLazyGetDocumentQuery();
1265
1668
  const getDocument = React__namespace.useCallback(
@@ -1284,10 +1687,10 @@ const useDocumentActions = () => {
1284
1687
  update
1285
1688
  };
1286
1689
  };
1287
- const ProtectedHistoryPage = React.lazy(
1288
- () => Promise.resolve().then(() => require("./History-BfX6XmZK.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1690
+ const ProtectedHistoryPage = React__namespace.lazy(
1691
+ () => Promise.resolve().then(() => require("./History-BqRQm2Lc.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1289
1692
  );
1290
- const routes$1 = [
1693
+ const routes$2 = [
1291
1694
  {
1292
1695
  path: ":collectionType/:slug/:id/history",
1293
1696
  Component: ProtectedHistoryPage
@@ -1297,32 +1700,45 @@ const routes$1 = [
1297
1700
  Component: ProtectedHistoryPage
1298
1701
  }
1299
1702
  ];
1703
+ const ProtectedPreviewPage = React__namespace.lazy(
1704
+ () => Promise.resolve().then(() => require("./Preview-BqRUSbFU.js")).then((mod) => ({ default: mod.ProtectedPreviewPage }))
1705
+ );
1706
+ const routes$1 = [
1707
+ {
1708
+ path: ":collectionType/:slug/:id/preview",
1709
+ Component: ProtectedPreviewPage
1710
+ },
1711
+ {
1712
+ path: ":collectionType/:slug/preview",
1713
+ Component: ProtectedPreviewPage
1714
+ }
1715
+ ];
1300
1716
  const ProtectedEditViewPage = React.lazy(
1301
- () => Promise.resolve().then(() => require("./EditViewPage-CoQEnFlC.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1717
+ () => Promise.resolve().then(() => require("./EditViewPage-C-TZmK5r.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1302
1718
  );
1303
1719
  const ProtectedListViewPage = React.lazy(
1304
- () => Promise.resolve().then(() => require("./ListViewPage-Bk9VO__I.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1720
+ () => Promise.resolve().then(() => require("./ListViewPage-CYkrfWsm.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1305
1721
  );
1306
1722
  const ProtectedListConfiguration = React.lazy(
1307
- () => Promise.resolve().then(() => require("./ListConfigurationPage-B3CXj8PY.js")).then((mod) => ({
1723
+ () => Promise.resolve().then(() => require("./ListConfigurationPage-BT69-TvF.js")).then((mod) => ({
1308
1724
  default: mod.ProtectedListConfiguration
1309
1725
  }))
1310
1726
  );
1311
1727
  const ProtectedEditConfigurationPage = React.lazy(
1312
- () => Promise.resolve().then(() => require("./EditConfigurationPage-tDtWj7R2.js")).then((mod) => ({
1728
+ () => Promise.resolve().then(() => require("./EditConfigurationPage-CZRKq8CN.js")).then((mod) => ({
1313
1729
  default: mod.ProtectedEditConfigurationPage
1314
1730
  }))
1315
1731
  );
1316
1732
  const ProtectedComponentConfigurationPage = React.lazy(
1317
- () => Promise.resolve().then(() => require("./ComponentConfigurationPage-DJcn1DrO.js")).then((mod) => ({
1733
+ () => Promise.resolve().then(() => require("./ComponentConfigurationPage-BPiQrhZ6.js")).then((mod) => ({
1318
1734
  default: mod.ProtectedComponentConfigurationPage
1319
1735
  }))
1320
1736
  );
1321
1737
  const NoPermissions = React.lazy(
1322
- () => Promise.resolve().then(() => require("./NoPermissionsPage-BQDM64_b.js")).then((mod) => ({ default: mod.NoPermissions }))
1738
+ () => Promise.resolve().then(() => require("./NoPermissionsPage-DjUdvhJ6.js")).then((mod) => ({ default: mod.NoPermissions }))
1323
1739
  );
1324
1740
  const NoContentType = React.lazy(
1325
- () => Promise.resolve().then(() => require("./NoContentTypePage-DsB2F7Z1.js")).then((mod) => ({ default: mod.NoContentType }))
1741
+ () => Promise.resolve().then(() => require("./NoContentTypePage-BZMfRsrg.js")).then((mod) => ({ default: mod.NoContentType }))
1326
1742
  );
1327
1743
  const CollectionTypePages = () => {
1328
1744
  const { collectionType } = reactRouterDom.useParams();
@@ -1334,7 +1750,7 @@ const CollectionTypePages = () => {
1334
1750
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1335
1751
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1336
1752
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1337
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1753
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1338
1754
  const routes = [
1339
1755
  {
1340
1756
  path: LIST_RELATIVE_PATH,
@@ -1368,6 +1784,7 @@ const routes = [
1368
1784
  path: "no-content-types",
1369
1785
  Component: NoContentType
1370
1786
  },
1787
+ ...routes$2,
1371
1788
  ...routes$1
1372
1789
  ];
1373
1790
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1436,12 +1853,14 @@ const DocumentActionButton = (action) => {
1436
1853
  /* @__PURE__ */ jsxRuntime.jsx(
1437
1854
  designSystem.Button,
1438
1855
  {
1439
- flex: 1,
1856
+ flex: "auto",
1440
1857
  startIcon: action.icon,
1441
1858
  disabled: action.disabled,
1442
1859
  onClick: handleClick(action),
1443
1860
  justifyContent: "center",
1444
1861
  variant: action.variant || "default",
1862
+ paddingTop: "7px",
1863
+ paddingBottom: "7px",
1445
1864
  children: action.label
1446
1865
  }
1447
1866
  ),
@@ -1449,7 +1868,7 @@ const DocumentActionButton = (action) => {
1449
1868
  DocumentActionConfirmDialog,
1450
1869
  {
1451
1870
  ...action.dialog,
1452
- variant: action.variant,
1871
+ variant: action.dialog?.variant ?? action.variant,
1453
1872
  isOpen: dialogId === action.id,
1454
1873
  onClose: handleClose
1455
1874
  }
@@ -1464,6 +1883,11 @@ const DocumentActionButton = (action) => {
1464
1883
  ) : null
1465
1884
  ] });
1466
1885
  };
1886
+ const MenuItem = styledComponents.styled(designSystem.Menu.Item)`
1887
+ &:hover {
1888
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
1889
+ }
1890
+ `;
1467
1891
  const DocumentActionsMenu = ({
1468
1892
  actions: actions2,
1469
1893
  children,
@@ -1506,9 +1930,9 @@ const DocumentActionsMenu = ({
1506
1930
  disabled: isDisabled,
1507
1931
  size: "S",
1508
1932
  endIcon: null,
1509
- paddingTop: "7px",
1510
- paddingLeft: "9px",
1511
- paddingRight: "9px",
1933
+ paddingTop: "4px",
1934
+ paddingLeft: "7px",
1935
+ paddingRight: "7px",
1512
1936
  variant,
1513
1937
  children: [
1514
1938
  /* @__PURE__ */ jsxRuntime.jsx(Icons.More, { "aria-hidden": true, focusable: false }),
@@ -1519,36 +1943,35 @@ const DocumentActionsMenu = ({
1519
1943
  ]
1520
1944
  }
1521
1945
  ),
1522
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1946
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1523
1947
  actions2.map((action) => {
1524
1948
  return /* @__PURE__ */ jsxRuntime.jsx(
1525
- designSystem.Menu.Item,
1949
+ MenuItem,
1526
1950
  {
1527
1951
  disabled: action.disabled,
1528
1952
  onSelect: handleClick(action),
1529
1953
  display: "block",
1530
- children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", gap: 4, children: [
1531
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1532
- action.icon,
1533
- action.label
1534
- ] }),
1535
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsxRuntime.jsx(
1536
- designSystem.Flex,
1537
- {
1538
- alignItems: "center",
1539
- background: "alternative100",
1540
- borderStyle: "solid",
1541
- borderColor: "alternative200",
1542
- borderWidth: "1px",
1543
- height: 5,
1544
- paddingLeft: 2,
1545
- paddingRight: 2,
1546
- hasRadius: true,
1547
- color: "alternative600",
1548
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1549
- }
1550
- )
1551
- ] })
1954
+ isVariantDanger: action.variant === "danger",
1955
+ isDisabled: action.disabled,
1956
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(
1957
+ designSystem.Flex,
1958
+ {
1959
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1960
+ gap: 2,
1961
+ tag: "span",
1962
+ children: [
1963
+ /* @__PURE__ */ jsxRuntime.jsx(
1964
+ designSystem.Flex,
1965
+ {
1966
+ tag: "span",
1967
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1968
+ children: action.icon
1969
+ }
1970
+ ),
1971
+ action.label
1972
+ ]
1973
+ }
1974
+ ) })
1552
1975
  },
1553
1976
  action.id
1554
1977
  );
@@ -1590,8 +2013,20 @@ const convertActionVariantToColor = (variant = "secondary") => {
1590
2013
  return "primary600";
1591
2014
  }
1592
2015
  };
1593
- const DocumentActionConfirmDialog = ({
1594
- onClose,
2016
+ const convertActionVariantToIconColor = (variant = "secondary") => {
2017
+ switch (variant) {
2018
+ case "danger":
2019
+ return "danger600";
2020
+ case "secondary":
2021
+ return "neutral500";
2022
+ case "success":
2023
+ return "success600";
2024
+ default:
2025
+ return "primary600";
2026
+ }
2027
+ };
2028
+ const DocumentActionConfirmDialog = ({
2029
+ onClose,
1595
2030
  onCancel,
1596
2031
  onConfirm,
1597
2032
  title,
@@ -1612,22 +2047,20 @@ const DocumentActionConfirmDialog = ({
1612
2047
  }
1613
2048
  onClose();
1614
2049
  };
1615
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog, { isOpen, title, onClose: handleClose, children: [
1616
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.DialogBody, { children: content }),
1617
- /* @__PURE__ */ jsxRuntime.jsx(
1618
- designSystem.DialogFooter,
1619
- {
1620
- startAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleClose, variant: "tertiary", children: formatMessage({
1621
- id: "app.components.Button.cancel",
1622
- defaultMessage: "Cancel"
1623
- }) }),
1624
- endAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, children: formatMessage({
1625
- id: "app.components.Button.confirm",
1626
- defaultMessage: "Confirm"
1627
- }) })
1628
- }
1629
- )
1630
- ] });
2050
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2051
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2052
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: content }),
2053
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
2054
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
2055
+ id: "app.components.Button.cancel",
2056
+ defaultMessage: "Cancel"
2057
+ }) }) }),
2058
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
2059
+ id: "app.components.Button.confirm",
2060
+ defaultMessage: "Confirm"
2061
+ }) })
2062
+ ] })
2063
+ ] }) });
1631
2064
  };
1632
2065
  const DocumentActionModal = ({
1633
2066
  isOpen,
@@ -1637,34 +2070,29 @@ const DocumentActionModal = ({
1637
2070
  content: Content,
1638
2071
  onModalClose
1639
2072
  }) => {
1640
- const id = React__namespace.useId();
1641
- if (!isOpen) {
1642
- return null;
1643
- }
1644
2073
  const handleClose = () => {
1645
2074
  if (onClose) {
1646
2075
  onClose();
1647
2076
  }
1648
2077
  onModalClose();
1649
2078
  };
1650
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ModalLayout, { borderRadius: "4px", overflow: "hidden", onClose: handleClose, labelledBy: id, children: [
1651
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: "neutral800", tag: "h2", id, children: title }) }),
1652
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalBody, { children: typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content }),
1653
- /* @__PURE__ */ jsxRuntime.jsx(
1654
- designSystem.Box,
1655
- {
1656
- paddingTop: 4,
1657
- paddingBottom: 4,
1658
- paddingLeft: 5,
1659
- paddingRight: 5,
1660
- borderWidth: "1px 0 0 0",
1661
- borderStyle: "solid",
1662
- borderColor: "neutral150",
1663
- background: "neutral100",
1664
- children: typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer
1665
- }
1666
- )
1667
- ] });
2079
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
2080
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: title }) }),
2081
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: Content }),
2082
+ typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer
2083
+ ] }) });
2084
+ };
2085
+ const transformData = (data) => {
2086
+ if (Array.isArray(data)) {
2087
+ return data.map(transformData);
2088
+ }
2089
+ if (typeof data === "object" && data !== null) {
2090
+ if ("apiData" in data) {
2091
+ return data.apiData;
2092
+ }
2093
+ return mapValues__default.default(transformData)(data);
2094
+ }
2095
+ return data;
1668
2096
  };
1669
2097
  const PublishAction$1 = ({
1670
2098
  activeTab,
@@ -1678,13 +2106,18 @@ const PublishAction$1 = ({
1678
2106
  const navigate = reactRouterDom.useNavigate();
1679
2107
  const { toggleNotification } = strapiAdmin.useNotification();
1680
2108
  const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
2109
+ const isListView = reactRouterDom.useMatch(LIST_PATH) !== null;
1681
2110
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2111
+ const { id } = reactRouterDom.useParams();
1682
2112
  const { formatMessage } = reactIntl.useIntl();
1683
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1684
- "PublishAction",
1685
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1686
- );
2113
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1687
2114
  const { publish } = useDocumentActions();
2115
+ const [
2116
+ countDraftRelations,
2117
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2118
+ ] = useLazyGetDraftRelationCountQuery();
2119
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React__namespace.useState(0);
2120
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React__namespace.useState(0);
1688
2121
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1689
2122
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1690
2123
  const modified = strapiAdmin.useForm("PublishAction", ({ modified: modified2 }) => modified2);
@@ -1693,10 +2126,107 @@ const PublishAction$1 = ({
1693
2126
  const validate = strapiAdmin.useForm("PublishAction", (state) => state.validate);
1694
2127
  const setErrors = strapiAdmin.useForm("PublishAction", (state) => state.setErrors);
1695
2128
  const formValues = strapiAdmin.useForm("PublishAction", ({ values }) => values);
2129
+ React__namespace.useEffect(() => {
2130
+ if (isErrorDraftRelations) {
2131
+ toggleNotification({
2132
+ type: "danger",
2133
+ message: formatMessage({
2134
+ id: getTranslation("error.records.fetch-draft-relatons"),
2135
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2136
+ })
2137
+ });
2138
+ }
2139
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2140
+ React__namespace.useEffect(() => {
2141
+ const localDraftRelations = /* @__PURE__ */ new Set();
2142
+ const extractDraftRelations = (data) => {
2143
+ const relations = data.connect || [];
2144
+ relations.forEach((relation) => {
2145
+ if (relation.status === "draft") {
2146
+ localDraftRelations.add(relation.id);
2147
+ }
2148
+ });
2149
+ };
2150
+ const traverseAndExtract = (data) => {
2151
+ Object.entries(data).forEach(([key, value]) => {
2152
+ if (key === "connect" && Array.isArray(value)) {
2153
+ extractDraftRelations({ connect: value });
2154
+ } else if (typeof value === "object" && value !== null) {
2155
+ traverseAndExtract(value);
2156
+ }
2157
+ });
2158
+ };
2159
+ if (!documentId || modified) {
2160
+ traverseAndExtract(formValues);
2161
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2162
+ }
2163
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2164
+ React__namespace.useEffect(() => {
2165
+ if (!document || !document.documentId || isListView) {
2166
+ return;
2167
+ }
2168
+ const fetchDraftRelationsCount = async () => {
2169
+ const { data, error } = await countDraftRelations({
2170
+ collectionType,
2171
+ model,
2172
+ documentId,
2173
+ params
2174
+ });
2175
+ if (error) {
2176
+ throw error;
2177
+ }
2178
+ if (data) {
2179
+ setServerCountOfDraftRelations(data.data);
2180
+ }
2181
+ };
2182
+ fetchDraftRelationsCount();
2183
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1696
2184
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1697
2185
  if (!schema?.options?.draftAndPublish) {
1698
2186
  return null;
1699
2187
  }
2188
+ const performPublish = async () => {
2189
+ setSubmitting(true);
2190
+ try {
2191
+ const { errors } = await validate(true, {
2192
+ status: "published"
2193
+ });
2194
+ if (errors) {
2195
+ toggleNotification({
2196
+ type: "danger",
2197
+ message: formatMessage({
2198
+ id: "content-manager.validation.error",
2199
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2200
+ })
2201
+ });
2202
+ return;
2203
+ }
2204
+ const res = await publish(
2205
+ {
2206
+ collectionType,
2207
+ model,
2208
+ documentId,
2209
+ params
2210
+ },
2211
+ transformData(formValues)
2212
+ );
2213
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2214
+ if (id === "create") {
2215
+ navigate({
2216
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2217
+ search: rawQuery
2218
+ });
2219
+ }
2220
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2221
+ setErrors(formatValidationErrors(res.error));
2222
+ }
2223
+ } finally {
2224
+ setSubmitting(false);
2225
+ }
2226
+ };
2227
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2228
+ const enableDraftRelationsCount = false;
2229
+ const hasDraftRelations = enableDraftRelationsCount;
1700
2230
  return {
1701
2231
  /**
1702
2232
  * Disabled when:
@@ -1706,52 +2236,40 @@ const PublishAction$1 = ({
1706
2236
  * - the document is already published & not modified
1707
2237
  * - the document is being created & not modified
1708
2238
  * - the user doesn't have the permission to publish
1709
- * - the user doesn't have the permission to create a new document
1710
- * - the user doesn't have the permission to update the document
1711
2239
  */
1712
- disabled: isCloning || isSubmitting || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2240
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1713
2241
  label: formatMessage({
1714
2242
  id: "app.utils.publish",
1715
2243
  defaultMessage: "Publish"
1716
2244
  }),
1717
2245
  onClick: async () => {
1718
- setSubmitting(true);
1719
- try {
1720
- const { errors } = await validate();
1721
- if (errors) {
1722
- toggleNotification({
1723
- type: "danger",
1724
- message: formatMessage({
1725
- id: "content-manager.validation.error",
1726
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1727
- })
1728
- });
1729
- return;
1730
- }
1731
- const res = await publish(
1732
- {
1733
- collectionType,
1734
- model,
1735
- documentId,
1736
- params
1737
- },
1738
- formValues
1739
- );
1740
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1741
- navigate({
1742
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1743
- search: rawQuery
1744
- });
1745
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1746
- setErrors(formatValidationErrors(res.error));
2246
+ await performPublish();
2247
+ },
2248
+ dialog: hasDraftRelations ? {
2249
+ type: "dialog",
2250
+ variant: "danger",
2251
+ footer: null,
2252
+ title: formatMessage({
2253
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2254
+ defaultMessage: "Confirmation"
2255
+ }),
2256
+ content: formatMessage(
2257
+ {
2258
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2259
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2260
+ },
2261
+ {
2262
+ count: totalDraftRelations
1747
2263
  }
1748
- } finally {
1749
- setSubmitting(false);
2264
+ ),
2265
+ onConfirm: async () => {
2266
+ await performPublish();
1750
2267
  }
1751
- }
2268
+ } : void 0
1752
2269
  };
1753
2270
  };
1754
2271
  PublishAction$1.type = "publish";
2272
+ PublishAction$1.position = "panel";
1755
2273
  const UpdateAction = ({
1756
2274
  activeTab,
1757
2275
  documentId,
@@ -1764,10 +2282,6 @@ const UpdateAction = ({
1764
2282
  const cloneMatch = reactRouterDom.useMatch(CLONE_PATH);
1765
2283
  const isCloning = cloneMatch !== null;
1766
2284
  const { formatMessage } = reactIntl.useIntl();
1767
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1768
- canCreate: canCreate2,
1769
- canUpdate: canUpdate2
1770
- }));
1771
2285
  const { create, update, clone } = useDocumentActions();
1772
2286
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1773
2287
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
@@ -1784,18 +2298,18 @@ const UpdateAction = ({
1784
2298
  * - the form is submitting
1785
2299
  * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1786
2300
  * - the active tab is the published tab
1787
- * - the user doesn't have the permission to create a new document
1788
- * - the user doesn't have the permission to update the document
1789
2301
  */
1790
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
2302
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1791
2303
  label: formatMessage({
1792
- id: "content-manager.containers.Edit.save",
2304
+ id: "global.save",
1793
2305
  defaultMessage: "Save"
1794
2306
  }),
1795
2307
  onClick: async () => {
1796
2308
  setSubmitting(true);
1797
2309
  try {
1798
- const { errors } = await validate();
2310
+ const { errors } = await validate(true, {
2311
+ status: "draft"
2312
+ });
1799
2313
  if (errors) {
1800
2314
  toggleNotification({
1801
2315
  type: "danger",
@@ -1813,13 +2327,16 @@ const UpdateAction = ({
1813
2327
  documentId: cloneMatch.params.origin,
1814
2328
  params
1815
2329
  },
1816
- document
2330
+ transformData(document)
1817
2331
  );
1818
2332
  if ("data" in res) {
1819
- navigate({
1820
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1821
- search: rawQuery
1822
- });
2333
+ navigate(
2334
+ {
2335
+ pathname: `../${res.data.documentId}`,
2336
+ search: rawQuery
2337
+ },
2338
+ { relative: "path" }
2339
+ );
1823
2340
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1824
2341
  setErrors(formatValidationErrors(res.error));
1825
2342
  }
@@ -1831,7 +2348,7 @@ const UpdateAction = ({
1831
2348
  documentId,
1832
2349
  params
1833
2350
  },
1834
- document
2351
+ transformData(document)
1835
2352
  );
1836
2353
  if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1837
2354
  setErrors(formatValidationErrors(res.error));
@@ -1844,13 +2361,16 @@ const UpdateAction = ({
1844
2361
  model,
1845
2362
  params
1846
2363
  },
1847
- document
2364
+ transformData(document)
1848
2365
  );
1849
2366
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1850
- navigate({
1851
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1852
- search: rawQuery
1853
- });
2367
+ navigate(
2368
+ {
2369
+ pathname: `../${res.data.documentId}`,
2370
+ search: rawQuery
2371
+ },
2372
+ { replace: true, relative: "path" }
2373
+ );
1854
2374
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1855
2375
  setErrors(formatValidationErrors(res.error));
1856
2376
  }
@@ -1862,6 +2382,7 @@ const UpdateAction = ({
1862
2382
  };
1863
2383
  };
1864
2384
  UpdateAction.type = "update";
2385
+ UpdateAction.position = "panel";
1865
2386
  const UNPUBLISH_DRAFT_OPTIONS = {
1866
2387
  KEEP: "keep",
1867
2388
  DISCARD: "discard"
@@ -1882,10 +2403,8 @@ const UnpublishAction$1 = ({
1882
2403
  const { toggleNotification } = strapiAdmin.useNotification();
1883
2404
  const [shouldKeepDraft, setShouldKeepDraft] = React__namespace.useState(true);
1884
2405
  const isDocumentModified = document?.status === "modified";
1885
- const handleChange = (e) => {
1886
- if ("value" in e.target) {
1887
- setShouldKeepDraft(e.target.value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
1888
- }
2406
+ const handleChange = (value) => {
2407
+ setShouldKeepDraft(value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
1889
2408
  };
1890
2409
  if (!schema?.options?.draftAndPublish) {
1891
2410
  return null;
@@ -1896,7 +2415,7 @@ const UnpublishAction$1 = ({
1896
2415
  id: "app.utils.unpublish",
1897
2416
  defaultMessage: "Unpublish"
1898
2417
  }),
1899
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2418
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
1900
2419
  onClick: async () => {
1901
2420
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1902
2421
  if (!documentId) {
@@ -1935,40 +2454,24 @@ const UnpublishAction$1 = ({
1935
2454
  }) })
1936
2455
  ] }),
1937
2456
  /* @__PURE__ */ jsxRuntime.jsxs(
1938
- designSystem.Flex,
2457
+ designSystem.Radio.Group,
1939
2458
  {
1940
- onChange: handleChange,
1941
- direction: "column",
1942
- alignItems: "flex-start",
1943
- tag: "fieldset",
1944
- borderWidth: 0,
1945
- gap: 3,
2459
+ defaultValue: UNPUBLISH_DRAFT_OPTIONS.KEEP,
2460
+ name: "discard-options",
2461
+ "aria-label": formatMessage({
2462
+ id: "content-manager.actions.unpublish.dialog.radio-label",
2463
+ defaultMessage: "Choose an option to unpublish the document."
2464
+ }),
2465
+ onValueChange: handleChange,
1946
2466
  children: [
1947
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { tag: "legend" }),
1948
- /* @__PURE__ */ jsxRuntime.jsx(
1949
- designSystem.Radio,
1950
- {
1951
- checked: shouldKeepDraft,
1952
- value: UNPUBLISH_DRAFT_OPTIONS.KEEP,
1953
- name: "discard-options",
1954
- children: formatMessage({
1955
- id: "content-manager.actions.unpublish.dialog.option.keep-draft",
1956
- defaultMessage: "Keep draft"
1957
- })
1958
- }
1959
- ),
1960
- /* @__PURE__ */ jsxRuntime.jsx(
1961
- designSystem.Radio,
1962
- {
1963
- checked: !shouldKeepDraft,
1964
- value: UNPUBLISH_DRAFT_OPTIONS.DISCARD,
1965
- name: "discard-options",
1966
- children: formatMessage({
1967
- id: "content-manager.actions.unpublish.dialog.option.replace-draft",
1968
- defaultMessage: "Replace draft"
1969
- })
1970
- }
1971
- )
2467
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { checked: shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.KEEP, children: formatMessage({
2468
+ id: "content-manager.actions.unpublish.dialog.option.keep-draft",
2469
+ defaultMessage: "Keep draft"
2470
+ }) }),
2471
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { checked: !shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.DISCARD, children: formatMessage({
2472
+ id: "content-manager.actions.unpublish.dialog.option.replace-draft",
2473
+ defaultMessage: "Replace draft"
2474
+ }) })
1972
2475
  ]
1973
2476
  }
1974
2477
  )
@@ -2002,6 +2505,7 @@ const UnpublishAction$1 = ({
2002
2505
  };
2003
2506
  };
2004
2507
  UnpublishAction$1.type = "unpublish";
2508
+ UnpublishAction$1.position = "panel";
2005
2509
  const DiscardAction = ({
2006
2510
  activeTab,
2007
2511
  documentId,
@@ -2024,7 +2528,7 @@ const DiscardAction = ({
2024
2528
  id: "content-manager.actions.discard.label",
2025
2529
  defaultMessage: "Discard changes"
2026
2530
  }),
2027
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2531
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
2028
2532
  position: ["panel", "table-row"],
2029
2533
  variant: "danger",
2030
2534
  dialog: {
@@ -2052,11 +2556,7 @@ const DiscardAction = ({
2052
2556
  };
2053
2557
  };
2054
2558
  DiscardAction.type = "discard";
2055
- const StyledCrossCircle = styledComponents.styled(Icons.CrossCircle)`
2056
- path {
2057
- fill: currentColor;
2058
- }
2059
- `;
2559
+ DiscardAction.position = "panel";
2060
2560
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2061
2561
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2062
2562
  const RelativeTime = React__namespace.forwardRef(
@@ -2069,7 +2569,7 @@ const RelativeTime = React__namespace.forwardRef(
2069
2569
  });
2070
2570
  const unit = intervals.find((intervalUnit) => {
2071
2571
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2072
- });
2572
+ }) ?? "seconds";
2073
2573
  const relativeTime = dateFns.isPast(timestamp) ? -interval[unit] : interval[unit];
2074
2574
  const customInterval = customIntervals.find(
2075
2575
  (custom) => interval[custom.unit] < custom.threshold
@@ -2103,34 +2603,34 @@ const getDisplayName = ({
2103
2603
  return email ?? "";
2104
2604
  };
2105
2605
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2106
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2107
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2108
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2606
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2607
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2608
+ const { formatMessage } = reactIntl.useIntl();
2609
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2610
+ id: `content-manager.containers.List.${status}`,
2611
+ defaultMessage: capitalise(status)
2612
+ }) }) });
2109
2613
  };
2110
2614
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2111
2615
  const { formatMessage } = reactIntl.useIntl();
2112
2616
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2617
+ const params = reactRouterDom.useParams();
2113
2618
  const title = isCreating ? formatMessage({
2114
2619
  id: "content-manager.containers.edit.title.new",
2115
2620
  defaultMessage: "Create an entry"
2116
2621
  }) : documentTitle;
2117
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 8, paddingBottom: 4, gap: 3, children: [
2118
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.BackButton, {}),
2119
- /* @__PURE__ */ jsxRuntime.jsxs(
2120
- designSystem.Flex,
2622
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2623
+ /* @__PURE__ */ jsxRuntime.jsx(
2624
+ strapiAdmin.BackButton,
2121
2625
  {
2122
- width: "100%",
2123
- justifyContent: "space-between",
2124
- paddingTop: 1,
2125
- gap: "80px",
2126
- alignItems: "flex-start",
2127
- children: [
2128
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2129
- /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2130
- ]
2626
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2131
2627
  }
2132
2628
  ),
2133
- status ? /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) : null
2629
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2630
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2631
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2632
+ ] }),
2633
+ status ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2134
2634
  ] });
2135
2635
  };
2136
2636
  const HeaderToolbar = () => {
@@ -2176,7 +2676,7 @@ const HeaderToolbar = () => {
2176
2676
  meta: isCloning ? void 0 : meta,
2177
2677
  collectionType
2178
2678
  },
2179
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2679
+ descriptions: plugins["content-manager"].apis.getDocumentActions("header"),
2180
2680
  children: (actions2) => {
2181
2681
  const headerActions = actions2.filter((action) => {
2182
2682
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2213,12 +2713,12 @@ const Information = ({ activeTab }) => {
2213
2713
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2214
2714
  label: formatMessage({
2215
2715
  id: "content-manager.containers.edit.information.last-published.label",
2216
- defaultMessage: "Last published"
2716
+ defaultMessage: "Published"
2217
2717
  }),
2218
2718
  value: formatMessage(
2219
2719
  {
2220
2720
  id: "content-manager.containers.edit.information.last-published.value",
2221
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2721
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2222
2722
  },
2223
2723
  {
2224
2724
  time: /* @__PURE__ */ jsxRuntime.jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2231,12 +2731,12 @@ const Information = ({ activeTab }) => {
2231
2731
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2232
2732
  label: formatMessage({
2233
2733
  id: "content-manager.containers.edit.information.last-draft.label",
2234
- defaultMessage: "Last draft"
2734
+ defaultMessage: "Updated"
2235
2735
  }),
2236
2736
  value: formatMessage(
2237
2737
  {
2238
2738
  id: "content-manager.containers.edit.information.last-draft.value",
2239
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2739
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2240
2740
  },
2241
2741
  {
2242
2742
  time: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2254,12 +2754,12 @@ const Information = ({ activeTab }) => {
2254
2754
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2255
2755
  label: formatMessage({
2256
2756
  id: "content-manager.containers.edit.information.document.label",
2257
- defaultMessage: "Document"
2757
+ defaultMessage: "Created"
2258
2758
  }),
2259
2759
  value: formatMessage(
2260
2760
  {
2261
2761
  id: "content-manager.containers.edit.information.document.value",
2262
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2762
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2263
2763
  },
2264
2764
  {
2265
2765
  time: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2297,25 +2797,77 @@ const Information = ({ activeTab }) => {
2297
2797
  );
2298
2798
  };
2299
2799
  const HeaderActions = ({ actions: actions2 }) => {
2300
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: actions2.map((action) => {
2301
- if ("options" in action) {
2800
+ const [dialogId, setDialogId] = React__namespace.useState(null);
2801
+ const handleClick = (action) => async (e) => {
2802
+ if (!("options" in action)) {
2803
+ const { onClick = () => false, dialog, id } = action;
2804
+ const muteDialog = await onClick(e);
2805
+ if (dialog && !muteDialog) {
2806
+ e.preventDefault();
2807
+ setDialogId(id);
2808
+ }
2809
+ }
2810
+ };
2811
+ const handleClose = () => {
2812
+ setDialogId(null);
2813
+ };
2814
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, children: actions2.map((action) => {
2815
+ if (action.options) {
2302
2816
  return /* @__PURE__ */ jsxRuntime.jsx(
2303
2817
  designSystem.SingleSelect,
2304
2818
  {
2305
2819
  size: "S",
2306
- disabled: action.disabled,
2307
- "aria-label": action.label,
2308
2820
  onChange: action.onSelect,
2309
- value: action.value,
2821
+ "aria-label": action.label,
2822
+ ...action,
2310
2823
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { ...option, children: label }, option.value))
2311
2824
  },
2312
2825
  action.id
2313
2826
  );
2314
2827
  } else {
2315
- return null;
2828
+ if (action.type === "icon") {
2829
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
2830
+ /* @__PURE__ */ jsxRuntime.jsx(
2831
+ designSystem.IconButton,
2832
+ {
2833
+ disabled: action.disabled,
2834
+ label: action.label,
2835
+ size: "S",
2836
+ onClick: handleClick(action),
2837
+ children: action.icon
2838
+ }
2839
+ ),
2840
+ action.dialog ? /* @__PURE__ */ jsxRuntime.jsx(
2841
+ HeaderActionDialog,
2842
+ {
2843
+ ...action.dialog,
2844
+ isOpen: dialogId === action.id,
2845
+ onClose: handleClose
2846
+ }
2847
+ ) : null
2848
+ ] }, action.id);
2849
+ }
2316
2850
  }
2317
2851
  }) });
2318
2852
  };
2853
+ const HeaderActionDialog = ({
2854
+ onClose,
2855
+ onCancel,
2856
+ title,
2857
+ content: Content,
2858
+ isOpen
2859
+ }) => {
2860
+ const handleClose = async () => {
2861
+ if (onCancel) {
2862
+ await onCancel();
2863
+ }
2864
+ onClose();
2865
+ };
2866
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2867
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2868
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content
2869
+ ] }) });
2870
+ };
2319
2871
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2320
2872
  const navigate = reactRouterDom.useNavigate();
2321
2873
  const { formatMessage } = reactIntl.useIntl();
@@ -2332,6 +2884,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2332
2884
  };
2333
2885
  };
2334
2886
  ConfigureTheViewAction.type = "configure-the-view";
2887
+ ConfigureTheViewAction.position = "header";
2335
2888
  const EditTheModelAction = ({ model }) => {
2336
2889
  const navigate = reactRouterDom.useNavigate();
2337
2890
  const { formatMessage } = reactIntl.useIntl();
@@ -2348,6 +2901,7 @@ const EditTheModelAction = ({ model }) => {
2348
2901
  };
2349
2902
  };
2350
2903
  EditTheModelAction.type = "edit-the-model";
2904
+ EditTheModelAction.position = "header";
2351
2905
  const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2352
2906
  const navigate = reactRouterDom.useNavigate();
2353
2907
  const { formatMessage } = reactIntl.useIntl();
@@ -2356,12 +2910,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2356
2910
  const { delete: deleteAction } = useDocumentActions();
2357
2911
  const { toggleNotification } = strapiAdmin.useNotification();
2358
2912
  const setSubmitting = strapiAdmin.useForm("DeleteAction", (state) => state.setSubmitting);
2913
+ const isLocalized = document?.locale != null;
2359
2914
  return {
2360
2915
  disabled: !canDelete || !document,
2361
- label: formatMessage({
2362
- id: "content-manager.actions.delete.label",
2363
- defaultMessage: "Delete document"
2364
- }),
2916
+ label: formatMessage(
2917
+ {
2918
+ id: "content-manager.actions.delete.label",
2919
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2920
+ },
2921
+ { isLocalized }
2922
+ ),
2365
2923
  icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2366
2924
  dialog: {
2367
2925
  type: "dialog",
@@ -2417,6 +2975,7 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2417
2975
  };
2418
2976
  };
2419
2977
  DeleteAction$1.type = "delete";
2978
+ DeleteAction$1.position = ["header", "table-row"];
2420
2979
  const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2421
2980
  const Panels = () => {
2422
2981
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
@@ -2451,7 +3010,7 @@ const ActionsPanel = () => {
2451
3010
  return {
2452
3011
  title: formatMessage({
2453
3012
  id: "content-manager.containers.edit.panels.default.title",
2454
- defaultMessage: "Document"
3013
+ defaultMessage: "Entry"
2455
3014
  }),
2456
3015
  content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2457
3016
  };
@@ -2479,7 +3038,7 @@ const ActionsPanelContent = () => {
2479
3038
  strapiAdmin.DescriptionComponentRenderer,
2480
3039
  {
2481
3040
  props,
2482
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3041
+ descriptions: plugins["content-manager"].apis.getDocumentActions("panel"),
2483
3042
  children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
2484
3043
  }
2485
3044
  ),
@@ -2506,314 +3065,12 @@ const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
2506
3065
  justifyContent: "stretch",
2507
3066
  alignItems: "flex-start",
2508
3067
  children: [
2509
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
3068
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
2510
3069
  children
2511
3070
  ]
2512
3071
  }
2513
- );
2514
- });
2515
- const HOOKS = {
2516
- /**
2517
- * Hook that allows to mutate the displayed headers of the list view table
2518
- * @constant
2519
- * @type {string}
2520
- */
2521
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2522
- /**
2523
- * Hook that allows to mutate the CM's collection types links pre-set filters
2524
- * @constant
2525
- * @type {string}
2526
- */
2527
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2528
- /**
2529
- * Hook that allows to mutate the CM's edit view layout
2530
- * @constant
2531
- * @type {string}
2532
- */
2533
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2534
- /**
2535
- * Hook that allows to mutate the CM's single types links pre-set filters
2536
- * @constant
2537
- * @type {string}
2538
- */
2539
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2540
- };
2541
- const contentTypesApi = contentManagerApi.injectEndpoints({
2542
- endpoints: (builder) => ({
2543
- getContentTypeConfiguration: builder.query({
2544
- query: (uid) => ({
2545
- url: `/content-manager/content-types/${uid}/configuration`,
2546
- method: "GET"
2547
- }),
2548
- transformResponse: (response) => response.data,
2549
- providesTags: (_result, _error, uid) => [
2550
- { type: "ContentTypesConfiguration", id: uid },
2551
- { type: "ContentTypeSettings", id: "LIST" }
2552
- ]
2553
- }),
2554
- getAllContentTypeSettings: builder.query({
2555
- query: () => "/content-manager/content-types-settings",
2556
- transformResponse: (response) => response.data,
2557
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2558
- }),
2559
- updateContentTypeConfiguration: builder.mutation({
2560
- query: ({ uid, ...body }) => ({
2561
- url: `/content-manager/content-types/${uid}/configuration`,
2562
- method: "PUT",
2563
- data: body
2564
- }),
2565
- transformResponse: (response) => response.data,
2566
- invalidatesTags: (_result, _error, { uid }) => [
2567
- { type: "ContentTypesConfiguration", id: uid },
2568
- { type: "ContentTypeSettings", id: "LIST" },
2569
- // Is this necessary?
2570
- { type: "InitialData" }
2571
- ]
2572
- })
2573
- })
2574
- });
2575
- const {
2576
- useGetContentTypeConfigurationQuery,
2577
- useGetAllContentTypeSettingsQuery,
2578
- useUpdateContentTypeConfigurationMutation
2579
- } = contentTypesApi;
2580
- const checkIfAttributeIsDisplayable = (attribute) => {
2581
- const { type } = attribute;
2582
- if (type === "relation") {
2583
- return !attribute.relation.toLowerCase().includes("morph");
2584
- }
2585
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2586
- };
2587
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2588
- if (!mainFieldName) {
2589
- return void 0;
2590
- }
2591
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2592
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2593
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2594
- );
2595
- return {
2596
- name: mainFieldName,
2597
- type: mainFieldType ?? "string"
2598
- };
2599
- };
2600
- const DEFAULT_SETTINGS = {
2601
- bulkable: false,
2602
- filterable: false,
2603
- searchable: false,
2604
- pagination: false,
2605
- defaultSortBy: "",
2606
- defaultSortOrder: "asc",
2607
- mainField: "id",
2608
- pageSize: 10
2609
- };
2610
- const useDocumentLayout = (model) => {
2611
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2612
- const [{ query }] = strapiAdmin.useQueryParams();
2613
- const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2614
- const { toggleNotification } = strapiAdmin.useNotification();
2615
- const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
2616
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2617
- const {
2618
- data,
2619
- isLoading: isLoadingConfigs,
2620
- error,
2621
- isFetching: isFetchingConfigs
2622
- } = useGetContentTypeConfigurationQuery(model);
2623
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2624
- React__namespace.useEffect(() => {
2625
- if (error) {
2626
- toggleNotification({
2627
- type: "danger",
2628
- message: formatAPIError(error)
2629
- });
2630
- }
2631
- }, [error, formatAPIError, toggleNotification]);
2632
- const editLayout = React__namespace.useMemo(
2633
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2634
- layout: [],
2635
- components: {},
2636
- metadatas: {},
2637
- options: {},
2638
- settings: DEFAULT_SETTINGS
2639
- },
2640
- [data, isLoading, schemas, schema, components]
2641
- );
2642
- const listLayout = React__namespace.useMemo(() => {
2643
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2644
- layout: [],
2645
- metadatas: {},
2646
- options: {},
2647
- settings: DEFAULT_SETTINGS
2648
- };
2649
- }, [data, isLoading, schemas, schema, components]);
2650
- const { layout: edit } = React__namespace.useMemo(
2651
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2652
- layout: editLayout,
2653
- query
2654
- }),
2655
- [editLayout, query, runHookWaterfall]
2656
- );
2657
- return {
2658
- error,
2659
- isLoading,
2660
- edit,
2661
- list: listLayout
2662
- };
2663
- };
2664
- const useDocLayout = () => {
2665
- const { model } = useDoc();
2666
- return useDocumentLayout(model);
2667
- };
2668
- const formatEditLayout = (data, {
2669
- schemas,
2670
- schema,
2671
- components
2672
- }) => {
2673
- let currentPanelIndex = 0;
2674
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2675
- data.contentType.layouts.edit,
2676
- schema?.attributes,
2677
- data.contentType.metadatas,
2678
- { configurations: data.components, schemas: components },
2679
- schemas
2680
- ).reduce((panels, row) => {
2681
- if (row.some((field) => field.type === "dynamiczone")) {
2682
- panels.push([row]);
2683
- currentPanelIndex += 2;
2684
- } else {
2685
- if (!panels[currentPanelIndex]) {
2686
- panels.push([]);
2687
- }
2688
- panels[currentPanelIndex].push(row);
2689
- }
2690
- return panels;
2691
- }, []);
2692
- const componentEditAttributes = Object.entries(data.components).reduce(
2693
- (acc, [uid, configuration]) => {
2694
- acc[uid] = {
2695
- layout: convertEditLayoutToFieldLayouts(
2696
- configuration.layouts.edit,
2697
- components[uid].attributes,
2698
- configuration.metadatas
2699
- ),
2700
- settings: {
2701
- ...configuration.settings,
2702
- icon: components[uid].info.icon,
2703
- displayName: components[uid].info.displayName
2704
- }
2705
- };
2706
- return acc;
2707
- },
2708
- {}
2709
- );
2710
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2711
- (acc, [attribute, metadata]) => {
2712
- return {
2713
- ...acc,
2714
- [attribute]: metadata.edit
2715
- };
2716
- },
2717
- {}
2718
- );
2719
- return {
2720
- layout: panelledEditAttributes,
2721
- components: componentEditAttributes,
2722
- metadatas: editMetadatas,
2723
- settings: {
2724
- ...data.contentType.settings,
2725
- displayName: schema?.info.displayName
2726
- },
2727
- options: {
2728
- ...schema?.options,
2729
- ...schema?.pluginOptions,
2730
- ...data.contentType.options
2731
- }
2732
- };
2733
- };
2734
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2735
- return rows.map(
2736
- (row) => row.map((field) => {
2737
- const attribute = attributes[field.name];
2738
- if (!attribute) {
2739
- return null;
2740
- }
2741
- const { edit: metadata } = metadatas[field.name];
2742
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2743
- return {
2744
- attribute,
2745
- disabled: !metadata.editable,
2746
- hint: metadata.description,
2747
- label: metadata.label ?? "",
2748
- name: field.name,
2749
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2750
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2751
- schemas,
2752
- components: components?.schemas ?? {}
2753
- }),
2754
- placeholder: metadata.placeholder ?? "",
2755
- required: attribute.required ?? false,
2756
- size: field.size,
2757
- unique: "unique" in attribute ? attribute.unique : false,
2758
- visible: metadata.visible ?? true,
2759
- type: attribute.type
2760
- };
2761
- }).filter((field) => field !== null)
2762
- );
2763
- };
2764
- const formatListLayout = (data, {
2765
- schemas,
2766
- schema,
2767
- components
2768
- }) => {
2769
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2770
- (acc, [attribute, metadata]) => {
2771
- return {
2772
- ...acc,
2773
- [attribute]: metadata.list
2774
- };
2775
- },
2776
- {}
2777
- );
2778
- const listAttributes = convertListLayoutToFieldLayouts(
2779
- data.contentType.layouts.list,
2780
- schema?.attributes,
2781
- listMetadatas,
2782
- { configurations: data.components, schemas: components },
2783
- schemas
2784
- );
2785
- return {
2786
- layout: listAttributes,
2787
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2788
- metadatas: listMetadatas,
2789
- options: {
2790
- ...schema?.options,
2791
- ...schema?.pluginOptions,
2792
- ...data.contentType.options
2793
- }
2794
- };
2795
- };
2796
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2797
- return columns.map((name) => {
2798
- const attribute = attributes[name];
2799
- if (!attribute) {
2800
- return null;
2801
- }
2802
- const metadata = metadatas[name];
2803
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2804
- return {
2805
- attribute,
2806
- label: metadata.label ?? "",
2807
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2808
- schemas,
2809
- components: components?.schemas ?? {}
2810
- }),
2811
- name,
2812
- searchable: metadata.searchable ?? true,
2813
- sortable: metadata.sortable ?? true
2814
- };
2815
- }).filter((field) => field !== null);
2816
- };
3072
+ );
3073
+ });
2817
3074
  const ConfirmBulkActionDialog = ({
2818
3075
  onToggleDialog,
2819
3076
  isOpen = false,
@@ -2821,30 +3078,23 @@ const ConfirmBulkActionDialog = ({
2821
3078
  endAction
2822
3079
  }) => {
2823
3080
  const { formatMessage } = reactIntl.useIntl();
2824
- return /* @__PURE__ */ jsxRuntime.jsxs(
2825
- designSystem.Dialog,
2826
- {
2827
- onClose: onToggleDialog,
2828
- title: formatMessage({
2829
- id: "app.components.ConfirmDialog.title",
2830
- defaultMessage: "Confirmation"
2831
- }),
2832
- isOpen,
2833
- children: [
2834
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.DialogBody, { icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, {}), children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: dialogBody }) }),
2835
- /* @__PURE__ */ jsxRuntime.jsx(
2836
- designSystem.DialogFooter,
2837
- {
2838
- startAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onToggleDialog, variant: "tertiary", children: formatMessage({
2839
- id: "app.components.Button.cancel",
2840
- defaultMessage: "Cancel"
2841
- }) }),
2842
- endAction
2843
- }
2844
- )
2845
- ]
2846
- }
2847
- );
3081
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
3082
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage({
3083
+ id: "app.components.ConfirmDialog.title",
3084
+ defaultMessage: "Confirmation"
3085
+ }) }),
3086
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3087
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
3088
+ dialogBody
3089
+ ] }) }),
3090
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
3091
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { fullWidth: true, onClick: onToggleDialog, variant: "tertiary", children: formatMessage({
3092
+ id: "app.components.Button.cancel",
3093
+ defaultMessage: "Cancel"
3094
+ }) }) }),
3095
+ endAction
3096
+ ] })
3097
+ ] }) });
2848
3098
  };
2849
3099
  const BoldChunk$1 = (chunks) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: chunks });
2850
3100
  const ConfirmDialogPublishAll = ({
@@ -2859,6 +3109,7 @@ const ConfirmDialogPublishAll = ({
2859
3109
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler(getTranslation);
2860
3110
  const { model, schema } = useDoc();
2861
3111
  const [{ query }] = strapiAdmin.useQueryParams();
3112
+ const enableDraftRelationsCount = false;
2862
3113
  const {
2863
3114
  data: countDraftRelations = 0,
2864
3115
  isLoading,
@@ -2870,7 +3121,7 @@ const ConfirmDialogPublishAll = ({
2870
3121
  locale: query?.plugins?.i18n?.locale
2871
3122
  },
2872
3123
  {
2873
- skip: selectedEntries.length === 0
3124
+ skip: !enableDraftRelationsCount
2874
3125
  }
2875
3126
  );
2876
3127
  React__namespace.useEffect(() => {
@@ -2949,16 +3200,30 @@ const formatErrorMessages = (errors, parentKey, formatMessage) => {
2949
3200
  )
2950
3201
  );
2951
3202
  } else {
2952
- messages.push(...formatErrorMessages(value, currentKey, formatMessage));
3203
+ messages.push(
3204
+ ...formatErrorMessages(
3205
+ // @ts-expect-error TODO: check why value is not compatible with FormErrors
3206
+ value,
3207
+ currentKey,
3208
+ formatMessage
3209
+ )
3210
+ );
2953
3211
  }
3212
+ } else {
3213
+ messages.push(
3214
+ formatMessage(
3215
+ {
3216
+ id: `${value}.withField`,
3217
+ defaultMessage: value
3218
+ },
3219
+ { field: currentKey }
3220
+ )
3221
+ );
2954
3222
  }
2955
3223
  });
2956
3224
  return messages;
2957
3225
  };
2958
- const EntryValidationText = ({
2959
- validationErrors,
2960
- isPublished = false
2961
- }) => {
3226
+ const EntryValidationText = ({ validationErrors, status }) => {
2962
3227
  const { formatMessage } = reactIntl.useIntl();
2963
3228
  if (validationErrors) {
2964
3229
  const validationErrorsMessages = formatErrorMessages(validationErrors, "", formatMessage).join(
@@ -2969,7 +3234,7 @@ const EntryValidationText = ({
2969
3234
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsxRuntime.jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
2970
3235
  ] });
2971
3236
  }
2972
- if (isPublished) {
3237
+ if (status === "published") {
2973
3238
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
2974
3239
  /* @__PURE__ */ jsxRuntime.jsx(Icons.CheckCircle, { fill: "success600" }),
2975
3240
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
@@ -2978,6 +3243,15 @@ const EntryValidationText = ({
2978
3243
  }) })
2979
3244
  ] });
2980
3245
  }
3246
+ if (status === "modified") {
3247
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
3248
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.ArrowsCounterClockwise, { fill: "alternative600" }),
3249
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
3250
+ id: "content-manager.bulk-publish.modified",
3251
+ defaultMessage: "Ready to publish changes"
3252
+ }) })
3253
+ ] });
3254
+ }
2981
3255
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
2982
3256
  /* @__PURE__ */ jsxRuntime.jsx(Icons.CheckCircle, { fill: "success600" }),
2983
3257
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
@@ -3029,10 +3303,10 @@ const SelectedEntriesTableContent = ({
3029
3303
  EntryValidationText,
3030
3304
  {
3031
3305
  validationErrors: validationErrors[row.documentId],
3032
- isPublished: row.status === "published"
3306
+ status: row.status
3033
3307
  }
3034
3308
  ) }),
3035
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
3309
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(
3036
3310
  designSystem.IconButton,
3037
3311
  {
3038
3312
  tag: reactRouterDom.Link,
@@ -3041,23 +3315,16 @@ const SelectedEntriesTableContent = ({
3041
3315
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3042
3316
  },
3043
3317
  state: { from: pathname },
3044
- label: formatMessage(
3045
- { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3046
- {
3047
- target: formatMessage(
3048
- {
3049
- id: "content-manager.components.ListViewHelperPluginTable.row-line",
3050
- defaultMessage: "item line {number}"
3051
- },
3052
- { number: index2 + 1 }
3053
- )
3054
- }
3055
- ),
3318
+ label: formatMessage({
3319
+ id: "content-manager.bulk-publish.edit",
3320
+ defaultMessage: "Edit"
3321
+ }),
3056
3322
  target: "_blank",
3057
3323
  marginLeft: "auto",
3058
- children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {})
3324
+ variant: "ghost",
3325
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, { width: "1.6rem", height: "1.6rem" })
3059
3326
  }
3060
- ) })
3327
+ ) }) })
3061
3328
  ] }, row.id)) })
3062
3329
  ] });
3063
3330
  };
@@ -3094,7 +3361,13 @@ const SelectedEntriesModalContent = ({
3094
3361
  );
3095
3362
  const { rows, validationErrors } = React__namespace.useMemo(() => {
3096
3363
  if (data.length > 0 && schema) {
3097
- const validate = createYupSchema(schema.attributes, components);
3364
+ const validate = createYupSchema(
3365
+ schema.attributes,
3366
+ components,
3367
+ // Since this is the "Publish" action, the validation
3368
+ // schema must enforce the rules for published entities
3369
+ { status: "published" }
3370
+ );
3098
3371
  const validationErrors2 = {};
3099
3372
  const rows2 = data.map((entry) => {
3100
3373
  try {
@@ -3170,7 +3443,7 @@ const SelectedEntriesModalContent = ({
3170
3443
  );
3171
3444
  };
3172
3445
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3173
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ModalBody, { children: [
3446
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Body, { children: [
3174
3447
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: getFormattedCountMessage() }),
3175
3448
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 5, children: /* @__PURE__ */ jsxRuntime.jsx(
3176
3449
  SelectedEntriesTableContent,
@@ -3182,27 +3455,24 @@ const SelectedEntriesModalContent = ({
3182
3455
  }
3183
3456
  ) })
3184
3457
  ] }),
3185
- /* @__PURE__ */ jsxRuntime.jsx(
3186
- designSystem.ModalFooter,
3187
- {
3188
- startActions: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: toggleModal, variant: "tertiary", children: formatMessage({
3189
- id: "app.components.Button.cancel",
3190
- defaultMessage: "Cancel"
3191
- }) }),
3192
- endActions: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
3193
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: refetch, variant: "tertiary", loading: isFetching, children: formatMessage({ id: "app.utils.refresh", defaultMessage: "Refresh" }) }),
3194
- /* @__PURE__ */ jsxRuntime.jsx(
3195
- designSystem.Button,
3196
- {
3197
- onClick: toggleDialog,
3198
- disabled: selectedEntries.length === 0 || selectedEntries.length === selectedEntriesWithErrorsCount || selectedEntriesPublished === selectedEntries.length || isLoading,
3199
- loading: isSubmittingForm,
3200
- children: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" })
3201
- }
3202
- )
3203
- ] })
3204
- }
3205
- ),
3458
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
3459
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: toggleModal, variant: "tertiary", children: formatMessage({
3460
+ id: "app.components.Button.cancel",
3461
+ defaultMessage: "Cancel"
3462
+ }) }),
3463
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
3464
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: refetch, variant: "tertiary", loading: isFetching, children: formatMessage({ id: "app.utils.refresh", defaultMessage: "Refresh" }) }),
3465
+ /* @__PURE__ */ jsxRuntime.jsx(
3466
+ designSystem.Button,
3467
+ {
3468
+ onClick: toggleDialog,
3469
+ disabled: selectedEntries.length === 0 || selectedEntries.length === selectedEntriesWithErrorsCount || selectedEntriesPublished === selectedEntries.length || isLoading,
3470
+ loading: isSubmittingForm,
3471
+ children: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" })
3472
+ }
3473
+ )
3474
+ ] })
3475
+ ] }),
3206
3476
  /* @__PURE__ */ jsxRuntime.jsx(
3207
3477
  ConfirmDialogPublishAll,
3208
3478
  {
@@ -3222,8 +3492,7 @@ const PublishAction = ({ documents, model }) => {
3222
3492
  const refetchList = () => {
3223
3493
  contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3224
3494
  };
3225
- if (!showPublishButton)
3226
- return null;
3495
+ if (!showPublishButton) return null;
3227
3496
  return {
3228
3497
  actionType: "publish",
3229
3498
  variant: "tertiary",
@@ -3267,143 +3536,10 @@ const BulkActionsRenderer = () => {
3267
3536
  documents: selectedRows
3268
3537
  },
3269
3538
  descriptions: plugins["content-manager"].apis.getBulkActions(),
3270
- children: (actions2) => actions2.map((action) => /* @__PURE__ */ jsxRuntime.jsx(BulkActionAction, { ...action }, action.id))
3539
+ children: (actions2) => actions2.map((action) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActionButton, { ...action }, action.id))
3271
3540
  }
3272
3541
  ) });
3273
3542
  };
3274
- const BulkActionAction = (action) => {
3275
- const [dialogId, setDialogId] = React__namespace.useState(null);
3276
- const { toggleNotification } = strapiAdmin.useNotification();
3277
- const handleClick = (action2) => (e) => {
3278
- const { onClick, dialog, id } = action2;
3279
- if (onClick) {
3280
- onClick(e);
3281
- }
3282
- if (dialog) {
3283
- switch (dialog.type) {
3284
- case "notification":
3285
- toggleNotification({
3286
- title: dialog.title,
3287
- message: dialog.content,
3288
- type: dialog.status,
3289
- timeout: dialog.timeout,
3290
- onClose: dialog.onClose
3291
- });
3292
- break;
3293
- case "dialog":
3294
- case "modal": {
3295
- e.preventDefault();
3296
- setDialogId(id);
3297
- }
3298
- }
3299
- }
3300
- };
3301
- const handleClose = () => {
3302
- setDialogId(null);
3303
- if (action.dialog?.type === "modal" && action.dialog?.onClose) {
3304
- action.dialog.onClose();
3305
- }
3306
- };
3307
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3308
- /* @__PURE__ */ jsxRuntime.jsx(
3309
- designSystem.Button,
3310
- {
3311
- disabled: action.disabled,
3312
- startIcon: action.icon,
3313
- variant: action.variant,
3314
- onClick: handleClick(action),
3315
- children: action.label
3316
- }
3317
- ),
3318
- action.dialog?.type === "dialog" ? /* @__PURE__ */ jsxRuntime.jsx(
3319
- BulkActionConfirmDialog,
3320
- {
3321
- ...action.dialog,
3322
- variant: action.variant,
3323
- isOpen: dialogId === action.id,
3324
- onClose: handleClose
3325
- }
3326
- ) : null,
3327
- action.dialog?.type === "modal" ? /* @__PURE__ */ jsxRuntime.jsx(
3328
- BulkActionModal,
3329
- {
3330
- ...action.dialog,
3331
- onModalClose: handleClose,
3332
- isOpen: dialogId === action.id
3333
- }
3334
- ) : null
3335
- ] });
3336
- };
3337
- const BulkActionConfirmDialog = ({
3338
- onClose,
3339
- onCancel,
3340
- onConfirm,
3341
- title,
3342
- content,
3343
- confirmButton,
3344
- isOpen,
3345
- variant = "secondary"
3346
- }) => {
3347
- const { formatMessage } = reactIntl.useIntl();
3348
- const handleClose = async () => {
3349
- if (onCancel) {
3350
- await onCancel();
3351
- }
3352
- onClose();
3353
- };
3354
- const handleConfirm = async () => {
3355
- if (onConfirm) {
3356
- await onConfirm();
3357
- }
3358
- onClose();
3359
- };
3360
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog, { isOpen, title, onClose: handleClose, children: [
3361
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.DialogBody, { icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, {}), children: content }),
3362
- /* @__PURE__ */ jsxRuntime.jsx(
3363
- designSystem.DialogFooter,
3364
- {
3365
- startAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleClose, variant: "tertiary", children: formatMessage({
3366
- id: "app.components.Button.cancel",
3367
- defaultMessage: "Cancel"
3368
- }) }),
3369
- endAction: /* @__PURE__ */ jsxRuntime.jsx(
3370
- designSystem.Button,
3371
- {
3372
- onClick: handleConfirm,
3373
- variant: variant === "danger-light" ? variant : "secondary",
3374
- startIcon: variant === "danger-light" ? /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}) : /* @__PURE__ */ jsxRuntime.jsx(Icons.Check, {}),
3375
- children: confirmButton ? confirmButton : formatMessage({
3376
- id: "app.components.Button.confirm",
3377
- defaultMessage: "Confirm"
3378
- })
3379
- }
3380
- )
3381
- }
3382
- )
3383
- ] });
3384
- };
3385
- const BulkActionModal = ({
3386
- isOpen,
3387
- title,
3388
- onClose,
3389
- content: Content,
3390
- onModalClose
3391
- }) => {
3392
- const id = React__namespace.useId();
3393
- if (!isOpen) {
3394
- return null;
3395
- }
3396
- const handleClose = () => {
3397
- if (onClose) {
3398
- onClose();
3399
- }
3400
- onModalClose();
3401
- };
3402
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ModalLayout, { borderRadius: "4px", overflow: "hidden", onClose: handleClose, labelledBy: id, children: [
3403
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: "neutral800", tag: "h2", id, children: title }) }),
3404
- /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose })
3405
- ] });
3406
- };
3407
3543
  const DeleteAction = ({ documents, model }) => {
3408
3544
  const { formatMessage } = reactIntl.useIntl();
3409
3545
  const { schema: contentType } = useDoc();
@@ -3424,8 +3560,7 @@ const DeleteAction = ({ documents, model }) => {
3424
3560
  selectRow([]);
3425
3561
  }
3426
3562
  };
3427
- if (!hasDeletePermission)
3428
- return null;
3563
+ if (!hasDeletePermission) return null;
3429
3564
  return {
3430
3565
  variant: "danger-light",
3431
3566
  label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
@@ -3436,6 +3571,7 @@ const DeleteAction = ({ documents, model }) => {
3436
3571
  defaultMessage: "Confirmation"
3437
3572
  }),
3438
3573
  content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3574
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
3439
3575
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
3440
3576
  id: "popUpWarning.bodyMessage.contentType.delete.all",
3441
3577
  defaultMessage: "Are you sure you want to delete these entries?"
@@ -3472,9 +3608,8 @@ const UnpublishAction = ({ documents, model }) => {
3472
3608
  selectRow([]);
3473
3609
  }
3474
3610
  };
3475
- const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published");
3476
- if (!showUnpublishButton)
3477
- return null;
3611
+ const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3612
+ if (!showUnpublishButton) return null;
3478
3613
  return {
3479
3614
  variant: "tertiary",
3480
3615
  label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
@@ -3485,6 +3620,7 @@ const UnpublishAction = ({ documents, model }) => {
3485
3620
  defaultMessage: "Confirmation"
3486
3621
  }),
3487
3622
  content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3623
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
3488
3624
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
3489
3625
  id: "popUpWarning.bodyMessage.contentType.unpublish.all",
3490
3626
  defaultMessage: "Are you sure you want to unpublish these entries?"
@@ -3578,7 +3714,7 @@ const TableActions = ({ document }) => {
3578
3714
  strapiAdmin.DescriptionComponentRenderer,
3579
3715
  {
3580
3716
  props,
3581
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3717
+ descriptions: plugins["content-manager"].apis.getDocumentActions("table-row").filter((action) => action.name !== "PublishAction"),
3582
3718
  children: (actions2) => {
3583
3719
  const tableRowActions = actions2.filter((action) => {
3584
3720
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3637,6 +3773,7 @@ const EditAction = ({ documentId }) => {
3637
3773
  };
3638
3774
  };
3639
3775
  EditAction.type = "edit";
3776
+ EditAction.position = "table-row";
3640
3777
  const StyledPencil = styledComponents.styled(Icons.Pencil)`
3641
3778
  path {
3642
3779
  fill: currentColor;
@@ -3689,7 +3826,7 @@ const CloneAction = ({ model, documentId }) => {
3689
3826
  }),
3690
3827
  content: /* @__PURE__ */ jsxRuntime.jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3691
3828
  footer: ({ onClose }) => {
3692
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
3829
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
3693
3830
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3694
3831
  id: "cancel",
3695
3832
  defaultMessage: "Cancel"
@@ -3713,6 +3850,7 @@ const CloneAction = ({ model, documentId }) => {
3713
3850
  };
3714
3851
  };
3715
3852
  CloneAction.type = "clone";
3853
+ CloneAction.position = "table-row";
3716
3854
  const StyledDuplicate = styledComponents.styled(Icons.Duplicate)`
3717
3855
  path {
3718
3856
  fill: currentColor;
@@ -3730,8 +3868,7 @@ class ContentManagerPlugin {
3730
3868
  documentActions = [
3731
3869
  ...DEFAULT_ACTIONS,
3732
3870
  ...DEFAULT_TABLE_ROW_ACTIONS,
3733
- ...DEFAULT_HEADER_ACTIONS,
3734
- HistoryAction
3871
+ ...DEFAULT_HEADER_ACTIONS
3735
3872
  ];
3736
3873
  editViewSidePanels = [ActionsPanel];
3737
3874
  headerActions = [];
@@ -3800,7 +3937,14 @@ class ContentManagerPlugin {
3800
3937
  addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3801
3938
  addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3802
3939
  getBulkActions: () => this.bulkActions,
3803
- getDocumentActions: () => this.documentActions,
3940
+ getDocumentActions: (position) => {
3941
+ if (position) {
3942
+ return this.documentActions.filter(
3943
+ (action) => action.position == void 0 || [action.position].flat().includes(position)
3944
+ );
3945
+ }
3946
+ return this.documentActions;
3947
+ },
3804
3948
  getEditViewSidePanels: () => this.editViewSidePanels,
3805
3949
  getHeaderActions: () => this.headerActions
3806
3950
  }
@@ -3810,16 +3954,71 @@ class ContentManagerPlugin {
3810
3954
  const getPrintableType = (value) => {
3811
3955
  const nativeType = typeof value;
3812
3956
  if (nativeType === "object") {
3813
- if (value === null)
3814
- return "null";
3815
- if (Array.isArray(value))
3816
- return "array";
3957
+ if (value === null) return "null";
3958
+ if (Array.isArray(value)) return "array";
3817
3959
  if (value instanceof Object && value.constructor.name !== "Object") {
3818
3960
  return value.constructor.name;
3819
3961
  }
3820
3962
  }
3821
3963
  return nativeType;
3822
3964
  };
3965
+ const HistoryAction = ({ model, document }) => {
3966
+ const { formatMessage } = reactIntl.useIntl();
3967
+ const [{ query }] = strapiAdmin.useQueryParams();
3968
+ const navigate = reactRouterDom.useNavigate();
3969
+ const { trackUsage } = strapiAdmin.useTracking();
3970
+ const { pathname } = reactRouterDom.useLocation();
3971
+ const pluginsQueryParams = qs.stringify({ plugins: query.plugins }, { encode: false });
3972
+ if (!window.strapi.features.isEnabled("cms-content-history")) {
3973
+ return null;
3974
+ }
3975
+ const handleOnClick = () => {
3976
+ const destination = { pathname: "history", search: pluginsQueryParams };
3977
+ trackUsage("willNavigate", {
3978
+ from: pathname,
3979
+ to: `${pathname}/${destination.pathname}`
3980
+ });
3981
+ navigate(destination);
3982
+ };
3983
+ return {
3984
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ClockCounterClockwise, {}),
3985
+ label: formatMessage({
3986
+ id: "content-manager.history.document-action",
3987
+ defaultMessage: "Content History"
3988
+ }),
3989
+ onClick: handleOnClick,
3990
+ disabled: (
3991
+ /**
3992
+ * The user is creating a new document.
3993
+ * It hasn't been saved yet, so there's no history to go to
3994
+ */
3995
+ !document || /**
3996
+ * The document has been created but the current dimension has never been saved.
3997
+ * For example, the user is creating a new locale in an existing document,
3998
+ * so there's no history for the document in that locale
3999
+ */
4000
+ !document.id || /**
4001
+ * History is only available for content types created by the user.
4002
+ * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
4003
+ * which start with `admin::` or `plugin::`
4004
+ */
4005
+ !model.startsWith("api::")
4006
+ ),
4007
+ position: "header"
4008
+ };
4009
+ };
4010
+ HistoryAction.type = "history";
4011
+ HistoryAction.position = "header";
4012
+ const historyAdmin = {
4013
+ bootstrap(app) {
4014
+ const { addDocumentAction } = app.getPlugin("content-manager").apis;
4015
+ addDocumentAction((actions2) => {
4016
+ const indexOfDeleteAction = actions2.findIndex((action) => action.type === "delete");
4017
+ actions2.splice(indexOfDeleteAction, 0, HistoryAction);
4018
+ return actions2;
4019
+ });
4020
+ }
4021
+ };
3823
4022
  const initialState = {
3824
4023
  collectionTypeLinks: [],
3825
4024
  components: [],
@@ -3856,6 +4055,92 @@ const { setInitialData } = actions;
3856
4055
  const reducer = toolkit.combineReducers({
3857
4056
  app: reducer$1
3858
4057
  });
4058
+ const previewApi = contentManagerApi.injectEndpoints({
4059
+ endpoints: (builder) => ({
4060
+ getPreviewUrl: builder.query({
4061
+ query({ query, params }) {
4062
+ return {
4063
+ url: `/content-manager/preview/url/${params.contentType}`,
4064
+ method: "GET",
4065
+ config: {
4066
+ params: query
4067
+ }
4068
+ };
4069
+ }
4070
+ })
4071
+ })
4072
+ });
4073
+ const { useGetPreviewUrlQuery } = previewApi;
4074
+ const ConditionalTooltip = ({ isShown, label, children }) => {
4075
+ if (isShown) {
4076
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label, children });
4077
+ }
4078
+ return children;
4079
+ };
4080
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4081
+ const { formatMessage } = reactIntl.useIntl();
4082
+ const { trackUsage } = strapiAdmin.useTracking();
4083
+ const { pathname } = reactRouterDom.useLocation();
4084
+ const [{ query }] = strapiAdmin.useQueryParams();
4085
+ const isModified = strapiAdmin.useForm("PreviewSidePanel", (state) => state.modified);
4086
+ const { data, error } = useGetPreviewUrlQuery({
4087
+ params: {
4088
+ contentType: model
4089
+ },
4090
+ query: {
4091
+ documentId,
4092
+ locale: document?.locale,
4093
+ status: document?.status
4094
+ }
4095
+ });
4096
+ if (!data?.data?.url || error) {
4097
+ return null;
4098
+ }
4099
+ const trackNavigation = () => {
4100
+ const destinationPathname = pathname.replace(/\/$/, "") + "/preview";
4101
+ trackUsage("willNavigate", { from: pathname, to: destinationPathname });
4102
+ };
4103
+ return {
4104
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4105
+ content: /* @__PURE__ */ jsxRuntime.jsx(
4106
+ ConditionalTooltip,
4107
+ {
4108
+ label: formatMessage({
4109
+ id: "content-manager.preview.panel.button-disabled-tooltip",
4110
+ defaultMessage: "Please save to open the preview"
4111
+ }),
4112
+ isShown: isModified,
4113
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { cursor: "not-allowed", width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
4114
+ designSystem.Button,
4115
+ {
4116
+ variant: "tertiary",
4117
+ tag: reactRouterDom.Link,
4118
+ to: { pathname: "preview", search: qs.stringify(query, { encode: false }) },
4119
+ onClick: trackNavigation,
4120
+ width: "100%",
4121
+ disabled: isModified,
4122
+ pointerEvents: isModified ? "none" : void 0,
4123
+ tabIndex: isModified ? -1 : void 0,
4124
+ children: formatMessage({
4125
+ id: "content-manager.preview.panel.button",
4126
+ defaultMessage: "Open preview"
4127
+ })
4128
+ }
4129
+ ) })
4130
+ }
4131
+ )
4132
+ };
4133
+ };
4134
+ const FEATURE_ID = "preview";
4135
+ const previewAdmin = {
4136
+ bootstrap(app) {
4137
+ if (!window.strapi.future.isEnabled(FEATURE_ID)) {
4138
+ return;
4139
+ }
4140
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4141
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4142
+ }
4143
+ };
3859
4144
  const index = {
3860
4145
  register(app) {
3861
4146
  const cm = new ContentManagerPlugin();
@@ -3870,15 +4155,32 @@ const index = {
3870
4155
  defaultMessage: "Content Manager"
3871
4156
  },
3872
4157
  permissions: [],
3873
- Component: () => Promise.resolve().then(() => require("./layout-b91XRlD2.js")).then((mod) => ({ default: mod.Layout })),
3874
4158
  position: 1
3875
4159
  });
4160
+ app.router.addRoute({
4161
+ path: "content-manager/*",
4162
+ lazy: async () => {
4163
+ const { Layout } = await Promise.resolve().then(() => require("./layout-szfTCeYm.js"));
4164
+ return {
4165
+ Component: Layout
4166
+ };
4167
+ },
4168
+ children: routes
4169
+ });
3876
4170
  app.registerPlugin(cm.config);
3877
4171
  },
4172
+ bootstrap(app) {
4173
+ if (typeof historyAdmin.bootstrap === "function") {
4174
+ historyAdmin.bootstrap(app);
4175
+ }
4176
+ if (typeof previewAdmin.bootstrap === "function") {
4177
+ previewAdmin.bootstrap(app);
4178
+ }
4179
+ },
3878
4180
  async registerTrads({ locales }) {
3879
4181
  const importedTrads = await Promise.all(
3880
4182
  locales.map((locale) => {
3881
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => Promise.resolve().then(() => require("./ar-BUUWXIYu.js")), "./translations/ca.json": () => Promise.resolve().then(() => require("./ca-Cmk45QO6.js")), "./translations/cs.json": () => Promise.resolve().then(() => require("./cs-CkJy6B2v.js")), "./translations/de.json": () => Promise.resolve().then(() => require("./de-CCEmbAah.js")), "./translations/en.json": () => Promise.resolve().then(() => require("./en-BN1bvFK7.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-EUonQTon.js")), "./translations/eu.json": () => Promise.resolve().then(() => require("./eu-VDH-3ovk.js")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-B7kGGg3E.js")), "./translations/gu.json": () => Promise.resolve().then(() => require("./gu-BRmF601H.js")), "./translations/hi.json": () => Promise.resolve().then(() => require("./hi-CCJBptSq.js")), "./translations/hu.json": () => Promise.resolve().then(() => require("./hu-sNV_yLYy.js")), "./translations/id.json": () => Promise.resolve().then(() => require("./id-B5Ser98A.js")), "./translations/it.json": () => Promise.resolve().then(() => require("./it-DkBIs7vD.js")), "./translations/ja.json": () => Promise.resolve().then(() => require("./ja-CcFe8diO.js")), "./translations/ko.json": () => Promise.resolve().then(() => require("./ko-woFZPmLk.js")), "./translations/ml.json": () => Promise.resolve().then(() => require("./ml-C2W8N8k1.js")), "./translations/ms.json": () => Promise.resolve().then(() => require("./ms-BuFotyP_.js")), "./translations/nl.json": () => Promise.resolve().then(() => require("./nl-bbEOHChV.js")), "./translations/pl.json": () => Promise.resolve().then(() => require("./pl-uzwG-hk7.js")), "./translations/pt-BR.json": () => Promise.resolve().then(() => require("./pt-BR-BiOz37D9.js")), "./translations/pt.json": () => Promise.resolve().then(() => require("./pt-CeXQuq50.js")), "./translations/ru.json": () => Promise.resolve().then(() => require("./ru-BT3ybNny.js")), "./translations/sa.json": () => Promise.resolve().then(() => require("./sa-CcvkYInH.js")), "./translations/sk.json": () => Promise.resolve().then(() => require("./sk-CvY09Xjv.js")), "./translations/sv.json": () => Promise.resolve().then(() => require("./sv-MYDuzgvT.js")), "./translations/th.json": () => Promise.resolve().then(() => require("./th-D9_GfAjc.js")), "./translations/tr.json": () => Promise.resolve().then(() => require("./tr-D9UH-O_R.js")), "./translations/uk.json": () => Promise.resolve().then(() => require("./uk-C8EiqJY7.js")), "./translations/vi.json": () => Promise.resolve().then(() => require("./vi-CJlYDheJ.js")), "./translations/zh-Hans.json": () => Promise.resolve().then(() => require("./zh-Hans-9kOncHGw.js")), "./translations/zh.json": () => Promise.resolve().then(() => require("./zh-CQQfszqR.js")) }), `./translations/${locale}.json`).then(({ default: data }) => {
4183
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => Promise.resolve().then(() => require("./ar-BUUWXIYu.js")), "./translations/ca.json": () => Promise.resolve().then(() => require("./ca-Cmk45QO6.js")), "./translations/cs.json": () => Promise.resolve().then(() => require("./cs-CkJy6B2v.js")), "./translations/de.json": () => Promise.resolve().then(() => require("./de-CCEmbAah.js")), "./translations/en.json": () => Promise.resolve().then(() => require("./en-BK8Xyl5I.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-9K52xZIr.js")), "./translations/eu.json": () => Promise.resolve().then(() => require("./eu-VDH-3ovk.js")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-B2Kyv8Z9.js")), "./translations/gu.json": () => Promise.resolve().then(() => require("./gu-BRmF601H.js")), "./translations/hi.json": () => Promise.resolve().then(() => require("./hi-CCJBptSq.js")), "./translations/hu.json": () => Promise.resolve().then(() => require("./hu-sNV_yLYy.js")), "./translations/id.json": () => Promise.resolve().then(() => require("./id-B5Ser98A.js")), "./translations/it.json": () => Promise.resolve().then(() => require("./it-DkBIs7vD.js")), "./translations/ja.json": () => Promise.resolve().then(() => require("./ja-7sfIbjxE.js")), "./translations/ko.json": () => Promise.resolve().then(() => require("./ko-woFZPmLk.js")), "./translations/ml.json": () => Promise.resolve().then(() => require("./ml-C2W8N8k1.js")), "./translations/ms.json": () => Promise.resolve().then(() => require("./ms-BuFotyP_.js")), "./translations/nl.json": () => Promise.resolve().then(() => require("./nl-bbEOHChV.js")), "./translations/pl.json": () => Promise.resolve().then(() => require("./pl-uzwG-hk7.js")), "./translations/pt-BR.json": () => Promise.resolve().then(() => require("./pt-BR-BiOz37D9.js")), "./translations/pt.json": () => Promise.resolve().then(() => require("./pt-CeXQuq50.js")), "./translations/ru.json": () => Promise.resolve().then(() => require("./ru-BT3ybNny.js")), "./translations/sa.json": () => Promise.resolve().then(() => require("./sa-CcvkYInH.js")), "./translations/sk.json": () => Promise.resolve().then(() => require("./sk-CvY09Xjv.js")), "./translations/sv.json": () => Promise.resolve().then(() => require("./sv-MYDuzgvT.js")), "./translations/th.json": () => Promise.resolve().then(() => require("./th-D9_GfAjc.js")), "./translations/tr.json": () => Promise.resolve().then(() => require("./tr-D9UH-O_R.js")), "./translations/uk.json": () => Promise.resolve().then(() => require("./uk-C8EiqJY7.js")), "./translations/vi.json": () => Promise.resolve().then(() => require("./vi-CJlYDheJ.js")), "./translations/zh-Hans.json": () => Promise.resolve().then(() => require("./zh-Hans-9kOncHGw.js")), "./translations/zh.json": () => Promise.resolve().then(() => require("./zh-CQQfszqR.js")) }), `./translations/${locale}.json`, 3).then(({ default: data }) => {
3882
4184
  return {
3883
4185
  data: prefixPluginTranslations(data, PLUGIN_ID),
3884
4186
  locale
@@ -3896,6 +4198,7 @@ const index = {
3896
4198
  };
3897
4199
  exports.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD;
3898
4200
  exports.BulkActionsRenderer = BulkActionsRenderer;
4201
+ exports.CLONE_PATH = CLONE_PATH;
3899
4202
  exports.COLLECTION_TYPES = COLLECTION_TYPES;
3900
4203
  exports.CREATOR_FIELDS = CREATOR_FIELDS;
3901
4204
  exports.DEFAULT_SETTINGS = DEFAULT_SETTINGS;
@@ -3922,8 +4225,8 @@ exports.getDisplayName = getDisplayName;
3922
4225
  exports.getMainField = getMainField;
3923
4226
  exports.getTranslation = getTranslation;
3924
4227
  exports.index = index;
3925
- exports.routes = routes;
3926
4228
  exports.setInitialData = setInitialData;
4229
+ exports.useContentManagerContext = useContentManagerContext;
3927
4230
  exports.useContentTypeSchema = useContentTypeSchema;
3928
4231
  exports.useDoc = useDoc;
3929
4232
  exports.useDocLayout = useDocLayout;
@@ -3935,5 +4238,6 @@ exports.useGetAllContentTypeSettingsQuery = useGetAllContentTypeSettingsQuery;
3935
4238
  exports.useGetAllDocumentsQuery = useGetAllDocumentsQuery;
3936
4239
  exports.useGetContentTypeConfigurationQuery = useGetContentTypeConfigurationQuery;
3937
4240
  exports.useGetInitialDataQuery = useGetInitialDataQuery;
4241
+ exports.useGetPreviewUrlQuery = useGetPreviewUrlQuery;
3938
4242
  exports.useUpdateContentTypeConfigurationMutation = useUpdateContentTypeConfigurationMutation;
3939
- //# sourceMappingURL=index-DzN3kBgx.js.map
4243
+ //# sourceMappingURL=index-CptTdHNy.js.map