@strapi/content-manager 0.0.0-experimental.bd712ad3930045f4a5d2144c119e0b7856e97fc4 → 0.0.0-experimental.bec5a58066c034a7ebf5b14df62560e68a456fa3

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 (206) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-C7ImeKGM.mjs → ComponentConfigurationPage-BaJMOQyq.mjs} +4 -4
  2. package/dist/_chunks/{ComponentConfigurationPage-C7ImeKGM.mjs.map → ComponentConfigurationPage-BaJMOQyq.mjs.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-BWQv6yRj.js → ComponentConfigurationPage-N-CTtgQa.js} +4 -4
  4. package/dist/_chunks/{ComponentConfigurationPage-BWQv6yRj.js.map → ComponentConfigurationPage-N-CTtgQa.js.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-CEGwxV-L.js → EditConfigurationPage-BHkjAbxH.js} +4 -4
  6. package/dist/_chunks/{EditConfigurationPage-CEGwxV-L.js.map → EditConfigurationPage-BHkjAbxH.js.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-MItFGzT9.mjs → EditConfigurationPage-CKK-5LfX.mjs} +4 -4
  8. package/dist/_chunks/{EditConfigurationPage-MItFGzT9.mjs.map → EditConfigurationPage-CKK-5LfX.mjs.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-DhmAg0NK.mjs → EditViewPage-B11aeMcf.mjs} +63 -12
  10. package/dist/_chunks/EditViewPage-B11aeMcf.mjs.map +1 -0
  11. package/dist/_chunks/{EditViewPage-CmMi2Xsn.js → EditViewPage-QPUftxUd.js} +62 -11
  12. package/dist/_chunks/EditViewPage-QPUftxUd.js.map +1 -0
  13. package/dist/_chunks/{Field-Cs62u5pl.mjs → Field-Bj_RgtGo.mjs} +215 -124
  14. package/dist/_chunks/Field-Bj_RgtGo.mjs.map +1 -0
  15. package/dist/_chunks/{Field-1DLtcLAI.js → Field-DUK83cfh.js} +216 -125
  16. package/dist/_chunks/Field-DUK83cfh.js.map +1 -0
  17. package/dist/_chunks/{Form-CqFA7F_V.js → Form-DHmBRlHd.js} +36 -17
  18. package/dist/_chunks/Form-DHmBRlHd.js.map +1 -0
  19. package/dist/_chunks/{Form-zYHtzGUX.mjs → Form-DLMSoXV7.mjs} +36 -17
  20. package/dist/_chunks/Form-DLMSoXV7.mjs.map +1 -0
  21. package/dist/_chunks/{History-DalgFQ3D.mjs → History-CfCSNlG9.mjs} +59 -106
  22. package/dist/_chunks/History-CfCSNlG9.mjs.map +1 -0
  23. package/dist/_chunks/{History-BblwXv7-.js → History-Di3zm4HT.js} +57 -104
  24. package/dist/_chunks/History-Di3zm4HT.js.map +1 -0
  25. package/dist/_chunks/{ListConfigurationPage-DWy-vRzs.mjs → ListConfigurationPage-0mtv_iqk.mjs} +18 -7
  26. package/dist/_chunks/ListConfigurationPage-0mtv_iqk.mjs.map +1 -0
  27. package/dist/_chunks/{ListConfigurationPage-Cpy4QqNd.js → ListConfigurationPage-Cq361KIt.js} +17 -6
  28. package/dist/_chunks/ListConfigurationPage-Cq361KIt.js.map +1 -0
  29. package/dist/_chunks/{ListViewPage-BkAwIW9s.mjs → ListViewPage-BxLVROX8.mjs} +106 -74
  30. package/dist/_chunks/ListViewPage-BxLVROX8.mjs.map +1 -0
  31. package/dist/_chunks/{ListViewPage-DFjn1DNW.js → ListViewPage-DFDcG8gM.js} +108 -76
  32. package/dist/_chunks/ListViewPage-DFDcG8gM.js.map +1 -0
  33. package/dist/_chunks/{NoContentTypePage-B9BCNNdL.mjs → NoContentTypePage-BRfDd67_.mjs} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-B9BCNNdL.mjs.map → NoContentTypePage-BRfDd67_.mjs.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-C-3ykoxs.js → NoContentTypePage-BSyvnDZZ.js} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-C-3ykoxs.js.map → NoContentTypePage-BSyvnDZZ.js.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-Bt_HWGat.mjs → NoPermissionsPage-CV9V8KWa.mjs} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-Bt_HWGat.mjs.map → NoPermissionsPage-CV9V8KWa.mjs.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-DKLmDZnZ.js → NoPermissionsPage-DyLphsn_.js} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-DKLmDZnZ.js.map → NoPermissionsPage-DyLphsn_.js.map} +1 -1
  41. package/dist/_chunks/Preview-C_B1nx3g.mjs +272 -0
  42. package/dist/_chunks/Preview-C_B1nx3g.mjs.map +1 -0
  43. package/dist/_chunks/Preview-D_3aO6Ly.js +291 -0
  44. package/dist/_chunks/Preview-D_3aO6Ly.js.map +1 -0
  45. package/dist/_chunks/{Relations-CJmTbZ8T.mjs → Relations-C6pwmDXh.mjs} +73 -37
  46. package/dist/_chunks/Relations-C6pwmDXh.mjs.map +1 -0
  47. package/dist/_chunks/{Relations-CrxfoH2n.js → Relations-Cne2AlrL.js} +72 -36
  48. package/dist/_chunks/Relations-Cne2AlrL.js.map +1 -0
  49. package/dist/_chunks/{en-Ux26r5pl.mjs → en-DhFUjrNW.mjs} +31 -18
  50. package/dist/_chunks/{en-Ux26r5pl.mjs.map → en-DhFUjrNW.mjs.map} +1 -1
  51. package/dist/_chunks/{en-fbKQxLGn.js → en-Ic0kXjxB.js} +31 -18
  52. package/dist/_chunks/{en-fbKQxLGn.js.map → en-Ic0kXjxB.js.map} +1 -1
  53. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  54. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  55. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  56. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  57. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  58. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  59. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  60. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  61. package/dist/_chunks/{index-D1344xdw.mjs → index-BpxR3En4.mjs} +1092 -721
  62. package/dist/_chunks/index-BpxR3En4.mjs.map +1 -0
  63. package/dist/_chunks/{index-Buwn78Rt.js → index-T-aWjbj2.js} +1073 -701
  64. package/dist/_chunks/index-T-aWjbj2.js.map +1 -0
  65. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  66. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  67. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  68. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  69. package/dist/_chunks/{layout-DRuJUpas.js → layout-BEuNwv-F.js} +21 -8
  70. package/dist/_chunks/layout-BEuNwv-F.js.map +1 -0
  71. package/dist/_chunks/{layout-ChVuUpa1.mjs → layout-DhMZ_lDx.mjs} +22 -9
  72. package/dist/_chunks/layout-DhMZ_lDx.mjs.map +1 -0
  73. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  74. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  75. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  76. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  77. package/dist/_chunks/{relations-B-deMCy4.mjs → relations-BdnxoX6f.mjs} +6 -7
  78. package/dist/_chunks/relations-BdnxoX6f.mjs.map +1 -0
  79. package/dist/_chunks/{relations-DuoUwyJr.js → relations-kLcuobLk.js} +6 -7
  80. package/dist/_chunks/relations-kLcuobLk.js.map +1 -0
  81. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  82. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  83. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  84. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  85. package/dist/admin/index.js +2 -1
  86. package/dist/admin/index.js.map +1 -1
  87. package/dist/admin/index.mjs +5 -4
  88. package/dist/admin/src/exports.d.ts +1 -1
  89. package/dist/admin/src/history/index.d.ts +3 -0
  90. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  91. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  92. package/dist/admin/src/index.d.ts +1 -0
  93. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  94. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -0
  95. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +2 -2
  96. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  97. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  98. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  99. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  100. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  101. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  102. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  103. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  104. package/dist/admin/src/preview/constants.d.ts +1 -0
  105. package/dist/admin/src/preview/index.d.ts +4 -0
  106. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  107. package/dist/admin/src/preview/routes.d.ts +3 -0
  108. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  109. package/dist/admin/src/router.d.ts +1 -1
  110. package/dist/admin/src/services/api.d.ts +1 -1
  111. package/dist/admin/src/services/components.d.ts +2 -2
  112. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  113. package/dist/admin/src/services/documents.d.ts +19 -20
  114. package/dist/admin/src/services/init.d.ts +1 -1
  115. package/dist/admin/src/services/relations.d.ts +2 -2
  116. package/dist/admin/src/services/uid.d.ts +3 -3
  117. package/dist/admin/src/utils/validation.d.ts +4 -1
  118. package/dist/server/index.js +591 -261
  119. package/dist/server/index.js.map +1 -1
  120. package/dist/server/index.mjs +592 -262
  121. package/dist/server/index.mjs.map +1 -1
  122. package/dist/server/src/bootstrap.d.ts.map +1 -1
  123. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  124. package/dist/server/src/controllers/index.d.ts.map +1 -1
  125. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  126. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  127. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  128. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  129. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  130. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  131. package/dist/server/src/history/services/history.d.ts.map +1 -1
  132. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  133. package/dist/server/src/history/services/utils.d.ts +4 -4
  134. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  135. package/dist/server/src/index.d.ts +4 -4
  136. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  137. package/dist/server/src/preview/constants.d.ts +2 -0
  138. package/dist/server/src/preview/constants.d.ts.map +1 -0
  139. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  140. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  141. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  142. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  143. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  144. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  145. package/dist/server/src/preview/index.d.ts +4 -0
  146. package/dist/server/src/preview/index.d.ts.map +1 -0
  147. package/dist/server/src/preview/routes/index.d.ts +8 -0
  148. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  149. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  150. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  151. package/dist/server/src/preview/services/index.d.ts +16 -0
  152. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  153. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  154. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  155. package/dist/server/src/preview/services/preview.d.ts +12 -0
  156. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  157. package/dist/server/src/preview/utils.d.ts +19 -0
  158. package/dist/server/src/preview/utils.d.ts.map +1 -0
  159. package/dist/server/src/register.d.ts.map +1 -1
  160. package/dist/server/src/routes/index.d.ts.map +1 -1
  161. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  162. package/dist/server/src/services/document-metadata.d.ts +8 -8
  163. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  164. package/dist/server/src/services/index.d.ts +4 -4
  165. package/dist/server/src/services/index.d.ts.map +1 -1
  166. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  167. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  168. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  169. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  170. package/dist/server/src/utils/index.d.ts +2 -0
  171. package/dist/server/src/utils/index.d.ts.map +1 -1
  172. package/dist/shared/contracts/collection-types.d.ts +3 -1
  173. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  174. package/dist/shared/contracts/index.d.ts +1 -0
  175. package/dist/shared/contracts/index.d.ts.map +1 -1
  176. package/dist/shared/contracts/preview.d.ts +27 -0
  177. package/dist/shared/contracts/preview.d.ts.map +1 -0
  178. package/dist/shared/index.js +4 -0
  179. package/dist/shared/index.js.map +1 -1
  180. package/dist/shared/index.mjs +4 -0
  181. package/dist/shared/index.mjs.map +1 -1
  182. package/package.json +14 -14
  183. package/dist/_chunks/EditViewPage-CmMi2Xsn.js.map +0 -1
  184. package/dist/_chunks/EditViewPage-DhmAg0NK.mjs.map +0 -1
  185. package/dist/_chunks/Field-1DLtcLAI.js.map +0 -1
  186. package/dist/_chunks/Field-Cs62u5pl.mjs.map +0 -1
  187. package/dist/_chunks/Form-CqFA7F_V.js.map +0 -1
  188. package/dist/_chunks/Form-zYHtzGUX.mjs.map +0 -1
  189. package/dist/_chunks/History-BblwXv7-.js.map +0 -1
  190. package/dist/_chunks/History-DalgFQ3D.mjs.map +0 -1
  191. package/dist/_chunks/ListConfigurationPage-Cpy4QqNd.js.map +0 -1
  192. package/dist/_chunks/ListConfigurationPage-DWy-vRzs.mjs.map +0 -1
  193. package/dist/_chunks/ListViewPage-BkAwIW9s.mjs.map +0 -1
  194. package/dist/_chunks/ListViewPage-DFjn1DNW.js.map +0 -1
  195. package/dist/_chunks/Relations-CJmTbZ8T.mjs.map +0 -1
  196. package/dist/_chunks/Relations-CrxfoH2n.js.map +0 -1
  197. package/dist/_chunks/index-Buwn78Rt.js.map +0 -1
  198. package/dist/_chunks/index-D1344xdw.mjs.map +0 -1
  199. package/dist/_chunks/layout-ChVuUpa1.mjs.map +0 -1
  200. package/dist/_chunks/layout-DRuJUpas.js.map +0 -1
  201. package/dist/_chunks/relations-B-deMCy4.mjs.map +0 -1
  202. package/dist/_chunks/relations-DuoUwyJr.js.map +0 -1
  203. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  204. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  205. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  206. package/strapi-server.js +0 -3
