@strapi/content-manager 0.0.0-experimental.779667bd163026468f566293decf331a0246fff9 → 0.0.0-experimental.78b47df46708173ab4833373f694257729db4b9e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-B1bIXVuX.mjs → ComponentConfigurationPage-7-qB29e7.mjs} +3 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-B1bIXVuX.mjs.map → ComponentConfigurationPage-7-qB29e7.mjs.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-Bqgx7Mes.js → ComponentConfigurationPage-DP7AC0UU.js} +3 -3
  4. package/dist/_chunks/{ComponentConfigurationPage-Bqgx7Mes.js.map → ComponentConfigurationPage-DP7AC0UU.js.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-ZO0vOO8q.mjs → EditConfigurationPage-CI4XoymK.mjs} +3 -3
  6. package/dist/_chunks/{EditConfigurationPage-ZO0vOO8q.mjs.map → EditConfigurationPage-CI4XoymK.mjs.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-BFEwvdMW.js → EditConfigurationPage-DITVliEI.js} +3 -3
  8. package/dist/_chunks/{EditConfigurationPage-BFEwvdMW.js.map → EditConfigurationPage-DITVliEI.js.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-DA95Ha6J.js → EditViewPage-CUS2EAhB.js} +24 -9
  10. package/dist/_chunks/EditViewPage-CUS2EAhB.js.map +1 -0
  11. package/dist/_chunks/{EditViewPage-DlLEyUL6.mjs → EditViewPage-Dzpno8xI.mjs} +24 -9
  12. package/dist/_chunks/EditViewPage-Dzpno8xI.mjs.map +1 -0
  13. package/dist/_chunks/{Field-Dq7bDnuh.mjs → Field-B_jG_EV9.mjs} +139 -99
  14. package/dist/_chunks/Field-B_jG_EV9.mjs.map +1 -0
  15. package/dist/_chunks/{Field-CnK8dO8N.js → Field-CtUU1Fg8.js} +141 -101
  16. package/dist/_chunks/Field-CtUU1Fg8.js.map +1 -0
  17. package/dist/_chunks/{Form-B_JE0dbz.mjs → Form-BXHao2mZ.mjs} +35 -16
  18. package/dist/_chunks/Form-BXHao2mZ.mjs.map +1 -0
  19. package/dist/_chunks/{Form-BpiR4piS.js → Form-DTqO0ymI.js} +35 -16
  20. package/dist/_chunks/Form-DTqO0ymI.js.map +1 -0
  21. package/dist/_chunks/{History-CBNGU7a-.mjs → History-2Ah2CQ4T.mjs} +21 -11
  22. package/dist/_chunks/History-2Ah2CQ4T.mjs.map +1 -0
  23. package/dist/_chunks/{History-DdIstl8b.js → History-C_uSGzO5.js} +21 -11
  24. package/dist/_chunks/History-C_uSGzO5.js.map +1 -0
  25. package/dist/_chunks/{ListConfigurationPage-5dr4qpue.mjs → ListConfigurationPage-BjSJlaoC.mjs} +14 -4
  26. package/dist/_chunks/ListConfigurationPage-BjSJlaoC.mjs.map +1 -0
  27. package/dist/_chunks/{ListConfigurationPage-DkKRparB.js → ListConfigurationPage-nyuP7OSy.js} +14 -4
  28. package/dist/_chunks/ListConfigurationPage-nyuP7OSy.js.map +1 -0
  29. package/dist/_chunks/{ListViewPage-DecLrYV6.mjs → ListViewPage-B75x3nz2.mjs} +47 -38
  30. package/dist/_chunks/ListViewPage-B75x3nz2.mjs.map +1 -0
  31. package/dist/_chunks/{ListViewPage-wE0lXqoD.js → ListViewPage-DHgHD8Xg.js} +49 -40
  32. package/dist/_chunks/ListViewPage-DHgHD8Xg.js.map +1 -0
  33. package/dist/_chunks/{NoContentTypePage-DEKR6tf9.js → NoContentTypePage-CDUKdZ7d.js} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-DEKR6tf9.js.map → NoContentTypePage-CDUKdZ7d.js.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-CiIcfYsd.mjs → NoContentTypePage-DUacQSyF.mjs} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-CiIcfYsd.mjs.map → NoContentTypePage-DUacQSyF.mjs.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-CM5UD8ee.mjs → NoPermissionsPage-SFllMekk.mjs} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-CM5UD8ee.mjs.map → NoPermissionsPage-SFllMekk.mjs.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-DmNfF2Bb.js → NoPermissionsPage-zwIZydDI.js} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-DmNfF2Bb.js.map → NoPermissionsPage-zwIZydDI.js.map} +1 -1
  41. package/dist/_chunks/{Relations-Dqz0C1fz.mjs → Relations-D2NRW8fC.mjs} +14 -10
  42. package/dist/_chunks/Relations-D2NRW8fC.mjs.map +1 -0
  43. package/dist/_chunks/{Relations-L0xYRoSQ.js → Relations-NFLaRNPr.js} +14 -10
  44. package/dist/_chunks/Relations-NFLaRNPr.js.map +1 -0
  45. package/dist/_chunks/{en-uOUIxfcQ.js → en-BlhnxQfj.js} +7 -6
  46. package/dist/_chunks/{en-uOUIxfcQ.js.map → en-BlhnxQfj.js.map} +1 -1
  47. package/dist/_chunks/{en-BrCTWlZv.mjs → en-C8YBvRrK.mjs} +7 -6
  48. package/dist/_chunks/{en-BrCTWlZv.mjs.map → en-C8YBvRrK.mjs.map} +1 -1
  49. package/dist/_chunks/{index-BSn97i8U.mjs → index-C9HxCo5R.mjs} +1947 -1777
  50. package/dist/_chunks/index-C9HxCo5R.mjs.map +1 -0
  51. package/dist/_chunks/{index-DyvUPg1a.js → index-ovJRE1FM.js} +1927 -1757
  52. package/dist/_chunks/index-ovJRE1FM.js.map +1 -0
  53. package/dist/_chunks/{layout-DPaHUusj.mjs → layout-DaUjDiWQ.mjs} +22 -9
  54. package/dist/_chunks/layout-DaUjDiWQ.mjs.map +1 -0
  55. package/dist/_chunks/{layout-TPqF2oJ5.js → layout-UNWstw_s.js} +21 -8
  56. package/dist/_chunks/layout-UNWstw_s.js.map +1 -0
  57. package/dist/_chunks/{relations-Ck7-ecDT.mjs → relations-D8iFAeRu.mjs} +2 -2
  58. package/dist/_chunks/{relations-Ck7-ecDT.mjs.map → relations-D8iFAeRu.mjs.map} +1 -1
  59. package/dist/_chunks/{relations-BWYS9gkn.js → relations-NN3coOG5.js} +2 -2
  60. package/dist/_chunks/{relations-BWYS9gkn.js.map → relations-NN3coOG5.js.map} +1 -1
  61. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  62. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  63. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  64. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  65. package/dist/admin/index.js +2 -1
  66. package/dist/admin/index.js.map +1 -1
  67. package/dist/admin/index.mjs +5 -4
  68. package/dist/admin/src/exports.d.ts +1 -1
  69. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  70. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  71. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  72. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  73. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  74. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  75. package/dist/admin/src/services/api.d.ts +1 -1
  76. package/dist/admin/src/services/components.d.ts +2 -2
  77. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  78. package/dist/admin/src/services/documents.d.ts +19 -17
  79. package/dist/admin/src/services/init.d.ts +1 -1
  80. package/dist/admin/src/services/relations.d.ts +2 -2
  81. package/dist/admin/src/services/uid.d.ts +3 -3
  82. package/dist/admin/src/utils/validation.d.ts +4 -1
  83. package/dist/server/index.js +184 -108
  84. package/dist/server/index.js.map +1 -1
  85. package/dist/server/index.mjs +185 -109
  86. package/dist/server/index.mjs.map +1 -1
  87. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  88. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  89. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  90. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  91. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  92. package/dist/server/src/history/services/history.d.ts.map +1 -1
  93. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  94. package/dist/server/src/history/services/utils.d.ts +2 -1
  95. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  96. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  97. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  98. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  99. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  100. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  101. package/dist/shared/contracts/collection-types.d.ts +3 -1
  102. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  103. package/package.json +11 -11
  104. package/dist/_chunks/EditViewPage-DA95Ha6J.js.map +0 -1
  105. package/dist/_chunks/EditViewPage-DlLEyUL6.mjs.map +0 -1
  106. package/dist/_chunks/Field-CnK8dO8N.js.map +0 -1
  107. package/dist/_chunks/Field-Dq7bDnuh.mjs.map +0 -1
  108. package/dist/_chunks/Form-B_JE0dbz.mjs.map +0 -1
  109. package/dist/_chunks/Form-BpiR4piS.js.map +0 -1
  110. package/dist/_chunks/History-CBNGU7a-.mjs.map +0 -1
  111. package/dist/_chunks/History-DdIstl8b.js.map +0 -1
  112. package/dist/_chunks/ListConfigurationPage-5dr4qpue.mjs.map +0 -1
  113. package/dist/_chunks/ListConfigurationPage-DkKRparB.js.map +0 -1
  114. package/dist/_chunks/ListViewPage-DecLrYV6.mjs.map +0 -1
  115. package/dist/_chunks/ListViewPage-wE0lXqoD.js.map +0 -1
  116. package/dist/_chunks/Relations-Dqz0C1fz.mjs.map +0 -1
  117. package/dist/_chunks/Relations-L0xYRoSQ.js.map +0 -1
  118. package/dist/_chunks/index-BSn97i8U.mjs.map +0 -1
  119. package/dist/_chunks/index-DyvUPg1a.js.map +0 -1
  120. package/dist/_chunks/layout-DPaHUusj.mjs.map +0 -1
  121. package/dist/_chunks/layout-TPqF2oJ5.js.map +0 -1
  122. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  123. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  124. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  125. 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 };
@@ -179,7 +179,8 @@ const contentManagerApi = strapiAdmin.adminApi.enhanceEndpoints({
179
179
  "Document",
180
180
  "InitialData",
181
181
  "HistoryVersion",
182
- "Relations"
182
+ "Relations",
183
+ "UidAvailability"
183
184
  ]
184
185
  });
185
186
  const documentApi = contentManagerApi.injectEndpoints({
@@ -193,7 +194,12 @@ const documentApi = contentManagerApi.injectEndpoints({
193
194
  params: query
194
195
  }
195
196
  }),
196
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
197
+ invalidatesTags: (_result, error, { model }) => {
198
+ if (error) {
199
+ return [];
200
+ }
201
+ return [{ type: "Document", id: `${model}_LIST` }];
202
+ }
197
203
  }),
198
204
  cloneDocument: builder.mutation({
199
205
  query: ({ model, sourceId, data, params }) => ({
@@ -204,7 +210,10 @@ const documentApi = contentManagerApi.injectEndpoints({
204
210
  params
205
211
  }
206
212
  }),
207
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
213
+ invalidatesTags: (_result, _error, { model }) => [
214
+ { type: "Document", id: `${model}_LIST` },
215
+ { type: "UidAvailability", id: model }
216
+ ]
208
217
  }),
209
218
  /**
210
219
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -221,7 +230,8 @@ const documentApi = contentManagerApi.injectEndpoints({
221
230
  }),
222
231
  invalidatesTags: (result, _error, { model }) => [
223
232
  { type: "Document", id: `${model}_LIST` },
224
- "Relations"
233
+ "Relations",
234
+ { type: "UidAvailability", id: model }
225
235
  ]
226
236
  }),
227
237
  deleteDocument: builder.mutation({
@@ -262,7 +272,8 @@ const documentApi = contentManagerApi.injectEndpoints({
262
272
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
263
273
  },
264
274
  { type: "Document", id: `${model}_LIST` },
265
- "Relations"
275
+ "Relations",
276
+ { type: "UidAvailability", id: model }
266
277
  ];
267
278
  }
268
279
  }),
@@ -280,6 +291,7 @@ const documentApi = contentManagerApi.injectEndpoints({
280
291
  }),
281
292
  providesTags: (result, _error, arg) => {
282
293
  return [
294
+ { type: "Document", id: `ALL_LIST` },
283
295
  { type: "Document", id: `${arg.model}_LIST` },
284
296
  ...result?.results.map(({ documentId }) => ({
285
297
  type: "Document",
@@ -318,6 +330,11 @@ const documentApi = contentManagerApi.injectEndpoints({
318
330
  {
319
331
  type: "Document",
320
332
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
333
+ },
334
+ // Make it easy to invalidate all individual documents queries for a model
335
+ {
336
+ type: "Document",
337
+ id: `${model}_ALL_ITEMS`
321
338
  }
322
339
  ];
323
340
  }
@@ -381,8 +398,21 @@ const documentApi = contentManagerApi.injectEndpoints({
381
398
  type: "Document",
382
399
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
383
400
  },
384
- "Relations"
401
+ "Relations",
402
+ { type: "UidAvailability", id: model }
385
403
  ];
404
+ },
405
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
406
+ const patchResult = dispatch(
407
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
408
+ Object.assign(draft.data, data);
409
+ })
410
+ );
411
+ try {
412
+ await queryFulfilled;
413
+ } catch {
414
+ patchResult.undo();
415
+ }
386
416
  }
387
417
  }),
388
418
  unpublishDocument: builder.mutation({
@@ -452,7 +482,7 @@ const buildValidParams = (query) => {
452
482
  const isBaseQueryError = (error) => {
453
483
  return error.name !== void 0;
454
484
  };
455
- const createYupSchema = (attributes = {}, components = {}) => {
485
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
456
486
  const createModelSchema = (attributes2) => yup__namespace.object().shape(
457
487
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
458
488
  if (DOCUMENT_META_FIELDS.includes(name)) {
@@ -465,7 +495,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
465
495
  addMinValidation,
466
496
  addMaxValidation,
467
497
  addRegexValidation
468
- ].map((fn) => fn(attribute));
498
+ ].map((fn) => fn(attribute, options));
469
499
  const transformSchema = pipe__default.default(...validations);
470
500
  switch (attribute.type) {
471
501
  case "component": {
@@ -514,7 +544,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
514
544
  } else if (Array.isArray(value)) {
515
545
  return yup__namespace.array().of(
516
546
  yup__namespace.object().shape({
517
- id: yup__namespace.string().required()
547
+ id: yup__namespace.number().required()
518
548
  })
519
549
  );
520
550
  } else if (typeof value === "object") {
@@ -566,6 +596,14 @@ const createAttributeSchema = (attribute) => {
566
596
  if (!value || typeof value === "string" && value.length === 0) {
567
597
  return true;
568
598
  }
599
+ if (typeof value === "object") {
600
+ try {
601
+ JSON.stringify(value);
602
+ return true;
603
+ } catch (err) {
604
+ return false;
605
+ }
606
+ }
569
607
  try {
570
608
  JSON.parse(value);
571
609
  return true;
@@ -584,13 +622,7 @@ const createAttributeSchema = (attribute) => {
584
622
  return yup__namespace.mixed();
585
623
  }
586
624
  };
587
- const addRequiredValidation = (attribute) => (schema) => {
588
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
589
- return schema.min(1, strapiAdmin.translatedErrors.required);
590
- }
591
- if (attribute.required && attribute.type !== "relation") {
592
- return schema.required(strapiAdmin.translatedErrors.required);
593
- }
625
+ const nullableSchema = (schema) => {
594
626
  return schema?.nullable ? schema.nullable() : (
595
627
  // In some cases '.nullable' will not be available on the schema.
596
628
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -598,7 +630,22 @@ const addRequiredValidation = (attribute) => (schema) => {
598
630
  schema
599
631
  );
600
632
  };
601
- const addMinLengthValidation = (attribute) => (schema) => {
633
+ const addRequiredValidation = (attribute, options) => (schema) => {
634
+ if (options.status === "draft") {
635
+ return nullableSchema(schema);
636
+ }
637
+ if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
638
+ return schema.min(1, strapiAdmin.translatedErrors.required);
639
+ }
640
+ if (attribute.required && attribute.type !== "relation") {
641
+ return schema.required(strapiAdmin.translatedErrors.required);
642
+ }
643
+ return nullableSchema(schema);
644
+ };
645
+ const addMinLengthValidation = (attribute, options) => (schema) => {
646
+ if (options.status === "draft") {
647
+ return schema;
648
+ }
602
649
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
603
650
  return schema.min(attribute.minLength, {
604
651
  ...strapiAdmin.translatedErrors.minLength,
@@ -620,11 +667,11 @@ const addMaxLengthValidation = (attribute) => (schema) => {
620
667
  }
621
668
  return schema;
622
669
  };
623
- const addMinValidation = (attribute) => (schema) => {
670
+ const addMinValidation = (attribute, options) => (schema) => {
624
671
  if ("min" in attribute) {
625
672
  const min = toInteger(attribute.min);
626
673
  if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
627
- if (!attribute.required && "test" in schema && min) {
674
+ if (options.status !== "draft" && !attribute.required && "test" in schema && min) {
628
675
  return schema.test(
629
676
  "custom-min",
630
677
  {
@@ -763,19 +810,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
763
810
  }, {});
764
811
  return componentsByKey;
765
812
  };
766
- const useDocument = (args, opts) => {
813
+ const HOOKS = {
814
+ /**
815
+ * Hook that allows to mutate the displayed headers of the list view table
816
+ * @constant
817
+ * @type {string}
818
+ */
819
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
820
+ /**
821
+ * Hook that allows to mutate the CM's collection types links pre-set filters
822
+ * @constant
823
+ * @type {string}
824
+ */
825
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
826
+ /**
827
+ * Hook that allows to mutate the CM's edit view layout
828
+ * @constant
829
+ * @type {string}
830
+ */
831
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
832
+ /**
833
+ * Hook that allows to mutate the CM's single types links pre-set filters
834
+ * @constant
835
+ * @type {string}
836
+ */
837
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
838
+ };
839
+ const contentTypesApi = contentManagerApi.injectEndpoints({
840
+ endpoints: (builder) => ({
841
+ getContentTypeConfiguration: builder.query({
842
+ query: (uid) => ({
843
+ url: `/content-manager/content-types/${uid}/configuration`,
844
+ method: "GET"
845
+ }),
846
+ transformResponse: (response) => response.data,
847
+ providesTags: (_result, _error, uid) => [
848
+ { type: "ContentTypesConfiguration", id: uid },
849
+ { type: "ContentTypeSettings", id: "LIST" }
850
+ ]
851
+ }),
852
+ getAllContentTypeSettings: builder.query({
853
+ query: () => "/content-manager/content-types-settings",
854
+ transformResponse: (response) => response.data,
855
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
856
+ }),
857
+ updateContentTypeConfiguration: builder.mutation({
858
+ query: ({ uid, ...body }) => ({
859
+ url: `/content-manager/content-types/${uid}/configuration`,
860
+ method: "PUT",
861
+ data: body
862
+ }),
863
+ transformResponse: (response) => response.data,
864
+ invalidatesTags: (_result, _error, { uid }) => [
865
+ { type: "ContentTypesConfiguration", id: uid },
866
+ { type: "ContentTypeSettings", id: "LIST" },
867
+ // Is this necessary?
868
+ { type: "InitialData" }
869
+ ]
870
+ })
871
+ })
872
+ });
873
+ const {
874
+ useGetContentTypeConfigurationQuery,
875
+ useGetAllContentTypeSettingsQuery,
876
+ useUpdateContentTypeConfigurationMutation
877
+ } = contentTypesApi;
878
+ const checkIfAttributeIsDisplayable = (attribute) => {
879
+ const { type } = attribute;
880
+ if (type === "relation") {
881
+ return !attribute.relation.toLowerCase().includes("morph");
882
+ }
883
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
884
+ };
885
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
886
+ if (!mainFieldName) {
887
+ return void 0;
888
+ }
889
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
890
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
891
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
892
+ );
893
+ return {
894
+ name: mainFieldName,
895
+ type: mainFieldType ?? "string"
896
+ };
897
+ };
898
+ const DEFAULT_SETTINGS = {
899
+ bulkable: false,
900
+ filterable: false,
901
+ searchable: false,
902
+ pagination: false,
903
+ defaultSortBy: "",
904
+ defaultSortOrder: "asc",
905
+ mainField: "id",
906
+ pageSize: 10
907
+ };
908
+ const useDocumentLayout = (model) => {
909
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
910
+ const [{ query }] = strapiAdmin.useQueryParams();
911
+ const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
767
912
  const { toggleNotification } = strapiAdmin.useNotification();
768
913
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
914
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
769
915
  const {
770
- currentData: data,
771
- isLoading: isLoadingDocument,
772
- isFetching: isFetchingDocument,
773
- error
774
- } = useGetDocumentQuery(args, {
775
- ...opts,
776
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
777
- });
778
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
916
+ data,
917
+ isLoading: isLoadingConfigs,
918
+ error,
919
+ isFetching: isFetchingConfigs
920
+ } = useGetContentTypeConfigurationQuery(model);
921
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
779
922
  React__namespace.useEffect(() => {
780
923
  if (error) {
781
924
  toggleNotification({
@@ -783,382 +926,638 @@ const useDocument = (args, opts) => {
783
926
  message: formatAPIError(error)
784
927
  });
785
928
  }
786
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
787
- const validationSchema = React__namespace.useMemo(() => {
788
- if (!schema) {
789
- return null;
790
- }
791
- return createYupSchema(schema.attributes, components);
792
- }, [schema, components]);
793
- const validate = React__namespace.useCallback(
794
- (document) => {
795
- if (!validationSchema) {
796
- throw new Error(
797
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
798
- );
799
- }
800
- try {
801
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
802
- return null;
803
- } catch (error2) {
804
- if (error2 instanceof yup.ValidationError) {
805
- return strapiAdmin.getYupValidationErrors(error2);
806
- }
807
- throw error2;
808
- }
929
+ }, [error, formatAPIError, toggleNotification]);
930
+ const editLayout = React__namespace.useMemo(
931
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
932
+ layout: [],
933
+ components: {},
934
+ metadatas: {},
935
+ options: {},
936
+ settings: DEFAULT_SETTINGS
809
937
  },
810
- [validationSchema]
938
+ [data, isLoading, schemas, schema, components]
939
+ );
940
+ const listLayout = React__namespace.useMemo(() => {
941
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
942
+ layout: [],
943
+ metadatas: {},
944
+ options: {},
945
+ settings: DEFAULT_SETTINGS
946
+ };
947
+ }, [data, isLoading, schemas, schema, components]);
948
+ const { layout: edit } = React__namespace.useMemo(
949
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
950
+ layout: editLayout,
951
+ query
952
+ }),
953
+ [editLayout, query, runHookWaterfall]
811
954
  );
