@strapi/content-manager 0.0.0-experimental.abc → 0.0.0-experimental.b1d7921ddb1b36c84b58a3946e1dc9dbda91b2dc

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 (123) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-KXSuLnQD.js → ComponentConfigurationPage-CQDCxI8x.js} +3 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-KXSuLnQD.js.map → ComponentConfigurationPage-CQDCxI8x.js.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-B3yDbeU1.mjs → ComponentConfigurationPage-jmWwucg_.mjs} +3 -3
  4. package/dist/_chunks/{ComponentConfigurationPage-B3yDbeU1.mjs.map → ComponentConfigurationPage-jmWwucg_.mjs.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-BQ17--5R.js → EditConfigurationPage-Ce4bIm4n.js} +3 -3
  6. package/dist/_chunks/{EditConfigurationPage-BQ17--5R.js.map → EditConfigurationPage-Ce4bIm4n.js.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-D7PrLO8j.mjs → EditConfigurationPage-W07CEdm2.mjs} +3 -3
  8. package/dist/_chunks/{EditConfigurationPage-D7PrLO8j.mjs.map → EditConfigurationPage-W07CEdm2.mjs.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-BgjdnGz2.js → EditViewPage-CqHMM0P0.js} +15 -5
  10. package/dist/_chunks/EditViewPage-CqHMM0P0.js.map +1 -0
  11. package/dist/_chunks/{EditViewPage-B7VgwJaG.mjs → EditViewPage-al5OO1NR.mjs} +15 -5
  12. package/dist/_chunks/EditViewPage-al5OO1NR.mjs.map +1 -0
  13. package/dist/_chunks/{Field-CdK7ZLmv.js → Field-DSOUlTCm.js} +100 -87
  14. package/dist/_chunks/Field-DSOUlTCm.js.map +1 -0
  15. package/dist/_chunks/{Field-tHCw4lGA.mjs → Field-EeG6NQ7x.mjs} +97 -84
  16. package/dist/_chunks/Field-EeG6NQ7x.mjs.map +1 -0
  17. package/dist/_chunks/{Form-BJxdTv3Q.mjs → Form-BAo9ANb_.mjs} +16 -8
  18. package/dist/_chunks/Form-BAo9ANb_.mjs.map +1 -0
  19. package/dist/_chunks/{Form-C_0KTVvV.js → Form-DAEfHKzm.js} +16 -8
  20. package/dist/_chunks/Form-DAEfHKzm.js.map +1 -0
  21. package/dist/_chunks/{History-DR2txJLE.mjs → History-BpLIu67W.mjs} +24 -11
  22. package/dist/_chunks/History-BpLIu67W.mjs.map +1 -0
  23. package/dist/_chunks/{History-nuEzM5qm.js → History-CTFvy6XH.js} +23 -10
  24. package/dist/_chunks/History-CTFvy6XH.js.map +1 -0
  25. package/dist/_chunks/{ListConfigurationPage-CnB86Psm.js → ListConfigurationPage-CDzlMBz_.js} +2 -2
  26. package/dist/_chunks/{ListConfigurationPage-CnB86Psm.js.map → ListConfigurationPage-CDzlMBz_.js.map} +1 -1
  27. package/dist/_chunks/{ListConfigurationPage-voFVtXu6.mjs → ListConfigurationPage-DOqj5f8Y.mjs} +2 -2
  28. package/dist/_chunks/{ListConfigurationPage-voFVtXu6.mjs.map → ListConfigurationPage-DOqj5f8Y.mjs.map} +1 -1
  29. package/dist/_chunks/{ListViewPage-B_GaWgRH.mjs → ListViewPage-BbXYNI0v.mjs} +40 -36
  30. package/dist/_chunks/ListViewPage-BbXYNI0v.mjs.map +1 -0
  31. package/dist/_chunks/{ListViewPage-SXIXm-RM.js → ListViewPage-D0fpPYKp.js} +43 -39
  32. package/dist/_chunks/ListViewPage-D0fpPYKp.js.map +1 -0
  33. package/dist/_chunks/{NoContentTypePage-BzsQ3hLZ.js → NoContentTypePage-DTzkSAV5.js} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-BzsQ3hLZ.js.map → NoContentTypePage-DTzkSAV5.js.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-CYiGpsbj.mjs → NoContentTypePage-w2Q0VVOT.mjs} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-CYiGpsbj.mjs.map → NoContentTypePage-w2Q0VVOT.mjs.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-IGkId4C5.js → NoPermissionsPage-BoI2rU68.js} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-IGkId4C5.js.map → NoPermissionsPage-BoI2rU68.js.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-B5baIHal.mjs → NoPermissionsPage-Km0Vk5Wp.mjs} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-B5baIHal.mjs.map → NoPermissionsPage-Km0Vk5Wp.mjs.map} +1 -1
  41. package/dist/_chunks/{Relations-CIYDdKU-.mjs → Relations-C_bpmSuQ.mjs} +50 -28
  42. package/dist/_chunks/Relations-C_bpmSuQ.mjs.map +1 -0
  43. package/dist/_chunks/{Relations-Dhuurpx2.js → Relations-D6Nz5ksc.js} +50 -28
  44. package/dist/_chunks/Relations-D6Nz5ksc.js.map +1 -0
  45. package/dist/_chunks/{en-uOUIxfcQ.js → en-Bm0D0IWz.js} +13 -12
  46. package/dist/_chunks/{en-uOUIxfcQ.js.map → en-Bm0D0IWz.js.map} +1 -1
  47. package/dist/_chunks/{en-BrCTWlZv.mjs → en-DKV44jRb.mjs} +13 -12
  48. package/dist/_chunks/{en-BrCTWlZv.mjs.map → en-DKV44jRb.mjs.map} +1 -1
  49. package/dist/_chunks/{index-CdT0kHZ8.js → index-BsMu2oVP.js} +1907 -1762
  50. package/dist/_chunks/index-BsMu2oVP.js.map +1 -0
  51. package/dist/_chunks/{index-C9TJPyni.mjs → index-DcQ6xogO.mjs} +1926 -1782
  52. package/dist/_chunks/index-DcQ6xogO.mjs.map +1 -0
  53. package/dist/_chunks/{layout-C6dxWYT7.js → layout-B4aCAdTt.js} +5 -4
  54. package/dist/_chunks/{layout-C6dxWYT7.js.map → layout-B4aCAdTt.js.map} +1 -1
  55. package/dist/_chunks/{layout-BNqvLR_b.mjs → layout-BavJ6v4B.mjs} +5 -4
  56. package/dist/_chunks/{layout-BNqvLR_b.mjs.map → layout-BavJ6v4B.mjs.map} +1 -1
  57. package/dist/_chunks/{relations-CkKqKw65.mjs → relations-DMG453Od.mjs} +2 -2
  58. package/dist/_chunks/{relations-CkKqKw65.mjs.map → relations-DMG453Od.mjs.map} +1 -1
  59. package/dist/_chunks/{relations-DtFaDnP1.js → relations-Lrm9nz_m.js} +2 -2
  60. package/dist/_chunks/{relations-DtFaDnP1.js.map → relations-Lrm9nz_m.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/services/historyVersion.d.ts +1 -1
  70. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  71. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +0 -32
  72. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  73. package/dist/admin/src/services/api.d.ts +1 -1
  74. package/dist/admin/src/services/components.d.ts +2 -2
  75. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  76. package/dist/admin/src/services/documents.d.ts +19 -17
  77. package/dist/admin/src/services/init.d.ts +1 -1
  78. package/dist/admin/src/services/relations.d.ts +2 -2
  79. package/dist/admin/src/services/uid.d.ts +3 -3
  80. package/dist/admin/src/utils/validation.d.ts +4 -1
  81. package/dist/server/index.js +117 -53
  82. package/dist/server/index.js.map +1 -1
  83. package/dist/server/index.mjs +117 -53
  84. package/dist/server/index.mjs.map +1 -1
  85. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  86. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  87. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  88. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  89. package/dist/server/src/history/services/history.d.ts.map +1 -1
  90. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  91. package/dist/server/src/history/services/utils.d.ts +1 -0
  92. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  93. package/dist/server/src/index.d.ts +4 -4
  94. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  95. package/dist/server/src/services/document-metadata.d.ts +8 -8
  96. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  97. package/dist/server/src/services/index.d.ts +4 -4
  98. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  99. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  100. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  101. package/dist/server/src/utils/index.d.ts +2 -0
  102. package/dist/server/src/utils/index.d.ts.map +1 -1
  103. package/dist/shared/contracts/collection-types.d.ts +3 -1
  104. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  105. package/package.json +12 -12
  106. package/dist/_chunks/EditViewPage-B7VgwJaG.mjs.map +0 -1
  107. package/dist/_chunks/EditViewPage-BgjdnGz2.js.map +0 -1
  108. package/dist/_chunks/Field-CdK7ZLmv.js.map +0 -1
  109. package/dist/_chunks/Field-tHCw4lGA.mjs.map +0 -1
  110. package/dist/_chunks/Form-BJxdTv3Q.mjs.map +0 -1
  111. package/dist/_chunks/Form-C_0KTVvV.js.map +0 -1
  112. package/dist/_chunks/History-DR2txJLE.mjs.map +0 -1
  113. package/dist/_chunks/History-nuEzM5qm.js.map +0 -1
  114. package/dist/_chunks/ListViewPage-B_GaWgRH.mjs.map +0 -1
  115. package/dist/_chunks/ListViewPage-SXIXm-RM.js.map +0 -1
  116. package/dist/_chunks/Relations-CIYDdKU-.mjs.map +0 -1
  117. package/dist/_chunks/Relations-Dhuurpx2.js.map +0 -1
  118. package/dist/_chunks/index-C9TJPyni.mjs.map +0 -1
  119. package/dist/_chunks/index-CdT0kHZ8.js.map +0 -1
  120. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  121. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  122. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  123. package/strapi-server.js +0 -3
@@ -4,12 +4,13 @@ const jsxRuntime = require("react/jsx-runtime");
4
4
  const strapiAdmin = require("@strapi/admin/strapi-admin");
5
5
  const React = require("react");
6
6
  const designSystem = require("@strapi/design-system");
7
+ const mapValues = require("lodash/fp/mapValues");
7
8
  const reactIntl = require("react-intl");
8
9
  const reactRouterDom = require("react-router-dom");
9
- const styledComponents = require("styled-components");
10
10
  const yup = require("yup");
11
11
  const pipe = require("lodash/fp/pipe");
12
12
  const dateFns = require("date-fns");
13
+ const styledComponents = require("styled-components");
13
14
  const qs = require("qs");
14
15
  const toolkit = require("@reduxjs/toolkit");
15
16
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
@@ -32,6 +33,7 @@ function _interopNamespace(e) {
32
33
  return Object.freeze(n);
33
34
  }
34
35
  const React__namespace = /* @__PURE__ */ _interopNamespace(React);
36
+ const mapValues__default = /* @__PURE__ */ _interopDefault(mapValues);
35
37
  const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
36
38
  const pipe__default = /* @__PURE__ */ _interopDefault(pipe);
37
39
  const __variableDynamicImportRuntimeHelper = (glob, path) => {
@@ -121,6 +123,7 @@ const DocumentRBAC = ({ children, permissions }) => {
121
123
  if (!slug) {
122
124
  throw new Error("Cannot find the slug param in the URL");
123
125
  }
126
+ const [{ rawQuery }] = strapiAdmin.useQueryParams();
124
127
  const userPermissions = strapiAdmin.useAuth("DocumentRBAC", (state) => state.permissions);
125
128
  const contentTypePermissions = React__namespace.useMemo(() => {
126
129
  const contentTypePermissions2 = userPermissions.filter(
@@ -131,7 +134,14 @@ const DocumentRBAC = ({ children, permissions }) => {
131
134
  return { ...acc, [action]: [permission] };
132
135
  }, {});
133
136
  }, [slug, userPermissions]);
134
- const { isLoading, allowedActions } = strapiAdmin.useRBAC(contentTypePermissions, permissions ?? void 0);
137
+ const { isLoading, allowedActions } = strapiAdmin.useRBAC(
138
+ contentTypePermissions,
139
+ permissions ?? void 0,
140
+ // TODO: useRBAC context should be typed and built differently
141
+ // We are passing raw query as context to the hook so that it can
142
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
143
+ rawQuery
144
+ );
135
145
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
136
146
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
137
147
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -179,7 +189,8 @@ const contentManagerApi = strapiAdmin.adminApi.enhanceEndpoints({
179
189
  "Document",
180
190
  "InitialData",
181
191
  "HistoryVersion",
182
- "Relations"
192
+ "Relations",
193
+ "UidAvailability"
183
194
  ]
184
195
  });
185
196
  const documentApi = contentManagerApi.injectEndpoints({
@@ -209,7 +220,10 @@ const documentApi = contentManagerApi.injectEndpoints({
209
220
  params
210
221
  }
211
222
  }),
212
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
223
+ invalidatesTags: (_result, _error, { model }) => [
224
+ { type: "Document", id: `${model}_LIST` },
225
+ { type: "UidAvailability", id: model }
226
+ ]
213
227
  }),
214
228
  /**
215
229
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -226,7 +240,8 @@ const documentApi = contentManagerApi.injectEndpoints({
226
240
  }),
227
241
  invalidatesTags: (result, _error, { model }) => [
228
242
  { type: "Document", id: `${model}_LIST` },
229
- "Relations"
243
+ "Relations",
244
+ { type: "UidAvailability", id: model }
230
245
  ]
231
246
  }),
232
247
  deleteDocument: builder.mutation({
@@ -267,7 +282,8 @@ const documentApi = contentManagerApi.injectEndpoints({
267
282
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
268
283
  },
269
284
  { type: "Document", id: `${model}_LIST` },
270
- "Relations"
285
+ "Relations",
286
+ { type: "UidAvailability", id: model }
271
287
  ];
272
288
  }
273
289
  }),
@@ -392,7 +408,8 @@ const documentApi = contentManagerApi.injectEndpoints({
392
408
  type: "Document",
393
409
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
394
410
  },
395
- "Relations"
411
+ "Relations",
412
+ { type: "UidAvailability", id: model }
396
413
  ];
397
414
  },
398
415
  async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
@@ -475,20 +492,39 @@ const buildValidParams = (query) => {
475
492
  const isBaseQueryError = (error) => {
476
493
  return error.name !== void 0;
477
494
  };
478
- const createYupSchema = (attributes = {}, components = {}) => {
495
+ const arrayValidator = (attribute, options) => ({
496
+ message: strapiAdmin.translatedErrors.required,
497
+ test(value) {
498
+ if (options.status === "draft") {
499
+ return true;
500
+ }
501
+ if (!attribute.required) {
502
+ return true;
503
+ }
504
+ if (!value) {
505
+ return false;
506
+ }
507
+ if (Array.isArray(value) && value.length === 0) {
508
+ return false;
509
+ }
510
+ return true;
511
+ }
512
+ });
513
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
479
514
  const createModelSchema = (attributes2) => yup__namespace.object().shape(
480
515
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
481
516
  if (DOCUMENT_META_FIELDS.includes(name)) {
482
517
  return acc;
483
518
  }
484
519
  const validations = [
520
+ addNullableValidation,
485
521
  addRequiredValidation,
486
522
  addMinLengthValidation,
487
523
  addMaxLengthValidation,
488
524
  addMinValidation,
489
525
  addMaxValidation,
490
526
  addRegexValidation
491
- ].map((fn) => fn(attribute));
527
+ ].map((fn) => fn(attribute, options));
492
528
  const transformSchema = pipe__default.default(...validations);
493
529
  switch (attribute.type) {
494
530
  case "component": {
@@ -498,12 +534,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
498
534
  ...acc,
499
535
  [name]: transformSchema(
500
536
  yup__namespace.array().of(createModelSchema(attributes3).nullable(false))
501
- )
537
+ ).test(arrayValidator(attribute, options))
502
538
  };
503
539
  } else {
504
540
  return {
505
541
  ...acc,
506
- [name]: transformSchema(createModelSchema(attributes3))
542
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
507
543
  };
508
544
  }
509
545
  }
@@ -525,7 +561,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
525
561
  }
526
562
  )
527
563
  )
528
- )
564
+ ).test(arrayValidator(attribute, options))
529
565
  };
530
566
  case "relation":
531
567
  return {
@@ -537,7 +573,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
537
573
  } else if (Array.isArray(value)) {
538
574
  return yup__namespace.array().of(
539
575
  yup__namespace.object().shape({
540
- id: yup__namespace.string().required()
576
+ id: yup__namespace.number().required()
541
577
  })
542
578
  );
543
579
  } else if (typeof value === "object") {
@@ -615,13 +651,7 @@ const createAttributeSchema = (attribute) => {
615
651
  return yup__namespace.mixed();
616
652
  }
617
653
  };
618
- const addRequiredValidation = (attribute) => (schema) => {
619
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
620
- return schema.min(1, strapiAdmin.translatedErrors.required);
621
- }
622
- if (attribute.required && attribute.type !== "relation") {
623
- return schema.required(strapiAdmin.translatedErrors.required);
624
- }
654
+ const nullableSchema = (schema) => {
625
655
  return schema?.nullable ? schema.nullable() : (
626
656
  // In some cases '.nullable' will not be available on the schema.
627
657
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -629,7 +659,22 @@ const addRequiredValidation = (attribute) => (schema) => {
629
659
  schema
630
660
  );
631
661
  };
632
- const addMinLengthValidation = (attribute) => (schema) => {
662
+ const addNullableValidation = () => (schema) => {
663
+ return nullableSchema(schema);
664
+ };
665
+ const addRequiredValidation = (attribute, options) => (schema) => {
666
+ if (options.status === "draft" || !attribute.required) {
667
+ return schema;
668
+ }
669
+ if (attribute.required && "required" in schema) {
670
+ return schema.required(strapiAdmin.translatedErrors.required);
671
+ }
672
+ return schema;
673
+ };
674
+ const addMinLengthValidation = (attribute, options) => (schema) => {
675
+ if (options.status === "draft") {
676
+ return schema;
677
+ }
633
678
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
634
679
  return schema.min(attribute.minLength, {
635
680
  ...strapiAdmin.translatedErrors.minLength,
@@ -651,32 +696,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
651
696
  }
652
697
  return schema;
653
698
  };
654
- const addMinValidation = (attribute) => (schema) => {
655
- if ("min" in attribute) {
699
+ const addMinValidation = (attribute, options) => (schema) => {
700
+ if (options.status === "draft") {
701
+ return schema;
702
+ }
703
+ if ("min" in attribute && "min" in schema) {
656
704
  const min = toInteger(attribute.min);
657
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
658
- if (!attribute.required && "test" in schema && min) {
659
- return schema.test(
660
- "custom-min",
661
- {
662
- ...strapiAdmin.translatedErrors.min,
663
- values: {
664
- min: attribute.min
665
- }
666
- },
667
- (value) => {
668
- if (!value) {
669
- return true;
670
- }
671
- if (Array.isArray(value) && value.length === 0) {
672
- return true;
673
- }
674
- return value.length >= min;
675
- }
676
- );
677
- }
678
- }
679
- if ("min" in schema && min) {
705
+ if (min) {
680
706
  return schema.min(min, {
681
707
  ...strapiAdmin.translatedErrors.min,
682
708
  values: {
@@ -794,19 +820,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
794
820
  }, {});
795
821
  return componentsByKey;
796
822
  };
797
- const useDocument = (args, opts) => {
823
+ const HOOKS = {
824
+ /**
825
+ * Hook that allows to mutate the displayed headers of the list view table
826
+ * @constant
827
+ * @type {string}
828
+ */
829
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
830
+ /**
831
+ * Hook that allows to mutate the CM's collection types links pre-set filters
832
+ * @constant
833
+ * @type {string}
834
+ */
835
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
836
+ /**
837
+ * Hook that allows to mutate the CM's edit view layout
838
+ * @constant
839
+ * @type {string}
840
+ */
841
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
842
+ /**
843
+ * Hook that allows to mutate the CM's single types links pre-set filters
844
+ * @constant
845
+ * @type {string}
846
+ */
847
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
848
+ };
849
+ const contentTypesApi = contentManagerApi.injectEndpoints({
850
+ endpoints: (builder) => ({
851
+ getContentTypeConfiguration: builder.query({
852
+ query: (uid) => ({
853
+ url: `/content-manager/content-types/${uid}/configuration`,
854
+ method: "GET"
855
+ }),
856
+ transformResponse: (response) => response.data,
857
+ providesTags: (_result, _error, uid) => [
858
+ { type: "ContentTypesConfiguration", id: uid },
859
+ { type: "ContentTypeSettings", id: "LIST" }
860
+ ]
861
+ }),
862
+ getAllContentTypeSettings: builder.query({
863
+ query: () => "/content-manager/content-types-settings",
864
+ transformResponse: (response) => response.data,
865
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
866
+ }),
867
+ updateContentTypeConfiguration: builder.mutation({
868
+ query: ({ uid, ...body }) => ({
869
+ url: `/content-manager/content-types/${uid}/configuration`,
870
+ method: "PUT",
871
+ data: body
872
+ }),
873
+ transformResponse: (response) => response.data,
874
+ invalidatesTags: (_result, _error, { uid }) => [
875
+ { type: "ContentTypesConfiguration", id: uid },
876
+ { type: "ContentTypeSettings", id: "LIST" },
877
+ // Is this necessary?
878
+ { type: "InitialData" }
879
+ ]
880
+ })
881
+ })
882
+ });
883
+ const {
884
+ useGetContentTypeConfigurationQuery,
885
+ useGetAllContentTypeSettingsQuery,
886
+ useUpdateContentTypeConfigurationMutation
887
+ } = contentTypesApi;
888
+ const checkIfAttributeIsDisplayable = (attribute) => {
889
+ const { type } = attribute;
890
+ if (type === "relation") {
891
+ return !attribute.relation.toLowerCase().includes("morph");
892
+ }
893
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
894
+ };
895
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
896
+ if (!mainFieldName) {
897
+ return void 0;
898
+ }
899
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
900
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
901
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
902
+ );
903
+ return {
904
+ name: mainFieldName,
905
+ type: mainFieldType ?? "string"
906
+ };
907
+ };
908
+ const DEFAULT_SETTINGS = {
909
+ bulkable: false,
910
+ filterable: false,
911
+ searchable: false,
912
+ pagination: false,
913
+ defaultSortBy: "",
914
+ defaultSortOrder: "asc",
915
+ mainField: "id",
916
+ pageSize: 10
917
+ };
918
+ const useDocumentLayout = (model) => {
919
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
920
+ const [{ query }] = strapiAdmin.useQueryParams();
921
+ const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
798
922
  const { toggleNotification } = strapiAdmin.useNotification();
799
923
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
924
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
800
925
  const {
801
- currentData: data,
802
- isLoading: isLoadingDocument,
803
- isFetching: isFetchingDocument,
804
- error
805
- } = useGetDocumentQuery(args, {
806
- ...opts,
807
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
808
- });
809
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
926
+ data,
927
+ isLoading: isLoadingConfigs,
928
+ error,
929
+ isFetching: isFetchingConfigs
930
+ } = useGetContentTypeConfigurationQuery(model);
931
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
810
932
  React__namespace.useEffect(() => {
811
933
  if (error) {
812
934
  toggleNotification({
@@ -814,362 +936,440 @@ const useDocument = (args, opts) => {
814
936
  message: formatAPIError(error)
815
937
  });
816
938
  }
817
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
818
- const validationSchema = React__namespace.useMemo(() => {
819
- if (!schema) {
820
- return null;
821
- }
822
- return createYupSchema(schema.attributes, components);
823
- }, [schema, components]);
824
- const validate = React__namespace.useCallback(
825
- (document) => {
826
- if (!validationSchema) {
827
- throw new Error(
828
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
829
- );
830
- }
831
- try {
832
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
833
- return null;
834
- } catch (error2) {
835
- if (error2 instanceof yup.ValidationError) {
836
- return strapiAdmin.getYupValidationErrors(error2);
837
- }
838
- throw error2;
839
- }
939
+ }, [error, formatAPIError, toggleNotification]);
940
+ const editLayout = React__namespace.useMemo(
941
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
942
+ layout: [],
943
+ components: {},
944
+ metadatas: {},
945
+ options: {},
946
+ settings: DEFAULT_SETTINGS
840
947
  },
841
- [validationSchema]
948
+ [data, isLoading, schemas, schema, components]
949
+ );
950
+ const listLayout = React__namespace.useMemo(() => {
951
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
952
+ layout: [],
953
+ metadatas: {},
954
+ options: {},
955
+ settings: DEFAULT_SETTINGS
956
+ };
957
+ }, [data, isLoading, schemas, schema, components]);
958
+ const { layout: edit } = React__namespace.useMemo(
959
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
960
+ layout: editLayout,
961
+ query
962
+ }),
963
+ [editLayout, query, runHookWaterfall]
842
964
  );
