@strapi/content-manager 0.0.0-experimental.5e04dee5c96cd68273df3b3cadf4635e19dc2afc → 0.0.0-experimental.5f6fc3bc1a7d1b7c5fe926a37d4869f00e812287

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 (109) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-B3yDbeU1.mjs → ComponentConfigurationPage-DJ5voqEK.mjs} +3 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-B3yDbeU1.mjs.map → ComponentConfigurationPage-DJ5voqEK.mjs.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-KXSuLnQD.js → ComponentConfigurationPage-_6osrv39.js} +3 -3
  4. package/dist/_chunks/{ComponentConfigurationPage-KXSuLnQD.js.map → ComponentConfigurationPage-_6osrv39.js.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-D7PrLO8j.mjs → EditConfigurationPage-CZofxSLy.mjs} +3 -3
  6. package/dist/_chunks/{EditConfigurationPage-D7PrLO8j.mjs.map → EditConfigurationPage-CZofxSLy.mjs.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-BQ17--5R.js → EditConfigurationPage-ZN3s568V.js} +3 -3
  8. package/dist/_chunks/{EditConfigurationPage-BQ17--5R.js.map → EditConfigurationPage-ZN3s568V.js.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-BgjdnGz2.js → EditViewPage-Co2IKQZH.js} +4 -4
  10. package/dist/_chunks/EditViewPage-Co2IKQZH.js.map +1 -0
  11. package/dist/_chunks/{EditViewPage-B7VgwJaG.mjs → EditViewPage-HYljoEY7.mjs} +4 -4
  12. package/dist/_chunks/EditViewPage-HYljoEY7.mjs.map +1 -0
  13. package/dist/_chunks/{Field-tHCw4lGA.mjs → Field-BOPUMZ1u.mjs} +31 -62
  14. package/dist/_chunks/Field-BOPUMZ1u.mjs.map +1 -0
  15. package/dist/_chunks/{Field-CdK7ZLmv.js → Field-G9CkFUtP.js} +34 -65
  16. package/dist/_chunks/Field-G9CkFUtP.js.map +1 -0
  17. package/dist/_chunks/{Form-BJxdTv3Q.mjs → Form-CDwNp7pU.mjs} +15 -7
  18. package/dist/_chunks/Form-CDwNp7pU.mjs.map +1 -0
  19. package/dist/_chunks/{Form-C_0KTVvV.js → Form-crsbkGxI.js} +15 -7
  20. package/dist/_chunks/Form-crsbkGxI.js.map +1 -0
  21. package/dist/_chunks/{History-DR2txJLE.mjs → History-BDZrgfZ3.mjs} +4 -4
  22. package/dist/_chunks/{History-DR2txJLE.mjs.map → History-BDZrgfZ3.mjs.map} +1 -1
  23. package/dist/_chunks/{History-nuEzM5qm.js → History-CWcM9HnW.js} +4 -4
  24. package/dist/_chunks/{History-nuEzM5qm.js.map → History-CWcM9HnW.js.map} +1 -1
  25. package/dist/_chunks/{ListConfigurationPage-voFVtXu6.mjs → ListConfigurationPage-BZ3ScUna.mjs} +2 -2
  26. package/dist/_chunks/{ListConfigurationPage-voFVtXu6.mjs.map → ListConfigurationPage-BZ3ScUna.mjs.map} +1 -1
  27. package/dist/_chunks/{ListConfigurationPage-CnB86Psm.js → ListConfigurationPage-DGzoQD_I.js} +2 -2
  28. package/dist/_chunks/{ListConfigurationPage-CnB86Psm.js.map → ListConfigurationPage-DGzoQD_I.js.map} +1 -1
  29. package/dist/_chunks/{ListViewPage-SXIXm-RM.js → ListViewPage-BBAC9aPu.js} +42 -38
  30. package/dist/_chunks/ListViewPage-BBAC9aPu.js.map +1 -0
  31. package/dist/_chunks/{ListViewPage-B_GaWgRH.mjs → ListViewPage-CsX7tWx-.mjs} +39 -35
  32. package/dist/_chunks/ListViewPage-CsX7tWx-.mjs.map +1 -0
  33. package/dist/_chunks/{NoContentTypePage-BzsQ3hLZ.js → NoContentTypePage-CwVDx_YC.js} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-BzsQ3hLZ.js.map → NoContentTypePage-CwVDx_YC.js.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-CYiGpsbj.mjs → NoContentTypePage-LClTUPWs.mjs} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-CYiGpsbj.mjs.map → NoContentTypePage-LClTUPWs.mjs.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-IGkId4C5.js → NoPermissionsPage-D2iWw-sn.js} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-IGkId4C5.js.map → NoPermissionsPage-D2iWw-sn.js.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-B5baIHal.mjs → NoPermissionsPage-S4Re3FwO.mjs} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-B5baIHal.mjs.map → NoPermissionsPage-S4Re3FwO.mjs.map} +1 -1
  41. package/dist/_chunks/{Relations-CIYDdKU-.mjs → Relations-Dmv0Tpe5.mjs} +3 -3
  42. package/dist/_chunks/{Relations-CIYDdKU-.mjs.map → Relations-Dmv0Tpe5.mjs.map} +1 -1
  43. package/dist/_chunks/{Relations-Dhuurpx2.js → Relations-jwuTFGOV.js} +3 -3
  44. package/dist/_chunks/{Relations-Dhuurpx2.js.map → Relations-jwuTFGOV.js.map} +1 -1
  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-C9TJPyni.mjs → index-BmUAydCA.mjs} +1032 -914
  50. package/dist/_chunks/index-BmUAydCA.mjs.map +1 -0
  51. package/dist/_chunks/{index-CdT0kHZ8.js → index-CBX6KyXv.js} +1012 -894
  52. package/dist/_chunks/index-CBX6KyXv.js.map +1 -0
  53. package/dist/_chunks/{layout-BNqvLR_b.mjs → layout-ClP-DC72.mjs} +5 -4
  54. package/dist/_chunks/{layout-BNqvLR_b.mjs.map → layout-ClP-DC72.mjs.map} +1 -1
  55. package/dist/_chunks/{layout-C6dxWYT7.js → layout-CxxkX9jY.js} +5 -4
  56. package/dist/_chunks/{layout-C6dxWYT7.js.map → layout-CxxkX9jY.js.map} +1 -1
  57. package/dist/_chunks/{relations-DtFaDnP1.js → relations-DIjTADIu.js} +2 -2
  58. package/dist/_chunks/{relations-DtFaDnP1.js.map → relations-DIjTADIu.js.map} +1 -1
  59. package/dist/_chunks/{relations-CkKqKw65.mjs → relations-op89RClB.mjs} +2 -2
  60. package/dist/_chunks/{relations-CkKqKw65.mjs.map → relations-op89RClB.mjs.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 +30 -1
  71. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +0 -32
  72. package/dist/admin/src/pages/EditView/components/Header.d.ts +10 -11
  73. package/dist/admin/src/services/api.d.ts +1 -1
  74. package/dist/admin/src/services/components.d.ts +2 -2
  75. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  76. package/dist/admin/src/services/documents.d.ts +19 -17
  77. package/dist/admin/src/services/init.d.ts +1 -1
  78. package/dist/admin/src/services/relations.d.ts +2 -2
  79. package/dist/admin/src/services/uid.d.ts +3 -3
  80. package/dist/admin/src/utils/validation.d.ts +4 -1
  81. package/dist/server/index.js +50 -25
  82. package/dist/server/index.js.map +1 -1
  83. package/dist/server/index.mjs +50 -25
  84. package/dist/server/index.mjs.map +1 -1
  85. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  86. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  87. package/dist/server/src/history/services/history.d.ts.map +1 -1
  88. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  89. package/dist/server/src/history/services/utils.d.ts +1 -0
  90. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  91. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  92. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  93. package/dist/shared/contracts/collection-types.d.ts +3 -1
  94. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  95. package/package.json +11 -11
  96. package/dist/_chunks/EditViewPage-B7VgwJaG.mjs.map +0 -1
  97. package/dist/_chunks/EditViewPage-BgjdnGz2.js.map +0 -1
  98. package/dist/_chunks/Field-CdK7ZLmv.js.map +0 -1
  99. package/dist/_chunks/Field-tHCw4lGA.mjs.map +0 -1
  100. package/dist/_chunks/Form-BJxdTv3Q.mjs.map +0 -1
  101. package/dist/_chunks/Form-C_0KTVvV.js.map +0 -1
  102. package/dist/_chunks/ListViewPage-B_GaWgRH.mjs.map +0 -1
  103. package/dist/_chunks/ListViewPage-SXIXm-RM.js.map +0 -1
  104. package/dist/_chunks/index-C9TJPyni.mjs.map +0 -1
  105. package/dist/_chunks/index-CdT0kHZ8.js.map +0 -1
  106. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  107. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  108. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  109. 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
  }),
@@ -392,7 +398,8 @@ const documentApi = contentManagerApi.injectEndpoints({
392
398
  type: "Document",
393
399
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
394
400
  },
395
- "Relations"
401
+ "Relations",
402
+ { type: "UidAvailability", id: model }
396
403
  ];
397
404
  },
398
405
  async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
