@strapi/content-manager 0.0.0-experimental.745741d19e90275ca6f7c928ca19f9bb0fd9d933 → 0.0.0-experimental.76999222c105ee5da1bc2540e3b5e5f57747300c

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 (194) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-DHNM3YBz.mjs → ComponentConfigurationPage-BUOQFZ08.mjs} +4 -4
  2. package/dist/_chunks/{ComponentConfigurationPage-DHNM3YBz.mjs.map → ComponentConfigurationPage-BUOQFZ08.mjs.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-BvHtG7uH.js → ComponentConfigurationPage-WtZ2yaRP.js} +4 -4
  4. package/dist/_chunks/{ComponentConfigurationPage-BvHtG7uH.js.map → ComponentConfigurationPage-WtZ2yaRP.js.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-DOmfCEMo.js → EditConfigurationPage-BVrCP5lF.js} +4 -4
  6. package/dist/_chunks/{EditConfigurationPage-DOmfCEMo.js.map → EditConfigurationPage-BVrCP5lF.js.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-Cp6HAEzN.mjs → EditConfigurationPage-D7HkxcAN.mjs} +4 -4
  8. package/dist/_chunks/{EditConfigurationPage-Cp6HAEzN.mjs.map → EditConfigurationPage-D7HkxcAN.mjs.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-BqNpC6hO.js → EditViewPage-BKQ83NAk.js} +30 -9
  10. package/dist/_chunks/EditViewPage-BKQ83NAk.js.map +1 -0
  11. package/dist/_chunks/{EditViewPage-BtkEx339.mjs → EditViewPage-zKmMBER4.mjs} +30 -9
  12. package/dist/_chunks/EditViewPage-zKmMBER4.mjs.map +1 -0
  13. package/dist/_chunks/{Field-R5NbffTB.mjs → Field-BbrX_tUG.mjs} +249 -152
  14. package/dist/_chunks/Field-BbrX_tUG.mjs.map +1 -0
  15. package/dist/_chunks/{Field-lsPFnAmH.js → Field-BhN0lyyZ.js} +251 -154
  16. package/dist/_chunks/Field-BhN0lyyZ.js.map +1 -0
  17. package/dist/_chunks/{Form-BHmXSfyy.mjs → Form-B-E8l73g.mjs} +36 -17
  18. package/dist/_chunks/Form-B-E8l73g.mjs.map +1 -0
  19. package/dist/_chunks/{Form-CcGboku8.js → Form-CkbrtNZd.js} +36 -17
  20. package/dist/_chunks/Form-CkbrtNZd.js.map +1 -0
  21. package/dist/_chunks/{History-Bsud8jwh.js → History-B2Dg9q7H.js} +61 -54
  22. package/dist/_chunks/History-B2Dg9q7H.js.map +1 -0
  23. package/dist/_chunks/{History-ByUPL3T3.mjs → History-C72HQ0-i.mjs} +62 -55
  24. package/dist/_chunks/History-C72HQ0-i.mjs.map +1 -0
  25. package/dist/_chunks/{ListConfigurationPage-DiT463qx.js → ListConfigurationPage-BQCc3BnJ.js} +21 -9
  26. package/dist/_chunks/ListConfigurationPage-BQCc3BnJ.js.map +1 -0
  27. package/dist/_chunks/{ListConfigurationPage-Bm5HACXf.mjs → ListConfigurationPage-BalSo5dp.mjs} +21 -9
  28. package/dist/_chunks/ListConfigurationPage-BalSo5dp.mjs.map +1 -0
  29. package/dist/_chunks/{ListViewPage-CsrC9L_d.js → ListViewPage-Cu5dZKZe.js} +61 -41
  30. package/dist/_chunks/ListViewPage-Cu5dZKZe.js.map +1 -0
  31. package/dist/_chunks/{ListViewPage-JSyNAAYu.mjs → ListViewPage-Dfue5wQ2.mjs} +59 -39
  32. package/dist/_chunks/ListViewPage-Dfue5wQ2.mjs.map +1 -0
  33. package/dist/_chunks/{NoContentTypePage-CsrQUpBE.mjs → NoContentTypePage-BLC8M9U0.mjs} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-CsrQUpBE.mjs.map → NoContentTypePage-BLC8M9U0.mjs.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-Bsvng4II.js → NoContentTypePage-MGzn4JPu.js} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-Bsvng4II.js.map → NoContentTypePage-MGzn4JPu.js.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-DNmf_pj0.mjs → NoPermissionsPage-BpAoEQy_.mjs} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-DNmf_pj0.mjs.map → NoPermissionsPage-BpAoEQy_.mjs.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-CdHNJtEf.js → NoPermissionsPage-DJPwEpOD.js} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-CdHNJtEf.js.map → NoPermissionsPage-DJPwEpOD.js.map} +1 -1
  41. package/dist/_chunks/{Relations-u8-37jK0.mjs → Relations-BULOkyWN.mjs} +73 -37
  42. package/dist/_chunks/Relations-BULOkyWN.mjs.map +1 -0
  43. package/dist/_chunks/{Relations-CghaPv2D.js → Relations-DTiqnyGx.js} +72 -36
  44. package/dist/_chunks/Relations-DTiqnyGx.js.map +1 -0
  45. package/dist/_chunks/{en-fbKQxLGn.js → en-C-J4DGEe.js} +20 -16
  46. package/dist/_chunks/{en-fbKQxLGn.js.map → en-C-J4DGEe.js.map} +1 -1
  47. package/dist/_chunks/{en-Ux26r5pl.mjs → en-DPfZ6tPQ.mjs} +20 -16
  48. package/dist/_chunks/{en-Ux26r5pl.mjs.map → en-DPfZ6tPQ.mjs.map} +1 -1
  49. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  50. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  51. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  52. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  53. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  54. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  55. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  56. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  57. package/dist/_chunks/{index-BOZx6IMg.js → index-76eawJUd.js} +1034 -671
  58. package/dist/_chunks/index-76eawJUd.js.map +1 -0
  59. package/dist/_chunks/{index-CaE6NG4a.mjs → index-DW7xp_LG.mjs} +1052 -690
  60. package/dist/_chunks/index-DW7xp_LG.mjs.map +1 -0
  61. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  62. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  63. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  64. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  65. package/dist/_chunks/{layout-Ciz224q5.js → layout-CVz8WiDC.js} +22 -9
  66. package/dist/_chunks/layout-CVz8WiDC.js.map +1 -0
  67. package/dist/_chunks/{layout-Bx7svTbY.mjs → layout-DNfLIjbP.mjs} +23 -10
  68. package/dist/_chunks/layout-DNfLIjbP.mjs.map +1 -0
  69. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  70. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  71. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  72. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  73. package/dist/_chunks/{relations-CP8sB2YZ.js → relations-B6K4WRjW.js} +3 -7
  74. package/dist/_chunks/relations-B6K4WRjW.js.map +1 -0
  75. package/dist/_chunks/{relations-Cxc1cEv3.mjs → relations-ByHSIjSe.mjs} +3 -7
  76. package/dist/_chunks/relations-ByHSIjSe.mjs.map +1 -0
  77. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  78. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  79. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  80. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  81. package/dist/admin/index.js +2 -1
  82. package/dist/admin/index.js.map +1 -1
  83. package/dist/admin/index.mjs +5 -4
  84. package/dist/admin/src/exports.d.ts +1 -1
  85. package/dist/admin/src/history/index.d.ts +3 -0
  86. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  87. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  88. package/dist/admin/src/index.d.ts +1 -0
  89. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -0
  90. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  91. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  92. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  93. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  94. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  95. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  96. package/dist/admin/src/preview/constants.d.ts +1 -0
  97. package/dist/admin/src/preview/index.d.ts +4 -0
  98. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  99. package/dist/admin/src/services/api.d.ts +1 -1
  100. package/dist/admin/src/services/components.d.ts +2 -2
  101. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  102. package/dist/admin/src/services/documents.d.ts +19 -17
  103. package/dist/admin/src/services/init.d.ts +1 -1
  104. package/dist/admin/src/services/relations.d.ts +2 -2
  105. package/dist/admin/src/services/uid.d.ts +3 -3
  106. package/dist/admin/src/utils/validation.d.ts +4 -1
  107. package/dist/server/index.js +544 -261
  108. package/dist/server/index.js.map +1 -1
  109. package/dist/server/index.mjs +545 -262
  110. package/dist/server/index.mjs.map +1 -1
  111. package/dist/server/src/bootstrap.d.ts.map +1 -1
  112. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  113. package/dist/server/src/controllers/index.d.ts.map +1 -1
  114. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  115. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  116. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  117. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  118. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  119. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  120. package/dist/server/src/history/services/history.d.ts.map +1 -1
  121. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  122. package/dist/server/src/history/services/utils.d.ts +4 -4
  123. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  124. package/dist/server/src/index.d.ts +4 -4
  125. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  126. package/dist/server/src/preview/constants.d.ts +2 -0
  127. package/dist/server/src/preview/constants.d.ts.map +1 -0
  128. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  129. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  130. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  131. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  132. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  133. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  134. package/dist/server/src/preview/index.d.ts +4 -0
  135. package/dist/server/src/preview/index.d.ts.map +1 -0
  136. package/dist/server/src/preview/routes/index.d.ts +8 -0
  137. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  138. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  139. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  140. package/dist/server/src/preview/services/index.d.ts +15 -0
  141. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  142. package/dist/server/src/preview/services/preview-config.d.ts +30 -0
  143. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  144. package/dist/server/src/preview/services/preview.d.ts +12 -0
  145. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  146. package/dist/server/src/preview/utils.d.ts +18 -0
  147. package/dist/server/src/preview/utils.d.ts.map +1 -0
  148. package/dist/server/src/routes/index.d.ts.map +1 -1
  149. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  150. package/dist/server/src/services/document-metadata.d.ts +8 -8
  151. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  152. package/dist/server/src/services/index.d.ts +4 -4
  153. package/dist/server/src/services/index.d.ts.map +1 -1
  154. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  155. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  156. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  157. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  158. package/dist/server/src/utils/index.d.ts +2 -0
  159. package/dist/server/src/utils/index.d.ts.map +1 -1
  160. package/dist/shared/contracts/collection-types.d.ts +3 -1
  161. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  162. package/dist/shared/contracts/index.d.ts +1 -0
  163. package/dist/shared/contracts/index.d.ts.map +1 -1
  164. package/dist/shared/contracts/preview.d.ts +27 -0
  165. package/dist/shared/contracts/preview.d.ts.map +1 -0
  166. package/dist/shared/index.js +4 -0
  167. package/dist/shared/index.js.map +1 -1
  168. package/dist/shared/index.mjs +4 -0
  169. package/dist/shared/index.mjs.map +1 -1
  170. package/package.json +13 -13
  171. package/dist/_chunks/EditViewPage-BqNpC6hO.js.map +0 -1
  172. package/dist/_chunks/EditViewPage-BtkEx339.mjs.map +0 -1
  173. package/dist/_chunks/Field-R5NbffTB.mjs.map +0 -1
  174. package/dist/_chunks/Field-lsPFnAmH.js.map +0 -1
  175. package/dist/_chunks/Form-BHmXSfyy.mjs.map +0 -1
  176. package/dist/_chunks/Form-CcGboku8.js.map +0 -1
  177. package/dist/_chunks/History-Bsud8jwh.js.map +0 -1
  178. package/dist/_chunks/History-ByUPL3T3.mjs.map +0 -1
  179. package/dist/_chunks/ListConfigurationPage-Bm5HACXf.mjs.map +0 -1
  180. package/dist/_chunks/ListConfigurationPage-DiT463qx.js.map +0 -1
  181. package/dist/_chunks/ListViewPage-CsrC9L_d.js.map +0 -1
  182. package/dist/_chunks/ListViewPage-JSyNAAYu.mjs.map +0 -1
  183. package/dist/_chunks/Relations-CghaPv2D.js.map +0 -1
  184. package/dist/_chunks/Relations-u8-37jK0.mjs.map +0 -1
  185. package/dist/_chunks/index-BOZx6IMg.js.map +0 -1
  186. package/dist/_chunks/index-CaE6NG4a.mjs.map +0 -1
  187. package/dist/_chunks/layout-Bx7svTbY.mjs.map +0 -1
  188. package/dist/_chunks/layout-Ciz224q5.js.map +0 -1
  189. package/dist/_chunks/relations-CP8sB2YZ.js.map +0 -1
  190. package/dist/_chunks/relations-Cxc1cEv3.mjs.map +0 -1
  191. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  192. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  193. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  194. package/strapi-server.js +0 -3
