@strapi/content-manager 0.0.0-experimental.745741d19e90275ca6f7c928ca19f9bb0fd9d933 → 0.0.0-experimental.78b47df46708173ab4833373f694257729db4b9e

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 (129) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-DHNM3YBz.mjs → ComponentConfigurationPage-7-qB29e7.mjs} +3 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-DHNM3YBz.mjs.map → ComponentConfigurationPage-7-qB29e7.mjs.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-BvHtG7uH.js → ComponentConfigurationPage-DP7AC0UU.js} +3 -3
  4. package/dist/_chunks/{ComponentConfigurationPage-BvHtG7uH.js.map → ComponentConfigurationPage-DP7AC0UU.js.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-Cp6HAEzN.mjs → EditConfigurationPage-CI4XoymK.mjs} +3 -3
  6. package/dist/_chunks/{EditConfigurationPage-Cp6HAEzN.mjs.map → EditConfigurationPage-CI4XoymK.mjs.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-DOmfCEMo.js → EditConfigurationPage-DITVliEI.js} +3 -3
  8. package/dist/_chunks/{EditConfigurationPage-DOmfCEMo.js.map → EditConfigurationPage-DITVliEI.js.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-BqNpC6hO.js → EditViewPage-CUS2EAhB.js} +24 -9
  10. package/dist/_chunks/EditViewPage-CUS2EAhB.js.map +1 -0
  11. package/dist/_chunks/{EditViewPage-BtkEx339.mjs → EditViewPage-Dzpno8xI.mjs} +24 -9
  12. package/dist/_chunks/EditViewPage-Dzpno8xI.mjs.map +1 -0
  13. package/dist/_chunks/{Field-R5NbffTB.mjs → Field-B_jG_EV9.mjs} +210 -145
  14. package/dist/_chunks/Field-B_jG_EV9.mjs.map +1 -0
  15. package/dist/_chunks/{Field-lsPFnAmH.js → Field-CtUU1Fg8.js} +212 -147
  16. package/dist/_chunks/Field-CtUU1Fg8.js.map +1 -0
  17. package/dist/_chunks/{Form-BHmXSfyy.mjs → Form-BXHao2mZ.mjs} +35 -16
  18. package/dist/_chunks/Form-BXHao2mZ.mjs.map +1 -0
  19. package/dist/_chunks/{Form-CcGboku8.js → Form-DTqO0ymI.js} +35 -16
  20. package/dist/_chunks/Form-DTqO0ymI.js.map +1 -0
  21. package/dist/_chunks/{History-ByUPL3T3.mjs → History-2Ah2CQ4T.mjs} +28 -18
  22. package/dist/_chunks/History-2Ah2CQ4T.mjs.map +1 -0
  23. package/dist/_chunks/{History-Bsud8jwh.js → History-C_uSGzO5.js} +28 -18
  24. package/dist/_chunks/History-C_uSGzO5.js.map +1 -0
  25. package/dist/_chunks/{ListConfigurationPage-Bm5HACXf.mjs → ListConfigurationPage-BjSJlaoC.mjs} +20 -8
  26. package/dist/_chunks/ListConfigurationPage-BjSJlaoC.mjs.map +1 -0
  27. package/dist/_chunks/{ListConfigurationPage-DiT463qx.js → ListConfigurationPage-nyuP7OSy.js} +20 -8
  28. package/dist/_chunks/ListConfigurationPage-nyuP7OSy.js.map +1 -0
  29. package/dist/_chunks/{ListViewPage-JSyNAAYu.mjs → ListViewPage-B75x3nz2.mjs} +47 -38
  30. package/dist/_chunks/ListViewPage-B75x3nz2.mjs.map +1 -0
  31. package/dist/_chunks/{ListViewPage-CsrC9L_d.js → ListViewPage-DHgHD8Xg.js} +49 -40
  32. package/dist/_chunks/ListViewPage-DHgHD8Xg.js.map +1 -0
  33. package/dist/_chunks/{NoContentTypePage-Bsvng4II.js → NoContentTypePage-CDUKdZ7d.js} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-Bsvng4II.js.map → NoContentTypePage-CDUKdZ7d.js.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-CsrQUpBE.mjs → NoContentTypePage-DUacQSyF.mjs} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-CsrQUpBE.mjs.map → NoContentTypePage-DUacQSyF.mjs.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-DNmf_pj0.mjs → NoPermissionsPage-SFllMekk.mjs} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-DNmf_pj0.mjs.map → NoPermissionsPage-SFllMekk.mjs.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-CdHNJtEf.js → NoPermissionsPage-zwIZydDI.js} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-CdHNJtEf.js.map → NoPermissionsPage-zwIZydDI.js.map} +1 -1
  41. package/dist/_chunks/{Relations-u8-37jK0.mjs → Relations-D2NRW8fC.mjs} +14 -10
  42. package/dist/_chunks/Relations-D2NRW8fC.mjs.map +1 -0
  43. package/dist/_chunks/{Relations-CghaPv2D.js → Relations-NFLaRNPr.js} +14 -10
  44. package/dist/_chunks/Relations-NFLaRNPr.js.map +1 -0
  45. package/dist/_chunks/{en-fbKQxLGn.js → en-BlhnxQfj.js} +11 -9
  46. package/dist/_chunks/{en-fbKQxLGn.js.map → en-BlhnxQfj.js.map} +1 -1
  47. package/dist/_chunks/{en-Ux26r5pl.mjs → en-C8YBvRrK.mjs} +11 -9
  48. package/dist/_chunks/{en-Ux26r5pl.mjs.map → en-C8YBvRrK.mjs.map} +1 -1
  49. package/dist/_chunks/{index-CaE6NG4a.mjs → index-C9HxCo5R.mjs} +971 -686
  50. package/dist/_chunks/index-C9HxCo5R.mjs.map +1 -0
  51. package/dist/_chunks/{index-BOZx6IMg.js → index-ovJRE1FM.js} +952 -667
  52. package/dist/_chunks/index-ovJRE1FM.js.map +1 -0
  53. package/dist/_chunks/{layout-Bx7svTbY.mjs → layout-DaUjDiWQ.mjs} +23 -10
  54. package/dist/_chunks/layout-DaUjDiWQ.mjs.map +1 -0
  55. package/dist/_chunks/{layout-Ciz224q5.js → layout-UNWstw_s.js} +22 -9
  56. package/dist/_chunks/layout-UNWstw_s.js.map +1 -0
  57. package/dist/_chunks/{relations-Cxc1cEv3.mjs → relations-D8iFAeRu.mjs} +2 -2
  58. package/dist/_chunks/{relations-Cxc1cEv3.mjs.map → relations-D8iFAeRu.mjs.map} +1 -1
  59. package/dist/_chunks/{relations-CP8sB2YZ.js → relations-NN3coOG5.js} +2 -2
  60. package/dist/_chunks/{relations-CP8sB2YZ.js.map → relations-NN3coOG5.js.map} +1 -1
  61. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  62. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  63. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  64. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  65. package/dist/admin/index.js +2 -1
  66. package/dist/admin/index.js.map +1 -1
  67. package/dist/admin/index.mjs +5 -4
  68. package/dist/admin/src/exports.d.ts +1 -1
  69. package/dist/admin/src/history/index.d.ts +3 -0
  70. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  71. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  72. package/dist/admin/src/index.d.ts +1 -0
  73. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -0
  74. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  75. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  76. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  77. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  78. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  79. package/dist/admin/src/services/api.d.ts +1 -1
  80. package/dist/admin/src/services/components.d.ts +2 -2
  81. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  82. package/dist/admin/src/services/documents.d.ts +19 -17
  83. package/dist/admin/src/services/init.d.ts +1 -1
  84. package/dist/admin/src/services/relations.d.ts +2 -2
  85. package/dist/admin/src/services/uid.d.ts +3 -3
  86. package/dist/admin/src/utils/validation.d.ts +4 -1
  87. package/dist/server/index.js +184 -108
  88. package/dist/server/index.js.map +1 -1
  89. package/dist/server/index.mjs +185 -109
  90. package/dist/server/index.mjs.map +1 -1
  91. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  92. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  93. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  94. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  95. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  96. package/dist/server/src/history/services/history.d.ts.map +1 -1
  97. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  98. package/dist/server/src/history/services/utils.d.ts +2 -1
  99. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  100. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  101. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  102. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  103. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  104. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  105. package/dist/shared/contracts/collection-types.d.ts +3 -1
  106. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  107. package/package.json +11 -11
  108. package/dist/_chunks/EditViewPage-BqNpC6hO.js.map +0 -1
  109. package/dist/_chunks/EditViewPage-BtkEx339.mjs.map +0 -1
  110. package/dist/_chunks/Field-R5NbffTB.mjs.map +0 -1
  111. package/dist/_chunks/Field-lsPFnAmH.js.map +0 -1
  112. package/dist/_chunks/Form-BHmXSfyy.mjs.map +0 -1
  113. package/dist/_chunks/Form-CcGboku8.js.map +0 -1
  114. package/dist/_chunks/History-Bsud8jwh.js.map +0 -1
  115. package/dist/_chunks/History-ByUPL3T3.mjs.map +0 -1
  116. package/dist/_chunks/ListConfigurationPage-Bm5HACXf.mjs.map +0 -1
  117. package/dist/_chunks/ListConfigurationPage-DiT463qx.js.map +0 -1
  118. package/dist/_chunks/ListViewPage-CsrC9L_d.js.map +0 -1
  119. package/dist/_chunks/ListViewPage-JSyNAAYu.mjs.map +0 -1
  120. package/dist/_chunks/Relations-CghaPv2D.js.map +0 -1
  121. package/dist/_chunks/Relations-u8-37jK0.mjs.map +0 -1
  122. package/dist/_chunks/index-BOZx6IMg.js.map +0 -1
  123. package/dist/_chunks/index-CaE6NG4a.mjs.map +0 -1
  124. package/dist/_chunks/layout-Bx7svTbY.mjs.map +0 -1
  125. package/dist/_chunks/layout-Ciz224q5.js.map +0 -1
  126. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  127. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  128. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  129. package/strapi-server.js +0 -3