812
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
813
955
  return {
814
- components,
815
- document: data?.data,
816
- meta: data?.meta,
956
+ error,
817
957
  isLoading,
818
- schema,
819
- validate
820
- };
821
- };
822
- const useDoc = () => {
823
- const { id, slug, collectionType, origin } = reactRouterDom.useParams();
824
- const [{ query }] = strapiAdmin.useQueryParams();
825
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
826
- if (!collectionType) {
827
- throw new Error("Could not find collectionType in url params");
828
- }
829
- if (!slug) {
830
- throw new Error("Could not find model in url params");
831
- }
832
- return {
833
- collectionType,
834
- model: slug,
835
- id: origin || id === "create" ? void 0 : id,
836
- ...useDocument(
837
- { documentId: origin || id, model: slug, collectionType, params },
838
- {
839
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
840
- }
841
- )
958
+ edit,
959
+ list: listLayout
842
960
  };
843
961
  };
844
- const prefixPluginTranslations = (trad, pluginId) => {
845
- if (!pluginId) {
846
- throw new TypeError("pluginId can't be empty");
847
- }
848
- return Object.keys(trad).reduce((acc, current) => {
849
- acc[`${pluginId}.${current}`] = trad[current];
850
- return acc;
851
- }, {});
852
- };
853
- const getTranslation = (id) => `content-manager.${id}`;
854
- const DEFAULT_UNEXPECTED_ERROR_MSG = {
855
- id: "notification.error",
856
- defaultMessage: "An error occurred, please try again"
962
+ const useDocLayout = () => {
963
+ const { model } = useDoc();
964
+ return useDocumentLayout(model);
857
965
  };
858
- const useDocumentActions = () => {
859
- const { toggleNotification } = strapiAdmin.useNotification();
860
- const { formatMessage } = reactIntl.useIntl();
861
- const { trackUsage } = strapiAdmin.useTracking();
862
- const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
863
- const [deleteDocument] = useDeleteDocumentMutation();
864
- const _delete = React__namespace.useCallback(
865
- async ({ collectionType, model, documentId, params }, trackerProperty) => {
866
- try {
867
- trackUsage("willDeleteEntry", trackerProperty);
868
- const res = await deleteDocument({
869
- collectionType,
870
- model,
871
- documentId,
872
- params
873
- });
874
- if ("error" in res) {
875
- toggleNotification({
876
- type: "danger",
877
- message: formatAPIError(res.error)
878
- });
879
- return { error: res.error };
880
- }
881
- toggleNotification({
882
- type: "success",
883
- message: formatMessage({
884
- id: getTranslation("success.record.delete"),
885
- defaultMessage: "Deleted document"
886
- })
887
- });
888
- trackUsage("didDeleteEntry", trackerProperty);
889
- return res.data;
890
- } catch (err) {
891
- toggleNotification({
892
- type: "danger",
893
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
894
- });
895
- trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
896
- throw err;
966
+ const formatEditLayout = (data, {
967
+ schemas,
968
+ schema,
969
+ components
970
+ }) => {
971
+ let currentPanelIndex = 0;
972
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
973
+ data.contentType.layouts.edit,
974
+ schema?.attributes,
975
+ data.contentType.metadatas,
976
+ { configurations: data.components, schemas: components },
977
+ schemas
978
+ ).reduce((panels, row) => {
979
+ if (row.some((field) => field.type === "dynamiczone")) {
980
+ panels.push([row]);
981
+ currentPanelIndex += 2;
982
+ } else {
983
+ if (!panels[currentPanelIndex]) {
984
+ panels.push([]);
897
985
  }
898
- },
899
- [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
900
- );
901
- const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
902
- const deleteMany = React__namespace.useCallback(
903
- async ({ model, documentIds, params }) => {
904
- try {
905
- trackUsage("willBulkDeleteEntries");
906
- const res = await deleteManyDocuments({
907
- model,
908
- documentIds,
909
- params
910
- });
911
- if ("error" in res) {
912
- toggleNotification({
913
- type: "danger",
914
- message: formatAPIError(res.error)
915
- });
916
- return { error: res.error };
986
+ panels[currentPanelIndex].push(row);
987
+ }
988
+ return panels;
989
+ }, []);
990
+ const componentEditAttributes = Object.entries(data.components).reduce(
991
+ (acc, [uid, configuration]) => {
992
+ acc[uid] = {
993
+ layout: convertEditLayoutToFieldLayouts(
994
+ configuration.layouts.edit,
995
+ components[uid].attributes,
996
+ configuration.metadatas,
997
+ { configurations: data.components, schemas: components }
998
+ ),
999
+ settings: {
1000
+ ...configuration.settings,
1001
+ icon: components[uid].info.icon,
1002
+ displayName: components[uid].info.displayName
917
1003
  }
918
- toggleNotification({
919
- type: "success",
920
- title: formatMessage({
921
- id: getTranslation("success.records.delete"),
922
- defaultMessage: "Successfully deleted."
923
- }),
924
- message: ""
925
- });
926
- trackUsage("didBulkDeleteEntries");
927
- return res.data;
928
- } catch (err) {
929
- toggleNotification({
930
- type: "danger",
931
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
932
- });
933
- trackUsage("didNotBulkDeleteEntries");
934
- throw err;
935
- }
1004
+ };
1005
+ return acc;
936
1006
  },
937
- [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1007
+ {}
938
1008
  );
939
- const [discardDocument] = useDiscardDocumentMutation();
940
- const discard = React__namespace.useCallback(
941
- async ({ collectionType, model, documentId, params }) => {
942
- try {
943
- const res = await discardDocument({
944
- collectionType,
945
- model,
946
- documentId,
947
- params
948
- });
949
- if ("error" in res) {
950
- toggleNotification({
951
- type: "danger",
952
- message: formatAPIError(res.error)
953
- });
954
- return { error: res.error };
955
- }
956
- toggleNotification({
957
- type: "success",
958
- message: formatMessage({
959
- id: "content-manager.success.record.discard",
960
- defaultMessage: "Changes discarded"
961
- })
962
- });
963
- return res.data;
964
- } catch (err) {
965
- toggleNotification({
966
- type: "danger",
967
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
968
- });
969
- throw err;
970
- }
1009
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1010
+ (acc, [attribute, metadata]) => {
1011
+ return {
1012
+ ...acc,
1013
+ [attribute]: metadata.edit
1014
+ };
971
1015
  },
972
- [discardDocument, formatAPIError, formatMessage, toggleNotification]
1016
+ {}
973
1017
  );
974
- const [publishDocument] = usePublishDocumentMutation();
975
- const publish = React__namespace.useCallback(
976
- async ({ collectionType, model, documentId, params }, data) => {
977
- try {
978
- trackUsage("willPublishEntry");
979
- const res = await publishDocument({
980
- collectionType,
981
- model,
982
- documentId,
983
- data,
984
- params
985
- });
986
- if ("error" in res) {
987
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
988
- return { error: res.error };
989
- }
990
- trackUsage("didPublishEntry");
991
- toggleNotification({
992
- type: "success",
993
- message: formatMessage({
994
- id: getTranslation("success.record.publish"),
995
- defaultMessage: "Published document"
996
- })
997
- });
998
- return res.data;
999
- } catch (err) {
1000
- toggleNotification({
1001
- type: "danger",
1002
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1003
- });
1004
- throw err;
1005
- }
1018
+ return {
1019
+ layout: panelledEditAttributes,
1020
+ components: componentEditAttributes,
1021
+ metadatas: editMetadatas,
1022
+ settings: {
1023
+ ...data.contentType.settings,
1024
+ displayName: schema?.info.displayName
1006
1025
  },
1007
- [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1008
- );
1009
- const [publishManyDocuments] = usePublishManyDocumentsMutation();
1010
- const publishMany = React__namespace.useCallback(
1011
- async ({ model, documentIds, params }) => {
1012
- try {
1013
- const res = await publishManyDocuments({
1014
- model,
1015
- documentIds,
1016
- params
1017
- });
1018
- if ("error" in res) {
1019
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1020
- return { error: res.error };
1021
- }
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;
1026
+ options: {
1027
+ ...schema?.options,
1028
+ ...schema?.pluginOptions,
1029
+ ...data.contentType.options
1030
+ }
1031
+ };
1032
+ };
1033
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1034
+ return rows.map(
1035
+ (row) => row.map((field) => {
1036
+ const attribute = attributes[field.name];
1037
+ if (!attribute) {
1038
+ return null;
1036
1039
  }
1037
- },
1038
- [
1039
- // trackUsage,
1040
- publishManyDocuments,
1041
- toggleNotification,
1042
- formatMessage,
1043
- formatAPIError
1044
- ]
1040
+ const { edit: metadata } = metadatas[field.name];
1041
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1042
+ return {
1043
+ attribute,
1044
+ disabled: !metadata.editable,
1045
+ hint: metadata.description,
1046
+ label: metadata.label ?? "",
1047
+ name: field.name,
1048
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1049
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1050
+ schemas,
1051
+ components: components?.schemas ?? {}
1052
+ }),
1053
+ placeholder: metadata.placeholder ?? "",
1054
+ required: attribute.required ?? false,
1055
+ size: field.size,
1056
+ unique: "unique" in attribute ? attribute.unique : false,
1057
+ visible: metadata.visible ?? true,
1058
+ type: attribute.type
1059
+ };
1060
+ }).filter((field) => field !== null)
1045
1061
  );
1046
- const [updateDocument] = useUpdateDocumentMutation();
1047
- const update = React__namespace.useCallback(
1048
- async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1049
- try {
1050
- trackUsage("willEditEntry", trackerProperty);
1051
- const res = await updateDocument({
1052
- collectionType,
1053
- model,
1054
- documentId,
1055
- data,
1056
- params
1057
- });
1058
- if ("error" in res) {
1059
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1060
- trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1061
- return { error: res.error };
1062
+ };
1063
+ const formatListLayout = (data, {
1064
+ schemas,
1065
+ schema,
1066
+ components
1067
+ }) => {
1068
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1069
+ (acc, [attribute, metadata]) => {
1070
+ return {
1071
+ ...acc,
1072
+ [attribute]: metadata.list
1073
+ };
1074
+ },
1075
+ {}
1076
+ );
1077
+ const listAttributes = convertListLayoutToFieldLayouts(
1078
+ data.contentType.layouts.list,
1079
+ schema?.attributes,
1080
+ listMetadatas,
1081
+ { configurations: data.components, schemas: components },
1082
+ schemas
1083
+ );
1084
+ return {
1085
+ layout: listAttributes,
1086
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1087
+ metadatas: listMetadatas,
1088
+ options: {
1089
+ ...schema?.options,
1090
+ ...schema?.pluginOptions,
1091
+ ...data.contentType.options
1092
+ }
1093
+ };
1094
+ };
1095
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1096
+ return columns.map((name) => {
1097
+ const attribute = attributes[name];
1098
+ if (!attribute) {
1099
+ return null;
1100
+ }
1101
+ const metadata = metadatas[name];
1102
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1103
+ return {
1104
+ attribute,
1105
+ label: metadata.label ?? "",
1106
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1107
+ schemas,
1108
+ components: components?.schemas ?? {}
1109
+ }),
1110
+ name,
1111
+ searchable: metadata.searchable ?? true,
1112
+ sortable: metadata.sortable ?? true
1113
+ };
1114
+ }).filter((field) => field !== null);
1115
+ };
1116
+ const useDocument = (args, opts) => {
1117
+ const { toggleNotification } = strapiAdmin.useNotification();
1118
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1119
+ const {
1120
+ currentData: data,
1121
+ isLoading: isLoadingDocument,
1122
+ isFetching: isFetchingDocument,
1123
+ error
1124
+ } = useGetDocumentQuery(args, {
1125
+ ...opts,
1126
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1127
+ });
1128
+ const {
1129
+ components,
1130
+ schema,
1131
+ schemas,
1132
+ isLoading: isLoadingSchema
1133
+ } = useContentTypeSchema(args.model);
1134
+ React__namespace.useEffect(() => {
1135
+ if (error) {
1136
+ toggleNotification({
1137
+ type: "danger",
1138
+ message: formatAPIError(error)
1139
+ });
1140
+ }
1141
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1142
+ const validationSchema = React__namespace.useMemo(() => {
1143
+ if (!schema) {
1144
+ return null;
1145
+ }
1146
+ return createYupSchema(schema.attributes, components);
1147
+ }, [schema, components]);
1148
+ const validate = React__namespace.useCallback(
1149
+ (document) => {
1150
+ if (!validationSchema) {
1151
+ throw new Error(
1152
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1153
+ );
1154
+ }
1155
+ try {
1156
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1157
+ return null;
1158
+ } catch (error2) {
1159
+ if (error2 instanceof yup.ValidationError) {
1160
+ return strapiAdmin.getYupValidationErrors(error2);
1062
1161
  }
1063
- trackUsage("didEditEntry", trackerProperty);
1064
- toggleNotification({
1065
- type: "success",
1066
- message: formatMessage({
1067
- id: getTranslation("success.record.save"),
1068
- defaultMessage: "Saved document"
1069
- })
1070
- });
1071
- return res.data;
1072
- } catch (err) {
1073
- trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1074
- toggleNotification({
1075
- type: "danger",
1076
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1077
- });
1078
- throw err;
1162
+ throw error2;
1079
1163
  }
1080
1164
  },
1081
- [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1165
+ [validationSchema]
1082
1166
  );
1083
- const [unpublishDocument] = useUnpublishDocumentMutation();
1084
- const unpublish = React__namespace.useCallback(
1085
- async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1167
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1168
+ const hasError = !!error;
1169
+ return {
1170
+ components,
1171
+ document: data?.data,
1172
+ meta: data?.meta,
1173
+ isLoading,
1174
+ hasError,
1175
+ schema,
1176
+ schemas,
1177
+ validate
1178
+ };
1179
+ };
1180
+ const useDoc = () => {
1181
+ const { id, slug, collectionType, origin } = reactRouterDom.useParams();
1182
+ const [{ query }] = strapiAdmin.useQueryParams();
1183
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1184
+ if (!collectionType) {
1185
+ throw new Error("Could not find collectionType in url params");
1186
+ }
1187
+ if (!slug) {
1188
+ throw new Error("Could not find model in url params");
1189
+ }
1190
+ return {
1191
+ collectionType,
1192
+ model: slug,
1193
+ id: origin || id === "create" ? void 0 : id,
1194
+ ...useDocument(
1195
+ { documentId: origin || id, model: slug, collectionType, params },
1196
+ {
1197
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1198
+ }
1199
+ )
1200
+ };
1201
+ };
1202
+ const useContentManagerContext = () => {
1203
+ const {
1204
+ collectionType,
1205
+ model,
1206
+ id,
1207
+ components,
1208
+ isLoading: isLoadingDoc,
1209
+ schema,
1210
+ schemas
1211
+ } = useDoc();
1212
+ const layout = useDocumentLayout(model);
1213
+ const form = strapiAdmin.useForm("useContentManagerContext", (state) => state);
1214
+ const isSingleType = collectionType === SINGLE_TYPES;
1215
+ const slug = model;
1216
+ const isCreatingEntry = id === "create";
1217
+ useContentTypeSchema();
1218
+ const isLoading = isLoadingDoc || layout.isLoading;
1219
+ const error = layout.error;
1220
+ return {
1221
+ error,
1222
+ isLoading,
1223
+ // Base metadata
1224
+ model,
1225
+ collectionType,
1226
+ id,
1227
+ slug,
1228
+ isCreatingEntry,
1229
+ isSingleType,
1230
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1231
+ // All schema infos
1232
+ components,
1233
+ contentType: schema,
1234
+ contentTypes: schemas,
1235
+ // Form state
1236
+ form,
1237
+ // layout infos
1238
+ layout
1239
+ };
1240
+ };
1241
+ const prefixPluginTranslations = (trad, pluginId) => {
1242
+ if (!pluginId) {
1243
+ throw new TypeError("pluginId can't be empty");
1244
+ }
1245
+ return Object.keys(trad).reduce((acc, current) => {
1246
+ acc[`${pluginId}.${current}`] = trad[current];
1247
+ return acc;
1248
+ }, {});
1249
+ };
1250
+ const getTranslation = (id) => `content-manager.${id}`;
1251
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1252
+ id: "notification.error",
1253
+ defaultMessage: "An error occurred, please try again"
1254
+ };
1255
+ const useDocumentActions = () => {
1256
+ const { toggleNotification } = strapiAdmin.useNotification();
1257
+ const { formatMessage } = reactIntl.useIntl();
1258
+ const { trackUsage } = strapiAdmin.useTracking();
1259
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1260
+ const navigate = reactRouterDom.useNavigate();
1261
+ const setCurrentStep = strapiAdmin.useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
1262
+ const [deleteDocument] = useDeleteDocumentMutation();
1263
+ const _delete = React__namespace.useCallback(
1264
+ async ({ collectionType, model, documentId, params }, trackerProperty) => {
1086
1265
  try {
1087
- trackUsage("willUnpublishEntry");
1088
- const res = await unpublishDocument({
1266
+ trackUsage("willDeleteEntry", trackerProperty);
1267
+ const res = await deleteDocument({
1089
1268
  collectionType,
1090
1269
  model,
1091
1270
  documentId,
1092
- params,
1093
- data: {
1094
- discardDraft
1095
- }
1271
+ params
1096
1272
  });
1097
1273
  if ("error" in res) {
1098
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1274
+ toggleNotification({
1275
+ type: "danger",
1276
+ message: formatAPIError(res.error)
1277
+ });
1099
1278
  return { error: res.error };
1100
1279
  }
1101
- trackUsage("didUnpublishEntry");
1102
1280
  toggleNotification({
1103
1281
  type: "success",
1104
1282
  message: formatMessage({
1105
- id: getTranslation("success.record.unpublish"),
1106
- defaultMessage: "Unpublished document"
1283
+ id: getTranslation("success.record.delete"),
1284
+ defaultMessage: "Deleted document"
1107
1285
  })
1108
1286
  });
1287
+ trackUsage("didDeleteEntry", trackerProperty);
1109
1288
  return res.data;
1110
1289
  } catch (err) {
1111
1290
  toggleNotification({
1112
1291
  type: "danger",
1113
1292
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1114
1293
  });
1294
+ trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
1115
1295
  throw err;
1116
1296
  }
1117
1297
  },
1118
- [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1298
+ [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
1119
1299
  );
1120
- const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1121
- const unpublishMany = React__namespace.useCallback(
1300
+ const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
1301
+ const deleteMany = React__namespace.useCallback(
1122
1302
  async ({ model, documentIds, params }) => {
1123
1303
  try {
1124
- trackUsage("willBulkUnpublishEntries");
1125
- const res = await unpublishManyDocuments({
1304
+ trackUsage("willBulkDeleteEntries");
1305
+ const res = await deleteManyDocuments({
1126
1306
  model,
1127
1307
  documentIds,
1128
1308
  params
1129
1309
  });
1130
1310
  if ("error" in res) {
1131
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1311
+ toggleNotification({
1312
+ type: "danger",
1313
+ message: formatAPIError(res.error)
1314
+ });
1132
1315
  return { error: res.error };
1133
1316
  }
1134
- trackUsage("didBulkUnpublishEntries");
1135
1317
  toggleNotification({
1136
1318
  type: "success",
1137
1319
  title: formatMessage({
1138
- id: getTranslation("success.records.unpublish"),
1139
- defaultMessage: "Successfully unpublished."
1320
+ id: getTranslation("success.records.delete"),
1321
+ defaultMessage: "Successfully deleted."
1140
1322
  }),
1141
1323
  message: ""
1142
1324
  });
1325
+ trackUsage("didBulkDeleteEntries");
1143
1326
  return res.data;
1144
1327
  } catch (err) {
1145
1328
  toggleNotification({
1146
1329
  type: "danger",
1147
1330
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1148
1331
  });
1149
- trackUsage("didNotBulkUnpublishEntries");
1332
+ trackUsage("didNotBulkDeleteEntries");
1150
1333
  throw err;
1151
1334
  }
1152
1335
  },
1153
- [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1336
+ [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1154
1337
  );
1155
- const [createDocument] = useCreateDocumentMutation();
1156
- const create = React__namespace.useCallback(
1157
- async ({ model, params }, data, trackerProperty) => {
1338
+ const [discardDocument] = useDiscardDocumentMutation();
1339
+ const discard = React__namespace.useCallback(
1340
+ async ({ collectionType, model, documentId, params }) => {
1158
1341
  try {
1159
- const res = await createDocument({
1342
+ const res = await discardDocument({
1343
+ collectionType,
1160
1344
  model,
1161
- data,
1345
+ documentId,
1346
+ params
1347
+ });
1348
+ if ("error" in res) {
1349
+ toggleNotification({
1350
+ type: "danger",
1351
+ message: formatAPIError(res.error)
1352
+ });
1353
+ return { error: res.error };
1354
+ }
1355
+ toggleNotification({
1356
+ type: "success",
1357
+ message: formatMessage({
1358
+ id: "content-manager.success.record.discard",
1359
+ defaultMessage: "Changes discarded"
1360
+ })
1361
+ });
1362
+ return res.data;
1363
+ } catch (err) {
1364
+ toggleNotification({
1365
+ type: "danger",
1366
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1367
+ });
1368
+ throw err;
1369
+ }
1370
+ },
1371
+ [discardDocument, formatAPIError, formatMessage, toggleNotification]
1372
+ );
1373
+ const [publishDocument] = usePublishDocumentMutation();
1374
+ const publish = React__namespace.useCallback(
1375
+ async ({ collectionType, model, documentId, params }, data) => {
1376
+ try {
1377
+ trackUsage("willPublishEntry");
1378
+ const res = await publishDocument({
1379
+ collectionType,
1380
+ model,
1381
+ documentId,
1382
+ data,
1383
+ params
1384
+ });
1385
+ if ("error" in res) {
1386
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1387
+ return { error: res.error };
1388
+ }
1389
+ trackUsage("didPublishEntry");
1390
+ toggleNotification({
1391
+ type: "success",
1392
+ message: formatMessage({
1393
+ id: getTranslation("success.record.publish"),
1394
+ defaultMessage: "Published document"
1395
+ })
1396
+ });
1397
+ return res.data;
1398
+ } catch (err) {
1399
+ toggleNotification({
1400
+ type: "danger",
1401
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1402
+ });
1403
+ throw err;
1404
+ }
1405
+ },
1406
+ [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1407
+ );
1408
+ const [publishManyDocuments] = usePublishManyDocumentsMutation();
1409
+ const publishMany = React__namespace.useCallback(
1410
+ async ({ model, documentIds, params }) => {
1411
+ try {
1412
+ const res = await publishManyDocuments({
1413
+ model,
1414
+ documentIds,
1415
+ params
1416
+ });
1417
+ if ("error" in res) {
1418
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1419
+ return { error: res.error };
1420
+ }
1421
+ toggleNotification({
1422
+ type: "success",
1423
+ message: formatMessage({
1424
+ id: getTranslation("success.record.publish"),
1425
+ defaultMessage: "Published document"
1426
+ })
1427
+ });
1428
+ return res.data;
1429
+ } catch (err) {
1430
+ toggleNotification({
1431
+ type: "danger",
1432
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1433
+ });
1434
+ throw err;
1435
+ }
1436
+ },
1437
+ [
1438
+ // trackUsage,
1439
+ publishManyDocuments,
1440
+ toggleNotification,
1441
+ formatMessage,
1442
+ formatAPIError
1443
+ ]
1444
+ );
1445
+ const [updateDocument] = useUpdateDocumentMutation();
1446
+ const update = React__namespace.useCallback(
1447
+ async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1448
+ try {
1449
+ trackUsage("willEditEntry", trackerProperty);
1450
+ const res = await updateDocument({
1451
+ collectionType,
1452
+ model,
1453
+ documentId,
1454
+ data,
1455
+ params
1456
+ });
1457
+ if ("error" in res) {
1458
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1459
+ trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1460
+ return { error: res.error };
1461
+ }
1462
+ trackUsage("didEditEntry", trackerProperty);
1463
+ toggleNotification({
1464
+ type: "success",
1465
+ message: formatMessage({
1466
+ id: getTranslation("success.record.save"),
1467
+ defaultMessage: "Saved document"
1468
+ })
1469
+ });
1470
+ return res.data;
1471
+ } catch (err) {
1472
+ trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1473
+ toggleNotification({
1474
+ type: "danger",
1475
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1476
+ });
1477
+ throw err;
1478
+ }
1479
+ },
1480
+ [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1481
+ );
1482
+ const [unpublishDocument] = useUnpublishDocumentMutation();
1483
+ const unpublish = React__namespace.useCallback(
1484
+ async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1485
+ try {
1486
+ trackUsage("willUnpublishEntry");
1487
+ const res = await unpublishDocument({
1488
+ collectionType,
1489
+ model,
1490
+ documentId,
1491
+ params,
1492
+ data: {
1493
+ discardDraft
1494
+ }
1495
+ });
1496
+ if ("error" in res) {
1497
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1498
+ return { error: res.error };
1499
+ }
1500
+ trackUsage("didUnpublishEntry");
1501
+ toggleNotification({
1502
+ type: "success",
1503
+ message: formatMessage({
1504
+ id: getTranslation("success.record.unpublish"),
1505
+ defaultMessage: "Unpublished document"
1506
+ })
1507
+ });
1508
+ return res.data;
1509
+ } catch (err) {
1510
+ toggleNotification({
1511
+ type: "danger",
1512
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1513
+ });
1514
+ throw err;
1515
+ }
1516
+ },
1517
+ [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1518
+ );
1519
+ const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1520
+ const unpublishMany = React__namespace.useCallback(
1521
+ async ({ model, documentIds, params }) => {
1522
+ try {
1523
+ trackUsage("willBulkUnpublishEntries");
1524
+ const res = await unpublishManyDocuments({
1525
+ model,
1526
+ documentIds,
1527
+ params
1528
+ });
1529
+ if ("error" in res) {
1530
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1531
+ return { error: res.error };
1532
+ }
1533
+ trackUsage("didBulkUnpublishEntries");
1534
+ toggleNotification({
1535
+ type: "success",
1536
+ title: formatMessage({
1537
+ id: getTranslation("success.records.unpublish"),
1538
+ defaultMessage: "Successfully unpublished."
1539
+ }),
1540
+ message: ""
1541
+ });
1542
+ return res.data;
1543
+ } catch (err) {
1544
+ toggleNotification({
1545
+ type: "danger",
1546
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1547
+ });
1548
+ trackUsage("didNotBulkUnpublishEntries");
1549
+ throw err;
1550
+ }
1551
+ },
1552
+ [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1553
+ );
1554
+ const [createDocument] = useCreateDocumentMutation();
1555
+ const create = React__namespace.useCallback(
1556
+ async ({ model, params }, data, trackerProperty) => {
1557
+ try {
1558
+ const res = await createDocument({
1559
+ model,
1560
+ data,
1162
1561
  params
1163
1562
  });
1164
1563
  if ("error" in res) {
@@ -1174,6 +1573,7 @@ const useDocumentActions = () => {
1174
1573
  defaultMessage: "Saved document"
1175
1574
  })
1176
1575
  });
1576
+ setCurrentStep("contentManager.success");
1177
1577
  return res.data;
1178
1578
  } catch (err) {
1179
1579
  toggleNotification({
@@ -1195,7 +1595,6 @@ const useDocumentActions = () => {
1195
1595
  sourceId
1196
1596
  });
1197
1597
  if ("error" in res) {
1198
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1199
1598
  return { error: res.error };
1200
1599
  }
1201
1600
  toggleNotification({
@@ -1214,7 +1613,7 @@ const useDocumentActions = () => {
1214
1613
  throw err;
1215
1614
  }
1216
1615
  },
1217
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1616
+ [autoCloneDocument, formatMessage, toggleNotification]
1218
1617
  );
1219
1618
  const [cloneDocument] = useCloneDocumentMutation();
1220
1619
  const clone = React__namespace.useCallback(
@@ -1240,6 +1639,7 @@ const useDocumentActions = () => {
1240
1639
  defaultMessage: "Cloned document"
1241
1640
  })
1242
1641
  });
1642
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1243
1643
  return res.data;
1244
1644
  } catch (err) {
1245
1645
  toggleNotification({
@@ -1250,7 +1650,7 @@ const useDocumentActions = () => {
1250
1650
  throw err;
1251
1651
  }
1252
1652
  },
1253
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1653
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1254
1654
  );
1255
1655
  const [getDoc] = useLazyGetDocumentQuery();
1256
1656
  const getDocument = React__namespace.useCallback(
@@ -1276,7 +1676,7 @@ const useDocumentActions = () => {
1276
1676
  };
1277
1677
  };
1278
1678
  const ProtectedHistoryPage = React.lazy(
1279
- () => Promise.resolve().then(() => require("./History-DdIstl8b.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1679
+ () => Promise.resolve().then(() => require("./History-C_uSGzO5.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1280
1680
  );
1281
1681
  const routes$1 = [
1282
1682
  {
@@ -1289,31 +1689,31 @@ const routes$1 = [
1289
1689
  }
1290
1690
  ];
1291
1691
  const ProtectedEditViewPage = React.lazy(
1292
- () => Promise.resolve().then(() => require("./EditViewPage-DA95Ha6J.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1692
+ () => Promise.resolve().then(() => require("./EditViewPage-CUS2EAhB.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1293
1693
  );
1294
1694
  const ProtectedListViewPage = React.lazy(
1295
- () => Promise.resolve().then(() => require("./ListViewPage-wE0lXqoD.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1695
+ () => Promise.resolve().then(() => require("./ListViewPage-DHgHD8Xg.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1296
1696
  );
1297
1697
  const ProtectedListConfiguration = React.lazy(
1298
- () => Promise.resolve().then(() => require("./ListConfigurationPage-DkKRparB.js")).then((mod) => ({
1698
+ () => Promise.resolve().then(() => require("./ListConfigurationPage-nyuP7OSy.js")).then((mod) => ({
1299
1699
  default: mod.ProtectedListConfiguration
1300
1700
  }))
1301
1701
  );
1302
1702
  const ProtectedEditConfigurationPage = React.lazy(
1303
- () => Promise.resolve().then(() => require("./EditConfigurationPage-BFEwvdMW.js")).then((mod) => ({
1703
+ () => Promise.resolve().then(() => require("./EditConfigurationPage-DITVliEI.js")).then((mod) => ({
1304
1704
  default: mod.ProtectedEditConfigurationPage
1305
1705
  }))
1306
1706
  );
1307
1707
  const ProtectedComponentConfigurationPage = React.lazy(
1308
- () => Promise.resolve().then(() => require("./ComponentConfigurationPage-Bqgx7Mes.js")).then((mod) => ({
1708
+ () => Promise.resolve().then(() => require("./ComponentConfigurationPage-DP7AC0UU.js")).then((mod) => ({
1309
1709
  default: mod.ProtectedComponentConfigurationPage
1310
1710
  }))
1311
1711
  );
1312
1712
  const NoPermissions = React.lazy(
1313
- () => Promise.resolve().then(() => require("./NoPermissionsPage-DmNfF2Bb.js")).then((mod) => ({ default: mod.NoPermissions }))
1713
+ () => Promise.resolve().then(() => require("./NoPermissionsPage-zwIZydDI.js")).then((mod) => ({ default: mod.NoPermissions }))
1314
1714
  );
1315
1715
  const NoContentType = React.lazy(
1316
- () => Promise.resolve().then(() => require("./NoContentTypePage-DEKR6tf9.js")).then((mod) => ({ default: mod.NoContentType }))
1716
+ () => Promise.resolve().then(() => require("./NoContentTypePage-CDUKdZ7d.js")).then((mod) => ({ default: mod.NoContentType }))
1317
1717
  );
1318
1718
  const CollectionTypePages = () => {
1319
1719
  const { collectionType } = reactRouterDom.useParams();
@@ -1344,1073 +1744,758 @@ const routes = [
1344
1744
  Component: ProtectedListConfiguration
1345
1745
  },
1346
1746
  {
1347
- path: "components/:slug/configurations/edit",
1348
- Component: ProtectedComponentConfigurationPage
1349
- },
1350
- {
1351
- path: ":collectionType/:slug/configurations/edit",
1352
- Component: ProtectedEditConfigurationPage
1353
- },
1354
- {
1355
- path: "403",
1356
- Component: NoPermissions
1357
- },
1358
- {
1359
- path: "no-content-types",
1360
- Component: NoContentType
1361
- },
1362
- ...routes$1
1363
- ];
1364
- const DocumentActions = ({ actions: actions2 }) => {
1365
- const { formatMessage } = reactIntl.useIntl();
1366
- const [primaryAction, secondaryAction, ...restActions] = actions2.filter((action) => {
1367
- if (action.position === void 0) {
1368
- return true;
1369
- }
1370
- const positions = Array.isArray(action.position) ? action.position : [action.position];
1371
- return positions.includes("panel");
1372
- });
1373
- if (!primaryAction) {
1374
- return null;
1375
- }
1376
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, alignItems: "stretch", width: "100%", children: [
1377
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1378
- /* @__PURE__ */ jsxRuntime.jsx(DocumentActionButton, { ...primaryAction, variant: primaryAction.variant || "default" }),
1379
- restActions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1380
- DocumentActionsMenu,
1381
- {
1382
- actions: restActions,
1383
- label: formatMessage({
1384
- id: "content-manager.containers.edit.panels.default.more-actions",
1385
- defaultMessage: "More document actions"
1386
- })
1387
- }
1388
- ) : null
1389
- ] }),
1390
- secondaryAction ? /* @__PURE__ */ jsxRuntime.jsx(
1391
- DocumentActionButton,
1392
- {
1393
- ...secondaryAction,
1394
- variant: secondaryAction.variant || "secondary"
1395
- }
1396
- ) : null
1397
- ] });
1398
- };
1399
- const DocumentActionButton = (action) => {
1400
- const [dialogId, setDialogId] = React__namespace.useState(null);
1401
- const { toggleNotification } = strapiAdmin.useNotification();
1402
- const handleClick = (action2) => async (e) => {
1403
- const { onClick = () => false, dialog, id } = action2;
1404
- const muteDialog = await onClick(e);
1405
- if (dialog && !muteDialog) {
1406
- switch (dialog.type) {
1407
- case "notification":
1408
- toggleNotification({
1409
- title: dialog.title,
1410
- message: dialog.content,
1411
- type: dialog.status,
1412
- timeout: dialog.timeout,
1413
- onClose: dialog.onClose
1414
- });
1415
- break;
1416
- case "dialog":
1417
- case "modal":
1418
- e.preventDefault();
1419
- setDialogId(id);
1420
- }
1421
- }
1422
- };
1423
- const handleClose = () => {
1424
- setDialogId(null);
1425
- };
1426
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1427
- /* @__PURE__ */ jsxRuntime.jsx(
1428
- designSystem.Button,
1429
- {
1430
- flex: 1,
1431
- startIcon: action.icon,
1432
- disabled: action.disabled,
1433
- onClick: handleClick(action),
1434
- justifyContent: "center",
1435
- variant: action.variant || "default",
1436
- children: action.label
1437
- }
1438
- ),
1439
- action.dialog?.type === "dialog" ? /* @__PURE__ */ jsxRuntime.jsx(
1440
- DocumentActionConfirmDialog,
1441
- {
1442
- ...action.dialog,
1443
- variant: action.dialog?.variant ?? action.variant,
1444
- isOpen: dialogId === action.id,
1445
- onClose: handleClose
1446
- }
1447
- ) : null,
1448
- action.dialog?.type === "modal" ? /* @__PURE__ */ jsxRuntime.jsx(
1449
- DocumentActionModal,
1450
- {
1451
- ...action.dialog,
1452
- onModalClose: handleClose,
1453
- isOpen: dialogId === action.id
1454
- }
1455
- ) : null
1456
- ] });
1457
- };
1458
- const DocumentActionsMenu = ({
1459
- actions: actions2,
1460
- children,
1461
- label,
1462
- variant = "tertiary"
1463
- }) => {
1464
- const [isOpen, setIsOpen] = React__namespace.useState(false);
1465
- const [dialogId, setDialogId] = React__namespace.useState(null);
1466
- const { formatMessage } = reactIntl.useIntl();
1467
- const { toggleNotification } = strapiAdmin.useNotification();
1468
- const isDisabled = actions2.every((action) => action.disabled) || actions2.length === 0;
1469
- const handleClick = (action) => async (e) => {
1470
- const { onClick = () => false, dialog, id } = action;
1471
- const muteDialog = await onClick(e);
1472
- if (dialog && !muteDialog) {
1473
- switch (dialog.type) {
1474
- case "notification":
1475
- toggleNotification({
1476
- title: dialog.title,
1477
- message: dialog.content,
1478
- type: dialog.status,
1479
- timeout: dialog.timeout,
1480
- onClose: dialog.onClose
1481
- });
1482
- break;
1483
- case "dialog":
1484
- case "modal":
1485
- setDialogId(id);
1486
- }
1487
- }
1488
- };
1489
- const handleClose = () => {
1490
- setDialogId(null);
1491
- setIsOpen(false);
1492
- };
1493
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
1494
- /* @__PURE__ */ jsxRuntime.jsxs(
1495
- designSystem.Menu.Trigger,
1496
- {
1497
- disabled: isDisabled,
1498
- size: "S",
1499
- endIcon: null,
1500
- paddingTop: "7px",
1501
- paddingLeft: "9px",
1502
- paddingRight: "9px",
1503
- variant,
1504
- children: [
1505
- /* @__PURE__ */ jsxRuntime.jsx(Icons.More, { "aria-hidden": true, focusable: false }),
1506
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { tag: "span", children: label || formatMessage({
1507
- id: "content-manager.containers.edit.panels.default.more-actions",
1508
- defaultMessage: "More document actions"
1509
- }) })
1510
- ]
1511
- }
1512
- ),
1513
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1514
- actions2.map((action) => {
1515
- return /* @__PURE__ */ jsxRuntime.jsx(
1516
- designSystem.Menu.Item,
1517
- {
1518
- disabled: action.disabled,
1519
- onSelect: handleClick(action),
1520
- display: "block",
1521
- children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", gap: 4, children: [
1522
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1523
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1524
- action.label
1525
- ] }),
1526
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsxRuntime.jsx(
1527
- designSystem.Flex,
1528
- {
1529
- alignItems: "center",
1530
- background: "alternative100",
1531
- borderStyle: "solid",
1532
- borderColor: "alternative200",
1533
- borderWidth: "1px",
1534
- height: 5,
1535
- paddingLeft: 2,
1536
- paddingRight: 2,
1537
- hasRadius: true,
1538
- color: "alternative600",
1539
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1540
- }
1541
- )
1542
- ] })
1543
- },
1544
- action.id
1545
- );
1546
- }),
1547
- children
1548
- ] }),
1549
- actions2.map((action) => {
1550
- return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
1551
- action.dialog?.type === "dialog" ? /* @__PURE__ */ jsxRuntime.jsx(
1552
- DocumentActionConfirmDialog,
1553
- {
1554
- ...action.dialog,
1555
- variant: action.variant,
1556
- isOpen: dialogId === action.id,
1557
- onClose: handleClose
1558
- }
1559
- ) : null,
1560
- action.dialog?.type === "modal" ? /* @__PURE__ */ jsxRuntime.jsx(
1561
- DocumentActionModal,
1562
- {
1563
- ...action.dialog,
1564
- onModalClose: handleClose,
1565
- isOpen: dialogId === action.id
1566
- }
1567
- ) : null
1568
- ] }, action.id);
1569
- })
1570
- ] });
1571
- };
1572
- const convertActionVariantToColor = (variant = "secondary") => {
1573
- switch (variant) {
1574
- case "danger":
1575
- return "danger600";
1576
- case "secondary":
1577
- return void 0;
1578
- case "success":
1579
- return "success600";
1580
- default:
1581
- return "primary600";
1582
- }
1583
- };
1584
- const convertActionVariantToIconColor = (variant = "secondary") => {
1585
- switch (variant) {
1586
- case "danger":
1587
- return "danger600";
1588
- case "secondary":
1589
- return "neutral500";
1590
- case "success":
1591
- return "success600";
1592
- default:
1593
- return "primary600";
1594
- }
1595
- };
1596
- const DocumentActionConfirmDialog = ({
1597
- onClose,
1598
- onCancel,
1599
- onConfirm,
1600
- title,
1601
- content,
1602
- isOpen,
1603
- variant = "secondary"
1604
- }) => {
1605
- const { formatMessage } = reactIntl.useIntl();
1606
- const handleClose = async () => {
1607
- if (onCancel) {
1608
- await onCancel();
1609
- }
1610
- onClose();
1611
- };
1612
- const handleConfirm = async () => {
1613
- if (onConfirm) {
1614
- await onConfirm();
1615
- }
1616
- onClose();
1617
- };
1618
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
1619
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
1620
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: content }),
1621
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
1622
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", children: formatMessage({
1623
- id: "app.components.Button.cancel",
1624
- defaultMessage: "Cancel"
1625
- }) }) }),
1626
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, children: formatMessage({
1627
- id: "app.components.Button.confirm",
1628
- defaultMessage: "Confirm"
1629
- }) })
1630
- ] })
1631
- ] }) });
1632
- };
1633
- const DocumentActionModal = ({
1634
- isOpen,
1635
- title,
1636
- onClose,
1637
- footer: Footer,
1638
- content: Content,
1639
- onModalClose
1640
- }) => {
1641
- const handleClose = () => {
1642
- if (onClose) {
1643
- onClose();
1644
- }
1645
- onModalClose();
1646
- };
1647
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
1648
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: title }) }),
1649
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content }),
1650
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Footer, { children: typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer })
1651
- ] }) });
1652
- };
1653
- const PublishAction$1 = ({
1654
- activeTab,
1655
- documentId,
1656
- model,
1657
- collectionType,
1658
- meta,
1659
- document
1660
- }) => {
1661
- const { schema } = useDoc();
1662
- const navigate = reactRouterDom.useNavigate();
1663
- const { toggleNotification } = strapiAdmin.useNotification();
1664
- const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
1665
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
1666
- const { formatMessage } = reactIntl.useIntl();
1667
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1668
- "PublishAction",
1669
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1670
- );
1671
- const { publish } = useDocumentActions();
1672
- const [
1673
- countDraftRelations,
1674
- { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
1675
- ] = useLazyGetDraftRelationCountQuery();
1676
- const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React__namespace.useState(0);
1677
- const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React__namespace.useState(0);
1678
- const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1679
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1680
- const modified = strapiAdmin.useForm("PublishAction", ({ modified: modified2 }) => modified2);
1681
- const setSubmitting = strapiAdmin.useForm("PublishAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
1682
- const isSubmitting = strapiAdmin.useForm("PublishAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
1683
- const validate = strapiAdmin.useForm("PublishAction", (state) => state.validate);
1684
- const setErrors = strapiAdmin.useForm("PublishAction", (state) => state.setErrors);
1685
- const formValues = strapiAdmin.useForm("PublishAction", ({ values }) => values);
1686
- React__namespace.useEffect(() => {
1687
- if (isErrorDraftRelations) {
1688
- toggleNotification({
1689
- type: "danger",
1690
- message: formatMessage({
1691
- id: getTranslation("error.records.fetch-draft-relatons"),
1692
- defaultMessage: "An error occurred while fetching draft relations on this document."
1693
- })
1694
- });
1695
- }
1696
- }, [isErrorDraftRelations, toggleNotification, formatMessage]);
1697
- React__namespace.useEffect(() => {
1698
- const localDraftRelations = /* @__PURE__ */ new Set();
1699
- const extractDraftRelations = (data) => {
1700
- const relations = data.connect || [];
1701
- relations.forEach((relation) => {
1702
- if (relation.status === "draft") {
1703
- localDraftRelations.add(relation.id);
1704
- }
1705
- });
1706
- };
1707
- const traverseAndExtract = (data) => {
1708
- Object.entries(data).forEach(([key, value]) => {
1709
- if (key === "connect" && Array.isArray(value)) {
1710
- extractDraftRelations({ connect: value });
1711
- } else if (typeof value === "object" && value !== null) {
1712
- traverseAndExtract(value);
1713
- }
1714
- });
1715
- };
1716
- if (!documentId || modified) {
1717
- traverseAndExtract(formValues);
1718
- setLocalCountOfDraftRelations(localDraftRelations.size);
1719
- }
1720
- }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1721
- React__namespace.useEffect(() => {
1722
- if (documentId) {
1723
- const fetchDraftRelationsCount = async () => {
1724
- const { data, error } = await countDraftRelations({
1725
- collectionType,
1726
- model,
1727
- documentId,
1728
- params
1729
- });
1730
- if (error) {
1731
- throw error;
1732
- }
1733
- if (data) {
1734
- setServerCountOfDraftRelations(data.data);
1735
- }
1736
- };
1737
- fetchDraftRelationsCount();
1747
+ path: "components/:slug/configurations/edit",
1748
+ Component: ProtectedComponentConfigurationPage
1749
+ },
1750
+ {
1751
+ path: ":collectionType/:slug/configurations/edit",
1752
+ Component: ProtectedEditConfigurationPage
1753
+ },
1754
+ {
1755
+ path: "403",
1756
+ Component: NoPermissions
1757
+ },
1758
+ {
1759
+ path: "no-content-types",
1760
+ Component: NoContentType
1761
+ },
1762
+ ...routes$1
1763
+ ];
1764
+ const DocumentActions = ({ actions: actions2 }) => {
1765
+ const { formatMessage } = reactIntl.useIntl();
1766
+ const [primaryAction, secondaryAction, ...restActions] = actions2.filter((action) => {
1767
+ if (action.position === void 0) {
1768
+ return true;
1738
1769
  }
1739
- }, [documentId, countDraftRelations, collectionType, model, params]);
1740
- const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1741
- if (!schema?.options?.draftAndPublish) {
1770
+ const positions = Array.isArray(action.position) ? action.position : [action.position];
1771
+ return positions.includes("panel");
1772
+ });
1773
+ if (!primaryAction) {
1742
1774
  return null;
1743
1775
  }
1744
- const performPublish = async () => {
1745
- setSubmitting(true);
1746
- try {
1747
- const { errors } = await validate();
1748
- if (errors) {
1749
- toggleNotification({
1750
- type: "danger",
1751
- message: formatMessage({
1752
- id: "content-manager.validation.error",
1753
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1754
- })
1755
- });
1756
- return;
1757
- }
1758
- const res = await publish(
1759
- {
1760
- collectionType,
1761
- model,
1762
- documentId,
1763
- params
1764
- },
1765
- formValues
1766
- );
1767
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1768
- navigate({
1769
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1770
- search: rawQuery
1771
- });
1772
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1773
- setErrors(formatValidationErrors(res.error));
1774
- }
1775
- } finally {
1776
- setSubmitting(false);
1777
- }
1778
- };
1779
- const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1780
- const hasDraftRelations = totalDraftRelations > 0;
1781
- return {
1782
- /**
1783
- * Disabled when:
1784
- * - currently if you're cloning a document we don't support publish & clone at the same time.
1785
- * - the form is submitting
1786
- * - the active tab is the published tab
1787
- * - the document is already published & not modified
1788
- * - the document is being created & not modified
1789
- * - the user doesn't have the permission to publish
1790
- * - the user doesn't have the permission to create a new document
1791
- * - the user doesn't have the permission to update the document
1792
- */
1793
- disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
1794
- label: formatMessage({
1795
- id: "app.utils.publish",
1796
- defaultMessage: "Publish"
1797
- }),
1798
- onClick: async () => {
1799
- if (hasDraftRelations) {
1800
- return;
1801
- }
1802
- await performPublish();
1803
- },
1804
- dialog: hasDraftRelations ? {
1805
- type: "dialog",
1806
- variant: "danger",
1807
- footer: null,
1808
- title: formatMessage({
1809
- id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
1810
- defaultMessage: "Confirmation"
1811
- }),
1812
- content: formatMessage(
1813
- {
1814
- id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
1815
- defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
1816
- },
1776
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, alignItems: "stretch", width: "100%", children: [
1777
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1778
+ /* @__PURE__ */ jsxRuntime.jsx(DocumentActionButton, { ...primaryAction, variant: primaryAction.variant || "default" }),
1779
+ restActions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1780
+ DocumentActionsMenu,
1817
1781
  {
1818
- count: totalDraftRelations
1782
+ actions: restActions,
1783
+ label: formatMessage({
1784
+ id: "content-manager.containers.edit.panels.default.more-actions",
1785
+ defaultMessage: "More document actions"
1786
+ })
1819
1787
  }
1820
- ),
1821
- onConfirm: async () => {
1822
- await performPublish();
1788
+ ) : null
1789
+ ] }),
1790
+ secondaryAction ? /* @__PURE__ */ jsxRuntime.jsx(
1791
+ DocumentActionButton,
1792
+ {
1793
+ ...secondaryAction,
1794
+ variant: secondaryAction.variant || "secondary"
1823
1795
  }
1824
- } : void 0
1825
- };
1796
+ ) : null
1797
+ ] });
1826
1798
  };