@@ -475,7 +482,7 @@ const buildValidParams = (query) => {
475
482
  const isBaseQueryError = (error) => {
476
483
  return error.name !== void 0;
477
484
  };
478
- const createYupSchema = (attributes = {}, components = {}) => {
485
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
479
486
  const createModelSchema = (attributes2) => yup__namespace.object().shape(
480
487
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
481
488
  if (DOCUMENT_META_FIELDS.includes(name)) {
@@ -488,7 +495,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
488
495
  addMinValidation,
489
496
  addMaxValidation,
490
497
  addRegexValidation
491
- ].map((fn) => fn(attribute));
498
+ ].map((fn) => fn(attribute, options));
492
499
  const transformSchema = pipe__default.default(...validations);
493
500
  switch (attribute.type) {
494
501
  case "component": {
@@ -615,13 +622,7 @@ const createAttributeSchema = (attribute) => {
615
622
  return yup__namespace.mixed();
616
623
  }
617
624
  };
618
- const addRequiredValidation = (attribute) => (schema) => {
619
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
620
- return schema.min(1, strapiAdmin.translatedErrors.required);
621
- }
622
- if (attribute.required && attribute.type !== "relation") {
623
- return schema.required(strapiAdmin.translatedErrors.required);
624
- }
625
+ const nullableSchema = (schema) => {
625
626
  return schema?.nullable ? schema.nullable() : (
626
627
  // In some cases '.nullable' will not be available on the schema.
627
628
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -629,7 +630,22 @@ const addRequiredValidation = (attribute) => (schema) => {
629
630
  schema
630
631
  );
631
632
  };
632
- 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
+ }
633
649
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
634
650
  return schema.min(attribute.minLength, {
635
651
  ...strapiAdmin.translatedErrors.minLength,
@@ -651,11 +667,11 @@ const addMaxLengthValidation = (attribute) => (schema) => {
651
667
  }
652
668
  return schema;
653
669
  };
654
- const addMinValidation = (attribute) => (schema) => {
670
+ const addMinValidation = (attribute, options) => (schema) => {
655
671
  if ("min" in attribute) {
656
672
  const min = toInteger(attribute.min);
657
673
  if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
658
- if (!attribute.required && "test" in schema && min) {
674
+ if (options.status !== "draft" && !attribute.required && "test" in schema && min) {
659
675
  return schema.test(
660
676
  "custom-min",
661
677
  {
@@ -794,19 +810,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
794
810
  }, {});
795
811
  return componentsByKey;
796
812
  };
797
- 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);
798
912
  const { toggleNotification } = strapiAdmin.useNotification();
799
913
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
914
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
800
915
  const {
801
- currentData: data,
802
- isLoading: isLoadingDocument,
803
- isFetching: isFetchingDocument,
804
- error
805
- } = useGetDocumentQuery(args, {
806
- ...opts,
807
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
808
- });
809
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
916
+ data,
917
+ isLoading: isLoadingConfigs,
918
+ error,
919
+ isFetching: isFetchingConfigs
920
+ } = useGetContentTypeConfigurationQuery(model);
921
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
810
922
  React__namespace.useEffect(() => {
811
923
  if (error) {
812
924
  toggleNotification({
@@ -814,395 +926,435 @@ const useDocument = (args, opts) => {
814
926
  message: formatAPIError(error)
815
927
  });
816
928
  }
817
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
818
- const validationSchema = React__namespace.useMemo(() => {
819
- if (!schema) {
820
- return null;
821
- }
822
- return createYupSchema(schema.attributes, components);
823
- }, [schema, components]);
824
- const validate = React__namespace.useCallback(
825
- (document) => {
826
- if (!validationSchema) {
827
- throw new Error(
828
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
829
- );
830
- }
831
- try {
832
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
833
- return null;
834
- } catch (error2) {
835
- if (error2 instanceof yup.ValidationError) {
836
- return strapiAdmin.getYupValidationErrors(error2);
837
- }
838
- throw error2;
839
- }
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
840
937
  },
841
- [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]
842
954
  );
843
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
844
955
  return {
845
- components,
846
- document: data?.data,
847
- meta: data?.meta,
956
+ error,
848
957
  isLoading,
849
- schema,
850
- validate
851
- };
852
- };
853
- const useDoc = () => {
854
- const { id, slug, collectionType, origin } = reactRouterDom.useParams();
855
- const [{ query }] = strapiAdmin.useQueryParams();
856
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
857
- if (!collectionType) {
858
- throw new Error("Could not find collectionType in url params");
859
- }
860
- if (!slug) {
861
- throw new Error("Could not find model in url params");
862
- }
863
- return {
864
- collectionType,
865
- model: slug,
866
- id: origin || id === "create" ? void 0 : id,
867
- ...useDocument(
868
- { documentId: origin || id, model: slug, collectionType, params },
869
- {
870
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
871
- }
872
- )
958
+ edit,
959
+ list: listLayout
873
960
  };
874
961
  };
875
- const prefixPluginTranslations = (trad, pluginId) => {
876
- if (!pluginId) {
877
- throw new TypeError("pluginId can't be empty");
878
- }
879
- return Object.keys(trad).reduce((acc, current) => {
880
- acc[`${pluginId}.${current}`] = trad[current];
881
- return acc;
882
- }, {});
883
- };
884
- const getTranslation = (id) => `content-manager.${id}`;
885
- const DEFAULT_UNEXPECTED_ERROR_MSG = {
886
- id: "notification.error",
887
- defaultMessage: "An error occurred, please try again"
962
+ const useDocLayout = () => {
963
+ const { model } = useDoc();
964
+ return useDocumentLayout(model);
888
965
  };
889
- const useDocumentActions = () => {
890
- const { toggleNotification } = strapiAdmin.useNotification();
891
- const { formatMessage } = reactIntl.useIntl();
892
- const { trackUsage } = strapiAdmin.useTracking();
893
- const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
894
- const [deleteDocument] = useDeleteDocumentMutation();
895
- const _delete = React__namespace.useCallback(
896
- async ({ collectionType, model, documentId, params }, trackerProperty) => {
897
- try {
898
- trackUsage("willDeleteEntry", trackerProperty);
899
- const res = await deleteDocument({
900
- collectionType,
901
- model,
902
- documentId,
903
- params
904
- });
905
- if ("error" in res) {
906
- toggleNotification({
907
- type: "danger",
908
- message: formatAPIError(res.error)
909
- });
910
- return { error: res.error };
911
- }
912
- toggleNotification({
913
- type: "success",
914
- message: formatMessage({
915
- id: getTranslation("success.record.delete"),
916
- defaultMessage: "Deleted document"
917
- })
918
- });
919
- trackUsage("didDeleteEntry", trackerProperty);
920
- return res.data;
921
- } catch (err) {
922
- toggleNotification({
923
- type: "danger",
924
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
925
- });
926
- trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
927
- throw err;
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([]);
928
985
  }
929
- },
930
- [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
931
- );
932
- const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
933
- const deleteMany = React__namespace.useCallback(
934
- async ({ model, documentIds, params }) => {
935
- try {
936
- trackUsage("willBulkDeleteEntries");
937
- const res = await deleteManyDocuments({
938
- model,
939
- documentIds,
940
- params
941
- });
942
- if ("error" in res) {
943
- toggleNotification({
944
- type: "danger",
945
- message: formatAPIError(res.error)
946
- });
947
- return { error: res.error };
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
948
1003
  }
949
- toggleNotification({
950
- type: "success",
951
- title: formatMessage({
952
- id: getTranslation("success.records.delete"),
953
- defaultMessage: "Successfully deleted."
954
- }),
955
- message: ""
956
- });
957
- trackUsage("didBulkDeleteEntries");
958
- return res.data;
959
- } catch (err) {
960
- toggleNotification({
961
- type: "danger",
962
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
963
- });
964
- trackUsage("didNotBulkDeleteEntries");
965
- throw err;
966
- }
1004
+ };
1005
+ return acc;
967
1006
  },
968
- [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1007
+ {}
969
1008
  );
970
- const [discardDocument] = useDiscardDocumentMutation();
971
- const discard = React__namespace.useCallback(
972
- async ({ collectionType, model, documentId, params }) => {
973
- try {
974
- const res = await discardDocument({
975
- collectionType,
976
- model,
977
- documentId,
978
- params
979
- });
980
- if ("error" in res) {
981
- toggleNotification({
982
- type: "danger",
983
- message: formatAPIError(res.error)
984
- });
985
- return { error: res.error };
986
- }
987
- toggleNotification({
988
- type: "success",
989
- message: formatMessage({
990
- id: "content-manager.success.record.discard",
991
- defaultMessage: "Changes discarded"
992
- })
993
- });
994
- return res.data;
995
- } catch (err) {
996
- toggleNotification({
997
- type: "danger",
998
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
999
- });
1000
- throw err;
1001
- }
1009
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1010
+ (acc, [attribute, metadata]) => {
1011
+ return {
1012
+ ...acc,
1013
+ [attribute]: metadata.edit
1014
+ };
1002
1015
  },
1003
- [discardDocument, formatAPIError, formatMessage, toggleNotification]
1016
+ {}
1004
1017
  );
1005
- const [publishDocument] = usePublishDocumentMutation();
1006
- const publish = React__namespace.useCallback(
1007
- async ({ collectionType, model, documentId, params }, data) => {
1008
- try {
1009
- trackUsage("willPublishEntry");
1010
- const res = await publishDocument({
1011
- collectionType,
1012
- model,
1013
- documentId,
1014
- data,
1015
- params
1016
- });
1017
- if ("error" in res) {
1018
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1019
- return { error: res.error };
1020
- }
1021
- trackUsage("didPublishEntry");
1022
- toggleNotification({
1023
- type: "success",
1024
- message: formatMessage({
1025
- id: getTranslation("success.record.publish"),
1026
- defaultMessage: "Published document"
1027
- })
1028
- });
1029
- return res.data;
1030
- } catch (err) {
1031
- toggleNotification({
1032
- type: "danger",
1033
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1034
- });
1035
- throw err;
1036
- }
1018
+ return {
1019
+ layout: panelledEditAttributes,
1020
+ components: componentEditAttributes,
1021
+ metadatas: editMetadatas,
1022
+ settings: {
1023
+ ...data.contentType.settings,
1024
+ displayName: schema?.info.displayName
1037
1025
  },
1038
- [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1039
- );
1040
- const [publishManyDocuments] = usePublishManyDocumentsMutation();
1041
- const publishMany = React__namespace.useCallback(
1042
- async ({ model, documentIds, params }) => {
1043
- try {
1044
- const res = await publishManyDocuments({
1045
- model,
1046
- documentIds,
1047
- params
1048
- });
1049
- if ("error" in res) {
1050
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1051
- return { error: res.error };
1052
- }
1053
- toggleNotification({
1054
- type: "success",
1055
- message: formatMessage({
1056
- id: getTranslation("success.record.publish"),
1057
- defaultMessage: "Published document"
1058
- })
1059
- });
1060
- return res.data;
1061
- } catch (err) {
1062
- toggleNotification({
1063
- type: "danger",
1064
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1065
- });
1066
- throw err;
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;
1067
1039
  }
1040
+ const { edit: metadata } = metadatas[field.name];
1041
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1042
+ return {
1043
+ attribute,
1044
+ disabled: !metadata.editable,
1045
+ hint: metadata.description,
1046
+ label: metadata.label ?? "",
1047
+ name: field.name,
1048
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1049
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1050
+ schemas,
1051
+ components: components?.schemas ?? {}
1052
+ }),
1053
+ placeholder: metadata.placeholder ?? "",
1054
+ required: attribute.required ?? false,
1055
+ size: field.size,
1056
+ unique: "unique" in attribute ? attribute.unique : false,
1057
+ visible: metadata.visible ?? true,
1058
+ type: attribute.type
1059
+ };
1060
+ }).filter((field) => field !== null)
1061
+ );
1062
+ };
1063
+ const formatListLayout = (data, {
1064
+ schemas,
1065
+ schema,
1066
+ components
1067
+ }) => {
1068
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1069
+ (acc, [attribute, metadata]) => {
1070
+ return {
1071
+ ...acc,
1072
+ [attribute]: metadata.list
1073
+ };
1068
1074
  },
1069
- [
1070
- // trackUsage,
1071
- publishManyDocuments,
1072
- toggleNotification,
1073
- formatMessage,
1074
- formatAPIError
1075
- ]
1075
+ {}
1076
1076
  );
1077
- const [updateDocument] = useUpdateDocumentMutation();
1078
- const update = React__namespace.useCallback(
1079
- async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1080
- try {
1081
- trackUsage("willEditEntry", trackerProperty);
1082
- const res = await updateDocument({
1083
- collectionType,
1084
- model,
1085
- documentId,
1086
- data,
1087
- params
1088
- });
1089
- if ("error" in res) {
1090
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1091
- trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1092
- return { error: res.error };
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);
1093
1161
  }
1094
- trackUsage("didEditEntry", trackerProperty);
1095
- toggleNotification({
1096
- type: "success",
1097
- message: formatMessage({
1098
- id: getTranslation("success.record.save"),
1099
- defaultMessage: "Saved document"
1100
- })
1101
- });
1102
- return res.data;
1103
- } catch (err) {
1104
- trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1105
- toggleNotification({
1106
- type: "danger",
1107
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1108
- });
1109
- throw err;
1162
+ throw error2;
1110
1163
  }
1111
1164
  },
1112
- [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1165
+ [validationSchema]
1113
1166
  );
1114
- const [unpublishDocument] = useUnpublishDocumentMutation();
1115
- const unpublish = React__namespace.useCallback(
1116
- async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1167
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1168
+ return {
1169
+ components,
1170
+ document: data?.data,
1171
+ meta: data?.meta,
1172
+ isLoading,
1173
+ schema,
1174
+ schemas,
1175
+ validate
1176
+ };
1177
+ };
1178
+ const useDoc = () => {
1179
+ const { id, slug, collectionType, origin } = reactRouterDom.useParams();
1180
+ const [{ query }] = strapiAdmin.useQueryParams();
1181
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1182
+ if (!collectionType) {
1183
+ throw new Error("Could not find collectionType in url params");
1184
+ }
1185
+ if (!slug) {
1186
+ throw new Error("Could not find model in url params");
1187
+ }
1188
+ return {
1189
+ collectionType,
1190
+ model: slug,
1191
+ id: origin || id === "create" ? void 0 : id,
1192
+ ...useDocument(
1193
+ { documentId: origin || id, model: slug, collectionType, params },
1194
+ {
1195
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1196
+ }
1197
+ )
1198
+ };
1199
+ };
1200
+ const useContentManagerContext = () => {
1201
+ const {
1202
+ collectionType,
1203
+ model,
1204
+ id,
1205
+ components,
1206
+ isLoading: isLoadingDoc,
1207
+ schema,
1208
+ schemas
1209
+ } = useDoc();
1210
+ const layout = useDocumentLayout(model);
1211
+ const form = strapiAdmin.useForm("useContentManagerContext", (state) => state);
1212
+ const isSingleType = collectionType === SINGLE_TYPES;
1213
+ const slug = model;
1214
+ const isCreatingEntry = id === "create";
1215
+ useContentTypeSchema();
1216
+ const isLoading = isLoadingDoc || layout.isLoading;
1217
+ const error = layout.error;
1218
+ return {
1219
+ error,
1220
+ isLoading,
1221
+ // Base metadata
1222
+ model,
1223
+ collectionType,
1224
+ id,
1225
+ slug,
1226
+ isCreatingEntry,
1227
+ isSingleType,
1228
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1229
+ // All schema infos
1230
+ components,
1231
+ contentType: schema,
1232
+ contentTypes: schemas,
1233
+ // Form state
1234
+ form,
1235
+ // layout infos
1236
+ layout
1237
+ };
1238
+ };
1239
+ const prefixPluginTranslations = (trad, pluginId) => {
1240
+ if (!pluginId) {
1241
+ throw new TypeError("pluginId can't be empty");
1242
+ }
1243
+ return Object.keys(trad).reduce((acc, current) => {
1244
+ acc[`${pluginId}.${current}`] = trad[current];
1245
+ return acc;
1246
+ }, {});
1247
+ };
1248
+ const getTranslation = (id) => `content-manager.${id}`;
1249
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1250
+ id: "notification.error",
1251
+ defaultMessage: "An error occurred, please try again"
1252
+ };
1253
+ const useDocumentActions = () => {
1254
+ const { toggleNotification } = strapiAdmin.useNotification();
1255
+ const { formatMessage } = reactIntl.useIntl();
1256
+ const { trackUsage } = strapiAdmin.useTracking();
1257
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1258
+ const navigate = reactRouterDom.useNavigate();
1259
+ const setCurrentStep = strapiAdmin.useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
1260
+ const [deleteDocument] = useDeleteDocumentMutation();
1261
+ const _delete = React__namespace.useCallback(
1262
+ async ({ collectionType, model, documentId, params }, trackerProperty) => {
1117
1263
  try {
1118
- trackUsage("willUnpublishEntry");
1119
- const res = await unpublishDocument({
1264
+ trackUsage("willDeleteEntry", trackerProperty);
1265
+ const res = await deleteDocument({
1120
1266
  collectionType,
1121
1267
  model,
1122
1268
  documentId,
1123
- params,
1124
- data: {
1125
- discardDraft
1126
- }
1269
+ params
1127
1270
  });
1128
1271
  if ("error" in res) {
1129
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1272
+ toggleNotification({
1273
+ type: "danger",
1274
+ message: formatAPIError(res.error)
1275
+ });
1130
1276
  return { error: res.error };
1131
1277
  }
1132
- trackUsage("didUnpublishEntry");
1133
1278
  toggleNotification({
1134
1279
  type: "success",
1135
1280
  message: formatMessage({
1136
- id: getTranslation("success.record.unpublish"),
1137
- defaultMessage: "Unpublished document"
1281
+ id: getTranslation("success.record.delete"),
1282
+ defaultMessage: "Deleted document"
1138
1283
  })
1139
1284
  });
1285
+ trackUsage("didDeleteEntry", trackerProperty);
1140
1286
  return res.data;
1141
1287
  } catch (err) {
1142
1288
  toggleNotification({
1143
1289
  type: "danger",
1144
1290
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1145
1291
  });
1292
+ trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
1146
1293
  throw err;
1147
1294
  }
1148
1295
  },
1149
- [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1296
+ [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
1150
1297
  );
1151
- const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1152
- const unpublishMany = React__namespace.useCallback(
1298
+ const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
1299
+ const deleteMany = React__namespace.useCallback(
1153
1300
  async ({ model, documentIds, params }) => {
1154
1301
  try {
1155
- trackUsage("willBulkUnpublishEntries");
1156
- const res = await unpublishManyDocuments({
1302
+ trackUsage("willBulkDeleteEntries");
1303
+ const res = await deleteManyDocuments({
1157
1304
  model,
1158
1305
  documentIds,
1159
1306
  params
1160
1307
  });
1161
1308
  if ("error" in res) {
1162
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1309
+ toggleNotification({
1310
+ type: "danger",
1311
+ message: formatAPIError(res.error)
1312
+ });
1163
1313
  return { error: res.error };
1164
1314
  }
1165
- trackUsage("didBulkUnpublishEntries");
1166
1315
  toggleNotification({
1167
1316
  type: "success",
1168
1317
  title: formatMessage({
1169
- id: getTranslation("success.records.unpublish"),
1170
- defaultMessage: "Successfully unpublished."
1318
+ id: getTranslation("success.records.delete"),
1319
+ defaultMessage: "Successfully deleted."
1171
1320
  }),
1172
1321
  message: ""
1173
1322
  });
1323
+ trackUsage("didBulkDeleteEntries");
1174
1324
  return res.data;
1175
1325
  } catch (err) {
1176
1326
  toggleNotification({
1177
1327
  type: "danger",
1178
1328
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1179
1329
  });
1180
- trackUsage("didNotBulkUnpublishEntries");
1330
+ trackUsage("didNotBulkDeleteEntries");
1181
1331
  throw err;
1182
1332
  }
1183
1333
  },
1184
- [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1334
+ [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1185
1335
  );
1186
- const [createDocument] = useCreateDocumentMutation();
1187
- const create = React__namespace.useCallback(
1188
- async ({ model, params }, data, trackerProperty) => {
1336
+ const [discardDocument] = useDiscardDocumentMutation();
1337
+ const discard = React__namespace.useCallback(
1338
+ async ({ collectionType, model, documentId, params }) => {
1189
1339
  try {
1190
- const res = await createDocument({
1340
+ const res = await discardDocument({
1341
+ collectionType,
1191
1342
  model,
1192
- data,
1343
+ documentId,
1193
1344
  params
1194
1345
  });
1195
1346
  if ("error" in res) {
1196
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1197
- trackUsage("didNotCreateEntry", { error: res.error, ...trackerProperty });
1347
+ toggleNotification({
1348
+ type: "danger",
1349
+ message: formatAPIError(res.error)
1350
+ });
1198
1351
  return { error: res.error };
1199
1352
  }
1200
- trackUsage("didCreateEntry", trackerProperty);
1201
1353
  toggleNotification({
1202
1354
  type: "success",
1203
1355
  message: formatMessage({
1204
- id: getTranslation("success.record.save"),
1205
- defaultMessage: "Saved document"
1356
+ id: "content-manager.success.record.discard",
1357
+ defaultMessage: "Changes discarded"
1206
1358
  })
1207
1359
  });
1208
1360
  return res.data;
@@ -1211,13 +1363,228 @@ const useDocumentActions = () => {
1211
1363
  type: "danger",
1212
1364
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1213
1365
  });
1214
- trackUsage("didNotCreateEntry", { error: err, ...trackerProperty });
1215
1366
  throw err;
1216
1367
  }
1217
1368
  },
1218
- [createDocument, formatAPIError, formatMessage, toggleNotification, trackUsage]
1369
+ [discardDocument, formatAPIError, formatMessage, toggleNotification]
1219
1370
  );
1220
- const [autoCloneDocument] = useAutoCloneDocumentMutation();
1371
+ const [publishDocument] = usePublishDocumentMutation();
1372
+ const publish = React__namespace.useCallback(
1373
+ async ({ collectionType, model, documentId, params }, data) => {
1374
+ try {
1375
+ trackUsage("willPublishEntry");
1376
+ const res = await publishDocument({
1377
+ collectionType,
1378
+ model,
1379
+ documentId,
1380
+ data,
1381
+ params
1382
+ });
1383
+ if ("error" in res) {
1384
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1385
+ return { error: res.error };
1386
+ }
1387
+ trackUsage("didPublishEntry");
1388
+ toggleNotification({
1389
+ type: "success",
1390
+ message: formatMessage({
1391
+ id: getTranslation("success.record.publish"),
1392
+ defaultMessage: "Published document"
1393
+ })
1394
+ });
1395
+ return res.data;
1396
+ } catch (err) {
1397
+ toggleNotification({
1398
+ type: "danger",
1399
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1400
+ });
1401
+ throw err;
1402
+ }
1403
+ },
1404
+ [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1405
+ );
1406
+ const [publishManyDocuments] = usePublishManyDocumentsMutation();
1407
+ const publishMany = React__namespace.useCallback(
1408
+ async ({ model, documentIds, params }) => {
1409
+ try {
1410
+ const res = await publishManyDocuments({
1411
+ model,
1412
+ documentIds,
1413
+ params
1414
+ });
1415
+ if ("error" in res) {
1416
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1417
+ return { error: res.error };
1418
+ }
1419
+ toggleNotification({
1420
+ type: "success",
1421
+ message: formatMessage({
1422
+ id: getTranslation("success.record.publish"),
1423
+ defaultMessage: "Published document"
1424
+ })
1425
+ });
1426
+ return res.data;
1427
+ } catch (err) {
1428
+ toggleNotification({
1429
+ type: "danger",
1430
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1431
+ });
1432
+ throw err;
1433
+ }
1434
+ },
1435
+ [
1436
+ // trackUsage,
1437
+ publishManyDocuments,
1438
+ toggleNotification,
1439
+ formatMessage,
1440
+ formatAPIError
1441
+ ]
1442
+ );
1443
+ const [updateDocument] = useUpdateDocumentMutation();
1444
+ const update = React__namespace.useCallback(
1445
+ async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1446
+ try {
1447
+ trackUsage("willEditEntry", trackerProperty);
1448
+ const res = await updateDocument({
1449
+ collectionType,
1450
+ model,
1451
+ documentId,
1452
+ data,
1453
+ params
1454
+ });
1455
+ if ("error" in res) {
1456
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1457
+ trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1458
+ return { error: res.error };
1459
+ }
1460
+ trackUsage("didEditEntry", trackerProperty);
1461
+ toggleNotification({
1462
+ type: "success",
1463
+ message: formatMessage({
1464
+ id: getTranslation("success.record.save"),
1465
+ defaultMessage: "Saved document"
1466
+ })
1467
+ });
1468
+ return res.data;
1469
+ } catch (err) {
1470
+ trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1471
+ toggleNotification({
1472
+ type: "danger",
1473
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1474
+ });
1475
+ throw err;
1476
+ }
1477
+ },
1478
+ [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1479
+ );
1480
+ const [unpublishDocument] = useUnpublishDocumentMutation();
1481
+ const unpublish = React__namespace.useCallback(
1482
+ async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1483
+ try {
1484
+ trackUsage("willUnpublishEntry");
1485
+ const res = await unpublishDocument({
1486
+ collectionType,
1487
+ model,
1488
+ documentId,
1489
+ params,
1490
+ data: {
1491
+ discardDraft
1492
+ }
1493
+ });
1494
+ if ("error" in res) {
1495
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1496
+ return { error: res.error };
1497
+ }
1498
+ trackUsage("didUnpublishEntry");
1499
+ toggleNotification({
1500
+ type: "success",
1501
+ message: formatMessage({
1502
+ id: getTranslation("success.record.unpublish"),
1503
+ defaultMessage: "Unpublished document"
1504
+ })
1505
+ });
1506
+ return res.data;
1507
+ } catch (err) {
1508
+ toggleNotification({
1509
+ type: "danger",
1510
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1511
+ });
1512
+ throw err;
1513
+ }
1514
+ },
1515
+ [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1516
+ );
1517
+ const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1518
+ const unpublishMany = React__namespace.useCallback(
1519
+ async ({ model, documentIds, params }) => {
1520
+ try {
1521
+ trackUsage("willBulkUnpublishEntries");
1522
+ const res = await unpublishManyDocuments({
1523
+ model,
1524
+ documentIds,
1525
+ params
1526
+ });
1527
+ if ("error" in res) {
1528
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1529
+ return { error: res.error };
1530
+ }
1531
+ trackUsage("didBulkUnpublishEntries");
1532
+ toggleNotification({
1533
+ type: "success",
1534
+ title: formatMessage({
1535
+ id: getTranslation("success.records.unpublish"),
1536
+ defaultMessage: "Successfully unpublished."
1537
+ }),
1538
+ message: ""
1539
+ });
1540
+ return res.data;
1541
+ } catch (err) {
1542
+ toggleNotification({
1543
+ type: "danger",
1544
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1545
+ });
1546
+ trackUsage("didNotBulkUnpublishEntries");
1547
+ throw err;
1548
+ }
1549
+ },
1550
+ [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1551
+ );
1552
+ const [createDocument] = useCreateDocumentMutation();
1553
+ const create = React__namespace.useCallback(
1554
+ async ({ model, params }, data, trackerProperty) => {
1555
+ try {
1556
+ const res = await createDocument({
1557
+ model,
1558
+ data,
1559
+ params
1560
+ });
1561
+ if ("error" in res) {
1562
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1563
+ trackUsage("didNotCreateEntry", { error: res.error, ...trackerProperty });
1564
+ return { error: res.error };
1565
+ }
1566
+ trackUsage("didCreateEntry", trackerProperty);
1567
+ toggleNotification({
1568
+ type: "success",
1569
+ message: formatMessage({
1570
+ id: getTranslation("success.record.save"),
1571
+ defaultMessage: "Saved document"
1572
+ })
1573
+ });
1574
+ setCurrentStep("contentManager.success");
1575
+ return res.data;
1576
+ } catch (err) {
1577
+ toggleNotification({
1578
+ type: "danger",
1579
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1580
+ });
1581
+ trackUsage("didNotCreateEntry", { error: err, ...trackerProperty });
1582
+ throw err;
1583
+ }
1584
+ },
1585
+ [createDocument, formatAPIError, formatMessage, toggleNotification, trackUsage]
1586
+ );
1587
+ const [autoCloneDocument] = useAutoCloneDocumentMutation();
1221
1588
  const autoClone = React__namespace.useCallback(
1222
1589
  async ({ model, sourceId }) => {
1223
1590
  try {
@@ -1244,7 +1611,7 @@ const useDocumentActions = () => {
1244
1611
  throw err;
1245
1612
  }
1246
1613
  },
1247
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1614
+ [autoCloneDocument, formatMessage, toggleNotification]
1248
1615
  );
1249
1616
  const [cloneDocument] = useCloneDocumentMutation();
1250
1617
  const clone = React__namespace.useCallback(
@@ -1270,6 +1637,7 @@ const useDocumentActions = () => {
1270
1637
  defaultMessage: "Cloned document"
1271
1638
  })
1272
1639
  });
1640
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1273
1641
  return res.data;
1274
1642
  } catch (err) {
1275
1643
  toggleNotification({
@@ -1280,7 +1648,7 @@ const useDocumentActions = () => {
1280
1648
  throw err;
1281
1649
  }
1282
1650
  },
1283
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1651
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1284
1652
  );
1285
1653
  const [getDoc] = useLazyGetDocumentQuery();
1286
1654
  const getDocument = React__namespace.useCallback(
@@ -1306,7 +1674,7 @@ const useDocumentActions = () => {
1306
1674
  };
1307
1675
  };
1308
1676
  const ProtectedHistoryPage = React.lazy(
1309
- () => Promise.resolve().then(() => require("./History-nuEzM5qm.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1677
+ () => Promise.resolve().then(() => require("./History-CWcM9HnW.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1310
1678
  );
1311
1679
  const routes$1 = [
1312
1680
  {
@@ -1319,31 +1687,31 @@ const routes$1 = [
1319
1687
  }
1320
1688
  ];
1321
1689
  const ProtectedEditViewPage = React.lazy(
1322
- () => Promise.resolve().then(() => require("./EditViewPage-BgjdnGz2.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1690
+ () => Promise.resolve().then(() => require("./EditViewPage-Co2IKQZH.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1323
1691
  );
1324
1692
  const ProtectedListViewPage = React.lazy(
1325
- () => Promise.resolve().then(() => require("./ListViewPage-SXIXm-RM.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1693
+ () => Promise.resolve().then(() => require("./ListViewPage-BBAC9aPu.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1326
1694
  );
1327
1695
  const ProtectedListConfiguration = React.lazy(
1328
- () => Promise.resolve().then(() => require("./ListConfigurationPage-CnB86Psm.js")).then((mod) => ({
1696
+ () => Promise.resolve().then(() => require("./ListConfigurationPage-DGzoQD_I.js")).then((mod) => ({
1329
1697
  default: mod.ProtectedListConfiguration
1330
1698
  }))
1331
1699
  );
1332
1700
  const ProtectedEditConfigurationPage = React.lazy(
1333
- () => Promise.resolve().then(() => require("./EditConfigurationPage-BQ17--5R.js")).then((mod) => ({
1701
+ () => Promise.resolve().then(() => require("./EditConfigurationPage-ZN3s568V.js")).then((mod) => ({
1334
1702
  default: mod.ProtectedEditConfigurationPage
1335
1703
  }))
1336
1704
  );
1337
1705
  const ProtectedComponentConfigurationPage = React.lazy(
1338
- () => Promise.resolve().then(() => require("./ComponentConfigurationPage-KXSuLnQD.js")).then((mod) => ({
1706
+ () => Promise.resolve().then(() => require("./ComponentConfigurationPage-_6osrv39.js")).then((mod) => ({
1339
1707
  default: mod.ProtectedComponentConfigurationPage
1340
1708
  }))
1341
1709
  );
1342
1710
  const NoPermissions = React.lazy(
1343
- () => Promise.resolve().then(() => require("./NoPermissionsPage-IGkId4C5.js")).then((mod) => ({ default: mod.NoPermissions }))
1711
+ () => Promise.resolve().then(() => require("./NoPermissionsPage-D2iWw-sn.js")).then((mod) => ({ default: mod.NoPermissions }))
1344
1712
  );
1345
1713
  const NoContentType = React.lazy(
1346
- () => Promise.resolve().then(() => require("./NoContentTypePage-BzsQ3hLZ.js")).then((mod) => ({ default: mod.NoContentType }))
1714
+ () => Promise.resolve().then(() => require("./NoContentTypePage-CwVDx_YC.js")).then((mod) => ({ default: mod.NoContentType }))
1347
1715
  );
1348
1716
  const CollectionTypePages = () => {
1349
1717
  const { collectionType } = reactRouterDom.useParams();
@@ -1524,7 +1892,7 @@ const DocumentActionsMenu = ({
1524
1892
  };
1525
1893
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
1526
1894
  /* @__PURE__ */ jsxRuntime.jsxs(
1527
- StyledMoreButton,
1895
+ designSystem.Menu.Trigger,
1528
1896
  {
1529
1897
  disabled: isDisabled,
1530
1898
  size: "S",
@@ -1542,7 +1910,7 @@ const DocumentActionsMenu = ({
1542
1910
  ]
1543
1911
  }
1544
1912
  ),
1545
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1913
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1546
1914
  actions2.map((action) => {
1547
1915
  return /* @__PURE__ */ jsxRuntime.jsx(
1548
1916
  designSystem.Menu.Item,
@@ -1640,11 +2008,6 @@ const convertActionVariantToIconColor = (variant = "secondary") => {
1640
2008
  return "primary600";
1641
2009
  }
1642
2010
  };
1643
- const StyledMoreButton = styledComponents.styled(designSystem.Menu.Trigger)`
1644
- & > span {
1645
- display: flex;
1646
- }
1647
- `;
1648
2011
  const DocumentActionConfirmDialog = ({
1649
2012
  onClose,
1650
2013
  onCancel,
@@ -1671,11 +2034,11 @@ const DocumentActionConfirmDialog = ({
1671
2034
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
1672
2035
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: content }),
1673
2036
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
1674
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", children: formatMessage({
2037
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1675
2038
  id: "app.components.Button.cancel",
1676
2039
  defaultMessage: "Cancel"
1677
2040
  }) }) }),
1678
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, children: formatMessage({
2041
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1679
2042
  id: "app.components.Button.confirm",
1680
2043
  defaultMessage: "Confirm"
1681
2044
  }) })
@@ -1714,12 +2077,10 @@ const PublishAction$1 = ({
1714
2077
  const navigate = reactRouterDom.useNavigate();
1715
2078
  const { toggleNotification } = strapiAdmin.useNotification();
1716
2079
  const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
2080
+ const isListView = reactRouterDom.useMatch(LIST_PATH) !== null;
1717
2081
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
1718
2082
  const { formatMessage } = reactIntl.useIntl();
1719
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1720
- "PublishAction",
1721
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1722
- );
2083
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1723
2084
  const { publish } = useDocumentActions();
1724
2085
  const [
1725
2086
  countDraftRelations,
@@ -1771,24 +2132,25 @@ const PublishAction$1 = ({
1771
2132
  }
1772
2133
  }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1773
2134
  React__namespace.useEffect(() => {
1774
- if (documentId) {
1775
- const fetchDraftRelationsCount = async () => {
1776
- const { data, error } = await countDraftRelations({
1777
- collectionType,
1778
- model,
1779
- documentId,
1780
- params
1781
- });
1782
- if (error) {
1783
- throw error;
1784
- }
1785
- if (data) {
1786
- setServerCountOfDraftRelations(data.data);
1787
- }
1788
- };
1789
- fetchDraftRelationsCount();
2135
+ if (!document || !document.documentId || isListView) {
2136
+ return;
1790
2137
  }
1791
- }, [documentId, countDraftRelations, collectionType, model, params]);
2138
+ const fetchDraftRelationsCount = async () => {
2139
+ const { data, error } = await countDraftRelations({
2140
+ collectionType,
2141
+ model,
2142
+ documentId,
2143
+ params
2144
+ });
2145
+ if (error) {
2146
+ throw error;
2147
+ }
2148
+ if (data) {
2149
+ setServerCountOfDraftRelations(data.data);
2150
+ }
2151
+ };
2152
+ fetchDraftRelationsCount();
2153
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1792
2154
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1793
2155
  if (!schema?.options?.draftAndPublish) {
1794
2156
  return null;
@@ -1829,7 +2191,8 @@ const PublishAction$1 = ({
1829
2191
  }
1830
2192
  };
1831
2193
  const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1832
- const hasDraftRelations = totalDraftRelations > 0;
2194
+ const enableDraftRelationsCount = false;
2195
+ const hasDraftRelations = enableDraftRelationsCount;
1833
2196
  return {
1834
2197
  /**
1835
2198
  * Disabled when:
@@ -1846,9 +2209,6 @@ const PublishAction$1 = ({
1846
2209
  defaultMessage: "Publish"
1847
2210
  }),
1848
2211
  onClick: async () => {
1849
- if (hasDraftRelations) {
1850
- return;
1851
- }
1852
2212
  await performPublish();
1853
2213
  },
1854
2214
  dialog: hasDraftRelations ? {
@@ -1887,10 +2247,6 @@ const UpdateAction = ({
1887
2247
  const cloneMatch = reactRouterDom.useMatch(CLONE_PATH);
1888
2248
  const isCloning = cloneMatch !== null;
1889
2249
  const { formatMessage } = reactIntl.useIntl();
1890
- useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1891
- canCreate: canCreate2,
1892
- canUpdate: canUpdate2
1893
- }));
1894
2250
  const { create, update, clone } = useDocumentActions();
1895
2251
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1896
2252
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
@@ -1916,16 +2272,18 @@ const UpdateAction = ({
1916
2272
  onClick: async () => {
1917
2273
  setSubmitting(true);
1918
2274
  try {
1919
- const { errors } = await validate();
1920
- if (errors) {
1921
- toggleNotification({
1922
- type: "danger",
1923
- message: formatMessage({
1924
- id: "content-manager.validation.error",
1925
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1926
- })
1927
- });
1928
- return;
2275
+ if (activeTab !== "draft") {
2276
+ const { errors } = await validate();
2277
+ if (errors) {
2278
+ toggleNotification({
2279
+ type: "danger",
2280
+ message: formatMessage({
2281
+ id: "content-manager.validation.error",
2282
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2283
+ })
2284
+ });
2285
+ return;
2286
+ }
1929
2287
  }
1930
2288
  if (isCloning) {
1931
2289
  const res = await clone(
@@ -2021,7 +2379,7 @@ const UnpublishAction$1 = ({
2021
2379
  id: "app.utils.unpublish",
2022
2380
  defaultMessage: "Unpublish"
2023
2381
  }),
2024
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2382
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
2025
2383
  onClick: async () => {
2026
2384
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
2027
2385
  if (!documentId) {
@@ -2133,7 +2491,7 @@ const DiscardAction = ({
2133
2491
  id: "content-manager.actions.discard.label",
2134
2492
  defaultMessage: "Discard changes"
2135
2493
  }),
2136
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2494
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
2137
2495
  position: ["panel", "table-row"],
2138
2496
  variant: "danger",
2139
2497
  dialog: {
@@ -2161,11 +2519,6 @@ const DiscardAction = ({
2161
2519
  };
2162
2520
  };
2163
2521
  DiscardAction.type = "discard";
2164
- const StyledCrossCircle = styledComponents.styled(Icons.CrossCircle)`
2165
- path {
2166
- fill: currentColor;
2167
- }
2168
- `;
2169
2522
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2170
2523
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2171
2524
  const RelativeTime = React__namespace.forwardRef(
@@ -2213,7 +2566,7 @@ const getDisplayName = ({
2213
2566
  };
2214
2567
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2215
2568
  const DocumentStatus = ({ status = "draft", ...restProps }) => {
2216
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2569
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2217
2570
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2218
2571
  };
2219
2572
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
@@ -2396,12 +2749,26 @@ const Information = ({ activeTab }) => {
2396
2749
  );
2397
2750
  };
2398
2751
  const HeaderActions = ({ actions: actions2 }) => {
2399
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: actions2.map((action) => {
2400
- if ("options" in action) {
2401
- return /* @__PURE__ */ jsxRuntime.jsx(
2402
- designSystem.SingleSelect,
2403
- {
2404
- size: "S",
2752
+ const [dialogId, setDialogId] = React__namespace.useState(null);
2753
+ const handleClick = (action) => async (e) => {
2754
+ if (!("options" in action)) {
2755
+ const { onClick = () => false, dialog, id } = action;
2756
+ const muteDialog = await onClick(e);
2757
+ if (dialog && !muteDialog) {
2758
+ e.preventDefault();
2759
+ setDialogId(id);
2760
+ }
2761
+ }
2762
+ };
2763
+ const handleClose = () => {
2764
+ setDialogId(null);
2765
+ };
2766
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, children: actions2.map((action) => {
2767
+ if (action.options) {
2768
+ return /* @__PURE__ */ jsxRuntime.jsx(
2769
+ designSystem.SingleSelect,
2770
+ {
2771
+ size: "S",
2405
2772
  disabled: action.disabled,
2406
2773
  "aria-label": action.label,
2407
2774
  onChange: action.onSelect,
@@ -2411,10 +2778,49 @@ const HeaderActions = ({ actions: actions2 }) => {
2411
2778
  action.id
2412
2779
  );
2413
2780
  } else {
2414
- return null;
2781
+ if (action.type === "icon") {
2782
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
2783
+ /* @__PURE__ */ jsxRuntime.jsx(
2784
+ designSystem.IconButton,
2785
+ {
2786
+ disabled: action.disabled,
2787
+ label: action.label,
2788
+ size: "S",
2789
+ onClick: handleClick(action),
2790
+ children: action.icon
2791
+ }
2792
+ ),
2793
+ action.dialog ? /* @__PURE__ */ jsxRuntime.jsx(
2794
+ HeaderActionDialog,
2795
+ {
2796
+ ...action.dialog,
2797
+ isOpen: dialogId === action.id,
2798
+ onClose: handleClose
2799
+ }
2800
+ ) : null
2801
+ ] }, action.id);
2802
+ }
2415
2803
  }
2416
2804
  }) });
2417
2805
  };
2806
+ const HeaderActionDialog = ({
2807
+ onClose,
2808
+ onCancel,
2809
+ title,
2810
+ content: Content,
2811
+ isOpen
2812
+ }) => {
2813
+ const handleClose = async () => {
2814
+ if (onCancel) {
2815
+ await onCancel();
2816
+ }
2817
+ onClose();
2818
+ };
2819
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2820
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2821
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content
2822
+ ] }) });
2823
+ };
2418
2824
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2419
2825
  const navigate = reactRouterDom.useNavigate();
2420
2826
  const { formatMessage } = reactIntl.useIntl();
@@ -2432,487 +2838,189 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2432
2838
  };
2433
2839
  ConfigureTheViewAction.type = "configure-the-view";
2434
2840
  const EditTheModelAction = ({ model }) => {
2435
- const navigate = reactRouterDom.useNavigate();
2436
- const { formatMessage } = reactIntl.useIntl();
2437
- return {
2438
- label: formatMessage({
2439
- id: "content-manager.link-to-ctb",
2440
- defaultMessage: "Edit the model"
2441
- }),
2442
- icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {}),
2443
- onClick: () => {
2444
- navigate(`/plugins/content-type-builder/content-types/${model}`);
2445
- },
2446
- position: "header"
2447
- };
2448
- };
2449
- EditTheModelAction.type = "edit-the-model";
2450
- const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2451
- const navigate = reactRouterDom.useNavigate();
2452
- const { formatMessage } = reactIntl.useIntl();
2453
- const listViewPathMatch = reactRouterDom.useMatch(LIST_PATH);
2454
- const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2455
- const { delete: deleteAction } = useDocumentActions();
2456
- const { toggleNotification } = strapiAdmin.useNotification();
2457
- const setSubmitting = strapiAdmin.useForm("DeleteAction", (state) => state.setSubmitting);
2458
- return {
2459
- disabled: !canDelete || !document,
2460
- label: formatMessage({
2461
- id: "content-manager.actions.delete.label",
2462
- defaultMessage: "Delete document"
2463
- }),
2464
- icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2465
- dialog: {
2466
- type: "dialog",
2467
- title: formatMessage({
2468
- id: "app.components.ConfirmDialog.title",
2469
- defaultMessage: "Confirmation"
2470
- }),
2471
- content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
2472
- /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2473
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2474
- id: "content-manager.actions.delete.dialog.body",
2475
- defaultMessage: "Are you sure?"
2476
- }) })
2477
- ] }),
2478
- onConfirm: async () => {
2479
- if (!listViewPathMatch) {
2480
- setSubmitting(true);
2481
- }
2482
- try {
2483
- if (!documentId && collectionType !== SINGLE_TYPES) {
2484
- console.error(
2485
- "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2486
- );
2487
- toggleNotification({
2488
- message: formatMessage({
2489
- id: "content-manager.actions.delete.error",
2490
- defaultMessage: "An error occurred while trying to delete the document."
2491
- }),
2492
- type: "danger"
2493
- });
2494
- return;
2495
- }
2496
- const res = await deleteAction({
2497
- documentId,
2498
- model,
2499
- collectionType,
2500
- params: {
2501
- locale: "*"
2502
- }
2503
- });
2504
- if (!("error" in res)) {
2505
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2506
- }
2507
- } finally {
2508
- if (!listViewPathMatch) {
2509
- setSubmitting(false);
2510
- }
2511
- }
2512
- }
2513
- },
2514
- variant: "danger",
2515
- position: ["header", "table-row"]
2516
- };
2517
- };
2518
- DeleteAction$1.type = "delete";
2519
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2520
- const Panels = () => {
2521
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2522
- const [
2523
- {
2524
- query: { status }
2525
- }
2526
- ] = strapiAdmin.useQueryParams({
2527
- status: "draft"
2528
- });
2529
- const { model, id, document, meta, collectionType } = useDoc();
2530
- const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2531
- const props = {
2532
- activeTab: status,
2533
- model,
2534
- documentId: id,
2535
- document: isCloning ? void 0 : document,
2536
- meta: isCloning ? void 0 : meta,
2537
- collectionType
2538
- };
2539
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2540
- strapiAdmin.DescriptionComponentRenderer,
2541
- {
2542
- props,
2543
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2544
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2545
- }
2546
- ) });
2547
- };
2548
- const ActionsPanel = () => {
2549
- const { formatMessage } = reactIntl.useIntl();
2550
- return {
2551
- title: formatMessage({
2552
- id: "content-manager.containers.edit.panels.default.title",
2553
- defaultMessage: "Document"
2554
- }),
2555
- content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2556
- };
2557
- };
2558
- ActionsPanel.type = "actions";
2559
- const ActionsPanelContent = () => {
2560
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2561
- const [
2562
- {
2563
- query: { status = "draft" }
2564
- }
2565
- ] = strapiAdmin.useQueryParams();
2566
- const { model, id, document, meta, collectionType } = useDoc();
2567
- const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
2568
- const props = {
2569
- activeTab: status,
2570
- model,
2571
- documentId: id,
2572
- document: isCloning ? void 0 : document,
2573
- meta: isCloning ? void 0 : meta,
2574
- collectionType
2575
- };
2576
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
2577
- /* @__PURE__ */ jsxRuntime.jsx(
2578
- strapiAdmin.DescriptionComponentRenderer,
2579
- {
2580
- props,
2581
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2582
- children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
2583
- }
2584
- ),
2585
- /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
2586
- ] });
2587
- };
2588
- const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
2589
- return /* @__PURE__ */ jsxRuntime.jsxs(
2590
- designSystem.Flex,
2591
- {
2592
- ref,
2593
- tag: "aside",
2594
- "aria-labelledby": "additional-information",
2595
- background: "neutral0",
2596
- borderColor: "neutral150",
2597
- hasRadius: true,
2598
- paddingBottom: 4,
2599
- paddingLeft: 4,
2600
- paddingRight: 4,
2601
- paddingTop: 4,
2602
- shadow: "tableShadow",
2603
- gap: 3,
2604
- direction: "column",
2605
- justifyContent: "stretch",
2606
- alignItems: "flex-start",
2607
- children: [
2608
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2609
- children
2610
- ]
2611
- }
2612
- );
2613
- });
2614
- const HOOKS = {
2615
- /**
2616
- * Hook that allows to mutate the displayed headers of the list view table
2617
- * @constant
2618
- * @type {string}
2619
- */
2620
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2621
- /**
2622
- * Hook that allows to mutate the CM's collection types links pre-set filters
2623
- * @constant
2624
- * @type {string}
2625
- */
2626
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2627
- /**
2628
- * Hook that allows to mutate the CM's edit view layout
2629
- * @constant
2630
- * @type {string}
2631
- */
2632
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2633
- /**
2634
- * Hook that allows to mutate the CM's single types links pre-set filters
2635
- * @constant
2636
- * @type {string}
2637
- */
2638
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2639
- };
2640
- const contentTypesApi = contentManagerApi.injectEndpoints({
2641
- endpoints: (builder) => ({
2642
- getContentTypeConfiguration: builder.query({
2643
- query: (uid) => ({
2644
- url: `/content-manager/content-types/${uid}/configuration`,
2645
- method: "GET"
2646
- }),
2647
- transformResponse: (response) => response.data,
2648
- providesTags: (_result, _error, uid) => [
2649
- { type: "ContentTypesConfiguration", id: uid },
2650
- { type: "ContentTypeSettings", id: "LIST" }
2651
- ]
2652
- }),
2653
- getAllContentTypeSettings: builder.query({
2654
- query: () => "/content-manager/content-types-settings",
2655
- transformResponse: (response) => response.data,
2656
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2657
- }),
2658
- updateContentTypeConfiguration: builder.mutation({
2659
- query: ({ uid, ...body }) => ({
2660
- url: `/content-manager/content-types/${uid}/configuration`,
2661
- method: "PUT",
2662
- data: body
2663
- }),
2664
- transformResponse: (response) => response.data,
2665
- invalidatesTags: (_result, _error, { uid }) => [
2666
- { type: "ContentTypesConfiguration", id: uid },
2667
- { type: "ContentTypeSettings", id: "LIST" },
2668
- // Is this necessary?
2669
- { type: "InitialData" }
2670
- ]
2671
- })
2672
- })
2673
- });
2674
- const {
2675
- useGetContentTypeConfigurationQuery,
2676
- useGetAllContentTypeSettingsQuery,
2677
- useUpdateContentTypeConfigurationMutation
2678
- } = contentTypesApi;
2679
- const checkIfAttributeIsDisplayable = (attribute) => {
2680
- const { type } = attribute;
2681
- if (type === "relation") {
2682
- return !attribute.relation.toLowerCase().includes("morph");
2683
- }
2684
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2685
- };
2686
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2687
- if (!mainFieldName) {
2688
- return void 0;
2689
- }
2690
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2691
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2692
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2693
- );
2694
- return {
2695
- name: mainFieldName,
2696
- type: mainFieldType ?? "string"
2697
- };
2698
- };
2699
- const DEFAULT_SETTINGS = {
2700
- bulkable: false,
2701
- filterable: false,
2702
- searchable: false,
2703
- pagination: false,
2704
- defaultSortBy: "",
2705
- defaultSortOrder: "asc",
2706
- mainField: "id",
2707
- pageSize: 10
2708
- };
2709
- const useDocumentLayout = (model) => {
2710
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2711
- const [{ query }] = strapiAdmin.useQueryParams();
2712
- const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2713
- const { toggleNotification } = strapiAdmin.useNotification();
2714
- const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
2715
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2716
- const {
2717
- data,
2718
- isLoading: isLoadingConfigs,
2719
- error,
2720
- isFetching: isFetchingConfigs
2721
- } = useGetContentTypeConfigurationQuery(model);
2722
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2723
- React__namespace.useEffect(() => {
2724
- if (error) {
2725
- toggleNotification({
2726
- type: "danger",
2727
- message: formatAPIError(error)
2728
- });
2729
- }
2730
- }, [error, formatAPIError, toggleNotification]);
2731
- const editLayout = React__namespace.useMemo(
2732
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2733
- layout: [],
2734
- components: {},
2735
- metadatas: {},
2736
- options: {},
2737
- settings: DEFAULT_SETTINGS
2738
- },
2739
- [data, isLoading, schemas, schema, components]
2740
- );
2741
- const listLayout = React__namespace.useMemo(() => {
2742
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2743
- layout: [],
2744
- metadatas: {},
2745
- options: {},
2746
- settings: DEFAULT_SETTINGS
2747
- };
2748
- }, [data, isLoading, schemas, schema, components]);
2749
- const { layout: edit } = React__namespace.useMemo(
2750
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2751
- layout: editLayout,
2752
- query
2753
- }),
2754
- [editLayout, query, runHookWaterfall]
2755
- );
2756
- return {
2757
- error,
2758
- isLoading,
2759
- edit,
2760
- list: listLayout
2761
- };
2762
- };
2763
- const useDocLayout = () => {
2764
- const { model } = useDoc();
2765
- return useDocumentLayout(model);
2766
- };
2767
- const formatEditLayout = (data, {
2768
- schemas,
2769
- schema,
2770
- components
2771
- }) => {
2772
- let currentPanelIndex = 0;
2773
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2774
- data.contentType.layouts.edit,
2775
- schema?.attributes,
2776
- data.contentType.metadatas,
2777
- { configurations: data.components, schemas: components },
2778
- schemas
2779
- ).reduce((panels, row) => {
2780
- if (row.some((field) => field.type === "dynamiczone")) {
2781
- panels.push([row]);
2782
- currentPanelIndex += 2;
2783
- } else {
2784
- if (!panels[currentPanelIndex]) {
2785
- panels.push([]);
2786
- }
2787
- panels[currentPanelIndex].push(row);
2788
- }
2789
- return panels;
2790
- }, []);
2791
- const componentEditAttributes = Object.entries(data.components).reduce(
2792
- (acc, [uid, configuration]) => {
2793
- acc[uid] = {
2794
- layout: convertEditLayoutToFieldLayouts(
2795
- configuration.layouts.edit,
2796
- components[uid].attributes,
2797
- configuration.metadatas
2798
- ),
2799
- settings: {
2800
- ...configuration.settings,
2801
- icon: components[uid].info.icon,
2802
- displayName: components[uid].info.displayName
2803
- }
2804
- };
2805
- return acc;
2806
- },
2807
- {}
2808
- );
2809
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2810
- (acc, [attribute, metadata]) => {
2811
- return {
2812
- ...acc,
2813
- [attribute]: metadata.edit
2814
- };
2815
- },
2816
- {}
2817
- );
2841
+ const navigate = reactRouterDom.useNavigate();
2842
+ const { formatMessage } = reactIntl.useIntl();
2818
2843
  return {
2819
- layout: panelledEditAttributes,
2820
- components: componentEditAttributes,
2821
- metadatas: editMetadatas,
2822
- settings: {
2823
- ...data.contentType.settings,
2824
- displayName: schema?.info.displayName
2844
+ label: formatMessage({
2845
+ id: "content-manager.link-to-ctb",
2846
+ defaultMessage: "Edit the model"
2847
+ }),
2848
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {}),
2849
+ onClick: () => {
2850
+ navigate(`/plugins/content-type-builder/content-types/${model}`);
2825
2851
  },
2826
- options: {
2827
- ...schema?.options,
2828
- ...schema?.pluginOptions,
2829
- ...data.contentType.options
2830
- }
2852
+ position: "header"
2831
2853
  };
2832
2854
  };
2833
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2834
- return rows.map(
2835
- (row) => row.map((field) => {
2836
- const attribute = attributes[field.name];
2837
- if (!attribute) {
2838
- return null;
2855
+ EditTheModelAction.type = "edit-the-model";
2856
+ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2857
+ const navigate = reactRouterDom.useNavigate();
2858
+ const { formatMessage } = reactIntl.useIntl();
2859
+ const listViewPathMatch = reactRouterDom.useMatch(LIST_PATH);
2860
+ const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2861
+ const { delete: deleteAction } = useDocumentActions();
2862
+ const { toggleNotification } = strapiAdmin.useNotification();
2863
+ const setSubmitting = strapiAdmin.useForm("DeleteAction", (state) => state.setSubmitting);
2864
+ const isLocalized = document?.locale != null;
2865
+ return {
2866
+ disabled: !canDelete || !document,
2867
+ label: formatMessage(
2868
+ {
2869
+ id: "content-manager.actions.delete.label",
2870
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2871
+ },
2872
+ { isLocalized }
2873
+ ),
2874
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2875
+ dialog: {
2876
+ type: "dialog",
2877
+ title: formatMessage({
2878
+ id: "app.components.ConfirmDialog.title",
2879
+ defaultMessage: "Confirmation"
2880
+ }),
2881
+ content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
2882
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2883
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2884
+ id: "content-manager.actions.delete.dialog.body",
2885
+ defaultMessage: "Are you sure?"
2886
+ }) })
2887
+ ] }),
2888
+ onConfirm: async () => {
2889
+ if (!listViewPathMatch) {
2890
+ setSubmitting(true);
2891
+ }
2892
+ try {
2893
+ if (!documentId && collectionType !== SINGLE_TYPES) {
2894
+ console.error(
2895
+ "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2896
+ );
2897
+ toggleNotification({
2898
+ message: formatMessage({
2899
+ id: "content-manager.actions.delete.error",
2900
+ defaultMessage: "An error occurred while trying to delete the document."
2901
+ }),
2902
+ type: "danger"
2903
+ });
2904
+ return;
2905
+ }
2906
+ const res = await deleteAction({
2907
+ documentId,
2908
+ model,
2909
+ collectionType,
2910
+ params: {
2911
+ locale: "*"
2912
+ }
2913
+ });
2914
+ if (!("error" in res)) {
2915
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2916
+ }
2917
+ } finally {
2918
+ if (!listViewPathMatch) {
2919
+ setSubmitting(false);
2920
+ }
2921
+ }
2839
2922
  }
2840
- const { edit: metadata } = metadatas[field.name];
2841
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2842
- return {
2843
- attribute,
2844
- disabled: !metadata.editable,
2845
- hint: metadata.description,
2846
- label: metadata.label ?? "",
2847
- name: field.name,
2848
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2849
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2850
- schemas,
2851
- components: components?.schemas ?? {}
2852
- }),
2853
- placeholder: metadata.placeholder ?? "",
2854
- required: attribute.required ?? false,
2855
- size: field.size,
2856
- unique: "unique" in attribute ? attribute.unique : false,
2857
- visible: metadata.visible ?? true,
2858
- type: attribute.type
2859
- };
2860
- }).filter((field) => field !== null)
2861
- );
2862
- };
2863
- const formatListLayout = (data, {
2864
- schemas,
2865
- schema,
2866
- components
2867
- }) => {
2868
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2869
- (acc, [attribute, metadata]) => {
2870
- return {
2871
- ...acc,
2872
- [attribute]: metadata.list
2873
- };
2874
2923
  },
2875
- {}
2876
- );
2877
- const listAttributes = convertListLayoutToFieldLayouts(
2878
- data.contentType.layouts.list,
2879
- schema?.attributes,
2880
- listMetadatas,
2881
- { configurations: data.components, schemas: components },
2882
- schemas
2883
- );
2884
- return {
2885
- layout: listAttributes,
2886
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2887
- metadatas: listMetadatas,
2888
- options: {
2889
- ...schema?.options,
2890
- ...schema?.pluginOptions,
2891
- ...data.contentType.options
2924
+ variant: "danger",
2925
+ position: ["header", "table-row"]
2926
+ };
2927
+ };
2928
+ DeleteAction$1.type = "delete";
2929
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2930
+ const Panels = () => {
2931
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2932
+ const [
2933
+ {
2934
+ query: { status }
2935
+ }
2936
+ ] = strapiAdmin.useQueryParams({
2937
+ status: "draft"
2938
+ });
2939
+ const { model, id, document, meta, collectionType } = useDoc();
2940
+ const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2941
+ const props = {
2942
+ activeTab: status,
2943
+ model,
2944
+ documentId: id,
2945
+ document: isCloning ? void 0 : document,
2946
+ meta: isCloning ? void 0 : meta,
2947
+ collectionType
2948
+ };
2949
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2950
+ strapiAdmin.DescriptionComponentRenderer,
2951
+ {
2952
+ props,
2953
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2954
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2892
2955
  }
2956
+ ) });
2957
+ };
2958
+ const ActionsPanel = () => {
2959
+ const { formatMessage } = reactIntl.useIntl();
2960
+ return {
2961
+ title: formatMessage({
2962
+ id: "content-manager.containers.edit.panels.default.title",
2963
+ defaultMessage: "Entry"
2964
+ }),
2965
+ content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2893
2966
  };
2894
2967
  };
2895
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2896
- return columns.map((name) => {
2897
- const attribute = attributes[name];
2898
- if (!attribute) {
2899
- return null;
2968
+ ActionsPanel.type = "actions";
2969
+ const ActionsPanelContent = () => {
2970
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2971
+ const [
2972
+ {
2973
+ query: { status = "draft" }
2900
2974
  }
2901
- const metadata = metadatas[name];
2902
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2903
- return {
2904
- attribute,
2905
- label: metadata.label ?? "",
2906
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2907
- schemas,
2908
- components: components?.schemas ?? {}
2909
- }),
2910
- name,
2911
- searchable: metadata.searchable ?? true,
2912
- sortable: metadata.sortable ?? true
2913
- };
2914
- }).filter((field) => field !== null);
2975
+ ] = strapiAdmin.useQueryParams();
2976
+ const { model, id, document, meta, collectionType } = useDoc();
2977
+ const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
2978
+ const props = {
2979
+ activeTab: status,
2980
+ model,
2981
+ documentId: id,
2982
+ document: isCloning ? void 0 : document,
2983
+ meta: isCloning ? void 0 : meta,
2984
+ collectionType
2985
+ };
2986
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
2987
+ /* @__PURE__ */ jsxRuntime.jsx(
2988
+ strapiAdmin.DescriptionComponentRenderer,
2989
+ {
2990
+ props,
2991
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
2992
+ children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
2993
+ }
2994
+ ),
2995
+ /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
2996
+ ] });
2915
2997
  };
2998
+ const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
2999
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3000
+ designSystem.Flex,
3001
+ {
3002
+ ref,
3003
+ tag: "aside",
3004
+ "aria-labelledby": "additional-information",
3005
+ background: "neutral0",
3006
+ borderColor: "neutral150",
3007
+ hasRadius: true,
3008
+ paddingBottom: 4,
3009
+ paddingLeft: 4,
3010
+ paddingRight: 4,
3011
+ paddingTop: 4,
3012
+ shadow: "tableShadow",
3013
+ gap: 3,
3014
+ direction: "column",
3015
+ justifyContent: "stretch",
3016
+ alignItems: "flex-start",
3017
+ children: [
3018
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
3019
+ children
3020
+ ]
3021
+ }
3022
+ );
3023
+ });
2916
3024
  const ConfirmBulkActionDialog = ({
2917
3025
  onToggleDialog,
2918
3026
  isOpen = false,
@@ -2951,6 +3059,7 @@ const ConfirmDialogPublishAll = ({
2951
3059
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler(getTranslation);
2952
3060
  const { model, schema } = useDoc();
2953
3061
  const [{ query }] = strapiAdmin.useQueryParams();
3062
+ const enableDraftRelationsCount = false;
2954
3063
  const {
2955
3064
  data: countDraftRelations = 0,
2956
3065
  isLoading,
@@ -2962,7 +3071,7 @@ const ConfirmDialogPublishAll = ({
2962
3071
  locale: query?.plugins?.i18n?.locale
2963
3072
  },
2964
3073
  {
2965
- skip: selectedEntries.length === 0
3074
+ skip: !enableDraftRelationsCount
2966
3075
  }
2967
3076
  );
2968
3077
  React__namespace.useEffect(() => {
@@ -3147,7 +3256,7 @@ const SelectedEntriesTableContent = ({
3147
3256
  status: row.status
3148
3257
  }
3149
3258
  ) }),
3150
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
3259
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(
3151
3260
  designSystem.IconButton,
3152
3261
  {
3153
3262
  tag: reactRouterDom.Link,
@@ -3170,9 +3279,10 @@ const SelectedEntriesTableContent = ({
3170
3279
  ),
3171
3280
  target: "_blank",
3172
3281
  marginLeft: "auto",
3173
- children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {})
3282
+ variant: "ghost",
3283
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, { width: "1.6rem", height: "1.6rem" })
3174
3284
  }
3175
- ) })
3285
+ ) }) })
3176
3286
  ] }, row.id)) })