843
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
844
965
  return {
845
- components,
846
- document: data?.data,
847
- meta: data?.meta,
966
+ error,
848
967
  isLoading,
849
- schema,
850
- validate
851
- };
852
- };
853
- const useDoc = () => {
854
- const { id, slug, collectionType, origin } = reactRouterDom.useParams();
855
- const [{ query }] = strapiAdmin.useQueryParams();
856
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
857
- if (!collectionType) {
858
- throw new Error("Could not find collectionType in url params");
859
- }
860
- if (!slug) {
861
- throw new Error("Could not find model in url params");
862
- }
863
- return {
864
- collectionType,
865
- model: slug,
866
- id: origin || id === "create" ? void 0 : id,
867
- ...useDocument(
868
- { documentId: origin || id, model: slug, collectionType, params },
869
- {
870
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
871
- }
872
- )
968
+ edit,
969
+ list: listLayout
873
970
  };
874
971
  };
875
- const prefixPluginTranslations = (trad, pluginId) => {
876
- if (!pluginId) {
877
- throw new TypeError("pluginId can't be empty");
878
- }
879
- return Object.keys(trad).reduce((acc, current) => {
880
- acc[`${pluginId}.${current}`] = trad[current];
881
- return acc;
882
- }, {});
883
- };
884
- const getTranslation = (id) => `content-manager.${id}`;
885
- const DEFAULT_UNEXPECTED_ERROR_MSG = {
886
- id: "notification.error",
887
- defaultMessage: "An error occurred, please try again"
972
+ const useDocLayout = () => {
973
+ const { model } = useDoc();
974
+ return useDocumentLayout(model);
888
975
  };
889
- const useDocumentActions = () => {
890
- const { toggleNotification } = strapiAdmin.useNotification();
891
- const { formatMessage } = reactIntl.useIntl();
892
- const { trackUsage } = strapiAdmin.useTracking();
893
- const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
894
- const [deleteDocument] = useDeleteDocumentMutation();
895
- const _delete = React__namespace.useCallback(
896
- async ({ collectionType, model, documentId, params }, trackerProperty) => {
897
- try {
898
- trackUsage("willDeleteEntry", trackerProperty);
899
- const res = await deleteDocument({
900
- collectionType,
901
- model,
902
- documentId,
903
- params
904
- });
905
- if ("error" in res) {
906
- toggleNotification({
907
- type: "danger",
908
- message: formatAPIError(res.error)
909
- });
910
- return { error: res.error };
911
- }
912
- toggleNotification({
913
- type: "success",
914
- message: formatMessage({
915
- id: getTranslation("success.record.delete"),
916
- defaultMessage: "Deleted document"
917
- })
918
- });
919
- trackUsage("didDeleteEntry", trackerProperty);
920
- return res.data;
921
- } catch (err) {
922
- toggleNotification({
923
- type: "danger",
924
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
925
- });
926
- trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
927
- throw err;
976
+ const formatEditLayout = (data, {
977
+ schemas,
978
+ schema,
979
+ components
980
+ }) => {
981
+ let currentPanelIndex = 0;
982
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
983
+ data.contentType.layouts.edit,
984
+ schema?.attributes,
985
+ data.contentType.metadatas,
986
+ { configurations: data.components, schemas: components },
987
+ schemas
988
+ ).reduce((panels, row) => {
989
+ if (row.some((field) => field.type === "dynamiczone")) {
990
+ panels.push([row]);
991
+ currentPanelIndex += 2;
992
+ } else {
993
+ if (!panels[currentPanelIndex]) {
994
+ panels.push([]);
928
995
  }
929
- },
930
- [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
931
- );
932
- const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
933
- const deleteMany = React__namespace.useCallback(
934
- async ({ model, documentIds, params }) => {
935
- try {
936
- trackUsage("willBulkDeleteEntries");
937
- const res = await deleteManyDocuments({
938
- model,
939
- documentIds,
940
- params
941
- });
942
- if ("error" in res) {
943
- toggleNotification({
944
- type: "danger",
945
- message: formatAPIError(res.error)
946
- });
947
- return { error: res.error };
996
+ panels[currentPanelIndex].push(row);
997
+ }
998
+ return panels;
999
+ }, []);
1000
+ const componentEditAttributes = Object.entries(data.components).reduce(
1001
+ (acc, [uid, configuration]) => {
1002
+ acc[uid] = {
1003
+ layout: convertEditLayoutToFieldLayouts(
1004
+ configuration.layouts.edit,
1005
+ components[uid].attributes,
1006
+ configuration.metadatas,
1007
+ { configurations: data.components, schemas: components }
1008
+ ),
1009
+ settings: {
1010
+ ...configuration.settings,
1011
+ icon: components[uid].info.icon,
1012
+ displayName: components[uid].info.displayName
948
1013
  }
949
- toggleNotification({
950
- type: "success",
951
- title: formatMessage({
952
- id: getTranslation("success.records.delete"),
953
- defaultMessage: "Successfully deleted."
954
- }),
955
- message: ""
956
- });
957
- trackUsage("didBulkDeleteEntries");
958
- return res.data;
959
- } catch (err) {
960
- toggleNotification({
961
- type: "danger",
962
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
963
- });
964
- trackUsage("didNotBulkDeleteEntries");
965
- throw err;
966
- }
1014
+ };
1015
+ return acc;
967
1016
  },
968
- [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1017
+ {}
969
1018
  );
970
- const [discardDocument] = useDiscardDocumentMutation();
971
- const discard = React__namespace.useCallback(
972
- async ({ collectionType, model, documentId, params }) => {
973
- try {
974
- const res = await discardDocument({
975
- collectionType,
976
- model,
977
- documentId,
978
- params
979
- });
980
- if ("error" in res) {
981
- toggleNotification({
982
- type: "danger",
983
- message: formatAPIError(res.error)
984
- });
985
- return { error: res.error };
986
- }
987
- toggleNotification({
988
- type: "success",
989
- message: formatMessage({
990
- id: "content-manager.success.record.discard",
991
- defaultMessage: "Changes discarded"
992
- })
993
- });
994
- return res.data;
995
- } catch (err) {
996
- toggleNotification({
997
- type: "danger",
998
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
999
- });
1000
- throw err;
1001
- }
1019
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1020
+ (acc, [attribute, metadata]) => {
1021
+ return {
1022
+ ...acc,
1023
+ [attribute]: metadata.edit
1024
+ };
1002
1025
  },
1003
- [discardDocument, formatAPIError, formatMessage, toggleNotification]
1026
+ {}
1004
1027
  );
1005
- const [publishDocument] = usePublishDocumentMutation();
1006
- const publish = React__namespace.useCallback(
1007
- async ({ collectionType, model, documentId, params }, data) => {
1008
- try {
1009
- trackUsage("willPublishEntry");
1010
- const res = await publishDocument({
1011
- collectionType,
1012
- model,
1013
- documentId,
1014
- data,
1015
- params
1016
- });
1017
- if ("error" in res) {
1018
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1019
- return { error: res.error };
1020
- }
1021
- trackUsage("didPublishEntry");
1022
- toggleNotification({
1023
- type: "success",
1024
- message: formatMessage({
1025
- id: getTranslation("success.record.publish"),
1026
- defaultMessage: "Published document"
1027
- })
1028
- });
1029
- return res.data;
1030
- } catch (err) {
1031
- toggleNotification({
1032
- type: "danger",
1033
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1034
- });
1035
- throw err;
1036
- }
1028
+ return {
1029
+ layout: panelledEditAttributes,
1030
+ components: componentEditAttributes,
1031
+ metadatas: editMetadatas,
1032
+ settings: {
1033
+ ...data.contentType.settings,
1034
+ displayName: schema?.info.displayName
1037
1035
  },
1038
- [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1039
- );
1040
- const [publishManyDocuments] = usePublishManyDocumentsMutation();
1041
- const publishMany = React__namespace.useCallback(
1042
- async ({ model, documentIds, params }) => {
1043
- try {
1044
- const res = await publishManyDocuments({
1045
- model,
1046
- documentIds,
1047
- params
1048
- });
1049
- if ("error" in res) {
1050
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1051
- return { error: res.error };
1052
- }
1053
- toggleNotification({
1054
- type: "success",
1055
- message: formatMessage({
1056
- id: getTranslation("success.record.publish"),
1057
- defaultMessage: "Published document"
1058
- })
1059
- });
1060
- return res.data;
1061
- } catch (err) {
1062
- toggleNotification({
1063
- type: "danger",
1064
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1065
- });
1066
- throw err;
1036
+ options: {
1037
+ ...schema?.options,
1038
+ ...schema?.pluginOptions,
1039
+ ...data.contentType.options
1040
+ }
1041
+ };
1042
+ };
1043
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1044
+ return rows.map(
1045
+ (row) => row.map((field) => {
1046
+ const attribute = attributes[field.name];
1047
+ if (!attribute) {
1048
+ return null;
1067
1049
  }
1068
- },
1069
- [
1070
- // trackUsage,
1071
- publishManyDocuments,
1072
- toggleNotification,
1073
- formatMessage,
1074
- formatAPIError
1075
- ]
1050
+ const { edit: metadata } = metadatas[field.name];
1051
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1052
+ return {
1053
+ attribute,
1054
+ disabled: !metadata.editable,
1055
+ hint: metadata.description,
1056
+ label: metadata.label ?? "",
1057
+ name: field.name,
1058
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1059
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1060
+ schemas,
1061
+ components: components?.schemas ?? {}
1062
+ }),
1063
+ placeholder: metadata.placeholder ?? "",
1064
+ required: attribute.required ?? false,
1065
+ size: field.size,
1066
+ unique: "unique" in attribute ? attribute.unique : false,
1067
+ visible: metadata.visible ?? true,
1068
+ type: attribute.type
1069
+ };
1070
+ }).filter((field) => field !== null)
1076
1071
  );
1077
- const [updateDocument] = useUpdateDocumentMutation();
1078
- const update = React__namespace.useCallback(
1079
- async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1080
- try {
1081
- trackUsage("willEditEntry", trackerProperty);
1082
- const res = await updateDocument({
1072
+ };
1073
+ const formatListLayout = (data, {
1074
+ schemas,
1075
+ schema,
1076
+ components
1077
+ }) => {
1078
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1079
+ (acc, [attribute, metadata]) => {
1080
+ return {
1081
+ ...acc,
1082
+ [attribute]: metadata.list
1083
+ };
1084
+ },
1085
+ {}
1086
+ );
1087
+ const listAttributes = convertListLayoutToFieldLayouts(
1088
+ data.contentType.layouts.list,
1089
+ schema?.attributes,
1090
+ listMetadatas,
1091
+ { configurations: data.components, schemas: components },
1092
+ schemas
1093
+ );
1094
+ return {
1095
+ layout: listAttributes,
1096
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1097
+ metadatas: listMetadatas,
1098
+ options: {
1099
+ ...schema?.options,
1100
+ ...schema?.pluginOptions,
1101
+ ...data.contentType.options
1102
+ }
1103
+ };
1104
+ };
1105
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1106
+ return columns.map((name) => {
1107
+ const attribute = attributes[name];
1108
+ if (!attribute) {
1109
+ return null;
1110
+ }
1111
+ const metadata = metadatas[name];
1112
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1113
+ return {
1114
+ attribute,
1115
+ label: metadata.label ?? "",
1116
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1117
+ schemas,
1118
+ components: components?.schemas ?? {}
1119
+ }),
1120
+ name,
1121
+ searchable: metadata.searchable ?? true,
1122
+ sortable: metadata.sortable ?? true
1123
+ };
1124
+ }).filter((field) => field !== null);
1125
+ };
1126
+ const useDocument = (args, opts) => {
1127
+ const { toggleNotification } = strapiAdmin.useNotification();
1128
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1129
+ const {
1130
+ currentData: data,
1131
+ isLoading: isLoadingDocument,
1132
+ isFetching: isFetchingDocument,
1133
+ error
1134
+ } = useGetDocumentQuery(args, {
1135
+ ...opts,
1136
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1137
+ });
1138
+ const {
1139
+ components,
1140
+ schema,
1141
+ schemas,
1142
+ isLoading: isLoadingSchema
1143
+ } = useContentTypeSchema(args.model);
1144
+ React__namespace.useEffect(() => {
1145
+ if (error) {
1146
+ toggleNotification({
1147
+ type: "danger",
1148
+ message: formatAPIError(error)
1149
+ });
1150
+ }
1151
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1152
+ const validationSchema = React__namespace.useMemo(() => {
1153
+ if (!schema) {
1154
+ return null;
1155
+ }
1156
+ return createYupSchema(schema.attributes, components);
1157
+ }, [schema, components]);
1158
+ const validate = React__namespace.useCallback(
1159
+ (document) => {
1160
+ if (!validationSchema) {
1161
+ throw new Error(
1162
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1163
+ );
1164
+ }
1165
+ try {
1166
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1167
+ return null;
1168
+ } catch (error2) {
1169
+ if (error2 instanceof yup.ValidationError) {
1170
+ return strapiAdmin.getYupValidationErrors(error2);
1171
+ }
1172
+ throw error2;
1173
+ }
1174
+ },
1175
+ [validationSchema]
1176
+ );
1177
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1178
+ const hasError = !!error;
1179
+ return {
1180
+ components,
1181
+ document: data?.data,
1182
+ meta: data?.meta,
1183
+ isLoading,
1184
+ hasError,
1185
+ schema,
1186
+ schemas,
1187
+ validate
1188
+ };
1189
+ };
1190
+ const useDoc = () => {
1191
+ const { id, slug, collectionType, origin } = reactRouterDom.useParams();
1192
+ const [{ query }] = strapiAdmin.useQueryParams();
1193
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1194
+ if (!collectionType) {
1195
+ throw new Error("Could not find collectionType in url params");
1196
+ }
1197
+ if (!slug) {
1198
+ throw new Error("Could not find model in url params");
1199
+ }
1200
+ const document = useDocument(
1201
+ { documentId: origin || id, model: slug, collectionType, params },
1202
+ {
1203
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1204
+ }
1205
+ );
1206
+ const returnId = origin || id === "create" ? void 0 : id;
1207
+ return {
1208
+ collectionType,
1209
+ model: slug,
1210
+ id: returnId,
1211
+ ...document
1212
+ };
1213
+ };
1214
+ const useContentManagerContext = () => {
1215
+ const {
1216
+ collectionType,
1217
+ model,
1218
+ id,
1219
+ components,
1220
+ isLoading: isLoadingDoc,
1221
+ schema,
1222
+ schemas
1223
+ } = useDoc();
1224
+ const layout = useDocumentLayout(model);
1225
+ const form = strapiAdmin.useForm("useContentManagerContext", (state) => state);
1226
+ const isSingleType = collectionType === SINGLE_TYPES;
1227
+ const slug = model;
1228
+ const isCreatingEntry = id === "create";
1229
+ useContentTypeSchema();
1230
+ const isLoading = isLoadingDoc || layout.isLoading;
1231
+ const error = layout.error;
1232
+ return {
1233
+ error,
1234
+ isLoading,
1235
+ // Base metadata
1236
+ model,
1237
+ collectionType,
1238
+ id,
1239
+ slug,
1240
+ isCreatingEntry,
1241
+ isSingleType,
1242
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1243
+ // All schema infos
1244
+ components,
1245
+ contentType: schema,
1246
+ contentTypes: schemas,
1247
+ // Form state
1248
+ form,
1249
+ // layout infos
1250
+ layout
1251
+ };
1252
+ };
1253
+ const prefixPluginTranslations = (trad, pluginId) => {
1254
+ if (!pluginId) {
1255
+ throw new TypeError("pluginId can't be empty");
1256
+ }
1257
+ return Object.keys(trad).reduce((acc, current) => {
1258
+ acc[`${pluginId}.${current}`] = trad[current];
1259
+ return acc;
1260
+ }, {});
1261
+ };
1262
+ const getTranslation = (id) => `content-manager.${id}`;
1263
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1264
+ id: "notification.error",
1265
+ defaultMessage: "An error occurred, please try again"
1266
+ };
1267
+ const useDocumentActions = () => {
1268
+ const { toggleNotification } = strapiAdmin.useNotification();
1269
+ const { formatMessage } = reactIntl.useIntl();
1270
+ const { trackUsage } = strapiAdmin.useTracking();
1271
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1272
+ const navigate = reactRouterDom.useNavigate();
1273
+ const setCurrentStep = strapiAdmin.useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
1274
+ const [deleteDocument] = useDeleteDocumentMutation();
1275
+ const _delete = React__namespace.useCallback(
1276
+ async ({ collectionType, model, documentId, params }, trackerProperty) => {
1277
+ try {
1278
+ trackUsage("willDeleteEntry", trackerProperty);
1279
+ const res = await deleteDocument({
1083
1280
  collectionType,
1084
1281
  model,
1085
1282
  documentId,
1086
- data,
1087
1283
  params
1088
1284
  });
1089
1285
  if ("error" in res) {
1090
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1091
- trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1286
+ toggleNotification({
1287
+ type: "danger",
1288
+ message: formatAPIError(res.error)
1289
+ });
1092
1290
  return { error: res.error };
1093
1291
  }
1094
- trackUsage("didEditEntry", trackerProperty);
1095
1292
  toggleNotification({
1096
1293
  type: "success",
1097
1294
  message: formatMessage({
1098
- id: getTranslation("success.record.save"),
1099
- defaultMessage: "Saved document"
1295
+ id: getTranslation("success.record.delete"),
1296
+ defaultMessage: "Deleted document"
1100
1297
  })
1101
1298
  });
1299
+ trackUsage("didDeleteEntry", trackerProperty);
1102
1300
  return res.data;
1103
1301
  } catch (err) {
1104
- trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1105
1302
  toggleNotification({
1106
1303
  type: "danger",
1107
1304
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1108
1305
  });
1306
+ trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
1109
1307
  throw err;
1110
1308
  }
1111
1309
  },
1112
- [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1310
+ [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
1113
1311
  );
1114
- const [unpublishDocument] = useUnpublishDocumentMutation();
1115
- const unpublish = React__namespace.useCallback(
1116
- async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1312
+ const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
1313
+ const deleteMany = React__namespace.useCallback(
1314
+ async ({ model, documentIds, params }) => {
1117
1315
  try {
1118
- trackUsage("willUnpublishEntry");
1119
- const res = await unpublishDocument({
1120
- collectionType,
1316
+ trackUsage("willBulkDeleteEntries");
1317
+ const res = await deleteManyDocuments({
1121
1318
  model,
1122
- documentId,
1123
- params,
1124
- data: {
1125
- discardDraft
1126
- }
1319
+ documentIds,
1320
+ params
1127
1321
  });
1128
1322
  if ("error" in res) {
1129
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1323
+ toggleNotification({
1324
+ type: "danger",
1325
+ message: formatAPIError(res.error)
1326
+ });
1130
1327
  return { error: res.error };
1131
1328
  }
1132
- trackUsage("didUnpublishEntry");
1133
1329
  toggleNotification({
1134
1330
  type: "success",
1135
- message: formatMessage({
1136
- id: getTranslation("success.record.unpublish"),
1137
- defaultMessage: "Unpublished document"
1138
- })
1331
+ title: formatMessage({
1332
+ id: getTranslation("success.records.delete"),
1333
+ defaultMessage: "Successfully deleted."
1334
+ }),
1335
+ message: ""
1139
1336
  });
1337
+ trackUsage("didBulkDeleteEntries");
1140
1338
  return res.data;
1141
1339
  } catch (err) {
1142
1340
  toggleNotification({
1143
1341
  type: "danger",
1144
1342
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1145
1343
  });
1344
+ trackUsage("didNotBulkDeleteEntries");
1146
1345
  throw err;
1147
1346
  }
1148
1347
  },
1149
- [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1348
+ [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1150
1349
  );
1151
- const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1152
- const unpublishMany = React__namespace.useCallback(
1153
- async ({ model, documentIds, params }) => {
1350
+ const [discardDocument] = useDiscardDocumentMutation();
1351
+ const discard = React__namespace.useCallback(
1352
+ async ({ collectionType, model, documentId, params }) => {
1154
1353
  try {
1155
- trackUsage("willBulkUnpublishEntries");
1156
- const res = await unpublishManyDocuments({
1354
+ const res = await discardDocument({
1355
+ collectionType,
1157
1356
  model,
1158
- documentIds,
1357
+ documentId,
1159
1358
  params
1160
1359
  });
1161
1360
  if ("error" in res) {
1162
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1361
+ toggleNotification({
1362
+ type: "danger",
1363
+ message: formatAPIError(res.error)
1364
+ });
1163
1365
  return { error: res.error };
1164
1366
  }
1165
- trackUsage("didBulkUnpublishEntries");
1166
1367
  toggleNotification({
1167
1368
  type: "success",
1168
- title: formatMessage({
1169
- id: getTranslation("success.records.unpublish"),
1170
- defaultMessage: "Successfully unpublished."
1171
- }),
1172
- message: ""
1369
+ message: formatMessage({
1370
+ id: "content-manager.success.record.discard",
1371
+ defaultMessage: "Changes discarded"
1372
+ })
1173
1373
  });
1174
1374
  return res.data;
1175
1375
  } catch (err) {
@@ -1177,13 +1377,193 @@ const useDocumentActions = () => {
1177
1377
  type: "danger",
1178
1378
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1179
1379
  });
1180
- trackUsage("didNotBulkUnpublishEntries");
1181
1380
  throw err;
1182
1381
  }
1183
1382
  },
1184
- [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1383
+ [discardDocument, formatAPIError, formatMessage, toggleNotification]
1185
1384
  );
1186
- const [createDocument] = useCreateDocumentMutation();
1385
+ const [publishDocument] = usePublishDocumentMutation();
1386
+ const publish = React__namespace.useCallback(
1387
+ async ({ collectionType, model, documentId, params }, data) => {
1388
+ try {
1389
+ trackUsage("willPublishEntry");
1390
+ const res = await publishDocument({
1391
+ collectionType,
1392
+ model,
1393
+ documentId,
1394
+ data,
1395
+ params
1396
+ });
1397
+ if ("error" in res) {
1398
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1399
+ return { error: res.error };
1400
+ }
1401
+ trackUsage("didPublishEntry");
1402
+ toggleNotification({
1403
+ type: "success",
1404
+ message: formatMessage({
1405
+ id: getTranslation("success.record.publish"),
1406
+ defaultMessage: "Published document"
1407
+ })
1408
+ });
1409
+ return res.data;
1410
+ } catch (err) {
1411
+ toggleNotification({
1412
+ type: "danger",
1413
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1414
+ });
1415
+ throw err;
1416
+ }
1417
+ },
1418
+ [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1419
+ );
1420
+ const [publishManyDocuments] = usePublishManyDocumentsMutation();
1421
+ const publishMany = React__namespace.useCallback(
1422
+ async ({ model, documentIds, params }) => {
1423
+ try {
1424
+ const res = await publishManyDocuments({
1425
+ model,
1426
+ documentIds,
1427
+ params
1428
+ });
1429
+ if ("error" in res) {
1430
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1431
+ return { error: res.error };
1432
+ }
1433
+ toggleNotification({
1434
+ type: "success",
1435
+ message: formatMessage({
1436
+ id: getTranslation("success.record.publish"),
1437
+ defaultMessage: "Published document"
1438
+ })
1439
+ });
1440
+ return res.data;
1441
+ } catch (err) {
1442
+ toggleNotification({
1443
+ type: "danger",
1444
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1445
+ });
1446
+ throw err;
1447
+ }
1448
+ },
1449
+ [
1450
+ // trackUsage,
1451
+ publishManyDocuments,
1452
+ toggleNotification,
1453
+ formatMessage,
1454
+ formatAPIError
1455
+ ]
1456
+ );
1457
+ const [updateDocument] = useUpdateDocumentMutation();
1458
+ const update = React__namespace.useCallback(
1459
+ async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1460
+ try {
1461
+ trackUsage("willEditEntry", trackerProperty);
1462
+ const res = await updateDocument({
1463
+ collectionType,
1464
+ model,
1465
+ documentId,
1466
+ data,
1467
+ params
1468
+ });
1469
+ if ("error" in res) {
1470
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1471
+ trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1472
+ return { error: res.error };
1473
+ }
1474
+ trackUsage("didEditEntry", trackerProperty);
1475
+ toggleNotification({
1476
+ type: "success",
1477
+ message: formatMessage({
1478
+ id: getTranslation("success.record.save"),
1479
+ defaultMessage: "Saved document"
1480
+ })
1481
+ });
1482
+ return res.data;
1483
+ } catch (err) {
1484
+ trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1485
+ toggleNotification({
1486
+ type: "danger",
1487
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1488
+ });
1489
+ throw err;
1490
+ }
1491
+ },
1492
+ [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1493
+ );
1494
+ const [unpublishDocument] = useUnpublishDocumentMutation();
1495
+ const unpublish = React__namespace.useCallback(
1496
+ async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1497
+ try {
1498
+ trackUsage("willUnpublishEntry");
1499
+ const res = await unpublishDocument({
1500
+ collectionType,
1501
+ model,
1502
+ documentId,
1503
+ params,
1504
+ data: {
1505
+ discardDraft
1506
+ }
1507
+ });
1508
+ if ("error" in res) {
1509
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1510
+ return { error: res.error };
1511
+ }
1512
+ trackUsage("didUnpublishEntry");
1513
+ toggleNotification({
1514
+ type: "success",
1515
+ message: formatMessage({
1516
+ id: getTranslation("success.record.unpublish"),
1517
+ defaultMessage: "Unpublished document"
1518
+ })
1519
+ });
1520
+ return res.data;
1521
+ } catch (err) {
1522
+ toggleNotification({
1523
+ type: "danger",
1524
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1525
+ });
1526
+ throw err;
1527
+ }
1528
+ },
1529
+ [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1530
+ );
1531
+ const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1532
+ const unpublishMany = React__namespace.useCallback(
1533
+ async ({ model, documentIds, params }) => {
1534
+ try {
1535
+ trackUsage("willBulkUnpublishEntries");
1536
+ const res = await unpublishManyDocuments({
1537
+ model,
1538
+ documentIds,
1539
+ params
1540
+ });
1541
+ if ("error" in res) {
1542
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1543
+ return { error: res.error };
1544
+ }
1545
+ trackUsage("didBulkUnpublishEntries");
1546
+ toggleNotification({
1547
+ type: "success",
1548
+ title: formatMessage({
1549
+ id: getTranslation("success.records.unpublish"),
1550
+ defaultMessage: "Successfully unpublished."
1551
+ }),
1552
+ message: ""
1553
+ });
1554
+ return res.data;
1555
+ } catch (err) {
1556
+ toggleNotification({
1557
+ type: "danger",
1558
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1559
+ });
1560
+ trackUsage("didNotBulkUnpublishEntries");
1561
+ throw err;
1562
+ }
1563
+ },
1564
+ [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1565
+ );
1566
+ const [createDocument] = useCreateDocumentMutation();
1187
1567
  const create = React__namespace.useCallback(
1188
1568
  async ({ model, params }, data, trackerProperty) => {
1189
1569
  try {
@@ -1205,6 +1585,7 @@ const useDocumentActions = () => {
1205
1585
  defaultMessage: "Saved document"
1206
1586
  })
1207
1587
  });
1588
+ setCurrentStep("contentManager.success");
1208
1589
  return res.data;
1209
1590
  } catch (err) {
1210
1591
  toggleNotification({
@@ -1244,7 +1625,7 @@ const useDocumentActions = () => {
1244
1625
  throw err;
1245
1626
  }
1246
1627
  },
1247
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1628
+ [autoCloneDocument, formatMessage, toggleNotification]
1248
1629
  );
1249
1630
  const [cloneDocument] = useCloneDocumentMutation();
1250
1631
  const clone = React__namespace.useCallback(
@@ -1270,6 +1651,7 @@ const useDocumentActions = () => {
1270
1651
  defaultMessage: "Cloned document"
1271
1652
  })
1272
1653
  });
1654
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1273
1655
  return res.data;
1274
1656
  } catch (err) {
1275
1657
  toggleNotification({
@@ -1280,7 +1662,7 @@ const useDocumentActions = () => {
1280
1662
  throw err;
1281
1663
  }
1282
1664
  },
1283
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1665
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1284
1666
  );
1285
1667
  const [getDoc] = useLazyGetDocumentQuery();
1286
1668
  const getDocument = React__namespace.useCallback(
@@ -1306,7 +1688,7 @@ const useDocumentActions = () => {
1306
1688
  };
1307
1689
  };
1308
1690
  const ProtectedHistoryPage = React.lazy(
1309
- () => Promise.resolve().then(() => require("./History-nuEzM5qm.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1691
+ () => Promise.resolve().then(() => require("./History-CTFvy6XH.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1310
1692
  );
1311
1693
  const routes$1 = [
1312
1694
  {
@@ -1319,31 +1701,31 @@ const routes$1 = [
1319
1701
  }
1320
1702
  ];
1321
1703
  const ProtectedEditViewPage = React.lazy(
1322
- () => Promise.resolve().then(() => require("./EditViewPage-BgjdnGz2.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1704
+ () => Promise.resolve().then(() => require("./EditViewPage-CqHMM0P0.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1323
1705
  );
1324
1706
  const ProtectedListViewPage = React.lazy(
1325
- () => Promise.resolve().then(() => require("./ListViewPage-SXIXm-RM.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1707
+ () => Promise.resolve().then(() => require("./ListViewPage-D0fpPYKp.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1326
1708
  );
1327
1709
  const ProtectedListConfiguration = React.lazy(
1328
- () => Promise.resolve().then(() => require("./ListConfigurationPage-CnB86Psm.js")).then((mod) => ({
1710
+ () => Promise.resolve().then(() => require("./ListConfigurationPage-CDzlMBz_.js")).then((mod) => ({
1329
1711
  default: mod.ProtectedListConfiguration
1330
1712
  }))
1331
1713
  );
1332
1714
  const ProtectedEditConfigurationPage = React.lazy(
1333
- () => Promise.resolve().then(() => require("./EditConfigurationPage-BQ17--5R.js")).then((mod) => ({
1715
+ () => Promise.resolve().then(() => require("./EditConfigurationPage-Ce4bIm4n.js")).then((mod) => ({
1334
1716
  default: mod.ProtectedEditConfigurationPage
1335
1717
  }))
1336
1718
  );
1337
1719
  const ProtectedComponentConfigurationPage = React.lazy(
1338
- () => Promise.resolve().then(() => require("./ComponentConfigurationPage-KXSuLnQD.js")).then((mod) => ({
1720
+ () => Promise.resolve().then(() => require("./ComponentConfigurationPage-CQDCxI8x.js")).then((mod) => ({
1339
1721
  default: mod.ProtectedComponentConfigurationPage
1340
1722
  }))
1341
1723
  );
1342
1724
  const NoPermissions = React.lazy(
1343
- () => Promise.resolve().then(() => require("./NoPermissionsPage-IGkId4C5.js")).then((mod) => ({ default: mod.NoPermissions }))
1725
+ () => Promise.resolve().then(() => require("./NoPermissionsPage-BoI2rU68.js")).then((mod) => ({ default: mod.NoPermissions }))
1344
1726
  );
1345
1727
  const NoContentType = React.lazy(
1346
- () => Promise.resolve().then(() => require("./NoContentTypePage-BzsQ3hLZ.js")).then((mod) => ({ default: mod.NoContentType }))
1728
+ () => Promise.resolve().then(() => require("./NoContentTypePage-DTzkSAV5.js")).then((mod) => ({ default: mod.NoContentType }))
1347
1729
  );
1348
1730
  const CollectionTypePages = () => {
1349
1731
  const { collectionType } = reactRouterDom.useParams();
@@ -1403,1065 +1785,743 @@ const DocumentActions = ({ actions: actions2 }) => {
1403
1785
  if (!primaryAction) {
1404
1786
  return null;
1405
1787
  }
1406
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, alignItems: "stretch", width: "100%", children: [
1407
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1408
- /* @__PURE__ */ jsxRuntime.jsx(DocumentActionButton, { ...primaryAction, variant: primaryAction.variant || "default" }),
1409
- restActions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1410
- DocumentActionsMenu,
1411
- {
1412
- actions: restActions,
1413
- label: formatMessage({
1414
- id: "content-manager.containers.edit.panels.default.more-actions",
1415
- defaultMessage: "More document actions"
1416
- })
1417
- }
1418
- ) : null
1419
- ] }),
1420
- secondaryAction ? /* @__PURE__ */ jsxRuntime.jsx(
1421
- DocumentActionButton,
1422
- {
1423
- ...secondaryAction,
1424
- variant: secondaryAction.variant || "secondary"
1425
- }
1426
- ) : null
1427
- ] });
1428
- };
1429
- const DocumentActionButton = (action) => {
1430
- const [dialogId, setDialogId] = React__namespace.useState(null);
1431
- const { toggleNotification } = strapiAdmin.useNotification();
1432
- const handleClick = (action2) => async (e) => {
1433
- const { onClick = () => false, dialog, id } = action2;
1434
- const muteDialog = await onClick(e);
1435
- if (dialog && !muteDialog) {
1436
- switch (dialog.type) {
1437
- case "notification":
1438
- toggleNotification({
1439
- title: dialog.title,
1440
- message: dialog.content,
1441
- type: dialog.status,
1442
- timeout: dialog.timeout,
1443
- onClose: dialog.onClose
1444
- });
1445
- break;
1446
- case "dialog":
1447
- case "modal":
1448
- e.preventDefault();
1449
- setDialogId(id);
1450
- }
1451
- }
1452
- };
1453
- const handleClose = () => {
1454
- setDialogId(null);
1455
- };
1456
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1457
- /* @__PURE__ */ jsxRuntime.jsx(
1458
- designSystem.Button,
1459
- {
1460
- flex: "auto",
1461
- startIcon: action.icon,
1462
- disabled: action.disabled,
1463
- onClick: handleClick(action),
1464
- justifyContent: "center",
1465
- variant: action.variant || "default",
1466
- paddingTop: "7px",
1467
- paddingBottom: "7px",
1468
- children: action.label
1469
- }
1470
- ),
1471
- action.dialog?.type === "dialog" ? /* @__PURE__ */ jsxRuntime.jsx(
1472
- DocumentActionConfirmDialog,
1473
- {
1474
- ...action.dialog,
1475
- variant: action.dialog?.variant ?? action.variant,
1476
- isOpen: dialogId === action.id,
1477
- onClose: handleClose
1478
- }
1479
- ) : null,
1480
- action.dialog?.type === "modal" ? /* @__PURE__ */ jsxRuntime.jsx(
1481
- DocumentActionModal,
1482
- {
1483
- ...action.dialog,
1484
- onModalClose: handleClose,
1485
- isOpen: dialogId === action.id
1486
- }
1487
- ) : null
1488
- ] });
1489
- };
1490
- const DocumentActionsMenu = ({
1491
- actions: actions2,
1492
- children,
1493
- label,
1494
- variant = "tertiary"
1495
- }) => {
1496
- const [isOpen, setIsOpen] = React__namespace.useState(false);
1497
- const [dialogId, setDialogId] = React__namespace.useState(null);
1498
- const { formatMessage } = reactIntl.useIntl();
1499
- const { toggleNotification } = strapiAdmin.useNotification();
1500
- const isDisabled = actions2.every((action) => action.disabled) || actions2.length === 0;
1501
- const handleClick = (action) => async (e) => {
1502
- const { onClick = () => false, dialog, id } = action;
1503
- const muteDialog = await onClick(e);
1504
- if (dialog && !muteDialog) {
1505
- switch (dialog.type) {
1506
- case "notification":
1507
- toggleNotification({
1508
- title: dialog.title,
1509
- message: dialog.content,
1510
- type: dialog.status,
1511
- timeout: dialog.timeout,
1512
- onClose: dialog.onClose
1513
- });
1514
- break;
1515
- case "dialog":
1516
- case "modal":
1517
- setDialogId(id);
1518
- }
1519
- }
1520
- };
1521
- const handleClose = () => {
1522
- setDialogId(null);
1523
- setIsOpen(false);
1524
- };
1525
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
1526
- /* @__PURE__ */ jsxRuntime.jsxs(
1527
- StyledMoreButton,
1528
- {
1529
- disabled: isDisabled,
1530
- size: "S",
1531
- endIcon: null,
1532
- paddingTop: "4px",
1533
- paddingLeft: "7px",
1534
- paddingRight: "7px",
1535
- variant,
1536
- children: [
1537
- /* @__PURE__ */ jsxRuntime.jsx(Icons.More, { "aria-hidden": true, focusable: false }),
1538
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { tag: "span", children: label || formatMessage({
1539
- id: "content-manager.containers.edit.panels.default.more-actions",
1540
- defaultMessage: "More document actions"
1541
- }) })
1542
- ]
1543
- }
1544
- ),
1545
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1546
- actions2.map((action) => {
1547
- return /* @__PURE__ */ jsxRuntime.jsx(
1548
- designSystem.Menu.Item,
1549
- {
1550
- disabled: action.disabled,
1551
- onSelect: handleClick(action),
1552
- display: "block",
1553
- children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", gap: 4, children: [
1554
- /* @__PURE__ */ jsxRuntime.jsxs(
1555
- designSystem.Flex,
1556
- {
1557
- color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1558
- gap: 2,
1559
- tag: "span",
1560
- children: [
1561
- /* @__PURE__ */ jsxRuntime.jsx(
1562
- designSystem.Flex,
1563
- {
1564
- tag: "span",
1565
- color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1566
- children: action.icon
1567
- }
1568
- ),
1569
- action.label
1570
- ]
1571
- }
1572
- ),
1573
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsxRuntime.jsx(
1574
- designSystem.Flex,
1575
- {
1576
- alignItems: "center",
1577
- background: "alternative100",
1578
- borderStyle: "solid",
1579
- borderColor: "alternative200",
1580
- borderWidth: "1px",
1581
- height: 5,
1582
- paddingLeft: 2,
1583
- paddingRight: 2,
1584
- hasRadius: true,
1585
- color: "alternative600",
1586
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1587
- }
1588
- )
1589
- ] })
1590
- },
1591
- action.id
1592
- );
1593
- }),
1594
- children
1595
- ] }),
1596
- actions2.map((action) => {
1597
- return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
1598
- action.dialog?.type === "dialog" ? /* @__PURE__ */ jsxRuntime.jsx(
1599
- DocumentActionConfirmDialog,
1600
- {
1601
- ...action.dialog,
1602
- variant: action.variant,
1603
- isOpen: dialogId === action.id,
1604
- onClose: handleClose
1605
- }
1606
- ) : null,
1607
- action.dialog?.type === "modal" ? /* @__PURE__ */ jsxRuntime.jsx(
1608
- DocumentActionModal,
1609
- {
1610
- ...action.dialog,
1611
- onModalClose: handleClose,
1612
- isOpen: dialogId === action.id
1613
- }
1614
- ) : null
1615
- ] }, action.id);
1616
- })
1617
- ] });
1618
- };
1619
- const convertActionVariantToColor = (variant = "secondary") => {
1620
- switch (variant) {
1621
- case "danger":
1622
- return "danger600";
1623
- case "secondary":
1624
- return void 0;
1625
- case "success":
1626
- return "success600";
1627
- default:
1628
- return "primary600";
1629
- }
1630
- };
1631
- const convertActionVariantToIconColor = (variant = "secondary") => {
1632
- switch (variant) {
1633
- case "danger":
1634
- return "danger600";
1635
- case "secondary":
1636
- return "neutral500";
1637
- case "success":
1638
- return "success600";
1639
- default:
1640
- return "primary600";
1641
- }
1642
- };
1643
- const StyledMoreButton = styledComponents.styled(designSystem.Menu.Trigger)`
1644
- & > span {
1645
- display: flex;
1646
- }
1647
- `;
1648
- const DocumentActionConfirmDialog = ({
1649
- onClose,
1650
- onCancel,
1651
- onConfirm,
1652
- title,
1653
- content,
1654
- isOpen,
1655
- variant = "secondary"
1656
- }) => {
1657
- const { formatMessage } = reactIntl.useIntl();
1658
- const handleClose = async () => {
1659
- if (onCancel) {
1660
- await onCancel();
1661
- }
1662
- onClose();
1663
- };
1664
- const handleConfirm = async () => {
1665
- if (onConfirm) {
1666
- await onConfirm();
1667
- }
1668
- onClose();
1669
- };
1670
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
1671
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
1672
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: content }),
1673
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
1674
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", children: formatMessage({
1675
- id: "app.components.Button.cancel",
1676
- defaultMessage: "Cancel"
1677
- }) }) }),
1678
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, children: formatMessage({
1679
- id: "app.components.Button.confirm",
1680
- defaultMessage: "Confirm"
1681
- }) })
1682
- ] })
1683
- ] }) });
1684
- };
1685
- const DocumentActionModal = ({
1686
- isOpen,
1687
- title,
1688
- onClose,
1689
- footer: Footer,
1690
- content: Content,
1691
- onModalClose
1692
- }) => {
1693
- const handleClose = () => {
1694
- if (onClose) {
1695
- onClose();
1696
- }
1697
- onModalClose();
1698
- };
1699
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
1700
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: title }) }),
1701
- typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: Content }),
1702
- typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer
1703
- ] }) });
1704
- };
1705
- const PublishAction$1 = ({
1706
- activeTab,
1707
- documentId,
1708
- model,
1709
- collectionType,
1710
- meta,
1711
- document
1712
- }) => {
1713
- const { schema } = useDoc();
1714
- const navigate = reactRouterDom.useNavigate();
1715
- const { toggleNotification } = strapiAdmin.useNotification();
1716
- const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
1717
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
1718
- const { formatMessage } = reactIntl.useIntl();
1719
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1720
- "PublishAction",
1721
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1722
- );
1723
- const { publish } = useDocumentActions();
1724
- const [
1725
- countDraftRelations,
1726
- { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
1727
- ] = useLazyGetDraftRelationCountQuery();
1728
- const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React__namespace.useState(0);
1729
- const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React__namespace.useState(0);
1730
- const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1731
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1732
- const modified = strapiAdmin.useForm("PublishAction", ({ modified: modified2 }) => modified2);
1733
- const setSubmitting = strapiAdmin.useForm("PublishAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
1734
- const isSubmitting = strapiAdmin.useForm("PublishAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
1735
- const validate = strapiAdmin.useForm("PublishAction", (state) => state.validate);
1736
- const setErrors = strapiAdmin.useForm("PublishAction", (state) => state.setErrors);
1737
- const formValues = strapiAdmin.useForm("PublishAction", ({ values }) => values);
1738
- React__namespace.useEffect(() => {
1739
- if (isErrorDraftRelations) {
1740
- toggleNotification({
1741
- type: "danger",
1742
- message: formatMessage({
1743
- id: getTranslation("error.records.fetch-draft-relatons"),
1744
- defaultMessage: "An error occurred while fetching draft relations on this document."
1745
- })
1746
- });
1747
- }
1748
- }, [isErrorDraftRelations, toggleNotification, formatMessage]);
1749
- React__namespace.useEffect(() => {
1750
- const localDraftRelations = /* @__PURE__ */ new Set();
1751
- const extractDraftRelations = (data) => {
1752
- const relations = data.connect || [];
1753
- relations.forEach((relation) => {
1754
- if (relation.status === "draft") {
1755
- localDraftRelations.add(relation.id);
1756
- }
1757
- });
1758
- };
1759
- const traverseAndExtract = (data) => {
1760
- Object.entries(data).forEach(([key, value]) => {
1761
- if (key === "connect" && Array.isArray(value)) {
1762
- extractDraftRelations({ connect: value });
1763
- } else if (typeof value === "object" && value !== null) {
1764
- traverseAndExtract(value);
1765
- }
1766
- });
1767
- };
1768
- if (!documentId || modified) {
1769
- traverseAndExtract(formValues);
1770
- setLocalCountOfDraftRelations(localDraftRelations.size);
1771
- }
1772
- }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1773
- React__namespace.useEffect(() => {
1774
- if (documentId) {
1775
- const fetchDraftRelationsCount = async () => {
1776
- const { data, error } = await countDraftRelations({
1777
- collectionType,
1778
- model,
1779
- documentId,
1780
- params
1781
- });
1782
- if (error) {
1783
- throw error;
1784
- }
1785
- if (data) {
1786
- setServerCountOfDraftRelations(data.data);
1787
- }
1788
- };
1789
- fetchDraftRelationsCount();
1790
- }
1791
- }, [documentId, countDraftRelations, collectionType, model, params]);
1792
- const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1793
- if (!schema?.options?.draftAndPublish) {
1794
- return null;
1795
- }
1796
- const performPublish = async () => {
1797
- setSubmitting(true);
1798
- try {
1799
- const { errors } = await validate();
1800
- if (errors) {
1801
- toggleNotification({
1802
- type: "danger",
1803
- message: formatMessage({
1804
- id: "content-manager.validation.error",
1805
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1806
- })
1807
- });
1808
- return;
1809
- }
1810
- const res = await publish(
1811
- {
1812
- collectionType,
1813
- model,
1814
- documentId,
1815
- params
1816
- },
1817
- formValues
1818
- );
1819
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1820
- navigate({
1821
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1822
- search: rawQuery
1823
- });
1824
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1825
- setErrors(formatValidationErrors(res.error));
1826
- }
1827
- } finally {
1828
- setSubmitting(false);
1829
- }
1830
- };
1831
- const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1832
- const hasDraftRelations = totalDraftRelations > 0;
1833
- return {
1834
- /**
1835
- * Disabled when:
1836
- * - currently if you're cloning a document we don't support publish & clone at the same time.
1837
- * - the form is submitting
1838
- * - the active tab is the published tab
1839
- * - the document is already published & not modified
1840
- * - the document is being created & not modified
1841
- * - the user doesn't have the permission to publish
1842
- */
1843
- disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1844
- label: formatMessage({
1845
- id: "app.utils.publish",
1846
- defaultMessage: "Publish"
1847
- }),
1848
- onClick: async () => {
1849
- if (hasDraftRelations) {
1850
- return;
1851
- }
1852
- await performPublish();
1853
- },
1854
- dialog: hasDraftRelations ? {
1855
- type: "dialog",
1856
- variant: "danger",
1857
- footer: null,
1858
- title: formatMessage({
1859
- id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
1860
- defaultMessage: "Confirmation"
1861
- }),
1862
- content: formatMessage(
1863
- {
1864
- id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
1865
- defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
1866
- },
1788
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, alignItems: "stretch", width: "100%", children: [
1789
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1790
+ /* @__PURE__ */ jsxRuntime.jsx(DocumentActionButton, { ...primaryAction, variant: primaryAction.variant || "default" }),
1791
+ restActions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1792
+ DocumentActionsMenu,
1867
1793
  {
1868
- count: totalDraftRelations
1794
+ actions: restActions,
1795
+ label: formatMessage({
1796
+ id: "content-manager.containers.edit.panels.default.more-actions",
1797
+ defaultMessage: "More document actions"
1798
+ })
1869
1799
  }
1870
- ),
1871
- onConfirm: async () => {
1872
- await performPublish();
1800
+ ) : null
1801
+ ] }),
1802
+ secondaryAction ? /* @__PURE__ */ jsxRuntime.jsx(
1803
+ DocumentActionButton,
1804
+ {
1805
+ ...secondaryAction,
1806
+ variant: secondaryAction.variant || "secondary"
1873
1807
  }
1874
- } : void 0
1875
- };
1808
+ ) : null
1809
+ ] });
1876
1810
  };