1827
- PublishAction$1.type = "publish";
1828
- const UpdateAction = ({
1829
- activeTab,
1830
- documentId,
1831
- model,
1832
- collectionType
1833
- }) => {
1834
- const navigate = reactRouterDom.useNavigate();
1799
+ const DocumentActionButton = (action) => {
1800
+ const [dialogId, setDialogId] = React__namespace.useState(null);
1835
1801
  const { toggleNotification } = strapiAdmin.useNotification();
1836
- const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
1837
- const cloneMatch = reactRouterDom.useMatch(CLONE_PATH);
1838
- const isCloning = cloneMatch !== null;
1839
- const { formatMessage } = reactIntl.useIntl();
1840
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1841
- canCreate: canCreate2,
1842
- canUpdate: canUpdate2
1843
- }));
1844
- const { create, update, clone } = useDocumentActions();
1845
- const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1846
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1847
- const isSubmitting = strapiAdmin.useForm("UpdateAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
1848
- const modified = strapiAdmin.useForm("UpdateAction", ({ modified: modified2 }) => modified2);
1849
- const setSubmitting = strapiAdmin.useForm("UpdateAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
1850
- const document = strapiAdmin.useForm("UpdateAction", ({ values }) => values);
1851
- const validate = strapiAdmin.useForm("UpdateAction", (state) => state.validate);
1852
- const setErrors = strapiAdmin.useForm("UpdateAction", (state) => state.setErrors);
1853
- const resetForm = strapiAdmin.useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
1854
- return {
1855
- /**
1856
- * Disabled when:
1857
- * - the form is submitting
1858
- * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1859
- * - the active tab is the published tab
1860
- * - the user doesn't have the permission to create a new document
1861
- * - the user doesn't have the permission to update the document
1862
- */
1863
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
1864
- label: formatMessage({
1865
- id: "content-manager.containers.Edit.save",
1866
- defaultMessage: "Save"
1867
- }),
1868
- onClick: async () => {
1869
- setSubmitting(true);
1870
- try {
1871
- const { errors } = await validate();
1872
- if (errors) {
1802
+ const handleClick = (action2) => async (e) => {
1803
+ const { onClick = () => false, dialog, id } = action2;
1804
+ const muteDialog = await onClick(e);
1805
+ if (dialog && !muteDialog) {
1806
+ switch (dialog.type) {
1807
+ case "notification":
1873
1808
  toggleNotification({
1874
- type: "danger",
1875
- message: formatMessage({
1876
- id: "content-manager.validation.error",
1877
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1878
- })
1809
+ title: dialog.title,
1810
+ message: dialog.content,
1811
+ type: dialog.status,
1812
+ timeout: dialog.timeout,
1813
+ onClose: dialog.onClose
1879
1814
  });
1880
- return;
1881
- }
1882
- if (isCloning) {
1883
- const res = await clone(
1884
- {
1885
- model,
1886
- documentId: cloneMatch.params.origin,
1887
- params
1888
- },
1889
- document
1890
- );
1891
- if ("data" in res) {
1892
- navigate({
1893
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1894
- search: rawQuery
1895
- });
1896
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1897
- setErrors(formatValidationErrors(res.error));
1898
- }
1899
- } else if (documentId || collectionType === SINGLE_TYPES) {
1900
- const res = await update(
1901
- {
1902
- collectionType,
1903
- model,
1904
- documentId,
1905
- params
1906
- },
1907
- document
1908
- );
1909
- if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1910
- setErrors(formatValidationErrors(res.error));
1911
- } else {
1912
- resetForm();
1913
- }
1914
- } else {
1915
- const res = await create(
1916
- {
1917
- model,
1918
- params
1919
- },
1920
- document
1921
- );
1922
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1923
- navigate(
1924
- {
1925
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1926
- search: rawQuery
1927
- },
1928
- { replace: true }
1929
- );
1930
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1931
- setErrors(formatValidationErrors(res.error));
1932
- }
1933
- }
1934
- } finally {
1935
- setSubmitting(false);
1815
+ break;
1816
+ case "dialog":
1817
+ case "modal":
1818
+ e.preventDefault();
1819
+ setDialogId(id);
1820
+ }
1821
+ }
1822
+ };
1823
+ const handleClose = () => {
1824
+ setDialogId(null);
1825
+ };
1826
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1827
+ /* @__PURE__ */ jsxRuntime.jsx(
1828
+ designSystem.Button,
1829
+ {
1830
+ flex: "auto",
1831
+ startIcon: action.icon,
1832
+ disabled: action.disabled,
1833
+ onClick: handleClick(action),
1834
+ justifyContent: "center",
1835
+ variant: action.variant || "default",
1836
+ paddingTop: "7px",
1837
+ paddingBottom: "7px",
1838
+ children: action.label
1839
+ }
1840
+ ),
1841
+ action.dialog?.type === "dialog" ? /* @__PURE__ */ jsxRuntime.jsx(
1842
+ DocumentActionConfirmDialog,
1843
+ {
1844
+ ...action.dialog,
1845
+ variant: action.dialog?.variant ?? action.variant,
1846
+ isOpen: dialogId === action.id,
1847
+ onClose: handleClose
1848
+ }
1849
+ ) : null,
1850
+ action.dialog?.type === "modal" ? /* @__PURE__ */ jsxRuntime.jsx(
1851
+ DocumentActionModal,
1852
+ {
1853
+ ...action.dialog,
1854
+ onModalClose: handleClose,
1855
+ isOpen: dialogId === action.id
1936
1856
  }
1937
- }
1938
- };
1939
- };
1940
- UpdateAction.type = "update";
1941
- const UNPUBLISH_DRAFT_OPTIONS = {
1942
- KEEP: "keep",
1943
- DISCARD: "discard"
1857
+ ) : null
1858
+ ] });
1944
1859
  };