@@ -2,15 +2,15 @@
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 reactIntl = require("react-intl");
8
+ const reactRouterDom = require("react-router-dom");
11
9
  const yup = require("yup");
12
10
  const pipe = require("lodash/fp/pipe");
13
11
  const dateFns = require("date-fns");
12
+ const styledComponents = require("styled-components");
13
+ const qs = require("qs");
14
14
  const toolkit = require("@reduxjs/toolkit");
15
15
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
16
16
  function _interopNamespace(e) {
@@ -70,42 +70,6 @@ const useInjectionZone = (area) => {
70
70
  const [page, position] = area.split(".");
71
71
  return contentManagerPlugin.getInjectedComponents(page, position);
72
72
  };
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
73
  const ID = "id";
110
74
  const CREATED_BY_ATTRIBUTE_NAME = "createdBy";
111
75
  const UPDATED_BY_ATTRIBUTE_NAME = "updatedBy";
@@ -215,7 +179,8 @@ const contentManagerApi = strapiAdmin.adminApi.enhanceEndpoints({
215
179
  "Document",
216
180
  "InitialData",
217
181
  "HistoryVersion",
218
- "Relations"
182
+ "Relations",
183
+ "UidAvailability"
219
184
  ]
220
185
  });
221
186
  const documentApi = contentManagerApi.injectEndpoints({
@@ -229,7 +194,12 @@ const documentApi = contentManagerApi.injectEndpoints({
229
194
  params: query
230
195
  }
231
196
  }),
232
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
197
+ invalidatesTags: (_result, error, { model }) => {
198
+ if (error) {
199
+ return [];
200
+ }
201
+ return [{ type: "Document", id: `${model}_LIST` }];
202
+ }
233
203
  }),
234
204
  cloneDocument: builder.mutation({
235
205
  query: ({ model, sourceId, data, params }) => ({
@@ -240,7 +210,10 @@ const documentApi = contentManagerApi.injectEndpoints({
240
210
  params
241
211
  }
242
212
  }),
243
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
213
+ invalidatesTags: (_result, _error, { model }) => [
214
+ { type: "Document", id: `${model}_LIST` },
215
+ { type: "UidAvailability", id: model }
216
+ ]
244
217
  }),
245
218
  /**
246
219
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -257,7 +230,8 @@ const documentApi = contentManagerApi.injectEndpoints({
257
230
  }),
258
231
  invalidatesTags: (result, _error, { model }) => [
259
232
  { type: "Document", id: `${model}_LIST` },
260
- "Relations"
233
+ "Relations",
234
+ { type: "UidAvailability", id: model }
261
235
  ]
262
236
  }),
263
237
  deleteDocument: builder.mutation({
@@ -298,7 +272,8 @@ const documentApi = contentManagerApi.injectEndpoints({
298
272
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
299
273
  },
300
274
  { type: "Document", id: `${model}_LIST` },
301
- "Relations"
275
+ "Relations",
276
+ { type: "UidAvailability", id: model }
302
277
  ];
303
278
  }
304
279
  }),
@@ -316,6 +291,7 @@ const documentApi = contentManagerApi.injectEndpoints({
316
291
  }),
317
292
  providesTags: (result, _error, arg) => {
318
293
  return [
294
+ { type: "Document", id: `ALL_LIST` },
319
295
  { type: "Document", id: `${arg.model}_LIST` },
320
296
  ...result?.results.map(({ documentId }) => ({
321
297
  type: "Document",
@@ -354,6 +330,11 @@ const documentApi = contentManagerApi.injectEndpoints({
354
330
  {
355
331
  type: "Document",
356
332
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
333
+ },
334
+ // Make it easy to invalidate all individual documents queries for a model
335
+ {
336
+ type: "Document",
337
+ id: `${model}_ALL_ITEMS`
357
338
  }
358
339
  ];
359
340
  }
@@ -417,8 +398,21 @@ const documentApi = contentManagerApi.injectEndpoints({
417
398
  type: "Document",
418
399
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
419
400
  },
420
- "Relations"
401
+ "Relations",
402
+ { type: "UidAvailability", id: model }
421
403
  ];
404
+ },
405
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
406
+ const patchResult = dispatch(
407
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
408
+ Object.assign(draft.data, data);
409
+ })
410
+ );
411
+ try {
412
+ await queryFulfilled;
413
+ } catch {
414
+ patchResult.undo();
415
+ }
422
416
  }
423
417
  }),
424
418
  unpublishDocument: builder.mutation({
@@ -488,7 +482,7 @@ const buildValidParams = (query) => {
488
482
  const isBaseQueryError = (error) => {
489
483
  return error.name !== void 0;
490
484
  };
491
- const createYupSchema = (attributes = {}, components = {}) => {
485
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
492
486
  const createModelSchema = (attributes2) => yup__namespace.object().shape(
493
487
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
494
488
  if (DOCUMENT_META_FIELDS.includes(name)) {
@@ -501,7 +495,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
501
495
  addMinValidation,
502
496
  addMaxValidation,
503
497
  addRegexValidation
504
- ].map((fn) => fn(attribute));
498
+ ].map((fn) => fn(attribute, options));
505
499
  const transformSchema = pipe__default.default(...validations);
506
500
  switch (attribute.type) {
507
501
  case "component": {
@@ -550,7 +544,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
550
544
  } else if (Array.isArray(value)) {
551
545
  return yup__namespace.array().of(
552
546
  yup__namespace.object().shape({
553
- id: yup__namespace.string().required()
547
+ id: yup__namespace.number().required()
554
548
  })
555
549
  );
556
550
  } else if (typeof value === "object") {
@@ -602,6 +596,14 @@ const createAttributeSchema = (attribute) => {
602
596
  if (!value || typeof value === "string" && value.length === 0) {
603
597
  return true;
604
598
  }
599
+ if (typeof value === "object") {
600
+ try {
601
+ JSON.stringify(value);
602
+ return true;
603
+ } catch (err) {
604
+ return false;
605
+ }
606
+ }
605
607
  try {
606
608
  JSON.parse(value);
607
609
  return true;
@@ -620,13 +622,7 @@ const createAttributeSchema = (attribute) => {
620
622
  return yup__namespace.mixed();
621
623
  }
622
624
  };
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
- }
625
+ const nullableSchema = (schema) => {
630
626
  return schema?.nullable ? schema.nullable() : (
631
627
  // In some cases '.nullable' will not be available on the schema.
632
628
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -634,7 +630,22 @@ const addRequiredValidation = (attribute) => (schema) => {
634
630
  schema
635
631
  );
636
632
  };
637
- const addMinLengthValidation = (attribute) => (schema) => {
633
+ const addRequiredValidation = (attribute, options) => (schema) => {
634
+ if (options.status === "draft") {
635
+ return nullableSchema(schema);
636
+ }
637
+ if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
638
+ return schema.min(1, strapiAdmin.translatedErrors.required);
639
+ }
640
+ if (attribute.required && attribute.type !== "relation") {
641
+ return schema.required(strapiAdmin.translatedErrors.required);
642
+ }
643
+ return nullableSchema(schema);
644
+ };
645
+ const addMinLengthValidation = (attribute, options) => (schema) => {
646
+ if (options.status === "draft") {
647
+ return schema;
648
+ }
638
649
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
639
650
  return schema.min(attribute.minLength, {
640
651
  ...strapiAdmin.translatedErrors.minLength,
@@ -656,9 +667,31 @@ const addMaxLengthValidation = (attribute) => (schema) => {
656
667
  }
657
668
  return schema;
658
669
  };
659
- const addMinValidation = (attribute) => (schema) => {
670
+ const addMinValidation = (attribute, options) => (schema) => {
660
671
  if ("min" in attribute) {
661
672
  const min = toInteger(attribute.min);
673
+ if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
674
+ if (options.status !== "draft" && !attribute.required && "test" in schema && min) {
675
+ return schema.test(
676
+ "custom-min",
677
+ {
678
+ ...strapiAdmin.translatedErrors.min,
679
+ values: {
680
+ min: attribute.min
681
+ }
682
+ },
683
+ (value) => {
684
+ if (!value) {
685
+ return true;
686
+ }
687
+ if (Array.isArray(value) && value.length === 0) {
688
+ return true;
689
+ }
690
+ return value.length >= min;
691
+ }
692
+ );
693
+ }
694
+ }
662
695
  if ("min" in schema && min) {
663
696
  return schema.min(min, {
664
697
  ...strapiAdmin.translatedErrors.min,
@@ -777,19 +810,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
777
810
  }, {});
778
811
  return componentsByKey;
779
812
  };
780
- const useDocument = (args, opts) => {
813
+ const HOOKS = {
814
+ /**
815
+ * Hook that allows to mutate the displayed headers of the list view table
816
+ * @constant
817
+ * @type {string}
818
+ */
819
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
820
+ /**
821
+ * Hook that allows to mutate the CM's collection types links pre-set filters
822
+ * @constant
823
+ * @type {string}
824
+ */
825
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
826
+ /**
827
+ * Hook that allows to mutate the CM's edit view layout
828
+ * @constant
829
+ * @type {string}
830
+ */
831
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
832
+ /**
833
+ * Hook that allows to mutate the CM's single types links pre-set filters
834
+ * @constant
835
+ * @type {string}
836
+ */
837
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
838
+ };
839
+ const contentTypesApi = contentManagerApi.injectEndpoints({
840
+ endpoints: (builder) => ({
841
+ getContentTypeConfiguration: builder.query({
842
+ query: (uid) => ({
843
+ url: `/content-manager/content-types/${uid}/configuration`,
844
+ method: "GET"
845
+ }),
846
+ transformResponse: (response) => response.data,
847
+ providesTags: (_result, _error, uid) => [
848
+ { type: "ContentTypesConfiguration", id: uid },
849
+ { type: "ContentTypeSettings", id: "LIST" }
850
+ ]
851
+ }),
852
+ getAllContentTypeSettings: builder.query({
853
+ query: () => "/content-manager/content-types-settings",
854
+ transformResponse: (response) => response.data,
855
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
856
+ }),
857
+ updateContentTypeConfiguration: builder.mutation({
858
+ query: ({ uid, ...body }) => ({
859
+ url: `/content-manager/content-types/${uid}/configuration`,
860
+ method: "PUT",
861
+ data: body
862
+ }),
863
+ transformResponse: (response) => response.data,
864
+ invalidatesTags: (_result, _error, { uid }) => [
865
+ { type: "ContentTypesConfiguration", id: uid },
866
+ { type: "ContentTypeSettings", id: "LIST" },
867
+ // Is this necessary?
868
+ { type: "InitialData" }
869
+ ]
870
+ })
871
+ })
872
+ });
873
+ const {
874
+ useGetContentTypeConfigurationQuery,
875
+ useGetAllContentTypeSettingsQuery,
876
+ useUpdateContentTypeConfigurationMutation
877
+ } = contentTypesApi;
878
+ const checkIfAttributeIsDisplayable = (attribute) => {
879
+ const { type } = attribute;
880
+ if (type === "relation") {
881
+ return !attribute.relation.toLowerCase().includes("morph");
882
+ }
883
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
884
+ };
885
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
886
+ if (!mainFieldName) {
887
+ return void 0;
888
+ }
889
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
890
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
891
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
892
+ );
893
+ return {
894
+ name: mainFieldName,
895
+ type: mainFieldType ?? "string"
896
+ };
897
+ };
898
+ const DEFAULT_SETTINGS = {
899
+ bulkable: false,
900
+ filterable: false,
901
+ searchable: false,
902
+ pagination: false,
903
+ defaultSortBy: "",
904
+ defaultSortOrder: "asc",
905
+ mainField: "id",
906
+ pageSize: 10
907
+ };
908
+ const useDocumentLayout = (model) => {
909
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
910
+ const [{ query }] = strapiAdmin.useQueryParams();
911
+ const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
781
912
  const { toggleNotification } = strapiAdmin.useNotification();
782
913
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
914
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
783
915
  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);