1877
- PublishAction$1.type = "publish";
1878
- const UpdateAction = ({
1879
- activeTab,
1880
- documentId,
1881
- model,
1882
- collectionType
1883
- }) => {
1884
- const navigate = reactRouterDom.useNavigate();
1811
+ const DocumentActionButton = (action) => {
1812
+ const [dialogId, setDialogId] = React__namespace.useState(null);
1885
1813
  const { toggleNotification } = strapiAdmin.useNotification();
1886
- const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
1887
- const cloneMatch = reactRouterDom.useMatch(CLONE_PATH);
1888
- const isCloning = cloneMatch !== null;
1889
- const { formatMessage } = reactIntl.useIntl();
1890
- useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1891
- canCreate: canCreate2,
1892
- canUpdate: canUpdate2
1893
- }));
1894
- const { create, update, clone } = useDocumentActions();
1895
- const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1896
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1897
- const isSubmitting = strapiAdmin.useForm("UpdateAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
1898
- const modified = strapiAdmin.useForm("UpdateAction", ({ modified: modified2 }) => modified2);
1899
- const setSubmitting = strapiAdmin.useForm("UpdateAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
1900
- const document = strapiAdmin.useForm("UpdateAction", ({ values }) => values);
1901
- const validate = strapiAdmin.useForm("UpdateAction", (state) => state.validate);
1902
- const setErrors = strapiAdmin.useForm("UpdateAction", (state) => state.setErrors);
1903
- const resetForm = strapiAdmin.useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
1904
- return {
1905
- /**
1906
- * Disabled when:
1907
- * - the form is submitting
1908
- * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1909
- * - the active tab is the published tab
1910
- */
1911
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1912
- label: formatMessage({
1913
- id: "content-manager.containers.Edit.save",
1914
- defaultMessage: "Save"
1915
- }),
1916
- onClick: async () => {
1917
- setSubmitting(true);
1918
- try {
1919
- const { errors } = await validate();
1920
- if (errors) {
1814
+ const handleClick = (action2) => async (e) => {
1815
+ const { onClick = () => false, dialog, id } = action2;
1816
+ const muteDialog = await onClick(e);
1817
+ if (dialog && !muteDialog) {
1818
+ switch (dialog.type) {
1819
+ case "notification":
1921
1820
  toggleNotification({
1922
- type: "danger",
1923
- message: formatMessage({
1924
- id: "content-manager.validation.error",
1925
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1926
- })
1821
+ title: dialog.title,
1822
+ message: dialog.content,
1823
+ type: dialog.status,
1824
+ timeout: dialog.timeout,
1825
+ onClose: dialog.onClose
1927
1826
  });
1928
- return;
1929
- }
1930
- if (isCloning) {
1931
- const res = await clone(
1932
- {
1933
- model,
1934
- documentId: cloneMatch.params.origin,
1935
- params
1936
- },
1937
- document
1938
- );
1939
- if ("data" in res) {
1940
- navigate(
1941
- {
1942
- pathname: `../${res.data.documentId}`,
1943
- search: rawQuery
1944
- },
1945
- { relative: "path" }
1946
- );
1947
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1948
- setErrors(formatValidationErrors(res.error));
1949
- }
1950
- } else if (documentId || collectionType === SINGLE_TYPES) {
1951
- const res = await update(
1952
- {
1953
- collectionType,
1954
- model,
1955
- documentId,
1956
- params
1957
- },
1958
- document
1959
- );
1960
- if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1961
- setErrors(formatValidationErrors(res.error));
1962
- } else {
1963
- resetForm();
1964
- }
1965
- } else {
1966
- const res = await create(
1967
- {
1968
- model,
1969
- params
1970
- },
1971
- document
1972
- );
1973
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1974
- navigate(
1975
- {
1976
- pathname: `../${res.data.documentId}`,
1977
- search: rawQuery
1978
- },
1979
- { replace: true, relative: "path" }
1980
- );
1981
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1982
- setErrors(formatValidationErrors(res.error));
1983
- }
1984
- }
1985
- } finally {
1986
- setSubmitting(false);
1827
+ break;
1828
+ case "dialog":
1829
+ case "modal":
1830
+ e.preventDefault();
1831
+ setDialogId(id);
1987
1832
  }
1988
1833
  }
1989
1834
  };
1990
- };
1991
- UpdateAction.type = "update";
1992
- const UNPUBLISH_DRAFT_OPTIONS = {
1993
- KEEP: "keep",
1994
- DISCARD: "discard"
1995
- };
1996
- const UnpublishAction$1 = ({
1997
- activeTab,
1998
- documentId,
1999
- model,
2000
- collectionType,
2001
- document
1835
+ const handleClose = () => {
1836
+ setDialogId(null);
1837
+ };
1838
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1839
+ /* @__PURE__ */ jsxRuntime.jsx(
1840
+ designSystem.Button,
1841
+ {
1842
+ flex: "auto",
1843
+ startIcon: action.icon,
1844
+ disabled: action.disabled,
1845
+ onClick: handleClick(action),
1846
+ justifyContent: "center",
1847
+ variant: action.variant || "default",
1848
+ paddingTop: "7px",
1849
+ paddingBottom: "7px",
1850
+ children: action.label
1851
+ }
1852
+ ),
1853
+ action.dialog?.type === "dialog" ? /* @__PURE__ */ jsxRuntime.jsx(
1854
+ DocumentActionConfirmDialog,
1855
+ {
1856
+ ...action.dialog,
1857
+ variant: action.dialog?.variant ?? action.variant,
1858
+ isOpen: dialogId === action.id,
1859
+ onClose: handleClose
1860
+ }
1861
+ ) : null,
1862
+ action.dialog?.type === "modal" ? /* @__PURE__ */ jsxRuntime.jsx(
1863
+ DocumentActionModal,
1864
+ {
1865
+ ...action.dialog,
1866
+ onModalClose: handleClose,
1867
+ isOpen: dialogId === action.id
1868
+ }
1869
+ ) : null
1870
+ ] });
1871
+ };
1872
+ const DocumentActionsMenu = ({
1873
+ actions: actions2,
1874
+ children,
1875
+ label,
1876
+ variant = "tertiary"
2002
1877
  }) => {
1878
+ const [isOpen, setIsOpen] = React__namespace.useState(false);
1879
+ const [dialogId, setDialogId] = React__namespace.useState(null);
2003
1880
  const { formatMessage } = reactIntl.useIntl();
2004
- const { schema } = useDoc();
2005
- const canPublish = useDocumentRBAC("UnpublishAction", ({ canPublish: canPublish2 }) => canPublish2);
2006
- const { unpublish } = useDocumentActions();
2007
- const [{ query }] = strapiAdmin.useQueryParams();
2008
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
2009
1881
  const { toggleNotification } = strapiAdmin.useNotification();
2010
- const [shouldKeepDraft, setShouldKeepDraft] = React__namespace.useState(true);
2011
- const isDocumentModified = document?.status === "modified";
2012
- const handleChange = (value) => {
2013
- setShouldKeepDraft(value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
2014
- };
2015
- if (!schema?.options?.draftAndPublish) {
2016
- return null;
2017
- }
2018
- return {
2019
- disabled: !canPublish || activeTab === "published" || document?.status !== "published" && document?.status !== "modified",
2020
- label: formatMessage({
2021
- id: "app.utils.unpublish",
2022
- defaultMessage: "Unpublish"
2023
- }),
2024
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2025
- onClick: async () => {
2026
- if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
2027
- if (!documentId) {
2028
- console.error(
2029
- "You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
2030
- );
1882
+ const isDisabled = actions2.every((action) => action.disabled) || actions2.length === 0;
1883
+ const handleClick = (action) => async (e) => {
1884
+ const { onClick = () => false, dialog, id } = action;
1885
+ const muteDialog = await onClick(e);
1886
+ if (dialog && !muteDialog) {
1887
+ switch (dialog.type) {
1888
+ case "notification":
2031
1889
  toggleNotification({
2032
- message: formatMessage({
2033
- id: "content-manager.actions.unpublish.error",
2034
- defaultMessage: "An error occurred while trying to unpublish the document."
2035
- }),
2036
- type: "danger"
1890
+ title: dialog.title,
1891
+ message: dialog.content,
1892
+ type: dialog.status,
1893
+ timeout: dialog.timeout,
1894
+ onClose: dialog.onClose
2037
1895
  });
2038
- }
2039
- return;
1896
+ break;
1897
+ case "dialog":
1898
+ case "modal":
1899
+ setDialogId(id);
2040
1900
  }
2041
- await unpublish({
2042
- collectionType,
2043
- model,
2044
- documentId,
2045
- params
2046
- });
2047
- },
2048
- dialog: isDocumentModified ? {
2049
- type: "dialog",
2050
- title: formatMessage({
2051
- id: "app.components.ConfirmDialog.title",
2052
- defaultMessage: "Confirmation"
2053
- }),
2054
- content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "flex-start", direction: "column", gap: 6, children: [
2055
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", direction: "column", gap: 2, children: [
2056
- /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2057
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2058
- id: "content-manager.actions.unpublish.dialog.body",
2059
- defaultMessage: "Are you sure?"
1901
+ }
1902
+ };
1903
+ const handleClose = () => {
1904
+ setDialogId(null);
1905
+ setIsOpen(false);
1906
+ };
1907
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
1908
+ /* @__PURE__ */ jsxRuntime.jsxs(
1909
+ designSystem.Menu.Trigger,
1910
+ {
1911
+ disabled: isDisabled,
1912
+ size: "S",
1913
+ endIcon: null,
1914
+ paddingTop: "4px",
1915
+ paddingLeft: "7px",
1916
+ paddingRight: "7px",
1917
+ variant,
1918
+ children: [
1919
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.More, { "aria-hidden": true, focusable: false }),
1920
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { tag: "span", children: label || formatMessage({
1921
+ id: "content-manager.containers.edit.panels.default.more-actions",
1922
+ defaultMessage: "More document actions"
2060
1923
  }) })
2061
- ] }),
2062
- /* @__PURE__ */ jsxRuntime.jsxs(
2063
- designSystem.Radio.Group,
2064
- {
2065
- defaultValue: UNPUBLISH_DRAFT_OPTIONS.KEEP,
2066
- name: "discard-options",
2067
- "aria-label": formatMessage({
2068
- id: "content-manager.actions.unpublish.dialog.radio-label",
2069
- defaultMessage: "Choose an option to unpublish the document."
2070
- }),
2071
- onValueChange: handleChange,
2072
- children: [
2073
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { checked: shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.KEEP, children: formatMessage({
2074
- id: "content-manager.actions.unpublish.dialog.option.keep-draft",
2075
- defaultMessage: "Keep draft"
2076
- }) }),
2077
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { checked: !shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.DISCARD, children: formatMessage({
2078
- id: "content-manager.actions.unpublish.dialog.option.replace-draft",
2079
- defaultMessage: "Replace draft"
2080
- }) })
2081
- ]
2082
- }
2083
- )
2084
- ] }),
2085
- onConfirm: async () => {
2086
- if (!documentId && collectionType !== SINGLE_TYPES) {
2087
- console.error(
2088
- "You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
2089
- );
2090
- toggleNotification({
2091
- message: formatMessage({
2092
- id: "content-manager.actions.unpublish.error",
2093
- defaultMessage: "An error occurred while trying to unpublish the document."
2094
- }),
2095
- type: "danger"
2096
- });
2097
- }
2098
- await unpublish(
1924
+ ]
1925
+ }
1926
+ ),
1927
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1928
+ actions2.map((action) => {
1929
+ return /* @__PURE__ */ jsxRuntime.jsx(
1930
+ designSystem.Menu.Item,
2099
1931
  {
2100
- collectionType,
2101
- model,
2102
- documentId,
2103
- params
1932
+ disabled: action.disabled,
1933
+ onSelect: handleClick(action),
1934
+ display: "block",
1935
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", gap: 4, children: [
1936
+ /* @__PURE__ */ jsxRuntime.jsxs(
1937
+ designSystem.Flex,
1938
+ {
1939
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1940
+ gap: 2,
1941
+ tag: "span",
1942
+ children: [
1943
+ /* @__PURE__ */ jsxRuntime.jsx(
1944
+ designSystem.Flex,
1945
+ {
1946
+ tag: "span",
1947
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1948
+ children: action.icon
1949
+ }
1950
+ ),
1951
+ action.label
1952
+ ]
1953
+ }
1954
+ ),
1955
+ action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsxRuntime.jsx(
1956
+ designSystem.Flex,
1957
+ {
1958
+ alignItems: "center",
1959
+ background: "alternative100",
1960
+ borderStyle: "solid",
1961
+ borderColor: "alternative200",
1962
+ borderWidth: "1px",
1963
+ height: 5,
1964
+ paddingLeft: 2,
1965
+ paddingRight: 2,
1966
+ hasRadius: true,
1967
+ color: "alternative600",
1968
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1969
+ }
1970
+ )
1971
+ ] })
2104
1972
  },
