@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
@@ -1,16 +1,16 @@
1
- import { CrossCircle, More, WarningCircle, ListPlus, Pencil, Trash, Check, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
1
+ import { More, Cross, WarningCircle, ListPlus, Pencil, Trash, Check, CrossCircle, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
2
2
  import { jsx, Fragment, jsxs } from "react/jsx-runtime";
3
- import { useStrapiApp, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useQueryParams, useTracking, useForm, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
3
+ import { useStrapiApp, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, useQueryParams, getYupValidationErrors, useForm, useTracking, useGuidedTour, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
4
4
  import * as React from "react";
5
5
  import { lazy } from "react";
6
- import { Menu, Button, VisuallyHidden, Flex, Typography, Dialog, Modal, Radio, Status, Box, SingleSelect, SingleSelectOption, Loader, IconButton, Tooltip, LinkButton } from "@strapi/design-system";
6
+ import { Button, Menu, VisuallyHidden, Flex, Typography, Dialog, Modal, Radio, Status, Box, SingleSelect, SingleSelectOption, IconButton, Loader, Tooltip, LinkButton } from "@strapi/design-system";
7
7
  import { useIntl } from "react-intl";
8
- import { useParams, Navigate, useNavigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
9
- import { styled } from "styled-components";
8
+ import { useParams, useNavigate, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
10
9
  import * as yup from "yup";
11
10
  import { ValidationError } from "yup";
12
11
  import pipe from "lodash/fp/pipe";
13
12
  import { intervalToDuration, isPast } from "date-fns";
13
+ import { styled } from "styled-components";
14
14
  import { stringify } from "qs";
15
15
  import { createSlice, combineReducers } from "@reduxjs/toolkit";
16
16
  const __variableDynamicImportRuntimeHelper = (glob, path) => {
@@ -158,7 +158,8 @@ const contentManagerApi = adminApi.enhanceEndpoints({
158
158
  "Document",
159
159
  "InitialData",
160
160
  "HistoryVersion",
161
- "Relations"
161
+ "Relations",
162
+ "UidAvailability"
162
163
  ]
163
164
  });
164
165
  const documentApi = contentManagerApi.injectEndpoints({
@@ -188,7 +189,10 @@ const documentApi = contentManagerApi.injectEndpoints({
188
189
  params
189
190
  }
190
191
  }),
191
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
192
+ invalidatesTags: (_result, _error, { model }) => [
193
+ { type: "Document", id: `${model}_LIST` },
194
+ { type: "UidAvailability", id: model }
195
+ ]
192
196
  }),
193
197
  /**
194
198
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -205,7 +209,8 @@ const documentApi = contentManagerApi.injectEndpoints({
205
209
  }),
206
210
  invalidatesTags: (result, _error, { model }) => [
207
211
  { type: "Document", id: `${model}_LIST` },
208
- "Relations"
212
+ "Relations",
213
+ { type: "UidAvailability", id: model }
209
214
  ]
210
215
  }),
211
216
  deleteDocument: builder.mutation({
@@ -246,7 +251,8 @@ const documentApi = contentManagerApi.injectEndpoints({
246
251
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
247
252
  },
248
253
  { type: "Document", id: `${model}_LIST` },
249
- "Relations"
254
+ "Relations",
255
+ { type: "UidAvailability", id: model }
250
256
  ];
251
257
  }
252
258
  }),
@@ -371,7 +377,8 @@ const documentApi = contentManagerApi.injectEndpoints({
371
377
  type: "Document",
372
378
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
373
379
  },
374
- "Relations"
380
+ "Relations",
381
+ { type: "UidAvailability", id: model }
375
382
  ];
376
383
  },
377
384
  async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
@@ -454,7 +461,7 @@ const buildValidParams = (query) => {
454
461
  const isBaseQueryError = (error) => {
455
462
  return error.name !== void 0;
456
463
  };
457
- const createYupSchema = (attributes = {}, components = {}) => {
464
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
458
465
  const createModelSchema = (attributes2) => yup.object().shape(
459
466
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
460
467
  if (DOCUMENT_META_FIELDS.includes(name)) {
@@ -467,7 +474,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
467
474
  addMinValidation,
468
475
  addMaxValidation,
469
476
  addRegexValidation
470
- ].map((fn) => fn(attribute));
477
+ ].map((fn) => fn(attribute, options));
471
478
  const transformSchema = pipe(...validations);
472
479
  switch (attribute.type) {
473
480
  case "component": {
@@ -594,13 +601,7 @@ const createAttributeSchema = (attribute) => {
594
601
  return yup.mixed();
595
602
  }
596
603
  };
597
- const addRequiredValidation = (attribute) => (schema) => {
598
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
599
- return schema.min(1, translatedErrors.required);
600
- }
601
- if (attribute.required && attribute.type !== "relation") {
602
- return schema.required(translatedErrors.required);
603
- }
604
+ const nullableSchema = (schema) => {
604
605
  return schema?.nullable ? schema.nullable() : (
605
606
  // In some cases '.nullable' will not be available on the schema.
606
607
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -608,7 +609,22 @@ const addRequiredValidation = (attribute) => (schema) => {
608
609
  schema
609
610
  );
610
611
  };
611
- const addMinLengthValidation = (attribute) => (schema) => {
612
+ const addRequiredValidation = (attribute, options) => (schema) => {
613
+ if (options.status === "draft") {
614
+ return nullableSchema(schema);
615
+ }
616
+ if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
617
+ return schema.min(1, translatedErrors.required);
618
+ }
619
+ if (attribute.required && attribute.type !== "relation") {
620
+ return schema.required(translatedErrors.required);
621
+ }
622
+ return nullableSchema(schema);
623
+ };
624
+ const addMinLengthValidation = (attribute, options) => (schema) => {
625
+ if (options.status === "draft") {
626
+ return schema;
627
+ }
612
628
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
613
629
  return schema.min(attribute.minLength, {
614
630
  ...translatedErrors.minLength,
@@ -630,11 +646,11 @@ const addMaxLengthValidation = (attribute) => (schema) => {
630
646
  }
631
647
  return schema;
632
648
  };
633
- const addMinValidation = (attribute) => (schema) => {
649
+ const addMinValidation = (attribute, options) => (schema) => {
634
650
  if ("min" in attribute) {
635
651
  const min = toInteger(attribute.min);
636
652
  if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
637
- if (!attribute.required && "test" in schema && min) {
653
+ if (options.status !== "draft" && !attribute.required && "test" in schema && min) {
638
654
  return schema.test(
639
655
  "custom-min",
640
656
  {
@@ -773,19 +789,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
773
789
  }, {});
774
790
  return componentsByKey;
775
791
  };
776
- const useDocument = (args, opts) => {
792
+ const HOOKS = {
793
+ /**
794
+ * Hook that allows to mutate the displayed headers of the list view table
795
+ * @constant
796
+ * @type {string}
797
+ */
798
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
799
+ /**
800
+ * Hook that allows to mutate the CM's collection types links pre-set filters
801
+ * @constant
802
+ * @type {string}
803
+ */
804
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
805
+ /**
806
+ * Hook that allows to mutate the CM's edit view layout
807
+ * @constant
808
+ * @type {string}
809
+ */
810
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
811
+ /**
812
+ * Hook that allows to mutate the CM's single types links pre-set filters
813
+ * @constant
814
+ * @type {string}
815
+ */
816
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
817
+ };
818
+ const contentTypesApi = contentManagerApi.injectEndpoints({
819
+ endpoints: (builder) => ({
820
+ getContentTypeConfiguration: builder.query({
821
+ query: (uid) => ({
822
+ url: `/content-manager/content-types/${uid}/configuration`,
823
+ method: "GET"
824
+ }),
825
+ transformResponse: (response) => response.data,
826
+ providesTags: (_result, _error, uid) => [
827
+ { type: "ContentTypesConfiguration", id: uid },
828
+ { type: "ContentTypeSettings", id: "LIST" }
829
+ ]
830
+ }),
831
+ getAllContentTypeSettings: builder.query({
832
+ query: () => "/content-manager/content-types-settings",
833
+ transformResponse: (response) => response.data,
834
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
835
+ }),
836
+ updateContentTypeConfiguration: builder.mutation({
837
+ query: ({ uid, ...body }) => ({
838
+ url: `/content-manager/content-types/${uid}/configuration`,
839
+ method: "PUT",
840
+ data: body
841
+ }),
842
+ transformResponse: (response) => response.data,
843
+ invalidatesTags: (_result, _error, { uid }) => [
844
+ { type: "ContentTypesConfiguration", id: uid },
845
+ { type: "ContentTypeSettings", id: "LIST" },
846
+ // Is this necessary?
847
+ { type: "InitialData" }
848
+ ]
849
+ })
850
+ })
851
+ });
852
+ const {
853
+ useGetContentTypeConfigurationQuery,
854
+ useGetAllContentTypeSettingsQuery,
855
+ useUpdateContentTypeConfigurationMutation
856
+ } = contentTypesApi;
857
+ const checkIfAttributeIsDisplayable = (attribute) => {
858
+ const { type } = attribute;
859
+ if (type === "relation") {
860
+ return !attribute.relation.toLowerCase().includes("morph");
861
+ }
862
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
863
+ };
864
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
865
+ if (!mainFieldName) {
866
+ return void 0;
867
+ }
868
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
869
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
870
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
871
+ );
872
+ return {
873
+ name: mainFieldName,
874
+ type: mainFieldType ?? "string"
875
+ };
876
+ };
877
+ const DEFAULT_SETTINGS = {
878
+ bulkable: false,
879
+ filterable: false,
880
+ searchable: false,
881
+ pagination: false,
882
+ defaultSortBy: "",
883
+ defaultSortOrder: "asc",
884
+ mainField: "id",
885
+ pageSize: 10
886
+ };
887
+ const useDocumentLayout = (model) => {
888
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
889
+ const [{ query }] = useQueryParams();
890
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
777
891
  const { toggleNotification } = useNotification();
778
892
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
893
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
779
894
  const {
780
- currentData: data,
781
- isLoading: isLoadingDocument,
782
- isFetching: isFetchingDocument,
783
- error
784
- } = useGetDocumentQuery(args, {
785
- ...opts,
786
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
787
- });
788
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
895
+ data,
896
+ isLoading: isLoadingConfigs,
897
+ error,
898
+ isFetching: isFetchingConfigs
899
+ } = useGetContentTypeConfigurationQuery(model);
900
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
789
901
  React.useEffect(() => {
790
902
  if (error) {
791
903
  toggleNotification({
@@ -793,395 +905,435 @@ const useDocument = (args, opts) => {
793
905
  message: formatAPIError(error)
794
906
  });
795
907
  }
796
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
797
- const validationSchema = React.useMemo(() => {
798
- if (!schema) {
799
- return null;
800
- }
801
- return createYupSchema(schema.attributes, components);
802
- }, [schema, components]);
803
- const validate = React.useCallback(
804
- (document) => {
805
- if (!validationSchema) {
806
- throw new Error(
807
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
808
- );
809
- }
810
- try {
811
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
812
- return null;
813
- } catch (error2) {
814
- if (error2 instanceof ValidationError) {
815
- return getYupValidationErrors(error2);
816
- }
817
- throw error2;
818
- }
908
+ }, [error, formatAPIError, toggleNotification]);
909
+ const editLayout = React.useMemo(
910
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
911
+ layout: [],
912
+ components: {},
913
+ metadatas: {},
914
+ options: {},
915
+ settings: DEFAULT_SETTINGS
819
916
  },
820
- [validationSchema]
917
+ [data, isLoading, schemas, schema, components]
918
+ );
919
+ const listLayout = React.useMemo(() => {
920
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
921
+ layout: [],
922
+ metadatas: {},
923
+ options: {},
924
+ settings: DEFAULT_SETTINGS
925
+ };
926
+ }, [data, isLoading, schemas, schema, components]);
927
+ const { layout: edit } = React.useMemo(
928
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
929
+ layout: editLayout,
930
+ query
931
+ }),
932
+ [editLayout, query, runHookWaterfall]
821
933
  );
822
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
823
934
  return {
824
- components,
825
- document: data?.data,
826
- meta: data?.meta,
935
+ error,
827
936
  isLoading,
828
- schema,
829
- validate
830
- };
831
- };
832
- const useDoc = () => {
833
- const { id, slug, collectionType, origin } = useParams();
834
- const [{ query }] = useQueryParams();
835
- const params = React.useMemo(() => buildValidParams(query), [query]);
836
- if (!collectionType) {
837
- throw new Error("Could not find collectionType in url params");
838
- }
839
- if (!slug) {
840
- throw new Error("Could not find model in url params");
841
- }
842
- return {
843
- collectionType,
844
- model: slug,
845
- id: origin || id === "create" ? void 0 : id,
846
- ...useDocument(
847
- { documentId: origin || id, model: slug, collectionType, params },
848
- {
849
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
850
- }
851
- )
937
+ edit,
938
+ list: listLayout
852
939
  };
853
940
  };