@@ -2,13 +2,14 @@
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");
@@ -32,6 +33,7 @@ function _interopNamespace(e) {
32
33
  return Object.freeze(n);
33
34
  }
34
35
  const React__namespace = /* @__PURE__ */ _interopNamespace(React);
36
+ const mapValues__default = /* @__PURE__ */ _interopDefault(mapValues);
35
37
  const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
36
38
  const pipe__default = /* @__PURE__ */ _interopDefault(pipe);
37
39
  const __variableDynamicImportRuntimeHelper = (glob, path) => {
@@ -70,42 +72,6 @@ const useInjectionZone = (area) => {
70
72
  const [page, position] = area.split(".");
71
73
  return contentManagerPlugin.getInjectedComponents(page, position);
72
74
  };
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
75
  const ID = "id";
110
76
  const CREATED_BY_ATTRIBUTE_NAME = "createdBy";
111
77
  const UPDATED_BY_ATTRIBUTE_NAME = "updatedBy";
@@ -157,6 +123,7 @@ const DocumentRBAC = ({ children, permissions }) => {
157
123
  if (!slug) {
158
124
  throw new Error("Cannot find the slug param in the URL");
159
125
  }
126
+ const [{ rawQuery }] = strapiAdmin.useQueryParams();
160
127
  const userPermissions = strapiAdmin.useAuth("DocumentRBAC", (state) => state.permissions);
161
128
  const contentTypePermissions = React__namespace.useMemo(() => {
162
129
  const contentTypePermissions2 = userPermissions.filter(
@@ -167,7 +134,14 @@ const DocumentRBAC = ({ children, permissions }) => {
167
134
  return { ...acc, [action]: [permission] };
168
135
  }, {});
169
136
  }, [slug, userPermissions]);
170
- const { isLoading, allowedActions } = strapiAdmin.useRBAC(contentTypePermissions, permissions ?? void 0);
137
+ const { isLoading, allowedActions } = strapiAdmin.useRBAC(
138
+ contentTypePermissions,
139
+ permissions ?? void 0,
140
+ // TODO: useRBAC context should be typed and built differently
141
+ // We are passing raw query as context to the hook so that it can
142
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
143
+ rawQuery
144
+ );
171
145
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
172
146
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
173
147
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -215,7 +189,8 @@ const contentManagerApi = strapiAdmin.adminApi.enhanceEndpoints({
215
189
  "Document",
216
190
  "InitialData",
217
191
  "HistoryVersion",
218
- "Relations"
192
+ "Relations",
193
+ "UidAvailability"
219
194
  ]
220
195
  });
221
196
  const documentApi = contentManagerApi.injectEndpoints({
@@ -229,7 +204,12 @@ const documentApi = contentManagerApi.injectEndpoints({
229
204
  params: query
230
205
  }
231
206
  }),
232
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
207
+ invalidatesTags: (_result, error, { model }) => {
208
+ if (error) {
209
+ return [];
210
+ }
211
+ return [{ type: "Document", id: `${model}_LIST` }];
212
+ }
233
213
  }),
234
214
  cloneDocument: builder.mutation({
235
215
  query: ({ model, sourceId, data, params }) => ({
@@ -240,7 +220,10 @@ const documentApi = contentManagerApi.injectEndpoints({
240
220
  params
241
221
  }
242
222
  }),
243
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
223
+ invalidatesTags: (_result, _error, { model }) => [
224
+ { type: "Document", id: `${model}_LIST` },
225
+ { type: "UidAvailability", id: model }
226
+ ]
244
227
  }),
245
228
  /**
246
229
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -257,7 +240,8 @@ const documentApi = contentManagerApi.injectEndpoints({
257
240
  }),
258
241
  invalidatesTags: (result, _error, { model }) => [
259
242
  { type: "Document", id: `${model}_LIST` },
260
- "Relations"
243
+ "Relations",
244
+ { type: "UidAvailability", id: model }
261
245
  ]
262
246
  }),
263
247
  deleteDocument: builder.mutation({
@@ -298,7 +282,8 @@ const documentApi = contentManagerApi.injectEndpoints({
298
282
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
299
283
  },
300
284
  { type: "Document", id: `${model}_LIST` },
301
- "Relations"
285
+ "Relations",
286
+ { type: "UidAvailability", id: model }
302
287
  ];
303
288
  }
304
289
  }),
@@ -311,11 +296,12 @@ const documentApi = contentManagerApi.injectEndpoints({
311
296
  url: `/content-manager/collection-types/${model}`,
312
297
  method: "GET",
313
298
  config: {
314
- params
299
+ params: qs.stringify(params, { encode: true })
315
300
  }
316
301
  }),
317
302
  providesTags: (result, _error, arg) => {
318
303
  return [
304
+ { type: "Document", id: `ALL_LIST` },
319
305
  { type: "Document", id: `${arg.model}_LIST` },
320
306
  ...result?.results.map(({ documentId }) => ({
321
307
  type: "Document",
@@ -354,6 +340,11 @@ const documentApi = contentManagerApi.injectEndpoints({
354
340
  {
355
341
  type: "Document",
356
342
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
343
+ },
344
+ // Make it easy to invalidate all individual documents queries for a model
345
+ {
346
+ type: "Document",
347
+ id: `${model}_ALL_ITEMS`
357
348
  }
358
349
  ];
359
350
  }
@@ -417,8 +408,21 @@ const documentApi = contentManagerApi.injectEndpoints({
417
408
  type: "Document",
418
409
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
419
410
  },
420
- "Relations"
411
+ "Relations",
412
+ { type: "UidAvailability", id: model }
421
413
  ];
414
+ },
415
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
416
+ const patchResult = dispatch(
417
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
418
+ Object.assign(draft.data, data);
419
+ })
420
+ );
421
+ try {
422
+ await queryFulfilled;
423
+ } catch {
424
+ patchResult.undo();
425
+ }
422
426
  }
423
427
  }),
424
428
  unpublishDocument: builder.mutation({
@@ -480,28 +484,44 @@ const buildValidParams = (query) => {
480
484
  {}
481
485
  )
482
486
  };
483
- if ("_q" in validQueryParams) {
484
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
485
- }
486
487
  return validQueryParams;
487
488
  };
488
489
  const isBaseQueryError = (error) => {
489
490
  return error.name !== void 0;
490
491
  };
491
- const createYupSchema = (attributes = {}, components = {}) => {
492
+ const arrayValidator = (attribute, options) => ({
493
+ message: strapiAdmin.translatedErrors.required,
494
+ test(value) {
495
+ if (options.status === "draft") {
496
+ return true;
497
+ }
498
+ if (!attribute.required) {
499
+ return true;
500
+ }
501
+ if (!value) {
502
+ return false;
503
+ }
504
+ if (Array.isArray(value) && value.length === 0) {
505
+ return false;
506
+ }
507
+ return true;
508
+ }
509
+ });
510
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
492
511
  const createModelSchema = (attributes2) => yup__namespace.object().shape(
493
512
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
494
513
  if (DOCUMENT_META_FIELDS.includes(name)) {
495
514
  return acc;
496
515
  }
497
516
  const validations = [
517
+ addNullableValidation,
498
518
  addRequiredValidation,
499
519
  addMinLengthValidation,
500
520
  addMaxLengthValidation,
501
521
  addMinValidation,
502
522
  addMaxValidation,
503
523
  addRegexValidation
504
- ].map((fn) => fn(attribute));
524
+ ].map((fn) => fn(attribute, options));
505
525
  const transformSchema = pipe__default.default(...validations);
506
526
  switch (attribute.type) {
507
527
  case "component": {
@@ -511,12 +531,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
511
531
  ...acc,
512
532
  [name]: transformSchema(
513
533
  yup__namespace.array().of(createModelSchema(attributes3).nullable(false))
514
- )
534
+ ).test(arrayValidator(attribute, options))
515
535
  };
516
536
  } else {
517
537
  return {
518
538
  ...acc,
519
- [name]: transformSchema(createModelSchema(attributes3))
539
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
520
540
  };
521
541
  }
522
542
  }
@@ -538,7 +558,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
538
558
  }
539
559
  )
540
560
  )
541
- )
561
+ ).test(arrayValidator(attribute, options))
542
562
  };
543
563
  case "relation":
544
564
  return {
@@ -550,7 +570,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
550
570
  } else if (Array.isArray(value)) {
551
571
  return yup__namespace.array().of(
552
572
  yup__namespace.object().shape({
553
- id: yup__namespace.string().required()
573
+ id: yup__namespace.number().required()
554
574
  })
555
575
  );
556
576
  } else if (typeof value === "object") {
@@ -602,6 +622,14 @@ const createAttributeSchema = (attribute) => {
602
622
  if (!value || typeof value === "string" && value.length === 0) {
603
623
  return true;
604
624
  }
625
+ if (typeof value === "object") {
626
+ try {
627
+ JSON.stringify(value);
628
+ return true;
629
+ } catch (err) {
630
+ return false;
631
+ }
632
+ }
605
633
  try {
606
634
  JSON.parse(value);
607
635
  return true;
@@ -620,13 +648,7 @@ const createAttributeSchema = (attribute) => {
620
648
  return yup__namespace.mixed();
621
649
  }
622
650
  };
623
- const addRequiredValidation = (attribute) => (schema) => {
624
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
625
- return schema.min(1, strapiAdmin.translatedErrors.required);
626
- }
627
- if (attribute.required && attribute.type !== "relation") {
628
- return schema.required(strapiAdmin.translatedErrors.required);
629
- }
651
+ const nullableSchema = (schema) => {
630
652
  return schema?.nullable ? schema.nullable() : (
631
653
  // In some cases '.nullable' will not be available on the schema.
632
654
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -634,7 +656,22 @@ const addRequiredValidation = (attribute) => (schema) => {
634
656
  schema
635
657
  );
636
658
  };
637
- const addMinLengthValidation = (attribute) => (schema) => {
659
+ const addNullableValidation = () => (schema) => {
660
+ return nullableSchema(schema);
661
+ };
662
+ const addRequiredValidation = (attribute, options) => (schema) => {
663
+ if (options.status === "draft" || !attribute.required) {
664
+ return schema;
665
+ }
666
+ if (attribute.required && "required" in schema) {
667
+ return schema.required(strapiAdmin.translatedErrors.required);
668
+ }
669
+ return schema;
670
+ };
671
+ const addMinLengthValidation = (attribute, options) => (schema) => {
672
+ if (options.status === "draft") {
673
+ return schema;
674
+ }
638
675
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
639
676
  return schema.min(attribute.minLength, {
640
677
  ...strapiAdmin.translatedErrors.minLength,
@@ -656,32 +693,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
656
693
  }
657
694
  return schema;
658
695
  };
659
- const addMinValidation = (attribute) => (schema) => {
660
- if ("min" in attribute) {
696
+ const addMinValidation = (attribute, options) => (schema) => {
697
+ if (options.status === "draft") {
698
+ return schema;
699
+ }
700
+ if ("min" in attribute && "min" in schema) {
661
701
  const min = toInteger(attribute.min);
662
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
663
- if (!attribute.required && "test" in schema && min) {
664
- return schema.test(
665
- "custom-min",
666
- {
667
- ...strapiAdmin.translatedErrors.min,
668
- values: {
669
- min: attribute.min
670
- }
671
- },
672
- (value) => {
673
- if (!value) {
674
- return true;
675
- }
676
- if (Array.isArray(value) && value.length === 0) {
677
- return true;
678
- }
679
- return value.length >= min;
680
- }
681
- );
682
- }
683
- }
684
- if ("min" in schema && min) {
702
+ if (min) {
685
703
  return schema.min(min, {
686
704
  ...strapiAdmin.translatedErrors.min,
687
705
  values: {
@@ -799,19 +817,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
799
817
  }, {});
800
818
  return componentsByKey;
801
819
  };
802
- const useDocument = (args, opts) => {
820
+ const HOOKS = {
821
+ /**
822
+ * Hook that allows to mutate the displayed headers of the list view table
823
+ * @constant
824
+ * @type {string}
825
+ */
826
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
827
+ /**
828
+ * Hook that allows to mutate the CM's collection types links pre-set filters
829
+ * @constant
830
+ * @type {string}
831
+ */
832
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
833
+ /**
834
+ * Hook that allows to mutate the CM's edit view layout
835
+ * @constant
836
+ * @type {string}
837
+ */
838
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
839
+ /**
840
+ * Hook that allows to mutate the CM's single types links pre-set filters
841
+ * @constant
842
+ * @type {string}
843
+ */
844
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
845
+ };
846
+ const contentTypesApi = contentManagerApi.injectEndpoints({
847
+ endpoints: (builder) => ({
848
+ getContentTypeConfiguration: builder.query({
849
+ query: (uid) => ({
850
+ url: `/content-manager/content-types/${uid}/configuration`,
851
+ method: "GET"
852
+ }),
853
+ transformResponse: (response) => response.data,
854
+ providesTags: (_result, _error, uid) => [
855
+ { type: "ContentTypesConfiguration", id: uid },
856
+ { type: "ContentTypeSettings", id: "LIST" }
857
+ ]
858
+ }),
859
+ getAllContentTypeSettings: builder.query({
860
+ query: () => "/content-manager/content-types-settings",
861
+ transformResponse: (response) => response.data,
862
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
863
+ }),
864
+ updateContentTypeConfiguration: builder.mutation({
865
+ query: ({ uid, ...body }) => ({
866
+ url: `/content-manager/content-types/${uid}/configuration`,
867
+ method: "PUT",
868
+ data: body
869
+ }),
870
+ transformResponse: (response) => response.data,
871
+ invalidatesTags: (_result, _error, { uid }) => [
872
+ { type: "ContentTypesConfiguration", id: uid },
873
+ { type: "ContentTypeSettings", id: "LIST" },
874
+ // Is this necessary?
875
+ { type: "InitialData" }
876
+ ]
877
+ })
878
+ })
879
+ });
880
+ const {
881
+ useGetContentTypeConfigurationQuery,
882
+ useGetAllContentTypeSettingsQuery,
883
+ useUpdateContentTypeConfigurationMutation
884
+ } = contentTypesApi;
885
+ const checkIfAttributeIsDisplayable = (attribute) => {
886
+ const { type } = attribute;
887
+ if (type === "relation") {
888
+ return !attribute.relation.toLowerCase().includes("morph");
889
+ }
890
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
891
+ };
892
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
893
+ if (!mainFieldName) {
894
+ return void 0;
895
+ }
896
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
897
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
898
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
899
+ );
900
+ return {
901
+ name: mainFieldName,
902
+ type: mainFieldType ?? "string"
903
+ };
904
+ };
905
+ const DEFAULT_SETTINGS = {
906
+ bulkable: false,
907
+ filterable: false,
908
+ searchable: false,
909
+ pagination: false,
910
+ defaultSortBy: "",
911
+ defaultSortOrder: "asc",
912
+ mainField: "id",
913
+ pageSize: 10
914
+ };
915
+ const useDocumentLayout = (model) => {
916
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
917
+ const [{ query }] = strapiAdmin.useQueryParams();
918
+ const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
803
919
  const { toggleNotification } = strapiAdmin.useNotification();
804
920
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
921
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
805
922
  const {
806
- currentData: data,
807
- isLoading: isLoadingDocument,
808
- isFetching: isFetchingDocument,
809
- error
810
- } = useGetDocumentQuery(args, {
811
- ...opts,
812
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
813
- });
814
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
923
+ data,
924
+ isLoading: isLoadingConfigs,
925
+ error,
926
+ isFetching: isFetchingConfigs
927
+ } = useGetContentTypeConfigurationQuery(model);
928
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
815
929
  React__namespace.useEffect(() => {
816
930
  if (error) {
817
931
  toggleNotification({
@@ -819,39 +933,255 @@ const useDocument = (args, opts) => {
819
933
  message: formatAPIError(error)
820
934
  });
821
935
  }
822
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
823
- const validationSchema = React__namespace.useMemo(() => {
824
- if (!schema) {
825
- return null;
826
- }
827
- return createYupSchema(schema.attributes, components);
828
- }, [schema, components]);
829
- const validate = React__namespace.useCallback(
830
- (document) => {
831
- if (!validationSchema) {
832
- throw new Error(
833
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
834
- );
835
- }
836
- try {
837
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
838
- return null;
839
- } catch (error2) {
840
- if (error2 instanceof yup.ValidationError) {
841
- return strapiAdmin.getYupValidationErrors(error2);
842
- }
843
- throw error2;
844
- }
936
+ }, [error, formatAPIError, toggleNotification]);
937
+ const editLayout = React__namespace.useMemo(
938
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
939
+ layout: [],
940
+ components: {},
941
+ metadatas: {},
942
+ options: {},
943
+ settings: DEFAULT_SETTINGS
845
944
  },
846
- [validationSchema]
945
+ [data, isLoading, schemas, schema, components]
946
+ );
947
+ const listLayout = React__namespace.useMemo(() => {
948
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
949
+ layout: [],
950
+ metadatas: {},
951
+ options: {},
952
+ settings: DEFAULT_SETTINGS
953
+ };
954
+ }, [data, isLoading, schemas, schema, components]);
955
+ const { layout: edit } = React__namespace.useMemo(
956
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
957
+ layout: editLayout,
958
+ query
959
+ }),
960
+ [editLayout, query, runHookWaterfall]
847
961
  );