2105
- !shouldKeepDraft
1973
+ action.id
2106
1974
  );
2107
- }
2108
- } : void 0,
2109
- variant: "danger",
2110
- position: ["panel", "table-row"]
1975
+ }),
1976
+ children
1977
+ ] }),
1978
+ actions2.map((action) => {
1979
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
1980
+ action.dialog?.type === "dialog" ? /* @__PURE__ */ jsxRuntime.jsx(
1981
+ DocumentActionConfirmDialog,
1982
+ {
1983
+ ...action.dialog,
1984
+ variant: action.variant,
1985
+ isOpen: dialogId === action.id,
1986
+ onClose: handleClose
1987
+ }
1988
+ ) : null,
1989
+ action.dialog?.type === "modal" ? /* @__PURE__ */ jsxRuntime.jsx(
1990
+ DocumentActionModal,
1991
+ {
1992
+ ...action.dialog,
1993
+ onModalClose: handleClose,
1994
+ isOpen: dialogId === action.id
1995
+ }
1996
+ ) : null
1997
+ ] }, action.id);
1998
+ })
1999
+ ] });
2000
+ };
2001
+ const convertActionVariantToColor = (variant = "secondary") => {
2002
+ switch (variant) {
2003
+ case "danger":
2004
+ return "danger600";
2005
+ case "secondary":
2006
+ return void 0;
2007
+ case "success":
2008
+ return "success600";
2009
+ default:
2010
+ return "primary600";
2011
+ }
2012
+ };
2013
+ const convertActionVariantToIconColor = (variant = "secondary") => {
2014
+ switch (variant) {
2015
+ case "danger":
2016
+ return "danger600";
2017
+ case "secondary":
2018
+ return "neutral500";
2019
+ case "success":
2020
+ return "success600";
2021
+ default:
2022
+ return "primary600";
2023
+ }
2024
+ };
2025
+ const DocumentActionConfirmDialog = ({
2026
+ onClose,
2027
+ onCancel,
2028
+ onConfirm,
2029
+ title,
2030
+ content,
2031
+ isOpen,
2032
+ variant = "secondary"
2033
+ }) => {
2034
+ const { formatMessage } = reactIntl.useIntl();
2035
+ const handleClose = async () => {
2036
+ if (onCancel) {
2037
+ await onCancel();
2038
+ }
2039
+ onClose();
2111
2040
  };
2041
+ const handleConfirm = async () => {
2042
+ if (onConfirm) {
2043
+ await onConfirm();
2044
+ }
2045
+ onClose();
2046
+ };
2047
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2048
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2049
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: content }),
2050
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
2051
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
2052
+ id: "app.components.Button.cancel",
2053
+ defaultMessage: "Cancel"
2054
+ }) }) }),
2055
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
2056
+ id: "app.components.Button.confirm",
2057
+ defaultMessage: "Confirm"
2058
+ }) })
2059
+ ] })
2060
+ ] }) });
2112
2061
  };