1945
- const UnpublishAction$1 = ({
1946
- activeTab,
1947
- documentId,
1948
- model,
1949
- collectionType,
1950
- document
1860
+ const DocumentActionsMenu = ({
1861
+ actions: actions2,
1862
+ children,
1863
+ label,
1864
+ variant = "tertiary"
1951
1865
  }) => {
1866
+ const [isOpen, setIsOpen] = React__namespace.useState(false);
1867
+ const [dialogId, setDialogId] = React__namespace.useState(null);
1952
1868
  const { formatMessage } = reactIntl.useIntl();
1953
- const { schema } = useDoc();
1954
- const canPublish = useDocumentRBAC("UnpublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1955
- const { unpublish } = useDocumentActions();
1956
- const [{ query }] = strapiAdmin.useQueryParams();
1957
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1958
1869
  const { toggleNotification } = strapiAdmin.useNotification();
1959
- const [shouldKeepDraft, setShouldKeepDraft] = React__namespace.useState(true);
1960
- const isDocumentModified = document?.status === "modified";
1961
- const handleChange = (value) => {
1962
- setShouldKeepDraft(value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
1963
- };
1964
- if (!schema?.options?.draftAndPublish) {
1965
- return null;
1966
- }
1967
- return {
1968
- disabled: !canPublish || activeTab === "published" || document?.status !== "published" && document?.status !== "modified",
1969
- label: formatMessage({
1970
- id: "app.utils.unpublish",
1971
- defaultMessage: "Unpublish"
1972
- }),
1973
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
1974
- onClick: async () => {
1975
- if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1976
- if (!documentId) {
1977
- console.error(
1978
- "You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
1979
- );
1870
+ const isDisabled = actions2.every((action) => action.disabled) || actions2.length === 0;
1871
+ const handleClick = (action) => async (e) => {
1872
+ const { onClick = () => false, dialog, id } = action;
1873
+ const muteDialog = await onClick(e);
1874
+ if (dialog && !muteDialog) {
1875
+ switch (dialog.type) {
1876
+ case "notification":
1980
1877
  toggleNotification({
1981
- message: formatMessage({
1982
- id: "content-manager.actions.unpublish.error",
1983
- defaultMessage: "An error occurred while trying to unpublish the document."
1984
- }),
1985
- type: "danger"
1878
+ title: dialog.title,
1879
+ message: dialog.content,
1880
+ type: dialog.status,
1881
+ timeout: dialog.timeout,
1882
+ onClose: dialog.onClose
1986
1883
  });
1987
- }
1988
- return;
1884
+ break;
1885
+ case "dialog":
1886
+ case "modal":
1887
+ setDialogId(id);
1989
1888
  }
1990
- await unpublish({
1991
- collectionType,
1992
- model,
1993
- documentId,
1994
- params
1995
- });
1996
- },
1997
- dialog: isDocumentModified ? {
1998
- type: "dialog",
1999
- title: formatMessage({
2000
- id: "app.components.ConfirmDialog.title",
2001
- defaultMessage: "Confirmation"
2002
- }),
2003
- content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "flex-start", direction: "column", gap: 6, children: [
2004
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", direction: "column", gap: 2, children: [
2005
- /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2006
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2007
- id: "content-manager.actions.unpublish.dialog.body",
2008
- defaultMessage: "Are you sure?"
1889
+ }
1890
+ };
1891
+ const handleClose = () => {
1892
+ setDialogId(null);
1893
+ setIsOpen(false);
1894
+ };
1895
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
1896
+ /* @__PURE__ */ jsxRuntime.jsxs(
1897
+ designSystem.Menu.Trigger,
1898
+ {
1899
+ disabled: isDisabled,
1900
+ size: "S",
1901
+ endIcon: null,
1902
+ paddingTop: "4px",
1903
+ paddingLeft: "7px",
1904
+ paddingRight: "7px",
1905
+ variant,
1906
+ children: [
1907
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.More, { "aria-hidden": true, focusable: false }),
1908
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { tag: "span", children: label || formatMessage({
1909
+ id: "content-manager.containers.edit.panels.default.more-actions",
1910
+ defaultMessage: "More document actions"
2009
1911
  }) })
2010
- ] }),
2011
- /* @__PURE__ */ jsxRuntime.jsxs(
2012
- designSystem.Radio.Group,
2013
- {
2014
- defaultValue: UNPUBLISH_DRAFT_OPTIONS.KEEP,
2015
- name: "discard-options",
2016
- "aria-label": formatMessage({
2017
- id: "content-manager.actions.unpublish.dialog.radio-label",
2018
- defaultMessage: "Choose an option to unpublish the document."
2019
- }),
2020
- onValueChange: handleChange,
2021
- children: [
2022
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { checked: shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.KEEP, children: formatMessage({
2023
- id: "content-manager.actions.unpublish.dialog.option.keep-draft",
2024
- defaultMessage: "Keep draft"
2025
- }) }),
2026
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { checked: !shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.DISCARD, children: formatMessage({
2027
- id: "content-manager.actions.unpublish.dialog.option.replace-draft",
2028
- defaultMessage: "Replace draft"
2029
- }) })
2030
- ]
2031
- }
2032
- )
2033
- ] }),
2034
- onConfirm: async () => {
2035
- if (!documentId && collectionType !== SINGLE_TYPES) {
2036
- console.error(
2037
- "You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
2038
- );
2039
- toggleNotification({
2040
- message: formatMessage({
2041
- id: "content-manager.actions.unpublish.error",
2042
- defaultMessage: "An error occurred while trying to unpublish the document."
2043
- }),
2044
- type: "danger"
2045
- });
2046
- }
2047
- await unpublish(
1912
+ ]
1913
+ }
1914
+ ),
1915
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1916
+ actions2.map((action) => {
1917
+ return /* @__PURE__ */ jsxRuntime.jsx(
1918
+ designSystem.Menu.Item,
2048
1919
  {
2049
- collectionType,
2050
- model,
2051
- documentId,
2052
- params
1920
+ disabled: action.disabled,
1921
+ onSelect: handleClick(action),
1922
+ display: "block",
1923
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", gap: 4, children: [
1924
+ /* @__PURE__ */ jsxRuntime.jsxs(
1925
+ designSystem.Flex,
1926
+ {
1927
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1928
+ gap: 2,
1929
+ tag: "span",
1930
+ children: [
1931
+ /* @__PURE__ */ jsxRuntime.jsx(
1932
+ designSystem.Flex,
1933
+ {
1934
+ tag: "span",
1935
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1936
+ children: action.icon
1937
+ }
1938
+ ),
1939
+ action.label
1940
+ ]
1941
+ }
1942
+ ),
1943
+ action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsxRuntime.jsx(
1944
+ designSystem.Flex,
1945
+ {
1946
+ alignItems: "center",
1947
+ background: "alternative100",
1948
+ borderStyle: "solid",
1949
+ borderColor: "alternative200",
1950
+ borderWidth: "1px",
1951
+ height: 5,
1952
+ paddingLeft: 2,
1953
+ paddingRight: 2,
1954
+ hasRadius: true,
1955
+ color: "alternative600",
1956
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1957
+ }
1958
+ )
1959
+ ] })
2053
1960
  },