854
- const prefixPluginTranslations = (trad, pluginId) => {
855
- if (!pluginId) {
856
- throw new TypeError("pluginId can't be empty");
857
- }
858
- return Object.keys(trad).reduce((acc, current) => {
859
- acc[`${pluginId}.${current}`] = trad[current];
860
- return acc;
861
- }, {});
862
- };
863
- const getTranslation = (id) => `content-manager.${id}`;
864
- const DEFAULT_UNEXPECTED_ERROR_MSG = {
865
- id: "notification.error",
866
- defaultMessage: "An error occurred, please try again"
941
+ const useDocLayout = () => {
942
+ const { model } = useDoc();
943
+ return useDocumentLayout(model);
867
944
  };
868
- const useDocumentActions = () => {
869
- const { toggleNotification } = useNotification();
870
- const { formatMessage } = useIntl();
871
- const { trackUsage } = useTracking();
872
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
873
- const [deleteDocument] = useDeleteDocumentMutation();
874
- const _delete = React.useCallback(
875
- async ({ collectionType, model, documentId, params }, trackerProperty) => {
876
- try {
877
- trackUsage("willDeleteEntry", trackerProperty);
878
- const res = await deleteDocument({
879
- collectionType,
880
- model,
881
- documentId,
882
- params
883
- });
884
- if ("error" in res) {
885
- toggleNotification({
886
- type: "danger",
887
- message: formatAPIError(res.error)
888
- });
889
- return { error: res.error };
890
- }
891
- toggleNotification({
892
- type: "success",
893
- message: formatMessage({
894
- id: getTranslation("success.record.delete"),
895
- defaultMessage: "Deleted document"
896
- })
897
- });
898
- trackUsage("didDeleteEntry", trackerProperty);
899
- return res.data;
900
- } catch (err) {
901
- toggleNotification({
902
- type: "danger",
903
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
904
- });
905
- trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
906
- throw err;
945
+ const formatEditLayout = (data, {
946
+ schemas,
947
+ schema,
948
+ components
949
+ }) => {
950
+ let currentPanelIndex = 0;
951
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
952
+ data.contentType.layouts.edit,
953
+ schema?.attributes,
954
+ data.contentType.metadatas,
955
+ { configurations: data.components, schemas: components },
956
+ schemas
957
+ ).reduce((panels, row) => {
958
+ if (row.some((field) => field.type === "dynamiczone")) {
959
+ panels.push([row]);
960
+ currentPanelIndex += 2;
961
+ } else {
962
+ if (!panels[currentPanelIndex]) {
963
+ panels.push([]);
907
964
  }
908
- },
909
- [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
910
- );
911
- const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
912
- const deleteMany = React.useCallback(
913
- async ({ model, documentIds, params }) => {
914
- try {
915
- trackUsage("willBulkDeleteEntries");
916
- const res = await deleteManyDocuments({
917
- model,
918
- documentIds,
919
- params
920
- });
921
- if ("error" in res) {
922
- toggleNotification({
923
- type: "danger",
924
- message: formatAPIError(res.error)
925
- });
926
- return { error: res.error };
965
+ panels[currentPanelIndex].push(row);
966
+ }
967
+ return panels;
968
+ }, []);
969
+ const componentEditAttributes = Object.entries(data.components).reduce(
970
+ (acc, [uid, configuration]) => {
971
+ acc[uid] = {
972
+ layout: convertEditLayoutToFieldLayouts(
973
+ configuration.layouts.edit,
974
+ components[uid].attributes,
975
+ configuration.metadatas,
976
+ { configurations: data.components, schemas: components }
977
+ ),
978
+ settings: {
979
+ ...configuration.settings,
980
+ icon: components[uid].info.icon,
981
+ displayName: components[uid].info.displayName
927
982
  }
928
- toggleNotification({
929
- type: "success",
930
- title: formatMessage({
931
- id: getTranslation("success.records.delete"),
932
- defaultMessage: "Successfully deleted."
933
- }),
934
- message: ""
935
- });
936
- trackUsage("didBulkDeleteEntries");
937
- return res.data;
938
- } catch (err) {
939
- toggleNotification({
940
- type: "danger",
941
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
942
- });
943
- trackUsage("didNotBulkDeleteEntries");
944
- throw err;
945
- }
983
+ };
984
+ return acc;
946
985
  },
947
- [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
986
+ {}
948
987
  );
949
- const [discardDocument] = useDiscardDocumentMutation();
950
- const discard = React.useCallback(
951
- async ({ collectionType, model, documentId, params }) => {
952
- try {
953
- const res = await discardDocument({
954
- collectionType,
955
- model,
956
- documentId,
957
- params
958
- });
959
- if ("error" in res) {
960
- toggleNotification({
961
- type: "danger",
962
- message: formatAPIError(res.error)
963
- });
964
- return { error: res.error };
965
- }
966
- toggleNotification({
967
- type: "success",
968
- message: formatMessage({
969
- id: "content-manager.success.record.discard",
970
- defaultMessage: "Changes discarded"
971
- })
972
- });
973
- return res.data;
974
- } catch (err) {
975
- toggleNotification({
976
- type: "danger",
977
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
978
- });
979
- throw err;
980
- }
988
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
989
+ (acc, [attribute, metadata]) => {
990
+ return {
991
+ ...acc,
992
+ [attribute]: metadata.edit
993
+ };
981
994
  },
982
- [discardDocument, formatAPIError, formatMessage, toggleNotification]
995
+ {}
983
996
  );
984
- const [publishDocument] = usePublishDocumentMutation();
985
- const publish = React.useCallback(
986
- async ({ collectionType, model, documentId, params }, data) => {
987
- try {
988
- trackUsage("willPublishEntry");
989
- const res = await publishDocument({
990
- collectionType,
991
- model,
992
- documentId,
993
- data,
994
- params
995
- });
996
- if ("error" in res) {
997
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
998
- return { error: res.error };
999
- }
1000
- trackUsage("didPublishEntry");
1001
- toggleNotification({
1002
- type: "success",
1003
- message: formatMessage({
1004
- id: getTranslation("success.record.publish"),
1005
- defaultMessage: "Published document"
1006
- })
1007
- });
1008
- return res.data;
1009
- } catch (err) {
1010
- toggleNotification({
1011
- type: "danger",
1012
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1013
- });
1014
- throw err;
1015
- }
997
+ return {
998
+ layout: panelledEditAttributes,
999
+ components: componentEditAttributes,
1000
+ metadatas: editMetadatas,
1001
+ settings: {
1002
+ ...data.contentType.settings,
1003
+ displayName: schema?.info.displayName
1016
1004
  },
1017
- [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1018
- );
1019
- const [publishManyDocuments] = usePublishManyDocumentsMutation();
1020
- const publishMany = React.useCallback(
1021
- async ({ model, documentIds, params }) => {
1022
- try {
1023
- const res = await publishManyDocuments({
1024
- model,
1025
- documentIds,
1026
- params
1027
- });
1028
- if ("error" in res) {
1029
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1030
- return { error: res.error };
1031
- }
1032
- toggleNotification({
1033
- type: "success",
1034
- message: formatMessage({
1035
- id: getTranslation("success.record.publish"),
1036
- defaultMessage: "Published document"
1037
- })
1038
- });
1039
- return res.data;
1040
- } catch (err) {
1041
- toggleNotification({
1042
- type: "danger",
1043
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1044
- });
1045
- throw err;
1005
+ options: {
1006
+ ...schema?.options,
1007
+ ...schema?.pluginOptions,
1008
+ ...data.contentType.options
1009
+ }
1010
+ };
1011
+ };
1012
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1013
+ return rows.map(
1014
+ (row) => row.map((field) => {
1015
+ const attribute = attributes[field.name];
1016
+ if (!attribute) {
1017
+ return null;
1046
1018
  }
1019
+ const { edit: metadata } = metadatas[field.name];
1020
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1021
+ return {
1022
+ attribute,
1023
+ disabled: !metadata.editable,
1024
+ hint: metadata.description,
1025
+ label: metadata.label ?? "",
1026
+ name: field.name,
1027
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1028
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1029
+ schemas,
1030
+ components: components?.schemas ?? {}
1031
+ }),
1032
+ placeholder: metadata.placeholder ?? "",
1033
+ required: attribute.required ?? false,
1034
+ size: field.size,
1035
+ unique: "unique" in attribute ? attribute.unique : false,
1036
+ visible: metadata.visible ?? true,
1037
+ type: attribute.type
1038
+ };
1039
+ }).filter((field) => field !== null)
1040
+ );
1041
+ };
1042
+ const formatListLayout = (data, {
1043
+ schemas,
1044
+ schema,
1045
+ components
1046
+ }) => {
1047
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1048
+ (acc, [attribute, metadata]) => {
1049
+ return {
1050
+ ...acc,
1051
+ [attribute]: metadata.list
1052
+ };
1047
1053
  },
1048
- [
1049
- // trackUsage,
1050
- publishManyDocuments,
1051
- toggleNotification,
1052
- formatMessage,
1053
- formatAPIError
1054
- ]
1054
+ {}
1055
1055
  );
1056
- const [updateDocument] = useUpdateDocumentMutation();
1057
- const update = React.useCallback(
1058
- async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1059
- try {
1060
- trackUsage("willEditEntry", trackerProperty);
1061
- const res = await updateDocument({
1062
- collectionType,
1063
- model,
1064
- documentId,
1065
- data,
1066
- params
1067
- });
1068
- if ("error" in res) {
1069
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1070
- trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1071
- return { error: res.error };
1056
+ const listAttributes = convertListLayoutToFieldLayouts(
1057
+ data.contentType.layouts.list,
1058
+ schema?.attributes,
1059
+ listMetadatas,
1060
+ { configurations: data.components, schemas: components },
1061
+ schemas
1062
+ );
1063
+ return {
1064
+ layout: listAttributes,
1065
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1066
+ metadatas: listMetadatas,
1067
+ options: {
1068
+ ...schema?.options,
1069
+ ...schema?.pluginOptions,
1070
+ ...data.contentType.options
1071
+ }
1072
+ };
1073
+ };
1074
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1075
+ return columns.map((name) => {
1076
+ const attribute = attributes[name];
1077
+ if (!attribute) {
1078
+ return null;
1079
+ }
1080
+ const metadata = metadatas[name];
1081
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1082
+ return {
1083
+ attribute,
1084
+ label: metadata.label ?? "",
1085
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1086
+ schemas,
1087
+ components: components?.schemas ?? {}
1088
+ }),
1089
+ name,
1090
+ searchable: metadata.searchable ?? true,
1091
+ sortable: metadata.sortable ?? true
1092
+ };
1093
+ }).filter((field) => field !== null);
1094
+ };
1095
+ const useDocument = (args, opts) => {
1096
+ const { toggleNotification } = useNotification();
1097
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1098
+ const {
1099
+ currentData: data,
1100
+ isLoading: isLoadingDocument,
1101
+ isFetching: isFetchingDocument,
1102
+ error
1103
+ } = useGetDocumentQuery(args, {
1104
+ ...opts,
1105
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1106
+ });
1107
+ const {
1108
+ components,
1109
+ schema,
1110
+ schemas,
1111
+ isLoading: isLoadingSchema
1112
+ } = useContentTypeSchema(args.model);
1113
+ React.useEffect(() => {
1114
+ if (error) {
1115
+ toggleNotification({
1116
+ type: "danger",
1117
+ message: formatAPIError(error)
1118
+ });
1119
+ }
1120
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1121
+ const validationSchema = React.useMemo(() => {
1122
+ if (!schema) {
1123
+ return null;
1124
+ }
1125
+ return createYupSchema(schema.attributes, components);
1126
+ }, [schema, components]);
1127
+ const validate = React.useCallback(
1128
+ (document) => {
1129
+ if (!validationSchema) {
1130
+ throw new Error(
1131
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1132
+ );
1133
+ }
1134
+ try {
1135
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1136
+ return null;
1137
+ } catch (error2) {
1138
+ if (error2 instanceof ValidationError) {
1139
+ return getYupValidationErrors(error2);
1072
1140
  }
1073
- trackUsage("didEditEntry", trackerProperty);
1074
- toggleNotification({
1075
- type: "success",
1076
- message: formatMessage({
1077
- id: getTranslation("success.record.save"),
1078
- defaultMessage: "Saved document"
1079
- })
1080
- });
1081
- return res.data;
1082
- } catch (err) {
1083
- trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1084
- toggleNotification({
1085
- type: "danger",
1086
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1087
- });
1088
- throw err;
1141
+ throw error2;
1089
1142
  }
1090
1143
  },
1091
- [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1144
+ [validationSchema]
1092
1145
  );
1093
- const [unpublishDocument] = useUnpublishDocumentMutation();
1094
- const unpublish = React.useCallback(
1095
- async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1146
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1147
+ return {
1148
+ components,
1149
+ document: data?.data,
1150
+ meta: data?.meta,
1151
+ isLoading,
1152
+ schema,
1153
+ schemas,
1154
+ validate
1155
+ };
1156
+ };
1157
+ const useDoc = () => {
1158
+ const { id, slug, collectionType, origin } = useParams();
1159
+ const [{ query }] = useQueryParams();
1160
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1161
+ if (!collectionType) {
1162
+ throw new Error("Could not find collectionType in url params");
1163
+ }
1164
+ if (!slug) {
1165
+ throw new Error("Could not find model in url params");
1166
+ }
1167
+ return {
1168
+ collectionType,
1169
+ model: slug,
1170
+ id: origin || id === "create" ? void 0 : id,
1171
+ ...useDocument(
1172
+ { documentId: origin || id, model: slug, collectionType, params },
1173
+ {
1174
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1175
+ }
1176
+ )
1177
+ };
1178
+ };
1179
+ const useContentManagerContext = () => {
1180
+ const {
1181
+ collectionType,
1182
+ model,
1183
+ id,
1184
+ components,
1185
+ isLoading: isLoadingDoc,
1186
+ schema,
1187
+ schemas
1188
+ } = useDoc();
1189
+ const layout = useDocumentLayout(model);
1190
+ const form = useForm("useContentManagerContext", (state) => state);
1191
+ const isSingleType = collectionType === SINGLE_TYPES;
1192
+ const slug = model;
1193
+ const isCreatingEntry = id === "create";
1194
+ useContentTypeSchema();
1195
+ const isLoading = isLoadingDoc || layout.isLoading;
1196
+ const error = layout.error;
1197
+ return {
1198
+ error,
1199
+ isLoading,
1200
+ // Base metadata
1201
+ model,
1202
+ collectionType,
1203
+ id,
1204
+ slug,
1205
+ isCreatingEntry,
1206
+ isSingleType,
1207
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1208
+ // All schema infos
1209
+ components,
1210
+ contentType: schema,
1211
+ contentTypes: schemas,
1212
+ // Form state
1213
+ form,
1214
+ // layout infos
1215
+ layout
1216
+ };
1217
+ };
1218
+ const prefixPluginTranslations = (trad, pluginId) => {
1219
+ if (!pluginId) {
1220
+ throw new TypeError("pluginId can't be empty");
1221
+ }
1222
+ return Object.keys(trad).reduce((acc, current) => {
1223
+ acc[`${pluginId}.${current}`] = trad[current];
1224
+ return acc;
1225
+ }, {});
1226
+ };
1227
+ const getTranslation = (id) => `content-manager.${id}`;
1228
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1229
+ id: "notification.error",
1230
+ defaultMessage: "An error occurred, please try again"
1231
+ };
1232
+ const useDocumentActions = () => {
1233
+ const { toggleNotification } = useNotification();
1234
+ const { formatMessage } = useIntl();
1235
+ const { trackUsage } = useTracking();
1236
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1237
+ const navigate = useNavigate();
1238
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
1239
+ const [deleteDocument] = useDeleteDocumentMutation();
1240
+ const _delete = React.useCallback(
1241
+ async ({ collectionType, model, documentId, params }, trackerProperty) => {
1096
1242
  try {
1097
- trackUsage("willUnpublishEntry");
1098
- const res = await unpublishDocument({
1243
+ trackUsage("willDeleteEntry", trackerProperty);
1244
+ const res = await deleteDocument({
1099
1245
  collectionType,
1100
1246
  model,
1101
1247
  documentId,
1102
- params,
1103
- data: {
1104
- discardDraft
1105
- }
1248
+ params
1106
1249
  });
1107
1250
  if ("error" in res) {
1108
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1251
+ toggleNotification({
1252
+ type: "danger",
1253
+ message: formatAPIError(res.error)
1254
+ });
1109
1255
  return { error: res.error };
1110
1256
  }
1111
- trackUsage("didUnpublishEntry");
1112
1257
  toggleNotification({
1113
1258
  type: "success",
1114
1259
  message: formatMessage({
1115
- id: getTranslation("success.record.unpublish"),
1116
- defaultMessage: "Unpublished document"
1260
+ id: getTranslation("success.record.delete"),
1261
+ defaultMessage: "Deleted document"
1117
1262
  })
1118
1263
  });
1264
+ trackUsage("didDeleteEntry", trackerProperty);
1119
1265
  return res.data;
1120
1266
  } catch (err) {
1121
1267
  toggleNotification({
1122
1268
  type: "danger",
1123
1269
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1124
1270
  });
1271
+ trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
1125
1272
  throw err;
1126
1273
  }
1127
1274
  },
1128
- [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1275
+ [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
1129
1276
  );
1130
- const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1131
- const unpublishMany = React.useCallback(
1277
+ const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
1278
+ const deleteMany = React.useCallback(
1132
1279
  async ({ model, documentIds, params }) => {
1133
1280
  try {
1134
- trackUsage("willBulkUnpublishEntries");
1135
- const res = await unpublishManyDocuments({
1281
+ trackUsage("willBulkDeleteEntries");
1282
+ const res = await deleteManyDocuments({
1136
1283
  model,
1137
1284
  documentIds,
1138
1285
  params
1139
1286
  });
1140
1287
  if ("error" in res) {
1141
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1288
+ toggleNotification({
1289
+ type: "danger",
1290
+ message: formatAPIError(res.error)
1291
+ });
1142
1292
  return { error: res.error };
1143
1293
  }
1144
- trackUsage("didBulkUnpublishEntries");
1145
1294
  toggleNotification({
1146
1295
  type: "success",
1147
1296
  title: formatMessage({
1148
- id: getTranslation("success.records.unpublish"),
1149
- defaultMessage: "Successfully unpublished."
1297
+ id: getTranslation("success.records.delete"),
1298
+ defaultMessage: "Successfully deleted."
1150
1299
  }),
1151
1300
  message: ""
1152
1301
  });
1302
+ trackUsage("didBulkDeleteEntries");
1153
1303
  return res.data;
1154
1304
  } catch (err) {
1155
1305
  toggleNotification({
1156
1306
  type: "danger",
1157
1307
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1158
1308
  });
1159
- trackUsage("didNotBulkUnpublishEntries");
1309
+ trackUsage("didNotBulkDeleteEntries");
1160
1310
  throw err;
1161
1311
  }
1162
1312
  },
1163
- [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1313
+ [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1164
1314
  );
1165
- const [createDocument] = useCreateDocumentMutation();
1166
- const create = React.useCallback(
1167
- async ({ model, params }, data, trackerProperty) => {
1315
+ const [discardDocument] = useDiscardDocumentMutation();
1316
+ const discard = React.useCallback(
1317
+ async ({ collectionType, model, documentId, params }) => {
1168
1318
  try {
1169
- const res = await createDocument({
1319
+ const res = await discardDocument({
1320
+ collectionType,
1170
1321
  model,
1171
- data,
1322
+ documentId,
1172
1323
  params
1173
1324
  });
1174
1325
  if ("error" in res) {
1175
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1176
- trackUsage("didNotCreateEntry", { error: res.error, ...trackerProperty });
1326
+ toggleNotification({
1327
+ type: "danger",
1328
+ message: formatAPIError(res.error)
1329
+ });
1177
1330
  return { error: res.error };
1178
1331
  }
1179
- trackUsage("didCreateEntry", trackerProperty);
1180
1332
  toggleNotification({
1181
1333
  type: "success",
1182
1334
  message: formatMessage({
1183
- id: getTranslation("success.record.save"),
1184
- defaultMessage: "Saved document"
1335
+ id: "content-manager.success.record.discard",
1336
+ defaultMessage: "Changes discarded"
1185
1337
  })
1186
1338
  });
1187
1339
  return res.data;
@@ -1190,13 +1342,228 @@ const useDocumentActions = () => {
1190
1342
  type: "danger",
1191
1343
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1192
1344
  });
1193
- trackUsage("didNotCreateEntry", { error: err, ...trackerProperty });
1194
1345
  throw err;
1195
1346
  }
1196
1347
  },
1197
- [createDocument, formatAPIError, formatMessage, toggleNotification, trackUsage]
1348
+ [discardDocument, formatAPIError, formatMessage, toggleNotification]
1198
1349
  );
1199
- const [autoCloneDocument] = useAutoCloneDocumentMutation();
1350
+ const [publishDocument] = usePublishDocumentMutation();
1351
+ const publish = React.useCallback(
1352
+ async ({ collectionType, model, documentId, params }, data) => {
1353
+ try {
1354
+ trackUsage("willPublishEntry");
1355
+ const res = await publishDocument({
1356
+ collectionType,
1357
+ model,
1358
+ documentId,
1359
+ data,
1360
+ params
1361
+ });
1362
+ if ("error" in res) {
1363
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1364
+ return { error: res.error };
1365
+ }
1366
+ trackUsage("didPublishEntry");
1367
+ toggleNotification({
1368
+ type: "success",
1369
+ message: formatMessage({
1370
+ id: getTranslation("success.record.publish"),
1371
+ defaultMessage: "Published document"
1372
+ })
1373
+ });
1374
+ return res.data;
1375
+ } catch (err) {
1376
+ toggleNotification({
1377
+ type: "danger",
1378
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1379
+ });
1380
+ throw err;
1381
+ }
1382
+ },
1383
+ [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1384
+ );
1385
+ const [publishManyDocuments] = usePublishManyDocumentsMutation();
1386
+ const publishMany = React.useCallback(
1387
+ async ({ model, documentIds, params }) => {
1388
+ try {
1389
+ const res = await publishManyDocuments({
1390
+ model,
1391
+ documentIds,
1392
+ params
1393
+ });
1394
+ if ("error" in res) {
1395
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1396
+ return { error: res.error };
1397
+ }
1398
+ toggleNotification({
1399
+ type: "success",
1400
+ message: formatMessage({
1401
+ id: getTranslation("success.record.publish"),
1402
+ defaultMessage: "Published document"
1403
+ })
1404
+ });
1405
+ return res.data;
1406
+ } catch (err) {
1407
+ toggleNotification({
1408
+ type: "danger",
1409
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1410
+ });
1411
+ throw err;
1412
+ }
1413
+ },
1414
+ [
1415
+ // trackUsage,
1416
+ publishManyDocuments,
1417
+ toggleNotification,
1418
+ formatMessage,
1419
+ formatAPIError
1420
+ ]
1421
+ );
1422
+ const [updateDocument] = useUpdateDocumentMutation();
1423
+ const update = React.useCallback(
1424
+ async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1425
+ try {
1426
+ trackUsage("willEditEntry", trackerProperty);
1427
+ const res = await updateDocument({
1428
+ collectionType,
1429
+ model,
1430
+ documentId,
1431
+ data,
1432
+ params
1433
+ });
1434
+ if ("error" in res) {
1435
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1436
+ trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1437
+ return { error: res.error };
1438
+ }
1439
+ trackUsage("didEditEntry", trackerProperty);
1440
+ toggleNotification({
1441
+ type: "success",
1442
+ message: formatMessage({
1443
+ id: getTranslation("success.record.save"),
1444
+ defaultMessage: "Saved document"
1445
+ })
1446
+ });
1447
+ return res.data;
1448
+ } catch (err) {
1449
+ trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1450
+ toggleNotification({
1451
+ type: "danger",
1452
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1453
+ });
1454
+ throw err;
1455
+ }
1456
+ },
1457
+ [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1458
+ );
1459
+ const [unpublishDocument] = useUnpublishDocumentMutation();
1460
+ const unpublish = React.useCallback(
1461
+ async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1462
+ try {
1463
+ trackUsage("willUnpublishEntry");
1464
+ const res = await unpublishDocument({
1465
+ collectionType,
1466
+ model,
1467
+ documentId,
1468
+ params,
1469
+ data: {
1470
+ discardDraft
1471
+ }
1472
+ });
1473
+ if ("error" in res) {
1474
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1475
+ return { error: res.error };
1476
+ }
1477
+ trackUsage("didUnpublishEntry");
1478
+ toggleNotification({
1479
+ type: "success",
1480
+ message: formatMessage({
1481
+ id: getTranslation("success.record.unpublish"),
1482
+ defaultMessage: "Unpublished document"
1483
+ })
1484
+ });
1485
+ return res.data;
1486
+ } catch (err) {
1487
+ toggleNotification({
1488
+ type: "danger",
1489
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1490
+ });
1491
+ throw err;
1492
+ }
1493
+ },
1494
+ [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1495
+ );
1496
+ const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1497
+ const unpublishMany = React.useCallback(
1498
+ async ({ model, documentIds, params }) => {
1499
+ try {
1500
+ trackUsage("willBulkUnpublishEntries");
1501
+ const res = await unpublishManyDocuments({
1502
+ model,
1503
+ documentIds,
1504
+ params
1505
+ });
1506
+ if ("error" in res) {
1507
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1508
+ return { error: res.error };
1509
+ }
1510
+ trackUsage("didBulkUnpublishEntries");
1511
+ toggleNotification({
1512
+ type: "success",
1513
+ title: formatMessage({
1514
+ id: getTranslation("success.records.unpublish"),
1515
+ defaultMessage: "Successfully unpublished."
1516
+ }),
1517
+ message: ""
1518
+ });
1519
+ return res.data;
1520
+ } catch (err) {
1521
+ toggleNotification({
1522
+ type: "danger",
1523
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1524
+ });
1525
+ trackUsage("didNotBulkUnpublishEntries");
1526
+ throw err;
1527
+ }
1528
+ },
1529
+ [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1530
+ );
1531
+ const [createDocument] = useCreateDocumentMutation();
1532
+ const create = React.useCallback(
1533
+ async ({ model, params }, data, trackerProperty) => {
1534
+ try {
1535
+ const res = await createDocument({
1536
+ model,
1537
+ data,
1538
+ params
1539
+ });
1540
+ if ("error" in res) {
1541
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1542
+ trackUsage("didNotCreateEntry", { error: res.error, ...trackerProperty });
1543
+ return { error: res.error };
1544
+ }
1545
+ trackUsage("didCreateEntry", trackerProperty);
1546
+ toggleNotification({
1547
+ type: "success",
1548
+ message: formatMessage({
1549
+ id: getTranslation("success.record.save"),
1550
+ defaultMessage: "Saved document"
1551
+ })
1552
+ });
1553
+ setCurrentStep("contentManager.success");
1554
+ return res.data;
1555
+ } catch (err) {
1556
+ toggleNotification({
1557
+ type: "danger",
1558
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1559
+ });
1560
+ trackUsage("didNotCreateEntry", { error: err, ...trackerProperty });
1561
+ throw err;
1562
+ }
1563
+ },
1564
+ [createDocument, formatAPIError, formatMessage, toggleNotification, trackUsage]
1565
+ );
1566
+ const [autoCloneDocument] = useAutoCloneDocumentMutation();
1200
1567
  const autoClone = React.useCallback(
1201
1568
  async ({ model, sourceId }) => {
1202
1569
  try {
@@ -1223,7 +1590,7 @@ const useDocumentActions = () => {
1223
1590
  throw err;
1224
1591
  }
1225
1592
  },
1226
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1593
+ [autoCloneDocument, formatMessage, toggleNotification]
1227
1594
  );
1228
1595
  const [cloneDocument] = useCloneDocumentMutation();
1229
1596
  const clone = React.useCallback(
@@ -1249,6 +1616,7 @@ const useDocumentActions = () => {
1249
1616
  defaultMessage: "Cloned document"
1250
1617
  })
1251
1618
  });
1619
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1252
1620
  return res.data;
1253
1621
  } catch (err) {
1254
1622
  toggleNotification({
@@ -1259,7 +1627,7 @@ const useDocumentActions = () => {
1259
1627
  throw err;
1260
1628
  }
1261
1629
  },
1262
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1630
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1263
1631
  );
1264
1632
  const [getDoc] = useLazyGetDocumentQuery();
1265
1633
  const getDocument = React.useCallback(
@@ -1285,7 +1653,7 @@ const useDocumentActions = () => {
1285
1653
  };
1286
1654
  };
1287
1655
  const ProtectedHistoryPage = lazy(
1288
- () => import("./History-DR2txJLE.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1656
+ () => import("./History-BDZrgfZ3.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1289
1657
  );
1290
1658
  const routes$1 = [
1291
1659
  {
@@ -1298,31 +1666,31 @@ const routes$1 = [
1298
1666
  }
1299
1667
  ];
1300
1668
  const ProtectedEditViewPage = lazy(
1301
- () => import("./EditViewPage-B7VgwJaG.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1669
+ () => import("./EditViewPage-HYljoEY7.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1302
1670
  );
1303
1671
  const ProtectedListViewPage = lazy(
1304
- () => import("./ListViewPage-B_GaWgRH.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1672
+ () => import("./ListViewPage-CsX7tWx-.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1305
1673
  );
1306
1674
  const ProtectedListConfiguration = lazy(
1307
- () => import("./ListConfigurationPage-voFVtXu6.mjs").then((mod) => ({
1675
+ () => import("./ListConfigurationPage-BZ3ScUna.mjs").then((mod) => ({
1308
1676
  default: mod.ProtectedListConfiguration
1309
1677
  }))
1310
1678
  );
1311
1679
  const ProtectedEditConfigurationPage = lazy(
1312
- () => import("./EditConfigurationPage-D7PrLO8j.mjs").then((mod) => ({
1680
+ () => import("./EditConfigurationPage-CZofxSLy.mjs").then((mod) => ({
1313
1681
  default: mod.ProtectedEditConfigurationPage
1314
1682
  }))
1315
1683
  );
1316
1684
  const ProtectedComponentConfigurationPage = lazy(
1317
- () => import("./ComponentConfigurationPage-B3yDbeU1.mjs").then((mod) => ({
1685
+ () => import("./ComponentConfigurationPage-DJ5voqEK.mjs").then((mod) => ({
1318
1686
  default: mod.ProtectedComponentConfigurationPage
1319
1687
  }))
1320
1688
  );
1321
1689
  const NoPermissions = lazy(
1322
- () => import("./NoPermissionsPage-B5baIHal.mjs").then((mod) => ({ default: mod.NoPermissions }))
1690
+ () => import("./NoPermissionsPage-S4Re3FwO.mjs").then((mod) => ({ default: mod.NoPermissions }))
1323
1691
  );
1324
1692
  const NoContentType = lazy(
1325
- () => import("./NoContentTypePage-CYiGpsbj.mjs").then((mod) => ({ default: mod.NoContentType }))
1693
+ () => import("./NoContentTypePage-LClTUPWs.mjs").then((mod) => ({ default: mod.NoContentType }))
1326
1694
  );
1327
1695
  const CollectionTypePages = () => {
1328
1696
  const { collectionType } = useParams();
@@ -1503,7 +1871,7 @@ const DocumentActionsMenu = ({
1503
1871
  };
1504
1872
  return /* @__PURE__ */ jsxs(Menu.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
1505
1873
  /* @__PURE__ */ jsxs(
1506
- StyledMoreButton,
1874
+ Menu.Trigger,
1507
1875
  {
1508
1876
  disabled: isDisabled,
1509
1877
  size: "S",
@@ -1521,7 +1889,7 @@ const DocumentActionsMenu = ({
1521
1889
  ]
1522
1890
  }
1523
1891
  ),
1524
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1892
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1525
1893
  actions2.map((action) => {
1526
1894
  return /* @__PURE__ */ jsx(
1527
1895
  Menu.Item,
@@ -1619,11 +1987,6 @@ const convertActionVariantToIconColor = (variant = "secondary") => {
1619
1987
  return "primary600";
1620
1988
  }
1621
1989
  };
1622
- const StyledMoreButton = styled(Menu.Trigger)`
1623
- & > span {
1624
- display: flex;
1625
- }
1626
- `;
1627
1990
  const DocumentActionConfirmDialog = ({
1628
1991
  onClose,
1629
1992
  onCancel,
@@ -1650,11 +2013,11 @@ const DocumentActionConfirmDialog = ({
1650
2013
  /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1651
2014
  /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1652
2015
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1653
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
2016
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1654
2017
  id: "app.components.Button.cancel",
1655
2018
  defaultMessage: "Cancel"
1656
2019
  }) }) }),
1657
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
2020
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1658
2021
  id: "app.components.Button.confirm",
1659
2022
  defaultMessage: "Confirm"
1660
2023
  }) })
@@ -1693,12 +2056,10 @@ const PublishAction$1 = ({
1693
2056
  const navigate = useNavigate();
1694
2057
  const { toggleNotification } = useNotification();
1695
2058
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2059
+ const isListView = useMatch(LIST_PATH) !== null;
1696
2060
  const isCloning = useMatch(CLONE_PATH) !== null;
1697
2061
  const { formatMessage } = useIntl();
1698
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1699
- "PublishAction",
1700
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1701
- );
2062
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1702
2063
  const { publish } = useDocumentActions();
1703
2064
  const [
1704
2065
  countDraftRelations,
@@ -1750,24 +2111,25 @@ const PublishAction$1 = ({
1750
2111
  }
1751
2112
  }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1752
2113
  React.useEffect(() => {
1753
- if (documentId) {
1754
- const fetchDraftRelationsCount = async () => {
1755
- const { data, error } = await countDraftRelations({
1756
- collectionType,
1757
- model,
1758
- documentId,
1759
- params
1760
- });
1761
- if (error) {
1762
- throw error;
1763
- }
1764
- if (data) {
1765
- setServerCountOfDraftRelations(data.data);
1766
- }
1767
- };
1768
- fetchDraftRelationsCount();
2114
+ if (!document || !document.documentId || isListView) {
2115
+ return;
1769
2116
  }
1770
- }, [documentId, countDraftRelations, collectionType, model, params]);
2117
+ const fetchDraftRelationsCount = async () => {
2118
+ const { data, error } = await countDraftRelations({
2119
+ collectionType,
2120
+ model,
2121
+ documentId,
2122
+ params
2123
+ });
2124
+ if (error) {
2125
+ throw error;
2126
+ }
2127
+ if (data) {
2128
+ setServerCountOfDraftRelations(data.data);
2129
+ }
2130
+ };
2131
+ fetchDraftRelationsCount();
2132
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1771
2133
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1772
2134
  if (!schema?.options?.draftAndPublish) {
1773
2135
  return null;
@@ -1808,7 +2170,8 @@ const PublishAction$1 = ({
1808
2170
  }
1809
2171
  };
1810
2172
  const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1811
- const hasDraftRelations = totalDraftRelations > 0;
2173
+ const enableDraftRelationsCount = false;
2174
+ const hasDraftRelations = enableDraftRelationsCount;
1812
2175
  return {
1813
2176
  /**
1814
2177
  * Disabled when:
@@ -1825,9 +2188,6 @@ const PublishAction$1 = ({
1825
2188
  defaultMessage: "Publish"
1826
2189
  }),
1827
2190
  onClick: async () => {
1828
- if (hasDraftRelations) {
1829
- return;
1830
- }
1831
2191
  await performPublish();
1832
2192
  },
1833
2193
  dialog: hasDraftRelations ? {
@@ -1866,10 +2226,6 @@ const UpdateAction = ({
1866
2226
  const cloneMatch = useMatch(CLONE_PATH);
1867
2227
  const isCloning = cloneMatch !== null;
1868
2228
  const { formatMessage } = useIntl();
1869
- useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1870
- canCreate: canCreate2,
1871
- canUpdate: canUpdate2
1872
- }));
1873
2229
  const { create, update, clone } = useDocumentActions();
1874
2230
  const [{ query, rawQuery }] = useQueryParams();
1875
2231
  const params = React.useMemo(() => buildValidParams(query), [query]);
@@ -1895,16 +2251,18 @@ const UpdateAction = ({
1895
2251
  onClick: async () => {
1896
2252
  setSubmitting(true);
1897
2253
  try {
1898
- const { errors } = await validate();
1899
- if (errors) {
1900
- toggleNotification({
1901
- type: "danger",
1902
- message: formatMessage({
1903
- id: "content-manager.validation.error",
1904
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1905
- })
1906
- });
1907
- return;
2254
+ if (activeTab !== "draft") {
2255
+ const { errors } = await validate();
2256
+ if (errors) {
2257
+ toggleNotification({
2258
+ type: "danger",
2259
+ message: formatMessage({
2260
+ id: "content-manager.validation.error",
2261
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2262
+ })
2263
+ });
2264
+ return;
2265
+ }
1908
2266
  }
1909
2267
  if (isCloning) {
1910
2268
  const res = await clone(
@@ -2000,7 +2358,7 @@ const UnpublishAction$1 = ({
2000
2358
  id: "app.utils.unpublish",
2001
2359
  defaultMessage: "Unpublish"
2002
2360
  }),
2003
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2361
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2004
2362
  onClick: async () => {
2005
2363
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
2006
2364
  if (!documentId) {
@@ -2112,7 +2470,7 @@ const DiscardAction = ({
2112
2470
  id: "content-manager.actions.discard.label",
2113
2471
  defaultMessage: "Discard changes"
2114
2472
  }),
2115
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2473
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2116
2474
  position: ["panel", "table-row"],
2117
2475
  variant: "danger",
2118
2476
  dialog: {
@@ -2140,11 +2498,6 @@ const DiscardAction = ({
2140
2498
  };
2141
2499
  };
2142
2500
  DiscardAction.type = "discard";
2143
- const StyledCrossCircle = styled(CrossCircle)`
2144
- path {
2145
- fill: currentColor;
2146
- }
2147
- `;
2148
2501
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2149
2502
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2150
2503
  const RelativeTime = React.forwardRef(
@@ -2192,7 +2545,7 @@ const getDisplayName = ({
2192
2545
  };
2193
2546
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2194
2547
  const DocumentStatus = ({ status = "draft", ...restProps }) => {
2195
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2548
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2196
2549
  return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2197
2550
  };
2198
2551
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
@@ -2375,12 +2728,26 @@ const Information = ({ activeTab }) => {
2375
2728
  );
2376
2729
  };
2377
2730
  const HeaderActions = ({ actions: actions2 }) => {
2378
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2379
- if ("options" in action) {
2380
- return /* @__PURE__ */ jsx(
2381
- SingleSelect,
2382
- {
2383
- size: "S",
2731
+ const [dialogId, setDialogId] = React.useState(null);
2732
+ const handleClick = (action) => async (e) => {
2733
+ if (!("options" in action)) {
2734
+ const { onClick = () => false, dialog, id } = action;
2735
+ const muteDialog = await onClick(e);
2736
+ if (dialog && !muteDialog) {
2737
+ e.preventDefault();
2738
+ setDialogId(id);
2739
+ }
2740
+ }
2741
+ };
2742
+ const handleClose = () => {
2743
+ setDialogId(null);
2744
+ };
2745
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2746
+ if (action.options) {
2747
+ return /* @__PURE__ */ jsx(
2748
+ SingleSelect,
2749
+ {
2750
+ size: "S",
2384
2751
  disabled: action.disabled,
2385
2752
  "aria-label": action.label,
2386
2753
  onChange: action.onSelect,
@@ -2390,10 +2757,49 @@ const HeaderActions = ({ actions: actions2 }) => {
2390
2757
  action.id
2391
2758
  );
2392
2759
  } else {
2393
- return null;
2760
+ if (action.type === "icon") {
2761
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
2762
+ /* @__PURE__ */ jsx(
2763
+ IconButton,
2764
+ {
2765
+ disabled: action.disabled,
2766
+ label: action.label,
2767
+ size: "S",
2768
+ onClick: handleClick(action),
2769
+ children: action.icon
2770
+ }
2771
+ ),
2772
+ action.dialog ? /* @__PURE__ */ jsx(
2773
+ HeaderActionDialog,
2774
+ {
2775
+ ...action.dialog,
2776
+ isOpen: dialogId === action.id,
2777
+ onClose: handleClose
2778
+ }
2779
+ ) : null
2780
+ ] }, action.id);
2781
+ }
2394
2782
  }
2395
2783
  }) });
2396
2784
  };
2785
+ const HeaderActionDialog = ({
2786
+ onClose,
2787
+ onCancel,
2788
+ title,
2789
+ content: Content,
2790
+ isOpen
2791
+ }) => {
2792
+ const handleClose = async () => {
2793
+ if (onCancel) {
2794
+ await onCancel();
2795
+ }
2796
+ onClose();
2797
+ };
2798
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2799
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2800
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
2801
+ ] }) });
2802
+ };
2397
2803
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2398
2804
  const navigate = useNavigate();
2399
2805
  const { formatMessage } = useIntl();
@@ -2411,487 +2817,189 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2411
2817
  };
2412
2818
  ConfigureTheViewAction.type = "configure-the-view";
2413
2819
  const EditTheModelAction = ({ model }) => {
2414
- const navigate = useNavigate();
2415
- const { formatMessage } = useIntl();
2416
- return {
2417
- label: formatMessage({
2418
- id: "content-manager.link-to-ctb",
2419
- defaultMessage: "Edit the model"
2420
- }),
2421
- icon: /* @__PURE__ */ jsx(Pencil, {}),
2422
- onClick: () => {
2423
- navigate(`/plugins/content-type-builder/content-types/${model}`);
2424
- },
2425
- position: "header"
2426
- };
2427
- };
2428
- EditTheModelAction.type = "edit-the-model";
2429
- const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2430
- const navigate = useNavigate();
2431
- const { formatMessage } = useIntl();
2432
- const listViewPathMatch = useMatch(LIST_PATH);
2433
- const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2434
- const { delete: deleteAction } = useDocumentActions();
2435
- const { toggleNotification } = useNotification();
2436
- const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2437
- return {
2438
- disabled: !canDelete || !document,
2439
- label: formatMessage({
2440
- id: "content-manager.actions.delete.label",
2441
- defaultMessage: "Delete document"
2442
- }),
2443
- icon: /* @__PURE__ */ jsx(Trash, {}),
2444
- dialog: {
2445
- type: "dialog",
2446
- title: formatMessage({
2447
- id: "app.components.ConfirmDialog.title",
2448
- defaultMessage: "Confirmation"
2449
- }),
2450
- content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
2451
- /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2452
- /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2453
- id: "content-manager.actions.delete.dialog.body",
2454
- defaultMessage: "Are you sure?"
2455
- }) })
2456
- ] }),
2457
- onConfirm: async () => {
2458
- if (!listViewPathMatch) {
2459
- setSubmitting(true);
2460
- }
2461
- try {
2462
- if (!documentId && collectionType !== SINGLE_TYPES) {
2463
- console.error(
2464
- "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2465
- );
2466
- toggleNotification({
2467
- message: formatMessage({
2468
- id: "content-manager.actions.delete.error",
2469
- defaultMessage: "An error occurred while trying to delete the document."
2470
- }),
2471
- type: "danger"
2472
- });
2473
- return;
2474
- }
2475
- const res = await deleteAction({
2476
- documentId,
2477
- model,
2478
- collectionType,
2479
- params: {
2480
- locale: "*"
2481
- }
2482
- });
2483
- if (!("error" in res)) {
2484
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2485
- }
2486
- } finally {
2487
- if (!listViewPathMatch) {
2488
- setSubmitting(false);
2489
- }
2490
- }
2491
- }
2492
- },
2493
- variant: "danger",
2494
- position: ["header", "table-row"]
2495
- };
2496
- };
2497
- DeleteAction$1.type = "delete";
2498
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2499
- const Panels = () => {
2500
- const isCloning = useMatch(CLONE_PATH) !== null;
2501
- const [
2502
- {
2503
- query: { status }
2504
- }
2505
- ] = useQueryParams({
2506
- status: "draft"
2507
- });
2508
- const { model, id, document, meta, collectionType } = useDoc();
2509
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2510
- const props = {
2511
- activeTab: status,
2512
- model,
2513
- documentId: id,
2514
- document: isCloning ? void 0 : document,
2515
- meta: isCloning ? void 0 : meta,
2516
- collectionType
2517
- };
2518
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2519
- DescriptionComponentRenderer,
2520
- {
2521
- props,
2522
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2523
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2524
- }
2525
- ) });
2526
- };
2527
- const ActionsPanel = () => {
2528
- const { formatMessage } = useIntl();
2529
- return {
2530
- title: formatMessage({
2531
- id: "content-manager.containers.edit.panels.default.title",
2532
- defaultMessage: "Document"
2533
- }),
2534
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2535
- };
2536
- };
2537
- ActionsPanel.type = "actions";
2538
- const ActionsPanelContent = () => {
2539
- const isCloning = useMatch(CLONE_PATH) !== null;
2540
- const [
2541
- {
2542
- query: { status = "draft" }
2543
- }
2544
- ] = useQueryParams();
2545
- const { model, id, document, meta, collectionType } = useDoc();
2546
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2547
- const props = {
2548
- activeTab: status,
2549
- model,
2550
- documentId: id,
2551
- document: isCloning ? void 0 : document,
2552
- meta: isCloning ? void 0 : meta,
2553
- collectionType
2554
- };
2555
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2556
- /* @__PURE__ */ jsx(
2557
- DescriptionComponentRenderer,
2558
- {
2559
- props,
2560
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2561
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2562
- }
2563
- ),
2564
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2565
- ] });
2566
- };
2567
- const Panel = React.forwardRef(({ children, title }, ref) => {
2568
- return /* @__PURE__ */ jsxs(
2569
- Flex,
2570
- {
2571
- ref,
2572
- tag: "aside",
2573
- "aria-labelledby": "additional-information",
2574
- background: "neutral0",
2575
- borderColor: "neutral150",
2576
- hasRadius: true,
2577
- paddingBottom: 4,
2578
- paddingLeft: 4,
2579
- paddingRight: 4,
2580
- paddingTop: 4,
2581
- shadow: "tableShadow",
2582
- gap: 3,
2583
- direction: "column",
2584
- justifyContent: "stretch",
2585
- alignItems: "flex-start",
2586
- children: [
2587
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2588
- children
2589
- ]
2590
- }
2591
- );
2592
- });
2593
- const HOOKS = {
2594
- /**
2595
- * Hook that allows to mutate the displayed headers of the list view table
2596
- * @constant
2597
- * @type {string}
2598
- */
2599
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2600
- /**
2601
- * Hook that allows to mutate the CM's collection types links pre-set filters
2602
- * @constant
2603
- * @type {string}
2604
- */
2605
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2606
- /**
2607
- * Hook that allows to mutate the CM's edit view layout
2608
- * @constant
2609
- * @type {string}
2610
- */
2611
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2612
- /**
2613
- * Hook that allows to mutate the CM's single types links pre-set filters
2614
- * @constant
2615
- * @type {string}
2616
- */
2617
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2618
- };
2619
- const contentTypesApi = contentManagerApi.injectEndpoints({
2620
- endpoints: (builder) => ({
2621
- getContentTypeConfiguration: builder.query({
2622
- query: (uid) => ({
2623
- url: `/content-manager/content-types/${uid}/configuration`,
2624
- method: "GET"
2625
- }),
2626
- transformResponse: (response) => response.data,
2627
- providesTags: (_result, _error, uid) => [
2628
- { type: "ContentTypesConfiguration", id: uid },
2629
- { type: "ContentTypeSettings", id: "LIST" }
2630
- ]
2631
- }),
2632
- getAllContentTypeSettings: builder.query({
2633
- query: () => "/content-manager/content-types-settings",
2634
- transformResponse: (response) => response.data,
2635
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2636
- }),
2637
- updateContentTypeConfiguration: builder.mutation({
2638
- query: ({ uid, ...body }) => ({
2639
- url: `/content-manager/content-types/${uid}/configuration`,
2640
- method: "PUT",
2641
- data: body
2642
- }),
2643
- transformResponse: (response) => response.data,
2644
- invalidatesTags: (_result, _error, { uid }) => [
2645
- { type: "ContentTypesConfiguration", id: uid },
2646
- { type: "ContentTypeSettings", id: "LIST" },
2647
- // Is this necessary?
2648
- { type: "InitialData" }
2649
- ]
2650
- })
2651
- })
2652
- });
2653
- const {
2654
- useGetContentTypeConfigurationQuery,
2655
- useGetAllContentTypeSettingsQuery,
2656
- useUpdateContentTypeConfigurationMutation
2657
- } = contentTypesApi;
2658
- const checkIfAttributeIsDisplayable = (attribute) => {
2659
- const { type } = attribute;
2660
- if (type === "relation") {
2661
- return !attribute.relation.toLowerCase().includes("morph");
2662
- }
2663
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2664
- };
2665
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2666
- if (!mainFieldName) {
2667
- return void 0;
2668
- }
2669
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2670
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2671
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2672
- );
2673
- return {
2674
- name: mainFieldName,
2675
- type: mainFieldType ?? "string"
2676
- };
2677
- };
2678
- const DEFAULT_SETTINGS = {
2679
- bulkable: false,
2680
- filterable: false,
2681
- searchable: false,
2682
- pagination: false,
2683
- defaultSortBy: "",
2684
- defaultSortOrder: "asc",
2685
- mainField: "id",
2686
- pageSize: 10
2687
- };
2688
- const useDocumentLayout = (model) => {
2689
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2690
- const [{ query }] = useQueryParams();
2691
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2692
- const { toggleNotification } = useNotification();
2693
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2694
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2695
- const {
2696
- data,
2697
- isLoading: isLoadingConfigs,
2698
- error,
2699
- isFetching: isFetchingConfigs
2700
- } = useGetContentTypeConfigurationQuery(model);
2701
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2702
- React.useEffect(() => {
2703
- if (error) {
2704
- toggleNotification({
2705
- type: "danger",
2706
- message: formatAPIError(error)
2707
- });
2708
- }
2709
- }, [error, formatAPIError, toggleNotification]);
2710
- const editLayout = React.useMemo(
2711
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2712
- layout: [],
2713
- components: {},
2714
- metadatas: {},
2715
- options: {},
2716
- settings: DEFAULT_SETTINGS
2717
- },
2718
- [data, isLoading, schemas, schema, components]
2719
- );
2720
- const listLayout = React.useMemo(() => {
2721
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2722
- layout: [],
2723
- metadatas: {},
2724
- options: {},
2725
- settings: DEFAULT_SETTINGS
2726
- };
2727
- }, [data, isLoading, schemas, schema, components]);
2728
- const { layout: edit } = React.useMemo(
2729
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2730
- layout: editLayout,
2731
- query
2732
- }),
2733
- [editLayout, query, runHookWaterfall]
2734
- );
2735
- return {
2736
- error,
2737
- isLoading,
2738
- edit,
2739
- list: listLayout
2740
- };
2741
- };
2742
- const useDocLayout = () => {
2743
- const { model } = useDoc();
2744
- return useDocumentLayout(model);
2745
- };
2746
- const formatEditLayout = (data, {
2747
- schemas,
2748
- schema,
2749
- components
2750
- }) => {
2751
- let currentPanelIndex = 0;
2752
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2753
- data.contentType.layouts.edit,
2754
- schema?.attributes,
2755
- data.contentType.metadatas,
2756
- { configurations: data.components, schemas: components },
2757
- schemas
2758
- ).reduce((panels, row) => {
2759
- if (row.some((field) => field.type === "dynamiczone")) {
2760
- panels.push([row]);
2761
- currentPanelIndex += 2;
2762
- } else {
2763
- if (!panels[currentPanelIndex]) {
2764
- panels.push([]);
2765
- }
2766
- panels[currentPanelIndex].push(row);
2767
- }
2768
- return panels;
2769
- }, []);
2770
- const componentEditAttributes = Object.entries(data.components).reduce(
2771
- (acc, [uid, configuration]) => {
2772
- acc[uid] = {
2773
- layout: convertEditLayoutToFieldLayouts(
2774
- configuration.layouts.edit,
2775
- components[uid].attributes,
2776
- configuration.metadatas
2777
- ),
2778
- settings: {
2779
- ...configuration.settings,
2780
- icon: components[uid].info.icon,
2781
- displayName: components[uid].info.displayName
2782
- }
2783
- };
2784
- return acc;
2785
- },
2786
- {}
2787
- );
2788
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2789
- (acc, [attribute, metadata]) => {
2790
- return {
2791
- ...acc,
2792
- [attribute]: metadata.edit
2793
- };
2794
- },
2795
- {}
2796
- );
2820
+ const navigate = useNavigate();
2821
+ const { formatMessage } = useIntl();
2797
2822
  return {
2798
- layout: panelledEditAttributes,
2799
- components: componentEditAttributes,
2800
- metadatas: editMetadatas,
2801
- settings: {
2802
- ...data.contentType.settings,
2803
- displayName: schema?.info.displayName
2823
+ label: formatMessage({
2824
+ id: "content-manager.link-to-ctb",
2825
+ defaultMessage: "Edit the model"
2826
+ }),
2827
+ icon: /* @__PURE__ */ jsx(Pencil, {}),
2828
+ onClick: () => {
2829
+ navigate(`/plugins/content-type-builder/content-types/${model}`);
2804
2830
  },
2805
- options: {
2806
- ...schema?.options,
2807
- ...schema?.pluginOptions,
2808
- ...data.contentType.options
2809
- }
2831
+ position: "header"
2810
2832
  };
2811
2833
  };
2812
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2813
- return rows.map(
2814
- (row) => row.map((field) => {
2815
- const attribute = attributes[field.name];
2816
- if (!attribute) {
2817
- return null;
2834
+ EditTheModelAction.type = "edit-the-model";
2835
+ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2836
+ const navigate = useNavigate();
2837
+ const { formatMessage } = useIntl();
2838
+ const listViewPathMatch = useMatch(LIST_PATH);
2839
+ const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2840
+ const { delete: deleteAction } = useDocumentActions();
2841
+ const { toggleNotification } = useNotification();
2842
+ const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2843
+ const isLocalized = document?.locale != null;
2844
+ return {
2845
+ disabled: !canDelete || !document,
2846
+ label: formatMessage(
2847
+ {
2848
+ id: "content-manager.actions.delete.label",
2849
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2850
+ },
2851
+ { isLocalized }
2852
+ ),
2853
+ icon: /* @__PURE__ */ jsx(Trash, {}),
2854
+ dialog: {
2855
+ type: "dialog",
2856
+ title: formatMessage({
2857
+ id: "app.components.ConfirmDialog.title",
2858
+ defaultMessage: "Confirmation"
2859
+ }),
2860
+ content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
2861
+ /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2862
+ /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2863
+ id: "content-manager.actions.delete.dialog.body",
2864
+ defaultMessage: "Are you sure?"
2865
+ }) })
2866
+ ] }),
2867
+ onConfirm: async () => {
2868
+ if (!listViewPathMatch) {
2869
+ setSubmitting(true);
2870
+ }
2871
+ try {
2872
+ if (!documentId && collectionType !== SINGLE_TYPES) {
2873
+ console.error(
2874
+ "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2875
+ );
2876
+ toggleNotification({
2877
+ message: formatMessage({
2878
+ id: "content-manager.actions.delete.error",
2879
+ defaultMessage: "An error occurred while trying to delete the document."
2880
+ }),
2881
+ type: "danger"
2882
+ });
2883
+ return;
2884
+ }
2885
+ const res = await deleteAction({
2886
+ documentId,
2887
+ model,
2888
+ collectionType,
2889
+ params: {
2890
+ locale: "*"
2891
+ }
2892
+ });
2893
+ if (!("error" in res)) {
2894
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2895
+ }
2896
+ } finally {
2897
+ if (!listViewPathMatch) {
2898
+ setSubmitting(false);
2899
+ }
2900
+ }
2818
2901
  }
2819
- const { edit: metadata } = metadatas[field.name];
2820
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2821
- return {
2822
- attribute,
2823
- disabled: !metadata.editable,
2824
- hint: metadata.description,
2825
- label: metadata.label ?? "",
2826
- name: field.name,
2827
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2828
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2829
- schemas,
2830
- components: components?.schemas ?? {}
2831
- }),
2832
- placeholder: metadata.placeholder ?? "",
2833
- required: attribute.required ?? false,
2834
- size: field.size,
2835
- unique: "unique" in attribute ? attribute.unique : false,
2836
- visible: metadata.visible ?? true,
2837
- type: attribute.type
2838
- };
2839
- }).filter((field) => field !== null)
2840
- );
2841
- };
2842
- const formatListLayout = (data, {
2843
- schemas,
2844
- schema,
2845
- components
2846
- }) => {
2847
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2848
- (acc, [attribute, metadata]) => {
2849
- return {
2850
- ...acc,
2851
- [attribute]: metadata.list
2852
- };
2853
2902
  },
2854
- {}
2855
- );
2856
- const listAttributes = convertListLayoutToFieldLayouts(
2857
- data.contentType.layouts.list,
2858
- schema?.attributes,
2859
- listMetadatas,
2860
- { configurations: data.components, schemas: components },
2861
- schemas
2862
- );
2863
- return {
2864
- layout: listAttributes,
2865
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2866
- metadatas: listMetadatas,
2867
- options: {
2868
- ...schema?.options,
2869
- ...schema?.pluginOptions,
2870
- ...data.contentType.options
2903
+ variant: "danger",
2904
+ position: ["header", "table-row"]
2905
+ };
2906
+ };
2907
+ DeleteAction$1.type = "delete";
2908
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2909
+ const Panels = () => {
2910
+ const isCloning = useMatch(CLONE_PATH) !== null;
2911
+ const [
2912
+ {
2913
+ query: { status }
2914
+ }
2915
+ ] = useQueryParams({
2916
+ status: "draft"
2917
+ });
2918
+ const { model, id, document, meta, collectionType } = useDoc();
2919
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
2920
+ const props = {
2921
+ activeTab: status,
2922
+ model,
2923
+ documentId: id,
2924
+ document: isCloning ? void 0 : document,
2925
+ meta: isCloning ? void 0 : meta,
2926
+ collectionType
2927
+ };
2928
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2929
+ DescriptionComponentRenderer,
2930
+ {
2931
+ props,
2932
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2933
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2871
2934
  }
2935
+ ) });
2936
+ };
2937
+ const ActionsPanel = () => {
2938
+ const { formatMessage } = useIntl();
2939
+ return {
2940
+ title: formatMessage({
2941
+ id: "content-manager.containers.edit.panels.default.title",
2942
+ defaultMessage: "Entry"
2943
+ }),
2944
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2872
2945
  };
2873
2946
  };
2874
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2875
- return columns.map((name) => {
2876
- const attribute = attributes[name];
2877
- if (!attribute) {
2878
- return null;
2947
+ ActionsPanel.type = "actions";
2948
+ const ActionsPanelContent = () => {
2949
+ const isCloning = useMatch(CLONE_PATH) !== null;
2950
+ const [
2951
+ {
2952
+ query: { status = "draft" }
2879
2953
  }
2880
- const metadata = metadatas[name];
2881
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2882
- return {
2883
- attribute,
2884
- label: metadata.label ?? "",
2885
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2886
- schemas,
2887
- components: components?.schemas ?? {}
2888
- }),
2889
- name,
2890
- searchable: metadata.searchable ?? true,
2891
- sortable: metadata.sortable ?? true
2892
- };
2893
- }).filter((field) => field !== null);
2954
+ ] = useQueryParams();
2955
+ const { model, id, document, meta, collectionType } = useDoc();
2956
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2957
+ const props = {
2958
+ activeTab: status,
2959
+ model,
2960
+ documentId: id,
2961
+ document: isCloning ? void 0 : document,
2962
+ meta: isCloning ? void 0 : meta,
2963
+ collectionType
2964
+ };
2965
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2966
+ /* @__PURE__ */ jsx(
2967
+ DescriptionComponentRenderer,
2968
+ {
2969
+ props,
2970
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
2971
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2972
+ }
2973
+ ),
2974
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2975
+ ] });
2894
2976
  };
2977
+ const Panel = React.forwardRef(({ children, title }, ref) => {
2978
+ return /* @__PURE__ */ jsxs(
2979
+ Flex,
2980
+ {
2981
+ ref,
2982
+ tag: "aside",
2983
+ "aria-labelledby": "additional-information",
2984
+ background: "neutral0",
2985
+ borderColor: "neutral150",
2986
+ hasRadius: true,
2987
+ paddingBottom: 4,
2988
+ paddingLeft: 4,
2989
+ paddingRight: 4,
2990
+ paddingTop: 4,
2991
+ shadow: "tableShadow",
2992
+ gap: 3,
2993
+ direction: "column",
2994
+ justifyContent: "stretch",
2995
+ alignItems: "flex-start",
2996
+ children: [
2997
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2998
+ children
2999
+ ]
3000
+ }
3001
+ );
3002
+ });
2895
3003
  const ConfirmBulkActionDialog = ({
2896
3004
  onToggleDialog,
2897
3005
  isOpen = false,
@@ -2930,6 +3038,7 @@ const ConfirmDialogPublishAll = ({
2930
3038
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2931
3039
  const { model, schema } = useDoc();
2932
3040
  const [{ query }] = useQueryParams();
3041
+ const enableDraftRelationsCount = false;
2933
3042
  const {
2934
3043
  data: countDraftRelations = 0,
2935
3044
  isLoading,
@@ -2941,7 +3050,7 @@ const ConfirmDialogPublishAll = ({
2941
3050
  locale: query?.plugins?.i18n?.locale
2942
3051
  },
2943
3052
  {
2944
- skip: selectedEntries.length === 0
3053
+ skip: !enableDraftRelationsCount
2945
3054
  }
2946
3055
  );
2947
3056
  React.useEffect(() => {
@@ -3126,7 +3235,7 @@ const SelectedEntriesTableContent = ({
3126
3235
  status: row.status
3127
3236
  }
3128
3237
  ) }),
3129
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3238
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3130
3239
  IconButton,
3131
3240
  {
3132
3241
  tag: Link,
@@ -3149,9 +3258,10 @@ const SelectedEntriesTableContent = ({
3149
3258
  ),
3150
3259
  target: "_blank",
3151
3260
  marginLeft: "auto",
3152
- children: /* @__PURE__ */ jsx(Pencil, {})
3261
+ variant: "ghost",
3262
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3153
3263
  }
3154
- ) })
3264
+ ) }) })
3155
3265
  ] }, row.id)) })