916
+ data,
917
+ isLoading: isLoadingConfigs,
918
+ error,
919
+ isFetching: isFetchingConfigs
920
+ } = useGetContentTypeConfigurationQuery(model);
921
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
793
922
  React__namespace.useEffect(() => {
794
923
  if (error) {
795
924
  toggleNotification({
@@ -797,48 +926,263 @@ const useDocument = (args, opts) => {
797
926
  message: formatAPIError(error)
798
927
  });
799
928
  }
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
- }
929
+ }, [error, formatAPIError, toggleNotification]);
930
+ const editLayout = React__namespace.useMemo(
931
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
932
+ layout: [],
933
+ components: {},
934
+ metadatas: {},
935
+ options: {},
936
+ settings: DEFAULT_SETTINGS
823
937
  },
824
- [validationSchema]
938
+ [data, isLoading, schemas, schema, components]
939
+ );
940
+ const listLayout = React__namespace.useMemo(() => {
941
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
942
+ layout: [],
943
+ metadatas: {},
944
+ options: {},
945
+ settings: DEFAULT_SETTINGS
946
+ };
947
+ }, [data, isLoading, schemas, schema, components]);
948
+ const { layout: edit } = React__namespace.useMemo(
949
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
950
+ layout: editLayout,
951
+ query
952
+ }),
953
+ [editLayout, query, runHookWaterfall]
825
954
  );
826
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
827
955
  return {
828
- components,
829
- document: data?.data,
830
- meta: data?.meta,
956
+ error,
831
957
  isLoading,
832
- schema,
833
- validate
958
+ edit,
959
+ list: listLayout
834
960
  };
835
961
  };