2054
- !shouldKeepDraft
1961
+ action.id
2055
1962
  );
2056
- }
2057
- } : void 0,
2058
- variant: "danger",
2059
- position: ["panel", "table-row"]
1963
+ }),
1964
+ children
1965
+ ] }),
1966
+ actions2.map((action) => {
1967
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
1968
+ action.dialog?.type === "dialog" ? /* @__PURE__ */ jsxRuntime.jsx(
1969
+ DocumentActionConfirmDialog,
1970
+ {
1971
+ ...action.dialog,
1972
+ variant: action.variant,
1973
+ isOpen: dialogId === action.id,
1974
+ onClose: handleClose
1975
+ }
1976
+ ) : null,
1977
+ action.dialog?.type === "modal" ? /* @__PURE__ */ jsxRuntime.jsx(
1978
+ DocumentActionModal,
1979
+ {
1980
+ ...action.dialog,
1981
+ onModalClose: handleClose,
1982
+ isOpen: dialogId === action.id
1983
+ }
1984
+ ) : null
1985
+ ] }, action.id);
1986
+ })
1987
+ ] });
1988
+ };
1989
+ const convertActionVariantToColor = (variant = "secondary") => {
1990
+ switch (variant) {
1991
+ case "danger":
1992
+ return "danger600";
1993
+ case "secondary":
1994
+ return void 0;
1995
+ case "success":
1996
+ return "success600";
1997
+ default:
1998
+ return "primary600";
1999
+ }
2000
+ };
2001
+ const convertActionVariantToIconColor = (variant = "secondary") => {
2002
+ switch (variant) {
2003
+ case "danger":
2004
+ return "danger600";
2005
+ case "secondary":
2006
+ return "neutral500";
2007
+ case "success":
2008
+ return "success600";
2009
+ default:
2010
+ return "primary600";
2011
+ }
2012
+ };
2013
+ const DocumentActionConfirmDialog = ({
2014
+ onClose,
2015
+ onCancel,
2016
+ onConfirm,
2017
+ title,
2018
+ content,
2019
+ isOpen,
2020
+ variant = "secondary"
2021
+ }) => {
2022
+ const { formatMessage } = reactIntl.useIntl();
2023
+ const handleClose = async () => {
2024
+ if (onCancel) {
2025
+ await onCancel();
2026
+ }
2027
+ onClose();
2060
2028
  };
2029
+ const handleConfirm = async () => {
2030
+ if (onConfirm) {
2031
+ await onConfirm();
2032
+ }
2033
+ onClose();
2034
+ };
2035
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2036
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2037
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: content }),
2038
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
2039
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
2040
+ id: "app.components.Button.cancel",
2041
+ defaultMessage: "Cancel"
2042
+ }) }) }),
2043
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
2044
+ id: "app.components.Button.confirm",
2045
+ defaultMessage: "Confirm"
2046
+ }) })
2047
+ ] })
2048
+ ] }) });
2061
2049
  };