848
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
849
962
  return {
850
- components,
851
- document: data?.data,
963
+ error,
964
+ isLoading,
965
+ edit,
966
+ list: listLayout
967
+ };
968
+ };
969
+ const useDocLayout = () => {
970
+ const { model } = useDoc();
971
+ return useDocumentLayout(model);
972
+ };
973
+ const formatEditLayout = (data, {
974
+ schemas,
975
+ schema,
976
+ components
977
+ }) => {
978
+ let currentPanelIndex = 0;
979
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
980
+ data.contentType.layouts.edit,
981
+ schema?.attributes,
982
+ data.contentType.metadatas,
983
+ { configurations: data.components, schemas: components },
984
+ schemas
985
+ ).reduce((panels, row) => {
986
+ if (row.some((field) => field.type === "dynamiczone")) {
987
+ panels.push([row]);
988
+ currentPanelIndex += 2;
989
+ } else {
990
+ if (!panels[currentPanelIndex]) {
991
+ panels.push([row]);
992
+ } else {
993
+ panels[currentPanelIndex].push(row);
994
+ }
995
+ }
996
+ return panels;
997
+ }, []);
998
+ const componentEditAttributes = Object.entries(data.components).reduce(
999
+ (acc, [uid, configuration]) => {
1000
+ acc[uid] = {
1001
+ layout: convertEditLayoutToFieldLayouts(
1002
+ configuration.layouts.edit,
1003
+ components[uid].attributes,
1004
+ configuration.metadatas,
1005
+ { configurations: data.components, schemas: components }
1006
+ ),
1007
+ settings: {
1008
+ ...configuration.settings,
1009
+ icon: components[uid].info.icon,
1010
+ displayName: components[uid].info.displayName
1011
+ }
1012
+ };
1013
+ return acc;
1014
+ },
1015
+ {}
1016
+ );
1017
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1018
+ (acc, [attribute, metadata]) => {
1019
+ return {
1020
+ ...acc,
1021
+ [attribute]: metadata.edit
1022
+ };
1023
+ },
1024
+ {}
1025
+ );
1026
+ return {
1027
+ layout: panelledEditAttributes,
1028
+ components: componentEditAttributes,
1029
+ metadatas: editMetadatas,
1030
+ settings: {
1031
+ ...data.contentType.settings,
1032
+ displayName: schema?.info.displayName
1033
+ },
1034
+ options: {
1035
+ ...schema?.options,
1036
+ ...schema?.pluginOptions,
1037
+ ...data.contentType.options
1038
+ }
1039
+ };
1040
+ };
1041
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1042
+ return rows.map(
1043
+ (row) => row.map((field) => {
1044
+ const attribute = attributes[field.name];
1045
+ if (!attribute) {
1046
+ return null;
1047
+ }
1048
+ const { edit: metadata } = metadatas[field.name];
1049
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1050
+ return {
1051
+ attribute,
1052
+ disabled: !metadata.editable,
1053
+ hint: metadata.description,
1054
+ label: metadata.label ?? "",
1055
+ name: field.name,
1056
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1057
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1058
+ schemas,
1059
+ components: components?.schemas ?? {}
1060
+ }),
1061
+ placeholder: metadata.placeholder ?? "",
1062
+ required: attribute.required ?? false,
1063
+ size: field.size,
1064
+ unique: "unique" in attribute ? attribute.unique : false,
1065
+ visible: metadata.visible ?? true,
1066
+ type: attribute.type
1067
+ };
1068
+ }).filter((field) => field !== null)
1069
+ );
1070
+ };
1071
+ const formatListLayout = (data, {
1072
+ schemas,
1073
+ schema,
1074
+ components
1075
+ }) => {
1076
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1077
+ (acc, [attribute, metadata]) => {
1078
+ return {
1079
+ ...acc,
1080
+ [attribute]: metadata.list
1081
+ };
1082
+ },
1083
+ {}
1084
+ );
1085
+ const listAttributes = convertListLayoutToFieldLayouts(
1086
+ data.contentType.layouts.list,
1087
+ schema?.attributes,
1088
+ listMetadatas,
1089
+ { configurations: data.components, schemas: components },
1090
+ schemas
1091
+ );
1092
+ return {
1093
+ layout: listAttributes,
1094
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1095
+ metadatas: listMetadatas,
1096
+ options: {
1097
+ ...schema?.options,
1098
+ ...schema?.pluginOptions,
1099
+ ...data.contentType.options
1100
+ }
1101
+ };
1102
+ };
1103
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1104
+ return columns.map((name) => {
1105
+ const attribute = attributes[name];
1106
+ if (!attribute) {
1107
+ return null;
1108
+ }
1109
+ const metadata = metadatas[name];
1110
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1111
+ return {
1112
+ attribute,
1113
+ label: metadata.label ?? "",
1114
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1115
+ schemas,
1116
+ components: components?.schemas ?? {}
1117
+ }),
1118
+ name,
1119
+ searchable: metadata.searchable ?? true,
1120
+ sortable: metadata.sortable ?? true
1121
+ };
1122
+ }).filter((field) => field !== null);
1123
+ };
1124
+ const useDocument = (args, opts) => {
1125
+ const { toggleNotification } = strapiAdmin.useNotification();
1126
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1127
+ const {
1128
+ currentData: data,
1129
+ isLoading: isLoadingDocument,
1130
+ isFetching: isFetchingDocument,
1131
+ error
1132
+ } = useGetDocumentQuery(args, {
1133
+ ...opts,
1134
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1135
+ });
1136
+ const {
1137
+ components,
1138
+ schema,
1139
+ schemas,
1140
+ isLoading: isLoadingSchema
1141
+ } = useContentTypeSchema(args.model);
1142
+ React__namespace.useEffect(() => {
1143
+ if (error) {
1144
+ toggleNotification({
1145
+ type: "danger",
1146
+ message: formatAPIError(error)
1147
+ });
1148
+ }
1149
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1150
+ const validationSchema = React__namespace.useMemo(() => {
1151
+ if (!schema) {
1152
+ return null;
1153
+ }
1154
+ return createYupSchema(schema.attributes, components);
1155
+ }, [schema, components]);
1156
+ const validate = React__namespace.useCallback(
1157
+ (document) => {
1158
+ if (!validationSchema) {
1159
+ throw new Error(
1160
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1161
+ );
1162
+ }
1163
+ try {
1164
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1165
+ return null;
1166
+ } catch (error2) {
1167
+ if (error2 instanceof yup.ValidationError) {
1168
+ return strapiAdmin.getYupValidationErrors(error2);
1169
+ }
1170
+ throw error2;
1171
+ }
1172
+ },
1173
+ [validationSchema]
1174
+ );
1175
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1176
+ const hasError = !!error;
1177
+ return {
1178
+ components,
1179
+ document: data?.data,
852
1180
  meta: data?.meta,
853
1181
  isLoading,
1182
+ hasError,
854
1183
  schema,
1184
+ schemas,
855
1185
  validate
856
1186
  };
857
1187
  };
@@ -865,22 +1195,60 @@ const useDoc = () => {
865
1195
  if (!slug) {
866
1196
  throw new Error("Could not find model in url params");
867
1197
  }
1198
+ const document = useDocument(
1199
+ { documentId: origin || id, model: slug, collectionType, params },
1200
+ {
1201
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1202
+ }
1203
+ );
1204
+ const returnId = origin || id === "create" ? void 0 : id;
868
1205
  return {
869
1206
  collectionType,
870
1207
  model: slug,
871
- id: origin || id === "create" ? void 0 : id,
872
- ...useDocument(
873
- { documentId: origin || id, model: slug, collectionType, params },
874
- {
875
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
876
- }
877
- )
1208
+ id: returnId,
1209
+ ...document
1210
+ };
1211
+ };
1212
+ const useContentManagerContext = () => {
1213
+ const {
1214
+ collectionType,
1215
+ model,
1216
+ id,
1217
+ components,
1218
+ isLoading: isLoadingDoc,
1219
+ schema,
1220
+ schemas
1221
+ } = useDoc();
1222
+ const layout = useDocumentLayout(model);
1223
+ const form = strapiAdmin.useForm("useContentManagerContext", (state) => state);
1224
+ const isSingleType = collectionType === SINGLE_TYPES;
1225
+ const slug = model;
1226
+ const isCreatingEntry = id === "create";
1227
+ useContentTypeSchema();
1228
+ const isLoading = isLoadingDoc || layout.isLoading;
1229
+ const error = layout.error;
1230
+ return {
1231
+ error,
1232
+ isLoading,
1233
+ // Base metadata
1234
+ model,
1235
+ collectionType,
1236
+ id,
1237
+ slug,
1238
+ isCreatingEntry,
1239
+ isSingleType,
1240
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1241
+ // All schema infos
1242
+ components,
1243
+ contentType: schema,
1244
+ contentTypes: schemas,
1245
+ // Form state
1246
+ form,
1247
+ // layout infos
1248
+ layout
878
1249
  };
879
1250
  };
880
1251
  const prefixPluginTranslations = (trad, pluginId) => {
881
- if (!pluginId) {
882
- throw new TypeError("pluginId can't be empty");
883
- }
884
1252
  return Object.keys(trad).reduce((acc, current) => {
885
1253
  acc[`${pluginId}.${current}`] = trad[current];
886
1254
  return acc;
@@ -896,6 +1264,8 @@ const useDocumentActions = () => {
896
1264
  const { formatMessage } = reactIntl.useIntl();
897
1265
  const { trackUsage } = strapiAdmin.useTracking();
898
1266
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1267
+ const navigate = reactRouterDom.useNavigate();
1268
+ const setCurrentStep = strapiAdmin.useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
899
1269
  const [deleteDocument] = useDeleteDocumentMutation();
900
1270
  const _delete = React__namespace.useCallback(
901
1271
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1210,6 +1580,7 @@ const useDocumentActions = () => {
1210
1580
  defaultMessage: "Saved document"
1211
1581
  })
1212
1582
  });
1583
+ setCurrentStep("contentManager.success");
1213
1584
  return res.data;
1214
1585
  } catch (err) {
1215
1586
  toggleNotification({
@@ -1231,7 +1602,6 @@ const useDocumentActions = () => {
1231
1602
  sourceId
1232
1603
  });
1233
1604
  if ("error" in res) {
1234
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1235
1605
  return { error: res.error };
1236
1606
  }
1237
1607
  toggleNotification({
@@ -1250,7 +1620,7 @@ const useDocumentActions = () => {
1250
1620
  throw err;
1251
1621
  }
1252
1622
  },
1253
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1623
+ [autoCloneDocument, formatMessage, toggleNotification]
1254
1624
  );
1255
1625
  const [cloneDocument] = useCloneDocumentMutation();
1256
1626
  const clone = React__namespace.useCallback(
@@ -1276,6 +1646,7 @@ const useDocumentActions = () => {
1276
1646
  defaultMessage: "Cloned document"
1277
1647
  })
1278
1648
  });
1649
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1279
1650
  return res.data;
1280
1651
  } catch (err) {
1281
1652
  toggleNotification({
@@ -1286,7 +1657,7 @@ const useDocumentActions = () => {
1286
1657
  throw err;
1287
1658
  }
1288
1659
  },
1289
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1660
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1290
1661
  );
1291
1662
  const [getDoc] = useLazyGetDocumentQuery();
1292
1663
  const getDocument = React__namespace.useCallback(
@@ -1311,10 +1682,10 @@ const useDocumentActions = () => {
1311
1682
  update
1312
1683
  };
1313
1684
  };
1314
- const ProtectedHistoryPage = React.lazy(
1315
- () => Promise.resolve().then(() => require("./History-BblwXv7-.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1685
+ const ProtectedHistoryPage = React__namespace.lazy(
1686
+ () => Promise.resolve().then(() => require("./History-Di3zm4HT.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1316
1687
  );
1317
- const routes$1 = [
1688
+ const routes$2 = [
1318
1689
  {
1319
1690
  path: ":collectionType/:slug/:id/history",
1320
1691
  Component: ProtectedHistoryPage
@@ -1324,32 +1695,45 @@ const routes$1 = [
1324
1695
  Component: ProtectedHistoryPage
1325
1696
  }
1326
1697
  ];
1698
+ const ProtectedPreviewPage = React__namespace.lazy(
1699
+ () => Promise.resolve().then(() => require("./Preview-D_3aO6Ly.js")).then((mod) => ({ default: mod.ProtectedPreviewPage }))
1700
+ );
1701
+ const routes$1 = [
1702
+ {
1703
+ path: ":collectionType/:slug/:id/preview",
1704
+ Component: ProtectedPreviewPage
1705
+ },
1706
+ {
1707
+ path: ":collectionType/:slug/preview",
1708
+ Component: ProtectedPreviewPage
1709
+ }
1710
+ ];
1327
1711
  const ProtectedEditViewPage = React.lazy(
1328
- () => Promise.resolve().then(() => require("./EditViewPage-CmMi2Xsn.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1712
+ () => Promise.resolve().then(() => require("./EditViewPage-QPUftxUd.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1329
1713
  );
1330
1714
  const ProtectedListViewPage = React.lazy(
1331
- () => Promise.resolve().then(() => require("./ListViewPage-DFjn1DNW.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1715
+ () => Promise.resolve().then(() => require("./ListViewPage-DFDcG8gM.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1332
1716
  );
1333
1717
  const ProtectedListConfiguration = React.lazy(
1334
- () => Promise.resolve().then(() => require("./ListConfigurationPage-Cpy4QqNd.js")).then((mod) => ({
1718
+ () => Promise.resolve().then(() => require("./ListConfigurationPage-Cq361KIt.js")).then((mod) => ({
1335
1719
  default: mod.ProtectedListConfiguration
1336
1720
  }))
1337
1721
  );
1338
1722
  const ProtectedEditConfigurationPage = React.lazy(
1339
- () => Promise.resolve().then(() => require("./EditConfigurationPage-CEGwxV-L.js")).then((mod) => ({
1723
+ () => Promise.resolve().then(() => require("./EditConfigurationPage-BHkjAbxH.js")).then((mod) => ({
1340
1724
  default: mod.ProtectedEditConfigurationPage
1341
1725
  }))
1342
1726
  );
1343
1727
  const ProtectedComponentConfigurationPage = React.lazy(
1344
- () => Promise.resolve().then(() => require("./ComponentConfigurationPage-BWQv6yRj.js")).then((mod) => ({
1728
+ () => Promise.resolve().then(() => require("./ComponentConfigurationPage-N-CTtgQa.js")).then((mod) => ({
1345
1729
  default: mod.ProtectedComponentConfigurationPage
1346
1730
  }))
1347
1731
  );
1348
1732
  const NoPermissions = React.lazy(
1349
- () => Promise.resolve().then(() => require("./NoPermissionsPage-DKLmDZnZ.js")).then((mod) => ({ default: mod.NoPermissions }))
1733
+ () => Promise.resolve().then(() => require("./NoPermissionsPage-DyLphsn_.js")).then((mod) => ({ default: mod.NoPermissions }))
1350
1734
  );
1351
1735
  const NoContentType = React.lazy(
1352
- () => Promise.resolve().then(() => require("./NoContentTypePage-C-3ykoxs.js")).then((mod) => ({ default: mod.NoContentType }))
1736
+ () => Promise.resolve().then(() => require("./NoContentTypePage-BSyvnDZZ.js")).then((mod) => ({ default: mod.NoContentType }))
1353
1737
  );
1354
1738
  const CollectionTypePages = () => {
1355
1739
  const { collectionType } = reactRouterDom.useParams();
@@ -1361,7 +1745,7 @@ const CollectionTypePages = () => {
1361
1745
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1362
1746
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1363
1747
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1364
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1748
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1365
1749
  const routes = [
1366
1750
  {
1367
1751
  path: LIST_RELATIVE_PATH,
@@ -1395,6 +1779,7 @@ const routes = [
1395
1779
  path: "no-content-types",
1396
1780
  Component: NoContentType
1397
1781
  },
1782
+ ...routes$2,
1398
1783
  ...routes$1
1399
1784
  ];
1400
1785
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1463,12 +1848,14 @@ const DocumentActionButton = (action) => {
1463
1848
  /* @__PURE__ */ jsxRuntime.jsx(
1464
1849
  designSystem.Button,
1465
1850
  {
1466
- flex: 1,
1851
+ flex: "auto",
1467
1852
  startIcon: action.icon,
1468
1853
  disabled: action.disabled,
1469
1854
  onClick: handleClick(action),
1470
1855
  justifyContent: "center",
1471
1856
  variant: action.variant || "default",
1857
+ paddingTop: "7px",
1858
+ paddingBottom: "7px",
1472
1859
  children: action.label
1473
1860
  }
1474
1861
  ),
@@ -1476,7 +1863,7 @@ const DocumentActionButton = (action) => {
1476
1863
  DocumentActionConfirmDialog,
1477
1864
  {
1478
1865
  ...action.dialog,
1479
- variant: action.variant,
1866
+ variant: action.dialog?.variant ?? action.variant,
1480
1867
  isOpen: dialogId === action.id,
1481
1868
  onClose: handleClose
1482
1869
  }
@@ -1491,6 +1878,11 @@ const DocumentActionButton = (action) => {
1491
1878
  ) : null
1492
1879
  ] });
1493
1880
  };
1881
+ const MenuItem = styledComponents.styled(designSystem.Menu.Item)`
1882
+ &:hover {
1883
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
1884
+ }
1885
+ `;
1494
1886
  const DocumentActionsMenu = ({
1495
1887
  actions: actions2,
1496
1888
  children,
@@ -1533,9 +1925,9 @@ const DocumentActionsMenu = ({
1533
1925
  disabled: isDisabled,
1534
1926
  size: "S",
1535
1927
  endIcon: null,
1536
- paddingTop: "7px",
1537
- paddingLeft: "9px",
1538
- paddingRight: "9px",
1928
+ paddingTop: "4px",
1929
+ paddingLeft: "7px",
1930
+ paddingRight: "7px",
1539
1931
  variant,
1540
1932
  children: [
1541
1933
  /* @__PURE__ */ jsxRuntime.jsx(Icons.More, { "aria-hidden": true, focusable: false }),
@@ -1546,36 +1938,35 @@ const DocumentActionsMenu = ({
1546
1938
  ]
1547
1939
  }
1548
1940
  ),
1549
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1941
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1550
1942
  actions2.map((action) => {
1551
1943
  return /* @__PURE__ */ jsxRuntime.jsx(
1552
- designSystem.Menu.Item,
1944
+ MenuItem,
1553
1945
  {
1554
1946
  disabled: action.disabled,
1555
1947
  onSelect: handleClick(action),
1556
1948
  display: "block",
1557
- children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", gap: 4, children: [
1558
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1559
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1560
- action.label
1561
- ] }),
1562
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsxRuntime.jsx(
1563
- designSystem.Flex,
1564
- {
1565
- alignItems: "center",
1566
- background: "alternative100",
1567
- borderStyle: "solid",
1568
- borderColor: "alternative200",
1569
- borderWidth: "1px",
1570
- height: 5,
1571
- paddingLeft: 2,
1572
- paddingRight: 2,
1573
- hasRadius: true,
1574
- color: "alternative600",
1575
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1576
- }
1577
- )
1578
- ] })
1949
+ isVariantDanger: action.variant === "danger",
1950
+ isDisabled: action.disabled,
1951
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(
1952
+ designSystem.Flex,
1953
+ {
1954
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1955
+ gap: 2,
1956
+ tag: "span",
1957
+ children: [
1958
+ /* @__PURE__ */ jsxRuntime.jsx(
1959
+ designSystem.Flex,
1960
+ {
1961
+ tag: "span",
1962
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1963
+ children: action.icon
1964
+ }
1965
+ ),
1966
+ action.label
1967
+ ]
1968
+ }
1969
+ ) })
1579
1970
  },
1580
1971
  action.id
1581
1972
  );
@@ -1655,11 +2046,11 @@ const DocumentActionConfirmDialog = ({
1655
2046
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
1656
2047
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: content }),
1657
2048
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
1658
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", children: formatMessage({
2049
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1659
2050
  id: "app.components.Button.cancel",
1660
2051
  defaultMessage: "Cancel"
1661
2052
  }) }) }),
1662
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, children: formatMessage({
2053
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1663
2054
  id: "app.components.Button.confirm",
1664
2055
  defaultMessage: "Confirm"
1665
2056
  }) })
@@ -1682,10 +2073,22 @@ const DocumentActionModal = ({
1682
2073
  };
1683
2074
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
1684
2075
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: title }) }),
1685
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content }),
1686
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Footer, { children: typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer })
2076
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: Content }),
2077
+ typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer
1687
2078
  ] }) });
1688
2079
  };
2080
+ const transformData = (data) => {
2081
+ if (Array.isArray(data)) {
2082
+ return data.map(transformData);
2083
+ }
2084
+ if (typeof data === "object" && data !== null) {
2085
+ if ("apiData" in data) {
2086
+ return data.apiData;
2087
+ }
2088
+ return mapValues__default.default(transformData)(data);
2089
+ }
2090
+ return data;
2091
+ };
1689
2092
  const PublishAction$1 = ({
1690
2093
  activeTab,
1691
2094
  documentId,
@@ -1698,13 +2101,18 @@ const PublishAction$1 = ({
1698
2101
  const navigate = reactRouterDom.useNavigate();
1699
2102
  const { toggleNotification } = strapiAdmin.useNotification();
1700
2103
  const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
2104
+ const isListView = reactRouterDom.useMatch(LIST_PATH) !== null;
1701
2105
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2106
+ const { id } = reactRouterDom.useParams();
1702
2107
  const { formatMessage } = reactIntl.useIntl();
1703
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1704
- "PublishAction",
1705
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1706
- );
2108
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1707
2109
  const { publish } = useDocumentActions();
2110
+ const [
2111
+ countDraftRelations,
2112
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2113
+ ] = useLazyGetDraftRelationCountQuery();
2114
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React__namespace.useState(0);
2115
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React__namespace.useState(0);
1708
2116
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1709
2117
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1710
2118
  const modified = strapiAdmin.useForm("PublishAction", ({ modified: modified2 }) => modified2);
@@ -1713,10 +2121,107 @@ const PublishAction$1 = ({
1713
2121
  const validate = strapiAdmin.useForm("PublishAction", (state) => state.validate);
1714
2122
  const setErrors = strapiAdmin.useForm("PublishAction", (state) => state.setErrors);
1715
2123
  const formValues = strapiAdmin.useForm("PublishAction", ({ values }) => values);
1716
- const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
2124
+ React__namespace.useEffect(() => {
2125
+ if (isErrorDraftRelations) {
2126
+ toggleNotification({
2127
+ type: "danger",
2128
+ message: formatMessage({
2129
+ id: getTranslation("error.records.fetch-draft-relatons"),
2130
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2131
+ })
2132
+ });
2133
+ }
2134
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2135
+ React__namespace.useEffect(() => {
2136
+ const localDraftRelations = /* @__PURE__ */ new Set();
2137
+ const extractDraftRelations = (data) => {
2138
+ const relations = data.connect || [];
2139
+ relations.forEach((relation) => {
2140
+ if (relation.status === "draft") {
2141
+ localDraftRelations.add(relation.id);
2142
+ }
2143
+ });
2144
+ };
2145
+ const traverseAndExtract = (data) => {
2146
+ Object.entries(data).forEach(([key, value]) => {
2147
+ if (key === "connect" && Array.isArray(value)) {
2148
+ extractDraftRelations({ connect: value });
2149
+ } else if (typeof value === "object" && value !== null) {
2150
+ traverseAndExtract(value);
2151
+ }
2152
+ });
2153
+ };
2154
+ if (!documentId || modified) {
2155
+ traverseAndExtract(formValues);
2156
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2157
+ }
2158
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2159
+ React__namespace.useEffect(() => {
2160
+ if (!document || !document.documentId || isListView) {
2161
+ return;
2162
+ }
2163
+ const fetchDraftRelationsCount = async () => {
2164
+ const { data, error } = await countDraftRelations({
2165
+ collectionType,
2166
+ model,
2167
+ documentId,
2168
+ params
2169
+ });
2170
+ if (error) {
2171
+ throw error;
2172
+ }
2173
+ if (data) {
2174
+ setServerCountOfDraftRelations(data.data);
2175
+ }
2176
+ };
2177
+ fetchDraftRelationsCount();
2178
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
2179
+ const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1717
2180
  if (!schema?.options?.draftAndPublish) {
1718
2181
  return null;
1719
2182
  }
2183
+ const performPublish = async () => {
2184
+ setSubmitting(true);
2185
+ try {
2186
+ const { errors } = await validate(true, {
2187
+ status: "published"
2188
+ });
2189
+ if (errors) {
2190
+ toggleNotification({
2191
+ type: "danger",
2192
+ message: formatMessage({
2193
+ id: "content-manager.validation.error",
2194
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2195
+ })
2196
+ });
2197
+ return;
2198
+ }
2199
+ const res = await publish(
2200
+ {
2201
+ collectionType,
2202
+ model,
2203
+ documentId,
2204
+ params
2205
+ },
2206
+ transformData(formValues)
2207
+ );
2208
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2209
+ if (id === "create") {
2210
+ navigate({
2211
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2212
+ search: rawQuery
2213
+ });
2214
+ }
2215
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2216
+ setErrors(formatValidationErrors(res.error));
2217
+ }
2218
+ } finally {
2219
+ setSubmitting(false);
2220
+ }
2221
+ };
2222
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2223
+ const enableDraftRelationsCount = false;
2224
+ const hasDraftRelations = enableDraftRelationsCount;
1720
2225
  return {
1721
2226
  /**
1722
2227
  * Disabled when:
@@ -1726,49 +2231,36 @@ const PublishAction$1 = ({
1726
2231
  * - the document is already published & not modified
1727
2232
  * - the document is being created & not modified
1728
2233
  * - the user doesn't have the permission to publish
1729
- * - the user doesn't have the permission to create a new document
1730
- * - the user doesn't have the permission to update the document
1731
2234
  */
1732
- disabled: isCloning || isSubmitting || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2235
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1733
2236
  label: formatMessage({
1734
2237
  id: "app.utils.publish",
1735
2238
  defaultMessage: "Publish"
1736
2239
  }),
1737
2240
  onClick: async () => {
1738
- setSubmitting(true);
1739
- try {
1740
- const { errors } = await validate();
1741
- if (errors) {
1742
- toggleNotification({
1743
- type: "danger",
1744
- message: formatMessage({
1745
- id: "content-manager.validation.error",
1746
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1747
- })
1748
- });
1749
- return;
1750
- }
1751
- const res = await publish(
1752
- {
1753
- collectionType,
1754
- model,
1755
- documentId,
1756
- params
1757
- },
1758
- formValues
1759
- );
1760
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1761
- navigate({
1762
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1763
- search: rawQuery
1764
- });
1765
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1766
- setErrors(formatValidationErrors(res.error));
2241
+ await performPublish();
2242
+ },
2243
+ dialog: hasDraftRelations ? {
2244
+ type: "dialog",
2245
+ variant: "danger",
2246
+ footer: null,
2247
+ title: formatMessage({
2248
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2249
+ defaultMessage: "Confirmation"
2250
+ }),
2251
+ content: formatMessage(
2252
+ {
2253
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2254
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2255
+ },
2256
+ {
2257
+ count: totalDraftRelations
1767
2258
  }
1768
- } finally {
1769
- setSubmitting(false);
2259
+ ),
2260
+ onConfirm: async () => {
2261
+ await performPublish();
1770
2262
  }
1771
- }
2263
+ } : void 0
1772
2264
  };
1773
2265
  };
1774
2266
  PublishAction$1.type = "publish";
@@ -1784,10 +2276,6 @@ const UpdateAction = ({
1784
2276
  const cloneMatch = reactRouterDom.useMatch(CLONE_PATH);
1785
2277
  const isCloning = cloneMatch !== null;
1786
2278
  const { formatMessage } = reactIntl.useIntl();
1787
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1788
- canCreate: canCreate2,
1789
- canUpdate: canUpdate2
1790
- }));
1791
2279
  const { create, update, clone } = useDocumentActions();
1792
2280
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1793
2281
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
@@ -1804,18 +2292,18 @@ const UpdateAction = ({
1804
2292
  * - the form is submitting
1805
2293
  * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1806
2294
  * - the active tab is the published tab
1807
- * - the user doesn't have the permission to create a new document
1808
- * - the user doesn't have the permission to update the document
1809
2295
  */
1810
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
2296
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1811
2297
  label: formatMessage({
1812
- id: "content-manager.containers.Edit.save",
2298
+ id: "global.save",
1813
2299
  defaultMessage: "Save"
1814
2300
  }),
1815
2301
  onClick: async () => {
1816
2302
  setSubmitting(true);
1817
2303
  try {
1818
- const { errors } = await validate();
2304
+ const { errors } = await validate(true, {
2305
+ status: "draft"
2306
+ });
1819
2307
  if (errors) {
1820
2308
  toggleNotification({
1821
2309
  type: "danger",
@@ -1833,13 +2321,16 @@ const UpdateAction = ({
1833
2321
  documentId: cloneMatch.params.origin,
1834
2322
  params
1835
2323
  },
1836
- document
2324
+ transformData(document)
1837
2325
  );
1838
2326
  if ("data" in res) {
1839
- navigate({
1840
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1841
- search: rawQuery
1842
- });
2327
+ navigate(
2328
+ {
2329
+ pathname: `../${res.data.documentId}`,
2330
+ search: rawQuery
2331
+ },
2332
+ { relative: "path" }
2333
+ );
1843
2334
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1844
2335
  setErrors(formatValidationErrors(res.error));
1845
2336
  }
@@ -1851,7 +2342,7 @@ const UpdateAction = ({
1851
2342
  documentId,
1852
2343
  params
1853
2344
  },
1854
- document
2345
+ transformData(document)
1855
2346
  );
1856
2347
  if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1857
2348
  setErrors(formatValidationErrors(res.error));
@@ -1864,15 +2355,15 @@ const UpdateAction = ({
1864
2355
  model,
1865
2356
  params
1866
2357
  },
1867
- document
2358
+ transformData(document)
1868
2359
  );
1869
2360
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1870
2361
  navigate(
1871
2362
  {
1872
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2363
+ pathname: `../${res.data.documentId}`,
1873
2364
  search: rawQuery
1874
2365
  },
1875
- { replace: true }
2366
+ { replace: true, relative: "path" }
1876
2367
  );
1877
2368
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1878
2369
  setErrors(formatValidationErrors(res.error));
@@ -1917,7 +2408,7 @@ const UnpublishAction$1 = ({
1917
2408
  id: "app.utils.unpublish",
1918
2409
  defaultMessage: "Unpublish"
1919
2410
  }),
1920
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2411
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
1921
2412
  onClick: async () => {
1922
2413
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1923
2414
  if (!documentId) {
@@ -2029,7 +2520,7 @@ const DiscardAction = ({
2029
2520
  id: "content-manager.actions.discard.label",
2030
2521
  defaultMessage: "Discard changes"
2031
2522
  }),
2032
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2523
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
2033
2524
  position: ["panel", "table-row"],
2034
2525
  variant: "danger",
2035
2526
  dialog: {
@@ -2057,11 +2548,6 @@ const DiscardAction = ({
2057
2548
  };
2058
2549
  };
2059
2550
  DiscardAction.type = "discard";
2060
- const StyledCrossCircle = styledComponents.styled(Icons.CrossCircle)`
2061
- path {
2062
- fill: currentColor;
2063
- }
2064
- `;
2065
2551
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2066
2552
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2067
2553
  const RelativeTime = React__namespace.forwardRef(
@@ -2074,7 +2560,7 @@ const RelativeTime = React__namespace.forwardRef(
2074
2560
  });
2075
2561
  const unit = intervals.find((intervalUnit) => {
2076
2562
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2077
- });
2563
+ }) ?? "seconds";
2078
2564
  const relativeTime = dateFns.isPast(timestamp) ? -interval[unit] : interval[unit];
2079
2565
  const customInterval = customIntervals.find(
2080
2566
  (custom) => interval[custom.unit] < custom.threshold
@@ -2108,34 +2594,34 @@ const getDisplayName = ({
2108
2594
  return email ?? "";
2109
2595
  };
2110
2596
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2111
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2112
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2113
- 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) }) });
2597
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2598
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2599
+ const { formatMessage } = reactIntl.useIntl();
2600
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2601
+ id: `content-manager.containers.List.${status}`,
2602
+ defaultMessage: capitalise(status)
2603
+ }) }) });
2114
2604
  };
2115
2605
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2116
2606
  const { formatMessage } = reactIntl.useIntl();
2117
2607
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2608
+ const params = reactRouterDom.useParams();
2118
2609
  const title = isCreating ? formatMessage({
2119
2610
  id: "content-manager.containers.edit.title.new",
2120
2611
  defaultMessage: "Create an entry"
2121
2612
  }) : documentTitle;
2122
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 8, paddingBottom: 4, gap: 3, children: [
2123
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.BackButton, {}),
2124
- /* @__PURE__ */ jsxRuntime.jsxs(
2125
- designSystem.Flex,
2613
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2614
+ /* @__PURE__ */ jsxRuntime.jsx(
2615
+ strapiAdmin.BackButton,
2126
2616
  {
2127
- width: "100%",
2128
- justifyContent: "space-between",
2129
- paddingTop: 1,
2130
- gap: "80px",
2131
- alignItems: "flex-start",
2132
- children: [
2133
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2134
- /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2135
- ]
2617
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2136
2618
  }
2137
2619
  ),
2138
- status ? /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) : null
2620
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2621
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2622
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2623
+ ] }),
2624
+ status ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2139
2625
  ] });
2140
2626
  };
2141
2627
  const HeaderToolbar = () => {
@@ -2218,12 +2704,12 @@ const Information = ({ activeTab }) => {
2218
2704
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2219
2705
  label: formatMessage({
2220
2706
  id: "content-manager.containers.edit.information.last-published.label",
2221
- defaultMessage: "Last published"
2707
+ defaultMessage: "Published"
2222
2708
  }),
2223
2709
  value: formatMessage(
2224
2710
  {
2225
2711
  id: "content-manager.containers.edit.information.last-published.value",
2226
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2712
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2227
2713
  },
2228
2714
  {
2229
2715
  time: /* @__PURE__ */ jsxRuntime.jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2236,12 +2722,12 @@ const Information = ({ activeTab }) => {
2236
2722
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2237
2723
  label: formatMessage({
2238
2724
  id: "content-manager.containers.edit.information.last-draft.label",
2239
- defaultMessage: "Last draft"
2725
+ defaultMessage: "Updated"
2240
2726
  }),
2241
2727
  value: formatMessage(
2242
2728
  {
2243
2729
  id: "content-manager.containers.edit.information.last-draft.value",
2244
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2730
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2245
2731
  },
2246
2732
  {
2247
2733
  time: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2259,12 +2745,12 @@ const Information = ({ activeTab }) => {
2259
2745
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2260
2746
  label: formatMessage({
2261
2747
  id: "content-manager.containers.edit.information.document.label",
2262
- defaultMessage: "Document"
2748
+ defaultMessage: "Created"
2263
2749
  }),
2264
2750
  value: formatMessage(
2265
2751
  {
2266
2752
  id: "content-manager.containers.edit.information.document.value",
2267
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2753
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2268
2754
  },
2269
2755
  {
2270
2756
  time: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2302,25 +2788,77 @@ const Information = ({ activeTab }) => {
2302
2788
  );
2303
2789
  };
2304
2790
  const HeaderActions = ({ actions: actions2 }) => {
2305
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: actions2.map((action) => {
2306
- if ("options" in action) {
2791
+ const [dialogId, setDialogId] = React__namespace.useState(null);
2792
+ const handleClick = (action) => async (e) => {
2793
+ if (!("options" in action)) {
2794
+ const { onClick = () => false, dialog, id } = action;
2795
+ const muteDialog = await onClick(e);
2796
+ if (dialog && !muteDialog) {
2797
+ e.preventDefault();
2798
+ setDialogId(id);
2799
+ }
2800
+ }
2801
+ };
2802
+ const handleClose = () => {
2803
+ setDialogId(null);
2804
+ };
2805
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, children: actions2.map((action) => {
2806
+ if (action.options) {
2307
2807
  return /* @__PURE__ */ jsxRuntime.jsx(
2308
2808
  designSystem.SingleSelect,
2309
2809
  {
2310
2810
  size: "S",
2311
- disabled: action.disabled,
2312
- "aria-label": action.label,
2313
2811
  onChange: action.onSelect,
2314
- value: action.value,
2812
+ "aria-label": action.label,
2813
+ ...action,
2315
2814
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { ...option, children: label }, option.value))
2316
2815
  },
2317
2816
  action.id
2318
2817
  );
2319
2818
  } else {
2320
- return null;
2819
+ if (action.type === "icon") {
2820
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
2821
+ /* @__PURE__ */ jsxRuntime.jsx(
2822
+ designSystem.IconButton,
2823
+ {
2824
+ disabled: action.disabled,
2825
+ label: action.label,
2826
+ size: "S",
2827
+ onClick: handleClick(action),
2828
+ children: action.icon
2829
+ }
2830
+ ),
2831
+ action.dialog ? /* @__PURE__ */ jsxRuntime.jsx(
2832
+ HeaderActionDialog,
2833
+ {
2834
+ ...action.dialog,
2835
+ isOpen: dialogId === action.id,
2836
+ onClose: handleClose
2837
+ }
2838
+ ) : null
2839
+ ] }, action.id);
2840
+ }
2321
2841
  }
2322
2842
  }) });
2323
2843
  };
2844
+ const HeaderActionDialog = ({
2845
+ onClose,
2846
+ onCancel,
2847
+ title,
2848
+ content: Content,
2849
+ isOpen
2850
+ }) => {
2851
+ const handleClose = async () => {
2852
+ if (onCancel) {
2853
+ await onCancel();
2854
+ }
2855
+ onClose();
2856
+ };
2857
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2858
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2859
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content
2860
+ ] }) });
2861
+ };
2324
2862
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2325
2863
  const navigate = reactRouterDom.useNavigate();
2326
2864
  const { formatMessage } = reactIntl.useIntl();
@@ -2361,12 +2899,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2361
2899
  const { delete: deleteAction } = useDocumentActions();
2362
2900
  const { toggleNotification } = strapiAdmin.useNotification();
2363
2901
  const setSubmitting = strapiAdmin.useForm("DeleteAction", (state) => state.setSubmitting);
2902
+ const isLocalized = document?.locale != null;
2364
2903
  return {
2365
2904
  disabled: !canDelete || !document,
2366
- label: formatMessage({
2367
- id: "content-manager.actions.delete.label",
2368
- defaultMessage: "Delete document"
2369
- }),
2905
+ label: formatMessage(
2906
+ {
2907
+ id: "content-manager.actions.delete.label",
2908
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2909
+ },
2910
+ { isLocalized }
2911
+ ),
2370
2912
  icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2371
2913
  dialog: {
2372
2914
  type: "dialog",
@@ -2417,408 +2959,106 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2417
2959
  }
2418
2960
  }
2419
2961
  },
2420
- variant: "danger",
2421
- position: ["header", "table-row"]
2422
- };
2423
- };
2424
- DeleteAction$1.type = "delete";
2425
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2426
- const Panels = () => {
2427
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2428
- const [
2429
- {
2430
- query: { status }
2431
- }
2432
- ] = strapiAdmin.useQueryParams({
2433
- status: "draft"
2434
- });
2435
- const { model, id, document, meta, collectionType } = useDoc();
2436
- const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2437
- const props = {
2438
- activeTab: status,
2439
- model,
2440
- documentId: id,
2441
- document: isCloning ? void 0 : document,
2442
- meta: isCloning ? void 0 : meta,
2443
- collectionType
2444
- };
2445
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2446
- strapiAdmin.DescriptionComponentRenderer,
2447
- {
2448
- props,
2449
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2450
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2451
- }
2452
- ) });
2453
- };
2454
- const ActionsPanel = () => {
2455
- const { formatMessage } = reactIntl.useIntl();
2456
- return {
2457
- title: formatMessage({
2458
- id: "content-manager.containers.edit.panels.default.title",
2459
- defaultMessage: "Document"
2460
- }),
2461
- content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2462
- };
2463
- };
2464
- ActionsPanel.type = "actions";
2465
- const ActionsPanelContent = () => {
2466
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2467
- const [
2468
- {
2469
- query: { status = "draft" }
2470
- }
2471
- ] = strapiAdmin.useQueryParams();
2472
- const { model, id, document, meta, collectionType } = useDoc();
2473
- const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
2474
- const props = {
2475
- activeTab: status,
2476
- model,
2477
- documentId: id,
2478
- document: isCloning ? void 0 : document,
2479
- meta: isCloning ? void 0 : meta,
2480
- collectionType
2481
- };
2482
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
2483
- /* @__PURE__ */ jsxRuntime.jsx(
2484
- strapiAdmin.DescriptionComponentRenderer,
2485
- {
2486
- props,
2487
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2488
- children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
2489
- }
2490
- ),
2491
- /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
2492
- ] });
2493
- };
2494
- const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
2495
- return /* @__PURE__ */ jsxRuntime.jsxs(
2496
- designSystem.Flex,
2497
- {
2498
- ref,
2499
- tag: "aside",
2500
- "aria-labelledby": "additional-information",
2501
- background: "neutral0",
2502
- borderColor: "neutral150",
2503
- hasRadius: true,
2504
- paddingBottom: 4,
2505
- paddingLeft: 4,
2506
- paddingRight: 4,
2507
- paddingTop: 4,
2508
- shadow: "tableShadow",
2509
- gap: 3,
2510
- direction: "column",
2511
- justifyContent: "stretch",
2512
- alignItems: "flex-start",
2513
- children: [
2514
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2515
- children
2516
- ]
2517
- }
2518
- );
2519
- });
2520
- const HOOKS = {
2521
- /**
2522
- * Hook that allows to mutate the displayed headers of the list view table
2523
- * @constant
2524
- * @type {string}
2525
- */
2526
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2527
- /**
2528
- * Hook that allows to mutate the CM's collection types links pre-set filters
2529
- * @constant
2530
- * @type {string}
2531
- */
2532
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2533
- /**
2534
- * Hook that allows to mutate the CM's edit view layout
2535
- * @constant
2536
- * @type {string}
2537
- */
2538
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2539
- /**
2540
- * Hook that allows to mutate the CM's single types links pre-set filters
2541
- * @constant
2542
- * @type {string}
2543
- */
2544
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2545
- };
2546
- const contentTypesApi = contentManagerApi.injectEndpoints({
2547
- endpoints: (builder) => ({
2548
- getContentTypeConfiguration: builder.query({
2549
- query: (uid) => ({
2550
- url: `/content-manager/content-types/${uid}/configuration`,
2551
- method: "GET"
2552
- }),
2553
- transformResponse: (response) => response.data,
2554
- providesTags: (_result, _error, uid) => [
2555
- { type: "ContentTypesConfiguration", id: uid },
2556
- { type: "ContentTypeSettings", id: "LIST" }
2557
- ]
2558
- }),
2559
- getAllContentTypeSettings: builder.query({
2560
- query: () => "/content-manager/content-types-settings",
2561
- transformResponse: (response) => response.data,
2562
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2563
- }),
2564
- updateContentTypeConfiguration: builder.mutation({
2565
- query: ({ uid, ...body }) => ({
2566
- url: `/content-manager/content-types/${uid}/configuration`,
2567
- method: "PUT",
2568
- data: body
2569
- }),
2570
- transformResponse: (response) => response.data,
2571
- invalidatesTags: (_result, _error, { uid }) => [
2572
- { type: "ContentTypesConfiguration", id: uid },
2573
- { type: "ContentTypeSettings", id: "LIST" },
2574
- // Is this necessary?
2575
- { type: "InitialData" }
2576
- ]
2577
- })
2578
- })
2579
- });
2580
- const {
2581
- useGetContentTypeConfigurationQuery,
2582
- useGetAllContentTypeSettingsQuery,
2583
- useUpdateContentTypeConfigurationMutation
2584
- } = contentTypesApi;
2585
- const checkIfAttributeIsDisplayable = (attribute) => {
2586
- const { type } = attribute;
2587
- if (type === "relation") {
2588
- return !attribute.relation.toLowerCase().includes("morph");
2589
- }
2590
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2591
- };
2592
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2593
- if (!mainFieldName) {
2594
- return void 0;
2595
- }
2596
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2597
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2598
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2599
- );
2600
- return {
2601
- name: mainFieldName,
2602
- type: mainFieldType ?? "string"
2603
- };
2604
- };
2605
- const DEFAULT_SETTINGS = {
2606
- bulkable: false,
2607
- filterable: false,
2608
- searchable: false,
2609
- pagination: false,
2610
- defaultSortBy: "",
2611
- defaultSortOrder: "asc",
2612
- mainField: "id",
2613
- pageSize: 10
2614
- };
2615
- const useDocumentLayout = (model) => {
2616
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2617
- const [{ query }] = strapiAdmin.useQueryParams();
2618
- const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2619
- const { toggleNotification } = strapiAdmin.useNotification();
2620
- const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
2621
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2622
- const {
2623
- data,
2624
- isLoading: isLoadingConfigs,
2625
- error,
2626
- isFetching: isFetchingConfigs
2627
- } = useGetContentTypeConfigurationQuery(model);
2628
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2629
- React__namespace.useEffect(() => {
2630
- if (error) {
2631
- toggleNotification({
2632
- type: "danger",
2633
- message: formatAPIError(error)
2634
- });
2635
- }
2636
- }, [error, formatAPIError, toggleNotification]);
2637
- const editLayout = React__namespace.useMemo(
2638
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2639
- layout: [],
2640
- components: {},
2641
- metadatas: {},
2642
- options: {},
2643
- settings: DEFAULT_SETTINGS
2644
- },
2645
- [data, isLoading, schemas, schema, components]
2646
- );
2647
- const listLayout = React__namespace.useMemo(() => {
2648
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2649
- layout: [],
2650
- metadatas: {},
2651
- options: {},
2652
- settings: DEFAULT_SETTINGS
2653
- };
2654
- }, [data, isLoading, schemas, schema, components]);
2655
- const { layout: edit } = React__namespace.useMemo(
2656
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2657
- layout: editLayout,
2658
- query
2659
- }),
2660
- [editLayout, query, runHookWaterfall]
2661
- );
2662
- return {
2663
- error,
2664
- isLoading,
2665
- edit,
2666
- list: listLayout
2667
- };
2668
- };
2669
- const useDocLayout = () => {
2670
- const { model } = useDoc();
2671
- return useDocumentLayout(model);
2672
- };
2673
- const formatEditLayout = (data, {
2674
- schemas,
2675
- schema,
2676
- components
2677
- }) => {
2678
- let currentPanelIndex = 0;
2679
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2680
- data.contentType.layouts.edit,
2681
- schema?.attributes,
2682
- data.contentType.metadatas,
2683
- { configurations: data.components, schemas: components },
2684
- schemas
2685
- ).reduce((panels, row) => {
2686
- if (row.some((field) => field.type === "dynamiczone")) {
2687
- panels.push([row]);
2688
- currentPanelIndex += 2;
2689
- } else {
2690
- if (!panels[currentPanelIndex]) {
2691
- panels.push([]);
2692
- }
2693
- panels[currentPanelIndex].push(row);
2694
- }
2695
- return panels;
2696
- }, []);
2697
- const componentEditAttributes = Object.entries(data.components).reduce(
2698
- (acc, [uid, configuration]) => {
2699
- acc[uid] = {
2700
- layout: convertEditLayoutToFieldLayouts(
2701
- configuration.layouts.edit,
2702
- components[uid].attributes,
2703
- configuration.metadatas
2704
- ),
2705
- settings: {
2706
- ...configuration.settings,
2707
- icon: components[uid].info.icon,
2708
- displayName: components[uid].info.displayName
2709
- }
2710
- };
2711
- return acc;
2712
- },
2713
- {}
2714
- );
2715
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2716
- (acc, [attribute, metadata]) => {
2717
- return {
2718
- ...acc,
2719
- [attribute]: metadata.edit
2720
- };
2721
- },
2722
- {}
2723
- );
2724
- return {
2725
- layout: panelledEditAttributes,
2726
- components: componentEditAttributes,
2727
- metadatas: editMetadatas,
2728
- settings: {
2729
- ...data.contentType.settings,
2730
- displayName: schema?.info.displayName
2731
- },
2732
- options: {
2733
- ...schema?.options,
2734
- ...schema?.pluginOptions,
2735
- ...data.contentType.options
2736
- }
2962
+ variant: "danger",
2963
+ position: ["header", "table-row"]
2737
2964
  };