836
- const useDoc = () => {
837
- const { id, slug, collectionType, origin } = reactRouterDom.useParams();
838
- const [{ query }] = strapiAdmin.useQueryParams();
839
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
840
- if (!collectionType) {
841
- throw new Error("Could not find collectionType in url params");
962
+ const useDocLayout = () => {
963
+ const { model } = useDoc();
964
+ return useDocumentLayout(model);
965
+ };
966
+ const formatEditLayout = (data, {
967
+ schemas,
968
+ schema,
969
+ components
970
+ }) => {
971
+ let currentPanelIndex = 0;
972
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
973
+ data.contentType.layouts.edit,
974
+ schema?.attributes,
975
+ data.contentType.metadatas,
976
+ { configurations: data.components, schemas: components },
977
+ schemas
978
+ ).reduce((panels, row) => {
979
+ if (row.some((field) => field.type === "dynamiczone")) {
980
+ panels.push([row]);
981
+ currentPanelIndex += 2;
982
+ } else {
983
+ if (!panels[currentPanelIndex]) {
984
+ panels.push([]);
985
+ }
986
+ panels[currentPanelIndex].push(row);
987
+ }
988
+ return panels;
989
+ }, []);
990
+ const componentEditAttributes = Object.entries(data.components).reduce(
991
+ (acc, [uid, configuration]) => {
992
+ acc[uid] = {
993
+ layout: convertEditLayoutToFieldLayouts(
994
+ configuration.layouts.edit,
995
+ components[uid].attributes,
996
+ configuration.metadatas,
997
+ { configurations: data.components, schemas: components }
998
+ ),
999
+ settings: {
1000
+ ...configuration.settings,
1001
+ icon: components[uid].info.icon,
1002
+ displayName: components[uid].info.displayName
1003
+ }
1004
+ };
1005
+ return acc;
1006
+ },
1007
+ {}
1008
+ );
1009
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1010
+ (acc, [attribute, metadata]) => {
1011
+ return {
1012
+ ...acc,
1013
+ [attribute]: metadata.edit
1014
+ };
1015
+ },
1016
+ {}
1017
+ );
1018
+ return {
1019
+ layout: panelledEditAttributes,
1020
+ components: componentEditAttributes,
1021
+ metadatas: editMetadatas,
1022
+ settings: {
1023
+ ...data.contentType.settings,
1024
+ displayName: schema?.info.displayName
1025
+ },
1026
+ options: {
1027
+ ...schema?.options,
1028
+ ...schema?.pluginOptions,
1029
+ ...data.contentType.options
1030
+ }
1031
+ };
1032
+ };
1033
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1034
+ return rows.map(
1035
+ (row) => row.map((field) => {
1036
+ const attribute = attributes[field.name];
1037
+ if (!attribute) {
1038
+ return null;
1039
+ }
1040
+ const { edit: metadata } = metadatas[field.name];
1041
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1042
+ return {
1043
+ attribute,
1044
+ disabled: !metadata.editable,
1045
+ hint: metadata.description,
1046
+ label: metadata.label ?? "",
1047
+ name: field.name,
1048
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1049
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1050
+ schemas,
1051
+ components: components?.schemas ?? {}
1052
+ }),
1053
+ placeholder: metadata.placeholder ?? "",
1054
+ required: attribute.required ?? false,
1055
+ size: field.size,
1056
+ unique: "unique" in attribute ? attribute.unique : false,
1057
+ visible: metadata.visible ?? true,
1058
+ type: attribute.type
1059
+ };
1060
+ }).filter((field) => field !== null)
1061
+ );
1062
+ };
1063
+ const formatListLayout = (data, {
1064
+ schemas,
1065
+ schema,
1066
+ components
1067
+ }) => {
1068
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1069
+ (acc, [attribute, metadata]) => {
1070
+ return {
1071
+ ...acc,
1072
+ [attribute]: metadata.list
1073
+ };
1074
+ },
1075
+ {}
1076
+ );
1077
+ const listAttributes = convertListLayoutToFieldLayouts(
1078
+ data.contentType.layouts.list,
1079
+ schema?.attributes,
1080
+ listMetadatas,
1081
+ { configurations: data.components, schemas: components },
1082
+ schemas
1083
+ );
1084
+ return {
1085
+ layout: listAttributes,
1086
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1087
+ metadatas: listMetadatas,
1088
+ options: {
1089
+ ...schema?.options,
1090
+ ...schema?.pluginOptions,
1091
+ ...data.contentType.options
1092
+ }
1093
+ };
1094
+ };
1095
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1096
+ return columns.map((name) => {
1097
+ const attribute = attributes[name];
1098
+ if (!attribute) {
1099
+ return null;
1100
+ }
1101
+ const metadata = metadatas[name];
1102
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1103
+ return {
1104
+ attribute,
1105
+ label: metadata.label ?? "",
1106
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1107
+ schemas,
1108
+ components: components?.schemas ?? {}
1109
+ }),
1110
+ name,
1111
+ searchable: metadata.searchable ?? true,
1112
+ sortable: metadata.sortable ?? true
1113
+ };
1114
+ }).filter((field) => field !== null);
1115
+ };
1116
+ const useDocument = (args, opts) => {
1117
+ const { toggleNotification } = strapiAdmin.useNotification();
1118
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1119
+ const {
1120
+ currentData: data,
1121
+ isLoading: isLoadingDocument,
1122
+ isFetching: isFetchingDocument,
1123
+ error
1124
+ } = useGetDocumentQuery(args, {
1125
+ ...opts,
1126
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1127
+ });
1128
+ const {
1129
+ components,
1130
+ schema,
1131
+ schemas,
1132
+ isLoading: isLoadingSchema
1133
+ } = useContentTypeSchema(args.model);
1134
+ React__namespace.useEffect(() => {
1135
+ if (error) {
1136
+ toggleNotification({
1137
+ type: "danger",
1138
+ message: formatAPIError(error)
1139
+ });
1140
+ }
1141
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1142
+ const validationSchema = React__namespace.useMemo(() => {
1143
+ if (!schema) {
1144
+ return null;
1145
+ }
1146
+ return createYupSchema(schema.attributes, components);
1147
+ }, [schema, components]);
1148
+ const validate = React__namespace.useCallback(
1149
+ (document) => {
1150
+ if (!validationSchema) {
1151
+ throw new Error(
1152
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1153
+ );
1154
+ }
1155
+ try {
1156
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1157
+ return null;
1158
+ } catch (error2) {
1159
+ if (error2 instanceof yup.ValidationError) {
1160
+ return strapiAdmin.getYupValidationErrors(error2);
1161
+ }
1162
+ throw error2;
1163
+ }
1164
+ },
1165
+ [validationSchema]
1166
+ );
1167
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1168
+ const hasError = !!error;
1169
+ return {
1170
+ components,
1171
+ document: data?.data,
1172
+ meta: data?.meta,
1173
+ isLoading,
1174
+ hasError,
1175
+ schema,
1176
+ schemas,
1177
+ validate
1178
+ };
1179
+ };
1180
+ const useDoc = () => {
1181
+ const { id, slug, collectionType, origin } = reactRouterDom.useParams();
1182
+ const [{ query }] = strapiAdmin.useQueryParams();
1183
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1184
+ if (!collectionType) {
1185
+ throw new Error("Could not find collectionType in url params");
842
1186
  }
843
1187
  if (!slug) {
844
1188
  throw new Error("Could not find model in url params");
@@ -855,6 +1199,45 @@ const useDoc = () => {
855
1199
  )
856
1200
  };
857
1201
  };
1202
+ const useContentManagerContext = () => {
1203
+ const {
1204
+ collectionType,
1205
+ model,
1206
+ id,
1207
+ components,
1208
+ isLoading: isLoadingDoc,
1209
+ schema,
1210
+ schemas
1211
+ } = useDoc();
1212
+ const layout = useDocumentLayout(model);
1213
+ const form = strapiAdmin.useForm("useContentManagerContext", (state) => state);
1214
+ const isSingleType = collectionType === SINGLE_TYPES;
1215
+ const slug = model;
1216
+ const isCreatingEntry = id === "create";
1217
+ useContentTypeSchema();
1218
+ const isLoading = isLoadingDoc || layout.isLoading;
1219
+ const error = layout.error;
1220
+ return {
1221
+ error,
1222
+ isLoading,
1223
+ // Base metadata
1224
+ model,
1225
+ collectionType,
1226
+ id,
1227
+ slug,
1228
+ isCreatingEntry,
1229
+ isSingleType,
1230
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1231
+ // All schema infos
1232
+ components,
1233
+ contentType: schema,
1234
+ contentTypes: schemas,
1235
+ // Form state
1236
+ form,
1237
+ // layout infos
1238
+ layout
1239
+ };
1240
+ };
858
1241
  const prefixPluginTranslations = (trad, pluginId) => {
859
1242
  if (!pluginId) {
860
1243
  throw new TypeError("pluginId can't be empty");
@@ -874,6 +1257,8 @@ const useDocumentActions = () => {
874
1257
  const { formatMessage } = reactIntl.useIntl();
875
1258
  const { trackUsage } = strapiAdmin.useTracking();
876
1259
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1260
+ const navigate = reactRouterDom.useNavigate();
1261
+ const setCurrentStep = strapiAdmin.useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
877
1262
  const [deleteDocument] = useDeleteDocumentMutation();
878
1263
  const _delete = React__namespace.useCallback(
879
1264
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1188,6 +1573,7 @@ const useDocumentActions = () => {
1188
1573
  defaultMessage: "Saved document"
1189
1574
  })
1190
1575
  });
1576
+ setCurrentStep("contentManager.success");
1191
1577
  return res.data;
1192
1578
  } catch (err) {
1193
1579
  toggleNotification({
@@ -1209,7 +1595,6 @@ const useDocumentActions = () => {
1209
1595
  sourceId
1210
1596
  });
1211
1597
  if ("error" in res) {
1212
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1213
1598
  return { error: res.error };
1214
1599
  }
1215
1600
  toggleNotification({
@@ -1228,7 +1613,7 @@ const useDocumentActions = () => {
1228
1613
  throw err;
1229
1614
  }
1230
1615
  },
1231
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1616
+ [autoCloneDocument, formatMessage, toggleNotification]
1232
1617
  );
1233
1618
  const [cloneDocument] = useCloneDocumentMutation();
1234
1619
  const clone = React__namespace.useCallback(
@@ -1254,6 +1639,7 @@ const useDocumentActions = () => {
1254
1639
  defaultMessage: "Cloned document"
1255
1640
  })
1256
1641
  });
1642
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1257
1643
  return res.data;
1258
1644
  } catch (err) {
1259
1645
  toggleNotification({
@@ -1264,7 +1650,7 @@ const useDocumentActions = () => {
1264
1650
  throw err;
1265
1651
  }
1266
1652
  },
1267
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1653
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1268
1654
  );
1269
1655
  const [getDoc] = useLazyGetDocumentQuery();
1270
1656
  const getDocument = React__namespace.useCallback(
@@ -1290,7 +1676,7 @@ const useDocumentActions = () => {
1290
1676
  };
1291
1677
  };
1292
1678
  const ProtectedHistoryPage = React.lazy(
1293
- () => Promise.resolve().then(() => require("./History-Bsud8jwh.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1679
+ () => Promise.resolve().then(() => require("./History-C_uSGzO5.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1294
1680
  );
1295
1681
  const routes$1 = [
1296
1682
  {
@@ -1303,31 +1689,31 @@ const routes$1 = [
1303
1689
  }
1304
1690
  ];
1305
1691
  const ProtectedEditViewPage = React.lazy(
1306
- () => Promise.resolve().then(() => require("./EditViewPage-BqNpC6hO.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1692
+ () => Promise.resolve().then(() => require("./EditViewPage-CUS2EAhB.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1307
1693
  );
1308
1694
  const ProtectedListViewPage = React.lazy(
1309
- () => Promise.resolve().then(() => require("./ListViewPage-CsrC9L_d.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1695
+ () => Promise.resolve().then(() => require("./ListViewPage-DHgHD8Xg.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1310
1696
  );
1311
1697
  const ProtectedListConfiguration = React.lazy(
1312
- () => Promise.resolve().then(() => require("./ListConfigurationPage-DiT463qx.js")).then((mod) => ({
1698
+ () => Promise.resolve().then(() => require("./ListConfigurationPage-nyuP7OSy.js")).then((mod) => ({
1313
1699
  default: mod.ProtectedListConfiguration
1314
1700
  }))
1315
1701
  );
1316
1702
  const ProtectedEditConfigurationPage = React.lazy(
1317
- () => Promise.resolve().then(() => require("./EditConfigurationPage-DOmfCEMo.js")).then((mod) => ({
1703
+ () => Promise.resolve().then(() => require("./EditConfigurationPage-DITVliEI.js")).then((mod) => ({
1318
1704
  default: mod.ProtectedEditConfigurationPage
1319
1705
  }))
1320
1706
  );
1321
1707
  const ProtectedComponentConfigurationPage = React.lazy(
1322
- () => Promise.resolve().then(() => require("./ComponentConfigurationPage-BvHtG7uH.js")).then((mod) => ({
1708
+ () => Promise.resolve().then(() => require("./ComponentConfigurationPage-DP7AC0UU.js")).then((mod) => ({
1323
1709
  default: mod.ProtectedComponentConfigurationPage
1324
1710
  }))
1325
1711
  );
1326
1712
  const NoPermissions = React.lazy(
1327
- () => Promise.resolve().then(() => require("./NoPermissionsPage-CdHNJtEf.js")).then((mod) => ({ default: mod.NoPermissions }))
1713
+ () => Promise.resolve().then(() => require("./NoPermissionsPage-zwIZydDI.js")).then((mod) => ({ default: mod.NoPermissions }))
1328
1714
  );
1329
1715
  const NoContentType = React.lazy(
1330
- () => Promise.resolve().then(() => require("./NoContentTypePage-Bsvng4II.js")).then((mod) => ({ default: mod.NoContentType }))
1716
+ () => Promise.resolve().then(() => require("./NoContentTypePage-CDUKdZ7d.js")).then((mod) => ({ default: mod.NoContentType }))
1331
1717
  );
1332
1718
  const CollectionTypePages = () => {
1333
1719
  const { collectionType } = reactRouterDom.useParams();
@@ -1441,12 +1827,14 @@ const DocumentActionButton = (action) => {
1441
1827
  /* @__PURE__ */ jsxRuntime.jsx(
1442
1828
  designSystem.Button,
1443
1829
  {
1444
- flex: 1,
1830
+ flex: "auto",
1445
1831
  startIcon: action.icon,
1446
1832
  disabled: action.disabled,
1447
1833
  onClick: handleClick(action),
1448
1834
  justifyContent: "center",
1449
1835
  variant: action.variant || "default",
1836
+ paddingTop: "7px",
1837
+ paddingBottom: "7px",
1450
1838
  children: action.label
1451
1839
  }
1452
1840
  ),
@@ -1454,7 +1842,7 @@ const DocumentActionButton = (action) => {
1454
1842
  DocumentActionConfirmDialog,
1455
1843
  {
1456
1844
  ...action.dialog,
1457
- variant: action.variant,
1845
+ variant: action.dialog?.variant ?? action.variant,
1458
1846
  isOpen: dialogId === action.id,
1459
1847
  onClose: handleClose
1460
1848
  }
@@ -1511,9 +1899,9 @@ const DocumentActionsMenu = ({
1511
1899
  disabled: isDisabled,
1512
1900
  size: "S",
1513
1901
  endIcon: null,
1514
- paddingTop: "7px",
1515
- paddingLeft: "9px",
1516
- paddingRight: "9px",
1902
+ paddingTop: "4px",
1903
+ paddingLeft: "7px",
1904
+ paddingRight: "7px",
1517
1905
  variant,
1518
1906
  children: [
1519
1907
  /* @__PURE__ */ jsxRuntime.jsx(Icons.More, { "aria-hidden": true, focusable: false }),
@@ -1524,7 +1912,7 @@ const DocumentActionsMenu = ({
1524
1912
  ]
1525
1913
  }
1526
1914
  ),
1527
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1915
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1528
1916
  actions2.map((action) => {
1529
1917
  return /* @__PURE__ */ jsxRuntime.jsx(
1530
1918
  designSystem.Menu.Item,
@@ -1533,10 +1921,25 @@ const DocumentActionsMenu = ({
1533
1921
  onSelect: handleClick(action),
1534
1922
  display: "block",
1535
1923
  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
- ] }),
1924
+ /* @__PURE__ */ jsxRuntime.jsxs(
1925
+ designSystem.Flex,
1926
+ {
1927
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1928
+ gap: 2,
1929
+ tag: "span",
1930
+ children: [
1931
+ /* @__PURE__ */ jsxRuntime.jsx(
1932
+ designSystem.Flex,
1933
+ {
1934
+ tag: "span",
1935
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1936
+ children: action.icon
1937
+ }
1938
+ ),
1939
+ action.label
1940
+ ]
1941
+ }
1942
+ ),
1540
1943
  action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsxRuntime.jsx(
1541
1944
  designSystem.Flex,
1542
1945
  {
@@ -1633,11 +2036,11 @@ const DocumentActionConfirmDialog = ({
1633
2036
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
1634
2037
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: content }),
1635
2038
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
1636
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", children: formatMessage({
2039
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1637
2040
  id: "app.components.Button.cancel",
1638
2041
  defaultMessage: "Cancel"
1639
2042
  }) }) }),
1640
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, children: formatMessage({
2043
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1641
2044
  id: "app.components.Button.confirm",
1642
2045
  defaultMessage: "Confirm"
1643
2046
  }) })
@@ -1660,8 +2063,8 @@ const DocumentActionModal = ({
1660
2063
  };
1661
2064
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
1662
2065
  /* @__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 })
2066
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: Content }),
2067
+ typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer
1665
2068
  ] }) });
1666
2069
  };
1667
2070
  const PublishAction$1 = ({
@@ -1676,13 +2079,17 @@ const PublishAction$1 = ({
1676
2079
  const navigate = reactRouterDom.useNavigate();
1677
2080
  const { toggleNotification } = strapiAdmin.useNotification();
1678
2081
  const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
2082
+ const isListView = reactRouterDom.useMatch(LIST_PATH) !== null;
1679
2083
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
1680
2084
  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
- );
2085
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1685
2086
  const { publish } = useDocumentActions();
2087
+ const [
2088
+ countDraftRelations,
2089
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2090
+ ] = useLazyGetDraftRelationCountQuery();
2091
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React__namespace.useState(0);
2092
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React__namespace.useState(0);
1686
2093
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1687
2094
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1688
2095
  const modified = strapiAdmin.useForm("PublishAction", ({ modified: modified2 }) => modified2);
@@ -1691,63 +2098,143 @@ const PublishAction$1 = ({
1691
2098
  const validate = strapiAdmin.useForm("PublishAction", (state) => state.validate);
1692
2099
  const setErrors = strapiAdmin.useForm("PublishAction", (state) => state.setErrors);
1693
2100
  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
1704
- * - the document is already published & not modified
1705
- * - the document is being created & not modified
1706
- * - 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
- */
1710
- disabled: isCloning || isSubmitting || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
1711
- label: formatMessage({
1712
- id: "app.utils.publish",
1713
- defaultMessage: "Publish"
1714
- }),
1715
- 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;
2101
+ React__namespace.useEffect(() => {
2102
+ if (isErrorDraftRelations) {
2103
+ toggleNotification({
2104
+ type: "danger",
2105
+ message: formatMessage({
2106
+ id: getTranslation("error.records.fetch-draft-relatons"),
2107
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2108
+ })
2109
+ });
2110
+ }
2111
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2112
+ React__namespace.useEffect(() => {
2113
+ const localDraftRelations = /* @__PURE__ */ new Set();
2114
+ const extractDraftRelations = (data) => {
2115
+ const relations = data.connect || [];
2116
+ relations.forEach((relation) => {
2117
+ if (relation.status === "draft") {
2118
+ localDraftRelations.add(relation.id);
1728
2119
  }
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));
2120
+ });
2121
+ };
2122
+ const traverseAndExtract = (data) => {
2123
+ Object.entries(data).forEach(([key, value]) => {
2124
+ if (key === "connect" && Array.isArray(value)) {
2125
+ extractDraftRelations({ connect: value });
2126
+ } else if (typeof value === "object" && value !== null) {
2127
+ traverseAndExtract(value);
1745
2128
  }
1746
- } finally {
1747
- setSubmitting(false);
2129
+ });
2130
+ };
2131
+ if (!documentId || modified) {
2132
+ traverseAndExtract(formValues);
2133
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2134
+ }
2135
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2136
+ React__namespace.useEffect(() => {
2137
+ if (!document || !document.documentId || isListView) {
2138
+ return;
2139
+ }
2140
+ const fetchDraftRelationsCount = async () => {
2141
+ const { data, error } = await countDraftRelations({
2142
+ collectionType,
2143
+ model,
2144
+ documentId,
2145
+ params
2146
+ });
2147
+ if (error) {
2148
+ throw error;
1748
2149
  }
2150
+ if (data) {
2151
+ setServerCountOfDraftRelations(data.data);
2152
+ }
2153
+ };
2154
+ fetchDraftRelationsCount();
2155
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
2156
+ const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
2157
+ if (!schema?.options?.draftAndPublish) {
2158
+ return null;
2159
+ }
2160
+ const performPublish = async () => {
2161
+ setSubmitting(true);
2162
+ try {
2163
+ const { errors } = await validate();
2164
+ if (errors) {
2165
+ toggleNotification({
2166
+ type: "danger",
2167
+ message: formatMessage({
2168
+ id: "content-manager.validation.error",
2169
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2170
+ })
2171
+ });
2172
+ return;
2173
+ }
2174
+ const res = await publish(
2175
+ {
2176
+ collectionType,
2177
+ model,
2178
+ documentId,
2179
+ params
2180
+ },
2181
+ formValues
2182
+ );
2183
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2184
+ navigate({
2185
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2186
+ search: rawQuery
2187
+ });
2188
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2189
+ setErrors(formatValidationErrors(res.error));
2190
+ }
2191
+ } finally {
2192
+ setSubmitting(false);
1749
2193
  }
1750
2194
  };
2195
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2196
+ const enableDraftRelationsCount = false;
2197
+ const hasDraftRelations = enableDraftRelationsCount;
2198
+ return {
2199
+ /**
2200
+ * Disabled when:
2201
+ * - currently if you're cloning a document we don't support publish & clone at the same time.
2202
+ * - the form is submitting
2203
+ * - the active tab is the published tab
2204
+ * - the document is already published & not modified
2205
+ * - the document is being created & not modified
2206
+ * - the user doesn't have the permission to publish
2207
+ */
2208
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
2209
+ label: formatMessage({
2210
+ id: "app.utils.publish",
2211
+ defaultMessage: "Publish"
2212
+ }),
2213
+ onClick: async () => {
2214
+ await performPublish();
2215
+ },
2216
+ dialog: hasDraftRelations ? {
2217
+ type: "dialog",
2218
+ variant: "danger",
2219
+ footer: null,
2220
+ title: formatMessage({
2221
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2222
+ defaultMessage: "Confirmation"
2223
+ }),
2224
+ content: formatMessage(
2225
+ {
2226
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2227
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2228
+ },
2229
+ {
2230
+ count: totalDraftRelations
2231
+ }
2232
+ ),
2233
+ onConfirm: async () => {
2234
+ await performPublish();
2235
+ }
2236
+ } : void 0
2237
+ };
1751
2238
  };
1752
2239
  PublishAction$1.type = "publish";
1753
2240
  const UpdateAction = ({
@@ -1762,10 +2249,6 @@ const UpdateAction = ({
1762
2249
  const cloneMatch = reactRouterDom.useMatch(CLONE_PATH);
1763
2250
  const isCloning = cloneMatch !== null;
1764
2251
  const { formatMessage } = reactIntl.useIntl();
1765
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1766
- canCreate: canCreate2,
1767
- canUpdate: canUpdate2
1768
- }));
1769
2252
  const { create, update, clone } = useDocumentActions();
1770
2253
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1771
2254
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
@@ -1782,10 +2265,8 @@ const UpdateAction = ({
1782
2265
  * - the form is submitting
1783
2266
  * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1784
2267
  * - 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
2268
  */
1788
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
2269
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1789
2270
  label: formatMessage({
1790
2271
  id: "content-manager.containers.Edit.save",
1791
2272
  defaultMessage: "Save"
@@ -1793,16 +2274,18 @@ const UpdateAction = ({
1793
2274
  onClick: async () => {
1794
2275
  setSubmitting(true);
1795
2276
  try {
1796
- const { errors } = await validate();
1797
- if (errors) {
1798
- toggleNotification({
1799
- type: "danger",
1800
- message: formatMessage({
1801
- id: "content-manager.validation.error",
1802
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1803
- })
1804
- });
1805
- return;
2277
+ if (activeTab !== "draft") {
2278
+ const { errors } = await validate();
2279
+ if (errors) {
2280
+ toggleNotification({
2281
+ type: "danger",
2282
+ message: formatMessage({
2283
+ id: "content-manager.validation.error",
2284
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2285
+ })
2286
+ });
2287
+ return;
2288
+ }
1806
2289
  }
1807
2290
  if (isCloning) {
1808
2291
  const res = await clone(
@@ -1814,10 +2297,13 @@ const UpdateAction = ({
1814
2297
  document
1815
2298
  );
1816
2299
  if ("data" in res) {
1817
- navigate({
1818
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1819
- search: rawQuery
1820
- });
2300
+ navigate(
2301
+ {
2302
+ pathname: `../${res.data.documentId}`,
2303
+ search: rawQuery
2304
+ },
2305
+ { relative: "path" }
2306
+ );
1821
2307
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1822
2308
  setErrors(formatValidationErrors(res.error));
1823
2309
  }
@@ -1847,10 +2333,10 @@ const UpdateAction = ({
1847
2333
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1848
2334
  navigate(
1849
2335
  {
1850
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2336
+ pathname: `../${res.data.documentId}`,
1851
2337
  search: rawQuery
1852
2338
  },
1853
- { replace: true }
2339
+ { replace: true, relative: "path" }
1854
2340
  );
1855
2341
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1856
2342
  setErrors(formatValidationErrors(res.error));
@@ -1895,7 +2381,7 @@ const UnpublishAction$1 = ({
1895
2381
  id: "app.utils.unpublish",
1896
2382
  defaultMessage: "Unpublish"
1897
2383
  }),
1898
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2384
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
1899
2385
  onClick: async () => {
1900
2386
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1901
2387
  if (!documentId) {
@@ -2007,7 +2493,7 @@ const DiscardAction = ({
2007
2493
  id: "content-manager.actions.discard.label",
2008
2494
  defaultMessage: "Discard changes"
2009
2495
  }),
2010
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2496
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
2011
2497
  position: ["panel", "table-row"],
2012
2498
  variant: "danger",
2013
2499
  dialog: {
@@ -2035,11 +2521,6 @@ const DiscardAction = ({
2035
2521
  };
2036
2522
  };
2037
2523
  DiscardAction.type = "discard";
2038
- const StyledCrossCircle = styledComponents.styled(Icons.CrossCircle)`
2039
- path {
2040
- fill: currentColor;
2041
- }
2042
- `;
2043
2524
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2044
2525
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2045
2526
  const RelativeTime = React__namespace.forwardRef(
@@ -2087,7 +2568,7 @@ const getDisplayName = ({
2087
2568
  };
2088
2569
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2089
2570
  const DocumentStatus = ({ status = "draft", ...restProps }) => {
2090
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2571
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2091
2572
  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
2573
  };
2093
2574
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
@@ -2097,23 +2578,13 @@ const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2097
2578
  id: "content-manager.containers.edit.title.new",
2098
2579
  defaultMessage: "Create an entry"
2099
2580
  }) : documentTitle;
2100
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 8, paddingBottom: 4, gap: 3, children: [
2581
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2101
2582
  /* @__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
2583
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2584
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2585
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2586
+ ] }),
2587
+ status ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2117
2588
  ] });
2118
2589
  };
2119
2590
  const HeaderToolbar = () => {
@@ -2280,25 +2751,77 @@ const Information = ({ activeTab }) => {
2280
2751
  );
2281
2752
  };
2282
2753
  const HeaderActions = ({ actions: actions2 }) => {
2283
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: actions2.map((action) => {
2284
- if ("options" in action) {
2754
+ const [dialogId, setDialogId] = React__namespace.useState(null);
2755
+ const handleClick = (action) => async (e) => {
2756
+ if (!("options" in action)) {
2757
+ const { onClick = () => false, dialog, id } = action;
2758
+ const muteDialog = await onClick(e);
2759
+ if (dialog && !muteDialog) {
2760
+ e.preventDefault();
2761
+ setDialogId(id);
2762
+ }
2763
+ }
2764
+ };
2765
+ const handleClose = () => {
2766
+ setDialogId(null);
2767
+ };
2768
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, children: actions2.map((action) => {
2769
+ if (action.options) {
2285
2770
  return /* @__PURE__ */ jsxRuntime.jsx(
2286
2771
  designSystem.SingleSelect,
2287
2772
  {
2288
2773
  size: "S",
2289
- disabled: action.disabled,
2290
- "aria-label": action.label,
2291
2774
  onChange: action.onSelect,
2292
- value: action.value,
2775
+ "aria-label": action.label,
2776
+ ...action,
2293
2777
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { ...option, children: label }, option.value))
2294
2778
  },
2295
2779
  action.id
2296
2780
  );
2297
2781
  } else {
2298
- return null;
2782
+ if (action.type === "icon") {
2783
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
2784
+ /* @__PURE__ */ jsxRuntime.jsx(
2785
+ designSystem.IconButton,
2786
+ {
2787
+ disabled: action.disabled,
2788
+ label: action.label,
2789
+ size: "S",
2790
+ onClick: handleClick(action),
2791
+ children: action.icon
2792
+ }
2793
+ ),
2794
+ action.dialog ? /* @__PURE__ */ jsxRuntime.jsx(
2795
+ HeaderActionDialog,
2796
+ {
2797
+ ...action.dialog,
2798
+ isOpen: dialogId === action.id,
2799
+ onClose: handleClose
2800
+ }
2801
+ ) : null
2802
+ ] }, action.id);
2803
+ }
2299
2804
  }
2300
2805
  }) });
2301
2806
  };
2807
+ const HeaderActionDialog = ({
2808
+ onClose,
2809
+ onCancel,
2810
+ title,
2811
+ content: Content,
2812
+ isOpen
2813
+ }) => {
2814
+ const handleClose = async () => {
2815
+ if (onCancel) {
2816
+ await onCancel();
2817
+ }
2818
+ onClose();
2819
+ };
2820
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2821
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2822
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content
2823
+ ] }) });
2824
+ };
2302
2825
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2303
2826
  const navigate = reactRouterDom.useNavigate();
2304
2827
  const { formatMessage } = reactIntl.useIntl();
@@ -2339,12 +2862,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2339
2862
  const { delete: deleteAction } = useDocumentActions();
2340
2863
  const { toggleNotification } = strapiAdmin.useNotification();
2341
2864
  const setSubmitting = strapiAdmin.useForm("DeleteAction", (state) => state.setSubmitting);
2865
+ const isLocalized = document?.locale != null;
2342
2866
  return {
2343
2867
  disabled: !canDelete || !document,
2344
- label: formatMessage({
2345
- id: "content-manager.actions.delete.label",
2346
- defaultMessage: "Delete document"
2347
- }),
2868
+ label: formatMessage(
2869
+ {
2870
+ id: "content-manager.actions.delete.label",
2871
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2872
+ },
2873
+ { isLocalized }
2874
+ ),
2348
2875
  icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2349
2876
  dialog: {
2350
2877
  type: "dialog",
@@ -2375,428 +2902,126 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2375
2902
  }),
2376
2903
  type: "danger"
2377
2904
  });
2378
- return;
2379
- }
2380
- const res = await deleteAction({
2381
- 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
2905
+ return;
2906
+ }
2907
+ const res = await deleteAction({
2908
+ documentId,
2909
+ model,
2910
+ collectionType,
2911
+ params: {
2912
+ locale: "*"
2913
+ }
2914
+ });
2915
+ if (!("error" in res)) {
2916
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2917
+ }
2918
+ } finally {
2919
+ if (!listViewPathMatch) {
2920
+ setSubmitting(false);
2921
+ }
2687
2922
  }
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
2923
+ }
2709
2924
  },
2710
- options: {
2711
- ...schema?.options,
2712
- ...schema?.pluginOptions,
2713
- ...data.contentType.options
2714
- }
2925
+ variant: "danger",
2926
+ position: ["header", "table-row"]
2715
2927
  };
2716
2928
  };
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
- );
2929
+ DeleteAction$1.type = "delete";
2930
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2931
+ const Panels = () => {
2932
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2933
+ const [
2934
+ {
2935
+ query: { status }
2936
+ }
2937
+ ] = strapiAdmin.useQueryParams({
2938
+ status: "draft"
2939
+ });
2940
+ const { model, id, document, meta, collectionType } = useDoc();
2941
+ const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2942
+ const props = {
2943
+ activeTab: status,
2944
+ model,
2945
+ documentId: id,
2946
+ document: isCloning ? void 0 : document,
2947
+ meta: isCloning ? void 0 : meta,
2948
+ collectionType
2949
+ };
2950
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2951
+ strapiAdmin.DescriptionComponentRenderer,
2952
+ {
2953
+ props,
2954
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2955
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2956
+ }
2957
+ ) });
2746
2958
  };
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
- );
2959
+ const ActionsPanel = () => {
2960
+ const { formatMessage } = reactIntl.useIntl();
2768
2961
  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
- }
2962
+ title: formatMessage({
2963
+ id: "content-manager.containers.edit.panels.default.title",
2964
+ defaultMessage: "Entry"
2965
+ }),
2966
+ content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2777
2967
  };
2778
2968
  };
2779
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2780
- return columns.map((name) => {
2781
- const attribute = attributes[name];
2782
- if (!attribute) {
2783
- return null;
2969
+ ActionsPanel.type = "actions";
2970
+ const ActionsPanelContent = () => {
2971
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2972
+ const [
2973
+ {
2974
+ query: { status = "draft" }
2784
2975
  }
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);
2976
+ ] = strapiAdmin.useQueryParams();
2977
+ const { model, id, document, meta, collectionType } = useDoc();
2978
+ const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
2979
+ const props = {
2980
+ activeTab: status,
2981
+ model,
2982
+ documentId: id,
2983
+ document: isCloning ? void 0 : document,
2984
+ meta: isCloning ? void 0 : meta,
2985
+ collectionType
2986
+ };
2987
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
2988
+ /* @__PURE__ */ jsxRuntime.jsx(
2989
+ strapiAdmin.DescriptionComponentRenderer,
2990
+ {
2991
+ props,
2992
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
2993
+ children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
2994
+ }
2995
+ ),
2996
+ /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
2997
+ ] });
2799
2998
  };
2999
+ const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
3000
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3001
+ designSystem.Flex,
3002
+ {
3003
+ ref,
3004
+ tag: "aside",
3005
+ "aria-labelledby": "additional-information",
3006
+ background: "neutral0",
3007
+ borderColor: "neutral150",
3008
+ hasRadius: true,
3009
+ paddingBottom: 4,
3010
+ paddingLeft: 4,
3011
+ paddingRight: 4,
3012
+ paddingTop: 4,
3013
+ shadow: "tableShadow",
3014
+ gap: 3,
3015
+ direction: "column",
3016
+ justifyContent: "stretch",
3017
+ alignItems: "flex-start",
3018
+ children: [
3019
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
3020
+ children
3021
+ ]
3022
+ }
3023
+ );
3024
+ });
2800
3025
  const ConfirmBulkActionDialog = ({
2801
3026
  onToggleDialog,
2802
3027
  isOpen = false,
@@ -2804,7 +3029,7 @@ const ConfirmBulkActionDialog = ({
2804
3029
  endAction
2805
3030
  }) => {
2806
3031
  const { formatMessage } = reactIntl.useIntl();
2807
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { onOpenChange: onToggleDialog, open: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
3032
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2808
3033
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage({
2809
3034
  id: "app.components.ConfirmDialog.title",
2810
3035
  defaultMessage: "Confirmation"
@@ -2835,6 +3060,7 @@ const ConfirmDialogPublishAll = ({
2835
3060
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler(getTranslation);
2836
3061
  const { model, schema } = useDoc();
2837
3062
  const [{ query }] = strapiAdmin.useQueryParams();
3063
+ const enableDraftRelationsCount = false;
2838
3064
  const {
2839
3065
  data: countDraftRelations = 0,
2840
3066
  isLoading,
@@ -2846,7 +3072,7 @@ const ConfirmDialogPublishAll = ({
2846
3072
  locale: query?.plugins?.i18n?.locale
2847
3073
  },
2848
3074
  {
2849
- skip: selectedEntries.length === 0
3075
+ skip: !enableDraftRelationsCount
2850
3076
  }
2851
3077
  );
2852
3078
  React__namespace.useEffect(() => {
@@ -3031,7 +3257,7 @@ const SelectedEntriesTableContent = ({
3031
3257
  status: row.status
3032
3258
  }
3033
3259
  ) }),
3034
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
3260
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(
3035
3261
  designSystem.IconButton,
3036
3262
  {
3037
3263
  tag: reactRouterDom.Link,
@@ -3054,9 +3280,10 @@ const SelectedEntriesTableContent = ({
3054
3280
  ),
3055
3281
  target: "_blank",
3056
3282
  marginLeft: "auto",
3057
- children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {})
3283
+ variant: "ghost",
3284
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, { width: "1.6rem", height: "1.6rem" })
3058
3285
  }
3059
- ) })
3286
+ ) }) })
3060
3287
  ] }, row.id)) })
3061
3288
  ] });
3062
3289
  };
@@ -3093,7 +3320,13 @@ const SelectedEntriesModalContent = ({
3093
3320
  );
3094
3321
  const { rows, validationErrors } = React__namespace.useMemo(() => {
3095
3322
  if (data.length > 0 && schema) {
3096
- const validate = createYupSchema(schema.attributes, components);
3323
+ const validate = createYupSchema(
3324
+ schema.attributes,
3325
+ components,
3326
+ // Since this is the "Publish" action, the validation
3327
+ // schema must enforce the rules for published entities
3328
+ { status: "published" }
3329
+ );
3097
3330
  const validationErrors2 = {};
3098
3331
  const rows2 = data.map((entry) => {
3099
3332
  try {
@@ -3443,7 +3676,7 @@ const TableActions = ({ document }) => {
3443
3676
  strapiAdmin.DescriptionComponentRenderer,
3444
3677
  {
3445
3678
  props,
3446
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3679
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3447
3680
  children: (actions2) => {
3448
3681
  const tableRowActions = actions2.filter((action) => {
3449
3682
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3554,7 +3787,7 @@ const CloneAction = ({ model, documentId }) => {
3554
3787
  }),
3555
3788
  content: /* @__PURE__ */ jsxRuntime.jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3556
3789
  footer: ({ onClose }) => {
3557
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
3790
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
3558
3791
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3559
3792
  id: "cancel",
3560
3793
  defaultMessage: "Cancel"
@@ -3595,8 +3828,7 @@ class ContentManagerPlugin {
3595
3828
  documentActions = [
3596
3829
  ...DEFAULT_ACTIONS,
3597
3830
  ...DEFAULT_TABLE_ROW_ACTIONS,
3598
- ...DEFAULT_HEADER_ACTIONS,
3599
- HistoryAction
3831
+ ...DEFAULT_HEADER_ACTIONS
3600
3832
  ];
3601
3833
  editViewSidePanels = [ActionsPanel];
3602
3834
  headerActions = [];
@@ -3685,6 +3917,52 @@ const getPrintableType = (value) => {
3685
3917
  }
3686
3918
  return nativeType;
3687
3919
  };
3920
+ const HistoryAction = ({ model, document }) => {
3921
+ const { formatMessage } = reactIntl.useIntl();
3922
+ const [{ query }] = strapiAdmin.useQueryParams();
3923
+ const navigate = reactRouterDom.useNavigate();
3924
+ const pluginsQueryParams = qs.stringify({ plugins: query.plugins }, { encode: false });
3925
+ if (!window.strapi.features.isEnabled("cms-content-history")) {
3926
+ return null;
3927
+ }
3928
+ return {
3929
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ClockCounterClockwise, {}),
3930
+ label: formatMessage({
3931
+ id: "content-manager.history.document-action",
3932
+ defaultMessage: "Content History"
3933
+ }),
3934
+ onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
3935
+ disabled: (
3936
+ /**
3937
+ * The user is creating a new document.
3938
+ * It hasn't been saved yet, so there's no history to go to
3939
+ */
3940
+ !document || /**
3941
+ * The document has been created but the current dimension has never been saved.
3942
+ * For example, the user is creating a new locale in an existing document,
3943
+ * so there's no history for the document in that locale
3944
+ */
3945
+ !document.id || /**
3946
+ * History is only available for content types created by the user.
3947
+ * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
3948
+ * which start with `admin::` or `plugin::`
3949
+ */
3950
+ !model.startsWith("api::")
3951
+ ),
3952
+ position: "header"
3953
+ };
3954
+ };
3955
+ HistoryAction.type = "history";
3956
+ const historyAdmin = {
3957
+ bootstrap(app) {
3958
+ const { addDocumentAction } = app.getPlugin("content-manager").apis;
3959
+ addDocumentAction((actions2) => {
3960
+ const indexOfDeleteAction = actions2.findIndex((action) => action.type === "delete");
3961
+ actions2.splice(indexOfDeleteAction, 0, HistoryAction);
3962
+ return actions2;
3963
+ });
3964
+ }
3965
+ };
3688
3966
  const initialState = {
3689
3967
  collectionTypeLinks: [],
3690
3968
  components: [],
@@ -3740,7 +4018,7 @@ const index = {
3740
4018
  app.router.addRoute({
3741
4019
  path: "content-manager/*",
3742
4020
  lazy: async () => {
3743
- const { Layout } = await Promise.resolve().then(() => require("./layout-Ciz224q5.js"));
4021
+ const { Layout } = await Promise.resolve().then(() => require("./layout-UNWstw_s.js"));
3744
4022
  return {
3745
4023
  Component: Layout
3746
4024
  };
@@ -3749,10 +4027,15 @@ const index = {
3749
4027
  });
3750
4028
  app.registerPlugin(cm.config);
3751
4029
  },
4030
+ bootstrap(app) {
4031
+ if (typeof historyAdmin.bootstrap === "function") {
4032
+ historyAdmin.bootstrap(app);
4033
+ }
4034
+ },
3752
4035
  async registerTrads({ locales }) {
3753
4036
  const importedTrads = await Promise.all(
3754
4037
  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 }) => {
4038
+ 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-BlhnxQfj.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 }) => {
3756
4039
  return {
3757
4040
  data: prefixPluginTranslations(data, PLUGIN_ID),
3758
4041
  locale
@@ -3770,6 +4053,7 @@ const index = {
3770
4053
  };
3771
4054
  exports.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD;
3772
4055
  exports.BulkActionsRenderer = BulkActionsRenderer;
4056
+ exports.CLONE_PATH = CLONE_PATH;
3773
4057
  exports.COLLECTION_TYPES = COLLECTION_TYPES;
3774
4058
  exports.CREATOR_FIELDS = CREATOR_FIELDS;
3775
4059
  exports.DEFAULT_SETTINGS = DEFAULT_SETTINGS;
@@ -3797,6 +4081,7 @@ exports.getMainField = getMainField;
3797
4081
  exports.getTranslation = getTranslation;
3798
4082
  exports.index = index;
3799
4083
  exports.setInitialData = setInitialData;
4084
+ exports.useContentManagerContext = useContentManagerContext;
3800
4085
  exports.useContentTypeSchema = useContentTypeSchema;
3801
4086
  exports.useDoc = useDoc;
3802
4087
  exports.useDocLayout = useDocLayout;
@@ -3809,4 +4094,4 @@ exports.useGetAllDocumentsQuery = useGetAllDocumentsQuery;
3809
4094
  exports.useGetContentTypeConfigurationQuery = useGetContentTypeConfigurationQuery;
3810
4095
  exports.useGetInitialDataQuery = useGetInitialDataQuery;
3811
4096
  exports.useUpdateContentTypeConfigurationMutation = useUpdateContentTypeConfigurationMutation;
3812
- //# sourceMappingURL=index-BOZx6IMg.js.map
4097
+ //# sourceMappingURL=index-ovJRE1FM.js.map