2062
- UnpublishAction$1.type = "unpublish";
2063
- const DiscardAction = ({
2050
+ const DocumentActionModal = ({
2051
+ isOpen,
2052
+ title,
2053
+ onClose,
2054
+ footer: Footer,
2055
+ content: Content,
2056
+ onModalClose
2057
+ }) => {
2058
+ const handleClose = () => {
2059
+ if (onClose) {
2060
+ onClose();
2061
+ }
2062
+ onModalClose();
2063
+ };
2064
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
2065
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: title }) }),
2066
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: Content }),
2067
+ typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer
2068
+ ] }) });
2069
+ };
2070
+ const PublishAction$1 = ({
2064
2071
  activeTab,
2065
2072
  documentId,
2066
2073
  model,
2067
2074
  collectionType,
2075
+ meta,
2068
2076
  document
2069
2077
  }) => {
2070
- const { formatMessage } = reactIntl.useIntl();
2071
2078
  const { schema } = useDoc();
2072
- const canUpdate = useDocumentRBAC("DiscardAction", ({ canUpdate: canUpdate2 }) => canUpdate2);
2073
- const { discard } = useDocumentActions();
2074
- const [{ query }] = strapiAdmin.useQueryParams();
2075
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
2076
- if (!schema?.options?.draftAndPublish) {
2077
- return null;
2078
- }
2079
- return {
2080
- disabled: !canUpdate || activeTab === "published" || document?.status !== "modified",
2081
- label: formatMessage({
2082
- id: "content-manager.actions.discard.label",
2083
- defaultMessage: "Discard changes"
2084
- }),
2085
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2086
- position: ["panel", "table-row"],
2087
- variant: "danger",
2088
- dialog: {
2089
- type: "dialog",
2090
- title: formatMessage({
2091
- id: "app.components.ConfirmDialog.title",
2092
- defaultMessage: "Confirmation"
2093
- }),
2094
- content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
2095
- /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2096
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2097
- id: "content-manager.actions.discard.dialog.body",
2098
- defaultMessage: "Are you sure?"
2099
- }) })
2100
- ] }),
2101
- onConfirm: async () => {
2102
- await discard({
2103
- collectionType,
2104
- model,
2105
- documentId,
2106
- params
2107
- });
2108
- }
2109
- }
2110
- };
2111
- };
2112
- DiscardAction.type = "discard";
2113
- const StyledCrossCircle = styledComponents.styled(Icons.CrossCircle)`
2114
- path {
2115
- fill: currentColor;
2116
- }
2117
- `;
2118
- const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2119
- const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2120
- const RelativeTime = React__namespace.forwardRef(
2121
- ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
2122
- const { formatRelativeTime, formatDate, formatTime } = reactIntl.useIntl();
2123
- const interval = dateFns.intervalToDuration({
2124
- start: timestamp,
2125
- end: Date.now()
2126
- // see https://github.com/date-fns/date-fns/issues/2891 – No idea why it's all partial it returns it every time.
2127
- });
2128
- const unit = intervals.find((intervalUnit) => {
2129
- return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2130
- });
2131
- const relativeTime = dateFns.isPast(timestamp) ? -interval[unit] : interval[unit];
2132
- const customInterval = customIntervals.find(
2133
- (custom) => interval[custom.unit] < custom.threshold
2134
- );
2135
- const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, { numeric: "auto" });
2136
- return /* @__PURE__ */ jsxRuntime.jsx(
2137
- "time",
2138
- {
2139
- ref: forwardedRef,
2140
- dateTime: timestamp.toISOString(),
2141
- role: "time",
2142
- title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
2143
- ...restProps,
2144
- children: displayText
2145
- }
2146
- );
2147
- }
2148
- );
2149
- const getDisplayName = ({
2150
- firstname,
2151
- lastname,
2152
- username,
2153
- email
2154
- } = {}) => {
2155
- if (username) {
2156
- return username;
2157
- }
2158
- if (firstname) {
2159
- return `${firstname} ${lastname ?? ""}`.trim();
2160
- }
2161
- return email ?? "";
2162
- };
2163
- const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2164
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2165
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2166
- 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) }) });
2167
- };
2168
- const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2169
- const { formatMessage } = reactIntl.useIntl();
2079
+ const navigate = reactRouterDom.useNavigate();
2080
+ const { toggleNotification } = strapiAdmin.useNotification();
2081
+ const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
2082
+ const isListView = reactRouterDom.useMatch(LIST_PATH) !== null;
2170
2083
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2171
- const title = isCreating ? formatMessage({
2172
- id: "content-manager.containers.edit.title.new",
2173
- defaultMessage: "Create an entry"
2174
- }) : documentTitle;
2175
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2176
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.BackButton, {}),
2177
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2178
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2179
- /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2180
- ] }),
2181
- status ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2182
- ] });
2183
- };
2184
- const HeaderToolbar = () => {
2185
2084
  const { formatMessage } = reactIntl.useIntl();
2186
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2085
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
2086
+ const { publish } = useDocumentActions();
2187
2087
  const [
2188
- {
2189
- query: { status = "draft" }
2088
+ countDraftRelations,
2089
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2090
+ ] = useLazyGetDraftRelationCountQuery();
2091
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React__namespace.useState(0);
2092
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React__namespace.useState(0);
2093
+ const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
2094
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
2095
+ const modified = strapiAdmin.useForm("PublishAction", ({ modified: modified2 }) => modified2);
2096
+ const setSubmitting = strapiAdmin.useForm("PublishAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
2097
+ const isSubmitting = strapiAdmin.useForm("PublishAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
2098
+ const validate = strapiAdmin.useForm("PublishAction", (state) => state.validate);
2099
+ const setErrors = strapiAdmin.useForm("PublishAction", (state) => state.setErrors);
2100
+ const formValues = strapiAdmin.useForm("PublishAction", ({ values }) => values);
2101
+ React__namespace.useEffect(() => {
2102
+ if (isErrorDraftRelations) {
2103
+ toggleNotification({
2104
+ type: "danger",
2105
+ message: formatMessage({
2106
+ id: getTranslation("error.records.fetch-draft-relatons"),
2107
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2108
+ })
2109
+ });
2190
2110
  }
2191
- ] = strapiAdmin.useQueryParams();
2192
- const { model, id, document, meta, collectionType } = useDoc();
2193
- const plugins = strapiAdmin.useStrapiApp("HeaderToolbar", (state) => state.plugins);
2194
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
2195
- /* @__PURE__ */ jsxRuntime.jsx(
2196
- strapiAdmin.DescriptionComponentRenderer,
2197
- {
2198
- props: {
2199
- activeTab: status,
2200
- model,
2201
- documentId: id,
2202
- document: isCloning ? void 0 : document,
2203
- meta: isCloning ? void 0 : meta,
2204
- collectionType
2205
- },
2206
- descriptions: plugins["content-manager"].apis.getHeaderActions(),
2207
- children: (actions2) => {
2208
- if (actions2.length > 0) {
2209
- return /* @__PURE__ */ jsxRuntime.jsx(HeaderActions, { actions: actions2 });
2210
- } else {
2211
- return null;
2212
- }
2111
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2112
+ React__namespace.useEffect(() => {
2113
+ const localDraftRelations = /* @__PURE__ */ new Set();
2114
+ const extractDraftRelations = (data) => {
2115
+ const relations = data.connect || [];
2116
+ relations.forEach((relation) => {
2117
+ if (relation.status === "draft") {
2118
+ localDraftRelations.add(relation.id);
2213
2119
  }
2214
- }
2215
- ),
2216
- /* @__PURE__ */ jsxRuntime.jsx(
2217
- strapiAdmin.DescriptionComponentRenderer,
2218
- {
2219
- props: {
2220
- activeTab: status,
2221
- model,
2222
- documentId: id,
2223
- document: isCloning ? void 0 : document,
2224
- meta: isCloning ? void 0 : meta,
2225
- collectionType
2226
- },
2227
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2228
- children: (actions2) => {
2229
- const headerActions = actions2.filter((action) => {
2230
- const positions = Array.isArray(action.position) ? action.position : [action.position];
2231
- return positions.includes("header");
2232
- });
2233
- return /* @__PURE__ */ jsxRuntime.jsx(
2234
- DocumentActionsMenu,
2235
- {
2236
- actions: headerActions,
2237
- label: formatMessage({
2238
- id: "content-manager.containers.edit.header.more-actions",
2239
- defaultMessage: "More actions"
2240
- }),
2241
- children: /* @__PURE__ */ jsxRuntime.jsx(Information, { activeTab: status })
2242
- }
2243
- );
2120
+ });
2121
+ };
2122
+ const traverseAndExtract = (data) => {
2123
+ Object.entries(data).forEach(([key, value]) => {
2124
+ if (key === "connect" && Array.isArray(value)) {
2125
+ extractDraftRelations({ connect: value });
2126
+ } else if (typeof value === "object" && value !== null) {
2127
+ traverseAndExtract(value);
2244
2128
  }
2129
+ });
2130
+ };
2131
+ if (!documentId || modified) {
2132
+ traverseAndExtract(formValues);
2133
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2134
+ }
2135
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2136
+ React__namespace.useEffect(() => {
2137
+ if (!document || !document.documentId || isListView) {
2138
+ return;
2139
+ }
2140
+ const fetchDraftRelationsCount = async () => {
2141
+ const { data, error } = await countDraftRelations({
2142
+ collectionType,
2143
+ model,
2144
+ documentId,
2145
+ params
2146
+ });
2147
+ if (error) {
2148
+ throw error;
2245
2149
  }
2246
- )
2247
- ] });
2248
- };
2249
- const Information = ({ activeTab }) => {
2250
- const { formatMessage } = reactIntl.useIntl();
2251
- const { document, meta } = useDoc();
2252
- if (!document || !document.id) {
2150
+ if (data) {
2151
+ setServerCountOfDraftRelations(data.data);
2152
+ }
2153
+ };
2154
+ fetchDraftRelationsCount();
2155
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
2156
+ const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
2157
+ if (!schema?.options?.draftAndPublish) {
2253
2158
  return null;
2254
2159
  }
2255
- const createAndUpdateDocument = activeTab === "draft" ? document : meta?.availableStatus.find((status) => status.publishedAt === null);
2256
- const publishDocument = activeTab === "published" ? document : meta?.availableStatus.find((status) => status.publishedAt !== null);
2257
- const creator = createAndUpdateDocument?.[CREATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[CREATED_BY_ATTRIBUTE_NAME]) : null;
2258
- const updator = createAndUpdateDocument?.[UPDATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[UPDATED_BY_ATTRIBUTE_NAME]) : null;
2259
- const information = [
2260
- {
2261
- isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2262
- label: formatMessage({
2263
- id: "content-manager.containers.edit.information.last-published.label",
2264
- defaultMessage: "Last published"
2265
- }),
2266
- value: formatMessage(
2160
+ const performPublish = async () => {
2161
+ setSubmitting(true);
2162
+ try {
2163
+ const { errors } = await validate();
2164
+ if (errors) {
2165
+ toggleNotification({
2166
+ type: "danger",
2167
+ message: formatMessage({
2168
+ id: "content-manager.validation.error",
2169
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2170
+ })
2171
+ });
2172
+ return;
2173
+ }
2174
+ const res = await publish(
2267
2175
  {
2268
- id: "content-manager.containers.edit.information.last-published.value",
2269
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2176
+ collectionType,
2177
+ model,
2178
+ documentId,
2179
+ params
2270
2180
  },
2271
- {
2272
- time: /* @__PURE__ */ jsxRuntime.jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
2273
- isAnonymous: !publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME],
2274
- author: publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME] ? getDisplayName(publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME]) : null
2275
- }
2276
- )
2181
+ formValues
2182
+ );
2183
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2184
+ navigate({
2185
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2186
+ search: rawQuery
2187
+ });
2188
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2189
+ setErrors(formatValidationErrors(res.error));
2190
+ }
2191
+ } finally {
2192
+ setSubmitting(false);
2193
+ }
2194
+ };
2195
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2196
+ const enableDraftRelationsCount = false;
2197
+ const hasDraftRelations = enableDraftRelationsCount;
2198
+ return {
2199
+ /**
2200
+ * Disabled when:
2201
+ * - currently if you're cloning a document we don't support publish & clone at the same time.
2202
+ * - the form is submitting
2203
+ * - the active tab is the published tab
2204
+ * - the document is already published & not modified
2205
+ * - the document is being created & not modified
2206
+ * - the user doesn't have the permission to publish
2207
+ */
2208
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
2209
+ label: formatMessage({
2210
+ id: "app.utils.publish",
2211
+ defaultMessage: "Publish"
2212
+ }),
2213
+ onClick: async () => {
2214
+ await performPublish();
2277
2215
  },
2278
- {
2279
- isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2280
- label: formatMessage({
2281
- id: "content-manager.containers.edit.information.last-draft.label",
2282
- defaultMessage: "Last draft"
2216
+ dialog: hasDraftRelations ? {
2217
+ type: "dialog",
2218
+ variant: "danger",
2219
+ footer: null,
2220
+ title: formatMessage({
2221
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2222
+ defaultMessage: "Confirmation"
2283
2223
  }),
2284
- value: formatMessage(
2224
+ content: formatMessage(
2285
2225
  {
2286
- id: "content-manager.containers.edit.information.last-draft.value",
2287
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2226
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2227
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2288
2228
  },
2289
2229
  {
2290
- time: /* @__PURE__ */ jsxRuntime.jsx(
2291
- RelativeTime,
2292
- {
2293
- timestamp: new Date(createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME])
2294
- }
2295
- ),
2296
- isAnonymous: !updator,
2297
- author: updator
2230
+ count: totalDraftRelations
2298
2231
  }
2299
- )
2300
- },
2301
- {
2302
- isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2303
- label: formatMessage({
2304
- id: "content-manager.containers.edit.information.document.label",
2305
- defaultMessage: "Document"
2306
- }),
2307
- value: formatMessage(
2308
- {
2309
- id: "content-manager.containers.edit.information.document.value",
2310
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2311
- },
2312
- {
2313
- time: /* @__PURE__ */ jsxRuntime.jsx(
2314
- RelativeTime,
2315
- {
2316
- timestamp: new Date(createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME])
2317
- }
2318
- ),
2319
- isAnonymous: !creator,
2320
- author: creator
2232
+ ),
2233
+ onConfirm: async () => {
2234
+ await performPublish();
2235
+ }
2236
+ } : void 0
2237
+ };
2238
+ };
2239
+ PublishAction$1.type = "publish";
2240
+ const UpdateAction = ({
2241
+ activeTab,
2242
+ documentId,
2243
+ model,
2244
+ collectionType
2245
+ }) => {
2246
+ const navigate = reactRouterDom.useNavigate();
2247
+ const { toggleNotification } = strapiAdmin.useNotification();
2248
+ const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
2249
+ const cloneMatch = reactRouterDom.useMatch(CLONE_PATH);
2250
+ const isCloning = cloneMatch !== null;
2251
+ const { formatMessage } = reactIntl.useIntl();
2252
+ const { create, update, clone } = useDocumentActions();
2253
+ const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
2254
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
2255
+ const isSubmitting = strapiAdmin.useForm("UpdateAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
2256
+ const modified = strapiAdmin.useForm("UpdateAction", ({ modified: modified2 }) => modified2);
2257
+ const setSubmitting = strapiAdmin.useForm("UpdateAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
2258
+ const document = strapiAdmin.useForm("UpdateAction", ({ values }) => values);
2259
+ const validate = strapiAdmin.useForm("UpdateAction", (state) => state.validate);
2260
+ const setErrors = strapiAdmin.useForm("UpdateAction", (state) => state.setErrors);
2261
+ const resetForm = strapiAdmin.useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
2262
+ return {
2263
+ /**
2264
+ * Disabled when:
2265
+ * - the form is submitting
2266
+ * - the document is not modified & we're not cloning (you can save a clone entity straight away)
2267
+ * - the active tab is the published tab
2268
+ */
2269
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
2270
+ label: formatMessage({
2271
+ id: "content-manager.containers.Edit.save",
2272
+ defaultMessage: "Save"
2273
+ }),
2274
+ onClick: async () => {
2275
+ setSubmitting(true);
2276
+ try {
2277
+ if (activeTab !== "draft") {
2278
+ const { errors } = await validate();
2279
+ if (errors) {
2280
+ toggleNotification({
2281
+ type: "danger",
2282
+ message: formatMessage({
2283
+ id: "content-manager.validation.error",
2284
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2285
+ })
2286
+ });
2287
+ return;
2288
+ }
2321
2289
  }
2322
- )
2323
- }
2324
- ].filter((info) => info.isDisplayed);
2325
- return /* @__PURE__ */ jsxRuntime.jsx(
2326
- designSystem.Flex,
2327
- {
2328
- borderWidth: "1px 0 0 0",
2329
- borderStyle: "solid",
2330
- borderColor: "neutral150",
2331
- direction: "column",
2332
- marginTop: 2,
2333
- tag: "dl",
2334
- padding: 5,
2335
- gap: 3,
2336
- alignItems: "flex-start",
2337
- marginLeft: "-0.4rem",
2338
- marginRight: "-0.4rem",
2339
- width: "calc(100% + 8px)",
2340
- children: information.map((info) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, direction: "column", alignItems: "flex-start", children: [
2341
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "dt", variant: "pi", fontWeight: "bold", children: info.label }),
2342
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "dd", variant: "pi", textColor: "neutral600", children: info.value })
2343
- ] }, info.label))
2290
+ if (isCloning) {
2291
+ const res = await clone(
2292
+ {
2293
+ model,
2294
+ documentId: cloneMatch.params.origin,
2295
+ params
2296
+ },
2297
+ document
2298
+ );
2299
+ if ("data" in res) {
2300
+ navigate(
2301
+ {
2302
+ pathname: `../${res.data.documentId}`,
2303
+ search: rawQuery
2304
+ },
2305
+ { relative: "path" }
2306
+ );
2307
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2308
+ setErrors(formatValidationErrors(res.error));
2309
+ }
2310
+ } else if (documentId || collectionType === SINGLE_TYPES) {
2311
+ const res = await update(
2312
+ {
2313
+ collectionType,
2314
+ model,
2315
+ documentId,
2316
+ params
2317
+ },
2318
+ document
2319
+ );
2320
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2321
+ setErrors(formatValidationErrors(res.error));
2322
+ } else {
2323
+ resetForm();
2324
+ }
2325
+ } else {
2326
+ const res = await create(
2327
+ {
2328
+ model,
2329
+ params
2330
+ },
2331
+ document
2332
+ );
2333
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2334
+ navigate(
2335
+ {
2336
+ pathname: `../${res.data.documentId}`,
2337
+ search: rawQuery
2338
+ },
2339
+ { replace: true, relative: "path" }
2340
+ );
2341
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2342
+ setErrors(formatValidationErrors(res.error));
2343
+ }
2344
+ }
2345
+ } finally {
2346
+ setSubmitting(false);
2347
+ }
2344
2348
  }
2345
- );
2349
+ };
2346
2350
  };
2347
- const HeaderActions = ({ actions: actions2 }) => {
2348
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: actions2.map((action) => {
2349
- if ("options" in action) {
2350
- return /* @__PURE__ */ jsxRuntime.jsx(
2351
- designSystem.SingleSelect,
2352
- {
2353
- size: "S",
2354
- disabled: action.disabled,
2355
- "aria-label": action.label,
2356
- onChange: action.onSelect,
2357
- value: action.value,
2358
- children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { ...option, children: label }, option.value))
2359
- },
2360
- action.id
2361
- );
2362
- } else {
2363
- return null;
2364
- }
2365
- }) });
2351
+ UpdateAction.type = "update";
2352
+ const UNPUBLISH_DRAFT_OPTIONS = {
2353
+ KEEP: "keep",
2354
+ DISCARD: "discard"
2366
2355
  };
2367
- const ConfigureTheViewAction = ({ collectionType, model }) => {
2368
- const navigate = reactRouterDom.useNavigate();
2356
+ const UnpublishAction$1 = ({
2357
+ activeTab,
2358
+ documentId,
2359
+ model,
2360
+ collectionType,
2361
+ document
2362
+ }) => {
2369
2363
  const { formatMessage } = reactIntl.useIntl();
2370
- return {
2371
- label: formatMessage({
2372
- id: "app.links.configure-view",
2373
- defaultMessage: "Configure the view"
2374
- }),
2375
- icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ListPlus, {}),
2376
- onClick: () => {
2377
- navigate(`../${collectionType}/${model}/configurations/edit`);
2378
- },
2379
- position: "header"
2364
+ const { schema } = useDoc();
2365
+ const canPublish = useDocumentRBAC("UnpublishAction", ({ canPublish: canPublish2 }) => canPublish2);
2366
+ const { unpublish } = useDocumentActions();
2367
+ const [{ query }] = strapiAdmin.useQueryParams();
2368
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
2369
+ const { toggleNotification } = strapiAdmin.useNotification();
2370
+ const [shouldKeepDraft, setShouldKeepDraft] = React__namespace.useState(true);
2371
+ const isDocumentModified = document?.status === "modified";
2372
+ const handleChange = (value) => {
2373
+ setShouldKeepDraft(value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
2380
2374
  };
2381
- };
2382
- ConfigureTheViewAction.type = "configure-the-view";
2383
- const EditTheModelAction = ({ model }) => {
2384
- const navigate = reactRouterDom.useNavigate();
2385
- const { formatMessage } = reactIntl.useIntl();
2375
+ if (!schema?.options?.draftAndPublish) {
2376
+ return null;
2377
+ }
2386
2378
  return {
2379
+ disabled: !canPublish || activeTab === "published" || document?.status !== "published" && document?.status !== "modified",
2387
2380
  label: formatMessage({
2388
- id: "content-manager.link-to-ctb",
2389
- defaultMessage: "Edit the model"
2381
+ id: "app.utils.unpublish",
2382
+ defaultMessage: "Unpublish"
2390
2383
  }),
2391
- icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {}),
2392
- onClick: () => {
2393
- navigate(`/plugins/content-type-builder/content-types/${model}`);
2384
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
2385
+ onClick: async () => {
2386
+ if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
2387
+ if (!documentId) {
2388
+ console.error(
2389
+ "You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
2390
+ );
2391
+ toggleNotification({
2392
+ message: formatMessage({
2393
+ id: "content-manager.actions.unpublish.error",
2394
+ defaultMessage: "An error occurred while trying to unpublish the document."
2395
+ }),
2396
+ type: "danger"
2397
+ });
2398
+ }
2399
+ return;
2400
+ }
2401
+ await unpublish({
2402
+ collectionType,
2403
+ model,
2404
+ documentId,
2405
+ params
2406
+ });
2394
2407
  },
2395
- position: "header"
2408
+ dialog: isDocumentModified ? {
2409
+ type: "dialog",
2410
+ title: formatMessage({
2411
+ id: "app.components.ConfirmDialog.title",
2412
+ defaultMessage: "Confirmation"
2413
+ }),
2414
+ content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "flex-start", direction: "column", gap: 6, children: [
2415
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", direction: "column", gap: 2, children: [
2416
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2417
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2418
+ id: "content-manager.actions.unpublish.dialog.body",
2419
+ defaultMessage: "Are you sure?"
2420
+ }) })
2421
+ ] }),
2422
+ /* @__PURE__ */ jsxRuntime.jsxs(
2423
+ designSystem.Radio.Group,
2424
+ {
2425
+ defaultValue: UNPUBLISH_DRAFT_OPTIONS.KEEP,
2426
+ name: "discard-options",
2427
+ "aria-label": formatMessage({
2428
+ id: "content-manager.actions.unpublish.dialog.radio-label",
2429
+ defaultMessage: "Choose an option to unpublish the document."
2430
+ }),
2431
+ onValueChange: handleChange,
2432
+ children: [
2433
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { checked: shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.KEEP, children: formatMessage({
2434
+ id: "content-manager.actions.unpublish.dialog.option.keep-draft",
2435
+ defaultMessage: "Keep draft"
2436
+ }) }),
2437
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Radio.Item, { checked: !shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.DISCARD, children: formatMessage({
2438
+ id: "content-manager.actions.unpublish.dialog.option.replace-draft",
2439
+ defaultMessage: "Replace draft"
2440
+ }) })
2441
+ ]
2442
+ }
2443
+ )
2444
+ ] }),
2445
+ onConfirm: async () => {
2446
+ if (!documentId && collectionType !== SINGLE_TYPES) {
2447
+ console.error(
2448
+ "You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
2449
+ );
2450
+ toggleNotification({
2451
+ message: formatMessage({
2452
+ id: "content-manager.actions.unpublish.error",
2453
+ defaultMessage: "An error occurred while trying to unpublish the document."
2454
+ }),
2455
+ type: "danger"
2456
+ });
2457
+ }
2458
+ await unpublish(
2459
+ {
2460
+ collectionType,
2461
+ model,
2462
+ documentId,
2463
+ params
2464
+ },
2465
+ !shouldKeepDraft
2466
+ );
2467
+ }
2468
+ } : void 0,
2469
+ variant: "danger",
2470
+ position: ["panel", "table-row"]
2396
2471
  };
2397
2472
  };
2398
- EditTheModelAction.type = "edit-the-model";
2399
- const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2400
- const navigate = reactRouterDom.useNavigate();
2473
+ UnpublishAction$1.type = "unpublish";
2474
+ const DiscardAction = ({
2475
+ activeTab,
2476
+ documentId,
2477
+ model,
2478
+ collectionType,
2479
+ document
2480
+ }) => {
2401
2481
  const { formatMessage } = reactIntl.useIntl();
2402
- const listViewPathMatch = reactRouterDom.useMatch(LIST_PATH);
2403
- const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2404
- const { delete: deleteAction } = useDocumentActions();
2405
- const { toggleNotification } = strapiAdmin.useNotification();
2406
- const setSubmitting = strapiAdmin.useForm("DeleteAction", (state) => state.setSubmitting);
2482
+ const { schema } = useDoc();
2483
+ const canUpdate = useDocumentRBAC("DiscardAction", ({ canUpdate: canUpdate2 }) => canUpdate2);
2484
+ const { discard } = useDocumentActions();
2485
+ const [{ query }] = strapiAdmin.useQueryParams();
2486
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
2487
+ if (!schema?.options?.draftAndPublish) {
2488
+ return null;
2489
+ }
2407
2490
  return {
2408
- disabled: !canDelete || !document,
2491
+ disabled: !canUpdate || activeTab === "published" || document?.status !== "modified",
2409
2492
  label: formatMessage({
2410
- id: "content-manager.actions.delete.label",
2411
- defaultMessage: "Delete document"
2493
+ id: "content-manager.actions.discard.label",
2494
+ defaultMessage: "Discard changes"
2412
2495
  }),
2413
- icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2496
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
2497
+ position: ["panel", "table-row"],
2498
+ variant: "danger",
2414
2499
  dialog: {
2415
2500
  type: "dialog",
2416
2501
  title: formatMessage({
@@ -2420,92 +2505,90 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2420
2505
  content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
2421
2506
  /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2422
2507
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2423
- id: "content-manager.actions.delete.dialog.body",
2508
+ id: "content-manager.actions.discard.dialog.body",
2424
2509
  defaultMessage: "Are you sure?"
2425
2510
  }) })
2426
2511
  ] }),
