@strapi/content-manager 0.0.0-experimental.f31889311d753b5f7d95198ae84d8fce1d156cd6 → 0.0.0-experimental.f74ae50eea1ce95176f088dba837e95b60fa2a4d

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