2738
2965
  };
2739
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2740
- return rows.map(
2741
- (row) => row.map((field) => {
2742
- const attribute = attributes[field.name];
2743
- if (!attribute) {
2744
- return null;
2745
- }
2746
- const { edit: metadata } = metadatas[field.name];
2747
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2748
- return {
2749
- attribute,
2750
- disabled: !metadata.editable,
2751
- hint: metadata.description,
2752
- label: metadata.label ?? "",
2753
- name: field.name,
2754
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2755
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2756
- schemas,
2757
- components: components?.schemas ?? {}
2758
- }),
2759
- placeholder: metadata.placeholder ?? "",
2760
- required: attribute.required ?? false,
2761
- size: field.size,
2762
- unique: "unique" in attribute ? attribute.unique : false,
2763
- visible: metadata.visible ?? true,
2764
- type: attribute.type
2765
- };
2766
- }).filter((field) => field !== null)
2767
- );
2966
+ DeleteAction$1.type = "delete";
2967
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2968
+ const Panels = () => {
2969
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2970
+ const [
2971
+ {
2972
+ query: { status }
2973
+ }
2974
+ ] = strapiAdmin.useQueryParams({
2975
+ status: "draft"
2976
+ });
2977
+ const { model, id, document, meta, collectionType } = useDoc();
2978
+ const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2979
+ const props = {
2980
+ activeTab: status,
2981
+ model,
2982
+ documentId: id,
2983
+ document: isCloning ? void 0 : document,
2984
+ meta: isCloning ? void 0 : meta,
2985
+ collectionType
2986
+ };
2987
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2988
+ strapiAdmin.DescriptionComponentRenderer,
2989
+ {
2990
+ props,
2991
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2992
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2993
+ }
2994
+ ) });
2768
2995
  };
