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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-CnL10QYC.mjs → ComponentConfigurationPage-7-qB29e7.mjs} +3 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-CnL10QYC.mjs.map → ComponentConfigurationPage-7-qB29e7.mjs.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-G4EIirP8.js → ComponentConfigurationPage-DP7AC0UU.js} +3 -3
  4. package/dist/_chunks/{ComponentConfigurationPage-G4EIirP8.js.map → ComponentConfigurationPage-DP7AC0UU.js.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-I2kKh9dx.mjs → EditConfigurationPage-CI4XoymK.mjs} +3 -3
  6. package/dist/_chunks/{EditConfigurationPage-I2kKh9dx.mjs.map → EditConfigurationPage-CI4XoymK.mjs.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-B2AA1kVF.js → EditConfigurationPage-DITVliEI.js} +3 -3
  8. package/dist/_chunks/{EditConfigurationPage-B2AA1kVF.js.map → EditConfigurationPage-DITVliEI.js.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-CHgoNwlc.js → EditViewPage-CUS2EAhB.js} +8 -4
  10. package/dist/_chunks/EditViewPage-CUS2EAhB.js.map +1 -0
  11. package/dist/_chunks/{EditViewPage-zFjJK0s8.mjs → EditViewPage-Dzpno8xI.mjs} +8 -4
  12. package/dist/_chunks/EditViewPage-Dzpno8xI.mjs.map +1 -0
  13. package/dist/_chunks/{Field-DPAzUS1M.mjs → Field-B_jG_EV9.mjs} +31 -19
  14. package/dist/_chunks/{Field-DPAzUS1M.mjs.map → Field-B_jG_EV9.mjs.map} +1 -1
  15. package/dist/_chunks/{Field-9DePZh-0.js → Field-CtUU1Fg8.js} +31 -19
  16. package/dist/_chunks/{Field-9DePZh-0.js.map → Field-CtUU1Fg8.js.map} +1 -1
  17. package/dist/_chunks/{Form-CEkENbkF.mjs → Form-BXHao2mZ.mjs} +2 -2
  18. package/dist/_chunks/{Form-CEkENbkF.mjs.map → Form-BXHao2mZ.mjs.map} +1 -1
  19. package/dist/_chunks/{Form-DPm-KZ1A.js → Form-DTqO0ymI.js} +2 -2
  20. package/dist/_chunks/{Form-DPm-KZ1A.js.map → Form-DTqO0ymI.js.map} +1 -1
  21. package/dist/_chunks/{History-utls71em.mjs → History-2Ah2CQ4T.mjs} +4 -4
  22. package/dist/_chunks/{History-utls71em.mjs.map → History-2Ah2CQ4T.mjs.map} +1 -1
  23. package/dist/_chunks/{History-DXSbTWez.js → History-C_uSGzO5.js} +4 -4
  24. package/dist/_chunks/{History-DXSbTWez.js.map → History-C_uSGzO5.js.map} +1 -1
  25. package/dist/_chunks/{ListConfigurationPage-CuMXWWqb.mjs → ListConfigurationPage-BjSJlaoC.mjs} +2 -2
  26. package/dist/_chunks/{ListConfigurationPage-CuMXWWqb.mjs.map → ListConfigurationPage-BjSJlaoC.mjs.map} +1 -1
  27. package/dist/_chunks/{ListConfigurationPage-D5C7ACZ_.js → ListConfigurationPage-nyuP7OSy.js} +2 -2
  28. package/dist/_chunks/{ListConfigurationPage-D5C7ACZ_.js.map → ListConfigurationPage-nyuP7OSy.js.map} +1 -1
  29. package/dist/_chunks/{ListViewPage-CdKd-PS_.mjs → ListViewPage-B75x3nz2.mjs} +4 -4
  30. package/dist/_chunks/{ListViewPage-CdKd-PS_.mjs.map → ListViewPage-B75x3nz2.mjs.map} +1 -1
  31. package/dist/_chunks/{ListViewPage-DfuwH1tt.js → ListViewPage-DHgHD8Xg.js} +4 -4
  32. package/dist/_chunks/{ListViewPage-DfuwH1tt.js.map → ListViewPage-DHgHD8Xg.js.map} +1 -1
  33. package/dist/_chunks/{NoContentTypePage-BIxlkWWi.js → NoContentTypePage-CDUKdZ7d.js} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-BIxlkWWi.js.map → NoContentTypePage-CDUKdZ7d.js.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-DkToTT7u.mjs → NoContentTypePage-DUacQSyF.mjs} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-DkToTT7u.mjs.map → NoContentTypePage-DUacQSyF.mjs.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-DlWi4BAH.mjs → NoPermissionsPage-SFllMekk.mjs} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-DlWi4BAH.mjs.map → NoPermissionsPage-SFllMekk.mjs.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-Bu4GWYb-.js → NoPermissionsPage-zwIZydDI.js} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-Bu4GWYb-.js.map → NoPermissionsPage-zwIZydDI.js.map} +1 -1
  41. package/dist/_chunks/{Relations-QP5yn9_z.mjs → Relations-D2NRW8fC.mjs} +13 -9
  42. package/dist/_chunks/Relations-D2NRW8fC.mjs.map +1 -0
  43. package/dist/_chunks/{Relations-CFjTESWQ.js → Relations-NFLaRNPr.js} +13 -9
  44. package/dist/_chunks/Relations-NFLaRNPr.js.map +1 -0
  45. package/dist/_chunks/{en-BVzUkPxZ.js → en-BlhnxQfj.js} +2 -2
  46. package/dist/_chunks/{en-BVzUkPxZ.js.map → en-BlhnxQfj.js.map} +1 -1
  47. package/dist/_chunks/{en-CPTj6CjC.mjs → en-C8YBvRrK.mjs} +2 -2
  48. package/dist/_chunks/{en-CPTj6CjC.mjs.map → en-C8YBvRrK.mjs.map} +1 -1
  49. package/dist/_chunks/{index-BHfS6_D5.mjs → index-C9HxCo5R.mjs} +873 -820
  50. package/dist/_chunks/index-C9HxCo5R.mjs.map +1 -0
  51. package/dist/_chunks/{index-DXiHxy70.js → index-ovJRE1FM.js} +871 -818
  52. package/dist/_chunks/index-ovJRE1FM.js.map +1 -0
  53. package/dist/_chunks/{layout-DX_52HSH.mjs → layout-DaUjDiWQ.mjs} +3 -3
  54. package/dist/_chunks/{layout-DX_52HSH.mjs.map → layout-DaUjDiWQ.mjs.map} +1 -1
  55. package/dist/_chunks/{layout-bE-WUnQ0.js → layout-UNWstw_s.js} +3 -3
  56. package/dist/_chunks/{layout-bE-WUnQ0.js.map → layout-UNWstw_s.js.map} +1 -1
  57. package/dist/_chunks/{relations-SCVAL_aJ.mjs → relations-D8iFAeRu.mjs} +2 -2
  58. package/dist/_chunks/{relations-SCVAL_aJ.mjs.map → relations-D8iFAeRu.mjs.map} +1 -1
  59. package/dist/_chunks/{relations-D706vblp.js → relations-NN3coOG5.js} +2 -2
  60. package/dist/_chunks/{relations-D706vblp.js.map → relations-NN3coOG5.js.map} +1 -1
  61. package/dist/admin/index.js +2 -1
  62. package/dist/admin/index.js.map +1 -1
  63. package/dist/admin/index.mjs +3 -2
  64. package/dist/admin/src/exports.d.ts +1 -1
  65. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  66. package/dist/admin/src/pages/EditView/components/Header.d.ts +1 -0
  67. package/dist/server/index.js +13 -6
  68. package/dist/server/index.js.map +1 -1
  69. package/dist/server/index.mjs +13 -6
  70. package/dist/server/index.mjs.map +1 -1
  71. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  72. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  73. package/package.json +10 -10
  74. package/dist/_chunks/EditViewPage-CHgoNwlc.js.map +0 -1
  75. package/dist/_chunks/EditViewPage-zFjJK0s8.mjs.map +0 -1
  76. package/dist/_chunks/Relations-CFjTESWQ.js.map +0 -1
  77. package/dist/_chunks/Relations-QP5yn9_z.mjs.map +0 -1
  78. package/dist/_chunks/index-BHfS6_D5.mjs.map +0 -1
  79. package/dist/_chunks/index-DXiHxy70.js.map +0 -1
  80. package/strapi-server.js +0 -3