2113
- UnpublishAction$1.type = "unpublish";
2114
- const DiscardAction = ({
2115
- activeTab,
2116
- documentId,
2117
- model,
2118
- collectionType,
2119
- document
2062
+ const DocumentActionModal = ({
2063
+ isOpen,
2064
+ title,
2065
+ onClose,
2066
+ footer: Footer,
2067
+ content: Content,
2068
+ onModalClose
2120
2069
  }) => {
2121
- const { formatMessage } = reactIntl.useIntl();
2122
- const { schema } = useDoc();
2123
- const canUpdate = useDocumentRBAC("DiscardAction", ({ canUpdate: canUpdate2 }) => canUpdate2);
2124
- const { discard } = useDocumentActions();
2125
- const [{ query }] = strapiAdmin.useQueryParams();
2126
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
2127
- if (!schema?.options?.draftAndPublish) {
2128
- return null;
2129
- }
2130
- return {
2131
- disabled: !canUpdate || activeTab === "published" || document?.status !== "modified",
2132
- label: formatMessage({
2133
- id: "content-manager.actions.discard.label",
2134
- defaultMessage: "Discard changes"
2135
- }),
2136
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2137
- position: ["panel", "table-row"],
2138
- variant: "danger",
2139
- dialog: {
2140
- type: "dialog",
2141
- title: formatMessage({
2142
- id: "app.components.ConfirmDialog.title",
2143
- defaultMessage: "Confirmation"
2144
- }),
2145
- content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
2146
- /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2147
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2148
- id: "content-manager.actions.discard.dialog.body",
2149
- defaultMessage: "Are you sure?"
2150
- }) })
2151
- ] }),
2152
- onConfirm: async () => {
2153
- await discard({
2154
- collectionType,
2155
- model,
2156
- documentId,
2157
- params
2158
- });
2159
- }
2070
+ const handleClose = () => {
2071
+ if (onClose) {
2072
+ onClose();
2160
2073
  }
2074
+ onModalClose();
2161
2075
  };
2076
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
2077
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: title }) }),
2078
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: Content }),
2079
+ typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer
2080
+ ] }) });
2162
2081
  };