2769
- const formatListLayout = (data, {
2770
- schemas,
2771
- schema,
2772
- components
2773
- }) => {
2774
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2775
- (acc, [attribute, metadata]) => {
2776
- return {
2777
- ...acc,
2778
- [attribute]: metadata.list
2779
- };
2780
- },
2781
- {}
2782
- );
2783
- const listAttributes = convertListLayoutToFieldLayouts(
2784
- data.contentType.layouts.list,
2785
- schema?.attributes,
2786
- listMetadatas,
2787
- { configurations: data.components, schemas: components },
2788
- schemas
2789
- );
2996
+ const ActionsPanel = () => {
2997
+ const { formatMessage } = reactIntl.useIntl();
2790
2998
  return {
2791
- layout: listAttributes,
2792
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2793
- metadatas: listMetadatas,
2794
- options: {
2795
- ...schema?.options,
2796
- ...schema?.pluginOptions,
2797
- ...data.contentType.options
2798
- }
2999
+ title: formatMessage({
3000
+ id: "content-manager.containers.edit.panels.default.title",
3001
+ defaultMessage: "Entry"
3002
+ }),
3003
+ content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2799
3004
  };
2800
3005
  };
2801
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2802
- return columns.map((name) => {
2803
- const attribute = attributes[name];
2804
- if (!attribute) {
2805
- return null;
3006
+ ActionsPanel.type = "actions";
3007
+ const ActionsPanelContent = () => {
3008
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
3009
+ const [
3010
+ {
3011
+ query: { status = "draft" }
2806
3012
  }
2807
- const metadata = metadatas[name];
2808
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2809
- return {
2810
- attribute,
2811
- label: metadata.label ?? "",
2812
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2813
- schemas,
2814
- components: components?.schemas ?? {}
2815
- }),
2816
- name,
2817
- searchable: metadata.searchable ?? true,
2818
- sortable: metadata.sortable ?? true
2819
- };
2820
- }).filter((field) => field !== null);
3013
+ ] = strapiAdmin.useQueryParams();
3014
+ const { model, id, document, meta, collectionType } = useDoc();
3015
+ const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
3016
+ const props = {
3017
+ activeTab: status,
3018
+ model,
3019
+ documentId: id,
3020
+ document: isCloning ? void 0 : document,
3021
+ meta: isCloning ? void 0 : meta,
3022
+ collectionType
3023
+ };
3024
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
3025
+ /* @__PURE__ */ jsxRuntime.jsx(
3026
+ strapiAdmin.DescriptionComponentRenderer,
3027
+ {
3028
+ props,
3029
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
3030
+ children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
3031
+ }
3032
+ ),
3033
+ /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
3034
+ ] });
2821
3035
  };