@@ -2,15 +2,16 @@
2
2
  const Icons = require("@strapi/icons");
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const strapiAdmin = require("@strapi/admin/strapi-admin");
5
- const qs = require("qs");
6
- const reactIntl = require("react-intl");
7
- const reactRouterDom = require("react-router-dom");
8
5
  const React = require("react");
9
6
  const designSystem = require("@strapi/design-system");
10
- const styledComponents = require("styled-components");
7
+ const mapValues = require("lodash/fp/mapValues");
8
+ const reactIntl = require("react-intl");
9
+ const reactRouterDom = require("react-router-dom");
11
10
  const yup = require("yup");
12
11
  const pipe = require("lodash/fp/pipe");
13
12
  const dateFns = require("date-fns");
13
+ const styledComponents = require("styled-components");
14
+ const qs = require("qs");
14
15
  const toolkit = require("@reduxjs/toolkit");
15
16
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
16
17
  function _interopNamespace(e) {
@@ -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
  }),
@@ -308,7 +293,7 @@ const documentApi = contentManagerApi.injectEndpoints({
308
293
  */
309
294
  getAllDocuments: builder.query({
310
295
  query: ({ model, params }) => ({
311
- url: `/content-manager/collection-types/${model}`,
296
+ url: `/content-manager/collection-types/${model}${params ? `?${params}` : ""}`,
312
297
  method: "GET",
313
298
  config: {
314
299
  params
@@ -316,6 +301,7 @@ const documentApi = contentManagerApi.injectEndpoints({
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({
@@ -488,20 +492,39 @@ const buildValidParams = (query) => {
488
492
  const isBaseQueryError = (error) => {
489
493
  return error.name !== void 0;
490
494
  };
491
- const createYupSchema = (attributes = {}, components = {}) => {
495
+ const arrayValidator = (attribute, options) => ({
496
+ message: strapiAdmin.translatedErrors.required,
497
+ test(value) {
498
+ if (options.status === "draft") {
499
+ return true;
500
+ }
501
+ if (!attribute.required) {
502
+ return true;
503
+ }
504
+ if (!value) {
505
+ return false;
506
+ }
507
+ if (Array.isArray(value) && value.length === 0) {
508
+ return false;
509
+ }
510
+ return true;
511
+ }
512
+ });
513
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
492
514
  const createModelSchema = (attributes2) => yup__namespace.object().shape(
493
515
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
494
516
  if (DOCUMENT_META_FIELDS.includes(name)) {
495
517
  return acc;
496
518
  }
497
519
  const validations = [
520
+ addNullableValidation,
498
521
  addRequiredValidation,
499
522
  addMinLengthValidation,
500
523
  addMaxLengthValidation,
501
524
  addMinValidation,
502
525
  addMaxValidation,
503
526
  addRegexValidation
504
- ].map((fn) => fn(attribute));
527
+ ].map((fn) => fn(attribute, options));
505
528
  const transformSchema = pipe__default.default(...validations);
506
529
  switch (attribute.type) {
507
530
  case "component": {
@@ -511,12 +534,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
511
534
  ...acc,
512
535
  [name]: transformSchema(
513
536
  yup__namespace.array().of(createModelSchema(attributes3).nullable(false))
514
- )
537
+ ).test(arrayValidator(attribute, options))
515
538
  };
516
539
  } else {
517
540
  return {
518
541
  ...acc,
519
- [name]: transformSchema(createModelSchema(attributes3))
542
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
520
543
  };
521
544
  }
522
545
  }
@@ -538,7 +561,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
538
561
  }
539
562
  )
540
563
  )
541
- )
564
+ ).test(arrayValidator(attribute, options))
542
565
  };
543
566
  case "relation":
544
567
  return {
@@ -550,7 +573,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
550
573
  } else if (Array.isArray(value)) {
551
574
  return yup__namespace.array().of(
552
575
  yup__namespace.object().shape({
553
- id: yup__namespace.string().required()
576
+ id: yup__namespace.number().required()
554
577
  })
555
578
  );
556
579
  } else if (typeof value === "object") {
@@ -602,6 +625,14 @@ const createAttributeSchema = (attribute) => {
602
625
  if (!value || typeof value === "string" && value.length === 0) {
603
626
  return true;
604
627
  }
628
+ if (typeof value === "object") {
629
+ try {
630
+ JSON.stringify(value);
631
+ return true;
632
+ } catch (err) {
633
+ return false;
634
+ }
635
+ }
605
636
  try {
606
637
  JSON.parse(value);
607
638
  return true;
@@ -620,13 +651,7 @@ const createAttributeSchema = (attribute) => {
620
651
  return yup__namespace.mixed();
621
652
  }
622
653
  };
623
- const addRequiredValidation = (attribute) => (schema) => {
624
- if (attribute.required && attribute.type !== "relation") {
625
- return schema.required({
626
- id: strapiAdmin.translatedErrors.required.id,
627
- defaultMessage: "This field is required."
628
- });
629
- }
654
+ const nullableSchema = (schema) => {
630
655
  return schema?.nullable ? schema.nullable() : (
631
656
  // In some cases '.nullable' will not be available on the schema.
632
657
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -634,7 +659,22 @@ const addRequiredValidation = (attribute) => (schema) => {
634
659
  schema
635
660
  );
636
661
  };
637
- const addMinLengthValidation = (attribute) => (schema) => {
662
+ const addNullableValidation = () => (schema) => {
663
+ return nullableSchema(schema);
664
+ };
665
+ const addRequiredValidation = (attribute, options) => (schema) => {
666
+ if (options.status === "draft" || !attribute.required) {
667
+ return schema;
668
+ }
669
+ if (attribute.required && "required" in schema) {
670
+ return schema.required(strapiAdmin.translatedErrors.required);
671
+ }
672
+ return schema;
673
+ };
674
+ const addMinLengthValidation = (attribute, options) => (schema) => {
675
+ if (options.status === "draft") {
676
+ return schema;
677
+ }
638
678
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
639
679
  return schema.min(attribute.minLength, {
640
680
  ...strapiAdmin.translatedErrors.minLength,
@@ -656,10 +696,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
656
696
  }
657
697
  return schema;
658
698
  };
659
- const addMinValidation = (attribute) => (schema) => {
660
- if ("min" in attribute) {
699
+ const addMinValidation = (attribute, options) => (schema) => {
700
+ if (options.status === "draft") {
701
+ return schema;
702
+ }
703
+ if ("min" in attribute && "min" in schema) {
661
704
  const min = toInteger(attribute.min);
662
- if ("min" in schema && min) {
705
+ if (min) {
663
706
  return schema.min(min, {
664
707
  ...strapiAdmin.translatedErrors.min,
665
708
  values: {
@@ -777,19 +820,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
777
820
  }, {});
778
821
  return componentsByKey;
779
822
  };
780
- const useDocument = (args, opts) => {
823
+ const HOOKS = {
824
+ /**
825
+ * Hook that allows to mutate the displayed headers of the list view table
826
+ * @constant
827
+ * @type {string}
828
+ */
829
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
830
+ /**
831
+ * Hook that allows to mutate the CM's collection types links pre-set filters
832
+ * @constant
833
+ * @type {string}
834
+ */
835
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
836
+ /**
837
+ * Hook that allows to mutate the CM's edit view layout
838
+ * @constant
839
+ * @type {string}
840
+ */
841
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
842
+ /**
843
+ * Hook that allows to mutate the CM's single types links pre-set filters
844
+ * @constant
845
+ * @type {string}
846
+ */
847
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
848
+ };
849
+ const contentTypesApi = contentManagerApi.injectEndpoints({
850
+ endpoints: (builder) => ({
851
+ getContentTypeConfiguration: builder.query({
852
+ query: (uid) => ({
853
+ url: `/content-manager/content-types/${uid}/configuration`,
854
+ method: "GET"
855
+ }),
856
+ transformResponse: (response) => response.data,
857
+ providesTags: (_result, _error, uid) => [
858
+ { type: "ContentTypesConfiguration", id: uid },
859
+ { type: "ContentTypeSettings", id: "LIST" }
860
+ ]
861
+ }),
862
+ getAllContentTypeSettings: builder.query({
863
+ query: () => "/content-manager/content-types-settings",
864
+ transformResponse: (response) => response.data,
865
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
866
+ }),
867
+ updateContentTypeConfiguration: builder.mutation({
868
+ query: ({ uid, ...body }) => ({
869
+ url: `/content-manager/content-types/${uid}/configuration`,
870
+ method: "PUT",
871
+ data: body
872
+ }),
873
+ transformResponse: (response) => response.data,
874
+ invalidatesTags: (_result, _error, { uid }) => [
875
+ { type: "ContentTypesConfiguration", id: uid },
876
+ { type: "ContentTypeSettings", id: "LIST" },
877
+ // Is this necessary?
878
+ { type: "InitialData" }
879
+ ]
880
+ })
881
+ })
882
+ });
883
+ const {
884
+ useGetContentTypeConfigurationQuery,
885
+ useGetAllContentTypeSettingsQuery,
886
+ useUpdateContentTypeConfigurationMutation
887
+ } = contentTypesApi;
888
+ const checkIfAttributeIsDisplayable = (attribute) => {
889
+ const { type } = attribute;
890
+ if (type === "relation") {
891
+ return !attribute.relation.toLowerCase().includes("morph");
892
+ }
893
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
894
+ };
895
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
896
+ if (!mainFieldName) {
897
+ return void 0;
898
+ }
899
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
900
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
901
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
902
+ );
903
+ return {
904
+ name: mainFieldName,
905
+ type: mainFieldType ?? "string"
906
+ };
907
+ };
908
+ const DEFAULT_SETTINGS = {
909
+ bulkable: false,
910
+ filterable: false,
911
+ searchable: false,
912
+ pagination: false,
913
+ defaultSortBy: "",
914
+ defaultSortOrder: "asc",
915
+ mainField: "id",
916
+ pageSize: 10
917
+ };
918
+ const useDocumentLayout = (model) => {
919
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
920
+ const [{ query }] = strapiAdmin.useQueryParams();
921
+ const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
781
922
  const { toggleNotification } = strapiAdmin.useNotification();
782
923
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
924
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
783
925
  const {
784
- currentData: data,
785
- isLoading: isLoadingDocument,
786
- isFetching: isFetchingDocument,
787
- error
788
- } = useGetDocumentQuery(args, {
789
- ...opts,
790
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
791
- });
792
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
926
+ data,
927
+ isLoading: isLoadingConfigs,
928
+ error,
929
+ isFetching: isFetchingConfigs
930
+ } = useGetContentTypeConfigurationQuery(model);
931
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
793
932
  React__namespace.useEffect(() => {
794
933
  if (error) {
795
934
  toggleNotification({
@@ -797,45 +936,261 @@ const useDocument = (args, opts) => {
797
936
  message: formatAPIError(error)
798
937
  });
799
938
  }
800
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
801
- const validationSchema = React__namespace.useMemo(() => {
802
- if (!schema) {
803
- return null;
804
- }
805
- return createYupSchema(schema.attributes, components);
806
- }, [schema, components]);
807
- const validate = React__namespace.useCallback(
808
- (document) => {
809
- if (!validationSchema) {
810
- throw new Error(
811
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
812
- );
813
- }
814
- try {
815
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
816
- return null;
817
- } catch (error2) {
818
- if (error2 instanceof yup.ValidationError) {
819
- return strapiAdmin.getYupValidationErrors(error2);
820
- }
821
- throw error2;
822
- }
939
+ }, [error, formatAPIError, toggleNotification]);
940
+ const editLayout = React__namespace.useMemo(
941
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
942
+ layout: [],
943
+ components: {},
944
+ metadatas: {},
945
+ options: {},
946
+ settings: DEFAULT_SETTINGS
823
947
  },
824
- [validationSchema]
948
+ [data, isLoading, schemas, schema, components]
949
+ );
950
+ const listLayout = React__namespace.useMemo(() => {
951
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
952
+ layout: [],
953
+ metadatas: {},
954
+ options: {},
955
+ settings: DEFAULT_SETTINGS
956
+ };
957
+ }, [data, isLoading, schemas, schema, components]);
958
+ const { layout: edit } = React__namespace.useMemo(
959
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
960
+ layout: editLayout,
961
+ query
962
+ }),
963
+ [editLayout, query, runHookWaterfall]
825
964
  );
826
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
827
965
  return {
828
- components,
829
- document: data?.data,
830
- meta: data?.meta,
966
+ error,
831
967
  isLoading,
832
- schema,
833
- validate
968
+ edit,
969
+ list: listLayout
834
970
  };
835
971
  };
836
- const useDoc = () => {
837
- const { id, slug, collectionType, origin } = reactRouterDom.useParams();
838
- const [{ query }] = strapiAdmin.useQueryParams();
972
+ const useDocLayout = () => {
973
+ const { model } = useDoc();
974
+ return useDocumentLayout(model);
975
+ };
976
+ const formatEditLayout = (data, {
977
+ schemas,
978
+ schema,
979
+ components
980
+ }) => {
981
+ let currentPanelIndex = 0;
982
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
983
+ data.contentType.layouts.edit,
984
+ schema?.attributes,
985
+ data.contentType.metadatas,
986
+ { configurations: data.components, schemas: components },
987
+ schemas
988
+ ).reduce((panels, row) => {
989
+ if (row.some((field) => field.type === "dynamiczone")) {
990
+ panels.push([row]);
991
+ currentPanelIndex += 2;
992
+ } else {
993
+ if (!panels[currentPanelIndex]) {
994
+ panels.push([row]);
995
+ } else {
996
+ panels[currentPanelIndex].push(row);
997
+ }
998
+ }
999
+ return panels;
1000
+ }, []);
1001
+ const componentEditAttributes = Object.entries(data.components).reduce(
1002
+ (acc, [uid, configuration]) => {
1003
+ acc[uid] = {
1004
+ layout: convertEditLayoutToFieldLayouts(
1005
+ configuration.layouts.edit,
1006
+ components[uid].attributes,
1007
+ configuration.metadatas,
1008
+ { configurations: data.components, schemas: components }
1009
+ ),
1010
+ settings: {
1011
+ ...configuration.settings,
1012
+ icon: components[uid].info.icon,
1013
+ displayName: components[uid].info.displayName
1014
+ }
1015
+ };
1016
+ return acc;
1017
+ },
1018
+ {}
1019
+ );
1020
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1021
+ (acc, [attribute, metadata]) => {
1022
+ return {
1023
+ ...acc,
1024
+ [attribute]: metadata.edit
1025
+ };
1026
+ },
1027
+ {}
1028
+ );
1029
+ return {
1030
+ layout: panelledEditAttributes,
1031
+ components: componentEditAttributes,
1032
+ metadatas: editMetadatas,
1033
+ settings: {
1034
+ ...data.contentType.settings,
1035
+ displayName: schema?.info.displayName
1036
+ },
1037
+ options: {
1038
+ ...schema?.options,
1039
+ ...schema?.pluginOptions,
1040
+ ...data.contentType.options
1041
+ }
1042
+ };
1043
+ };
1044
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1045
+ return rows.map(
1046
+ (row) => row.map((field) => {
1047
+ const attribute = attributes[field.name];
1048
+ if (!attribute) {
1049
+ return null;
1050
+ }
1051
+ const { edit: metadata } = metadatas[field.name];
1052
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1053
+ return {
1054
+ attribute,
1055
+ disabled: !metadata.editable,
1056
+ hint: metadata.description,
1057
+ label: metadata.label ?? "",
1058
+ name: field.name,
1059
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1060
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1061
+ schemas,
1062
+ components: components?.schemas ?? {}
1063
+ }),
1064
+ placeholder: metadata.placeholder ?? "",
1065
+ required: attribute.required ?? false,
1066
+ size: field.size,
1067
+ unique: "unique" in attribute ? attribute.unique : false,
1068
+ visible: metadata.visible ?? true,
1069
+ type: attribute.type
1070
+ };
1071
+ }).filter((field) => field !== null)
1072
+ );
1073
+ };
1074
+ const formatListLayout = (data, {
1075
+ schemas,
1076
+ schema,
1077
+ components
1078
+ }) => {
1079
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1080
+ (acc, [attribute, metadata]) => {
1081
+ return {
1082
+ ...acc,
1083
+ [attribute]: metadata.list
1084
+ };
1085
+ },
1086
+ {}
1087
+ );
1088
+ const listAttributes = convertListLayoutToFieldLayouts(
1089
+ data.contentType.layouts.list,
1090
+ schema?.attributes,
1091
+ listMetadatas,
1092
+ { configurations: data.components, schemas: components },
1093
+ schemas
1094
+ );
1095
+ return {
1096
+ layout: listAttributes,
1097
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1098
+ metadatas: listMetadatas,
1099
+ options: {
1100
+ ...schema?.options,
1101
+ ...schema?.pluginOptions,
1102
+ ...data.contentType.options
1103
+ }
1104
+ };
1105
+ };
1106
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1107
+ return columns.map((name) => {
1108
+ const attribute = attributes[name];
1109
+ if (!attribute) {
1110
+ return null;
1111
+ }
1112
+ const metadata = metadatas[name];
1113
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1114
+ return {
1115
+ attribute,
1116
+ label: metadata.label ?? "",
1117
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1118
+ schemas,
1119
+ components: components?.schemas ?? {}
1120
+ }),
1121
+ name,
1122
+ searchable: metadata.searchable ?? true,
1123
+ sortable: metadata.sortable ?? true
1124
+ };
1125
+ }).filter((field) => field !== null);
1126
+ };
1127
+ const useDocument = (args, opts) => {
1128
+ const { toggleNotification } = strapiAdmin.useNotification();
1129
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1130
+ const {
1131
+ currentData: data,
1132
+ isLoading: isLoadingDocument,
1133
+ isFetching: isFetchingDocument,
1134
+ error
1135
+ } = useGetDocumentQuery(args, {
1136
+ ...opts,
1137
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1138
+ });
1139
+ const {
1140
+ components,
1141
+ schema,
1142
+ schemas,
1143
+ isLoading: isLoadingSchema
1144
+ } = useContentTypeSchema(args.model);
1145
+ React__namespace.useEffect(() => {
1146
+ if (error) {
1147
+ toggleNotification({
1148
+ type: "danger",
1149
+ message: formatAPIError(error)
1150
+ });
1151
+ }
1152
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1153
+ const validationSchema = React__namespace.useMemo(() => {
1154
+ if (!schema) {
1155
+ return null;
1156
+ }
1157
+ return createYupSchema(schema.attributes, components);
1158
+ }, [schema, components]);
1159
+ const validate = React__namespace.useCallback(
1160
+ (document) => {
1161
+ if (!validationSchema) {
1162
+ throw new Error(
1163
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1164
+ );
1165
+ }
1166
+ try {
1167
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1168
+ return null;
1169
+ } catch (error2) {
1170
+ if (error2 instanceof yup.ValidationError) {
1171
+ return strapiAdmin.getYupValidationErrors(error2);
1172
+ }
1173
+ throw error2;
1174
+ }
1175
+ },
1176
+ [validationSchema]
1177
+ );
1178
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1179
+ const hasError = !!error;
1180
+ return {
1181
+ components,
1182
+ document: data?.data,
1183
+ meta: data?.meta,
1184
+ isLoading,
1185
+ hasError,
1186
+ schema,
1187
+ schemas,
1188
+ validate
1189
+ };
1190
+ };
1191
+ const useDoc = () => {
1192
+ const { id, slug, collectionType, origin } = reactRouterDom.useParams();
1193
+ const [{ query }] = strapiAdmin.useQueryParams();
839
1194
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
840
1195
  if (!collectionType) {
841
1196
  throw new Error("Could not find collectionType in url params");
@@ -843,22 +1198,60 @@ const useDoc = () => {
843
1198
  if (!slug) {
844
1199
  throw new Error("Could not find model in url params");
845
1200
  }
1201
+ const document = useDocument(
1202
+ { documentId: origin || id, model: slug, collectionType, params },
1203
+ {
1204
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1205
+ }
1206
+ );
1207
+ const returnId = origin || id === "create" ? void 0 : id;
846
1208
  return {
847
1209
  collectionType,
848
1210
  model: slug,
849
- id: origin || id === "create" ? void 0 : id,
850
- ...useDocument(
851
- { documentId: origin || id, model: slug, collectionType, params },
852
- {
853
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
854
- }
855
- )
1211
+ id: returnId,
1212
+ ...document
1213
+ };
1214
+ };
1215
+ const useContentManagerContext = () => {
1216
+ const {
1217
+ collectionType,
1218
+ model,
1219
+ id,
1220
+ components,
1221
+ isLoading: isLoadingDoc,
1222
+ schema,
1223
+ schemas
1224
+ } = useDoc();
1225
+ const layout = useDocumentLayout(model);
1226
+ const form = strapiAdmin.useForm("useContentManagerContext", (state) => state);
1227
+ const isSingleType = collectionType === SINGLE_TYPES;
1228
+ const slug = model;
1229
+ const isCreatingEntry = id === "create";
1230
+ useContentTypeSchema();
1231
+ const isLoading = isLoadingDoc || layout.isLoading;
1232
+ const error = layout.error;
1233
+ return {
1234
+ error,
1235
+ isLoading,
1236
+ // Base metadata
1237
+ model,
1238
+ collectionType,
1239
+ id,
1240
+ slug,
1241
+ isCreatingEntry,
1242
+ isSingleType,
1243
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1244
+ // All schema infos
1245
+ components,
1246
+ contentType: schema,
1247
+ contentTypes: schemas,
1248
+ // Form state
1249
+ form,
1250
+ // layout infos
1251
+ layout
856
1252
  };
857
1253
  };
858
1254
  const prefixPluginTranslations = (trad, pluginId) => {
859
- if (!pluginId) {
860
- throw new TypeError("pluginId can't be empty");
861
- }
862
1255
  return Object.keys(trad).reduce((acc, current) => {
863
1256
  acc[`${pluginId}.${current}`] = trad[current];
864
1257
  return acc;
@@ -874,6 +1267,8 @@ const useDocumentActions = () => {
874
1267
  const { formatMessage } = reactIntl.useIntl();
875
1268
  const { trackUsage } = strapiAdmin.useTracking();
876
1269
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1270
+ const navigate = reactRouterDom.useNavigate();
1271
+ const setCurrentStep = strapiAdmin.useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
877
1272
  const [deleteDocument] = useDeleteDocumentMutation();
878
1273
  const _delete = React__namespace.useCallback(
879
1274
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1188,6 +1583,7 @@ const useDocumentActions = () => {
1188
1583
  defaultMessage: "Saved document"
1189
1584
  })
1190
1585
  });
1586
+ setCurrentStep("contentManager.success");
1191
1587
  return res.data;
1192
1588
  } catch (err) {
1193
1589
  toggleNotification({
@@ -1209,7 +1605,6 @@ const useDocumentActions = () => {
1209
1605
  sourceId
1210
1606
  });
1211
1607
  if ("error" in res) {
1212
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1213
1608
  return { error: res.error };
1214
1609
  }
1215
1610
  toggleNotification({
@@ -1228,7 +1623,7 @@ const useDocumentActions = () => {
1228
1623
  throw err;
1229
1624
  }
1230
1625
  },
1231
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1626
+ [autoCloneDocument, formatMessage, toggleNotification]
1232
1627
  );
1233
1628
  const [cloneDocument] = useCloneDocumentMutation();
1234
1629
  const clone = React__namespace.useCallback(
@@ -1254,6 +1649,7 @@ const useDocumentActions = () => {
1254
1649
  defaultMessage: "Cloned document"
1255
1650
  })
1256
1651
  });
1652
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1257
1653
  return res.data;
1258
1654
  } catch (err) {
1259
1655
  toggleNotification({
@@ -1264,7 +1660,7 @@ const useDocumentActions = () => {
1264
1660
  throw err;
1265
1661
  }
1266
1662
  },
1267
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1663
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1268
1664
  );
1269
1665
  const [getDoc] = useLazyGetDocumentQuery();
1270
1666
  const getDocument = React__namespace.useCallback(
@@ -1290,7 +1686,7 @@ const useDocumentActions = () => {
1290
1686
  };
1291
1687
  };
1292
1688
  const ProtectedHistoryPage = React.lazy(
1293
- () => Promise.resolve().then(() => require("./History-Bsud8jwh.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1689
+ () => Promise.resolve().then(() => require("./History-B2Dg9q7H.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1294
1690
  );
1295
1691
  const routes$1 = [
1296
1692
  {
@@ -1303,31 +1699,31 @@ const routes$1 = [
1303
1699
  }
1304
1700
  ];
1305
1701
  const ProtectedEditViewPage = React.lazy(
1306
- () => Promise.resolve().then(() => require("./EditViewPage-BqNpC6hO.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1702
+ () => Promise.resolve().then(() => require("./EditViewPage-BKQ83NAk.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1307
1703
  );
1308
1704
  const ProtectedListViewPage = React.lazy(
1309
- () => Promise.resolve().then(() => require("./ListViewPage-CsrC9L_d.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1705
+ () => Promise.resolve().then(() => require("./ListViewPage-Cu5dZKZe.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1310
1706
  );
1311
1707
  const ProtectedListConfiguration = React.lazy(
1312
- () => Promise.resolve().then(() => require("./ListConfigurationPage-DiT463qx.js")).then((mod) => ({
1708
+ () => Promise.resolve().then(() => require("./ListConfigurationPage-BQCc3BnJ.js")).then((mod) => ({
1313
1709
  default: mod.ProtectedListConfiguration
1314
1710
  }))
1315
1711
  );
1316
1712
  const ProtectedEditConfigurationPage = React.lazy(
1317
- () => Promise.resolve().then(() => require("./EditConfigurationPage-DOmfCEMo.js")).then((mod) => ({
1713
+ () => Promise.resolve().then(() => require("./EditConfigurationPage-BVrCP5lF.js")).then((mod) => ({
1318
1714
  default: mod.ProtectedEditConfigurationPage
1319
1715
  }))
1320
1716
  );
1321
1717
  const ProtectedComponentConfigurationPage = React.lazy(
1322
- () => Promise.resolve().then(() => require("./ComponentConfigurationPage-BvHtG7uH.js")).then((mod) => ({
1718
+ () => Promise.resolve().then(() => require("./ComponentConfigurationPage-WtZ2yaRP.js")).then((mod) => ({
1323
1719
  default: mod.ProtectedComponentConfigurationPage
1324
1720
  }))
1325
1721
  );
1326
1722
  const NoPermissions = React.lazy(
1327
- () => Promise.resolve().then(() => require("./NoPermissionsPage-CdHNJtEf.js")).then((mod) => ({ default: mod.NoPermissions }))
1723
+ () => Promise.resolve().then(() => require("./NoPermissionsPage-DJPwEpOD.js")).then((mod) => ({ default: mod.NoPermissions }))
1328
1724
  );
1329
1725
  const NoContentType = React.lazy(
1330
- () => Promise.resolve().then(() => require("./NoContentTypePage-Bsvng4II.js")).then((mod) => ({ default: mod.NoContentType }))
1726
+ () => Promise.resolve().then(() => require("./NoContentTypePage-MGzn4JPu.js")).then((mod) => ({ default: mod.NoContentType }))
1331
1727
  );
1332
1728
  const CollectionTypePages = () => {
1333
1729
  const { collectionType } = reactRouterDom.useParams();
@@ -1441,12 +1837,14 @@ const DocumentActionButton = (action) => {
1441
1837
  /* @__PURE__ */ jsxRuntime.jsx(
1442
1838
  designSystem.Button,
1443
1839
  {
1444
- flex: 1,
1840
+ flex: "auto",
1445
1841
  startIcon: action.icon,
1446
1842
  disabled: action.disabled,
1447
1843
  onClick: handleClick(action),
1448
1844
  justifyContent: "center",
1449
1845
  variant: action.variant || "default",
1846
+ paddingTop: "7px",
1847
+ paddingBottom: "7px",
1450
1848
  children: action.label
1451
1849
  }
1452
1850
  ),
@@ -1454,7 +1852,7 @@ const DocumentActionButton = (action) => {
1454
1852
  DocumentActionConfirmDialog,
1455
1853
  {
1456
1854
  ...action.dialog,
1457
- variant: action.variant,
1855
+ variant: action.dialog?.variant ?? action.variant,
1458
1856
  isOpen: dialogId === action.id,
1459
1857
  onClose: handleClose
1460
1858
  }
@@ -1511,9 +1909,9 @@ const DocumentActionsMenu = ({
1511
1909
  disabled: isDisabled,
1512
1910
  size: "S",
1513
1911
  endIcon: null,
1514
- paddingTop: "7px",
1515
- paddingLeft: "9px",
1516
- paddingRight: "9px",
1912
+ paddingTop: "4px",
1913
+ paddingLeft: "7px",
1914
+ paddingRight: "7px",
1517
1915
  variant,
1518
1916
  children: [
1519
1917
  /* @__PURE__ */ jsxRuntime.jsx(Icons.More, { "aria-hidden": true, focusable: false }),
@@ -1524,7 +1922,7 @@ const DocumentActionsMenu = ({
1524
1922
  ]
1525
1923
  }
1526
1924
  ),
1527
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1925
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1528
1926
  actions2.map((action) => {
1529
1927
  return /* @__PURE__ */ jsxRuntime.jsx(
1530
1928
  designSystem.Menu.Item,
@@ -1533,10 +1931,25 @@ const DocumentActionsMenu = ({
1533
1931
  onSelect: handleClick(action),
1534
1932
  display: "block",
1535
1933
  children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", gap: 4, children: [
1536
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1537
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1538
- action.label
1539
- ] }),
1934
+ /* @__PURE__ */ jsxRuntime.jsxs(
1935
+ designSystem.Flex,
1936
+ {
1937
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1938
+ gap: 2,
1939
+ tag: "span",
1940
+ children: [
1941
+ /* @__PURE__ */ jsxRuntime.jsx(
1942
+ designSystem.Flex,
1943
+ {
1944
+ tag: "span",
1945
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1946
+ children: action.icon
1947
+ }
1948
+ ),
1949
+ action.label
1950
+ ]
1951
+ }
1952
+ ),
1540
1953
  action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsxRuntime.jsx(
1541
1954
  designSystem.Flex,
1542
1955
  {
@@ -1633,11 +2046,11 @@ const DocumentActionConfirmDialog = ({
1633
2046
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
1634
2047
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: content }),
1635
2048
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
1636
- /* @__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({
1637
2050
  id: "app.components.Button.cancel",
1638
2051
  defaultMessage: "Cancel"
1639
2052
  }) }) }),
1640
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, children: formatMessage({
2053
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1641
2054
  id: "app.components.Button.confirm",
1642
2055
  defaultMessage: "Confirm"
1643
2056
  }) })
@@ -1660,10 +2073,22 @@ const DocumentActionModal = ({
1660
2073
  };
1661
2074
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
1662
2075
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: title }) }),
1663
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content }),
1664
- /* @__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
1665
2078
  ] }) });
1666
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
+ };
1667
2092
  const PublishAction$1 = ({
1668
2093
  activeTab,
1669
2094
  documentId,
@@ -1676,13 +2101,17 @@ const PublishAction$1 = ({
1676
2101
  const navigate = reactRouterDom.useNavigate();
1677
2102
  const { toggleNotification } = strapiAdmin.useNotification();
1678
2103
  const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
2104
+ const isListView = reactRouterDom.useMatch(LIST_PATH) !== null;
1679
2105
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
1680
2106
  const { formatMessage } = reactIntl.useIntl();
1681
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1682
- "PublishAction",
1683
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1684
- );
2107
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1685
2108
  const { publish } = useDocumentActions();
2109
+ const [
2110
+ countDraftRelations,
2111
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2112
+ ] = useLazyGetDraftRelationCountQuery();
2113
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React__namespace.useState(0);
2114
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React__namespace.useState(0);
1686
2115
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1687
2116
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1688
2117
  const modified = strapiAdmin.useForm("PublishAction", ({ modified: modified2 }) => modified2);
@@ -1691,62 +2120,144 @@ const PublishAction$1 = ({
1691
2120
  const validate = strapiAdmin.useForm("PublishAction", (state) => state.validate);
1692
2121
  const setErrors = strapiAdmin.useForm("PublishAction", (state) => state.setErrors);
1693
2122
  const formValues = strapiAdmin.useForm("PublishAction", ({ values }) => values);
1694
- const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1695
- if (!schema?.options?.draftAndPublish) {
1696
- return null;
1697
- }
1698
- return {
1699
- /**
1700
- * Disabled when:
1701
- * - currently if you're cloning a document we don't support publish & clone at the same time.
1702
- * - the form is submitting
1703
- * - the active tab is the published tab
2123
+ React__namespace.useEffect(() => {
2124
+ if (isErrorDraftRelations) {
2125
+ toggleNotification({
2126
+ type: "danger",
2127
+ message: formatMessage({
2128
+ id: getTranslation("error.records.fetch-draft-relatons"),
2129
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2130
+ })
2131
+ });
2132
+ }
2133
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2134
+ React__namespace.useEffect(() => {
2135
+ const localDraftRelations = /* @__PURE__ */ new Set();
2136
+ const extractDraftRelations = (data) => {
2137
+ const relations = data.connect || [];
2138
+ relations.forEach((relation) => {
2139
+ if (relation.status === "draft") {
2140
+ localDraftRelations.add(relation.id);
2141
+ }
2142
+ });
2143
+ };
2144
+ const traverseAndExtract = (data) => {
2145
+ Object.entries(data).forEach(([key, value]) => {
2146
+ if (key === "connect" && Array.isArray(value)) {
2147
+ extractDraftRelations({ connect: value });
2148
+ } else if (typeof value === "object" && value !== null) {
2149
+ traverseAndExtract(value);
2150
+ }
2151
+ });
2152
+ };
2153
+ if (!documentId || modified) {
2154
+ traverseAndExtract(formValues);
2155
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2156
+ }
2157
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2158
+ React__namespace.useEffect(() => {
2159
+ if (!document || !document.documentId || isListView) {
2160
+ return;
2161
+ }
2162
+ const fetchDraftRelationsCount = async () => {
2163
+ const { data, error } = await countDraftRelations({
2164
+ collectionType,
2165
+ model,
2166
+ documentId,
2167
+ params
2168
+ });
2169
+ if (error) {
2170
+ throw error;
2171
+ }
2172
+ if (data) {
2173
+ setServerCountOfDraftRelations(data.data);
2174
+ }
2175
+ };
2176
+ fetchDraftRelationsCount();
2177
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
2178
+ const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
2179
+ if (!schema?.options?.draftAndPublish) {
2180
+ return null;
2181
+ }
2182
+ const performPublish = async () => {
2183
+ setSubmitting(true);
2184
+ try {
2185
+ const { errors } = await validate(true, {
2186
+ status: "published"
2187
+ });
2188
+ if (errors) {
2189
+ toggleNotification({
2190
+ type: "danger",
2191
+ message: formatMessage({
2192
+ id: "content-manager.validation.error",
2193
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2194
+ })
2195
+ });
2196
+ return;
2197
+ }
2198
+ const res = await publish(
2199
+ {
2200
+ collectionType,
2201
+ model,
2202
+ documentId,
2203
+ params
2204
+ },
2205
+ transformData(formValues)
2206
+ );
2207
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2208
+ navigate({
2209
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2210
+ search: rawQuery
2211
+ });
2212
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2213
+ setErrors(formatValidationErrors(res.error));
2214
+ }
2215
+ } finally {
2216
+ setSubmitting(false);
2217
+ }
2218
+ };
2219
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2220
+ const enableDraftRelationsCount = false;
2221
+ const hasDraftRelations = enableDraftRelationsCount;
2222
+ return {
2223
+ /**
2224
+ * Disabled when:
2225
+ * - currently if you're cloning a document we don't support publish & clone at the same time.
2226
+ * - the form is submitting
2227
+ * - the active tab is the published tab
1704
2228
  * - the document is already published & not modified
1705
2229
  * - the document is being created & not modified
1706
2230
  * - the user doesn't have the permission to publish
1707
- * - the user doesn't have the permission to create a new document
1708
- * - the user doesn't have the permission to update the document
1709
2231
  */
1710
- disabled: isCloning || isSubmitting || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2232
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1711
2233
  label: formatMessage({
1712
2234
  id: "app.utils.publish",
1713
2235
  defaultMessage: "Publish"
1714
2236
  }),
1715
2237
  onClick: async () => {
1716
- setSubmitting(true);
1717
- try {
1718
- const { errors } = await validate();
1719
- if (errors) {
1720
- toggleNotification({
1721
- type: "danger",
1722
- message: formatMessage({
1723
- id: "content-manager.validation.error",
1724
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1725
- })
1726
- });
1727
- return;
1728
- }
1729
- const res = await publish(
1730
- {
1731
- collectionType,
1732
- model,
1733
- documentId,
1734
- params
1735
- },
1736
- formValues
1737
- );
1738
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1739
- navigate({
1740
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1741
- search: rawQuery
1742
- });
1743
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1744
- setErrors(formatValidationErrors(res.error));
2238
+ await performPublish();
2239
+ },
2240
+ dialog: hasDraftRelations ? {
2241
+ type: "dialog",
2242
+ variant: "danger",
2243
+ footer: null,
2244
+ title: formatMessage({
2245
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2246
+ defaultMessage: "Confirmation"
2247
+ }),
2248
+ content: formatMessage(
2249
+ {
2250
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2251
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2252
+ },
2253
+ {
2254
+ count: totalDraftRelations
1745
2255
  }
1746
- } finally {
1747
- setSubmitting(false);
2256
+ ),
2257
+ onConfirm: async () => {
2258
+ await performPublish();
1748
2259
  }
1749
- }
2260
+ } : void 0
1750
2261
  };
1751
2262
  };
1752
2263
  PublishAction$1.type = "publish";
@@ -1762,10 +2273,6 @@ const UpdateAction = ({
1762
2273
  const cloneMatch = reactRouterDom.useMatch(CLONE_PATH);
1763
2274
  const isCloning = cloneMatch !== null;
1764
2275
  const { formatMessage } = reactIntl.useIntl();
1765
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1766
- canCreate: canCreate2,
1767
- canUpdate: canUpdate2
1768
- }));
1769
2276
  const { create, update, clone } = useDocumentActions();
1770
2277
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1771
2278
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
@@ -1782,10 +2289,8 @@ const UpdateAction = ({
1782
2289
  * - the form is submitting
1783
2290
  * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1784
2291
  * - the active tab is the published tab
1785
- * - the user doesn't have the permission to create a new document
1786
- * - the user doesn't have the permission to update the document
1787
2292
  */
1788
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
2293
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1789
2294
  label: formatMessage({
1790
2295
  id: "content-manager.containers.Edit.save",
1791
2296
  defaultMessage: "Save"
@@ -1793,7 +2298,9 @@ const UpdateAction = ({
1793
2298
  onClick: async () => {
1794
2299
  setSubmitting(true);
1795
2300
  try {
1796
- const { errors } = await validate();
2301
+ const { errors } = await validate(true, {
2302
+ status: "draft"
2303
+ });
1797
2304
  if (errors) {
1798
2305
  toggleNotification({
1799
2306
  type: "danger",
@@ -1811,13 +2318,16 @@ const UpdateAction = ({
1811
2318
  documentId: cloneMatch.params.origin,
1812
2319
  params
1813
2320
  },
1814
- document
2321
+ transformData(document)
1815
2322
  );
1816
2323
  if ("data" in res) {
1817
- navigate({
1818
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1819
- search: rawQuery
1820
- });
2324
+ navigate(
2325
+ {
2326
+ pathname: `../${res.data.documentId}`,
2327
+ search: rawQuery
2328
+ },
2329
+ { relative: "path" }
2330
+ );
1821
2331
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1822
2332
  setErrors(formatValidationErrors(res.error));
1823
2333
  }
@@ -1829,7 +2339,7 @@ const UpdateAction = ({
1829
2339
  documentId,
1830
2340
  params
1831
2341
  },
1832
- document
2342
+ transformData(document)
1833
2343
  );
1834
2344
  if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1835
2345
  setErrors(formatValidationErrors(res.error));
@@ -1842,15 +2352,15 @@ const UpdateAction = ({
1842
2352
  model,
1843
2353
  params
1844
2354
  },
1845
- document
2355
+ transformData(document)
1846
2356
  );
1847
2357
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1848
2358
  navigate(
1849
2359
  {
1850
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2360
+ pathname: `../${res.data.documentId}`,
1851
2361
  search: rawQuery
1852
2362
  },
1853
- { replace: true }
2363
+ { replace: true, relative: "path" }
1854
2364
  );
1855
2365
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1856
2366
  setErrors(formatValidationErrors(res.error));
@@ -1895,7 +2405,7 @@ const UnpublishAction$1 = ({
1895
2405
  id: "app.utils.unpublish",
1896
2406
  defaultMessage: "Unpublish"
1897
2407
  }),
1898
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2408
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
1899
2409
  onClick: async () => {
1900
2410
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1901
2411
  if (!documentId) {
@@ -2007,7 +2517,7 @@ const DiscardAction = ({
2007
2517
  id: "content-manager.actions.discard.label",
2008
2518
  defaultMessage: "Discard changes"
2009
2519
  }),
2010
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2520
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
2011
2521
  position: ["panel", "table-row"],
2012
2522
  variant: "danger",
2013
2523
  dialog: {
@@ -2035,11 +2545,6 @@ const DiscardAction = ({
2035
2545
  };
2036
2546
  };
2037
2547
  DiscardAction.type = "discard";
2038
- const StyledCrossCircle = styledComponents.styled(Icons.CrossCircle)`
2039
- path {
2040
- fill: currentColor;
2041
- }
2042
- `;
2043
2548
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2044
2549
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2045
2550
  const RelativeTime = React__namespace.forwardRef(
@@ -2087,7 +2592,7 @@ const getDisplayName = ({
2087
2592
  };
2088
2593
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2089
2594
  const DocumentStatus = ({ status = "draft", ...restProps }) => {
2090
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2595
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2091
2596
  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) }) });
2092
2597
  };
2093
2598
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
@@ -2097,23 +2602,13 @@ const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2097
2602
  id: "content-manager.containers.edit.title.new",
2098
2603
  defaultMessage: "Create an entry"
2099
2604
  }) : documentTitle;
2100
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 8, paddingBottom: 4, gap: 3, children: [
2605
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2101
2606
  /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.BackButton, {}),
2102
- /* @__PURE__ */ jsxRuntime.jsxs(
2103
- designSystem.Flex,
2104
- {
2105
- width: "100%",
2106
- justifyContent: "space-between",
2107
- paddingTop: 1,
2108
- gap: "80px",
2109
- alignItems: "flex-start",
2110
- children: [
2111
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2112
- /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2113
- ]
2114
- }
2115
- ),
2116
- status ? /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) : null
2607
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2608
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2609
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2610
+ ] }),
2611
+ status ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2117
2612
  ] });
2118
2613
  };
2119
2614
  const HeaderToolbar = () => {
@@ -2196,12 +2691,12 @@ const Information = ({ activeTab }) => {
2196
2691
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2197
2692
  label: formatMessage({
2198
2693
  id: "content-manager.containers.edit.information.last-published.label",
2199
- defaultMessage: "Last published"
2694
+ defaultMessage: "Published"
2200
2695
  }),
2201
2696
  value: formatMessage(
2202
2697
  {
2203
2698
  id: "content-manager.containers.edit.information.last-published.value",
2204
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2699
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2205
2700
  },
2206
2701
  {
2207
2702
  time: /* @__PURE__ */ jsxRuntime.jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2214,12 +2709,12 @@ const Information = ({ activeTab }) => {
2214
2709
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2215
2710
  label: formatMessage({
2216
2711
  id: "content-manager.containers.edit.information.last-draft.label",
2217
- defaultMessage: "Last draft"
2712
+ defaultMessage: "Updated"
2218
2713
  }),
2219
2714
  value: formatMessage(
2220
2715
  {
2221
2716
  id: "content-manager.containers.edit.information.last-draft.value",
2222
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2717
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2223
2718
  },
2224
2719
  {
2225
2720
  time: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2237,12 +2732,12 @@ const Information = ({ activeTab }) => {
2237
2732
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2238
2733
  label: formatMessage({
2239
2734
  id: "content-manager.containers.edit.information.document.label",
2240
- defaultMessage: "Document"
2735
+ defaultMessage: "Created"
2241
2736
  }),
2242
2737
  value: formatMessage(
2243
2738
  {
2244
2739
  id: "content-manager.containers.edit.information.document.value",
2245
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2740
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2246
2741
  },
2247
2742
  {
2248
2743
  time: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2280,25 +2775,77 @@ const Information = ({ activeTab }) => {
2280
2775
  );
2281
2776
  };
2282
2777
  const HeaderActions = ({ actions: actions2 }) => {
2283
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: actions2.map((action) => {
2284
- if ("options" in action) {
2778
+ const [dialogId, setDialogId] = React__namespace.useState(null);
2779
+ const handleClick = (action) => async (e) => {
2780
+ if (!("options" in action)) {
2781
+ const { onClick = () => false, dialog, id } = action;
2782
+ const muteDialog = await onClick(e);
2783
+ if (dialog && !muteDialog) {
2784
+ e.preventDefault();
2785
+ setDialogId(id);
2786
+ }
2787
+ }
2788
+ };
2789
+ const handleClose = () => {
2790
+ setDialogId(null);
2791
+ };
2792
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, children: actions2.map((action) => {
2793
+ if (action.options) {
2285
2794
  return /* @__PURE__ */ jsxRuntime.jsx(
2286
2795
  designSystem.SingleSelect,
2287
2796
  {
2288
2797
  size: "S",
2289
- disabled: action.disabled,
2290
- "aria-label": action.label,
2291
2798
  onChange: action.onSelect,
2292
- value: action.value,
2799
+ "aria-label": action.label,
2800
+ ...action,
2293
2801
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { ...option, children: label }, option.value))
2294
2802
  },
2295
2803
  action.id
2296
2804
  );
2297
2805
  } else {
2298
- return null;
2806
+ if (action.type === "icon") {
2807
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
2808
+ /* @__PURE__ */ jsxRuntime.jsx(
2809
+ designSystem.IconButton,
2810
+ {
2811
+ disabled: action.disabled,
2812
+ label: action.label,
2813
+ size: "S",
2814
+ onClick: handleClick(action),
2815
+ children: action.icon
2816
+ }
2817
+ ),
2818
+ action.dialog ? /* @__PURE__ */ jsxRuntime.jsx(
2819
+ HeaderActionDialog,
2820
+ {
2821
+ ...action.dialog,
2822
+ isOpen: dialogId === action.id,
2823
+ onClose: handleClose
2824
+ }
2825
+ ) : null
2826
+ ] }, action.id);
2827
+ }
2299
2828
  }
2300
2829
  }) });
2301
2830
  };
2831
+ const HeaderActionDialog = ({
2832
+ onClose,
2833
+ onCancel,
2834
+ title,
2835
+ content: Content,
2836
+ isOpen
2837
+ }) => {
2838
+ const handleClose = async () => {
2839
+ if (onCancel) {
2840
+ await onCancel();
2841
+ }
2842
+ onClose();
2843
+ };
2844
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2845
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2846
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content
2847
+ ] }) });
2848
+ };
2302
2849
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2303
2850
  const navigate = reactRouterDom.useNavigate();
2304
2851
  const { formatMessage } = reactIntl.useIntl();
@@ -2339,12 +2886,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2339
2886
  const { delete: deleteAction } = useDocumentActions();
2340
2887
  const { toggleNotification } = strapiAdmin.useNotification();
2341
2888
  const setSubmitting = strapiAdmin.useForm("DeleteAction", (state) => state.setSubmitting);
2889
+ const isLocalized = document?.locale != null;
2342
2890
  return {
2343
2891
  disabled: !canDelete || !document,
2344
- label: formatMessage({
2345
- id: "content-manager.actions.delete.label",
2346
- defaultMessage: "Delete document"
2347
- }),
2892
+ label: formatMessage(
2893
+ {
2894
+ id: "content-manager.actions.delete.label",
2895
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2896
+ },
2897
+ { isLocalized }
2898
+ ),
2348
2899
  icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2349
2900
  dialog: {
2350
2901
  type: "dialog",
@@ -2379,424 +2930,122 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2379
2930
  }
2380
2931
  const res = await deleteAction({
2381
2932
  documentId,
2382
- model,
2383
- collectionType,
2384
- params: {
2385
- locale: "*"
2386
- }
2387
- });
2388
- if (!("error" in res)) {
2389
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2390
- }
2391
- } finally {
2392
- if (!listViewPathMatch) {
2393
- setSubmitting(false);
2394
- }
2395
- }
2396
- }
2397
- },
2398
- variant: "danger",
2399
- position: ["header", "table-row"]
2400
- };
2401
- };
2402
- DeleteAction$1.type = "delete";
2403
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2404
- const Panels = () => {
2405
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2406
- const [
2407
- {
2408
- query: { status }
2409
- }
2410
- ] = strapiAdmin.useQueryParams({
2411
- status: "draft"
2412
- });
2413
- const { model, id, document, meta, collectionType } = useDoc();
2414
- const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2415
- const props = {
2416
- activeTab: status,
2417
- model,
2418
- documentId: id,
2419
- document: isCloning ? void 0 : document,
2420
- meta: isCloning ? void 0 : meta,
2421
- collectionType
2422
- };
2423
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2424
- strapiAdmin.DescriptionComponentRenderer,
2425
- {
2426
- props,
2427
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2428
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2429
- }
2430
- ) });
2431
- };
2432
- const ActionsPanel = () => {
2433
- const { formatMessage } = reactIntl.useIntl();
2434
- return {
2435
- title: formatMessage({
2436
- id: "content-manager.containers.edit.panels.default.title",
2437
- defaultMessage: "Document"
2438
- }),
2439
- content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2440
- };
2441
- };
2442
- ActionsPanel.type = "actions";
2443
- const ActionsPanelContent = () => {
2444
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2445
- const [
2446
- {
2447
- query: { status = "draft" }
2448
- }
2449
- ] = strapiAdmin.useQueryParams();
2450
- const { model, id, document, meta, collectionType } = useDoc();
2451
- const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
2452
- const props = {
2453
- activeTab: status,
2454
- model,
2455
- documentId: id,
2456
- document: isCloning ? void 0 : document,
2457
- meta: isCloning ? void 0 : meta,
2458
- collectionType
2459
- };
2460
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
2461
- /* @__PURE__ */ jsxRuntime.jsx(
2462
- strapiAdmin.DescriptionComponentRenderer,
2463
- {
2464
- props,
2465
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2466
- children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
2467
- }
2468
- ),
2469
- /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
2470
- ] });
2471
- };
2472
- const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
2473
- return /* @__PURE__ */ jsxRuntime.jsxs(
2474
- designSystem.Flex,
2475
- {
2476
- ref,
2477
- tag: "aside",
2478
- "aria-labelledby": "additional-information",
2479
- background: "neutral0",
2480
- borderColor: "neutral150",
2481
- hasRadius: true,
2482
- paddingBottom: 4,
2483
- paddingLeft: 4,
2484
- paddingRight: 4,
2485
- paddingTop: 4,
2486
- shadow: "tableShadow",
2487
- gap: 3,
2488
- direction: "column",
2489
- justifyContent: "stretch",
2490
- alignItems: "flex-start",
2491
- children: [
2492
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2493
- children
2494
- ]
2495
- }
2496
- );
2497
- });
2498
- const HOOKS = {
2499
- /**
2500
- * Hook that allows to mutate the displayed headers of the list view table
2501
- * @constant
2502
- * @type {string}
2503
- */
2504
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2505
- /**
2506
- * Hook that allows to mutate the CM's collection types links pre-set filters
2507
- * @constant
2508
- * @type {string}
2509
- */
2510
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2511
- /**
2512
- * Hook that allows to mutate the CM's edit view layout
2513
- * @constant
2514
- * @type {string}
2515
- */
2516
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2517
- /**
2518
- * Hook that allows to mutate the CM's single types links pre-set filters
2519
- * @constant
2520
- * @type {string}
2521
- */
2522
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2523
- };
2524
- const contentTypesApi = contentManagerApi.injectEndpoints({
2525
- endpoints: (builder) => ({
2526
- getContentTypeConfiguration: builder.query({
2527
- query: (uid) => ({
2528
- url: `/content-manager/content-types/${uid}/configuration`,
2529
- method: "GET"
2530
- }),
2531
- transformResponse: (response) => response.data,
2532
- providesTags: (_result, _error, uid) => [
2533
- { type: "ContentTypesConfiguration", id: uid },
2534
- { type: "ContentTypeSettings", id: "LIST" }
2535
- ]
2536
- }),
2537
- getAllContentTypeSettings: builder.query({
2538
- query: () => "/content-manager/content-types-settings",
2539
- transformResponse: (response) => response.data,
2540
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2541
- }),
2542
- updateContentTypeConfiguration: builder.mutation({
2543
- query: ({ uid, ...body }) => ({
2544
- url: `/content-manager/content-types/${uid}/configuration`,
2545
- method: "PUT",
2546
- data: body
2547
- }),
2548
- transformResponse: (response) => response.data,
2549
- invalidatesTags: (_result, _error, { uid }) => [
2550
- { type: "ContentTypesConfiguration", id: uid },
2551
- { type: "ContentTypeSettings", id: "LIST" },
2552
- // Is this necessary?
2553
- { type: "InitialData" }
2554
- ]
2555
- })
2556
- })
2557
- });
2558
- const {
2559
- useGetContentTypeConfigurationQuery,
2560
- useGetAllContentTypeSettingsQuery,
2561
- useUpdateContentTypeConfigurationMutation
2562
- } = contentTypesApi;
2563
- const checkIfAttributeIsDisplayable = (attribute) => {
2564
- const { type } = attribute;
2565
- if (type === "relation") {
2566
- return !attribute.relation.toLowerCase().includes("morph");
2567
- }
2568
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2569
- };
2570
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2571
- if (!mainFieldName) {
2572
- return void 0;
2573
- }
2574
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2575
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2576
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2577
- );
2578
- return {
2579
- name: mainFieldName,
2580
- type: mainFieldType ?? "string"
2581
- };
2582
- };
2583
- const DEFAULT_SETTINGS = {
2584
- bulkable: false,
2585
- filterable: false,
2586
- searchable: false,
2587
- pagination: false,
2588
- defaultSortBy: "",
2589
- defaultSortOrder: "asc",
2590
- mainField: "id",
2591
- pageSize: 10
2592
- };
2593
- const useDocumentLayout = (model) => {
2594
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2595
- const [{ query }] = strapiAdmin.useQueryParams();
2596
- const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2597
- const { toggleNotification } = strapiAdmin.useNotification();
2598
- const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
2599
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2600
- const {
2601
- data,
2602
- isLoading: isLoadingConfigs,
2603
- error,
2604
- isFetching: isFetchingConfigs
2605
- } = useGetContentTypeConfigurationQuery(model);
2606
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2607
- React__namespace.useEffect(() => {
2608
- if (error) {
2609
- toggleNotification({
2610
- type: "danger",
2611
- message: formatAPIError(error)
2612
- });
2613
- }
2614
- }, [error, formatAPIError, toggleNotification]);
2615
- const editLayout = React__namespace.useMemo(
2616
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2617
- layout: [],
2618
- components: {},
2619
- metadatas: {},
2620
- options: {},
2621
- settings: DEFAULT_SETTINGS
2622
- },
2623
- [data, isLoading, schemas, schema, components]
2624
- );
2625
- const listLayout = React__namespace.useMemo(() => {
2626
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2627
- layout: [],
2628
- metadatas: {},
2629
- options: {},
2630
- settings: DEFAULT_SETTINGS
2631
- };
2632
- }, [data, isLoading, schemas, schema, components]);
2633
- const { layout: edit } = React__namespace.useMemo(
2634
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2635
- layout: editLayout,
2636
- query
2637
- }),
2638
- [editLayout, query, runHookWaterfall]
2639
- );
2640
- return {
2641
- error,
2642
- isLoading,
2643
- edit,
2644
- list: listLayout
2645
- };
2646
- };
2647
- const useDocLayout = () => {
2648
- const { model } = useDoc();
2649
- return useDocumentLayout(model);
2650
- };
2651
- const formatEditLayout = (data, {
2652
- schemas,
2653
- schema,
2654
- components
2655
- }) => {
2656
- let currentPanelIndex = 0;
2657
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2658
- data.contentType.layouts.edit,
2659
- schema?.attributes,
2660
- data.contentType.metadatas,
2661
- { configurations: data.components, schemas: components },
2662
- schemas
2663
- ).reduce((panels, row) => {
2664
- if (row.some((field) => field.type === "dynamiczone")) {
2665
- panels.push([row]);
2666
- currentPanelIndex += 2;
2667
- } else {
2668
- if (!panels[currentPanelIndex]) {
2669
- panels.push([]);
2670
- }
2671
- panels[currentPanelIndex].push(row);
2672
- }
2673
- return panels;
2674
- }, []);
2675
- const componentEditAttributes = Object.entries(data.components).reduce(
2676
- (acc, [uid, configuration]) => {
2677
- acc[uid] = {
2678
- layout: convertEditLayoutToFieldLayouts(
2679
- configuration.layouts.edit,
2680
- components[uid].attributes,
2681
- configuration.metadatas
2682
- ),
2683
- settings: {
2684
- ...configuration.settings,
2685
- icon: components[uid].info.icon,
2686
- displayName: components[uid].info.displayName
2687
- }
2688
- };
2689
- return acc;
2690
- },
2691
- {}
2692
- );
2693
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2694
- (acc, [attribute, metadata]) => {
2695
- return {
2696
- ...acc,
2697
- [attribute]: metadata.edit
2698
- };
2699
- },
2700
- {}
2701
- );
2702
- return {
2703
- layout: panelledEditAttributes,
2704
- components: componentEditAttributes,
2705
- metadatas: editMetadatas,
2706
- settings: {
2707
- ...data.contentType.settings,
2708
- displayName: schema?.info.displayName
2933
+ model,
2934
+ collectionType,
2935
+ params: {
2936
+ locale: "*"
2937
+ }
2938
+ });
2939
+ if (!("error" in res)) {
2940
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2941
+ }
2942
+ } finally {
2943
+ if (!listViewPathMatch) {
2944
+ setSubmitting(false);
2945
+ }
2946
+ }
2947
+ }
2709
2948
  },
2710
- options: {
2711
- ...schema?.options,
2712
- ...schema?.pluginOptions,
2713
- ...data.contentType.options
2714
- }
2949
+ variant: "danger",
2950
+ position: ["header", "table-row"]
2715
2951
  };
2716
2952
  };
2717
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2718
- return rows.map(
2719
- (row) => row.map((field) => {
2720
- const attribute = attributes[field.name];
2721
- if (!attribute) {
2722
- return null;
2723
- }
2724
- const { edit: metadata } = metadatas[field.name];
2725
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2726
- return {
2727
- attribute,
2728
- disabled: !metadata.editable,
2729
- hint: metadata.description,
2730
- label: metadata.label ?? "",
2731
- name: field.name,
2732
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2733
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2734
- schemas,
2735
- components: components?.schemas ?? {}
2736
- }),
2737
- placeholder: metadata.placeholder ?? "",
2738
- required: attribute.required ?? false,
2739
- size: field.size,
2740
- unique: "unique" in attribute ? attribute.unique : false,
2741
- visible: metadata.visible ?? true,
2742
- type: attribute.type
2743
- };
2744
- }).filter((field) => field !== null)
2745
- );
2953
+ DeleteAction$1.type = "delete";
2954
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2955
+ const Panels = () => {
2956
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2957
+ const [
2958
+ {
2959
+ query: { status }
2960
+ }
2961
+ ] = strapiAdmin.useQueryParams({
2962
+ status: "draft"
2963
+ });
2964
+ const { model, id, document, meta, collectionType } = useDoc();
2965
+ const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2966
+ const props = {
2967
+ activeTab: status,
2968
+ model,
2969
+ documentId: id,
2970
+ document: isCloning ? void 0 : document,
2971
+ meta: isCloning ? void 0 : meta,
2972
+ collectionType
2973
+ };
2974
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2975
+ strapiAdmin.DescriptionComponentRenderer,
2976
+ {
2977
+ props,
2978
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2979
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2980
+ }
2981
+ ) });
2746
2982
  };
2747
- const formatListLayout = (data, {
2748
- schemas,
2749
- schema,
2750
- components
2751
- }) => {
2752
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2753
- (acc, [attribute, metadata]) => {
2754
- return {
2755
- ...acc,
2756
- [attribute]: metadata.list
2757
- };
2758
- },
2759
- {}
2760
- );
2761
- const listAttributes = convertListLayoutToFieldLayouts(
2762
- data.contentType.layouts.list,
2763
- schema?.attributes,
2764
- listMetadatas,
2765
- { configurations: data.components, schemas: components },
2766
- schemas
2767
- );
2983
+ const ActionsPanel = () => {
2984
+ const { formatMessage } = reactIntl.useIntl();
2768
2985
  return {
2769
- layout: listAttributes,
2770
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2771
- metadatas: listMetadatas,
2772
- options: {
2773
- ...schema?.options,
2774
- ...schema?.pluginOptions,
2775
- ...data.contentType.options
2776
- }
2986
+ title: formatMessage({
2987
+ id: "content-manager.containers.edit.panels.default.title",
2988
+ defaultMessage: "Entry"
2989
+ }),
2990
+ content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2777
2991
  };
2778
2992
  };
2779
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2780
- return columns.map((name) => {
2781
- const attribute = attributes[name];
2782
- if (!attribute) {
2783
- return null;
2993
+ ActionsPanel.type = "actions";
2994
+ const ActionsPanelContent = () => {
2995
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2996
+ const [
2997
+ {
2998
+ query: { status = "draft" }
2784
2999
  }
2785
- const metadata = metadatas[name];
2786
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2787
- return {
2788
- attribute,
2789
- label: metadata.label ?? "",
2790
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2791
- schemas,
2792
- components: components?.schemas ?? {}
2793
- }),
2794
- name,
2795
- searchable: metadata.searchable ?? true,
2796
- sortable: metadata.sortable ?? true
2797
- };
2798
- }).filter((field) => field !== null);
3000
+ ] = strapiAdmin.useQueryParams();
3001
+ const { model, id, document, meta, collectionType } = useDoc();
3002
+ const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
3003
+ const props = {
3004
+ activeTab: status,
3005
+ model,
3006
+ documentId: id,
3007
+ document: isCloning ? void 0 : document,
3008
+ meta: isCloning ? void 0 : meta,
3009
+ collectionType
3010
+ };
3011
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
3012
+ /* @__PURE__ */ jsxRuntime.jsx(
3013
+ strapiAdmin.DescriptionComponentRenderer,
3014
+ {
3015
+ props,
3016
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
3017
+ children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
3018
+ }
3019
+ ),
3020
+ /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
3021
+ ] });
2799
3022
  };
3023
+ const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
3024
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3025
+ designSystem.Flex,
3026
+ {
3027
+ ref,
3028
+ tag: "aside",
3029
+ "aria-labelledby": "additional-information",
3030
+ background: "neutral0",
3031
+ borderColor: "neutral150",
3032
+ hasRadius: true,
3033
+ paddingBottom: 4,
3034
+ paddingLeft: 4,
3035
+ paddingRight: 4,
3036
+ paddingTop: 4,
3037
+ shadow: "tableShadow",
3038
+ gap: 3,
3039
+ direction: "column",
3040
+ justifyContent: "stretch",
3041
+ alignItems: "flex-start",
3042
+ children: [
3043
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3044
+ children
3045
+ ]
3046
+ }
3047
+ );
3048
+ });
2800
3049
  const ConfirmBulkActionDialog = ({
2801
3050
  onToggleDialog,
2802
3051
  isOpen = false,
@@ -2804,7 +3053,7 @@ const ConfirmBulkActionDialog = ({
2804
3053
  endAction
2805
3054
  }) => {
2806
3055
  const { formatMessage } = reactIntl.useIntl();
2807
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { onOpenChange: onToggleDialog, open: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
3056
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2808
3057
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage({
2809
3058
  id: "app.components.ConfirmDialog.title",
2810
3059
  defaultMessage: "Confirmation"
@@ -2835,6 +3084,7 @@ const ConfirmDialogPublishAll = ({
2835
3084
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler(getTranslation);
2836
3085
  const { model, schema } = useDoc();
2837
3086
  const [{ query }] = strapiAdmin.useQueryParams();
3087
+ const enableDraftRelationsCount = false;
2838
3088
  const {
2839
3089
  data: countDraftRelations = 0,
2840
3090
  isLoading,
@@ -2846,7 +3096,7 @@ const ConfirmDialogPublishAll = ({
2846
3096
  locale: query?.plugins?.i18n?.locale
2847
3097
  },
2848
3098
  {
2849
- skip: selectedEntries.length === 0
3099
+ skip: !enableDraftRelationsCount
2850
3100
  }
2851
3101
  );
2852
3102
  React__namespace.useEffect(() => {
@@ -3031,7 +3281,7 @@ const SelectedEntriesTableContent = ({
3031
3281
  status: row.status
3032
3282
  }
3033
3283
  ) }),
3034
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
3284
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(
3035
3285
  designSystem.IconButton,
3036
3286
  {
3037
3287
  tag: reactRouterDom.Link,
@@ -3054,9 +3304,10 @@ const SelectedEntriesTableContent = ({
3054
3304
  ),
3055
3305
  target: "_blank",
3056
3306
  marginLeft: "auto",
3057
- children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {})
3307
+ variant: "ghost",
3308
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, { width: "1.6rem", height: "1.6rem" })
3058
3309
  }
3059
- ) })
3310
+ ) }) })
3060
3311
  ] }, row.id)) })
3061
3312
  ] });