3177
3287
  ] });
3178
3288
  };
@@ -3209,7 +3319,13 @@ const SelectedEntriesModalContent = ({
3209
3319
  );
3210
3320
  const { rows, validationErrors } = React__namespace.useMemo(() => {
3211
3321
  if (data.length > 0 && schema) {
3212
- const validate = createYupSchema(schema.attributes, components);
3322
+ const validate = createYupSchema(
3323
+ schema.attributes,
3324
+ components,
3325
+ // Since this is the "Publish" action, the validation
3326
+ // schema must enforce the rules for published entities
3327
+ { status: "published" }
3328
+ );
3213
3329
  const validationErrors2 = {};
3214
3330
  const rows2 = data.map((entry) => {
3215
3331
  try {
@@ -3559,7 +3675,7 @@ const TableActions = ({ document }) => {
3559
3675
  strapiAdmin.DescriptionComponentRenderer,
3560
3676
  {
3561
3677
  props,
3562
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3678
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3563
3679
  children: (actions2) => {
3564
3680
  const tableRowActions = actions2.filter((action) => {
3565
3681
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3670,7 +3786,7 @@ const CloneAction = ({ model, documentId }) => {
3670
3786
  }),
3671
3787
  content: /* @__PURE__ */ jsxRuntime.jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3672
3788
  footer: ({ onClose }) => {
3673
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
3789
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
3674
3790
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3675
3791
  id: "cancel",
3676
3792
  defaultMessage: "Cancel"
@@ -3901,7 +4017,7 @@ const index = {
3901
4017
  app.router.addRoute({
3902
4018
  path: "content-manager/*",
3903
4019
  lazy: async () => {
3904
- const { Layout } = await Promise.resolve().then(() => require("./layout-C6dxWYT7.js"));
4020
+ const { Layout } = await Promise.resolve().then(() => require("./layout-CxxkX9jY.js"));
3905
4021
  return {
3906
4022
  Component: Layout
3907
4023
  };
@@ -3918,7 +4034,7 @@ const index = {
3918
4034
  async registerTrads({ locales }) {
3919
4035
  const importedTrads = await Promise.all(
3920
4036
  locales.map((locale) => {
3921
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => Promise.resolve().then(() => require("./ar-BUUWXIYu.js")), "./translations/ca.json": () => Promise.resolve().then(() => require("./ca-Cmk45QO6.js")), "./translations/cs.json": () => Promise.resolve().then(() => require("./cs-CkJy6B2v.js")), "./translations/de.json": () => Promise.resolve().then(() => require("./de-CCEmbAah.js")), "./translations/en.json": () => Promise.resolve().then(() => require("./en-uOUIxfcQ.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-EUonQTon.js")), "./translations/eu.json": () => Promise.resolve().then(() => require("./eu-VDH-3ovk.js")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-B7kGGg3E.js")), "./translations/gu.json": () => Promise.resolve().then(() => require("./gu-BRmF601H.js")), "./translations/hi.json": () => Promise.resolve().then(() => require("./hi-CCJBptSq.js")), "./translations/hu.json": () => Promise.resolve().then(() => require("./hu-sNV_yLYy.js")), "./translations/id.json": () => Promise.resolve().then(() => require("./id-B5Ser98A.js")), "./translations/it.json": () => Promise.resolve().then(() => require("./it-DkBIs7vD.js")), "./translations/ja.json": () => Promise.resolve().then(() => require("./ja-CcFe8diO.js")), "./translations/ko.json": () => Promise.resolve().then(() => require("./ko-woFZPmLk.js")), "./translations/ml.json": () => Promise.resolve().then(() => require("./ml-C2W8N8k1.js")), "./translations/ms.json": () => Promise.resolve().then(() => require("./ms-BuFotyP_.js")), "./translations/nl.json": () => Promise.resolve().then(() => require("./nl-bbEOHChV.js")), "./translations/pl.json": () => Promise.resolve().then(() => require("./pl-uzwG-hk7.js")), "./translations/pt-BR.json": () => Promise.resolve().then(() => require("./pt-BR-BiOz37D9.js")), "./translations/pt.json": () => Promise.resolve().then(() => require("./pt-CeXQuq50.js")), "./translations/ru.json": () => Promise.resolve().then(() => require("./ru-BT3ybNny.js")), "./translations/sa.json": () => Promise.resolve().then(() => require("./sa-CcvkYInH.js")), "./translations/sk.json": () => Promise.resolve().then(() => require("./sk-CvY09Xjv.js")), "./translations/sv.json": () => Promise.resolve().then(() => require("./sv-MYDuzgvT.js")), "./translations/th.json": () => Promise.resolve().then(() => require("./th-D9_GfAjc.js")), "./translations/tr.json": () => Promise.resolve().then(() => require("./tr-D9UH-O_R.js")), "./translations/uk.json": () => Promise.resolve().then(() => require("./uk-C8EiqJY7.js")), "./translations/vi.json": () => Promise.resolve().then(() => require("./vi-CJlYDheJ.js")), "./translations/zh-Hans.json": () => Promise.resolve().then(() => require("./zh-Hans-9kOncHGw.js")), "./translations/zh.json": () => Promise.resolve().then(() => require("./zh-CQQfszqR.js")) }), `./translations/${locale}.json`).then(({ default: data }) => {
4037
+ 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 }) => {
3922
4038
  return {
3923
4039
  data: prefixPluginTranslations(data, PLUGIN_ID),
3924
4040
  locale
@@ -3936,6 +4052,7 @@ const index = {
3936
4052
  };
3937
4053
  exports.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD;
3938
4054
  exports.BulkActionsRenderer = BulkActionsRenderer;
4055
+ exports.CLONE_PATH = CLONE_PATH;
3939
4056
  exports.COLLECTION_TYPES = COLLECTION_TYPES;
3940
4057
  exports.CREATOR_FIELDS = CREATOR_FIELDS;
3941
4058
  exports.DEFAULT_SETTINGS = DEFAULT_SETTINGS;
@@ -3963,6 +4080,7 @@ exports.getMainField = getMainField;
3963
4080
  exports.getTranslation = getTranslation;
3964
4081
  exports.index = index;
3965
4082
  exports.setInitialData = setInitialData;
4083
+ exports.useContentManagerContext = useContentManagerContext;
3966
4084
  exports.useContentTypeSchema = useContentTypeSchema;
3967
4085
  exports.useDoc = useDoc;
3968
4086
  exports.useDocLayout = useDocLayout;
@@ -3975,4 +4093,4 @@ exports.useGetAllDocumentsQuery = useGetAllDocumentsQuery;
3975
4093
  exports.useGetContentTypeConfigurationQuery = useGetContentTypeConfigurationQuery;
3976
4094
  exports.useGetInitialDataQuery = useGetInitialDataQuery;
3977
4095
  exports.useUpdateContentTypeConfigurationMutation = useUpdateContentTypeConfigurationMutation;
3978
- //# sourceMappingURL=index-CdT0kHZ8.js.map
4096
+ //# sourceMappingURL=index-CBX6KyXv.js.map