3156
3266
  ] });
3157
3267
  };
@@ -3188,7 +3298,13 @@ const SelectedEntriesModalContent = ({
3188
3298
  );
3189
3299
  const { rows, validationErrors } = React.useMemo(() => {
3190
3300
  if (data.length > 0 && schema) {
3191
- const validate = createYupSchema(schema.attributes, components);
3301
+ const validate = createYupSchema(
3302
+ schema.attributes,
3303
+ components,
3304
+ // Since this is the "Publish" action, the validation
3305
+ // schema must enforce the rules for published entities
3306
+ { status: "published" }
3307
+ );
3192
3308
  const validationErrors2 = {};
3193
3309
  const rows2 = data.map((entry) => {
3194
3310
  try {
@@ -3538,7 +3654,7 @@ const TableActions = ({ document }) => {
3538
3654
  DescriptionComponentRenderer,
3539
3655
  {
3540
3656
  props,
3541
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3657
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3542
3658
  children: (actions2) => {
3543
3659
  const tableRowActions = actions2.filter((action) => {
3544
3660
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3649,7 +3765,7 @@ const CloneAction = ({ model, documentId }) => {
3649
3765
  }),
3650
3766
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3651
3767
  footer: ({ onClose }) => {
3652
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3768
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3653
3769
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3654
3770
  id: "cancel",
3655
3771
  defaultMessage: "Cancel"
@@ -3880,7 +3996,7 @@ const index = {
3880
3996
  app.router.addRoute({
3881
3997
  path: "content-manager/*",
3882
3998
  lazy: async () => {
3883
- const { Layout } = await import("./layout-BNqvLR_b.mjs");
3999
+ const { Layout } = await import("./layout-ClP-DC72.mjs");
3884
4000
  return {
3885
4001
  Component: Layout
3886
4002
  };
@@ -3897,7 +4013,7 @@ const index = {
3897
4013
  async registerTrads({ locales }) {
3898
4014
  const importedTrads = await Promise.all(
3899
4015
  locales.map((locale) => {
3900
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-BrCTWlZv.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
4016
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-C8YBvRrK.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
3901
4017
  return {
3902
4018
  data: prefixPluginTranslations(data, PLUGIN_ID),
3903
4019
  locale
@@ -3918,13 +4034,15 @@ export {
3918
4034
  BulkActionsRenderer as B,
3919
4035
  COLLECTION_TYPES as C,
3920
4036
  DocumentStatus as D,
3921
- DEFAULT_SETTINGS as E,
3922
- convertEditLayoutToFieldLayouts as F,
3923
- useDocument as G,
4037
+ extractContentTypeComponents as E,
4038
+ DEFAULT_SETTINGS as F,
4039
+ convertEditLayoutToFieldLayouts as G,
3924
4040
  HOOKS as H,
3925
4041
  InjectionZone as I,
3926
- index as J,
3927
- useDocumentActions as K,
4042
+ useDocument as J,
4043
+ index as K,
4044
+ useContentManagerContext as L,
4045
+ useDocumentActions as M,
3928
4046
  Panels as P,
3929
4047
  RelativeTime as R,
3930
4048
  SINGLE_TYPES as S,
@@ -3942,18 +4060,18 @@ export {
3942
4060
  PERMISSIONS as k,
3943
4061
  DocumentRBAC as l,
3944
4062
  DOCUMENT_META_FIELDS as m,
3945
- useDocLayout as n,
3946
- useGetContentTypeConfigurationQuery as o,
3947
- CREATOR_FIELDS as p,
3948
- getMainField as q,
3949
- getDisplayName as r,
4063
+ CLONE_PATH as n,
4064
+ useDocLayout as o,
4065
+ useGetContentTypeConfigurationQuery as p,
4066
+ CREATOR_FIELDS as q,
4067
+ getMainField as r,
3950
4068
  setInitialData as s,
3951
- checkIfAttributeIsDisplayable as t,
4069
+ getDisplayName as t,
3952
4070
  useContentTypeSchema as u,
3953
- useGetAllDocumentsQuery as v,
3954
- convertListLayoutToFieldLayouts as w,
3955
- capitalise as x,
3956
- useUpdateContentTypeConfigurationMutation as y,
3957
- extractContentTypeComponents as z
4071
+ checkIfAttributeIsDisplayable as v,
4072
+ useGetAllDocumentsQuery as w,
4073
+ convertListLayoutToFieldLayouts as x,
4074
+ capitalise as y,
4075
+ useUpdateContentTypeConfigurationMutation as z
3958
4076
  };
3959
- //# sourceMappingURL=index-C9TJPyni.mjs.map
4077
+ //# sourceMappingURL=index-BmUAydCA.mjs.map