@strapi/content-manager 0.0.0-experimental.fb442e5e12dd3f611303691bf85a249520ba348b → 0.0.0-experimental.fccfa141baa6df7776ec7cb83f7fe923626ba6f5

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