2427
2512
  onConfirm: async () => {
2428
- if (!listViewPathMatch) {
2429
- setSubmitting(true);
2430
- }
2431
- try {
2432
- if (!documentId && collectionType !== SINGLE_TYPES) {
2433
- console.error(
2434
- "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2435
- );
2436
- toggleNotification({
2437
- message: formatMessage({
2438
- id: "content-manager.actions.delete.error",
2439
- defaultMessage: "An error occurred while trying to delete the document."
2440
- }),
2441
- type: "danger"
2442
- });
2443
- return;
2444
- }
2445
- const res = await deleteAction({
2446
- documentId,
2447
- model,
2448
- collectionType,
2449
- params: {
2450
- locale: "*"
2451
- }
2452
- });
2453
- if (!("error" in res)) {
2454
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2455
- }
2456
- } finally {
2457
- if (!listViewPathMatch) {
2458
- setSubmitting(false);
2459
- }
2460
- }
2513
+ await discard({
2514
+ collectionType,
2515
+ model,
2516
+ documentId,
2517
+ params
2518
+ });
2519
+ }
2520
+ }
2521
+ };
2522
+ };
2523
+ DiscardAction.type = "discard";
2524
+ const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2525
+ const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2526
+ const RelativeTime = React__namespace.forwardRef(
2527
+ ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
2528
+ const { formatRelativeTime, formatDate, formatTime } = reactIntl.useIntl();
2529
+ const interval = dateFns.intervalToDuration({
2530
+ start: timestamp,
2531
+ end: Date.now()
2532
+ // see https://github.com/date-fns/date-fns/issues/2891 – No idea why it's all partial it returns it every time.
2533
+ });
2534
+ const unit = intervals.find((intervalUnit) => {
2535
+ return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2536
+ });
2537
+ const relativeTime = dateFns.isPast(timestamp) ? -interval[unit] : interval[unit];
2538
+ const customInterval = customIntervals.find(
2539
+ (custom) => interval[custom.unit] < custom.threshold
2540
+ );
2541
+ const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, { numeric: "auto" });
2542
+ return /* @__PURE__ */ jsxRuntime.jsx(
2543
+ "time",
2544
+ {
2545
+ ref: forwardedRef,
2546
+ dateTime: timestamp.toISOString(),
2547
+ role: "time",
2548
+ title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
2549
+ ...restProps,
2550
+ children: displayText
2461
2551
  }
2462
- },
2463
- variant: "danger",
2464
- position: ["header", "table-row"]
2465
- };
2552
+ );
2553
+ }
2554
+ );
2555
+ const getDisplayName = ({
2556
+ firstname,
2557
+ lastname,
2558
+ username,
2559
+ email
2560
+ } = {}) => {
2561
+ if (username) {
2562
+ return username;
2563
+ }
2564
+ if (firstname) {
2565
+ return `${firstname} ${lastname ?? ""}`.trim();
2566
+ }
2567
+ return email ?? "";
2466
2568
  };
2467
- DeleteAction$1.type = "delete";
2468
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2469
- const Panels = () => {
2470
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2471
- const [
2472
- {
2473
- query: { status }
2474
- }
2475
- ] = strapiAdmin.useQueryParams({
2476
- status: "draft"
2477
- });
2478
- const { model, id, document, meta, collectionType } = useDoc();
2479
- const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2480
- const props = {
2481
- activeTab: status,
2482
- model,
2483
- documentId: id,
2484
- document: isCloning ? void 0 : document,
2485
- meta: isCloning ? void 0 : meta,
2486
- collectionType
2487
- };
2488
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2489
- strapiAdmin.DescriptionComponentRenderer,
2490
- {
2491
- props,
2492
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2493
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2494
- }
2495
- ) });
2569
+ const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2570
+ const DocumentStatus = ({ status = "draft", ...restProps }) => {
2571
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2572
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2496
2573
  };
2497
- const ActionsPanel = () => {
2574
+ const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2498
2575
  const { formatMessage } = reactIntl.useIntl();
2499
- return {
2500
- title: formatMessage({
2501
- id: "content-manager.containers.edit.panels.default.title",
2502
- defaultMessage: "Document"
2503
- }),
2504
- content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2505
- };
2576
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2577
+ const title = isCreating ? formatMessage({
2578
+ id: "content-manager.containers.edit.title.new",
2579
+ defaultMessage: "Create an entry"
2580
+ }) : documentTitle;
2581
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2582
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.BackButton, {}),
2583
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2584
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2585
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2586
+ ] }),
2587
+ status ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2588
+ ] });
2506
2589
  };
2507
- ActionsPanel.type = "actions";
2508
- const ActionsPanelContent = () => {
2590
+ const HeaderToolbar = () => {
2591
+ const { formatMessage } = reactIntl.useIntl();
2509
2592
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2510
2593
  const [
2511
2594
  {
@@ -2513,355 +2596,432 @@ const ActionsPanelContent = () => {
2513
2596
  }
2514
2597
  ] = strapiAdmin.useQueryParams();
2515
2598
  const { model, id, document, meta, collectionType } = useDoc();
2516
- const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
2517
- const props = {
2518
- activeTab: status,
2519
- model,
2520
- documentId: id,
2521
- document: isCloning ? void 0 : document,
2522
- meta: isCloning ? void 0 : meta,
2523
- collectionType
2524
- };
2525
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
2599
+ const plugins = strapiAdmin.useStrapiApp("HeaderToolbar", (state) => state.plugins);
2600
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
2526
2601
  /* @__PURE__ */ jsxRuntime.jsx(
2527
2602
  strapiAdmin.DescriptionComponentRenderer,
2528
2603
  {
2529
- props,
2530
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2531
- children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
2604
+ props: {
2605
+ activeTab: status,
2606
+ model,
2607
+ documentId: id,
2608
+ document: isCloning ? void 0 : document,
2609
+ meta: isCloning ? void 0 : meta,
2610
+ collectionType
2611
+ },
2612
+ descriptions: plugins["content-manager"].apis.getHeaderActions(),
2613
+ children: (actions2) => {
2614
+ if (actions2.length > 0) {
2615
+ return /* @__PURE__ */ jsxRuntime.jsx(HeaderActions, { actions: actions2 });
2616
+ } else {
2617
+ return null;
2618
+ }
2619
+ }
2532
2620
  }
2533
2621
  ),
2534
- /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
2622
+ /* @__PURE__ */ jsxRuntime.jsx(
2623
+ strapiAdmin.DescriptionComponentRenderer,
2624
+ {
2625
+ props: {
2626
+ activeTab: status,
2627
+ model,
2628
+ documentId: id,
2629
+ document: isCloning ? void 0 : document,
2630
+ meta: isCloning ? void 0 : meta,
2631
+ collectionType
2632
+ },
2633
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
2634
+ children: (actions2) => {
2635
+ const headerActions = actions2.filter((action) => {
2636
+ const positions = Array.isArray(action.position) ? action.position : [action.position];
2637
+ return positions.includes("header");
2638
+ });
2639
+ return /* @__PURE__ */ jsxRuntime.jsx(
2640
+ DocumentActionsMenu,
2641
+ {
2642
+ actions: headerActions,
2643
+ label: formatMessage({
2644
+ id: "content-manager.containers.edit.header.more-actions",
2645
+ defaultMessage: "More actions"
2646
+ }),
2647
+ children: /* @__PURE__ */ jsxRuntime.jsx(Information, { activeTab: status })
2648
+ }
2649
+ );
2650
+ }
2651
+ }
2652
+ )
2535
2653
  ] });
2536
2654
  };
2537
- const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
2538
- return /* @__PURE__ */ jsxRuntime.jsxs(
2539
- designSystem.Flex,
2655
+ const Information = ({ activeTab }) => {
2656
+ const { formatMessage } = reactIntl.useIntl();
2657
+ const { document, meta } = useDoc();
2658
+ if (!document || !document.id) {
2659
+ return null;
2660
+ }
2661
+ const createAndUpdateDocument = activeTab === "draft" ? document : meta?.availableStatus.find((status) => status.publishedAt === null);
2662
+ const publishDocument = activeTab === "published" ? document : meta?.availableStatus.find((status) => status.publishedAt !== null);
2663
+ const creator = createAndUpdateDocument?.[CREATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[CREATED_BY_ATTRIBUTE_NAME]) : null;
2664
+ const updator = createAndUpdateDocument?.[UPDATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[UPDATED_BY_ATTRIBUTE_NAME]) : null;
2665
+ const information = [
2540
2666
  {
2541
- ref,
2542
- tag: "aside",
2543
- "aria-labelledby": "additional-information",
2544
- background: "neutral0",
2545
- borderColor: "neutral150",
2546
- hasRadius: true,
2547
- paddingBottom: 4,
2548
- paddingLeft: 4,
2549
- paddingRight: 4,
2550
- paddingTop: 4,
2551
- shadow: "tableShadow",
2552
- gap: 3,
2553
- direction: "column",
2554
- justifyContent: "stretch",
2555
- alignItems: "flex-start",
2556
- children: [
2557
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2558
- children
2559
- ]
2560
- }
2561
- );
2562
- });
2563
- const HOOKS = {
2564
- /**
2565
- * Hook that allows to mutate the displayed headers of the list view table
2566
- * @constant
2567
- * @type {string}
2568
- */
2569
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2570
- /**
2571
- * Hook that allows to mutate the CM's collection types links pre-set filters
2572
- * @constant
2573
- * @type {string}
2574
- */
2575
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2576
- /**
2577
- * Hook that allows to mutate the CM's edit view layout
2578
- * @constant
2579
- * @type {string}
2580
- */
2581
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2582
- /**
2583
- * Hook that allows to mutate the CM's single types links pre-set filters
2584
- * @constant
2585
- * @type {string}
2586
- */
2587
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2588
- };
2589
- const contentTypesApi = contentManagerApi.injectEndpoints({
2590
- endpoints: (builder) => ({
2591
- getContentTypeConfiguration: builder.query({
2592
- query: (uid) => ({
2593
- url: `/content-manager/content-types/${uid}/configuration`,
2594
- method: "GET"
2667
+ isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2668
+ label: formatMessage({
2669
+ id: "content-manager.containers.edit.information.last-published.label",
2670
+ defaultMessage: "Last published"
2595
2671
  }),
2596
- transformResponse: (response) => response.data,
2597
- providesTags: (_result, _error, uid) => [
2598
- { type: "ContentTypesConfiguration", id: uid },
2599
- { type: "ContentTypeSettings", id: "LIST" }
2600
- ]
2601
- }),
2602
- getAllContentTypeSettings: builder.query({
2603
- query: () => "/content-manager/content-types-settings",
2604
- transformResponse: (response) => response.data,
2605
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2606
- }),
2607
- updateContentTypeConfiguration: builder.mutation({
2608
- query: ({ uid, ...body }) => ({
2609
- url: `/content-manager/content-types/${uid}/configuration`,
2610
- method: "PUT",
2611
- data: body
2672
+ value: formatMessage(
2673
+ {
2674
+ id: "content-manager.containers.edit.information.last-published.value",
2675
+ defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2676
+ },
2677
+ {
2678
+ time: /* @__PURE__ */ jsxRuntime.jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
2679
+ isAnonymous: !publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME],
2680
+ author: publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME] ? getDisplayName(publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME]) : null
2681
+ }
2682
+ )
2683
+ },
2684
+ {
2685
+ isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2686
+ label: formatMessage({
2687
+ id: "content-manager.containers.edit.information.last-draft.label",
2688
+ defaultMessage: "Last draft"
2689
+ }),
2690
+ value: formatMessage(
2691
+ {
2692
+ id: "content-manager.containers.edit.information.last-draft.value",
2693
+ defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2694
+ },
2695
+ {
2696
+ time: /* @__PURE__ */ jsxRuntime.jsx(
2697
+ RelativeTime,
2698
+ {
2699
+ timestamp: new Date(createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME])
2700
+ }
2701
+ ),
2702
+ isAnonymous: !updator,
2703
+ author: updator
2704
+ }
2705
+ )
2706
+ },
2707
+ {
2708
+ isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2709
+ label: formatMessage({
2710
+ id: "content-manager.containers.edit.information.document.label",
2711
+ defaultMessage: "Document"
2612
2712
  }),
2613
- transformResponse: (response) => response.data,
2614
- invalidatesTags: (_result, _error, { uid }) => [
2615
- { type: "ContentTypesConfiguration", id: uid },
2616
- { type: "ContentTypeSettings", id: "LIST" },
2617
- // Is this necessary?
2618
- { type: "InitialData" }
2619
- ]
2620
- })
2621
- })
2622
- });
2623
- const {
2624
- useGetContentTypeConfigurationQuery,
2625
- useGetAllContentTypeSettingsQuery,
2626
- useUpdateContentTypeConfigurationMutation
2627
- } = contentTypesApi;
2628
- const checkIfAttributeIsDisplayable = (attribute) => {
2629
- const { type } = attribute;
2630
- if (type === "relation") {
2631
- return !attribute.relation.toLowerCase().includes("morph");
2632
- }
2633
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2634
- };
2635
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2636
- if (!mainFieldName) {
2637
- return void 0;
2638
- }
2639
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2640
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2641
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2713
+ value: formatMessage(
2714
+ {
2715
+ id: "content-manager.containers.edit.information.document.value",
2716
+ defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2717
+ },
2718
+ {
2719
+ time: /* @__PURE__ */ jsxRuntime.jsx(
2720
+ RelativeTime,
2721
+ {
2722
+ timestamp: new Date(createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME])
2723
+ }
2724
+ ),
2725
+ isAnonymous: !creator,
2726
+ author: creator
2727
+ }
2728
+ )
2729
+ }
2730
+ ].filter((info) => info.isDisplayed);
2731
+ return /* @__PURE__ */ jsxRuntime.jsx(
2732
+ designSystem.Flex,
2733
+ {
2734
+ borderWidth: "1px 0 0 0",
2735
+ borderStyle: "solid",
2736
+ borderColor: "neutral150",
2737
+ direction: "column",
2738
+ marginTop: 2,
2739
+ tag: "dl",
2740
+ padding: 5,
2741
+ gap: 3,
2742
+ alignItems: "flex-start",
2743
+ marginLeft: "-0.4rem",
2744
+ marginRight: "-0.4rem",
2745
+ width: "calc(100% + 8px)",
2746
+ children: information.map((info) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, direction: "column", alignItems: "flex-start", children: [
2747
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "dt", variant: "pi", fontWeight: "bold", children: info.label }),
2748
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "dd", variant: "pi", textColor: "neutral600", children: info.value })
2749
+ ] }, info.label))
2750
+ }
2642
2751
  );
2643
- return {
2644
- name: mainFieldName,
2645
- type: mainFieldType ?? "string"
2646
- };
2647
- };
2648
- const DEFAULT_SETTINGS = {
2649
- bulkable: false,
2650
- filterable: false,
2651
- searchable: false,
2652
- pagination: false,
2653
- defaultSortBy: "",
2654
- defaultSortOrder: "asc",
2655
- mainField: "id",
2656
- pageSize: 10
2657
2752
  };
2658
- const useDocumentLayout = (model) => {
2659
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2660
- const [{ query }] = strapiAdmin.useQueryParams();
2661
- const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2662
- const { toggleNotification } = strapiAdmin.useNotification();
2663
- const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
2664
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2665
- const {
2666
- data,
2667
- isLoading: isLoadingConfigs,
2668
- error,
2669
- isFetching: isFetchingConfigs
2670
- } = useGetContentTypeConfigurationQuery(model);
2671
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2672
- React__namespace.useEffect(() => {
2673
- if (error) {
2674
- toggleNotification({
2675
- type: "danger",
2676
- message: formatAPIError(error)
2677
- });
2753
+ const HeaderActions = ({ actions: actions2 }) => {
2754
+ const [dialogId, setDialogId] = React__namespace.useState(null);
2755
+ const handleClick = (action) => async (e) => {
2756
+ if (!("options" in action)) {
2757
+ const { onClick = () => false, dialog, id } = action;
2758
+ const muteDialog = await onClick(e);
2759
+ if (dialog && !muteDialog) {
2760
+ e.preventDefault();
2761
+ setDialogId(id);
2762
+ }
2678
2763
  }
2679
- }, [error, formatAPIError, toggleNotification]);
2680
- const editLayout = React__namespace.useMemo(
2681
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2682
- layout: [],
2683
- components: {},
2684
- metadatas: {},
2685
- options: {},
2686
- settings: DEFAULT_SETTINGS
2687
- },
2688
- [data, isLoading, schemas, schema, components]
2689
- );
2690
- const listLayout = React__namespace.useMemo(() => {
2691
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2692
- layout: [],
2693
- metadatas: {},
2694
- options: {},
2695
- settings: DEFAULT_SETTINGS
2696
- };
2697
- }, [data, isLoading, schemas, schema, components]);
2698
- const { layout: edit } = React__namespace.useMemo(
2699
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2700
- layout: editLayout,
2701
- query
2702
- }),
2703
- [editLayout, query, runHookWaterfall]
2704
- );
2705
- return {
2706
- error,
2707
- isLoading,
2708
- edit,
2709
- list: listLayout
2710
2764
  };