2163
- DiscardAction.type = "discard";
2164
- const StyledCrossCircle = styledComponents.styled(Icons.CrossCircle)`
2165
- path {
2166
- fill: currentColor;
2167
- }
2168
- `;
2169
- const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2170
- const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2171
- const RelativeTime = React__namespace.forwardRef(
2172
- ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
2173
- const { formatRelativeTime, formatDate, formatTime } = reactIntl.useIntl();
2174
- const interval = dateFns.intervalToDuration({
2175
- start: timestamp,
2176
- end: Date.now()
2177
- // see https://github.com/date-fns/date-fns/issues/2891 – No idea why it's all partial it returns it every time.
2178
- });
2179
- const unit = intervals.find((intervalUnit) => {
2180
- return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2181
- });
2182
- const relativeTime = dateFns.isPast(timestamp) ? -interval[unit] : interval[unit];
2183
- const customInterval = customIntervals.find(
2184
- (custom) => interval[custom.unit] < custom.threshold
2185
- );
2186
- const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, { numeric: "auto" });
2187
- return /* @__PURE__ */ jsxRuntime.jsx(
2188
- "time",
2189
- {
2190
- ref: forwardedRef,
2191
- dateTime: timestamp.toISOString(),
2192
- role: "time",
2193
- title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
2194
- ...restProps,
2195
- children: displayText
2196
- }
2197
- );
2198
- }
2199
- );
2200
- const getDisplayName = ({
2201
- firstname,
2202
- lastname,
2203
- username,
2204
- email
2205
- } = {}) => {
2206
- if (username) {
2207
- return username;
2082
+ const transformData = (data) => {
2083
+ if (Array.isArray(data)) {
2084
+ return data.map(transformData);
2208
2085
  }
2209
- if (firstname) {
2210
- return `${firstname} ${lastname ?? ""}`.trim();
2086
+ if (typeof data === "object" && data !== null) {
2087
+ if ("apiData" in data) {
2088
+ return data.apiData;
2089
+ }
2090
+ return mapValues__default.default(transformData)(data);
2211
2091
  }
2212
- return email ?? "";
2213
- };
2214
- const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2215
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2216
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2217
- 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
+ return data;
2218
2093
  };
2219
- const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2220
- const { formatMessage } = reactIntl.useIntl();
2094
+ const PublishAction$1 = ({
2095
+ activeTab,
2096
+ documentId,
2097
+ model,
2098
+ collectionType,
2099
+ meta,
2100
+ document
2101
+ }) => {
2102
+ const { schema } = useDoc();
2103
+ const navigate = reactRouterDom.useNavigate();
2104
+ const { toggleNotification } = strapiAdmin.useNotification();
2105
+ const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
2106
+ const isListView = reactRouterDom.useMatch(LIST_PATH) !== null;
2221
2107
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2222
- const title = isCreating ? formatMessage({
2223
- id: "content-manager.containers.edit.title.new",
2224
- defaultMessage: "Create an entry"
2225
- }) : documentTitle;
2226
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2227
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.BackButton, {}),
2228
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2229
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2230
- /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2231
- ] }),
2232
- status ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2233
- ] });
2234
- };
2235
- const HeaderToolbar = () => {
2236
2108
  const { formatMessage } = reactIntl.useIntl();
2237
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2109
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
2110
+ const { publish } = useDocumentActions();
2238
2111
  const [
2239
- {
2240
- query: { status = "draft" }
2112
+ countDraftRelations,
2113
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2114
+ ] = useLazyGetDraftRelationCountQuery();
2115
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React__namespace.useState(0);
2116
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React__namespace.useState(0);
2117
+ const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
2118
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
2119
+ const modified = strapiAdmin.useForm("PublishAction", ({ modified: modified2 }) => modified2);
2120
+ const setSubmitting = strapiAdmin.useForm("PublishAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
2121
+ const isSubmitting = strapiAdmin.useForm("PublishAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
2122
+ const validate = strapiAdmin.useForm("PublishAction", (state) => state.validate);
2123
+ const setErrors = strapiAdmin.useForm("PublishAction", (state) => state.setErrors);
2124
+ const formValues = strapiAdmin.useForm("PublishAction", ({ values }) => values);
2125
+ React__namespace.useEffect(() => {
2126
+ if (isErrorDraftRelations) {
2127
+ toggleNotification({
2128
+ type: "danger",
2129
+ message: formatMessage({
2130
+ id: getTranslation("error.records.fetch-draft-relatons"),
2131
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2132
+ })
2133
+ });
2241
2134
  }
2242
- ] = strapiAdmin.useQueryParams();
2243
- const { model, id, document, meta, collectionType } = useDoc();
2244
- const plugins = strapiAdmin.useStrapiApp("HeaderToolbar", (state) => state.plugins);
2245
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
2246
- /* @__PURE__ */ jsxRuntime.jsx(
2247
- strapiAdmin.DescriptionComponentRenderer,
2248
- {
2249
- props: {
2250
- activeTab: status,
2251
- model,
2252
- documentId: id,
2253
- document: isCloning ? void 0 : document,
2254
- meta: isCloning ? void 0 : meta,
2255
- collectionType
2256
- },
2257
- descriptions: plugins["content-manager"].apis.getHeaderActions(),
2258
- children: (actions2) => {
2259
- if (actions2.length > 0) {
2260
- return /* @__PURE__ */ jsxRuntime.jsx(HeaderActions, { actions: actions2 });
2261
- } else {
2262
- return null;
2263
- }
2135
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2136
+ React__namespace.useEffect(() => {
2137
+ const localDraftRelations = /* @__PURE__ */ new Set();
2138
+ const extractDraftRelations = (data) => {
2139
+ const relations = data.connect || [];
2140
+ relations.forEach((relation) => {
2141
+ if (relation.status === "draft") {
2142
+ localDraftRelations.add(relation.id);
2264
2143
  }
2265
- }
2266
- ),
2267
- /* @__PURE__ */ jsxRuntime.jsx(
2268
- strapiAdmin.DescriptionComponentRenderer,
2269
- {
2270
- props: {
2271
- activeTab: status,
2272
- model,
2273
- documentId: id,
2274
- document: isCloning ? void 0 : document,
2275
- meta: isCloning ? void 0 : meta,
2276
- collectionType
2277
- },
2278
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2279
- children: (actions2) => {
2280
- const headerActions = actions2.filter((action) => {
2281
- const positions = Array.isArray(action.position) ? action.position : [action.position];
2282
- return positions.includes("header");
2283
- });
2284
- return /* @__PURE__ */ jsxRuntime.jsx(
2285
- DocumentActionsMenu,
2286
- {
2287
- actions: headerActions,
2288
- label: formatMessage({
2289
- id: "content-manager.containers.edit.header.more-actions",
2290
- defaultMessage: "More actions"
2291
- }),
2292
- children: /* @__PURE__ */ jsxRuntime.jsx(Information, { activeTab: status })
2293
- }
2294
- );
2144
+ });
2145
+ };
2146
+ const traverseAndExtract = (data) => {
2147
+ Object.entries(data).forEach(([key, value]) => {
2148
+ if (key === "connect" && Array.isArray(value)) {
2149
+ extractDraftRelations({ connect: value });
2150
+ } else if (typeof value === "object" && value !== null) {
2151
+ traverseAndExtract(value);
2295
2152
  }
2153
+ });
2154
+ };
2155
+ if (!documentId || modified) {
2156
+ traverseAndExtract(formValues);
2157
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2158
+ }
2159
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2160
+ React__namespace.useEffect(() => {
2161
+ if (!document || !document.documentId || isListView) {
2162
+ return;
2163
+ }
2164
+ const fetchDraftRelationsCount = async () => {
2165
+ const { data, error } = await countDraftRelations({
2166
+ collectionType,
2167
+ model,
2168
+ documentId,
2169
+ params
2170
+ });
2171
+ if (error) {
2172
+ throw error;
2296
2173
  }
2297
- )
2298
- ] });
2299
- };
2300
- const Information = ({ activeTab }) => {
2301
- const { formatMessage } = reactIntl.useIntl();
2302
- const { document, meta } = useDoc();
2303
- if (!document || !document.id) {
2174
+ if (data) {
2175
+ setServerCountOfDraftRelations(data.data);
2176
+ }
2177
+ };
2178
+ fetchDraftRelationsCount();
2179
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
2180
+ const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
2181
+ if (!schema?.options?.draftAndPublish) {
2304
2182
  return null;
2305
2183
  }
2306
- const createAndUpdateDocument = activeTab === "draft" ? document : meta?.availableStatus.find((status) => status.publishedAt === null);
2307
- const publishDocument = activeTab === "published" ? document : meta?.availableStatus.find((status) => status.publishedAt !== null);
2308
- const creator = createAndUpdateDocument?.[CREATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[CREATED_BY_ATTRIBUTE_NAME]) : null;
2309
- const updator = createAndUpdateDocument?.[UPDATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[UPDATED_BY_ATTRIBUTE_NAME]) : null;
2310
- const information = [
2311
- {
2312
- isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2313
- label: formatMessage({
2314
- id: "content-manager.containers.edit.information.last-published.label",
2315
- defaultMessage: "Last published"
2316
- }),
2317
- value: formatMessage(
2184
+ const performPublish = async () => {
2185
+ setSubmitting(true);
2186
+ try {
2187
+ const { errors } = await validate(true, {
2188
+ status: "published"
2189
+ });
2190
+ if (errors) {
2191
+ toggleNotification({
2192
+ type: "danger",
2193
+ message: formatMessage({
2194
+ id: "content-manager.validation.error",
2195
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2196
+ })
2197
+ });
2198
+ return;
2199
+ }
2200
+ const res = await publish(
2318
2201
  {
2319
- id: "content-manager.containers.edit.information.last-published.value",
2320
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2202
+ collectionType,
2203
+ model,
2204
+ documentId,
2205
+ params
2321
2206
  },
2322
- {
2323
- time: /* @__PURE__ */ jsxRuntime.jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
2324
- isAnonymous: !publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME],
2325
- author: publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME] ? getDisplayName(publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME]) : null
2326
- }
2327
- )
2207
+ transformData(formValues)
2208
+ );
2209
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2210
+ navigate({
2211
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2212
+ search: rawQuery
2213
+ });
2214
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2215
+ setErrors(formatValidationErrors(res.error));
2216
+ }
2217
+ } finally {
2218
+ setSubmitting(false);
2219
+ }
2220
+ };
2221
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2222
+ const enableDraftRelationsCount = false;
2223
+ const hasDraftRelations = enableDraftRelationsCount;
2224
+ return {
2225
+ /**
2226
+ * Disabled when:
2227
+ * - currently if you're cloning a document we don't support publish & clone at the same time.
2228
+ * - the form is submitting
2229
+ * - the active tab is the published tab
2230
+ * - the document is already published & not modified
2231
+ * - the document is being created & not modified
2232
+ * - the user doesn't have the permission to publish
2233
+ */
2234
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
2235
+ label: formatMessage({
2236
+ id: "app.utils.publish",
2237
+ defaultMessage: "Publish"
2238
+ }),
2239
+ onClick: async () => {
2240
+ await performPublish();
2328
2241
  },
2329
- {
2330
- isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2331
- label: formatMessage({
2332
- id: "content-manager.containers.edit.information.last-draft.label",
2333
- defaultMessage: "Last draft"
2242
+ dialog: hasDraftRelations ? {
2243
+ type: "dialog",
2244
+ variant: "danger",
2245
+ footer: null,
2246
+ title: formatMessage({
2247
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2248
+ defaultMessage: "Confirmation"
2334
2249
  }),
2335
- value: formatMessage(
2250
+ content: formatMessage(
2336
2251
  {
2337
- id: "content-manager.containers.edit.information.last-draft.value",
2338
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2252
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2253
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2339
2254
  },
2340
2255
  {
2341
- time: /* @__PURE__ */ jsxRuntime.jsx(
2342
- RelativeTime,
2343
- {
2344
- timestamp: new Date(createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME])
2345
- }
2346
- ),
2347
- isAnonymous: !updator,
2348
- author: updator
2256
+ count: totalDraftRelations
2349
2257
  }
2350
- )
2351
- },
2352
- {
2353
- isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2354
- label: formatMessage({
2355
- id: "content-manager.containers.edit.information.document.label",
2356
- defaultMessage: "Document"
2357
- }),
2358
- value: formatMessage(
2359
- {
2360
- id: "content-manager.containers.edit.information.document.value",
2361
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2362
- },
2363
- {
2364
- time: /* @__PURE__ */ jsxRuntime.jsx(
2365
- RelativeTime,
2366
- {
2367
- timestamp: new Date(createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME])
2368
- }
2369
- ),
2370
- isAnonymous: !creator,
2371
- author: creator
2258
+ ),
2259
+ onConfirm: async () => {
2260
+ await performPublish();
2261
+ }
2262
+ } : void 0
2263
+ };
2264
+ };
2265
+ PublishAction$1.type = "publish";
2266
+ const UpdateAction = ({
2267
+ activeTab,
2268
+ documentId,
2269
+ model,
2270
+ collectionType
2271
+ }) => {
2272
+ const navigate = reactRouterDom.useNavigate();
2273
+ const { toggleNotification } = strapiAdmin.useNotification();
2274
+ const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
2275
+ const cloneMatch = reactRouterDom.useMatch(CLONE_PATH);
2276
+ const isCloning = cloneMatch !== null;
2277
+ const { formatMessage } = reactIntl.useIntl();
2278
+ const { create, update, clone } = useDocumentActions();
2279
+ const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
2280
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
2281
+ const isSubmitting = strapiAdmin.useForm("UpdateAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
2282
+ const modified = strapiAdmin.useForm("UpdateAction", ({ modified: modified2 }) => modified2);
2283
+ const setSubmitting = strapiAdmin.useForm("UpdateAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
2284
+ const document = strapiAdmin.useForm("UpdateAction", ({ values }) => values);
2285
+ const validate = strapiAdmin.useForm("UpdateAction", (state) => state.validate);
2286
+ const setErrors = strapiAdmin.useForm("UpdateAction", (state) => state.setErrors);
2287
+ const resetForm = strapiAdmin.useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
2288
+ return {
2289
+ /**
2290
+ * Disabled when:
2291
+ * - the form is submitting
2292
+ * - the document is not modified & we're not cloning (you can save a clone entity straight away)
2293
+ * - the active tab is the published tab
2294
+ */
2295
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
2296
+ label: formatMessage({
2297
+ id: "content-manager.containers.Edit.save",
2298
+ defaultMessage: "Save"
2299
+ }),
2300
+ onClick: async () => {
2301
+ setSubmitting(true);
2302
+ try {
2303
+ const { errors } = await validate(true, {
2304
+ status: "draft"
2305
+ });
2306
+ if (errors) {
2307
+ toggleNotification({
2308
+ type: "danger",
2309
+ message: formatMessage({
2310
+ id: "content-manager.validation.error",
2311
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2312
+ })
2313
+ });
2314
+ return;
2372
2315
  }
2373
- )
2374
- }
2375
- ].filter((info) => info.isDisplayed);
2376
- return /* @__PURE__ */ jsxRuntime.jsx(
2377
- designSystem.Flex,
2378
- {
2379
- borderWidth: "1px 0 0 0",
2380
- borderStyle: "solid",
2381
- borderColor: "neutral150",
2382
- direction: "column",
2383
- marginTop: 2,
2384
- tag: "dl",
2385
- padding: 5,
2386
- gap: 3,
2387
- alignItems: "flex-start",
2388
- marginLeft: "-0.4rem",
2389
- marginRight: "-0.4rem",
2390
- width: "calc(100% + 8px)",
2391
- children: information.map((info) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, direction: "column", alignItems: "flex-start", children: [
2392
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "dt", variant: "pi", fontWeight: "bold", children: info.label }),
2393
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "dd", variant: "pi", textColor: "neutral600", children: info.value })
2394
- ] }, info.label))
2316
+ if (isCloning) {
2317
+ const res = await clone(
2318
+ {
2319
+ model,
2320
+ documentId: cloneMatch.params.origin,
2321
+ params
2322
+ },
2323
+ transformData(document)
2324
+ );
2325
+ if ("data" in res) {
2326
+ navigate(
2327
+ {
2328
+ pathname: `../${res.data.documentId}`,
2329
+ search: rawQuery
2330
+ },
2331
+ { relative: "path" }
2332
+ );
2333
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2334
+ setErrors(formatValidationErrors(res.error));
2335
+ }
2336
+ } else if (documentId || collectionType === SINGLE_TYPES) {
2337
+ const res = await update(
2338
+ {
2339
+ collectionType,
2340
+ model,
2341
+ documentId,
2342
+ params
2343
+ },
2344
+ transformData(document)
2345
+ );
2346
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2347
+ setErrors(formatValidationErrors(res.error));
2348
+ } else {
2349
+ resetForm();
2350
+ }
2351
+ } else {
2352
+ const res = await create(
2353
+ {
2354
+ model,
2355
+ params
2356
+ },
2357
+ transformData(document)
2358
+ );
2359
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2360
+ navigate(
2361
+ {
2362
+ pathname: `../${res.data.documentId}`,
2363
+ search: rawQuery
2364
+ },
2365
+ { replace: true, relative: "path" }
2366
+ );
2367
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2368
+ setErrors(formatValidationErrors(res.error));
2369
+ }
2370
+ }
2371
+ } finally {
2372
+ setSubmitting(false);
2373
+ }
2395
2374
  }
2396
- );
2375
+ };
2397
2376
  };
2398
- const HeaderActions = ({ actions: actions2 }) => {
2399
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: actions2.map((action) => {
2400
- if ("options" in action) {
2401
- return /* @__PURE__ */ jsxRuntime.jsx(
2402
- designSystem.SingleSelect,
2403
- {
2404
- size: "S",
2405
- disabled: action.disabled,
2406
- "aria-label": action.label,
2407
- onChange: action.onSelect,
2408
- value: action.value,
2409
- children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { ...option, children: label }, option.value))
2410
- },
2411
- action.id
2412
- );
2413
- } else {
2414
- return null;
2415
- }
2416
- }) });
2377
+ UpdateAction.type = "update";
2378
+ const UNPUBLISH_DRAFT_OPTIONS = {
2379
+ KEEP: "keep",
2380
+ DISCARD: "discard"
2417
2381
  };
2418
- const ConfigureTheViewAction = ({ collectionType, model }) => {
2419
- const navigate = reactRouterDom.useNavigate();
2382
+ const UnpublishAction$1 = ({
2383
+ activeTab,
2384
+ documentId,
2385
+ model,
2386
+ collectionType,
2387
+ document
2388
+ }) => {
2420
2389
  const { formatMessage } = reactIntl.useIntl();
2421
- return {
2422
- label: formatMessage({
2423
- id: "app.links.configure-view",
2424
- defaultMessage: "Configure the view"
2425
- }),
2426
- icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ListPlus, {}),
2427
- onClick: () => {
2428
- navigate(`../${collectionType}/${model}/configurations/edit`);
2429
- },
2430
- position: "header"
2390
+ const { schema } = useDoc();
2391
+ const canPublish = useDocumentRBAC("UnpublishAction", ({ canPublish: canPublish2 }) => canPublish2);
2392
+ const { unpublish } = useDocumentActions();
2393
+ const [{ query }] = strapiAdmin.useQueryParams();
2394
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
2395
+ const { toggleNotification } = strapiAdmin.useNotification();
2396
+ const [shouldKeepDraft, setShouldKeepDraft] = React__namespace.useState(true);
2397
+ const isDocumentModified = document?.status === "modified";
2398
+ const handleChange = (value) => {
2399
+ setShouldKeepDraft(value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
2431
2400
  };
2432
- };
2433
- ConfigureTheViewAction.type = "configure-the-view";
2434
- const EditTheModelAction = ({ model }) => {
2435
- const navigate = reactRouterDom.useNavigate();
2436
- const { formatMessage } = reactIntl.useIntl();
2401
+ if (!schema?.options?.draftAndPublish) {
2402
+ return null;
2403
+ }
2437
2404
  return {
2405
+ disabled: !canPublish || activeTab === "published" || document?.status !== "published" && document?.status !== "modified",
2438
2406
  label: formatMessage({
2439
- id: "content-manager.link-to-ctb",
2440
- defaultMessage: "Edit the model"
2407
+ id: "app.utils.unpublish",
2408
+ defaultMessage: "Unpublish"
2441
2409
  }),
2442
- icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {}),
2443
- onClick: () => {
2444
- navigate(`/plugins/content-type-builder/content-types/${model}`);
2410
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
2411
+ onClick: async () => {
2412
+ if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
2413
+ if (!documentId) {
2414
+ console.error(
2415
+ "You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
2416
+ );
2417
+ toggleNotification({
2418
+ message: formatMessage({
2419
+ id: "content-manager.actions.unpublish.error",
2420
+ defaultMessage: "An error occurred while trying to unpublish the document."
2421
+ }),
2422
+ type: "danger"
2423
+ });
2424
+ }
2425
+ return;
2426
+ }
2427
+ await unpublish({
2428
+ collectionType,
2429
+ model,
2430
+ documentId,
2431
+ params
2432
+ });
2445
2433
  },
2446
- position: "header"
2434
+ dialog: isDocumentModified ? {
2435
+ type: "dialog",
2436
+ title: formatMessage({
2437
+ id: "app.components.ConfirmDialog.title",
2438
+ defaultMessage: "Confirmation"
2439
+ }),
2440
+ content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "flex-start", direction: "column", gap: 6, children: [
2441
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", direction: "column", gap: 2, children: [
2442
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2443
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2444
+ id: "content-manager.actions.unpublish.dialog.body",
2445
+ defaultMessage: "Are you sure?"
2446
+ }) })
2447
+ ] }),
2448
+ /* @__PURE__ */ jsxRuntime.jsxs(
2449
+ designSystem.Radio.Group,
2450
+ {
2451
+ defaultValue: UNPUBLISH_DRAFT_OPTIONS.KEEP,
2452
+ name: "discard-options",
2453
+ "aria-label": formatMessage({
2454
+ id: "content-manager.actions.unpublish.dialog.radio-label",
2455
+ defaultMessage: "Choose an option to unpublish the document."
2456
+ }),
2457
+ onValueChange: handleChange,
2458
+ children: [
2459
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { checked: shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.KEEP, children: formatMessage({
2460
+ id: "content-manager.actions.unpublish.dialog.option.keep-draft",
2461
+ defaultMessage: "Keep draft"
2462
+ }) }),
2463
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { checked: !shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.DISCARD, children: formatMessage({
2464
+ id: "content-manager.actions.unpublish.dialog.option.replace-draft",
2465
+ defaultMessage: "Replace draft"
2466
+ }) })
2467
+ ]
2468
+ }
2469
+ )
2470
+ ] }),
2471
+ onConfirm: async () => {
2472
+ if (!documentId && collectionType !== SINGLE_TYPES) {
2473
+ console.error(
2474
+ "You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
2475
+ );
2476
+ toggleNotification({
2477
+ message: formatMessage({
2478
+ id: "content-manager.actions.unpublish.error",
2479
+ defaultMessage: "An error occurred while trying to unpublish the document."
2480
+ }),
2481
+ type: "danger"
2482
+ });
2483
+ }
2484
+ await unpublish(
2485
+ {
2486
+ collectionType,
2487
+ model,
2488
+ documentId,
2489
+ params
2490
+ },
2491
+ !shouldKeepDraft
2492
+ );
2493
+ }
2494
+ } : void 0,
2495
+ variant: "danger",
2496
+ position: ["panel", "table-row"]
2447
2497
  };