3036
+ const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
3037
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3038
+ designSystem.Flex,
3039
+ {
3040
+ ref,
3041
+ tag: "aside",
3042
+ "aria-labelledby": "additional-information",
3043
+ background: "neutral0",
3044
+ borderColor: "neutral150",
3045
+ hasRadius: true,
3046
+ paddingBottom: 4,
3047
+ paddingLeft: 4,
3048
+ paddingRight: 4,
3049
+ paddingTop: 4,
3050
+ shadow: "tableShadow",
3051
+ gap: 3,
3052
+ direction: "column",
3053
+ justifyContent: "stretch",
3054
+ alignItems: "flex-start",
3055
+ children: [
3056
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3057
+ children
3058
+ ]
3059
+ }
3060
+ );
3061
+ });
2822
3062
  const ConfirmBulkActionDialog = ({
2823
3063
  onToggleDialog,
2824
3064
  isOpen = false,
@@ -2826,7 +3066,7 @@ const ConfirmBulkActionDialog = ({
2826
3066
  endAction
2827
3067
  }) => {
2828
3068
  const { formatMessage } = reactIntl.useIntl();
2829
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { onOpenChange: onToggleDialog, open: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
3069
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2830
3070
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage({
2831
3071
  id: "app.components.ConfirmDialog.title",
2832
3072
  defaultMessage: "Confirmation"
@@ -2857,6 +3097,7 @@ const ConfirmDialogPublishAll = ({
2857
3097
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler(getTranslation);
2858
3098
  const { model, schema } = useDoc();
2859
3099
  const [{ query }] = strapiAdmin.useQueryParams();
3100
+ const enableDraftRelationsCount = false;
2860
3101
  const {
2861
3102
  data: countDraftRelations = 0,
2862
3103
  isLoading,
@@ -2868,7 +3109,7 @@ const ConfirmDialogPublishAll = ({
2868
3109
  locale: query?.plugins?.i18n?.locale
2869
3110
  },
2870
3111
  {
2871
- skip: selectedEntries.length === 0
3112
+ skip: !enableDraftRelationsCount
2872
3113
  }
2873
3114
  );
2874
3115
  React__namespace.useEffect(() => {
@@ -3053,7 +3294,7 @@ const SelectedEntriesTableContent = ({
3053
3294
  status: row.status
3054
3295
  }
3055
3296
  ) }),
3056
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
3297
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(
3057
3298
  designSystem.IconButton,
3058
3299
  {
3059
3300
  tag: reactRouterDom.Link,
@@ -3062,23 +3303,16 @@ const SelectedEntriesTableContent = ({
3062
3303
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3063
3304
  },
3064
3305
  state: { from: pathname },
3065
- label: formatMessage(
3066
- { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3067
- {
3068
- target: formatMessage(
3069
- {
3070
- id: "content-manager.components.ListViewHelperPluginTable.row-line",
3071
- defaultMessage: "item line {number}"
3072
- },
3073
- { number: index2 + 1 }
3074
- )
3075
- }
3076
- ),
3306
+ label: formatMessage({
3307
+ id: "content-manager.bulk-publish.edit",
3308
+ defaultMessage: "Edit"
3309
+ }),
3077
3310
  target: "_blank",
3078
3311
  marginLeft: "auto",
3079
- children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {})
3312
+ variant: "ghost",
3313
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, { width: "1.6rem", height: "1.6rem" })
3080
3314
  }
3081
- ) })
3315
+ ) }) })
3082
3316
  ] }, row.id)) })