3062
3313
  };
@@ -3093,7 +3344,13 @@ const SelectedEntriesModalContent = ({
3093
3344
  );
3094
3345
  const { rows, validationErrors } = React__namespace.useMemo(() => {
3095
3346
  if (data.length > 0 && schema) {
3096
- const validate = createYupSchema(schema.attributes, components);
3347
+ const validate = createYupSchema(
3348
+ schema.attributes,
3349
+ components,
3350
+ // Since this is the "Publish" action, the validation
3351
+ // schema must enforce the rules for published entities
3352
+ { status: "published" }
3353
+ );
3097
3354
  const validationErrors2 = {};
3098
3355
  const rows2 = data.map((entry) => {
3099
3356
  try {
@@ -3443,7 +3700,7 @@ const TableActions = ({ document }) => {
3443
3700
  strapiAdmin.DescriptionComponentRenderer,
3444
3701
  {
3445
3702
  props,
3446
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3703
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3447
3704
  children: (actions2) => {
3448
3705
  const tableRowActions = actions2.filter((action) => {
3449
3706
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3554,7 +3811,7 @@ const CloneAction = ({ model, documentId }) => {
3554
3811
  }),
3555
3812
  content: /* @__PURE__ */ jsxRuntime.jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3556
3813
  footer: ({ onClose }) => {
3557
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
3814
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
3558
3815
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3559
3816
  id: "cancel",
3560
3817
  defaultMessage: "Cancel"
@@ -3595,8 +3852,7 @@ class ContentManagerPlugin {
3595
3852
  documentActions = [
3596
3853
  ...DEFAULT_ACTIONS,
3597
3854
  ...DEFAULT_TABLE_ROW_ACTIONS,
3598
- ...DEFAULT_HEADER_ACTIONS,
3599
- HistoryAction
3855
+ ...DEFAULT_HEADER_ACTIONS
3600
3856
  ];
3601
3857
  editViewSidePanels = [ActionsPanel];
3602
3858
  headerActions = [];
@@ -3685,6 +3941,52 @@ const getPrintableType = (value) => {
3685
3941
  }
3686
3942
  return nativeType;
3687
3943
  };
3944
+ const HistoryAction = ({ model, document }) => {
3945
+ const { formatMessage } = reactIntl.useIntl();
3946
+ const [{ query }] = strapiAdmin.useQueryParams();
3947
+ const navigate = reactRouterDom.useNavigate();
3948
+ const pluginsQueryParams = qs.stringify({ plugins: query.plugins }, { encode: false });
3949
+ if (!window.strapi.features.isEnabled("cms-content-history")) {
3950
+ return null;
3951
+ }
3952
+ return {
3953
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ClockCounterClockwise, {}),
3954
+ label: formatMessage({
3955
+ id: "content-manager.history.document-action",
3956
+ defaultMessage: "Content History"
3957
+ }),
3958
+ onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
3959
+ disabled: (
3960
+ /**
3961
+ * The user is creating a new document.
3962
+ * It hasn't been saved yet, so there's no history to go to
3963
+ */
3964
+ !document || /**
3965
+ * The document has been created but the current dimension has never been saved.
3966
+ * For example, the user is creating a new locale in an existing document,
3967
+ * so there's no history for the document in that locale
3968
+ */
3969
+ !document.id || /**
3970
+ * History is only available for content types created by the user.
3971
+ * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
3972
+ * which start with `admin::` or `plugin::`
3973
+ */
3974
+ !model.startsWith("api::")
3975
+ ),
3976
+ position: "header"
3977
+ };
3978
+ };
3979
+ HistoryAction.type = "history";
3980
+ const historyAdmin = {
3981
+ bootstrap(app) {
3982
+ const { addDocumentAction } = app.getPlugin("content-manager").apis;
3983
+ addDocumentAction((actions2) => {
3984
+ const indexOfDeleteAction = actions2.findIndex((action) => action.type === "delete");
3985
+ actions2.splice(indexOfDeleteAction, 0, HistoryAction);
3986
+ return actions2;
3987
+ });
3988
+ }
3989
+ };
3688
3990
  const initialState = {
3689
3991
  collectionTypeLinks: [],
3690
3992
  components: [],
@@ -3721,6 +4023,57 @@ const { setInitialData } = actions;
3721
4023
  const reducer = toolkit.combineReducers({
3722
4024
  app: reducer$1
3723
4025
  });
4026
+ const previewApi = contentManagerApi.injectEndpoints({
4027
+ endpoints: (builder) => ({
4028
+ getPreviewUrl: builder.query({
4029
+ query({ query, params }) {
4030
+ return {
4031
+ url: `/content-manager/preview/url/${params.contentType}`,
4032
+ method: "GET",
4033
+ config: {
4034
+ params: query
4035
+ }
4036
+ };
4037
+ }
4038
+ })
4039
+ })
4040
+ });
4041
+ const { useGetPreviewUrlQuery } = previewApi;
4042
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4043
+ const { formatMessage } = reactIntl.useIntl();
4044
+ const { data, error } = useGetPreviewUrlQuery({
4045
+ params: {
4046
+ contentType: model
4047
+ },
4048
+ query: {
4049
+ documentId,
4050
+ locale: document?.locale,
4051
+ status: document?.status
4052
+ }
4053
+ });
4054
+ if (!data?.data?.url || error) {
4055
+ return null;
4056
+ }
4057
+ return {
4058
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4059
+ content: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", fullWidth: true, tag: reactRouterDom.Link, to: data.data.url, target: "_blank", children: formatMessage({
4060
+ id: "content-manager.preview.panel.button",
4061
+ defaultMessage: "Open preview"
4062
+ }) })
4063
+ };
4064
+ };
4065
+ const FEATURE_ID = "preview";
4066
+ const previewAdmin = {
4067
+ bootstrap(app) {
4068
+ if (!window.strapi.future.isEnabled(FEATURE_ID)) {
4069
+ return;
4070
+ }
4071
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4072
+ if ("addEditViewSidePanel" in contentManagerPluginApis && typeof contentManagerPluginApis.addEditViewSidePanel === "function") {
4073
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4074
+ }
4075
+ }
4076
+ };
3724
4077
  const index = {
3725
4078
  register(app) {
3726
4079
  const cm = new ContentManagerPlugin();
@@ -3740,7 +4093,7 @@ const index = {
3740
4093
  app.router.addRoute({
3741
4094
  path: "content-manager/*",
3742
4095
  lazy: async () => {
3743
- const { Layout } = await Promise.resolve().then(() => require("./layout-Ciz224q5.js"));
4096
+ const { Layout } = await Promise.resolve().then(() => require("./layout-CVz8WiDC.js"));
3744
4097
  return {
3745
4098
  Component: Layout
3746
4099
  };
@@ -3749,10 +4102,18 @@ const index = {
3749
4102
  });
3750
4103
  app.registerPlugin(cm.config);
3751
4104
  },
4105
+ bootstrap(app) {
4106
+ if (typeof historyAdmin.bootstrap === "function") {
4107
+ historyAdmin.bootstrap(app);
4108
+ }
4109
+ if (typeof previewAdmin.bootstrap === "function") {
4110
+ previewAdmin.bootstrap(app);
4111
+ }
4112
+ },
3752
4113
  async registerTrads({ locales }) {
3753
4114
  const importedTrads = await Promise.all(
3754
4115
  locales.map((locale) => {
3755
- 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 }) => {
4116
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => Promise.resolve().then(() => require("./ar-BUUWXIYu.js")), "./translations/ca.json": () => Promise.resolve().then(() => require("./ca-Cmk45QO6.js")), "./translations/cs.json": () => Promise.resolve().then(() => require("./cs-CkJy6B2v.js")), "./translations/de.json": () => Promise.resolve().then(() => require("./de-CCEmbAah.js")), "./translations/en.json": () => Promise.resolve().then(() => require("./en-C-J4DGEe.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 }) => {
3756
4117
  return {
3757
4118
  data: prefixPluginTranslations(data, PLUGIN_ID),
3758
4119
  locale
@@ -3770,6 +4131,7 @@ const index = {
3770
4131
  };
3771
4132
  exports.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD;
3772
4133
  exports.BulkActionsRenderer = BulkActionsRenderer;
4134
+ exports.CLONE_PATH = CLONE_PATH;
3773
4135
  exports.COLLECTION_TYPES = COLLECTION_TYPES;
3774
4136
  exports.CREATOR_FIELDS = CREATOR_FIELDS;
3775
4137
  exports.DEFAULT_SETTINGS = DEFAULT_SETTINGS;
@@ -3797,6 +4159,7 @@ exports.getMainField = getMainField;
3797
4159
  exports.getTranslation = getTranslation;
3798
4160
  exports.index = index;
3799
4161
  exports.setInitialData = setInitialData;
4162
+ exports.useContentManagerContext = useContentManagerContext;
3800
4163
  exports.useContentTypeSchema = useContentTypeSchema;
3801
4164
  exports.useDoc = useDoc;
3802
4165
  exports.useDocLayout = useDocLayout;
@@ -3809,4 +4172,4 @@ exports.useGetAllDocumentsQuery = useGetAllDocumentsQuery;
3809
4172
  exports.useGetContentTypeConfigurationQuery = useGetContentTypeConfigurationQuery;
3810
4173
  exports.useGetInitialDataQuery = useGetInitialDataQuery;
3811
4174
  exports.useUpdateContentTypeConfigurationMutation = useUpdateContentTypeConfigurationMutation;
3812
- //# sourceMappingURL=index-BOZx6IMg.js.map
4175
+ //# sourceMappingURL=index-76eawJUd.js.map