2448
2498
  };
2449
- EditTheModelAction.type = "edit-the-model";
2450
- const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2451
- const navigate = reactRouterDom.useNavigate();
2499
+ UnpublishAction$1.type = "unpublish";
2500
+ const DiscardAction = ({
2501
+ activeTab,
2502
+ documentId,
2503
+ model,
2504
+ collectionType,
2505
+ document
2506
+ }) => {
2452
2507
  const { formatMessage } = reactIntl.useIntl();
2453
- const listViewPathMatch = reactRouterDom.useMatch(LIST_PATH);
2454
- const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2455
- const { delete: deleteAction } = useDocumentActions();
2456
- const { toggleNotification } = strapiAdmin.useNotification();
2457
- const setSubmitting = strapiAdmin.useForm("DeleteAction", (state) => state.setSubmitting);
2508
+ const { schema } = useDoc();
2509
+ const canUpdate = useDocumentRBAC("DiscardAction", ({ canUpdate: canUpdate2 }) => canUpdate2);
2510
+ const { discard } = useDocumentActions();
2511
+ const [{ query }] = strapiAdmin.useQueryParams();
2512
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
2513
+ if (!schema?.options?.draftAndPublish) {
2514
+ return null;
2515
+ }
2458
2516
  return {
2459
- disabled: !canDelete || !document,
2517
+ disabled: !canUpdate || activeTab === "published" || document?.status !== "modified",
2460
2518
  label: formatMessage({
2461
- id: "content-manager.actions.delete.label",
2462
- defaultMessage: "Delete document"
2519
+ id: "content-manager.actions.discard.label",
2520
+ defaultMessage: "Discard changes"
2463
2521
  }),
2464
- icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2522
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
2523
+ position: ["panel", "table-row"],
2524
+ variant: "danger",
2465
2525
  dialog: {
2466
2526
  type: "dialog",
2467
2527
  title: formatMessage({
@@ -2471,92 +2531,90 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2471
2531
  content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
2472
2532
  /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2473
2533
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2474
- id: "content-manager.actions.delete.dialog.body",
2534
+ id: "content-manager.actions.discard.dialog.body",
2475
2535
  defaultMessage: "Are you sure?"
2476
2536
  }) })
2477
2537
  ] }),
2478
2538
  onConfirm: async () => {
2479
- if (!listViewPathMatch) {
2480
- setSubmitting(true);
2481
- }
2482
- try {
2483
- if (!documentId && collectionType !== SINGLE_TYPES) {
2484
- console.error(
2485
- "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2486
- );
2487
- toggleNotification({
2488
- message: formatMessage({
2489
- id: "content-manager.actions.delete.error",
2490
- defaultMessage: "An error occurred while trying to delete the document."
2491
- }),
2492
- type: "danger"
2493
- });
2494
- return;
2495
- }
2496
- const res = await deleteAction({
2497
- documentId,
2498
- model,
2499
- collectionType,
2500
- params: {
2501
- locale: "*"
2502
- }
2503
- });
2504
- if (!("error" in res)) {
2505
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2506
- }
2507
- } finally {
2508
- if (!listViewPathMatch) {
2509
- setSubmitting(false);
2510
- }
2511
- }
2539
+ await discard({
2540
+ collectionType,
2541
+ model,
2542
+ documentId,
2543
+ params
2544
+ });
2545
+ }
2546
+ }
2547
+ };
2548
+ };
2549
+ DiscardAction.type = "discard";
2550
+ const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2551
+ const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2552
+ const RelativeTime = React__namespace.forwardRef(
2553
+ ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
2554
+ const { formatRelativeTime, formatDate, formatTime } = reactIntl.useIntl();
2555
+ const interval = dateFns.intervalToDuration({
2556
+ start: timestamp,
2557
+ end: Date.now()
2558
+ // see https://github.com/date-fns/date-fns/issues/2891 – No idea why it's all partial it returns it every time.
2559
+ });
2560
+ const unit = intervals.find((intervalUnit) => {
2561
+ return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2562
+ });
2563
+ const relativeTime = dateFns.isPast(timestamp) ? -interval[unit] : interval[unit];
2564
+ const customInterval = customIntervals.find(
2565
+ (custom) => interval[custom.unit] < custom.threshold
2566
+ );
2567
+ const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, { numeric: "auto" });
2568
+ return /* @__PURE__ */ jsxRuntime.jsx(
2569
+ "time",
2570
+ {
2571
+ ref: forwardedRef,
2572
+ dateTime: timestamp.toISOString(),
2573
+ role: "time",
2574
+ title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
2575
+ ...restProps,
2576
+ children: displayText
2512
2577
  }
2513
- },
2514
- variant: "danger",
2515
- position: ["header", "table-row"]
2516
- };
2578
+ );
2579
+ }
2580
+ );
2581
+ const getDisplayName = ({
2582
+ firstname,
2583
+ lastname,
2584
+ username,
2585
+ email
2586
+ } = {}) => {
2587
+ if (username) {
2588
+ return username;
2589
+ }
2590
+ if (firstname) {
2591
+ return `${firstname} ${lastname ?? ""}`.trim();
2592
+ }
2593
+ return email ?? "";
2517
2594
  };
2518
- DeleteAction$1.type = "delete";
2519
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2520
- const Panels = () => {
2521
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2522
- const [
2523
- {
2524
- query: { status }
2525
- }
2526
- ] = strapiAdmin.useQueryParams({
2527
- status: "draft"
2528
- });
2529
- const { model, id, document, meta, collectionType } = useDoc();
2530
- const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2531
- const props = {
2532
- activeTab: status,
2533
- model,
2534
- documentId: id,
2535
- document: isCloning ? void 0 : document,
2536
- meta: isCloning ? void 0 : meta,
2537
- collectionType
2538
- };
2539
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2540
- strapiAdmin.DescriptionComponentRenderer,
2541
- {
2542
- props,
2543
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2544
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2545
- }
2546
- ) });
2595
+ const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2596
+ const DocumentStatus = ({ status = "draft", ...restProps }) => {
2597
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2598
+ 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) }) });
2547
2599
  };
2548
- const ActionsPanel = () => {
2600
+ const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2549
2601
  const { formatMessage } = reactIntl.useIntl();
2550
- return {
2551
- title: formatMessage({
2552
- id: "content-manager.containers.edit.panels.default.title",
2553
- defaultMessage: "Document"
2554
- }),
2555
- content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2556
- };
2602
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2603
+ const title = isCreating ? formatMessage({
2604
+ id: "content-manager.containers.edit.title.new",
2605
+ defaultMessage: "Create an entry"
2606
+ }) : documentTitle;
2607
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2608
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.BackButton, {}),
2609
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2610
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2611
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2612
+ ] }),
2613
+ status ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2614
+ ] });
2557
2615
  };
2558
- ActionsPanel.type = "actions";
2559
- const ActionsPanelContent = () => {
2616
+ const HeaderToolbar = () => {
2617
+ const { formatMessage } = reactIntl.useIntl();
2560
2618
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2561
2619
  const [
2562
2620
  {
@@ -2564,355 +2622,432 @@ const ActionsPanelContent = () => {
2564
2622
  }
2565
2623
  ] = strapiAdmin.useQueryParams();
2566
2624
  const { model, id, document, meta, collectionType } = useDoc();
2567
- const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
2568
- const props = {
2569
- activeTab: status,
2570
- model,
2571
- documentId: id,
2572
- document: isCloning ? void 0 : document,
2573
- meta: isCloning ? void 0 : meta,
2574
- collectionType
2575
- };
2576
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
2625
+ const plugins = strapiAdmin.useStrapiApp("HeaderToolbar", (state) => state.plugins);
2626
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
2577
2627
  /* @__PURE__ */ jsxRuntime.jsx(
2578
2628
  strapiAdmin.DescriptionComponentRenderer,
2579
2629
  {
2580
- props,
2581
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2582
- children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
2630
+ props: {
2631
+ activeTab: status,
2632
+ model,
2633
+ documentId: id,
2634
+ document: isCloning ? void 0 : document,
2635
+ meta: isCloning ? void 0 : meta,
2636
+ collectionType
2637
+ },
2638
+ descriptions: plugins["content-manager"].apis.getHeaderActions(),
2639
+ children: (actions2) => {
2640
+ if (actions2.length > 0) {
2641
+ return /* @__PURE__ */ jsxRuntime.jsx(HeaderActions, { actions: actions2 });
2642
+ } else {
2643
+ return null;
2644
+ }
2645
+ }
2583
2646
  }
2584
2647
  ),
2585
- /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
2648
+ /* @__PURE__ */ jsxRuntime.jsx(
2649
+ strapiAdmin.DescriptionComponentRenderer,
2650
+ {
2651
+ props: {
2652
+ activeTab: status,
2653
+ model,
2654
+ documentId: id,
2655
+ document: isCloning ? void 0 : document,
2656
+ meta: isCloning ? void 0 : meta,
2657
+ collectionType
2658
+ },
2659
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
2660
+ children: (actions2) => {
2661
+ const headerActions = actions2.filter((action) => {
2662
+ const positions = Array.isArray(action.position) ? action.position : [action.position];
2663
+ return positions.includes("header");
2664
+ });
2665
+ return /* @__PURE__ */ jsxRuntime.jsx(
2666
+ DocumentActionsMenu,
2667
+ {
2668
+ actions: headerActions,
2669
+ label: formatMessage({
2670
+ id: "content-manager.containers.edit.header.more-actions",
2671
+ defaultMessage: "More actions"
2672
+ }),
2673
+ children: /* @__PURE__ */ jsxRuntime.jsx(Information, { activeTab: status })
2674
+ }
2675
+ );
2676
+ }
2677
+ }
2678
+ )
2586
2679
  ] });
2587
2680
  };
2588
- const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
2589
- return /* @__PURE__ */ jsxRuntime.jsxs(
2590
- designSystem.Flex,
2681
+ const Information = ({ activeTab }) => {
2682
+ const { formatMessage } = reactIntl.useIntl();
2683
+ const { document, meta } = useDoc();
2684
+ if (!document || !document.id) {
2685
+ return null;
2686
+ }
2687
+ const createAndUpdateDocument = activeTab === "draft" ? document : meta?.availableStatus.find((status) => status.publishedAt === null);
2688
+ const publishDocument = activeTab === "published" ? document : meta?.availableStatus.find((status) => status.publishedAt !== null);
2689
+ const creator = createAndUpdateDocument?.[CREATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[CREATED_BY_ATTRIBUTE_NAME]) : null;
2690
+ const updator = createAndUpdateDocument?.[UPDATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[UPDATED_BY_ATTRIBUTE_NAME]) : null;
2691
+ const information = [
2591
2692
  {
2592
- ref,
2593
- tag: "aside",
2594
- "aria-labelledby": "additional-information",
2595
- background: "neutral0",
2596
- borderColor: "neutral150",
2597
- hasRadius: true,
2598
- paddingBottom: 4,
2599
- paddingLeft: 4,
2600
- paddingRight: 4,
2601
- paddingTop: 4,
2602
- shadow: "tableShadow",
2603
- gap: 3,
2604
- direction: "column",
2605
- justifyContent: "stretch",
2606
- alignItems: "flex-start",
2607
- children: [
2608
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2609
- children
2610
- ]
2611
- }
2612
- );
2613
- });
2614
- const HOOKS = {
2615
- /**
2616
- * Hook that allows to mutate the displayed headers of the list view table
2617
- * @constant
2618
- * @type {string}
2619
- */
2620
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2621
- /**
2622
- * Hook that allows to mutate the CM's collection types links pre-set filters
2623
- * @constant
2624
- * @type {string}
2625
- */
2626
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2627
- /**
2628
- * Hook that allows to mutate the CM's edit view layout
2629
- * @constant
2630
- * @type {string}
2631
- */
2632
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2633
- /**
2634
- * Hook that allows to mutate the CM's single types links pre-set filters
2635
- * @constant
2636
- * @type {string}
2637
- */
2638
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2639
- };
2640
- const contentTypesApi = contentManagerApi.injectEndpoints({
2641
- endpoints: (builder) => ({
2642
- getContentTypeConfiguration: builder.query({
2643
- query: (uid) => ({
2644
- url: `/content-manager/content-types/${uid}/configuration`,
2645
- method: "GET"
2693
+ isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2694
+ label: formatMessage({
2695
+ id: "content-manager.containers.edit.information.last-published.label",
2696
+ defaultMessage: "Published"
2646
2697
  }),
2647
- transformResponse: (response) => response.data,
2648
- providesTags: (_result, _error, uid) => [
2649
- { type: "ContentTypesConfiguration", id: uid },
2650
- { type: "ContentTypeSettings", id: "LIST" }
2651
- ]
2652
- }),
2653
- getAllContentTypeSettings: builder.query({
2654
- query: () => "/content-manager/content-types-settings",
2655
- transformResponse: (response) => response.data,
2656
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2657
- }),
2658
- updateContentTypeConfiguration: builder.mutation({
2659
- query: ({ uid, ...body }) => ({
2660
- url: `/content-manager/content-types/${uid}/configuration`,
2661
- method: "PUT",
2662
- data: body
2698
+ value: formatMessage(
2699
+ {
2700
+ id: "content-manager.containers.edit.information.last-published.value",
2701
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2702
+ },
2703
+ {
2704
+ time: /* @__PURE__ */ jsxRuntime.jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
2705
+ isAnonymous: !publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME],
2706
+ author: publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME] ? getDisplayName(publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME]) : null
2707
+ }
2708
+ )
2709
+ },
2710
+ {
2711
+ isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2712
+ label: formatMessage({
2713
+ id: "content-manager.containers.edit.information.last-draft.label",
2714
+ defaultMessage: "Updated"
2715
+ }),
2716
+ value: formatMessage(
2717
+ {
2718
+ id: "content-manager.containers.edit.information.last-draft.value",
2719
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2720
+ },
2721
+ {
2722
+ time: /* @__PURE__ */ jsxRuntime.jsx(
2723
+ RelativeTime,
2724
+ {
2725
+ timestamp: new Date(createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME])
2726
+ }
2727
+ ),
2728
+ isAnonymous: !updator,
2729
+ author: updator
2730
+ }
2731
+ )
2732
+ },
2733
+ {
2734
+ isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2735
+ label: formatMessage({
2736
+ id: "content-manager.containers.edit.information.document.label",
2737
+ defaultMessage: "Created"
2663
2738
  }),
2664
- transformResponse: (response) => response.data,
2665
- invalidatesTags: (_result, _error, { uid }) => [
2666
- { type: "ContentTypesConfiguration", id: uid },
2667
- { type: "ContentTypeSettings", id: "LIST" },
2668
- // Is this necessary?
2669
- { type: "InitialData" }
2670
- ]
2671
- })
2672
- })
2673
- });
2674
- const {
2675
- useGetContentTypeConfigurationQuery,
2676
- useGetAllContentTypeSettingsQuery,
2677
- useUpdateContentTypeConfigurationMutation
2678
- } = contentTypesApi;
2679
- const checkIfAttributeIsDisplayable = (attribute) => {
2680
- const { type } = attribute;
2681
- if (type === "relation") {
2682
- return !attribute.relation.toLowerCase().includes("morph");
2683
- }
2684
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2685
- };
2686
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2687
- if (!mainFieldName) {
2688
- return void 0;
2689
- }
2690
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2691
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2692
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2739
+ value: formatMessage(
2740
+ {
2741
+ id: "content-manager.containers.edit.information.document.value",
2742
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2743
+ },
2744
+ {
2745
+ time: /* @__PURE__ */ jsxRuntime.jsx(
2746
+ RelativeTime,
2747
+ {
2748
+ timestamp: new Date(createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME])
2749
+ }
2750
+ ),
2751
+ isAnonymous: !creator,
2752
+ author: creator
2753
+ }
2754
+ )
2755
+ }
2756
+ ].filter((info) => info.isDisplayed);
2757
+ return /* @__PURE__ */ jsxRuntime.jsx(
2758
+ designSystem.Flex,
2759
+ {
2760
+ borderWidth: "1px 0 0 0",
2761
+ borderStyle: "solid",
2762
+ borderColor: "neutral150",
2763
+ direction: "column",
2764
+ marginTop: 2,
2765
+ tag: "dl",
2766
+ padding: 5,
2767
+ gap: 3,
2768
+ alignItems: "flex-start",
2769
+ marginLeft: "-0.4rem",
2770
+ marginRight: "-0.4rem",
2771
+ width: "calc(100% + 8px)",
2772
+ children: information.map((info) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, direction: "column", alignItems: "flex-start", children: [
2773
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "dt", variant: "pi", fontWeight: "bold", children: info.label }),
2774
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "dd", variant: "pi", textColor: "neutral600", children: info.value })
2775
+ ] }, info.label))
2776
+ }
2693
2777
  );
2694
- return {
2695
- name: mainFieldName,
2696
- type: mainFieldType ?? "string"
2697
- };
2698
- };
2699
- const DEFAULT_SETTINGS = {
2700
- bulkable: false,
2701
- filterable: false,
2702
- searchable: false,
2703
- pagination: false,
2704
- defaultSortBy: "",
2705
- defaultSortOrder: "asc",
2706
- mainField: "id",
2707
- pageSize: 10
2708
2778
  };
2709
- const useDocumentLayout = (model) => {
2710
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2711
- const [{ query }] = strapiAdmin.useQueryParams();
2712
- const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2713
- const { toggleNotification } = strapiAdmin.useNotification();
2714
- const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
2715
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2716
- const {
2717
- data,
2718
- isLoading: isLoadingConfigs,
2719
- error,
2720
- isFetching: isFetchingConfigs
2721
- } = useGetContentTypeConfigurationQuery(model);
2722
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2723
- React__namespace.useEffect(() => {
2724
- if (error) {
2725
- toggleNotification({
2726
- type: "danger",
2727
- message: formatAPIError(error)
2728
- });
2779
+ const HeaderActions = ({ actions: actions2 }) => {
2780
+ const [dialogId, setDialogId] = React__namespace.useState(null);
2781
+ const handleClick = (action) => async (e) => {
2782
+ if (!("options" in action)) {
2783
+ const { onClick = () => false, dialog, id } = action;
2784
+ const muteDialog = await onClick(e);
2785
+ if (dialog && !muteDialog) {
2786
+ e.preventDefault();
2787
+ setDialogId(id);
2788
+ }
2729
2789
  }
2730
- }, [error, formatAPIError, toggleNotification]);
2731
- const editLayout = React__namespace.useMemo(
2732
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2733
- layout: [],
2734
- components: {},
2735
- metadatas: {},
2736
- options: {},
2737
- settings: DEFAULT_SETTINGS
2738
- },
2739
- [data, isLoading, schemas, schema, components]
2740
- );
2741
- const listLayout = React__namespace.useMemo(() => {
2742
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2743
- layout: [],
2744
- metadatas: {},
2745
- options: {},
2746
- settings: DEFAULT_SETTINGS
2747
- };
2748
- }, [data, isLoading, schemas, schema, components]);
2749
- const { layout: edit } = React__namespace.useMemo(
2750
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2751
- layout: editLayout,
2752
- query
2753
- }),
2754
- [editLayout, query, runHookWaterfall]
2755
- );
2756
- return {
2757
- error,
2758
- isLoading,
2759
- edit,
2760
- list: listLayout
2761
2790
  };