3083
3317
  ] });
3084
3318
  };
@@ -3115,7 +3349,13 @@ const SelectedEntriesModalContent = ({
3115
3349
  );
3116
3350
  const { rows, validationErrors } = React__namespace.useMemo(() => {
3117
3351
  if (data.length > 0 && schema) {
3118
- const validate = createYupSchema(schema.attributes, components);
3352
+ const validate = createYupSchema(
3353
+ schema.attributes,
3354
+ components,
3355
+ // Since this is the "Publish" action, the validation
3356
+ // schema must enforce the rules for published entities
3357
+ { status: "published" }
3358
+ );
3119
3359
  const validationErrors2 = {};
3120
3360
  const rows2 = data.map((entry) => {
3121
3361
  try {
@@ -3465,7 +3705,7 @@ const TableActions = ({ document }) => {
3465
3705
  strapiAdmin.DescriptionComponentRenderer,
3466
3706
  {
3467
3707
  props,
3468
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3708
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3469
3709
  children: (actions2) => {
3470
3710
  const tableRowActions = actions2.filter((action) => {
3471
3711
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3576,7 +3816,7 @@ const CloneAction = ({ model, documentId }) => {
3576
3816
  }),
3577
3817
  content: /* @__PURE__ */ jsxRuntime.jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3578
3818
  footer: ({ onClose }) => {
3579
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
3819
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
3580
3820
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3581
3821
  id: "cancel",
3582
3822
  defaultMessage: "Cancel"
@@ -3617,8 +3857,7 @@ class ContentManagerPlugin {
3617
3857
  documentActions = [
3618
3858
  ...DEFAULT_ACTIONS,
3619
3859
  ...DEFAULT_TABLE_ROW_ACTIONS,
3620
- ...DEFAULT_HEADER_ACTIONS,
3621
- HistoryAction
3860
+ ...DEFAULT_HEADER_ACTIONS
3622
3861
  ];
3623
3862
  editViewSidePanels = [ActionsPanel];
3624
3863
  headerActions = [];
@@ -3707,6 +3946,62 @@ const getPrintableType = (value) => {
3707
3946
  }
3708
3947
  return nativeType;
3709
3948
  };
3949
+ const HistoryAction = ({ model, document }) => {
3950
+ const { formatMessage } = reactIntl.useIntl();
3951
+ const [{ query }] = strapiAdmin.useQueryParams();
3952
+ const navigate = reactRouterDom.useNavigate();
3953
+ const { trackUsage } = strapiAdmin.useTracking();
3954
+ const { pathname } = reactRouterDom.useLocation();
3955
+ const pluginsQueryParams = qs.stringify({ plugins: query.plugins }, { encode: false });
3956
+ if (!window.strapi.features.isEnabled("cms-content-history")) {
3957
+ return null;
3958
+ }
3959
+ const handleOnClick = () => {
3960
+ const destination = { pathname: "history", search: pluginsQueryParams };
3961
+ trackUsage("willNavigate", {
3962
+ from: pathname,
3963
+ to: `${pathname}/${destination.pathname}`
3964
+ });
3965
+ navigate(destination);
3966
+ };
3967
+ return {
3968
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ClockCounterClockwise, {}),
3969
+ label: formatMessage({
3970
+ id: "content-manager.history.document-action",
3971
+ defaultMessage: "Content History"
3972
+ }),
3973
+ onClick: handleOnClick,
3974
+ disabled: (
3975
+ /**
3976
+ * The user is creating a new document.
3977
+ * It hasn't been saved yet, so there's no history to go to
3978
+ */
3979
+ !document || /**
3980
+ * The document has been created but the current dimension has never been saved.
3981
+ * For example, the user is creating a new locale in an existing document,
3982
+ * so there's no history for the document in that locale
3983
+ */
3984
+ !document.id || /**
3985
+ * History is only available for content types created by the user.
3986
+ * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
3987
+ * which start with `admin::` or `plugin::`
3988
+ */
3989
+ !model.startsWith("api::")
3990
+ ),
3991
+ position: "header"
3992
+ };
3993
+ };
3994
+ HistoryAction.type = "history";
3995
+ const historyAdmin = {
3996
+ bootstrap(app) {
3997
+ const { addDocumentAction } = app.getPlugin("content-manager").apis;
3998
+ addDocumentAction((actions2) => {
3999
+ const indexOfDeleteAction = actions2.findIndex((action) => action.type === "delete");
4000
+ actions2.splice(indexOfDeleteAction, 0, HistoryAction);
4001
+ return actions2;
4002
+ });
4003
+ }
4004
+ };
3710
4005
  const initialState = {
3711
4006
  collectionTypeLinks: [],
3712
4007
  components: [],
@@ -3743,6 +4038,72 @@ const { setInitialData } = actions;
3743
4038
  const reducer = toolkit.combineReducers({
3744
4039
  app: reducer$1
3745
4040
  });
4041
+ const previewApi = contentManagerApi.injectEndpoints({
4042
+ endpoints: (builder) => ({
4043
+ getPreviewUrl: builder.query({
4044
+ query({ query, params }) {
4045
+ return {
4046
+ url: `/content-manager/preview/url/${params.contentType}`,
4047
+ method: "GET",
4048
+ config: {
4049
+ params: query
4050
+ }
4051
+ };
4052
+ }
4053
+ })
4054
+ })
4055
+ });
4056
+ const { useGetPreviewUrlQuery } = previewApi;
4057
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4058
+ const { formatMessage } = reactIntl.useIntl();
4059
+ const { trackUsage } = strapiAdmin.useTracking();
4060
+ const { pathname } = reactRouterDom.useLocation();
4061
+ const [{ query }] = strapiAdmin.useQueryParams();
4062
+ const { data, error } = useGetPreviewUrlQuery({
4063
+ params: {
4064
+ contentType: model
4065
+ },
4066
+ query: {
4067
+ documentId,
4068
+ locale: document?.locale,
4069
+ status: document?.status
4070
+ }
4071
+ });
4072
+ if (!data?.data?.url || error) {
4073
+ return null;
4074
+ }
4075
+ const trackNavigation = () => {
4076
+ const destinationPathname = pathname.replace(/\/$/, "") + "/preview";
4077
+ trackUsage("willNavigate", { from: pathname, to: destinationPathname });
4078
+ };
4079
+ return {
4080
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4081
+ content: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
4082
+ designSystem.Button,
4083
+ {
4084
+ variant: "tertiary",
4085
+ tag: reactRouterDom.Link,
4086
+ to: { pathname: "preview", search: qs.stringify(query, { encode: false }) },
4087
+ onClick: trackNavigation,
4088
+ flex: "auto",
4089
+ children: formatMessage({
4090
+ id: "content-manager.preview.panel.button",
4091
+ defaultMessage: "Open preview"
4092
+ })
4093
+ }
4094
+ ) })
4095
+ };
4096
+ };
4097
+ const FEATURE_ID = "preview";
4098
+ const previewAdmin = {
4099
+ bootstrap(app) {
4100
+ if (!window.strapi.future.isEnabled(FEATURE_ID)) {
4101
+ return;
4102
+ }
4103
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4104
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4105
+ }
4106
+ };
3746
4107
  const index = {
3747
4108
  register(app) {
3748
4109
  const cm = new ContentManagerPlugin();
@@ -3762,7 +4123,7 @@ const index = {
3762
4123
  app.router.addRoute({
3763
4124
  path: "content-manager/*",
3764
4125
  lazy: async () => {
3765
- const { Layout } = await Promise.resolve().then(() => require("./layout-DRuJUpas.js"));
4126
+ const { Layout } = await Promise.resolve().then(() => require("./layout-BEuNwv-F.js"));
3766
4127
  return {
3767
4128
  Component: Layout
3768
4129
  };
@@ -3771,10 +4132,18 @@ const index = {
3771
4132
  });
3772
4133
  app.registerPlugin(cm.config);
3773
4134
  },
4135
+ bootstrap(app) {
4136
+ if (typeof historyAdmin.bootstrap === "function") {
4137
+ historyAdmin.bootstrap(app);
4138
+ }
4139
+ if (typeof previewAdmin.bootstrap === "function") {
4140
+ previewAdmin.bootstrap(app);
4141
+ }
4142
+ },
3774
4143
  async registerTrads({ locales }) {
3775
4144
  const importedTrads = await Promise.all(
3776
4145
  locales.map((locale) => {
3777
- 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-fbKQxLGn.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 }) => {
4146
+ 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-Ic0kXjxB.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`).then(({ default: data }) => {
3778
4147
  return {
3779
4148
  data: prefixPluginTranslations(data, PLUGIN_ID),
3780
4149
  locale
@@ -3792,6 +4161,7 @@ const index = {
3792
4161
  };
3793
4162
  exports.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD;
3794
4163
  exports.BulkActionsRenderer = BulkActionsRenderer;
4164
+ exports.CLONE_PATH = CLONE_PATH;
3795
4165
  exports.COLLECTION_TYPES = COLLECTION_TYPES;
3796
4166
  exports.CREATOR_FIELDS = CREATOR_FIELDS;
3797
4167
  exports.DEFAULT_SETTINGS = DEFAULT_SETTINGS;
@@ -3819,6 +4189,7 @@ exports.getMainField = getMainField;
3819
4189
  exports.getTranslation = getTranslation;
3820
4190
  exports.index = index;
3821
4191
  exports.setInitialData = setInitialData;
4192
+ exports.useContentManagerContext = useContentManagerContext;
3822
4193
  exports.useContentTypeSchema = useContentTypeSchema;
3823
4194
  exports.useDoc = useDoc;
3824
4195
  exports.useDocLayout = useDocLayout;
@@ -3830,5 +4201,6 @@ exports.useGetAllContentTypeSettingsQuery = useGetAllContentTypeSettingsQuery;
3830
4201
  exports.useGetAllDocumentsQuery = useGetAllDocumentsQuery;
3831
4202
  exports.useGetContentTypeConfigurationQuery = useGetContentTypeConfigurationQuery;
3832
4203
  exports.useGetInitialDataQuery = useGetInitialDataQuery;
4204
+ exports.useGetPreviewUrlQuery = useGetPreviewUrlQuery;
3833
4205
  exports.useUpdateContentTypeConfigurationMutation = useUpdateContentTypeConfigurationMutation;
3834
- //# sourceMappingURL=index-Buwn78Rt.js.map
4206
+ //# sourceMappingURL=index-T-aWjbj2.js.map