2711
- };
2712
- const useDocLayout = () => {
2713
- const { model } = useDoc();
2714
- return useDocumentLayout(model);
2715
- };
2716
- const formatEditLayout = (data, {
2717
- schemas,
2718
- schema,
2719
- components
2720
- }) => {
2721
- let currentPanelIndex = 0;
2722
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2723
- data.contentType.layouts.edit,
2724
- schema?.attributes,
2725
- data.contentType.metadatas,
2726
- { configurations: data.components, schemas: components },
2727
- schemas
2728
- ).reduce((panels, row) => {
2729
- if (row.some((field) => field.type === "dynamiczone")) {
2730
- panels.push([row]);
2731
- currentPanelIndex += 2;
2765
+ const handleClose = () => {
2766
+ setDialogId(null);
2767
+ };
2768
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, children: actions2.map((action) => {
2769
+ if (action.options) {
2770
+ return /* @__PURE__ */ jsxRuntime.jsx(
2771
+ designSystem.SingleSelect,
2772
+ {
2773
+ size: "S",
2774
+ onChange: action.onSelect,
2775
+ "aria-label": action.label,
2776
+ ...action,
2777
+ children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { ...option, children: label }, option.value))
2778
+ },
2779
+ action.id
2780
+ );
2732
2781
  } else {
2733
- if (!panels[currentPanelIndex]) {
2734
- panels.push([]);
2782
+ if (action.type === "icon") {
2783
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
2784
+ /* @__PURE__ */ jsxRuntime.jsx(
2785
+ designSystem.IconButton,
2786
+ {
2787
+ disabled: action.disabled,
2788
+ label: action.label,
2789
+ size: "S",
2790
+ onClick: handleClick(action),
2791
+ children: action.icon
2792
+ }
2793
+ ),
2794
+ action.dialog ? /* @__PURE__ */ jsxRuntime.jsx(
2795
+ HeaderActionDialog,
2796
+ {
2797
+ ...action.dialog,
2798
+ isOpen: dialogId === action.id,
2799
+ onClose: handleClose
2800
+ }
2801
+ ) : null
2802
+ ] }, action.id);
2735
2803
  }
2736
- panels[currentPanelIndex].push(row);
2737
2804
  }
2738
- return panels;
2739
- }, []);
2740
- const componentEditAttributes = Object.entries(data.components).reduce(
2741
- (acc, [uid, configuration]) => {
2742
- acc[uid] = {
2743
- layout: convertEditLayoutToFieldLayouts(
2744
- configuration.layouts.edit,
2745
- components[uid].attributes,
2746
- configuration.metadatas
2747
- ),
2748
- settings: {
2749
- ...configuration.settings,
2750
- icon: components[uid].info.icon,
2751
- displayName: components[uid].info.displayName
2752
- }
2753
- };
2754
- return acc;
2755
- },
2756
- {}
2757
- );
2758
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2759
- (acc, [attribute, metadata]) => {
2760
- return {
2761
- ...acc,
2762
- [attribute]: metadata.edit
2763
- };
2805
+ }) });
2806
+ };
2807
+ const HeaderActionDialog = ({
2808
+ onClose,
2809
+ onCancel,
2810
+ title,
2811
+ content: Content,
2812
+ isOpen
2813
+ }) => {
2814
+ const handleClose = async () => {
2815
+ if (onCancel) {
2816
+ await onCancel();
2817
+ }
2818
+ onClose();
2819
+ };
2820
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2821
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2822
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content
2823
+ ] }) });
2824
+ };
2825
+ const ConfigureTheViewAction = ({ collectionType, model }) => {
2826
+ const navigate = reactRouterDom.useNavigate();
2827
+ const { formatMessage } = reactIntl.useIntl();
2828
+ return {
2829
+ label: formatMessage({
2830
+ id: "app.links.configure-view",
2831
+ defaultMessage: "Configure the view"
2832
+ }),
2833
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ListPlus, {}),
2834
+ onClick: () => {
2835
+ navigate(`../${collectionType}/${model}/configurations/edit`);
2764
2836
  },
2765
- {}
2766
- );
2837
+ position: "header"
2838
+ };
2839
+ };
2840
+ ConfigureTheViewAction.type = "configure-the-view";
2841
+ const EditTheModelAction = ({ model }) => {
2842
+ const navigate = reactRouterDom.useNavigate();
2843
+ const { formatMessage } = reactIntl.useIntl();
2767
2844
  return {
2768
- layout: panelledEditAttributes,
2769
- components: componentEditAttributes,
2770
- metadatas: editMetadatas,
2771
- settings: {
2772
- ...data.contentType.settings,
2773
- displayName: schema?.info.displayName
2845
+ label: formatMessage({
2846
+ id: "content-manager.link-to-ctb",
2847
+ defaultMessage: "Edit the model"
2848
+ }),
2849
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {}),
2850
+ onClick: () => {
2851
+ navigate(`/plugins/content-type-builder/content-types/${model}`);
2774
2852
  },
2775
- options: {
2776
- ...schema?.options,
2777
- ...schema?.pluginOptions,
2778
- ...data.contentType.options
2779
- }
2853
+ position: "header"
2780
2854
  };
2781
2855
  };
2782
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2783
- return rows.map(
2784
- (row) => row.map((field) => {
2785
- const attribute = attributes[field.name];
2786
- if (!attribute) {
2787
- return null;
2856
+ EditTheModelAction.type = "edit-the-model";
2857
+ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2858
+ const navigate = reactRouterDom.useNavigate();
2859
+ const { formatMessage } = reactIntl.useIntl();
2860
+ const listViewPathMatch = reactRouterDom.useMatch(LIST_PATH);
2861
+ const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2862
+ const { delete: deleteAction } = useDocumentActions();
2863
+ const { toggleNotification } = strapiAdmin.useNotification();
2864
+ const setSubmitting = strapiAdmin.useForm("DeleteAction", (state) => state.setSubmitting);
2865
+ const isLocalized = document?.locale != null;
2866
+ return {
2867
+ disabled: !canDelete || !document,
2868
+ label: formatMessage(
2869
+ {
2870
+ id: "content-manager.actions.delete.label",
2871
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2872
+ },
2873
+ { isLocalized }
2874
+ ),
2875
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2876
+ dialog: {
2877
+ type: "dialog",
2878
+ title: formatMessage({
2879
+ id: "app.components.ConfirmDialog.title",
2880
+ defaultMessage: "Confirmation"
2881
+ }),
2882
+ content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
2883
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2884
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2885
+ id: "content-manager.actions.delete.dialog.body",
2886
+ defaultMessage: "Are you sure?"
2887
+ }) })
2888
+ ] }),
2889
+ onConfirm: async () => {
2890
+ if (!listViewPathMatch) {
2891
+ setSubmitting(true);
2892
+ }
2893
+ try {
2894
+ if (!documentId && collectionType !== SINGLE_TYPES) {
2895
+ console.error(
2896
+ "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2897
+ );
2898
+ toggleNotification({
2899
+ message: formatMessage({
2900
+ id: "content-manager.actions.delete.error",
2901
+ defaultMessage: "An error occurred while trying to delete the document."
2902
+ }),
2903
+ type: "danger"
2904
+ });
2905
+ return;
2906
+ }
2907
+ const res = await deleteAction({
2908
+ documentId,
2909
+ model,
2910
+ collectionType,
2911
+ params: {
2912
+ locale: "*"
2913
+ }
2914
+ });
2915
+ if (!("error" in res)) {
2916
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2917
+ }
2918
+ } finally {
2919
+ if (!listViewPathMatch) {
2920
+ setSubmitting(false);
2921
+ }
2922
+ }
2788
2923
  }
2789
- const { edit: metadata } = metadatas[field.name];
2790
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2791
- return {
2792
- attribute,
2793
- disabled: !metadata.editable,
2794
- hint: metadata.description,
2795
- label: metadata.label ?? "",
2796
- name: field.name,
2797
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2798
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2799
- schemas,
2800
- components: components?.schemas ?? {}
2801
- }),
2802
- placeholder: metadata.placeholder ?? "",
2803
- required: attribute.required ?? false,
2804
- size: field.size,
2805
- unique: "unique" in attribute ? attribute.unique : false,
2806
- visible: metadata.visible ?? true,
2807
- type: attribute.type
2808
- };
2809
- }).filter((field) => field !== null)
2810
- );
2811
- };
2812
- const formatListLayout = (data, {
2813
- schemas,
2814
- schema,
2815
- components
2816
- }) => {
2817
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2818
- (acc, [attribute, metadata]) => {
2819
- return {
2820
- ...acc,
2821
- [attribute]: metadata.list
2822
- };
2823
2924
  },
2824
- {}
2825
- );
2826
- const listAttributes = convertListLayoutToFieldLayouts(
2827
- data.contentType.layouts.list,
2828
- schema?.attributes,
2829
- listMetadatas,
2830
- { configurations: data.components, schemas: components },
2831
- schemas
2832
- );
2833
- return {
2834
- layout: listAttributes,
2835
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2836
- metadatas: listMetadatas,
2837
- options: {
2838
- ...schema?.options,
2839
- ...schema?.pluginOptions,
2840
- ...data.contentType.options
2925
+ variant: "danger",
2926
+ position: ["header", "table-row"]
2927
+ };
2928
+ };
2929
+ DeleteAction$1.type = "delete";
2930
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2931
+ const Panels = () => {
2932
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2933
+ const [
2934
+ {
2935
+ query: { status }
2936
+ }
2937
+ ] = strapiAdmin.useQueryParams({
2938
+ status: "draft"
2939
+ });
2940
+ const { model, id, document, meta, collectionType } = useDoc();
2941
+ const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2942
+ const props = {
2943
+ activeTab: status,
2944
+ model,
2945
+ documentId: id,
2946
+ document: isCloning ? void 0 : document,
2947
+ meta: isCloning ? void 0 : meta,
2948
+ collectionType
2949
+ };
2950
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2951
+ strapiAdmin.DescriptionComponentRenderer,
2952
+ {
2953
+ props,
2954
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2955
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2841
2956
  }
2957
+ ) });
2958
+ };
2959
+ const ActionsPanel = () => {
2960
+ const { formatMessage } = reactIntl.useIntl();
2961
+ return {
2962
+ title: formatMessage({
2963
+ id: "content-manager.containers.edit.panels.default.title",
2964
+ defaultMessage: "Entry"
2965
+ }),
2966
+ content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2842
2967
  };
2843
2968
  };
2844
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2845
- return columns.map((name) => {
2846
- const attribute = attributes[name];
2847
- if (!attribute) {
2848
- return null;
2969
+ ActionsPanel.type = "actions";
2970
+ const ActionsPanelContent = () => {
2971
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2972
+ const [
2973
+ {
2974
+ query: { status = "draft" }
2849
2975
  }
2850
- const metadata = metadatas[name];
2851
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2852
- return {
2853
- attribute,
2854
- label: metadata.label ?? "",
2855
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2856
- schemas,
2857
- components: components?.schemas ?? {}
2858
- }),
2859
- name,
2860
- searchable: metadata.searchable ?? true,
2861
- sortable: metadata.sortable ?? true
2862
- };
2863
- }).filter((field) => field !== null);
2976
+ ] = strapiAdmin.useQueryParams();
2977
+ const { model, id, document, meta, collectionType } = useDoc();
2978
+ const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
2979
+ const props = {
2980
+ activeTab: status,
2981
+ model,
2982
+ documentId: id,
2983
+ document: isCloning ? void 0 : document,
2984
+ meta: isCloning ? void 0 : meta,
2985
+ collectionType
2986
+ };
2987
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
2988
+ /* @__PURE__ */ jsxRuntime.jsx(
2989
+ strapiAdmin.DescriptionComponentRenderer,
2990
+ {
2991
+ props,
2992
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
2993
+ children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
2994
+ }
2995
+ ),
2996
+ /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
2997
+ ] });
2864
2998
  };
2999
+ const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
3000
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3001
+ designSystem.Flex,
3002
+ {
3003
+ ref,
3004
+ tag: "aside",
3005
+ "aria-labelledby": "additional-information",
3006
+ background: "neutral0",
3007
+ borderColor: "neutral150",
3008
+ hasRadius: true,
3009
+ paddingBottom: 4,
3010
+ paddingLeft: 4,
3011
+ paddingRight: 4,
3012
+ paddingTop: 4,
3013
+ shadow: "tableShadow",
3014
+ gap: 3,
3015
+ direction: "column",
3016
+ justifyContent: "stretch",
3017
+ alignItems: "flex-start",
3018
+ children: [
3019
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
3020
+ children
3021
+ ]
3022
+ }
3023
+ );
3024
+ });
2865
3025
  const ConfirmBulkActionDialog = ({
2866
3026
  onToggleDialog,
2867
3027
  isOpen = false,
@@ -2900,6 +3060,7 @@ const ConfirmDialogPublishAll = ({
2900
3060
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler(getTranslation);
2901
3061
  const { model, schema } = useDoc();
2902
3062
  const [{ query }] = strapiAdmin.useQueryParams();
3063
+ const enableDraftRelationsCount = false;
2903
3064
  const {
2904
3065
  data: countDraftRelations = 0,
2905
3066
  isLoading,
@@ -2911,7 +3072,7 @@ const ConfirmDialogPublishAll = ({
2911
3072
  locale: query?.plugins?.i18n?.locale
2912
3073
  },
2913
3074
  {
2914
- skip: selectedEntries.length === 0
3075
+ skip: !enableDraftRelationsCount
2915
3076
  }
2916
3077
  );
2917
3078
  React__namespace.useEffect(() => {
@@ -3096,7 +3257,7 @@ const SelectedEntriesTableContent = ({
3096
3257
  status: row.status
3097
3258
  }
3098
3259
  ) }),
3099
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
3260
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(
3100
3261
  designSystem.IconButton,
3101
3262
  {
3102
3263
  tag: reactRouterDom.Link,
@@ -3119,9 +3280,10 @@ const SelectedEntriesTableContent = ({
3119
3280
  ),
3120
3281
  target: "_blank",
3121
3282
  marginLeft: "auto",
3122
- children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {})
3283
+ variant: "ghost",
3284
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, { width: "1.6rem", height: "1.6rem" })
3123
3285
  }
3124
- ) })
3286
+ ) }) })
3125
3287
  ] }, row.id)) })
3126
3288
  ] });
3127
3289
  };
@@ -3158,7 +3320,13 @@ const SelectedEntriesModalContent = ({
3158
3320
  );
3159
3321
  const { rows, validationErrors } = React__namespace.useMemo(() => {
3160
3322
  if (data.length > 0 && schema) {
3161
- const validate = createYupSchema(schema.attributes, components);
3323
+ const validate = createYupSchema(
3324
+ schema.attributes,
3325
+ components,
3326
+ // Since this is the "Publish" action, the validation
3327
+ // schema must enforce the rules for published entities
3328
+ { status: "published" }
3329
+ );
3162
3330
  const validationErrors2 = {};
3163
3331
  const rows2 = data.map((entry) => {
3164
3332
  try {
@@ -3508,7 +3676,7 @@ const TableActions = ({ document }) => {
3508
3676
  strapiAdmin.DescriptionComponentRenderer,
3509
3677
  {
3510
3678
  props,
3511
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3679
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3512
3680
  children: (actions2) => {
3513
3681
  const tableRowActions = actions2.filter((action) => {
3514
3682
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3619,7 +3787,7 @@ const CloneAction = ({ model, documentId }) => {
3619
3787
  }),
3620
3788
  content: /* @__PURE__ */ jsxRuntime.jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3621
3789
  footer: ({ onClose }) => {
3622
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
3790
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
3623
3791
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3624
3792
  id: "cancel",
3625
3793
  defaultMessage: "Cancel"
@@ -3850,7 +4018,7 @@ const index = {
3850
4018
  app.router.addRoute({
3851
4019
  path: "content-manager/*",
3852
4020
  lazy: async () => {
3853
- const { Layout } = await Promise.resolve().then(() => require("./layout-TPqF2oJ5.js"));
4021
+ const { Layout } = await Promise.resolve().then(() => require("./layout-UNWstw_s.js"));
3854
4022
  return {
3855
4023
  Component: Layout
3856
4024
  };
@@ -3867,7 +4035,7 @@ const index = {
3867
4035
  async registerTrads({ locales }) {
3868
4036
  const importedTrads = await Promise.all(
3869
4037
  locales.map((locale) => {
3870
- 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 }) => {
4038
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => Promise.resolve().then(() => require("./ar-BUUWXIYu.js")), "./translations/ca.json": () => Promise.resolve().then(() => require("./ca-Cmk45QO6.js")), "./translations/cs.json": () => Promise.resolve().then(() => require("./cs-CkJy6B2v.js")), "./translations/de.json": () => Promise.resolve().then(() => require("./de-CCEmbAah.js")), "./translations/en.json": () => Promise.resolve().then(() => require("./en-BlhnxQfj.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-EUonQTon.js")), "./translations/eu.json": () => Promise.resolve().then(() => require("./eu-VDH-3ovk.js")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-B7kGGg3E.js")), "./translations/gu.json": () => Promise.resolve().then(() => require("./gu-BRmF601H.js")), "./translations/hi.json": () => Promise.resolve().then(() => require("./hi-CCJBptSq.js")), "./translations/hu.json": () => Promise.resolve().then(() => require("./hu-sNV_yLYy.js")), "./translations/id.json": () => Promise.resolve().then(() => require("./id-B5Ser98A.js")), "./translations/it.json": () => Promise.resolve().then(() => require("./it-DkBIs7vD.js")), "./translations/ja.json": () => Promise.resolve().then(() => require("./ja-CcFe8diO.js")), "./translations/ko.json": () => Promise.resolve().then(() => require("./ko-woFZPmLk.js")), "./translations/ml.json": () => Promise.resolve().then(() => require("./ml-C2W8N8k1.js")), "./translations/ms.json": () => Promise.resolve().then(() => require("./ms-BuFotyP_.js")), "./translations/nl.json": () => Promise.resolve().then(() => require("./nl-bbEOHChV.js")), "./translations/pl.json": () => Promise.resolve().then(() => require("./pl-uzwG-hk7.js")), "./translations/pt-BR.json": () => Promise.resolve().then(() => require("./pt-BR-BiOz37D9.js")), "./translations/pt.json": () => Promise.resolve().then(() => require("./pt-CeXQuq50.js")), "./translations/ru.json": () => Promise.resolve().then(() => require("./ru-BT3ybNny.js")), "./translations/sa.json": () => Promise.resolve().then(() => require("./sa-CcvkYInH.js")), "./translations/sk.json": () => Promise.resolve().then(() => require("./sk-CvY09Xjv.js")), "./translations/sv.json": () => Promise.resolve().then(() => require("./sv-MYDuzgvT.js")), "./translations/th.json": () => Promise.resolve().then(() => require("./th-D9_GfAjc.js")), "./translations/tr.json": () => Promise.resolve().then(() => require("./tr-D9UH-O_R.js")), "./translations/uk.json": () => Promise.resolve().then(() => require("./uk-C8EiqJY7.js")), "./translations/vi.json": () => Promise.resolve().then(() => require("./vi-CJlYDheJ.js")), "./translations/zh-Hans.json": () => Promise.resolve().then(() => require("./zh-Hans-9kOncHGw.js")), "./translations/zh.json": () => Promise.resolve().then(() => require("./zh-CQQfszqR.js")) }), `./translations/${locale}.json`).then(({ default: data }) => {
3871
4039
  return {
3872
4040
  data: prefixPluginTranslations(data, PLUGIN_ID),
3873
4041
  locale
@@ -3885,6 +4053,7 @@ const index = {
3885
4053
  };
3886
4054
  exports.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD;
3887
4055
  exports.BulkActionsRenderer = BulkActionsRenderer;
4056
+ exports.CLONE_PATH = CLONE_PATH;
3888
4057
  exports.COLLECTION_TYPES = COLLECTION_TYPES;
3889
4058
  exports.CREATOR_FIELDS = CREATOR_FIELDS;
3890
4059
  exports.DEFAULT_SETTINGS = DEFAULT_SETTINGS;
@@ -3912,6 +4081,7 @@ exports.getMainField = getMainField;
3912
4081
  exports.getTranslation = getTranslation;
3913
4082
  exports.index = index;
3914
4083
  exports.setInitialData = setInitialData;
4084
+ exports.useContentManagerContext = useContentManagerContext;
3915
4085
  exports.useContentTypeSchema = useContentTypeSchema;
3916
4086
  exports.useDoc = useDoc;
3917
4087
  exports.useDocLayout = useDocLayout;
@@ -3924,4 +4094,4 @@ exports.useGetAllDocumentsQuery = useGetAllDocumentsQuery;
3924
4094
  exports.useGetContentTypeConfigurationQuery = useGetContentTypeConfigurationQuery;
3925
4095
  exports.useGetInitialDataQuery = useGetInitialDataQuery;
3926
4096
  exports.useUpdateContentTypeConfigurationMutation = useUpdateContentTypeConfigurationMutation;
3927
- //# sourceMappingURL=index-DyvUPg1a.js.map
4097
+ //# sourceMappingURL=index-ovJRE1FM.js.map