2762
- };
2763
- const useDocLayout = () => {
2764
- const { model } = useDoc();
2765
- return useDocumentLayout(model);
2766
- };
2767
- const formatEditLayout = (data, {
2768
- schemas,
2769
- schema,
2770
- components
2771
- }) => {
2772
- let currentPanelIndex = 0;
2773
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2774
- data.contentType.layouts.edit,
2775
- schema?.attributes,
2776
- data.contentType.metadatas,
2777
- { configurations: data.components, schemas: components },
2778
- schemas
2779
- ).reduce((panels, row) => {
2780
- if (row.some((field) => field.type === "dynamiczone")) {
2781
- panels.push([row]);
2782
- currentPanelIndex += 2;
2791
+ const handleClose = () => {
2792
+ setDialogId(null);
2793
+ };
2794
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, children: actions2.map((action) => {
2795
+ if (action.options) {
2796
+ return /* @__PURE__ */ jsxRuntime.jsx(
2797
+ designSystem.SingleSelect,
2798
+ {
2799
+ size: "S",
2800
+ onChange: action.onSelect,
2801
+ "aria-label": action.label,
2802
+ ...action,
2803
+ children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { ...option, children: label }, option.value))
2804
+ },
2805
+ action.id
2806
+ );
2783
2807
  } else {
2784
- if (!panels[currentPanelIndex]) {
2785
- panels.push([]);
2808
+ if (action.type === "icon") {
2809
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
2810
+ /* @__PURE__ */ jsxRuntime.jsx(
2811
+ designSystem.IconButton,
2812
+ {
2813
+ disabled: action.disabled,
2814
+ label: action.label,
2815
+ size: "S",
2816
+ onClick: handleClick(action),
2817
+ children: action.icon
2818
+ }
2819
+ ),
2820
+ action.dialog ? /* @__PURE__ */ jsxRuntime.jsx(
2821
+ HeaderActionDialog,
2822
+ {
2823
+ ...action.dialog,
2824
+ isOpen: dialogId === action.id,
2825
+ onClose: handleClose
2826
+ }
2827
+ ) : null
2828
+ ] }, action.id);
2786
2829
  }
2787
- panels[currentPanelIndex].push(row);
2788
2830
  }
2789
- return panels;
2790
- }, []);
2791
- const componentEditAttributes = Object.entries(data.components).reduce(
2792
- (acc, [uid, configuration]) => {
2793
- acc[uid] = {
2794
- layout: convertEditLayoutToFieldLayouts(
2795
- configuration.layouts.edit,
2796
- components[uid].attributes,
2797
- configuration.metadatas
2798
- ),
2799
- settings: {
2800
- ...configuration.settings,
2801
- icon: components[uid].info.icon,
2802
- displayName: components[uid].info.displayName
2803
- }
2804
- };
2805
- return acc;
2806
- },
2807
- {}
2808
- );
2809
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2810
- (acc, [attribute, metadata]) => {
2811
- return {
2812
- ...acc,
2813
- [attribute]: metadata.edit
2814
- };
2831
+ }) });
2832
+ };
2833
+ const HeaderActionDialog = ({
2834
+ onClose,
2835
+ onCancel,
2836
+ title,
2837
+ content: Content,
2838
+ isOpen
2839
+ }) => {
2840
+ const handleClose = async () => {
2841
+ if (onCancel) {
2842
+ await onCancel();
2843
+ }
2844
+ onClose();
2845
+ };
2846
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2847
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2848
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content
2849
+ ] }) });
2850
+ };
2851
+ const ConfigureTheViewAction = ({ collectionType, model }) => {
2852
+ const navigate = reactRouterDom.useNavigate();
2853
+ const { formatMessage } = reactIntl.useIntl();
2854
+ return {
2855
+ label: formatMessage({
2856
+ id: "app.links.configure-view",
2857
+ defaultMessage: "Configure the view"
2858
+ }),
2859
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ListPlus, {}),
2860
+ onClick: () => {
2861
+ navigate(`../${collectionType}/${model}/configurations/edit`);
2815
2862
  },
2816
- {}
2817
- );
2863
+ position: "header"
2864
+ };
2865
+ };
2866
+ ConfigureTheViewAction.type = "configure-the-view";
2867
+ const EditTheModelAction = ({ model }) => {
2868
+ const navigate = reactRouterDom.useNavigate();
2869
+ const { formatMessage } = reactIntl.useIntl();
2818
2870
  return {
2819
- layout: panelledEditAttributes,
2820
- components: componentEditAttributes,
2821
- metadatas: editMetadatas,
2822
- settings: {
2823
- ...data.contentType.settings,
2824
- displayName: schema?.info.displayName
2871
+ label: formatMessage({
2872
+ id: "content-manager.link-to-ctb",
2873
+ defaultMessage: "Edit the model"
2874
+ }),
2875
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {}),
2876
+ onClick: () => {
2877
+ navigate(`/plugins/content-type-builder/content-types/${model}`);
2825
2878
  },
2826
- options: {
2827
- ...schema?.options,
2828
- ...schema?.pluginOptions,
2829
- ...data.contentType.options
2830
- }
2879
+ position: "header"
2831
2880
  };
2832
2881
  };
2833
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2834
- return rows.map(
2835
- (row) => row.map((field) => {
2836
- const attribute = attributes[field.name];
2837
- if (!attribute) {
2838
- return null;
2882
+ EditTheModelAction.type = "edit-the-model";
2883
+ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2884
+ const navigate = reactRouterDom.useNavigate();
2885
+ const { formatMessage } = reactIntl.useIntl();
2886
+ const listViewPathMatch = reactRouterDom.useMatch(LIST_PATH);
2887
+ const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2888
+ const { delete: deleteAction } = useDocumentActions();
2889
+ const { toggleNotification } = strapiAdmin.useNotification();
2890
+ const setSubmitting = strapiAdmin.useForm("DeleteAction", (state) => state.setSubmitting);
2891
+ const isLocalized = document?.locale != null;
2892
+ return {
2893
+ disabled: !canDelete || !document,
2894
+ label: formatMessage(
2895
+ {
2896
+ id: "content-manager.actions.delete.label",
2897
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2898
+ },
2899
+ { isLocalized }
2900
+ ),
2901
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2902
+ dialog: {
2903
+ type: "dialog",
2904
+ title: formatMessage({
2905
+ id: "app.components.ConfirmDialog.title",
2906
+ defaultMessage: "Confirmation"
2907
+ }),
2908
+ content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
2909
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2910
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2911
+ id: "content-manager.actions.delete.dialog.body",
2912
+ defaultMessage: "Are you sure?"
2913
+ }) })
2914
+ ] }),
2915
+ onConfirm: async () => {
2916
+ if (!listViewPathMatch) {
2917
+ setSubmitting(true);
2918
+ }
2919
+ try {
2920
+ if (!documentId && collectionType !== SINGLE_TYPES) {
2921
+ console.error(
2922
+ "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2923
+ );
2924
+ toggleNotification({
2925
+ message: formatMessage({
2926
+ id: "content-manager.actions.delete.error",
2927
+ defaultMessage: "An error occurred while trying to delete the document."
2928
+ }),
2929
+ type: "danger"
2930
+ });
2931
+ return;
2932
+ }
2933
+ const res = await deleteAction({
2934
+ documentId,
2935
+ model,
2936
+ collectionType,
2937
+ params: {
2938
+ locale: "*"
2939
+ }
2940
+ });
2941
+ if (!("error" in res)) {
2942
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2943
+ }
2944
+ } finally {
2945
+ if (!listViewPathMatch) {
2946
+ setSubmitting(false);
2947
+ }
2948
+ }
2839
2949
  }
2840
- const { edit: metadata } = metadatas[field.name];
2841
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2842
- return {
2843
- attribute,
2844
- disabled: !metadata.editable,
2845
- hint: metadata.description,
2846
- label: metadata.label ?? "",
2847
- name: field.name,
2848
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2849
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2850
- schemas,
2851
- components: components?.schemas ?? {}
2852
- }),
2853
- placeholder: metadata.placeholder ?? "",
2854
- required: attribute.required ?? false,
2855
- size: field.size,
2856
- unique: "unique" in attribute ? attribute.unique : false,
2857
- visible: metadata.visible ?? true,
2858
- type: attribute.type
2859
- };
2860
- }).filter((field) => field !== null)
2861
- );
2862
- };
2863
- const formatListLayout = (data, {
2864
- schemas,
2865
- schema,
2866
- components
2867
- }) => {
2868
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2869
- (acc, [attribute, metadata]) => {
2870
- return {
2871
- ...acc,
2872
- [attribute]: metadata.list
2873
- };
2874
2950
  },
2875
- {}
2876
- );
2877
- const listAttributes = convertListLayoutToFieldLayouts(
2878
- data.contentType.layouts.list,
2879
- schema?.attributes,
2880
- listMetadatas,
2881
- { configurations: data.components, schemas: components },
2882
- schemas
2883
- );
2884
- return {
2885
- layout: listAttributes,
2886
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2887
- metadatas: listMetadatas,
2888
- options: {
2889
- ...schema?.options,
2890
- ...schema?.pluginOptions,
2891
- ...data.contentType.options
2951
+ variant: "danger",
2952
+ position: ["header", "table-row"]
2953
+ };
2954
+ };
2955
+ DeleteAction$1.type = "delete";
2956
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2957
+ const Panels = () => {
2958
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2959
+ const [
2960
+ {
2961
+ query: { status }
2962
+ }
2963
+ ] = strapiAdmin.useQueryParams({
2964
+ status: "draft"
2965
+ });
2966
+ const { model, id, document, meta, collectionType } = useDoc();
2967
+ const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2968
+ const props = {
2969
+ activeTab: status,
2970
+ model,
2971
+ documentId: id,
2972
+ document: isCloning ? void 0 : document,
2973
+ meta: isCloning ? void 0 : meta,
2974
+ collectionType
2975
+ };
2976
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2977
+ strapiAdmin.DescriptionComponentRenderer,
2978
+ {
2979
+ props,
2980
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2981
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2892
2982
  }
2983
+ ) });
2984
+ };
2985
+ const ActionsPanel = () => {
2986
+ const { formatMessage } = reactIntl.useIntl();
2987
+ return {
2988
+ title: formatMessage({
2989
+ id: "content-manager.containers.edit.panels.default.title",
2990
+ defaultMessage: "Entry"
2991
+ }),
2992
+ content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2893
2993
  };
2894
2994
  };
2895
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2896
- return columns.map((name) => {
2897
- const attribute = attributes[name];
2898
- if (!attribute) {
2899
- return null;
2995
+ ActionsPanel.type = "actions";
2996
+ const ActionsPanelContent = () => {
2997
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2998
+ const [
2999
+ {
3000
+ query: { status = "draft" }
2900
3001
  }
2901
- const metadata = metadatas[name];
2902
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2903
- return {
2904
- attribute,
2905
- label: metadata.label ?? "",
2906
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2907
- schemas,
2908
- components: components?.schemas ?? {}
2909
- }),
2910
- name,
2911
- searchable: metadata.searchable ?? true,
2912
- sortable: metadata.sortable ?? true
2913
- };
2914
- }).filter((field) => field !== null);
3002
+ ] = strapiAdmin.useQueryParams();
3003
+ const { model, id, document, meta, collectionType } = useDoc();
3004
+ const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
3005
+ const props = {
3006
+ activeTab: status,
3007
+ model,
3008
+ documentId: id,
3009
+ document: isCloning ? void 0 : document,
3010
+ meta: isCloning ? void 0 : meta,
3011
+ collectionType
3012
+ };
3013
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
3014
+ /* @__PURE__ */ jsxRuntime.jsx(
3015
+ strapiAdmin.DescriptionComponentRenderer,
3016
+ {
3017
+ props,
3018
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
3019
+ children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
3020
+ }
3021
+ ),
3022
+ /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
3023
+ ] });
2915
3024
  };
3025
+ const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
3026
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3027
+ designSystem.Flex,
3028
+ {
3029
+ ref,
3030
+ tag: "aside",
3031
+ "aria-labelledby": "additional-information",
3032
+ background: "neutral0",
3033
+ borderColor: "neutral150",
3034
+ hasRadius: true,
3035
+ paddingBottom: 4,
3036
+ paddingLeft: 4,
3037
+ paddingRight: 4,
3038
+ paddingTop: 4,
3039
+ shadow: "tableShadow",
3040
+ gap: 3,
3041
+ direction: "column",
3042
+ justifyContent: "stretch",
3043
+ alignItems: "flex-start",
3044
+ children: [
3045
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
3046
+ children
3047
+ ]
3048
+ }
3049
+ );
3050
+ });
2916
3051
  const ConfirmBulkActionDialog = ({
2917
3052
  onToggleDialog,
2918
3053
  isOpen = false,
@@ -2951,6 +3086,7 @@ const ConfirmDialogPublishAll = ({
2951
3086
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler(getTranslation);
2952
3087
  const { model, schema } = useDoc();
2953
3088
  const [{ query }] = strapiAdmin.useQueryParams();
3089
+ const enableDraftRelationsCount = false;
2954
3090
  const {
2955
3091
  data: countDraftRelations = 0,
2956
3092
  isLoading,
@@ -2962,7 +3098,7 @@ const ConfirmDialogPublishAll = ({
2962
3098
  locale: query?.plugins?.i18n?.locale
2963
3099
  },
2964
3100
  {
2965
- skip: selectedEntries.length === 0
3101
+ skip: !enableDraftRelationsCount
2966
3102
  }
2967
3103
  );
2968
3104
  React__namespace.useEffect(() => {
@@ -3147,7 +3283,7 @@ const SelectedEntriesTableContent = ({
3147
3283
  status: row.status
3148
3284
  }
3149
3285
  ) }),
3150
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
3286
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(
3151
3287
  designSystem.IconButton,
3152
3288
  {
3153
3289
  tag: reactRouterDom.Link,
@@ -3170,9 +3306,10 @@ const SelectedEntriesTableContent = ({
3170
3306
  ),
3171
3307
  target: "_blank",
3172
3308
  marginLeft: "auto",
3173
- children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {})
3309
+ variant: "ghost",
3310
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, { width: "1.6rem", height: "1.6rem" })
3174
3311
  }
3175
- ) })
3312
+ ) }) })
3176
3313
  ] }, row.id)) })
3177
3314
  ] });
3178
3315
  };
@@ -3209,7 +3346,13 @@ const SelectedEntriesModalContent = ({
3209
3346
  );
3210
3347
  const { rows, validationErrors } = React__namespace.useMemo(() => {
3211
3348
  if (data.length > 0 && schema) {
3212
- const validate = createYupSchema(schema.attributes, components);
3349
+ const validate = createYupSchema(
3350
+ schema.attributes,
3351
+ components,
3352
+ // Since this is the "Publish" action, the validation
3353
+ // schema must enforce the rules for published entities
3354
+ { status: "published" }
3355
+ );
3213
3356
  const validationErrors2 = {};
3214
3357
  const rows2 = data.map((entry) => {
3215
3358
  try {
@@ -3559,7 +3702,7 @@ const TableActions = ({ document }) => {
3559
3702
  strapiAdmin.DescriptionComponentRenderer,
3560
3703
  {
3561
3704
  props,
3562
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3705
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3563
3706
  children: (actions2) => {
3564
3707
  const tableRowActions = actions2.filter((action) => {
3565
3708
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3670,7 +3813,7 @@ const CloneAction = ({ model, documentId }) => {
3670
3813
  }),
3671
3814
  content: /* @__PURE__ */ jsxRuntime.jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3672
3815
  footer: ({ onClose }) => {
3673
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
3816
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
3674
3817
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3675
3818
  id: "cancel",
3676
3819
  defaultMessage: "Cancel"
@@ -3901,7 +4044,7 @@ const index = {
3901
4044
  app.router.addRoute({
3902
4045
  path: "content-manager/*",
3903
4046
  lazy: async () => {
3904
- const { Layout } = await Promise.resolve().then(() => require("./layout-C6dxWYT7.js"));
4047
+ const { Layout } = await Promise.resolve().then(() => require("./layout-B4aCAdTt.js"));
3905
4048
  return {
3906
4049
  Component: Layout
3907
4050
  };
@@ -3918,7 +4061,7 @@ const index = {
3918
4061
  async registerTrads({ locales }) {
3919
4062
  const importedTrads = await Promise.all(
3920
4063
  locales.map((locale) => {
3921
- 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-uOUIxfcQ.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 }) => {
4064
+ 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-Bm0D0IWz.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 }) => {
3922
4065
  return {
3923
4066
  data: prefixPluginTranslations(data, PLUGIN_ID),
3924
4067
  locale
@@ -3936,6 +4079,7 @@ const index = {
3936
4079
  };
3937
4080
  exports.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD;
3938
4081
  exports.BulkActionsRenderer = BulkActionsRenderer;
4082
+ exports.CLONE_PATH = CLONE_PATH;
3939
4083
  exports.COLLECTION_TYPES = COLLECTION_TYPES;
3940
4084
  exports.CREATOR_FIELDS = CREATOR_FIELDS;
3941
4085
  exports.DEFAULT_SETTINGS = DEFAULT_SETTINGS;
@@ -3963,6 +4107,7 @@ exports.getMainField = getMainField;
3963
4107
  exports.getTranslation = getTranslation;
3964
4108
  exports.index = index;
3965
4109
  exports.setInitialData = setInitialData;
4110
+ exports.useContentManagerContext = useContentManagerContext;
3966
4111
  exports.useContentTypeSchema = useContentTypeSchema;
3967
4112
  exports.useDoc = useDoc;
3968
4113
  exports.useDocLayout = useDocLayout;
@@ -3975,4 +4120,4 @@ exports.useGetAllDocumentsQuery = useGetAllDocumentsQuery;
3975
4120
  exports.useGetContentTypeConfigurationQuery = useGetContentTypeConfigurationQuery;
3976
4121
  exports.useGetInitialDataQuery = useGetInitialDataQuery;
3977
4122
  exports.useUpdateContentTypeConfigurationMutation = useUpdateContentTypeConfigurationMutation;
3978
- //# sourceMappingURL=index-CdT0kHZ8.js.map
4123
+ //# sourceMappingURL=index-BsMu2oVP.js.map