@@ -1,6 +1,6 @@
1
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
6
  import { Button, Menu, VisuallyHidden, Flex, Typography, Dialog, Modal, Radio, Status, Box, SingleSelect, SingleSelectOption, IconButton, Loader, Tooltip, LinkButton } from "@strapi/design-system";
@@ -523,7 +523,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
523
523
  } else if (Array.isArray(value)) {
524
524
  return yup.array().of(
525
525
  yup.object().shape({
526
- id: yup.string().required()
526
+ id: yup.number().required()
527
527
  })
528
528
  );
529
529
  } else if (typeof value === "object") {
@@ -789,19 +789,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
789
789
  }, {});
790
790
  return componentsByKey;
791
791
  };
792
- 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);
793
891
  const { toggleNotification } = useNotification();
794
892
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
893
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
795
894
  const {
796
- currentData: data,
797
- isLoading: isLoadingDocument,
798
- isFetching: isFetchingDocument,
799
- error
800
- } = useGetDocumentQuery(args, {
801
- ...opts,
802
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
803
- });
804
- 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;
805
901
  React.useEffect(() => {
806
902
  if (error) {
807
903
  toggleNotification({
@@ -809,396 +905,437 @@ const useDocument = (args, opts) => {
809
905
  message: formatAPIError(error)
810
906
  });
811
907
  }
812
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
813
- const validationSchema = React.useMemo(() => {
814
- if (!schema) {
815
- return null;
816
- }
817
- return createYupSchema(schema.attributes, components);
818
- }, [schema, components]);
819
- const validate = React.useCallback(
820
- (document) => {
821
- if (!validationSchema) {
822
- throw new Error(
823
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
824
- );
825
- }
826
- try {
827
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
828
- return null;
829
- } catch (error2) {
830
- if (error2 instanceof ValidationError) {
831
- return getYupValidationErrors(error2);
832
- }
833
- throw error2;
834
- }
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
835
916
  },
836
- [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]
837
933
  );
838
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
839
934
  return {
840
- components,
841
- document: data?.data,
842
- meta: data?.meta,
935
+ error,
843
936
  isLoading,
844
- schema,
845
- validate
846
- };
847
- };
848
- const useDoc = () => {
849
- const { id, slug, collectionType, origin } = useParams();
850
- const [{ query }] = useQueryParams();
851
- const params = React.useMemo(() => buildValidParams(query), [query]);
852
- if (!collectionType) {
853
- throw new Error("Could not find collectionType in url params");
854
- }
855
- if (!slug) {
856
- throw new Error("Could not find model in url params");
857
- }
858
- return {
859
- collectionType,
860
- model: slug,
861
- id: origin || id === "create" ? void 0 : id,
862
- ...useDocument(
863
- { documentId: origin || id, model: slug, collectionType, params },
864
- {
865
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
866
- }
867
- )
937
+ edit,
938
+ list: listLayout
868
939
  };
869
940
  };
870
- const prefixPluginTranslations = (trad, pluginId) => {
871
- if (!pluginId) {
872
- throw new TypeError("pluginId can't be empty");
873
- }
874
- return Object.keys(trad).reduce((acc, current) => {
875
- acc[`${pluginId}.${current}`] = trad[current];
876
- return acc;
877
- }, {});
878
- };
879
- const getTranslation = (id) => `content-manager.${id}`;
880
- const DEFAULT_UNEXPECTED_ERROR_MSG = {
881
- id: "notification.error",
882
- defaultMessage: "An error occurred, please try again"
941
+ const useDocLayout = () => {
942
+ const { model } = useDoc();
943
+ return useDocumentLayout(model);
883
944
  };
884
- const useDocumentActions = () => {
885
- const { toggleNotification } = useNotification();
886
- const { formatMessage } = useIntl();
887
- const { trackUsage } = useTracking();
888
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
889
- const navigate = useNavigate();
890
- const [deleteDocument] = useDeleteDocumentMutation();
891
- const _delete = React.useCallback(
892
- async ({ collectionType, model, documentId, params }, trackerProperty) => {
893
- try {
894
- trackUsage("willDeleteEntry", trackerProperty);
895
- const res = await deleteDocument({
896
- collectionType,
897
- model,
898
- documentId,
899
- params
900
- });
901
- if ("error" in res) {
902
- toggleNotification({
903
- type: "danger",
904
- message: formatAPIError(res.error)
905
- });
906
- return { error: res.error };
907
- }
908
- toggleNotification({
909
- type: "success",
910
- message: formatMessage({
911
- id: getTranslation("success.record.delete"),
912
- defaultMessage: "Deleted document"
913
- })
914
- });
915
- trackUsage("didDeleteEntry", trackerProperty);
916
- return res.data;
917
- } catch (err) {
918
- toggleNotification({
919
- type: "danger",
920
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
921
- });
922
- trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
923
- throw err;
924
- }
925
- },
926
- [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
927
- );
928
- const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
929
- const deleteMany = React.useCallback(
930
- async ({ model, documentIds, params }) => {
931
- try {
932
- trackUsage("willBulkDeleteEntries");
933
- const res = await deleteManyDocuments({
934
- model,
935
- documentIds,
936
- params
937
- });
938
- if ("error" in res) {
939
- toggleNotification({
940
- type: "danger",
941
- message: formatAPIError(res.error)
942
- });
943
- return { error: res.error };
944
- }
945
- toggleNotification({
946
- type: "success",
947
- title: formatMessage({
948
- id: getTranslation("success.records.delete"),
949
- defaultMessage: "Successfully deleted."
950
- }),
951
- message: ""
952
- });
953
- trackUsage("didBulkDeleteEntries");
954
- return res.data;
955
- } catch (err) {
956
- toggleNotification({
957
- type: "danger",
958
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
959
- });
960
- trackUsage("didNotBulkDeleteEntries");
961
- 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([]);
962
964
  }
963
- },
964
- [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
965
- );
966
- const [discardDocument] = useDiscardDocumentMutation();
967
- const discard = React.useCallback(
968
- async ({ collectionType, model, documentId, params }) => {
969
- try {
970
- const res = await discardDocument({
971
- collectionType,
972
- model,
973
- documentId,
974
- params
975
- });
976
- if ("error" in res) {
977
- toggleNotification({
978
- type: "danger",
979
- message: formatAPIError(res.error)
980
- });
981
- 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
982
982
  }
983
- toggleNotification({
984
- type: "success",
985
- message: formatMessage({
986
- id: "content-manager.success.record.discard",
987
- defaultMessage: "Changes discarded"
988
- })
989
- });
990
- return res.data;
991
- } catch (err) {
992
- toggleNotification({
993
- type: "danger",
994
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
995
- });
996
- throw err;
997
- }
983
+ };
984
+ return acc;
998
985
  },
999
- [discardDocument, formatAPIError, formatMessage, toggleNotification]
986
+ {}
1000
987
  );
1001
- const [publishDocument] = usePublishDocumentMutation();
1002
- const publish = React.useCallback(
1003
- async ({ collectionType, model, documentId, params }, data) => {
1004
- try {
1005
- trackUsage("willPublishEntry");
1006
- const res = await publishDocument({
1007
- collectionType,
1008
- model,
1009
- documentId,
1010
- data,
1011
- params
1012
- });
1013
- if ("error" in res) {
1014
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1015
- return { error: res.error };
1016
- }
1017
- trackUsage("didPublishEntry");
1018
- toggleNotification({
1019
- type: "success",
1020
- message: formatMessage({
1021
- id: getTranslation("success.record.publish"),
1022
- defaultMessage: "Published document"
1023
- })
1024
- });
1025
- return res.data;
1026
- } catch (err) {
1027
- toggleNotification({
1028
- type: "danger",
1029
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1030
- });
1031
- throw err;
1032
- }
988
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
989
+ (acc, [attribute, metadata]) => {
990
+ return {
991
+ ...acc,
992
+ [attribute]: metadata.edit
993
+ };
1033
994
  },
1034
- [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
995
+ {}
1035
996
  );
1036
- const [publishManyDocuments] = usePublishManyDocumentsMutation();
1037
- const publishMany = React.useCallback(
1038
- async ({ model, documentIds, params }) => {
1039
- try {
1040
- const res = await publishManyDocuments({
1041
- model,
1042
- documentIds,
1043
- params
1044
- });
1045
- if ("error" in res) {
1046
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1047
- return { error: res.error };
1048
- }
1049
- toggleNotification({
1050
- type: "success",
1051
- message: formatMessage({
1052
- id: getTranslation("success.record.publish"),
1053
- defaultMessage: "Published document"
1054
- })
1055
- });
1056
- return res.data;
1057
- } catch (err) {
1058
- toggleNotification({
1059
- type: "danger",
1060
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1061
- });
1062
- throw err;
997
+ return {
998
+ layout: panelledEditAttributes,
999
+ components: componentEditAttributes,
1000
+ metadatas: editMetadatas,
1001
+ settings: {
1002
+ ...data.contentType.settings,
1003
+ displayName: schema?.info.displayName
1004
+ },
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;
1063
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
+ };
1064
1053
  },
1065
- [
1066
- // trackUsage,
1067
- publishManyDocuments,
1068
- toggleNotification,
1069
- formatMessage,
1070
- formatAPIError
1071
- ]
1054
+ {}
1072
1055
  );
1073
- const [updateDocument] = useUpdateDocumentMutation();
1074
- const update = React.useCallback(
1075
- async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1076
- try {
1077
- trackUsage("willEditEntry", trackerProperty);
1078
- const res = await updateDocument({
1079
- collectionType,
1080
- model,
1081
- documentId,
1082
- data,
1083
- params
1084
- });
1085
- if ("error" in res) {
1086
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1087
- trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1088
- return { error: res.error };
1089
- }
1090
- trackUsage("didEditEntry", trackerProperty);
1091
- toggleNotification({
1092
- type: "success",
1093
- message: formatMessage({
1094
- id: getTranslation("success.record.save"),
1095
- defaultMessage: "Saved document"
1096
- })
1097
- });
1098
- return res.data;
1099
- } catch (err) {
1100
- trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1101
- toggleNotification({
1102
- type: "danger",
1103
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1104
- });
1105
- throw err;
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);
1140
+ }
1141
+ throw error2;
1106
1142
  }
1107
1143
  },
1108
- [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1144
+ [validationSchema]
1109
1145
  );
1110
- const [unpublishDocument] = useUnpublishDocumentMutation();
1111
- const unpublish = React.useCallback(
1112
- async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1146
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1147
+ const hasError = !!error;
1148
+ return {
1149
+ components,
1150
+ document: data?.data,
1151
+ meta: data?.meta,
1152
+ isLoading,
1153
+ hasError,
1154
+ schema,
1155
+ schemas,
1156
+ validate
1157
+ };
1158
+ };
1159
+ const useDoc = () => {
1160
+ const { id, slug, collectionType, origin } = useParams();
1161
+ const [{ query }] = useQueryParams();
1162
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1163
+ if (!collectionType) {
1164
+ throw new Error("Could not find collectionType in url params");
1165
+ }
1166
+ if (!slug) {
1167
+ throw new Error("Could not find model in url params");
1168
+ }
1169
+ return {
1170
+ collectionType,
1171
+ model: slug,
1172
+ id: origin || id === "create" ? void 0 : id,
1173
+ ...useDocument(
1174
+ { documentId: origin || id, model: slug, collectionType, params },
1175
+ {
1176
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1177
+ }
1178
+ )
1179
+ };
1180
+ };
1181
+ const useContentManagerContext = () => {
1182
+ const {
1183
+ collectionType,
1184
+ model,
1185
+ id,
1186
+ components,
1187
+ isLoading: isLoadingDoc,
1188
+ schema,
1189
+ schemas
1190
+ } = useDoc();
1191
+ const layout = useDocumentLayout(model);
1192
+ const form = useForm("useContentManagerContext", (state) => state);
1193
+ const isSingleType = collectionType === SINGLE_TYPES;
1194
+ const slug = model;
1195
+ const isCreatingEntry = id === "create";
1196
+ useContentTypeSchema();
1197
+ const isLoading = isLoadingDoc || layout.isLoading;
1198
+ const error = layout.error;
1199
+ return {
1200
+ error,
1201
+ isLoading,
1202
+ // Base metadata
1203
+ model,
1204
+ collectionType,
1205
+ id,
1206
+ slug,
1207
+ isCreatingEntry,
1208
+ isSingleType,
1209
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1210
+ // All schema infos
1211
+ components,
1212
+ contentType: schema,
1213
+ contentTypes: schemas,
1214
+ // Form state
1215
+ form,
1216
+ // layout infos
1217
+ layout
1218
+ };
1219
+ };
1220
+ const prefixPluginTranslations = (trad, pluginId) => {
1221
+ if (!pluginId) {
1222
+ throw new TypeError("pluginId can't be empty");
1223
+ }
1224
+ return Object.keys(trad).reduce((acc, current) => {
1225
+ acc[`${pluginId}.${current}`] = trad[current];
1226
+ return acc;
1227
+ }, {});
1228
+ };
1229
+ const getTranslation = (id) => `content-manager.${id}`;
1230
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1231
+ id: "notification.error",
1232
+ defaultMessage: "An error occurred, please try again"
1233
+ };
1234
+ const useDocumentActions = () => {
1235
+ const { toggleNotification } = useNotification();
1236
+ const { formatMessage } = useIntl();
1237
+ const { trackUsage } = useTracking();
1238
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1239
+ const navigate = useNavigate();
1240
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
1241
+ const [deleteDocument] = useDeleteDocumentMutation();
1242
+ const _delete = React.useCallback(
1243
+ async ({ collectionType, model, documentId, params }, trackerProperty) => {
1113
1244
  try {
1114
- trackUsage("willUnpublishEntry");
1115
- const res = await unpublishDocument({
1245
+ trackUsage("willDeleteEntry", trackerProperty);
1246
+ const res = await deleteDocument({
1116
1247
  collectionType,
1117
1248
  model,
1118
1249
  documentId,
1119
- params,
1120
- data: {
1121
- discardDraft
1122
- }
1250
+ params
1123
1251
  });
1124
1252
  if ("error" in res) {
1125
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1253
+ toggleNotification({
1254
+ type: "danger",
1255
+ message: formatAPIError(res.error)
1256
+ });
1126
1257
  return { error: res.error };
1127
1258
  }
1128
- trackUsage("didUnpublishEntry");
1129
1259
  toggleNotification({
1130
1260
  type: "success",
1131
1261
  message: formatMessage({
1132
- id: getTranslation("success.record.unpublish"),
1133
- defaultMessage: "Unpublished document"
1262
+ id: getTranslation("success.record.delete"),
1263
+ defaultMessage: "Deleted document"
1134
1264
  })
1135
1265
  });
1266
+ trackUsage("didDeleteEntry", trackerProperty);
1136
1267
  return res.data;
1137
1268
  } catch (err) {
1138
1269
  toggleNotification({
1139
1270
  type: "danger",
1140
1271
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1141
1272
  });
1273
+ trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
1142
1274
  throw err;
1143
1275
  }
1144
1276
  },
1145
- [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1277
+ [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
1146
1278
  );
1147
- const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1148
- const unpublishMany = React.useCallback(
1279
+ const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
1280
+ const deleteMany = React.useCallback(
1149
1281
  async ({ model, documentIds, params }) => {
1150
1282
  try {
1151
- trackUsage("willBulkUnpublishEntries");
1152
- const res = await unpublishManyDocuments({
1283
+ trackUsage("willBulkDeleteEntries");
1284
+ const res = await deleteManyDocuments({
1153
1285
  model,
1154
1286
  documentIds,
1155
1287
  params
1156
1288
  });
1157
1289
  if ("error" in res) {
1158
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1290
+ toggleNotification({
1291
+ type: "danger",
1292
+ message: formatAPIError(res.error)
1293
+ });
1159
1294
  return { error: res.error };
1160
1295
  }
1161
- trackUsage("didBulkUnpublishEntries");
1162
1296
  toggleNotification({
1163
1297
  type: "success",
1164
1298
  title: formatMessage({
1165
- id: getTranslation("success.records.unpublish"),
1166
- defaultMessage: "Successfully unpublished."
1299
+ id: getTranslation("success.records.delete"),
1300
+ defaultMessage: "Successfully deleted."
1167
1301
  }),
1168
1302
  message: ""
1169
1303
  });
1304
+ trackUsage("didBulkDeleteEntries");
1170
1305
  return res.data;
1171
1306
  } catch (err) {
1172
1307
  toggleNotification({
1173
1308
  type: "danger",
1174
1309
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1175
1310
  });
1176
- trackUsage("didNotBulkUnpublishEntries");
1311
+ trackUsage("didNotBulkDeleteEntries");
1177
1312
  throw err;
1178
1313
  }
1179
1314
  },
1180
- [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1315
+ [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1181
1316
  );
1182
- const [createDocument] = useCreateDocumentMutation();
1183
- const create = React.useCallback(
1184
- async ({ model, params }, data, trackerProperty) => {
1317
+ const [discardDocument] = useDiscardDocumentMutation();
1318
+ const discard = React.useCallback(
1319
+ async ({ collectionType, model, documentId, params }) => {
1185
1320
  try {
1186
- const res = await createDocument({
1321
+ const res = await discardDocument({
1322
+ collectionType,
1187
1323
  model,
1188
- data,
1324
+ documentId,
1189
1325
  params
1190
1326
  });
1191
1327
  if ("error" in res) {
1192
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1193
- trackUsage("didNotCreateEntry", { error: res.error, ...trackerProperty });
1328
+ toggleNotification({
1329
+ type: "danger",
1330
+ message: formatAPIError(res.error)
1331
+ });
1194
1332
  return { error: res.error };
1195
1333
  }
1196
- trackUsage("didCreateEntry", trackerProperty);
1197
1334
  toggleNotification({
1198
1335
  type: "success",
1199
1336
  message: formatMessage({
1200
- id: getTranslation("success.record.save"),
1201
- defaultMessage: "Saved document"
1337
+ id: "content-manager.success.record.discard",
1338
+ defaultMessage: "Changes discarded"
1202
1339
  })
1203
1340
  });
1204
1341
  return res.data;
@@ -1207,19 +1344,234 @@ const useDocumentActions = () => {
1207
1344
  type: "danger",
1208
1345
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1209
1346
  });
1210
- trackUsage("didNotCreateEntry", { error: err, ...trackerProperty });
1211
1347
  throw err;
1212
1348
  }
1213
1349
  },
1214
- [createDocument, formatAPIError, formatMessage, toggleNotification, trackUsage]
1350
+ [discardDocument, formatAPIError, formatMessage, toggleNotification]
1215
1351
  );
1216
- const [autoCloneDocument] = useAutoCloneDocumentMutation();
1217
- const autoClone = React.useCallback(
1218
- async ({ model, sourceId }) => {
1352
+ const [publishDocument] = usePublishDocumentMutation();
1353
+ const publish = React.useCallback(
1354
+ async ({ collectionType, model, documentId, params }, data) => {
1219
1355
  try {
1220
- const res = await autoCloneDocument({
1356
+ trackUsage("willPublishEntry");
1357
+ const res = await publishDocument({
1358
+ collectionType,
1221
1359
  model,
1222
- sourceId
1360
+ documentId,
1361
+ data,
1362
+ params
1363
+ });
1364
+ if ("error" in res) {
1365
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1366
+ return { error: res.error };
1367
+ }
1368
+ trackUsage("didPublishEntry");
1369
+ toggleNotification({
1370
+ type: "success",
1371
+ message: formatMessage({
1372
+ id: getTranslation("success.record.publish"),
1373
+ defaultMessage: "Published document"
1374
+ })
1375
+ });
1376
+ return res.data;
1377
+ } catch (err) {
1378
+ toggleNotification({
1379
+ type: "danger",
1380
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1381
+ });
1382
+ throw err;
1383
+ }
1384
+ },
1385
+ [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1386
+ );
1387
+ const [publishManyDocuments] = usePublishManyDocumentsMutation();
1388
+ const publishMany = React.useCallback(
1389
+ async ({ model, documentIds, params }) => {
1390
+ try {
1391
+ const res = await publishManyDocuments({
1392
+ model,
1393
+ documentIds,
1394
+ params
1395
+ });
1396
+ if ("error" in res) {
1397
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1398
+ return { error: res.error };
1399
+ }
1400
+ toggleNotification({
1401
+ type: "success",
1402
+ message: formatMessage({
1403
+ id: getTranslation("success.record.publish"),
1404
+ defaultMessage: "Published document"
1405
+ })
1406
+ });
1407
+ return res.data;
1408
+ } catch (err) {
1409
+ toggleNotification({
1410
+ type: "danger",
1411
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1412
+ });
1413
+ throw err;
1414
+ }
1415
+ },
1416
+ [
1417
+ // trackUsage,
1418
+ publishManyDocuments,
1419
+ toggleNotification,
1420
+ formatMessage,
1421
+ formatAPIError
1422
+ ]
1423
+ );
1424
+ const [updateDocument] = useUpdateDocumentMutation();
1425
+ const update = React.useCallback(
1426
+ async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1427
+ try {
1428
+ trackUsage("willEditEntry", trackerProperty);
1429
+ const res = await updateDocument({
1430
+ collectionType,
1431
+ model,
1432
+ documentId,
1433
+ data,
1434
+ params
1435
+ });
1436
+ if ("error" in res) {
1437
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1438
+ trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1439
+ return { error: res.error };
1440
+ }
1441
+ trackUsage("didEditEntry", trackerProperty);
1442
+ toggleNotification({
1443
+ type: "success",
1444
+ message: formatMessage({
1445
+ id: getTranslation("success.record.save"),
1446
+ defaultMessage: "Saved document"
1447
+ })
1448
+ });
1449
+ return res.data;
1450
+ } catch (err) {
1451
+ trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1452
+ toggleNotification({
1453
+ type: "danger",
1454
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1455
+ });
1456
+ throw err;
1457
+ }
1458
+ },
1459
+ [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1460
+ );
1461
+ const [unpublishDocument] = useUnpublishDocumentMutation();
1462
+ const unpublish = React.useCallback(
1463
+ async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1464
+ try {
1465
+ trackUsage("willUnpublishEntry");
1466
+ const res = await unpublishDocument({
1467
+ collectionType,
1468
+ model,
1469
+ documentId,
1470
+ params,
1471
+ data: {
1472
+ discardDraft
1473
+ }
1474
+ });
1475
+ if ("error" in res) {
1476
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1477
+ return { error: res.error };
1478
+ }
1479
+ trackUsage("didUnpublishEntry");
1480
+ toggleNotification({
1481
+ type: "success",
1482
+ message: formatMessage({
1483
+ id: getTranslation("success.record.unpublish"),
1484
+ defaultMessage: "Unpublished document"
1485
+ })
1486
+ });
1487
+ return res.data;
1488
+ } catch (err) {
1489
+ toggleNotification({
1490
+ type: "danger",
1491
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1492
+ });
1493
+ throw err;
1494
+ }
1495
+ },
1496
+ [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1497
+ );
1498
+ const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1499
+ const unpublishMany = React.useCallback(
1500
+ async ({ model, documentIds, params }) => {
1501
+ try {
1502
+ trackUsage("willBulkUnpublishEntries");
1503
+ const res = await unpublishManyDocuments({
1504
+ model,
1505
+ documentIds,
1506
+ params
1507
+ });
1508
+ if ("error" in res) {
1509
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1510
+ return { error: res.error };
1511
+ }
1512
+ trackUsage("didBulkUnpublishEntries");
1513
+ toggleNotification({
1514
+ type: "success",
1515
+ title: formatMessage({
1516
+ id: getTranslation("success.records.unpublish"),
1517
+ defaultMessage: "Successfully unpublished."
1518
+ }),
1519
+ message: ""
1520
+ });
1521
+ return res.data;
1522
+ } catch (err) {
1523
+ toggleNotification({
1524
+ type: "danger",
1525
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1526
+ });
1527
+ trackUsage("didNotBulkUnpublishEntries");
1528
+ throw err;
1529
+ }
1530
+ },
1531
+ [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1532
+ );
1533
+ const [createDocument] = useCreateDocumentMutation();
1534
+ const create = React.useCallback(
1535
+ async ({ model, params }, data, trackerProperty) => {
1536
+ try {
1537
+ const res = await createDocument({
1538
+ model,
1539
+ data,
1540
+ params
1541
+ });
1542
+ if ("error" in res) {
1543
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1544
+ trackUsage("didNotCreateEntry", { error: res.error, ...trackerProperty });
1545
+ return { error: res.error };
1546
+ }
1547
+ trackUsage("didCreateEntry", trackerProperty);
1548
+ toggleNotification({
1549
+ type: "success",
1550
+ message: formatMessage({
1551
+ id: getTranslation("success.record.save"),
1552
+ defaultMessage: "Saved document"
1553
+ })
1554
+ });
1555
+ setCurrentStep("contentManager.success");
1556
+ return res.data;
1557
+ } catch (err) {
1558
+ toggleNotification({
1559
+ type: "danger",
1560
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1561
+ });
1562
+ trackUsage("didNotCreateEntry", { error: err, ...trackerProperty });
1563
+ throw err;
1564
+ }
1565
+ },
1566
+ [createDocument, formatAPIError, formatMessage, toggleNotification, trackUsage]
1567
+ );
1568
+ const [autoCloneDocument] = useAutoCloneDocumentMutation();
1569
+ const autoClone = React.useCallback(
1570
+ async ({ model, sourceId }) => {
1571
+ try {
1572
+ const res = await autoCloneDocument({
1573
+ model,
1574
+ sourceId
1223
1575
  });
1224
1576
  if ("error" in res) {
1225
1577
  return { error: res.error };
@@ -1303,7 +1655,7 @@ const useDocumentActions = () => {
1303
1655
  };
1304
1656
  };
1305
1657
  const ProtectedHistoryPage = lazy(
1306
- () => import("./History-utls71em.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1658
+ () => import("./History-2Ah2CQ4T.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1307
1659
  );
1308
1660
  const routes$1 = [
1309
1661
  {
@@ -1316,31 +1668,31 @@ const routes$1 = [
1316
1668
  }
1317
1669
  ];
1318
1670
  const ProtectedEditViewPage = lazy(
1319
- () => import("./EditViewPage-zFjJK0s8.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1671
+ () => import("./EditViewPage-Dzpno8xI.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1320
1672
  );
1321
1673
  const ProtectedListViewPage = lazy(
1322
- () => import("./ListViewPage-CdKd-PS_.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1674
+ () => import("./ListViewPage-B75x3nz2.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1323
1675
  );
1324
1676
  const ProtectedListConfiguration = lazy(
1325
- () => import("./ListConfigurationPage-CuMXWWqb.mjs").then((mod) => ({
1677
+ () => import("./ListConfigurationPage-BjSJlaoC.mjs").then((mod) => ({
1326
1678
  default: mod.ProtectedListConfiguration
1327
1679
  }))
1328
1680
  );
1329
1681
  const ProtectedEditConfigurationPage = lazy(
1330
- () => import("./EditConfigurationPage-I2kKh9dx.mjs").then((mod) => ({
1682
+ () => import("./EditConfigurationPage-CI4XoymK.mjs").then((mod) => ({
1331
1683
  default: mod.ProtectedEditConfigurationPage
1332
1684
  }))
1333
1685
  );
1334
1686
  const ProtectedComponentConfigurationPage = lazy(
1335
- () => import("./ComponentConfigurationPage-CnL10QYC.mjs").then((mod) => ({
1687
+ () => import("./ComponentConfigurationPage-7-qB29e7.mjs").then((mod) => ({
1336
1688
  default: mod.ProtectedComponentConfigurationPage
1337
1689
  }))
1338
1690
  );
1339
1691
  const NoPermissions = lazy(
1340
- () => import("./NoPermissionsPage-DlWi4BAH.mjs").then((mod) => ({ default: mod.NoPermissions }))
1692
+ () => import("./NoPermissionsPage-SFllMekk.mjs").then((mod) => ({ default: mod.NoPermissions }))
1341
1693
  );
1342
1694
  const NoContentType = lazy(
1343
- () => import("./NoContentTypePage-DkToTT7u.mjs").then((mod) => ({ default: mod.NoContentType }))
1695
+ () => import("./NoContentTypePage-DUacQSyF.mjs").then((mod) => ({ default: mod.NoContentType }))
1344
1696
  );
1345
1697
  const CollectionTypePages = () => {
1346
1698
  const { collectionType } = useParams();
@@ -2398,10 +2750,9 @@ const HeaderActions = ({ actions: actions2 }) => {
2398
2750
  SingleSelect,
2399
2751
  {
2400
2752
  size: "S",
2401
- disabled: action.disabled,
2402
- "aria-label": action.label,
2403
2753
  onChange: action.onSelect,
2404
- value: action.value,
2754
+ "aria-label": action.label,
2755
+ ...action,
2405
2756
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2406
2757
  },
2407
2758
  action.id
@@ -2465,490 +2816,191 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2465
2816
  position: "header"
2466
2817
  };
2467
2818
  };
2468
- ConfigureTheViewAction.type = "configure-the-view";
2469
- const EditTheModelAction = ({ model }) => {
2470
- const navigate = useNavigate();
2471
- const { formatMessage } = useIntl();
2472
- return {
2473
- label: formatMessage({
2474
- id: "content-manager.link-to-ctb",
2475
- defaultMessage: "Edit the model"
2476
- }),
2477
- icon: /* @__PURE__ */ jsx(Pencil, {}),
2478
- onClick: () => {
2479
- navigate(`/plugins/content-type-builder/content-types/${model}`);
2480
- },
2481
- position: "header"
2482
- };
2483
- };
2484
- EditTheModelAction.type = "edit-the-model";
2485
- const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2486
- const navigate = useNavigate();
2487
- const { formatMessage } = useIntl();
2488
- const listViewPathMatch = useMatch(LIST_PATH);
2489
- const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2490
- const { delete: deleteAction } = useDocumentActions();
2491
- const { toggleNotification } = useNotification();
2492
- const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2493
- return {
2494
- disabled: !canDelete || !document,
2495
- label: formatMessage({
2496
- id: "content-manager.actions.delete.label",
2497
- defaultMessage: "Delete document"
2498
- }),
2499
- icon: /* @__PURE__ */ jsx(Trash, {}),
2500
- dialog: {
2501
- type: "dialog",
2502
- title: formatMessage({
2503
- id: "app.components.ConfirmDialog.title",
2504
- defaultMessage: "Confirmation"
2505
- }),
2506
- content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
2507
- /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2508
- /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2509
- id: "content-manager.actions.delete.dialog.body",
2510
- defaultMessage: "Are you sure?"
2511
- }) })
2512
- ] }),
2513
- onConfirm: async () => {
2514
- if (!listViewPathMatch) {
2515
- setSubmitting(true);
2516
- }
2517
- try {
2518
- if (!documentId && collectionType !== SINGLE_TYPES) {
2519
- console.error(
2520
- "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2521
- );
2522
- toggleNotification({
2523
- message: formatMessage({
2524
- id: "content-manager.actions.delete.error",
2525
- defaultMessage: "An error occurred while trying to delete the document."
2526
- }),
2527
- type: "danger"
2528
- });
2529
- return;
2530
- }
2531
- const res = await deleteAction({
2532
- documentId,
2533
- model,
2534
- collectionType,
2535
- params: {
2536
- locale: "*"
2537
- }
2538
- });
2539
- if (!("error" in res)) {
2540
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2541
- }
2542
- } finally {
2543
- if (!listViewPathMatch) {
2544
- setSubmitting(false);
2545
- }
2546
- }
2547
- }
2548
- },
2549
- variant: "danger",
2550
- position: ["header", "table-row"]
2551
- };
2552
- };
2553
- DeleteAction$1.type = "delete";
2554
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2555
- const Panels = () => {
2556
- const isCloning = useMatch(CLONE_PATH) !== null;
2557
- const [
2558
- {
2559
- query: { status }
2560
- }
2561
- ] = useQueryParams({
2562
- status: "draft"
2563
- });
2564
- const { model, id, document, meta, collectionType } = useDoc();
2565
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2566
- const props = {
2567
- activeTab: status,
2568
- model,
2569
- documentId: id,
2570
- document: isCloning ? void 0 : document,
2571
- meta: isCloning ? void 0 : meta,
2572
- collectionType
2573
- };
2574
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2575
- DescriptionComponentRenderer,
2576
- {
2577
- props,
2578
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2579
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2580
- }
2581
- ) });
2582
- };
2583
- const ActionsPanel = () => {
2584
- const { formatMessage } = useIntl();
2585
- return {
2586
- title: formatMessage({
2587
- id: "content-manager.containers.edit.panels.default.title",
2588
- defaultMessage: "Entry"
2589
- }),
2590
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2591
- };
2592
- };
2593
- ActionsPanel.type = "actions";
2594
- const ActionsPanelContent = () => {
2595
- const isCloning = useMatch(CLONE_PATH) !== null;
2596
- const [
2597
- {
2598
- query: { status = "draft" }
2599
- }
2600
- ] = useQueryParams();
2601
- const { model, id, document, meta, collectionType } = useDoc();
2602
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2603
- const props = {
2604
- activeTab: status,
2605
- model,
2606
- documentId: id,
2607
- document: isCloning ? void 0 : document,
2608
- meta: isCloning ? void 0 : meta,
2609
- collectionType
2610
- };
2611
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2612
- /* @__PURE__ */ jsx(
2613
- DescriptionComponentRenderer,
2614
- {
2615
- props,
2616
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2617
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2618
- }
2619
- ),
2620
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2621
- ] });
2622
- };
2623
- const Panel = React.forwardRef(({ children, title }, ref) => {
2624
- return /* @__PURE__ */ jsxs(
2625
- Flex,
2626
- {
2627
- ref,
2628
- tag: "aside",
2629
- "aria-labelledby": "additional-information",
2630
- background: "neutral0",
2631
- borderColor: "neutral150",
2632
- hasRadius: true,
2633
- paddingBottom: 4,
2634
- paddingLeft: 4,
2635
- paddingRight: 4,
2636
- paddingTop: 4,
2637
- shadow: "tableShadow",
2638
- gap: 3,
2639
- direction: "column",
2640
- justifyContent: "stretch",
2641
- alignItems: "flex-start",
2642
- children: [
2643
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2644
- children
2645
- ]
2646
- }
2647
- );
2648
- });
2649
- const HOOKS = {
2650
- /**
2651
- * Hook that allows to mutate the displayed headers of the list view table
2652
- * @constant
2653
- * @type {string}
2654
- */
2655
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2656
- /**
2657
- * Hook that allows to mutate the CM's collection types links pre-set filters
2658
- * @constant
2659
- * @type {string}
2660
- */
2661
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2662
- /**
2663
- * Hook that allows to mutate the CM's edit view layout
2664
- * @constant
2665
- * @type {string}
2666
- */
2667
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2668
- /**
2669
- * Hook that allows to mutate the CM's single types links pre-set filters
2670
- * @constant
2671
- * @type {string}
2672
- */
2673
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2674
- };
2675
- const contentTypesApi = contentManagerApi.injectEndpoints({
2676
- endpoints: (builder) => ({
2677
- getContentTypeConfiguration: builder.query({
2678
- query: (uid) => ({
2679
- url: `/content-manager/content-types/${uid}/configuration`,
2680
- method: "GET"
2681
- }),
2682
- transformResponse: (response) => response.data,
2683
- providesTags: (_result, _error, uid) => [
2684
- { type: "ContentTypesConfiguration", id: uid },
2685
- { type: "ContentTypeSettings", id: "LIST" }
2686
- ]
2687
- }),
2688
- getAllContentTypeSettings: builder.query({
2689
- query: () => "/content-manager/content-types-settings",
2690
- transformResponse: (response) => response.data,
2691
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2692
- }),
2693
- updateContentTypeConfiguration: builder.mutation({
2694
- query: ({ uid, ...body }) => ({
2695
- url: `/content-manager/content-types/${uid}/configuration`,
2696
- method: "PUT",
2697
- data: body
2698
- }),
2699
- transformResponse: (response) => response.data,
2700
- invalidatesTags: (_result, _error, { uid }) => [
2701
- { type: "ContentTypesConfiguration", id: uid },
2702
- { type: "ContentTypeSettings", id: "LIST" },
2703
- // Is this necessary?
2704
- { type: "InitialData" }
2705
- ]
2706
- })
2707
- })
2708
- });
2709
- const {
2710
- useGetContentTypeConfigurationQuery,
2711
- useGetAllContentTypeSettingsQuery,
2712
- useUpdateContentTypeConfigurationMutation
2713
- } = contentTypesApi;
2714
- const checkIfAttributeIsDisplayable = (attribute) => {
2715
- const { type } = attribute;
2716
- if (type === "relation") {
2717
- return !attribute.relation.toLowerCase().includes("morph");
2718
- }
2719
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2720
- };
2721
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2722
- if (!mainFieldName) {
2723
- return void 0;
2724
- }
2725
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2726
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2727
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2728
- );
2729
- return {
2730
- name: mainFieldName,
2731
- type: mainFieldType ?? "string"
2732
- };
2733
- };
2734
- const DEFAULT_SETTINGS = {
2735
- bulkable: false,
2736
- filterable: false,
2737
- searchable: false,
2738
- pagination: false,
2739
- defaultSortBy: "",
2740
- defaultSortOrder: "asc",
2741
- mainField: "id",
2742
- pageSize: 10
2743
- };
2744
- const useDocumentLayout = (model) => {
2745
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2746
- const [{ query }] = useQueryParams();
2747
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2748
- const { toggleNotification } = useNotification();
2749
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2750
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2751
- const {
2752
- data,
2753
- isLoading: isLoadingConfigs,
2754
- error,
2755
- isFetching: isFetchingConfigs
2756
- } = useGetContentTypeConfigurationQuery(model);
2757
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2758
- React.useEffect(() => {
2759
- if (error) {
2760
- toggleNotification({
2761
- type: "danger",
2762
- message: formatAPIError(error)
2763
- });
2764
- }
2765
- }, [error, formatAPIError, toggleNotification]);
2766
- const editLayout = React.useMemo(
2767
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2768
- layout: [],
2769
- components: {},
2770
- metadatas: {},
2771
- options: {},
2772
- settings: DEFAULT_SETTINGS
2773
- },
2774
- [data, isLoading, schemas, schema, components]
2775
- );
2776
- const listLayout = React.useMemo(() => {
2777
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2778
- layout: [],
2779
- metadatas: {},
2780
- options: {},
2781
- settings: DEFAULT_SETTINGS
2782
- };
2783
- }, [data, isLoading, schemas, schema, components]);
2784
- const { layout: edit } = React.useMemo(
2785
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2786
- layout: editLayout,
2787
- query
2788
- }),
2789
- [editLayout, query, runHookWaterfall]
2790
- );
2791
- return {
2792
- error,
2793
- isLoading,
2794
- edit,
2795
- list: listLayout
2796
- };
2797
- };
2798
- const useDocLayout = () => {
2799
- const { model } = useDoc();
2800
- return useDocumentLayout(model);
2801
- };
2802
- const formatEditLayout = (data, {
2803
- schemas,
2804
- schema,
2805
- components
2806
- }) => {
2807
- let currentPanelIndex = 0;
2808
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2809
- data.contentType.layouts.edit,
2810
- schema?.attributes,
2811
- data.contentType.metadatas,
2812
- { configurations: data.components, schemas: components },
2813
- schemas
2814
- ).reduce((panels, row) => {
2815
- if (row.some((field) => field.type === "dynamiczone")) {
2816
- panels.push([row]);
2817
- currentPanelIndex += 2;
2818
- } else {
2819
- if (!panels[currentPanelIndex]) {
2820
- panels.push([]);
2821
- }
2822
- panels[currentPanelIndex].push(row);
2823
- }
2824
- return panels;
2825
- }, []);
2826
- const componentEditAttributes = Object.entries(data.components).reduce(
2827
- (acc, [uid, configuration]) => {
2828
- acc[uid] = {
2829
- layout: convertEditLayoutToFieldLayouts(
2830
- configuration.layouts.edit,
2831
- components[uid].attributes,
2832
- configuration.metadatas,
2833
- { configurations: data.components, schemas: components }
2834
- ),
2835
- settings: {
2836
- ...configuration.settings,
2837
- icon: components[uid].info.icon,
2838
- displayName: components[uid].info.displayName
2839
- }
2840
- };
2841
- return acc;
2842
- },
2843
- {}
2844
- );
2845
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2846
- (acc, [attribute, metadata]) => {
2847
- return {
2848
- ...acc,
2849
- [attribute]: metadata.edit
2850
- };
2851
- },
2852
- {}
2853
- );
2819
+ ConfigureTheViewAction.type = "configure-the-view";
2820
+ const EditTheModelAction = ({ model }) => {
2821
+ const navigate = useNavigate();
2822
+ const { formatMessage } = useIntl();
2854
2823
  return {
2855
- layout: panelledEditAttributes,
2856
- components: componentEditAttributes,
2857
- metadatas: editMetadatas,
2858
- settings: {
2859
- ...data.contentType.settings,
2860
- displayName: schema?.info.displayName
2824
+ label: formatMessage({
2825
+ id: "content-manager.link-to-ctb",
2826
+ defaultMessage: "Edit the model"
2827
+ }),
2828
+ icon: /* @__PURE__ */ jsx(Pencil, {}),
2829
+ onClick: () => {
2830
+ navigate(`/plugins/content-type-builder/content-types/${model}`);
2861
2831
  },
2862
- options: {
2863
- ...schema?.options,
2864
- ...schema?.pluginOptions,
2865
- ...data.contentType.options
2866
- }
2832
+ position: "header"
2867
2833
  };
2868
2834
  };
2869
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2870
- return rows.map(
2871
- (row) => row.map((field) => {
2872
- const attribute = attributes[field.name];
2873
- if (!attribute) {
2874
- return null;
2835
+ EditTheModelAction.type = "edit-the-model";
2836
+ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2837
+ const navigate = useNavigate();
2838
+ const { formatMessage } = useIntl();
2839
+ const listViewPathMatch = useMatch(LIST_PATH);
2840
+ const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2841
+ const { delete: deleteAction } = useDocumentActions();
2842
+ const { toggleNotification } = useNotification();
2843
+ const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2844
+ const isLocalized = document?.locale != null;
2845
+ return {
2846
+ disabled: !canDelete || !document,
2847
+ label: formatMessage(
2848
+ {
2849
+ id: "content-manager.actions.delete.label",
2850
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2851
+ },
2852
+ { isLocalized }
2853
+ ),
2854
+ icon: /* @__PURE__ */ jsx(Trash, {}),
2855
+ dialog: {
2856
+ type: "dialog",
2857
+ title: formatMessage({
2858
+ id: "app.components.ConfirmDialog.title",
2859
+ defaultMessage: "Confirmation"
2860
+ }),
2861
+ content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
2862
+ /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2863
+ /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2864
+ id: "content-manager.actions.delete.dialog.body",
2865
+ defaultMessage: "Are you sure?"
2866
+ }) })
2867
+ ] }),
2868
+ onConfirm: async () => {
2869
+ if (!listViewPathMatch) {
2870
+ setSubmitting(true);
2871
+ }
2872
+ try {
2873
+ if (!documentId && collectionType !== SINGLE_TYPES) {
2874
+ console.error(
2875
+ "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2876
+ );
2877
+ toggleNotification({
2878
+ message: formatMessage({
2879
+ id: "content-manager.actions.delete.error",
2880
+ defaultMessage: "An error occurred while trying to delete the document."
2881
+ }),
2882
+ type: "danger"
2883
+ });
2884
+ return;
2885
+ }
2886
+ const res = await deleteAction({
2887
+ documentId,
2888
+ model,
2889
+ collectionType,
2890
+ params: {
2891
+ locale: "*"
2892
+ }
2893
+ });
2894
+ if (!("error" in res)) {
2895
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2896
+ }
2897
+ } finally {
2898
+ if (!listViewPathMatch) {
2899
+ setSubmitting(false);
2900
+ }
2901
+ }
2875
2902
  }
2876
- const { edit: metadata } = metadatas[field.name];
2877
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2878
- return {
2879
- attribute,
2880
- disabled: !metadata.editable,
2881
- hint: metadata.description,
2882
- label: metadata.label ?? "",
2883
- name: field.name,
2884
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2885
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2886
- schemas,
2887
- components: components?.schemas ?? {}
2888
- }),
2889
- placeholder: metadata.placeholder ?? "",
2890
- required: attribute.required ?? false,
2891
- size: field.size,
2892
- unique: "unique" in attribute ? attribute.unique : false,
2893
- visible: metadata.visible ?? true,
2894
- type: attribute.type
2895
- };
2896
- }).filter((field) => field !== null)
2897
- );
2898
- };
2899
- const formatListLayout = (data, {
2900
- schemas,
2901
- schema,
2902
- components
2903
- }) => {
2904
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2905
- (acc, [attribute, metadata]) => {
2906
- return {
2907
- ...acc,
2908
- [attribute]: metadata.list
2909
- };
2910
2903
  },
2911
- {}
2912
- );
2913
- const listAttributes = convertListLayoutToFieldLayouts(
2914
- data.contentType.layouts.list,
2915
- schema?.attributes,
2916
- listMetadatas,
2917
- { configurations: data.components, schemas: components },
2918
- schemas
2919
- );
2920
- return {
2921
- layout: listAttributes,
2922
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2923
- metadatas: listMetadatas,
2924
- options: {
2925
- ...schema?.options,
2926
- ...schema?.pluginOptions,
2927
- ...data.contentType.options
2904
+ variant: "danger",
2905
+ position: ["header", "table-row"]
2906
+ };
2907
+ };
2908
+ DeleteAction$1.type = "delete";
2909
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2910
+ const Panels = () => {
2911
+ const isCloning = useMatch(CLONE_PATH) !== null;
2912
+ const [
2913
+ {
2914
+ query: { status }
2915
+ }
2916
+ ] = useQueryParams({
2917
+ status: "draft"
2918
+ });
2919
+ const { model, id, document, meta, collectionType } = useDoc();
2920
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
2921
+ const props = {
2922
+ activeTab: status,
2923
+ model,
2924
+ documentId: id,
2925
+ document: isCloning ? void 0 : document,
2926
+ meta: isCloning ? void 0 : meta,
2927
+ collectionType
2928
+ };
2929
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2930
+ DescriptionComponentRenderer,
2931
+ {
2932
+ props,
2933
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2934
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2928
2935
  }
2936
+ ) });
2937
+ };
2938
+ const ActionsPanel = () => {
2939
+ const { formatMessage } = useIntl();
2940
+ return {
2941
+ title: formatMessage({
2942
+ id: "content-manager.containers.edit.panels.default.title",
2943
+ defaultMessage: "Entry"
2944
+ }),
2945
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2929
2946
  };
2930
2947
  };
2931
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2932
- return columns.map((name) => {
2933
- const attribute = attributes[name];
2934
- if (!attribute) {
2935
- return null;
2948
+ ActionsPanel.type = "actions";
2949
+ const ActionsPanelContent = () => {
2950
+ const isCloning = useMatch(CLONE_PATH) !== null;
2951
+ const [
2952
+ {
2953
+ query: { status = "draft" }
2936
2954
  }
2937
- const metadata = metadatas[name];
2938
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2939
- return {
2940
- attribute,
2941
- label: metadata.label ?? "",
2942
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2943
- schemas,
2944
- components: components?.schemas ?? {}
2945
- }),
2946
- name,
2947
- searchable: metadata.searchable ?? true,
2948
- sortable: metadata.sortable ?? true
2949
- };
2950
- }).filter((field) => field !== null);
2955
+ ] = useQueryParams();
2956
+ const { model, id, document, meta, collectionType } = useDoc();
2957
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2958
+ const props = {
2959
+ activeTab: status,
2960
+ model,
2961
+ documentId: id,
2962
+ document: isCloning ? void 0 : document,
2963
+ meta: isCloning ? void 0 : meta,
2964
+ collectionType
2965
+ };
2966
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2967
+ /* @__PURE__ */ jsx(
2968
+ DescriptionComponentRenderer,
2969
+ {
2970
+ props,
2971
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
2972
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2973
+ }
2974
+ ),
2975
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2976
+ ] });
2951
2977
  };
2978
+ const Panel = React.forwardRef(({ children, title }, ref) => {
2979
+ return /* @__PURE__ */ jsxs(
2980
+ Flex,
2981
+ {
2982
+ ref,
2983
+ tag: "aside",
2984
+ "aria-labelledby": "additional-information",
2985
+ background: "neutral0",
2986
+ borderColor: "neutral150",
2987
+ hasRadius: true,
2988
+ paddingBottom: 4,
2989
+ paddingLeft: 4,
2990
+ paddingRight: 4,
2991
+ paddingTop: 4,
2992
+ shadow: "tableShadow",
2993
+ gap: 3,
2994
+ direction: "column",
2995
+ justifyContent: "stretch",
2996
+ alignItems: "flex-start",
2997
+ children: [
2998
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2999
+ children
3000
+ ]
3001
+ }
3002
+ );
3003
+ });
2952
3004
  const ConfirmBulkActionDialog = ({
2953
3005
  onToggleDialog,
2954
3006
  isOpen = false,
@@ -3945,7 +3997,7 @@ const index = {
3945
3997
  app.router.addRoute({
3946
3998
  path: "content-manager/*",
3947
3999
  lazy: async () => {
3948
- const { Layout } = await import("./layout-DX_52HSH.mjs");
4000
+ const { Layout } = await import("./layout-DaUjDiWQ.mjs");
3949
4001
  return {
3950
4002
  Component: Layout
3951
4003
  };
@@ -3962,7 +4014,7 @@ const index = {
3962
4014
  async registerTrads({ locales }) {
3963
4015
  const importedTrads = await Promise.all(
3964
4016
  locales.map((locale) => {
3965
- 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-CPTj6CjC.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 }) => {
4017
+ 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 }) => {
3966
4018
  return {
3967
4019
  data: prefixPluginTranslations(data, PLUGIN_ID),
3968
4020
  locale
@@ -3990,7 +4042,8 @@ export {
3990
4042
  InjectionZone as I,
3991
4043
  useDocument as J,
3992
4044
  index as K,
3993
- useDocumentActions as L,
4045
+ useContentManagerContext as L,
4046
+ useDocumentActions as M,
3994
4047
  Panels as P,
3995
4048
  RelativeTime as R,
3996
4049
  SINGLE_TYPES as S,
@@ -4022,4 +4075,4 @@ export {
4022
4075
  capitalise as y,
4023
4076
  useUpdateContentTypeConfigurationMutation as z
4024
4077
  };
4025
- //# sourceMappingURL=index-BHfS6_D5.mjs.map
4078
+ //# sourceMappingURL=index-C9HxCo5R.mjs.map