@strapi/content-manager 5.0.0-rc.9 → 5.0.0

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 (110) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-BwnO0su2.mjs → ComponentConfigurationPage-DfFSZQxe.mjs} +3 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-BwnO0su2.mjs.map → ComponentConfigurationPage-DfFSZQxe.mjs.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-BOKmq7mz.js → ComponentConfigurationPage-FqfsxQ1j.js} +3 -3
  4. package/dist/_chunks/{ComponentConfigurationPage-BOKmq7mz.js.map → ComponentConfigurationPage-FqfsxQ1j.js.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-N3OaN53v.js → EditConfigurationPage-Cn0e8t3I.js} +3 -3
  6. package/dist/_chunks/{EditConfigurationPage-N3OaN53v.js.map → EditConfigurationPage-Cn0e8t3I.js.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-DJDSIf3D.mjs → EditConfigurationPage-DdPNAbl3.mjs} +3 -3
  8. package/dist/_chunks/{EditConfigurationPage-DJDSIf3D.mjs.map → EditConfigurationPage-DdPNAbl3.mjs.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-IZbGSeL3.mjs → EditViewPage-B82x_x1b.mjs} +15 -5
  10. package/dist/_chunks/EditViewPage-B82x_x1b.mjs.map +1 -0
  11. package/dist/_chunks/{EditViewPage-l9qOdDUI.js → EditViewPage-DlxEHhUt.js} +15 -5
  12. package/dist/_chunks/EditViewPage-DlxEHhUt.js.map +1 -0
  13. package/dist/_chunks/{Field-CWjyW47-.js → Field-COL25JiC.js} +100 -87
  14. package/dist/_chunks/Field-COL25JiC.js.map +1 -0
  15. package/dist/_chunks/{Field-D3mNj-iY.mjs → Field-DufHXW17.mjs} +97 -84
  16. package/dist/_chunks/Field-DufHXW17.mjs.map +1 -0
  17. package/dist/_chunks/{Form-DFXXFrJV.js → Form-BssUwrTO.js} +16 -8
  18. package/dist/_chunks/Form-BssUwrTO.js.map +1 -0
  19. package/dist/_chunks/{Form-Ddy3XYS-.mjs → Form-u_kAOhwB.mjs} +16 -8
  20. package/dist/_chunks/Form-u_kAOhwB.mjs.map +1 -0
  21. package/dist/_chunks/{History-pesEeFFT.js → History-C9t9UqpO.js} +23 -10
  22. package/dist/_chunks/History-C9t9UqpO.js.map +1 -0
  23. package/dist/_chunks/{History-BEFrLZfz.mjs → History-DRwA3oMM.mjs} +24 -11
  24. package/dist/_chunks/History-DRwA3oMM.mjs.map +1 -0
  25. package/dist/_chunks/{ListConfigurationPage-BIoi-Sog.js → ListConfigurationPage-BXYPohh-.js} +2 -2
  26. package/dist/_chunks/{ListConfigurationPage-BIoi-Sog.js.map → ListConfigurationPage-BXYPohh-.js.map} +1 -1
  27. package/dist/_chunks/{ListConfigurationPage-CB_lBFVX.mjs → ListConfigurationPage-BxfQJzPk.mjs} +2 -2
  28. package/dist/_chunks/{ListConfigurationPage-CB_lBFVX.mjs.map → ListConfigurationPage-BxfQJzPk.mjs.map} +1 -1
  29. package/dist/_chunks/{ListViewPage-DBbH7Esr.mjs → ListViewPage-CELx2ysp.mjs} +40 -36
  30. package/dist/_chunks/ListViewPage-CELx2ysp.mjs.map +1 -0
  31. package/dist/_chunks/{ListViewPage-C6FK36UI.js → ListViewPage-D2VD8Szg.js} +43 -39
  32. package/dist/_chunks/ListViewPage-D2VD8Szg.js.map +1 -0
  33. package/dist/_chunks/{NoContentTypePage-Dod8M_xM.js → NoContentTypePage-BV9IjJSM.js} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-Dod8M_xM.js.map → NoContentTypePage-BV9IjJSM.js.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-BU9Omzp4.mjs → NoContentTypePage-DtJ9jcfk.mjs} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-BU9Omzp4.mjs.map → NoContentTypePage-DtJ9jcfk.mjs.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-B88W05rh.mjs → NoPermissionsPage-DWleVYK7.mjs} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-B88W05rh.mjs.map → NoPermissionsPage-DWleVYK7.mjs.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-CMLH3uMk.js → NoPermissionsPage-Dp8NpF9I.js} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-CMLH3uMk.js.map → NoPermissionsPage-Dp8NpF9I.js.map} +1 -1
  41. package/dist/_chunks/{Relations-BBJRxopY.mjs → Relations-BTcf5xaw.mjs} +32 -23
  42. package/dist/_chunks/Relations-BTcf5xaw.mjs.map +1 -0
  43. package/dist/_chunks/{Relations-OXbsEvNt.js → Relations-DR7EUgyC.js} +32 -23
  44. package/dist/_chunks/Relations-DR7EUgyC.js.map +1 -0
  45. package/dist/_chunks/{en-uOUIxfcQ.js → en-Bm0D0IWz.js} +13 -12
  46. package/dist/_chunks/{en-uOUIxfcQ.js.map → en-Bm0D0IWz.js.map} +1 -1
  47. package/dist/_chunks/{en-BrCTWlZv.mjs → en-DKV44jRb.mjs} +13 -12
  48. package/dist/_chunks/{en-BrCTWlZv.mjs.map → en-DKV44jRb.mjs.map} +1 -1
  49. package/dist/_chunks/{index-Bj8wwbdx.js → index-BdMf2lfT.js} +1879 -1765
  50. package/dist/_chunks/index-BdMf2lfT.js.map +1 -0
  51. package/dist/_chunks/{index-BgiNQ7Q9.mjs → index-wnqzm4Q8.mjs} +1899 -1785
  52. package/dist/_chunks/index-wnqzm4Q8.mjs.map +1 -0
  53. package/dist/_chunks/{layout-LOFMsdWo.mjs → layout-2CfjL0T9.mjs} +5 -4
  54. package/dist/_chunks/{layout-LOFMsdWo.mjs.map → layout-2CfjL0T9.mjs.map} +1 -1
  55. package/dist/_chunks/{layout-DBvOIgWG.js → layout-B2MyZU-_.js} +5 -4
  56. package/dist/_chunks/{layout-DBvOIgWG.js.map → layout-B2MyZU-_.js.map} +1 -1
  57. package/dist/_chunks/{relations-BLilXMyN.js → relations-BH7JJGGe.js} +2 -2
  58. package/dist/_chunks/{relations-BLilXMyN.js.map → relations-BH7JJGGe.js.map} +1 -1
  59. package/dist/_chunks/{relations-BTQKCDqX.mjs → relations-C0w0GcXi.mjs} +2 -2
  60. package/dist/_chunks/{relations-BTQKCDqX.mjs.map → relations-C0w0GcXi.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 +32 -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 +11 -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/server/index.js +48 -21
  81. package/dist/server/index.js.map +1 -1
  82. package/dist/server/index.mjs +48 -21
  83. package/dist/server/index.mjs.map +1 -1
  84. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  85. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  86. package/dist/server/src/history/services/history.d.ts.map +1 -1
  87. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  88. package/dist/server/src/history/services/utils.d.ts +1 -0
  89. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  90. package/dist/shared/contracts/collection-types.d.ts +3 -1
  91. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  92. package/package.json +12 -12
  93. package/dist/_chunks/EditViewPage-IZbGSeL3.mjs.map +0 -1
  94. package/dist/_chunks/EditViewPage-l9qOdDUI.js.map +0 -1
  95. package/dist/_chunks/Field-CWjyW47-.js.map +0 -1
  96. package/dist/_chunks/Field-D3mNj-iY.mjs.map +0 -1
  97. package/dist/_chunks/Form-DFXXFrJV.js.map +0 -1
  98. package/dist/_chunks/Form-Ddy3XYS-.mjs.map +0 -1
  99. package/dist/_chunks/History-BEFrLZfz.mjs.map +0 -1
  100. package/dist/_chunks/History-pesEeFFT.js.map +0 -1
  101. package/dist/_chunks/ListViewPage-C6FK36UI.js.map +0 -1
  102. package/dist/_chunks/ListViewPage-DBbH7Esr.mjs.map +0 -1
  103. package/dist/_chunks/Relations-BBJRxopY.mjs.map +0 -1
  104. package/dist/_chunks/Relations-OXbsEvNt.js.map +0 -1
  105. package/dist/_chunks/index-BgiNQ7Q9.mjs.map +0 -1
  106. package/dist/_chunks/index-Bj8wwbdx.js.map +0 -1
  107. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  108. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  109. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  110. 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, useQueryParams, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, 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) => {
@@ -100,6 +100,7 @@ const DocumentRBAC = ({ children, permissions }) => {
100
100
  if (!slug) {
101
101
  throw new Error("Cannot find the slug param in the URL");
102
102
  }
103
+ const [{ rawQuery }] = useQueryParams();
103
104
  const userPermissions = useAuth("DocumentRBAC", (state) => state.permissions);
104
105
  const contentTypePermissions = React.useMemo(() => {
105
106
  const contentTypePermissions2 = userPermissions.filter(
@@ -110,7 +111,14 @@ const DocumentRBAC = ({ children, permissions }) => {
110
111
  return { ...acc, [action]: [permission] };
111
112
  }, {});
112
113
  }, [slug, userPermissions]);
113
- const { isLoading, allowedActions } = useRBAC(contentTypePermissions, permissions ?? void 0);
114
+ const { isLoading, allowedActions } = useRBAC(
115
+ contentTypePermissions,
116
+ permissions ?? void 0,
117
+ // TODO: useRBAC context should be typed and built differently
118
+ // We are passing raw query as context to the hook so that it can
119
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
120
+ rawQuery
121
+ );
114
122
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
115
123
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
116
124
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -158,7 +166,8 @@ const contentManagerApi = adminApi.enhanceEndpoints({
158
166
  "Document",
159
167
  "InitialData",
160
168
  "HistoryVersion",
161
- "Relations"
169
+ "Relations",
170
+ "UidAvailability"
162
171
  ]
163
172
  });
164
173
  const documentApi = contentManagerApi.injectEndpoints({
@@ -188,7 +197,10 @@ const documentApi = contentManagerApi.injectEndpoints({
188
197
  params
189
198
  }
190
199
  }),
191
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
200
+ invalidatesTags: (_result, _error, { model }) => [
201
+ { type: "Document", id: `${model}_LIST` },
202
+ { type: "UidAvailability", id: model }
203
+ ]
192
204
  }),
193
205
  /**
194
206
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -205,7 +217,8 @@ const documentApi = contentManagerApi.injectEndpoints({
205
217
  }),
206
218
  invalidatesTags: (result, _error, { model }) => [
207
219
  { type: "Document", id: `${model}_LIST` },
208
- "Relations"
220
+ "Relations",
221
+ { type: "UidAvailability", id: model }
209
222
  ]
210
223
  }),
211
224
  deleteDocument: builder.mutation({
@@ -246,7 +259,8 @@ const documentApi = contentManagerApi.injectEndpoints({
246
259
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
247
260
  },
248
261
  { type: "Document", id: `${model}_LIST` },
249
- "Relations"
262
+ "Relations",
263
+ { type: "UidAvailability", id: model }
250
264
  ];
251
265
  }
252
266
  }),
@@ -371,7 +385,8 @@ const documentApi = contentManagerApi.injectEndpoints({
371
385
  type: "Document",
372
386
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
373
387
  },
374
- "Relations"
388
+ "Relations",
389
+ { type: "UidAvailability", id: model }
375
390
  ];
376
391
  },
377
392
  async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
@@ -454,6 +469,24 @@ const buildValidParams = (query) => {
454
469
  const isBaseQueryError = (error) => {
455
470
  return error.name !== void 0;
456
471
  };
472
+ const arrayValidator = (attribute, options) => ({
473
+ message: translatedErrors.required,
474
+ test(value) {
475
+ if (options.status === "draft") {
476
+ return true;
477
+ }
478
+ if (!attribute.required) {
479
+ return true;
480
+ }
481
+ if (!value) {
482
+ return false;
483
+ }
484
+ if (Array.isArray(value) && value.length === 0) {
485
+ return false;
486
+ }
487
+ return true;
488
+ }
489
+ });
457
490
  const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
458
491
  const createModelSchema = (attributes2) => yup.object().shape(
459
492
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
@@ -461,6 +494,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
461
494
  return acc;
462
495
  }
463
496
  const validations = [
497
+ addNullableValidation,
464
498
  addRequiredValidation,
465
499
  addMinLengthValidation,
466
500
  addMaxLengthValidation,
@@ -477,12 +511,12 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
477
511
  ...acc,
478
512
  [name]: transformSchema(
479
513
  yup.array().of(createModelSchema(attributes3).nullable(false))
480
- )
514
+ ).test(arrayValidator(attribute, options))
481
515
  };
482
516
  } else {
483
517
  return {
484
518
  ...acc,
485
- [name]: transformSchema(createModelSchema(attributes3))
519
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
486
520
  };
487
521
  }
488
522
  }
@@ -504,7 +538,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
504
538
  }
505
539
  )
506
540
  )
507
- )
541
+ ).test(arrayValidator(attribute, options))
508
542
  };
509
543
  case "relation":
510
544
  return {
@@ -516,7 +550,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
516
550
  } else if (Array.isArray(value)) {
517
551
  return yup.array().of(
518
552
  yup.object().shape({
519
- id: yup.string().required()
553
+ id: yup.number().required()
520
554
  })
521
555
  );
522
556
  } else if (typeof value === "object") {
@@ -602,17 +636,17 @@ const nullableSchema = (schema) => {
602
636
  schema
603
637
  );
604
638
  };
639
+ const addNullableValidation = () => (schema) => {
640
+ return nullableSchema(schema);
641
+ };
605
642
  const addRequiredValidation = (attribute, options) => (schema) => {
606
- if (options.status === "draft") {
607
- return nullableSchema(schema);
608
- }
609
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
610
- return schema.min(1, translatedErrors.required);
643
+ if (options.status === "draft" || !attribute.required) {
644
+ return schema;
611
645
  }
612
- if (attribute.required && attribute.type !== "relation") {
646
+ if (attribute.required && "required" in schema) {
613
647
  return schema.required(translatedErrors.required);
614
648
  }
615
- return nullableSchema(schema);
649
+ return schema;
616
650
  };
617
651
  const addMinLengthValidation = (attribute, options) => (schema) => {
618
652
  if (options.status === "draft") {
@@ -640,31 +674,12 @@ const addMaxLengthValidation = (attribute) => (schema) => {
640
674
  return schema;
641
675
  };
642
676
  const addMinValidation = (attribute, options) => (schema) => {
643
- if ("min" in attribute) {
677
+ if (options.status === "draft") {
678
+ return schema;
679
+ }
680
+ if ("min" in attribute && "min" in schema) {
644
681
  const min = toInteger(attribute.min);
645
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
646
- if (options.status !== "draft" && !attribute.required && "test" in schema && min) {
647
- return schema.test(
648
- "custom-min",
649
- {
650
- ...translatedErrors.min,
651
- values: {
652
- min: attribute.min
653
- }
654
- },
655
- (value) => {
656
- if (!value) {
657
- return true;
658
- }
659
- if (Array.isArray(value) && value.length === 0) {
660
- return true;
661
- }
662
- return value.length >= min;
663
- }
664
- );
665
- }
666
- }
667
- if ("min" in schema && min) {
682
+ if (min) {
668
683
  return schema.min(min, {
669
684
  ...translatedErrors.min,
670
685
  values: {
@@ -782,19 +797,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
782
797
  }, {});
783
798
  return componentsByKey;
784
799
  };
785
- const useDocument = (args, opts) => {
800
+ const HOOKS = {
801
+ /**
802
+ * Hook that allows to mutate the displayed headers of the list view table
803
+ * @constant
804
+ * @type {string}
805
+ */
806
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
807
+ /**
808
+ * Hook that allows to mutate the CM's collection types links pre-set filters
809
+ * @constant
810
+ * @type {string}
811
+ */
812
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
813
+ /**
814
+ * Hook that allows to mutate the CM's edit view layout
815
+ * @constant
816
+ * @type {string}
817
+ */
818
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
819
+ /**
820
+ * Hook that allows to mutate the CM's single types links pre-set filters
821
+ * @constant
822
+ * @type {string}
823
+ */
824
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
825
+ };
826
+ const contentTypesApi = contentManagerApi.injectEndpoints({
827
+ endpoints: (builder) => ({
828
+ getContentTypeConfiguration: builder.query({
829
+ query: (uid) => ({
830
+ url: `/content-manager/content-types/${uid}/configuration`,
831
+ method: "GET"
832
+ }),
833
+ transformResponse: (response) => response.data,
834
+ providesTags: (_result, _error, uid) => [
835
+ { type: "ContentTypesConfiguration", id: uid },
836
+ { type: "ContentTypeSettings", id: "LIST" }
837
+ ]
838
+ }),
839
+ getAllContentTypeSettings: builder.query({
840
+ query: () => "/content-manager/content-types-settings",
841
+ transformResponse: (response) => response.data,
842
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
843
+ }),
844
+ updateContentTypeConfiguration: builder.mutation({
845
+ query: ({ uid, ...body }) => ({
846
+ url: `/content-manager/content-types/${uid}/configuration`,
847
+ method: "PUT",
848
+ data: body
849
+ }),
850
+ transformResponse: (response) => response.data,
851
+ invalidatesTags: (_result, _error, { uid }) => [
852
+ { type: "ContentTypesConfiguration", id: uid },
853
+ { type: "ContentTypeSettings", id: "LIST" },
854
+ // Is this necessary?
855
+ { type: "InitialData" }
856
+ ]
857
+ })
858
+ })
859
+ });
860
+ const {
861
+ useGetContentTypeConfigurationQuery,
862
+ useGetAllContentTypeSettingsQuery,
863
+ useUpdateContentTypeConfigurationMutation
864
+ } = contentTypesApi;
865
+ const checkIfAttributeIsDisplayable = (attribute) => {
866
+ const { type } = attribute;
867
+ if (type === "relation") {
868
+ return !attribute.relation.toLowerCase().includes("morph");
869
+ }
870
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
871
+ };
872
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
873
+ if (!mainFieldName) {
874
+ return void 0;
875
+ }
876
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
877
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
878
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
879
+ );
880
+ return {
881
+ name: mainFieldName,
882
+ type: mainFieldType ?? "string"
883
+ };
884
+ };
885
+ const DEFAULT_SETTINGS = {
886
+ bulkable: false,
887
+ filterable: false,
888
+ searchable: false,
889
+ pagination: false,
890
+ defaultSortBy: "",
891
+ defaultSortOrder: "asc",
892
+ mainField: "id",
893
+ pageSize: 10
894
+ };
895
+ const useDocumentLayout = (model) => {
896
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
897
+ const [{ query }] = useQueryParams();
898
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
786
899
  const { toggleNotification } = useNotification();
787
900
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
901
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
788
902
  const {
789
- currentData: data,
790
- isLoading: isLoadingDocument,
791
- isFetching: isFetchingDocument,
792
- error
793
- } = useGetDocumentQuery(args, {
794
- ...opts,
795
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
796
- });
797
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
903
+ data,
904
+ isLoading: isLoadingConfigs,
905
+ error,
906
+ isFetching: isFetchingConfigs
907
+ } = useGetContentTypeConfigurationQuery(model);
908
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
798
909
  React.useEffect(() => {
799
910
  if (error) {
800
911
  toggleNotification({
@@ -802,362 +913,440 @@ const useDocument = (args, opts) => {
802
913
  message: formatAPIError(error)
803
914
  });
804
915
  }
805
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
806
- const validationSchema = React.useMemo(() => {
807
- if (!schema) {
808
- return null;
809
- }
810
- return createYupSchema(schema.attributes, components);
811
- }, [schema, components]);
812
- const validate = React.useCallback(
813
- (document) => {
814
- if (!validationSchema) {
815
- throw new Error(
816
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
817
- );
818
- }
819
- try {
820
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
821
- return null;
822
- } catch (error2) {
823
- if (error2 instanceof ValidationError) {
824
- return getYupValidationErrors(error2);
825
- }
826
- throw error2;
827
- }
916
+ }, [error, formatAPIError, toggleNotification]);
917
+ const editLayout = React.useMemo(
918
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
919
+ layout: [],
920
+ components: {},
921
+ metadatas: {},
922
+ options: {},
923
+ settings: DEFAULT_SETTINGS
828
924
  },
829
- [validationSchema]
925
+ [data, isLoading, schemas, schema, components]
926
+ );
927
+ const listLayout = React.useMemo(() => {
928
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
929
+ layout: [],
930
+ metadatas: {},
931
+ options: {},
932
+ settings: DEFAULT_SETTINGS
933
+ };
934
+ }, [data, isLoading, schemas, schema, components]);
935
+ const { layout: edit } = React.useMemo(
936
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
937
+ layout: editLayout,
938
+ query
939
+ }),
940
+ [editLayout, query, runHookWaterfall]
830
941
  );
831
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
832
942
  return {
833
- components,
834
- document: data?.data,
835
- meta: data?.meta,
943
+ error,
836
944
  isLoading,
837
- schema,
838
- validate
839
- };
840
- };
841
- const useDoc = () => {
842
- const { id, slug, collectionType, origin } = useParams();
843
- const [{ query }] = useQueryParams();
844
- const params = React.useMemo(() => buildValidParams(query), [query]);
845
- if (!collectionType) {
846
- throw new Error("Could not find collectionType in url params");
847
- }
848
- if (!slug) {
849
- throw new Error("Could not find model in url params");
850
- }
851
- return {
852
- collectionType,
853
- model: slug,
854
- id: origin || id === "create" ? void 0 : id,
855
- ...useDocument(
856
- { documentId: origin || id, model: slug, collectionType, params },
857
- {
858
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
859
- }
860
- )
945
+ edit,
946
+ list: listLayout
861
947
  };
862
948
  };
863
- const prefixPluginTranslations = (trad, pluginId) => {
864
- if (!pluginId) {
865
- throw new TypeError("pluginId can't be empty");
866
- }
867
- return Object.keys(trad).reduce((acc, current) => {
868
- acc[`${pluginId}.${current}`] = trad[current];
869
- return acc;
870
- }, {});
871
- };
872
- const getTranslation = (id) => `content-manager.${id}`;
873
- const DEFAULT_UNEXPECTED_ERROR_MSG = {
874
- id: "notification.error",
875
- defaultMessage: "An error occurred, please try again"
949
+ const useDocLayout = () => {
950
+ const { model } = useDoc();
951
+ return useDocumentLayout(model);
876
952
  };
877
- const useDocumentActions = () => {
878
- const { toggleNotification } = useNotification();
879
- const { formatMessage } = useIntl();
880
- const { trackUsage } = useTracking();
881
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
882
- const [deleteDocument] = useDeleteDocumentMutation();
883
- const _delete = React.useCallback(
884
- async ({ collectionType, model, documentId, params }, trackerProperty) => {
885
- try {
886
- trackUsage("willDeleteEntry", trackerProperty);
887
- const res = await deleteDocument({
888
- collectionType,
889
- model,
890
- documentId,
891
- params
892
- });
893
- if ("error" in res) {
894
- toggleNotification({
895
- type: "danger",
896
- message: formatAPIError(res.error)
897
- });
898
- return { error: res.error };
899
- }
900
- toggleNotification({
901
- type: "success",
902
- message: formatMessage({
903
- id: getTranslation("success.record.delete"),
904
- defaultMessage: "Deleted document"
905
- })
906
- });
907
- trackUsage("didDeleteEntry", trackerProperty);
908
- return res.data;
909
- } catch (err) {
910
- toggleNotification({
911
- type: "danger",
912
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
913
- });
914
- trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
915
- throw err;
953
+ const formatEditLayout = (data, {
954
+ schemas,
955
+ schema,
956
+ components
957
+ }) => {
958
+ let currentPanelIndex = 0;
959
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
960
+ data.contentType.layouts.edit,
961
+ schema?.attributes,
962
+ data.contentType.metadatas,
963
+ { configurations: data.components, schemas: components },
964
+ schemas
965
+ ).reduce((panels, row) => {
966
+ if (row.some((field) => field.type === "dynamiczone")) {
967
+ panels.push([row]);
968
+ currentPanelIndex += 2;
969
+ } else {
970
+ if (!panels[currentPanelIndex]) {
971
+ panels.push([]);
916
972
  }
917
- },
918
- [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
919
- );
920
- const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
921
- const deleteMany = React.useCallback(
922
- async ({ model, documentIds, params }) => {
923
- try {
924
- trackUsage("willBulkDeleteEntries");
925
- const res = await deleteManyDocuments({
926
- model,
927
- documentIds,
928
- params
929
- });
930
- if ("error" in res) {
931
- toggleNotification({
932
- type: "danger",
933
- message: formatAPIError(res.error)
934
- });
935
- return { error: res.error };
973
+ panels[currentPanelIndex].push(row);
974
+ }
975
+ return panels;
976
+ }, []);
977
+ const componentEditAttributes = Object.entries(data.components).reduce(
978
+ (acc, [uid, configuration]) => {
979
+ acc[uid] = {
980
+ layout: convertEditLayoutToFieldLayouts(
981
+ configuration.layouts.edit,
982
+ components[uid].attributes,
983
+ configuration.metadatas,
984
+ { configurations: data.components, schemas: components }
985
+ ),
986
+ settings: {
987
+ ...configuration.settings,
988
+ icon: components[uid].info.icon,
989
+ displayName: components[uid].info.displayName
936
990
  }
937
- toggleNotification({
938
- type: "success",
939
- title: formatMessage({
940
- id: getTranslation("success.records.delete"),
941
- defaultMessage: "Successfully deleted."
942
- }),
943
- message: ""
944
- });
945
- trackUsage("didBulkDeleteEntries");
946
- return res.data;
947
- } catch (err) {
948
- toggleNotification({
949
- type: "danger",
950
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
951
- });
952
- trackUsage("didNotBulkDeleteEntries");
953
- throw err;
954
- }
991
+ };
992
+ return acc;
955
993
  },
956
- [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
994
+ {}
957
995
  );
958
- const [discardDocument] = useDiscardDocumentMutation();
959
- const discard = React.useCallback(
960
- async ({ collectionType, model, documentId, params }) => {
961
- try {
962
- const res = await discardDocument({
963
- collectionType,
964
- model,
965
- documentId,
966
- params
967
- });
968
- if ("error" in res) {
969
- toggleNotification({
970
- type: "danger",
971
- message: formatAPIError(res.error)
972
- });
973
- return { error: res.error };
974
- }
975
- toggleNotification({
976
- type: "success",
977
- message: formatMessage({
978
- id: "content-manager.success.record.discard",
979
- defaultMessage: "Changes discarded"
980
- })
981
- });
982
- return res.data;
983
- } catch (err) {
984
- toggleNotification({
985
- type: "danger",
986
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
987
- });
988
- throw err;
989
- }
996
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
997
+ (acc, [attribute, metadata]) => {
998
+ return {
999
+ ...acc,
1000
+ [attribute]: metadata.edit
1001
+ };
990
1002
  },
991
- [discardDocument, formatAPIError, formatMessage, toggleNotification]
1003
+ {}
992
1004
  );
993
- const [publishDocument] = usePublishDocumentMutation();
994
- const publish = React.useCallback(
995
- async ({ collectionType, model, documentId, params }, data) => {
996
- try {
997
- trackUsage("willPublishEntry");
998
- const res = await publishDocument({
999
- collectionType,
1000
- model,
1001
- documentId,
1002
- data,
1003
- params
1004
- });
1005
- if ("error" in res) {
1006
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1007
- return { error: res.error };
1008
- }
1009
- trackUsage("didPublishEntry");
1010
- toggleNotification({
1011
- type: "success",
1012
- message: formatMessage({
1013
- id: getTranslation("success.record.publish"),
1014
- defaultMessage: "Published document"
1015
- })
1016
- });
1017
- return res.data;
1018
- } catch (err) {
1019
- toggleNotification({
1020
- type: "danger",
1021
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1022
- });
1023
- throw err;
1024
- }
1005
+ return {
1006
+ layout: panelledEditAttributes,
1007
+ components: componentEditAttributes,
1008
+ metadatas: editMetadatas,
1009
+ settings: {
1010
+ ...data.contentType.settings,
1011
+ displayName: schema?.info.displayName
1025
1012
  },
1026
- [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1027
- );
1028
- const [publishManyDocuments] = usePublishManyDocumentsMutation();
1029
- const publishMany = React.useCallback(
1030
- async ({ model, documentIds, params }) => {
1031
- try {
1032
- const res = await publishManyDocuments({
1033
- model,
1034
- documentIds,
1035
- params
1036
- });
1037
- if ("error" in res) {
1038
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1039
- return { error: res.error };
1040
- }
1041
- toggleNotification({
1042
- type: "success",
1043
- message: formatMessage({
1044
- id: getTranslation("success.record.publish"),
1045
- defaultMessage: "Published document"
1046
- })
1047
- });
1048
- return res.data;
1049
- } catch (err) {
1050
- toggleNotification({
1051
- type: "danger",
1052
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1053
- });
1054
- throw err;
1013
+ options: {
1014
+ ...schema?.options,
1015
+ ...schema?.pluginOptions,
1016
+ ...data.contentType.options
1017
+ }
1018
+ };
1019
+ };
1020
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1021
+ return rows.map(
1022
+ (row) => row.map((field) => {
1023
+ const attribute = attributes[field.name];
1024
+ if (!attribute) {
1025
+ return null;
1055
1026
  }
1056
- },
1057
- [
1058
- // trackUsage,
1059
- publishManyDocuments,
1060
- toggleNotification,
1061
- formatMessage,
1062
- formatAPIError
1063
- ]
1027
+ const { edit: metadata } = metadatas[field.name];
1028
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1029
+ return {
1030
+ attribute,
1031
+ disabled: !metadata.editable,
1032
+ hint: metadata.description,
1033
+ label: metadata.label ?? "",
1034
+ name: field.name,
1035
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1036
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1037
+ schemas,
1038
+ components: components?.schemas ?? {}
1039
+ }),
1040
+ placeholder: metadata.placeholder ?? "",
1041
+ required: attribute.required ?? false,
1042
+ size: field.size,
1043
+ unique: "unique" in attribute ? attribute.unique : false,
1044
+ visible: metadata.visible ?? true,
1045
+ type: attribute.type
1046
+ };
1047
+ }).filter((field) => field !== null)
1064
1048
  );
1065
- const [updateDocument] = useUpdateDocumentMutation();
1066
- const update = React.useCallback(
1067
- async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1068
- try {
1069
- trackUsage("willEditEntry", trackerProperty);
1070
- const res = await updateDocument({
1049
+ };
1050
+ const formatListLayout = (data, {
1051
+ schemas,
1052
+ schema,
1053
+ components
1054
+ }) => {
1055
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1056
+ (acc, [attribute, metadata]) => {
1057
+ return {
1058
+ ...acc,
1059
+ [attribute]: metadata.list
1060
+ };
1061
+ },
1062
+ {}
1063
+ );
1064
+ const listAttributes = convertListLayoutToFieldLayouts(
1065
+ data.contentType.layouts.list,
1066
+ schema?.attributes,
1067
+ listMetadatas,
1068
+ { configurations: data.components, schemas: components },
1069
+ schemas
1070
+ );
1071
+ return {
1072
+ layout: listAttributes,
1073
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1074
+ metadatas: listMetadatas,
1075
+ options: {
1076
+ ...schema?.options,
1077
+ ...schema?.pluginOptions,
1078
+ ...data.contentType.options
1079
+ }
1080
+ };
1081
+ };
1082
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1083
+ return columns.map((name) => {
1084
+ const attribute = attributes[name];
1085
+ if (!attribute) {
1086
+ return null;
1087
+ }
1088
+ const metadata = metadatas[name];
1089
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1090
+ return {
1091
+ attribute,
1092
+ label: metadata.label ?? "",
1093
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1094
+ schemas,
1095
+ components: components?.schemas ?? {}
1096
+ }),
1097
+ name,
1098
+ searchable: metadata.searchable ?? true,
1099
+ sortable: metadata.sortable ?? true
1100
+ };
1101
+ }).filter((field) => field !== null);
1102
+ };
1103
+ const useDocument = (args, opts) => {
1104
+ const { toggleNotification } = useNotification();
1105
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1106
+ const {
1107
+ currentData: data,
1108
+ isLoading: isLoadingDocument,
1109
+ isFetching: isFetchingDocument,
1110
+ error
1111
+ } = useGetDocumentQuery(args, {
1112
+ ...opts,
1113
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1114
+ });
1115
+ const {
1116
+ components,
1117
+ schema,
1118
+ schemas,
1119
+ isLoading: isLoadingSchema
1120
+ } = useContentTypeSchema(args.model);
1121
+ React.useEffect(() => {
1122
+ if (error) {
1123
+ toggleNotification({
1124
+ type: "danger",
1125
+ message: formatAPIError(error)
1126
+ });
1127
+ }
1128
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1129
+ const validationSchema = React.useMemo(() => {
1130
+ if (!schema) {
1131
+ return null;
1132
+ }
1133
+ return createYupSchema(schema.attributes, components);
1134
+ }, [schema, components]);
1135
+ const validate = React.useCallback(
1136
+ (document) => {
1137
+ if (!validationSchema) {
1138
+ throw new Error(
1139
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1140
+ );
1141
+ }
1142
+ try {
1143
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1144
+ return null;
1145
+ } catch (error2) {
1146
+ if (error2 instanceof ValidationError) {
1147
+ return getYupValidationErrors(error2);
1148
+ }
1149
+ throw error2;
1150
+ }
1151
+ },
1152
+ [validationSchema]
1153
+ );
1154
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1155
+ const hasError = !!error;
1156
+ return {
1157
+ components,
1158
+ document: data?.data,
1159
+ meta: data?.meta,
1160
+ isLoading,
1161
+ hasError,
1162
+ schema,
1163
+ schemas,
1164
+ validate
1165
+ };
1166
+ };
1167
+ const useDoc = () => {
1168
+ const { id, slug, collectionType, origin } = useParams();
1169
+ const [{ query }] = useQueryParams();
1170
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1171
+ if (!collectionType) {
1172
+ throw new Error("Could not find collectionType in url params");
1173
+ }
1174
+ if (!slug) {
1175
+ throw new Error("Could not find model in url params");
1176
+ }
1177
+ const document = useDocument(
1178
+ { documentId: origin || id, model: slug, collectionType, params },
1179
+ {
1180
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1181
+ }
1182
+ );
1183
+ const returnId = origin || id === "create" ? void 0 : id;
1184
+ return {
1185
+ collectionType,
1186
+ model: slug,
1187
+ id: returnId,
1188
+ ...document
1189
+ };
1190
+ };
1191
+ const useContentManagerContext = () => {
1192
+ const {
1193
+ collectionType,
1194
+ model,
1195
+ id,
1196
+ components,
1197
+ isLoading: isLoadingDoc,
1198
+ schema,
1199
+ schemas
1200
+ } = useDoc();
1201
+ const layout = useDocumentLayout(model);
1202
+ const form = useForm("useContentManagerContext", (state) => state);
1203
+ const isSingleType = collectionType === SINGLE_TYPES;
1204
+ const slug = model;
1205
+ const isCreatingEntry = id === "create";
1206
+ useContentTypeSchema();
1207
+ const isLoading = isLoadingDoc || layout.isLoading;
1208
+ const error = layout.error;
1209
+ return {
1210
+ error,
1211
+ isLoading,
1212
+ // Base metadata
1213
+ model,
1214
+ collectionType,
1215
+ id,
1216
+ slug,
1217
+ isCreatingEntry,
1218
+ isSingleType,
1219
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1220
+ // All schema infos
1221
+ components,
1222
+ contentType: schema,
1223
+ contentTypes: schemas,
1224
+ // Form state
1225
+ form,
1226
+ // layout infos
1227
+ layout
1228
+ };
1229
+ };
1230
+ const prefixPluginTranslations = (trad, pluginId) => {
1231
+ if (!pluginId) {
1232
+ throw new TypeError("pluginId can't be empty");
1233
+ }
1234
+ return Object.keys(trad).reduce((acc, current) => {
1235
+ acc[`${pluginId}.${current}`] = trad[current];
1236
+ return acc;
1237
+ }, {});
1238
+ };
1239
+ const getTranslation = (id) => `content-manager.${id}`;
1240
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1241
+ id: "notification.error",
1242
+ defaultMessage: "An error occurred, please try again"
1243
+ };
1244
+ const useDocumentActions = () => {
1245
+ const { toggleNotification } = useNotification();
1246
+ const { formatMessage } = useIntl();
1247
+ const { trackUsage } = useTracking();
1248
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1249
+ const navigate = useNavigate();
1250
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
1251
+ const [deleteDocument] = useDeleteDocumentMutation();
1252
+ const _delete = React.useCallback(
1253
+ async ({ collectionType, model, documentId, params }, trackerProperty) => {
1254
+ try {
1255
+ trackUsage("willDeleteEntry", trackerProperty);
1256
+ const res = await deleteDocument({
1071
1257
  collectionType,
1072
1258
  model,
1073
1259
  documentId,
1074
- data,
1075
1260
  params
1076
1261
  });
1077
1262
  if ("error" in res) {
1078
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1079
- trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1263
+ toggleNotification({
1264
+ type: "danger",
1265
+ message: formatAPIError(res.error)
1266
+ });
1080
1267
  return { error: res.error };
1081
1268
  }
1082
- trackUsage("didEditEntry", trackerProperty);
1083
1269
  toggleNotification({
1084
1270
  type: "success",
1085
1271
  message: formatMessage({
1086
- id: getTranslation("success.record.save"),
1087
- defaultMessage: "Saved document"
1272
+ id: getTranslation("success.record.delete"),
1273
+ defaultMessage: "Deleted document"
1088
1274
  })
1089
1275
  });
1276
+ trackUsage("didDeleteEntry", trackerProperty);
1090
1277
  return res.data;
1091
1278
  } catch (err) {
1092
- trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1093
1279
  toggleNotification({
1094
1280
  type: "danger",
1095
1281
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1096
1282
  });
1283
+ trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
1097
1284
  throw err;
1098
1285
  }
1099
1286
  },
1100
- [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1287
+ [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
1101
1288
  );
1102
- const [unpublishDocument] = useUnpublishDocumentMutation();
1103
- const unpublish = React.useCallback(
1104
- async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1289
+ const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
1290
+ const deleteMany = React.useCallback(
1291
+ async ({ model, documentIds, params }) => {
1105
1292
  try {
1106
- trackUsage("willUnpublishEntry");
1107
- const res = await unpublishDocument({
1108
- collectionType,
1293
+ trackUsage("willBulkDeleteEntries");
1294
+ const res = await deleteManyDocuments({
1109
1295
  model,
1110
- documentId,
1111
- params,
1112
- data: {
1113
- discardDraft
1114
- }
1296
+ documentIds,
1297
+ params
1115
1298
  });
1116
1299
  if ("error" in res) {
1117
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1300
+ toggleNotification({
1301
+ type: "danger",
1302
+ message: formatAPIError(res.error)
1303
+ });
1118
1304
  return { error: res.error };
1119
1305
  }
1120
- trackUsage("didUnpublishEntry");
1121
1306
  toggleNotification({
1122
1307
  type: "success",
1123
- message: formatMessage({
1124
- id: getTranslation("success.record.unpublish"),
1125
- defaultMessage: "Unpublished document"
1126
- })
1308
+ title: formatMessage({
1309
+ id: getTranslation("success.records.delete"),
1310
+ defaultMessage: "Successfully deleted."
1311
+ }),
1312
+ message: ""
1127
1313
  });
1314
+ trackUsage("didBulkDeleteEntries");
1128
1315
  return res.data;
1129
1316
  } catch (err) {
1130
1317
  toggleNotification({
1131
1318
  type: "danger",
1132
1319
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1133
1320
  });
1321
+ trackUsage("didNotBulkDeleteEntries");
1134
1322
  throw err;
1135
1323
  }
1136
1324
  },
1137
- [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1325
+ [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1138
1326
  );
1139
- const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1140
- const unpublishMany = React.useCallback(
1141
- async ({ model, documentIds, params }) => {
1327
+ const [discardDocument] = useDiscardDocumentMutation();
1328
+ const discard = React.useCallback(
1329
+ async ({ collectionType, model, documentId, params }) => {
1142
1330
  try {
1143
- trackUsage("willBulkUnpublishEntries");
1144
- const res = await unpublishManyDocuments({
1331
+ const res = await discardDocument({
1332
+ collectionType,
1145
1333
  model,
1146
- documentIds,
1334
+ documentId,
1147
1335
  params
1148
1336
  });
1149
1337
  if ("error" in res) {
1150
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1338
+ toggleNotification({
1339
+ type: "danger",
1340
+ message: formatAPIError(res.error)
1341
+ });
1151
1342
  return { error: res.error };
1152
1343
  }
1153
- trackUsage("didBulkUnpublishEntries");
1154
1344
  toggleNotification({
1155
1345
  type: "success",
1156
- title: formatMessage({
1157
- id: getTranslation("success.records.unpublish"),
1158
- defaultMessage: "Successfully unpublished."
1159
- }),
1160
- message: ""
1346
+ message: formatMessage({
1347
+ id: "content-manager.success.record.discard",
1348
+ defaultMessage: "Changes discarded"
1349
+ })
1161
1350
  });
1162
1351
  return res.data;
1163
1352
  } catch (err) {
@@ -1165,14 +1354,194 @@ const useDocumentActions = () => {
1165
1354
  type: "danger",
1166
1355
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1167
1356
  });
1168
- trackUsage("didNotBulkUnpublishEntries");
1169
1357
  throw err;
1170
1358
  }
1171
1359
  },
1172
- [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1360
+ [discardDocument, formatAPIError, formatMessage, toggleNotification]
1173
1361
  );
1174
- const [createDocument] = useCreateDocumentMutation();
1175
- const create = React.useCallback(
1362
+ const [publishDocument] = usePublishDocumentMutation();
1363
+ const publish = React.useCallback(
1364
+ async ({ collectionType, model, documentId, params }, data) => {
1365
+ try {
1366
+ trackUsage("willPublishEntry");
1367
+ const res = await publishDocument({
1368
+ collectionType,
1369
+ model,
1370
+ documentId,
1371
+ data,
1372
+ params
1373
+ });
1374
+ if ("error" in res) {
1375
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1376
+ return { error: res.error };
1377
+ }
1378
+ trackUsage("didPublishEntry");
1379
+ toggleNotification({
1380
+ type: "success",
1381
+ message: formatMessage({
1382
+ id: getTranslation("success.record.publish"),
1383
+ defaultMessage: "Published document"
1384
+ })
1385
+ });
1386
+ return res.data;
1387
+ } catch (err) {
1388
+ toggleNotification({
1389
+ type: "danger",
1390
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1391
+ });
1392
+ throw err;
1393
+ }
1394
+ },
1395
+ [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1396
+ );
1397
+ const [publishManyDocuments] = usePublishManyDocumentsMutation();
1398
+ const publishMany = React.useCallback(
1399
+ async ({ model, documentIds, params }) => {
1400
+ try {
1401
+ const res = await publishManyDocuments({
1402
+ model,
1403
+ documentIds,
1404
+ params
1405
+ });
1406
+ if ("error" in res) {
1407
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1408
+ return { error: res.error };
1409
+ }
1410
+ toggleNotification({
1411
+ type: "success",
1412
+ message: formatMessage({
1413
+ id: getTranslation("success.record.publish"),
1414
+ defaultMessage: "Published document"
1415
+ })
1416
+ });
1417
+ return res.data;
1418
+ } catch (err) {
1419
+ toggleNotification({
1420
+ type: "danger",
1421
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1422
+ });
1423
+ throw err;
1424
+ }
1425
+ },
1426
+ [
1427
+ // trackUsage,
1428
+ publishManyDocuments,
1429
+ toggleNotification,
1430
+ formatMessage,
1431
+ formatAPIError
1432
+ ]
1433
+ );
1434
+ const [updateDocument] = useUpdateDocumentMutation();
1435
+ const update = React.useCallback(
1436
+ async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1437
+ try {
1438
+ trackUsage("willEditEntry", trackerProperty);
1439
+ const res = await updateDocument({
1440
+ collectionType,
1441
+ model,
1442
+ documentId,
1443
+ data,
1444
+ params
1445
+ });
1446
+ if ("error" in res) {
1447
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1448
+ trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1449
+ return { error: res.error };
1450
+ }
1451
+ trackUsage("didEditEntry", trackerProperty);
1452
+ toggleNotification({
1453
+ type: "success",
1454
+ message: formatMessage({
1455
+ id: getTranslation("success.record.save"),
1456
+ defaultMessage: "Saved document"
1457
+ })
1458
+ });
1459
+ return res.data;
1460
+ } catch (err) {
1461
+ trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1462
+ toggleNotification({
1463
+ type: "danger",
1464
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1465
+ });
1466
+ throw err;
1467
+ }
1468
+ },
1469
+ [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1470
+ );
1471
+ const [unpublishDocument] = useUnpublishDocumentMutation();
1472
+ const unpublish = React.useCallback(
1473
+ async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1474
+ try {
1475
+ trackUsage("willUnpublishEntry");
1476
+ const res = await unpublishDocument({
1477
+ collectionType,
1478
+ model,
1479
+ documentId,
1480
+ params,
1481
+ data: {
1482
+ discardDraft
1483
+ }
1484
+ });
1485
+ if ("error" in res) {
1486
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1487
+ return { error: res.error };
1488
+ }
1489
+ trackUsage("didUnpublishEntry");
1490
+ toggleNotification({
1491
+ type: "success",
1492
+ message: formatMessage({
1493
+ id: getTranslation("success.record.unpublish"),
1494
+ defaultMessage: "Unpublished document"
1495
+ })
1496
+ });
1497
+ return res.data;
1498
+ } catch (err) {
1499
+ toggleNotification({
1500
+ type: "danger",
1501
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1502
+ });
1503
+ throw err;
1504
+ }
1505
+ },
1506
+ [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1507
+ );
1508
+ const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1509
+ const unpublishMany = React.useCallback(
1510
+ async ({ model, documentIds, params }) => {
1511
+ try {
1512
+ trackUsage("willBulkUnpublishEntries");
1513
+ const res = await unpublishManyDocuments({
1514
+ model,
1515
+ documentIds,
1516
+ params
1517
+ });
1518
+ if ("error" in res) {
1519
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1520
+ return { error: res.error };
1521
+ }
1522
+ trackUsage("didBulkUnpublishEntries");
1523
+ toggleNotification({
1524
+ type: "success",
1525
+ title: formatMessage({
1526
+ id: getTranslation("success.records.unpublish"),
1527
+ defaultMessage: "Successfully unpublished."
1528
+ }),
1529
+ message: ""
1530
+ });
1531
+ return res.data;
1532
+ } catch (err) {
1533
+ toggleNotification({
1534
+ type: "danger",
1535
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1536
+ });
1537
+ trackUsage("didNotBulkUnpublishEntries");
1538
+ throw err;
1539
+ }
1540
+ },
1541
+ [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1542
+ );
1543
+ const [createDocument] = useCreateDocumentMutation();
1544
+ const create = React.useCallback(
1176
1545
  async ({ model, params }, data, trackerProperty) => {
1177
1546
  try {
1178
1547
  const res = await createDocument({
@@ -1193,6 +1562,7 @@ const useDocumentActions = () => {
1193
1562
  defaultMessage: "Saved document"
1194
1563
  })
1195
1564
  });
1565
+ setCurrentStep("contentManager.success");
1196
1566
  return res.data;
1197
1567
  } catch (err) {
1198
1568
  toggleNotification({
@@ -1232,7 +1602,7 @@ const useDocumentActions = () => {
1232
1602
  throw err;
1233
1603
  }
1234
1604
  },
1235
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1605
+ [autoCloneDocument, formatMessage, toggleNotification]
1236
1606
  );
1237
1607
  const [cloneDocument] = useCloneDocumentMutation();
1238
1608
  const clone = React.useCallback(
@@ -1258,6 +1628,7 @@ const useDocumentActions = () => {
1258
1628
  defaultMessage: "Cloned document"
1259
1629
  })
1260
1630
  });
1631
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1261
1632
  return res.data;
1262
1633
  } catch (err) {
1263
1634
  toggleNotification({
@@ -1268,7 +1639,7 @@ const useDocumentActions = () => {
1268
1639
  throw err;
1269
1640
  }
1270
1641
  },
1271
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1642
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1272
1643
  );
1273
1644
  const [getDoc] = useLazyGetDocumentQuery();
1274
1645
  const getDocument = React.useCallback(
@@ -1294,7 +1665,7 @@ const useDocumentActions = () => {
1294
1665
  };
1295
1666
  };
1296
1667
  const ProtectedHistoryPage = lazy(
1297
- () => import("./History-BEFrLZfz.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1668
+ () => import("./History-DRwA3oMM.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1298
1669
  );
1299
1670
  const routes$1 = [
1300
1671
  {
@@ -1307,31 +1678,31 @@ const routes$1 = [
1307
1678
  }
1308
1679
  ];
1309
1680
  const ProtectedEditViewPage = lazy(
1310
- () => import("./EditViewPage-IZbGSeL3.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1681
+ () => import("./EditViewPage-B82x_x1b.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1311
1682
  );
1312
1683
  const ProtectedListViewPage = lazy(
1313
- () => import("./ListViewPage-DBbH7Esr.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1684
+ () => import("./ListViewPage-CELx2ysp.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1314
1685
  );
1315
1686
  const ProtectedListConfiguration = lazy(
1316
- () => import("./ListConfigurationPage-CB_lBFVX.mjs").then((mod) => ({
1687
+ () => import("./ListConfigurationPage-BxfQJzPk.mjs").then((mod) => ({
1317
1688
  default: mod.ProtectedListConfiguration
1318
1689
  }))
1319
1690
  );
1320
1691
  const ProtectedEditConfigurationPage = lazy(
1321
- () => import("./EditConfigurationPage-DJDSIf3D.mjs").then((mod) => ({
1692
+ () => import("./EditConfigurationPage-DdPNAbl3.mjs").then((mod) => ({
1322
1693
  default: mod.ProtectedEditConfigurationPage
1323
1694
  }))
1324
1695
  );
1325
1696
  const ProtectedComponentConfigurationPage = lazy(
1326
- () => import("./ComponentConfigurationPage-BwnO0su2.mjs").then((mod) => ({
1697
+ () => import("./ComponentConfigurationPage-DfFSZQxe.mjs").then((mod) => ({
1327
1698
  default: mod.ProtectedComponentConfigurationPage
1328
1699
  }))
1329
1700
  );
1330
1701
  const NoPermissions = lazy(
1331
- () => import("./NoPermissionsPage-B88W05rh.mjs").then((mod) => ({ default: mod.NoPermissions }))
1702
+ () => import("./NoPermissionsPage-DWleVYK7.mjs").then((mod) => ({ default: mod.NoPermissions }))
1332
1703
  );
1333
1704
  const NoContentType = lazy(
1334
- () => import("./NoContentTypePage-BU9Omzp4.mjs").then((mod) => ({ default: mod.NoContentType }))
1705
+ () => import("./NoContentTypePage-DtJ9jcfk.mjs").then((mod) => ({ default: mod.NoContentType }))
1335
1706
  );
1336
1707
  const CollectionTypePages = () => {
1337
1708
  const { collectionType } = useParams();
@@ -1385,1073 +1756,737 @@ const DocumentActions = ({ actions: actions2 }) => {
1385
1756
  if (action.position === void 0) {
1386
1757
  return true;
1387
1758
  }
1388
- const positions = Array.isArray(action.position) ? action.position : [action.position];
1389
- return positions.includes("panel");
1390
- });
1391
- if (!primaryAction) {
1392
- return null;
1393
- }
1394
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, alignItems: "stretch", width: "100%", children: [
1395
- /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
1396
- /* @__PURE__ */ jsx(DocumentActionButton, { ...primaryAction, variant: primaryAction.variant || "default" }),
1397
- restActions.length > 0 ? /* @__PURE__ */ jsx(
1398
- DocumentActionsMenu,
1399
- {
1400
- actions: restActions,
1401
- label: formatMessage({
1402
- id: "content-manager.containers.edit.panels.default.more-actions",
1403
- defaultMessage: "More document actions"
1404
- })
1405
- }
1406
- ) : null
1407
- ] }),
1408
- secondaryAction ? /* @__PURE__ */ jsx(
1409
- DocumentActionButton,
1410
- {
1411
- ...secondaryAction,
1412
- variant: secondaryAction.variant || "secondary"
1413
- }
1414
- ) : null
1415
- ] });
1416
- };
1417
- const DocumentActionButton = (action) => {
1418
- const [dialogId, setDialogId] = React.useState(null);
1419
- const { toggleNotification } = useNotification();
1420
- const handleClick = (action2) => async (e) => {
1421
- const { onClick = () => false, dialog, id } = action2;
1422
- const muteDialog = await onClick(e);
1423
- if (dialog && !muteDialog) {
1424
- switch (dialog.type) {
1425
- case "notification":
1426
- toggleNotification({
1427
- title: dialog.title,
1428
- message: dialog.content,
1429
- type: dialog.status,
1430
- timeout: dialog.timeout,
1431
- onClose: dialog.onClose
1432
- });
1433
- break;
1434
- case "dialog":
1435
- case "modal":
1436
- e.preventDefault();
1437
- setDialogId(id);
1438
- }
1439
- }
1440
- };
1441
- const handleClose = () => {
1442
- setDialogId(null);
1443
- };
1444
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1445
- /* @__PURE__ */ jsx(
1446
- Button,
1447
- {
1448
- flex: "auto",
1449
- startIcon: action.icon,
1450
- disabled: action.disabled,
1451
- onClick: handleClick(action),
1452
- justifyContent: "center",
1453
- variant: action.variant || "default",
1454
- paddingTop: "7px",
1455
- paddingBottom: "7px",
1456
- children: action.label
1457
- }
1458
- ),
1459
- action.dialog?.type === "dialog" ? /* @__PURE__ */ jsx(
1460
- DocumentActionConfirmDialog,
1461
- {
1462
- ...action.dialog,
1463
- variant: action.dialog?.variant ?? action.variant,
1464
- isOpen: dialogId === action.id,
1465
- onClose: handleClose
1466
- }
1467
- ) : null,
1468
- action.dialog?.type === "modal" ? /* @__PURE__ */ jsx(
1469
- DocumentActionModal,
1470
- {
1471
- ...action.dialog,
1472
- onModalClose: handleClose,
1473
- isOpen: dialogId === action.id
1474
- }
1475
- ) : null
1476
- ] });
1477
- };
1478
- const DocumentActionsMenu = ({
1479
- actions: actions2,
1480
- children,
1481
- label,
1482
- variant = "tertiary"
1483
- }) => {
1484
- const [isOpen, setIsOpen] = React.useState(false);
1485
- const [dialogId, setDialogId] = React.useState(null);
1486
- const { formatMessage } = useIntl();
1487
- const { toggleNotification } = useNotification();
1488
- const isDisabled = actions2.every((action) => action.disabled) || actions2.length === 0;
1489
- const handleClick = (action) => async (e) => {
1490
- const { onClick = () => false, dialog, id } = action;
1491
- const muteDialog = await onClick(e);
1492
- if (dialog && !muteDialog) {
1493
- switch (dialog.type) {
1494
- case "notification":
1495
- toggleNotification({
1496
- title: dialog.title,
1497
- message: dialog.content,
1498
- type: dialog.status,
1499
- timeout: dialog.timeout,
1500
- onClose: dialog.onClose
1501
- });
1502
- break;
1503
- case "dialog":
1504
- case "modal":
1505
- setDialogId(id);
1506
- }
1507
- }
1508
- };
1509
- const handleClose = () => {
1510
- setDialogId(null);
1511
- setIsOpen(false);
1512
- };
1513
- return /* @__PURE__ */ jsxs(Menu.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
1514
- /* @__PURE__ */ jsxs(
1515
- StyledMoreButton,
1516
- {
1517
- disabled: isDisabled,
1518
- size: "S",
1519
- endIcon: null,
1520
- paddingTop: "4px",
1521
- paddingLeft: "7px",
1522
- paddingRight: "7px",
1523
- variant,
1524
- children: [
1525
- /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
1526
- /* @__PURE__ */ jsx(VisuallyHidden, { tag: "span", children: label || formatMessage({
1527
- id: "content-manager.containers.edit.panels.default.more-actions",
1528
- defaultMessage: "More document actions"
1529
- }) })
1530
- ]
1531
- }
1532
- ),
1533
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1534
- actions2.map((action) => {
1535
- return /* @__PURE__ */ jsx(
1536
- Menu.Item,
1537
- {
1538
- disabled: action.disabled,
1539
- onSelect: handleClick(action),
1540
- display: "block",
1541
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1542
- /* @__PURE__ */ jsxs(
1543
- Flex,
1544
- {
1545
- color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1546
- gap: 2,
1547
- tag: "span",
1548
- children: [
1549
- /* @__PURE__ */ jsx(
1550
- Flex,
1551
- {
1552
- tag: "span",
1553
- color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1554
- children: action.icon
1555
- }
1556
- ),
1557
- action.label
1558
- ]
1559
- }
1560
- ),
1561
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1562
- Flex,
1563
- {
1564
- alignItems: "center",
1565
- background: "alternative100",
1566
- borderStyle: "solid",
1567
- borderColor: "alternative200",
1568
- borderWidth: "1px",
1569
- height: 5,
1570
- paddingLeft: 2,
1571
- paddingRight: 2,
1572
- hasRadius: true,
1573
- color: "alternative600",
1574
- children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1575
- }
1576
- )
1577
- ] })
1578
- },
1579
- action.id
1580
- );
1581
- }),
1582
- children
1583
- ] }),
1584
- actions2.map((action) => {
1585
- return /* @__PURE__ */ jsxs(React.Fragment, { children: [
1586
- action.dialog?.type === "dialog" ? /* @__PURE__ */ jsx(
1587
- DocumentActionConfirmDialog,
1588
- {
1589
- ...action.dialog,
1590
- variant: action.variant,
1591
- isOpen: dialogId === action.id,
1592
- onClose: handleClose
1593
- }
1594
- ) : null,
1595
- action.dialog?.type === "modal" ? /* @__PURE__ */ jsx(
1596
- DocumentActionModal,
1597
- {
1598
- ...action.dialog,
1599
- onModalClose: handleClose,
1600
- isOpen: dialogId === action.id
1601
- }
1602
- ) : null
1603
- ] }, action.id);
1604
- })
1605
- ] });
1606
- };
1607
- const convertActionVariantToColor = (variant = "secondary") => {
1608
- switch (variant) {
1609
- case "danger":
1610
- return "danger600";
1611
- case "secondary":
1612
- return void 0;
1613
- case "success":
1614
- return "success600";
1615
- default:
1616
- return "primary600";
1617
- }
1618
- };
1619
- const convertActionVariantToIconColor = (variant = "secondary") => {
1620
- switch (variant) {
1621
- case "danger":
1622
- return "danger600";
1623
- case "secondary":
1624
- return "neutral500";
1625
- case "success":
1626
- return "success600";
1627
- default:
1628
- return "primary600";
1629
- }
1630
- };
1631
- const StyledMoreButton = styled(Menu.Trigger)`
1632
- & > span {
1633
- display: flex;
1634
- }
1635
- `;
1636
- const DocumentActionConfirmDialog = ({
1637
- onClose,
1638
- onCancel,
1639
- onConfirm,
1640
- title,
1641
- content,
1642
- isOpen,
1643
- variant = "secondary"
1644
- }) => {
1645
- const { formatMessage } = useIntl();
1646
- const handleClose = async () => {
1647
- if (onCancel) {
1648
- await onCancel();
1649
- }
1650
- onClose();
1651
- };
1652
- const handleConfirm = async () => {
1653
- if (onConfirm) {
1654
- await onConfirm();
1655
- }
1656
- onClose();
1657
- };
1658
- return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
1659
- /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1660
- /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1661
- /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1662
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
1663
- id: "app.components.Button.cancel",
1664
- defaultMessage: "Cancel"
1665
- }) }) }),
1666
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
1667
- id: "app.components.Button.confirm",
1668
- defaultMessage: "Confirm"
1669
- }) })
1670
- ] })
1671
- ] }) });
1672
- };
1673
- const DocumentActionModal = ({
1674
- isOpen,
1675
- title,
1676
- onClose,
1677
- footer: Footer,
1678
- content: Content,
1679
- onModalClose
1680
- }) => {
1681
- const handleClose = () => {
1682
- if (onClose) {
1683
- onClose();
1684
- }
1685
- onModalClose();
1686
- };
1687
- return /* @__PURE__ */ jsx(Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
1688
- /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: title }) }),
1689
- typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsx(Modal.Body, { children: Content }),
1690
- typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1691
- ] }) });
1692
- };
1693
- const PublishAction$1 = ({
1694
- activeTab,
1695
- documentId,
1696
- model,
1697
- collectionType,
1698
- meta,
1699
- document
1700
- }) => {
1701
- const { schema } = useDoc();
1702
- const navigate = useNavigate();
1703
- const { toggleNotification } = useNotification();
1704
- const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
1705
- const isCloning = useMatch(CLONE_PATH) !== null;
1706
- const { formatMessage } = useIntl();
1707
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1708
- "PublishAction",
1709
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1710
- );
1711
- const { publish } = useDocumentActions();
1712
- const [
1713
- countDraftRelations,
1714
- { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
1715
- ] = useLazyGetDraftRelationCountQuery();
1716
- const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React.useState(0);
1717
- const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React.useState(0);
1718
- const [{ query, rawQuery }] = useQueryParams();
1719
- const params = React.useMemo(() => buildValidParams(query), [query]);
1720
- const modified = useForm("PublishAction", ({ modified: modified2 }) => modified2);
1721
- const setSubmitting = useForm("PublishAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
1722
- const isSubmitting = useForm("PublishAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
1723
- const validate = useForm("PublishAction", (state) => state.validate);
1724
- const setErrors = useForm("PublishAction", (state) => state.setErrors);
1725
- const formValues = useForm("PublishAction", ({ values }) => values);
1726
- React.useEffect(() => {
1727
- if (isErrorDraftRelations) {
1728
- toggleNotification({
1729
- type: "danger",
1730
- message: formatMessage({
1731
- id: getTranslation("error.records.fetch-draft-relatons"),
1732
- defaultMessage: "An error occurred while fetching draft relations on this document."
1733
- })
1734
- });
1735
- }
1736
- }, [isErrorDraftRelations, toggleNotification, formatMessage]);
1737
- React.useEffect(() => {
1738
- const localDraftRelations = /* @__PURE__ */ new Set();
1739
- const extractDraftRelations = (data) => {
1740
- const relations = data.connect || [];
1741
- relations.forEach((relation) => {
1742
- if (relation.status === "draft") {
1743
- localDraftRelations.add(relation.id);
1744
- }
1745
- });
1746
- };
1747
- const traverseAndExtract = (data) => {
1748
- Object.entries(data).forEach(([key, value]) => {
1749
- if (key === "connect" && Array.isArray(value)) {
1750
- extractDraftRelations({ connect: value });
1751
- } else if (typeof value === "object" && value !== null) {
1752
- traverseAndExtract(value);
1753
- }
1754
- });
1755
- };
1756
- if (!documentId || modified) {
1757
- traverseAndExtract(formValues);
1758
- setLocalCountOfDraftRelations(localDraftRelations.size);
1759
- }
1760
- }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1761
- React.useEffect(() => {
1762
- if (documentId) {
1763
- const fetchDraftRelationsCount = async () => {
1764
- const { data, error } = await countDraftRelations({
1765
- collectionType,
1766
- model,
1767
- documentId,
1768
- params
1769
- });
1770
- if (error) {
1771
- throw error;
1772
- }
1773
- if (data) {
1774
- setServerCountOfDraftRelations(data.data);
1775
- }
1776
- };
1777
- fetchDraftRelationsCount();
1778
- }
1779
- }, [documentId, countDraftRelations, collectionType, model, params]);
1780
- const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1781
- if (!schema?.options?.draftAndPublish) {
1759
+ const positions = Array.isArray(action.position) ? action.position : [action.position];
1760
+ return positions.includes("panel");
1761
+ });
1762
+ if (!primaryAction) {
1782
1763
  return null;
1783
1764
  }
1784
- const performPublish = async () => {
1785
- setSubmitting(true);
1786
- try {
1787
- const { errors } = await validate();
1788
- if (errors) {
1789
- toggleNotification({
1790
- type: "danger",
1791
- message: formatMessage({
1792
- id: "content-manager.validation.error",
1793
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1794
- })
1795
- });
1796
- return;
1797
- }
1798
- const res = await publish(
1799
- {
1800
- collectionType,
1801
- model,
1802
- documentId,
1803
- params
1804
- },
1805
- formValues
1806
- );
1807
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1808
- navigate({
1809
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1810
- search: rawQuery
1811
- });
1812
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1813
- setErrors(formatValidationErrors(res.error));
1814
- }
1815
- } finally {
1816
- setSubmitting(false);
1817
- }
1818
- };
1819
- const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1820
- const hasDraftRelations = totalDraftRelations > 0;
1821
- return {
1822
- /**
1823
- * Disabled when:
1824
- * - currently if you're cloning a document we don't support publish & clone at the same time.
1825
- * - the form is submitting
1826
- * - the active tab is the published tab
1827
- * - the document is already published & not modified
1828
- * - the document is being created & not modified
1829
- * - the user doesn't have the permission to publish
1830
- */
1831
- disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1832
- label: formatMessage({
1833
- id: "app.utils.publish",
1834
- defaultMessage: "Publish"
1835
- }),
1836
- onClick: async () => {
1837
- if (hasDraftRelations) {
1838
- return;
1839
- }
1840
- await performPublish();
1841
- },
1842
- dialog: hasDraftRelations ? {
1843
- type: "dialog",
1844
- variant: "danger",
1845
- footer: null,
1846
- title: formatMessage({
1847
- id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
1848
- defaultMessage: "Confirmation"
1849
- }),
1850
- content: formatMessage(
1851
- {
1852
- id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
1853
- defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
1854
- },
1765
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, alignItems: "stretch", width: "100%", children: [
1766
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
1767
+ /* @__PURE__ */ jsx(DocumentActionButton, { ...primaryAction, variant: primaryAction.variant || "default" }),
1768
+ restActions.length > 0 ? /* @__PURE__ */ jsx(
1769
+ DocumentActionsMenu,
1855
1770
  {
1856
- count: totalDraftRelations
1771
+ actions: restActions,
1772
+ label: formatMessage({
1773
+ id: "content-manager.containers.edit.panels.default.more-actions",
1774
+ defaultMessage: "More document actions"
1775
+ })
1857
1776
  }
1858
- ),
1859
- onConfirm: async () => {
1860
- await performPublish();
1777
+ ) : null
1778
+ ] }),
1779
+ secondaryAction ? /* @__PURE__ */ jsx(
1780
+ DocumentActionButton,
1781
+ {
1782
+ ...secondaryAction,
1783
+ variant: secondaryAction.variant || "secondary"
1861
1784
  }
1862
- } : void 0
1863
- };
1785
+ ) : null
1786
+ ] });
1864
1787
  };
1865
- PublishAction$1.type = "publish";
1866
- const UpdateAction = ({
1867
- activeTab,
1868
- documentId,
1869
- model,
1870
- collectionType
1871
- }) => {
1872
- const navigate = useNavigate();
1788
+ const DocumentActionButton = (action) => {
1789
+ const [dialogId, setDialogId] = React.useState(null);
1873
1790
  const { toggleNotification } = useNotification();
1874
- const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
1875
- const cloneMatch = useMatch(CLONE_PATH);
1876
- const isCloning = cloneMatch !== null;
1877
- const { formatMessage } = useIntl();
1878
- useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1879
- canCreate: canCreate2,
1880
- canUpdate: canUpdate2
1881
- }));
1882
- const { create, update, clone } = useDocumentActions();
1883
- const [{ query, rawQuery }] = useQueryParams();
1884
- const params = React.useMemo(() => buildValidParams(query), [query]);
1885
- const isSubmitting = useForm("UpdateAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
1886
- const modified = useForm("UpdateAction", ({ modified: modified2 }) => modified2);
1887
- const setSubmitting = useForm("UpdateAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
1888
- const document = useForm("UpdateAction", ({ values }) => values);
1889
- const validate = useForm("UpdateAction", (state) => state.validate);
1890
- const setErrors = useForm("UpdateAction", (state) => state.setErrors);
1891
- const resetForm = useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
1892
- return {
1893
- /**
1894
- * Disabled when:
1895
- * - the form is submitting
1896
- * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1897
- * - the active tab is the published tab
1898
- */
1899
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1900
- label: formatMessage({
1901
- id: "content-manager.containers.Edit.save",
1902
- defaultMessage: "Save"
1903
- }),
1904
- onClick: async () => {
1905
- setSubmitting(true);
1906
- try {
1907
- if (activeTab !== "draft") {
1908
- const { errors } = await validate();
1909
- if (errors) {
1910
- toggleNotification({
1911
- type: "danger",
1912
- message: formatMessage({
1913
- id: "content-manager.validation.error",
1914
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1915
- })
1916
- });
1917
- return;
1918
- }
1919
- }
1920
- if (isCloning) {
1921
- const res = await clone(
1922
- {
1923
- model,
1924
- documentId: cloneMatch.params.origin,
1925
- params
1926
- },
1927
- document
1928
- );
1929
- if ("data" in res) {
1930
- navigate(
1931
- {
1932
- pathname: `../${res.data.documentId}`,
1933
- search: rawQuery
1934
- },
1935
- { relative: "path" }
1936
- );
1937
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1938
- setErrors(formatValidationErrors(res.error));
1939
- }
1940
- } else if (documentId || collectionType === SINGLE_TYPES) {
1941
- const res = await update(
1942
- {
1943
- collectionType,
1944
- model,
1945
- documentId,
1946
- params
1947
- },
1948
- document
1949
- );
1950
- if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1951
- setErrors(formatValidationErrors(res.error));
1952
- } else {
1953
- resetForm();
1954
- }
1955
- } else {
1956
- const res = await create(
1957
- {
1958
- model,
1959
- params
1960
- },
1961
- document
1962
- );
1963
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1964
- navigate(
1965
- {
1966
- pathname: `../${res.data.documentId}`,
1967
- search: rawQuery
1968
- },
1969
- { replace: true, relative: "path" }
1970
- );
1971
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1972
- setErrors(formatValidationErrors(res.error));
1973
- }
1974
- }
1975
- } finally {
1976
- setSubmitting(false);
1791
+ const handleClick = (action2) => async (e) => {
1792
+ const { onClick = () => false, dialog, id } = action2;
1793
+ const muteDialog = await onClick(e);
1794
+ if (dialog && !muteDialog) {
1795
+ switch (dialog.type) {
1796
+ case "notification":
1797
+ toggleNotification({
1798
+ title: dialog.title,
1799
+ message: dialog.content,
1800
+ type: dialog.status,
1801
+ timeout: dialog.timeout,
1802
+ onClose: dialog.onClose
1803
+ });
1804
+ break;
1805
+ case "dialog":
1806
+ case "modal":
1807
+ e.preventDefault();
1808
+ setDialogId(id);
1977
1809
  }
1978
1810
  }
1979
1811
  };
1812
+ const handleClose = () => {
1813
+ setDialogId(null);
1814
+ };
1815
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1816
+ /* @__PURE__ */ jsx(
1817
+ Button,
1818
+ {
1819
+ flex: "auto",
1820
+ startIcon: action.icon,
1821
+ disabled: action.disabled,
1822
+ onClick: handleClick(action),
1823
+ justifyContent: "center",
1824
+ variant: action.variant || "default",
1825
+ paddingTop: "7px",
1826
+ paddingBottom: "7px",
1827
+ children: action.label
1828
+ }
1829
+ ),
1830
+ action.dialog?.type === "dialog" ? /* @__PURE__ */ jsx(
1831
+ DocumentActionConfirmDialog,
1832
+ {
1833
+ ...action.dialog,
1834
+ variant: action.dialog?.variant ?? action.variant,
1835
+ isOpen: dialogId === action.id,
1836
+ onClose: handleClose
1837
+ }
1838
+ ) : null,
1839
+ action.dialog?.type === "modal" ? /* @__PURE__ */ jsx(
1840
+ DocumentActionModal,
1841
+ {
1842
+ ...action.dialog,
1843
+ onModalClose: handleClose,
1844
+ isOpen: dialogId === action.id
1845
+ }
1846
+ ) : null
1847
+ ] });
1980
1848
  };
1981
- UpdateAction.type = "update";
1982
- const UNPUBLISH_DRAFT_OPTIONS = {
1983
- KEEP: "keep",
1984
- DISCARD: "discard"
1985
- };
1986
- const UnpublishAction$1 = ({
1987
- activeTab,
1988
- documentId,
1989
- model,
1990
- collectionType,
1991
- document
1849
+ const DocumentActionsMenu = ({
1850
+ actions: actions2,
1851
+ children,
1852
+ label,
1853
+ variant = "tertiary"
1992
1854
  }) => {
1855
+ const [isOpen, setIsOpen] = React.useState(false);
1856
+ const [dialogId, setDialogId] = React.useState(null);
1993
1857
  const { formatMessage } = useIntl();
1994
- const { schema } = useDoc();
1995
- const canPublish = useDocumentRBAC("UnpublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1996
- const { unpublish } = useDocumentActions();
1997
- const [{ query }] = useQueryParams();
1998
- const params = React.useMemo(() => buildValidParams(query), [query]);
1999
1858
  const { toggleNotification } = useNotification();
2000
- const [shouldKeepDraft, setShouldKeepDraft] = React.useState(true);
2001
- const isDocumentModified = document?.status === "modified";
2002
- const handleChange = (value) => {
2003
- setShouldKeepDraft(value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
2004
- };
2005
- if (!schema?.options?.draftAndPublish) {
2006
- return null;
2007
- }
2008
- return {
2009
- disabled: !canPublish || activeTab === "published" || document?.status !== "published" && document?.status !== "modified",
2010
- label: formatMessage({
2011
- id: "app.utils.unpublish",
2012
- defaultMessage: "Unpublish"
2013
- }),
2014
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2015
- onClick: async () => {
2016
- if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
2017
- if (!documentId) {
2018
- console.error(
2019
- "You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
2020
- );
1859
+ const isDisabled = actions2.every((action) => action.disabled) || actions2.length === 0;
1860
+ const handleClick = (action) => async (e) => {
1861
+ const { onClick = () => false, dialog, id } = action;
1862
+ const muteDialog = await onClick(e);
1863
+ if (dialog && !muteDialog) {
1864
+ switch (dialog.type) {
1865
+ case "notification":
2021
1866
  toggleNotification({
2022
- message: formatMessage({
2023
- id: "content-manager.actions.unpublish.error",
2024
- defaultMessage: "An error occurred while trying to unpublish the document."
2025
- }),
2026
- type: "danger"
1867
+ title: dialog.title,
1868
+ message: dialog.content,
1869
+ type: dialog.status,
1870
+ timeout: dialog.timeout,
1871
+ onClose: dialog.onClose
2027
1872
  });
2028
- }
2029
- return;
1873
+ break;
1874
+ case "dialog":
1875
+ case "modal":
1876
+ setDialogId(id);
2030
1877
  }
2031
- await unpublish({
2032
- collectionType,
2033
- model,
2034
- documentId,
2035
- params
2036
- });
2037
- },
2038
- dialog: isDocumentModified ? {
2039
- type: "dialog",
2040
- title: formatMessage({
2041
- id: "app.components.ConfirmDialog.title",
2042
- defaultMessage: "Confirmation"
2043
- }),
2044
- content: /* @__PURE__ */ jsxs(Flex, { alignItems: "flex-start", direction: "column", gap: 6, children: [
2045
- /* @__PURE__ */ jsxs(Flex, { width: "100%", direction: "column", gap: 2, children: [
2046
- /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2047
- /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2048
- id: "content-manager.actions.unpublish.dialog.body",
2049
- defaultMessage: "Are you sure?"
1878
+ }
1879
+ };
1880
+ const handleClose = () => {
1881
+ setDialogId(null);
1882
+ setIsOpen(false);
1883
+ };
1884
+ return /* @__PURE__ */ jsxs(Menu.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
1885
+ /* @__PURE__ */ jsxs(
1886
+ Menu.Trigger,
1887
+ {
1888
+ disabled: isDisabled,
1889
+ size: "S",
1890
+ endIcon: null,
1891
+ paddingTop: "4px",
1892
+ paddingLeft: "7px",
1893
+ paddingRight: "7px",
1894
+ variant,
1895
+ children: [
1896
+ /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
1897
+ /* @__PURE__ */ jsx(VisuallyHidden, { tag: "span", children: label || formatMessage({
1898
+ id: "content-manager.containers.edit.panels.default.more-actions",
1899
+ defaultMessage: "More document actions"
2050
1900
  }) })
2051
- ] }),
2052
- /* @__PURE__ */ jsxs(
2053
- Radio.Group,
2054
- {
2055
- defaultValue: UNPUBLISH_DRAFT_OPTIONS.KEEP,
2056
- name: "discard-options",
2057
- "aria-label": formatMessage({
2058
- id: "content-manager.actions.unpublish.dialog.radio-label",
2059
- defaultMessage: "Choose an option to unpublish the document."
2060
- }),
2061
- onValueChange: handleChange,
2062
- children: [
2063
- /* @__PURE__ */ jsx(Radio.Item, { checked: shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.KEEP, children: formatMessage({
2064
- id: "content-manager.actions.unpublish.dialog.option.keep-draft",
2065
- defaultMessage: "Keep draft"
2066
- }) }),
2067
- /* @__PURE__ */ jsx(Radio.Item, { checked: !shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.DISCARD, children: formatMessage({
2068
- id: "content-manager.actions.unpublish.dialog.option.replace-draft",
2069
- defaultMessage: "Replace draft"
2070
- }) })
2071
- ]
2072
- }
2073
- )
2074
- ] }),
2075
- onConfirm: async () => {
2076
- if (!documentId && collectionType !== SINGLE_TYPES) {
2077
- console.error(
2078
- "You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
2079
- );
2080
- toggleNotification({
2081
- message: formatMessage({
2082
- id: "content-manager.actions.unpublish.error",
2083
- defaultMessage: "An error occurred while trying to unpublish the document."
2084
- }),
2085
- type: "danger"
2086
- });
2087
- }
2088
- await unpublish(
1901
+ ]
1902
+ }
1903
+ ),
1904
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1905
+ actions2.map((action) => {
1906
+ return /* @__PURE__ */ jsx(
1907
+ Menu.Item,
2089
1908
  {
2090
- collectionType,
2091
- model,
2092
- documentId,
2093
- params
1909
+ disabled: action.disabled,
1910
+ onSelect: handleClick(action),
1911
+ display: "block",
1912
+ children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1913
+ /* @__PURE__ */ jsxs(
1914
+ Flex,
1915
+ {
1916
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1917
+ gap: 2,
1918
+ tag: "span",
1919
+ children: [
1920
+ /* @__PURE__ */ jsx(
1921
+ Flex,
1922
+ {
1923
+ tag: "span",
1924
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1925
+ children: action.icon
1926
+ }
1927
+ ),
1928
+ action.label
1929
+ ]
1930
+ }
1931
+ ),
1932
+ action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1933
+ Flex,
1934
+ {
1935
+ alignItems: "center",
1936
+ background: "alternative100",
1937
+ borderStyle: "solid",
1938
+ borderColor: "alternative200",
1939
+ borderWidth: "1px",
1940
+ height: 5,
1941
+ paddingLeft: 2,
1942
+ paddingRight: 2,
1943
+ hasRadius: true,
1944
+ color: "alternative600",
1945
+ children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1946
+ }
1947
+ )
1948
+ ] })
2094
1949
  },
2095
- !shouldKeepDraft
1950
+ action.id
2096
1951
  );
2097
- }
2098
- } : void 0,
2099
- variant: "danger",
2100
- position: ["panel", "table-row"]
1952
+ }),
1953
+ children
1954
+ ] }),
1955
+ actions2.map((action) => {
1956
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
1957
+ action.dialog?.type === "dialog" ? /* @__PURE__ */ jsx(
1958
+ DocumentActionConfirmDialog,
1959
+ {
1960
+ ...action.dialog,
1961
+ variant: action.variant,
1962
+ isOpen: dialogId === action.id,
1963
+ onClose: handleClose
1964
+ }
1965
+ ) : null,
1966
+ action.dialog?.type === "modal" ? /* @__PURE__ */ jsx(
1967
+ DocumentActionModal,
1968
+ {
1969
+ ...action.dialog,
1970
+ onModalClose: handleClose,
1971
+ isOpen: dialogId === action.id
1972
+ }
1973
+ ) : null
1974
+ ] }, action.id);
1975
+ })
1976
+ ] });
1977
+ };
1978
+ const convertActionVariantToColor = (variant = "secondary") => {
1979
+ switch (variant) {
1980
+ case "danger":
1981
+ return "danger600";
1982
+ case "secondary":
1983
+ return void 0;
1984
+ case "success":
1985
+ return "success600";
1986
+ default:
1987
+ return "primary600";
1988
+ }
1989
+ };
1990
+ const convertActionVariantToIconColor = (variant = "secondary") => {
1991
+ switch (variant) {
1992
+ case "danger":
1993
+ return "danger600";
1994
+ case "secondary":
1995
+ return "neutral500";
1996
+ case "success":
1997
+ return "success600";
1998
+ default:
1999
+ return "primary600";
2000
+ }
2001
+ };
2002
+ const DocumentActionConfirmDialog = ({
2003
+ onClose,
2004
+ onCancel,
2005
+ onConfirm,
2006
+ title,
2007
+ content,
2008
+ isOpen,
2009
+ variant = "secondary"
2010
+ }) => {
2011
+ const { formatMessage } = useIntl();
2012
+ const handleClose = async () => {
2013
+ if (onCancel) {
2014
+ await onCancel();
2015
+ }
2016
+ onClose();
2017
+ };
2018
+ const handleConfirm = async () => {
2019
+ if (onConfirm) {
2020
+ await onConfirm();
2021
+ }
2022
+ onClose();
2101
2023
  };
2024
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2025
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2026
+ /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
2027
+ /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
2028
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
2029
+ id: "app.components.Button.cancel",
2030
+ defaultMessage: "Cancel"
2031
+ }) }) }),
2032
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
2033
+ id: "app.components.Button.confirm",
2034
+ defaultMessage: "Confirm"
2035
+ }) })
2036
+ ] })
2037
+ ] }) });
2102
2038
  };
2103
- UnpublishAction$1.type = "unpublish";
2104
- const DiscardAction = ({
2039
+ const DocumentActionModal = ({
2040
+ isOpen,
2041
+ title,
2042
+ onClose,
2043
+ footer: Footer,
2044
+ content: Content,
2045
+ onModalClose
2046
+ }) => {
2047
+ const handleClose = () => {
2048
+ if (onClose) {
2049
+ onClose();
2050
+ }
2051
+ onModalClose();
2052
+ };
2053
+ return /* @__PURE__ */ jsx(Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
2054
+ /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: title }) }),
2055
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsx(Modal.Body, { children: Content }),
2056
+ typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
2057
+ ] }) });
2058
+ };
2059
+ const PublishAction$1 = ({
2105
2060
  activeTab,
2106
2061
  documentId,
2107
2062
  model,
2108
2063
  collectionType,
2064
+ meta,
2109
2065
  document
2110
2066
  }) => {
2111
- const { formatMessage } = useIntl();
2112
2067
  const { schema } = useDoc();
2113
- const canUpdate = useDocumentRBAC("DiscardAction", ({ canUpdate: canUpdate2 }) => canUpdate2);
2114
- const { discard } = useDocumentActions();
2115
- const [{ query }] = useQueryParams();
2116
- const params = React.useMemo(() => buildValidParams(query), [query]);
2117
- if (!schema?.options?.draftAndPublish) {
2118
- return null;
2119
- }
2120
- return {
2121
- disabled: !canUpdate || activeTab === "published" || document?.status !== "modified",
2122
- label: formatMessage({
2123
- id: "content-manager.actions.discard.label",
2124
- defaultMessage: "Discard changes"
2125
- }),
2126
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2127
- position: ["panel", "table-row"],
2128
- variant: "danger",
2129
- dialog: {
2130
- type: "dialog",
2131
- title: formatMessage({
2132
- id: "app.components.ConfirmDialog.title",
2133
- defaultMessage: "Confirmation"
2134
- }),
2135
- content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
2136
- /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2137
- /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2138
- id: "content-manager.actions.discard.dialog.body",
2139
- defaultMessage: "Are you sure?"
2140
- }) })
2141
- ] }),
2142
- onConfirm: async () => {
2143
- await discard({
2144
- collectionType,
2145
- model,
2146
- documentId,
2147
- params
2148
- });
2149
- }
2150
- }
2151
- };
2152
- };
2153
- DiscardAction.type = "discard";
2154
- const StyledCrossCircle = styled(CrossCircle)`
2155
- path {
2156
- fill: currentColor;
2157
- }
2158
- `;
2159
- const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2160
- const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2161
- const RelativeTime = React.forwardRef(
2162
- ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
2163
- const { formatRelativeTime, formatDate, formatTime } = useIntl();
2164
- const interval = intervalToDuration({
2165
- start: timestamp,
2166
- end: Date.now()
2167
- // see https://github.com/date-fns/date-fns/issues/2891 – No idea why it's all partial it returns it every time.
2168
- });
2169
- const unit = intervals.find((intervalUnit) => {
2170
- return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2171
- });
2172
- const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2173
- const customInterval = customIntervals.find(
2174
- (custom) => interval[custom.unit] < custom.threshold
2175
- );
2176
- const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, { numeric: "auto" });
2177
- return /* @__PURE__ */ jsx(
2178
- "time",
2179
- {
2180
- ref: forwardedRef,
2181
- dateTime: timestamp.toISOString(),
2182
- role: "time",
2183
- title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
2184
- ...restProps,
2185
- children: displayText
2186
- }
2187
- );
2188
- }
2189
- );
2190
- const getDisplayName = ({
2191
- firstname,
2192
- lastname,
2193
- username,
2194
- email
2195
- } = {}) => {
2196
- if (username) {
2197
- return username;
2198
- }
2199
- if (firstname) {
2200
- return `${firstname} ${lastname ?? ""}`.trim();
2201
- }
2202
- return email ?? "";
2203
- };
2204
- const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2205
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2206
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2207
- return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2208
- };
2209
- const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2210
- const { formatMessage } = useIntl();
2068
+ const navigate = useNavigate();
2069
+ const { toggleNotification } = useNotification();
2070
+ const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2071
+ const isListView = useMatch(LIST_PATH) !== null;
2211
2072
  const isCloning = useMatch(CLONE_PATH) !== null;
2212
- const title = isCreating ? formatMessage({
2213
- id: "content-manager.containers.edit.title.new",
2214
- defaultMessage: "Create an entry"
2215
- }) : documentTitle;
2216
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2217
- /* @__PURE__ */ jsx(BackButton, {}),
2218
- /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2219
- /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2220
- /* @__PURE__ */ jsx(HeaderToolbar, {})
2221
- ] }),
2222
- status ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2223
- ] });
2224
- };
2225
- const HeaderToolbar = () => {
2226
2073
  const { formatMessage } = useIntl();
2227
- const isCloning = useMatch(CLONE_PATH) !== null;
2074
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
2075
+ const { publish } = useDocumentActions();
2228
2076
  const [
2229
- {
2230
- query: { status = "draft" }
2077
+ countDraftRelations,
2078
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2079
+ ] = useLazyGetDraftRelationCountQuery();
2080
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React.useState(0);
2081
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React.useState(0);
2082
+ const [{ query, rawQuery }] = useQueryParams();
2083
+ const params = React.useMemo(() => buildValidParams(query), [query]);
2084
+ const modified = useForm("PublishAction", ({ modified: modified2 }) => modified2);
2085
+ const setSubmitting = useForm("PublishAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
2086
+ const isSubmitting = useForm("PublishAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
2087
+ const validate = useForm("PublishAction", (state) => state.validate);
2088
+ const setErrors = useForm("PublishAction", (state) => state.setErrors);
2089
+ const formValues = useForm("PublishAction", ({ values }) => values);
2090
+ React.useEffect(() => {
2091
+ if (isErrorDraftRelations) {
2092
+ toggleNotification({
2093
+ type: "danger",
2094
+ message: formatMessage({
2095
+ id: getTranslation("error.records.fetch-draft-relatons"),
2096
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2097
+ })
2098
+ });
2231
2099
  }
2232
- ] = useQueryParams();
2233
- const { model, id, document, meta, collectionType } = useDoc();
2234
- const plugins = useStrapiApp("HeaderToolbar", (state) => state.plugins);
2235
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
2236
- /* @__PURE__ */ jsx(
2237
- DescriptionComponentRenderer,
2238
- {
2239
- props: {
2240
- activeTab: status,
2241
- model,
2242
- documentId: id,
2243
- document: isCloning ? void 0 : document,
2244
- meta: isCloning ? void 0 : meta,
2245
- collectionType
2246
- },
2247
- descriptions: plugins["content-manager"].apis.getHeaderActions(),
2248
- children: (actions2) => {
2249
- if (actions2.length > 0) {
2250
- return /* @__PURE__ */ jsx(HeaderActions, { actions: actions2 });
2251
- } else {
2252
- return null;
2253
- }
2100
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2101
+ React.useEffect(() => {
2102
+ const localDraftRelations = /* @__PURE__ */ new Set();
2103
+ const extractDraftRelations = (data) => {
2104
+ const relations = data.connect || [];
2105
+ relations.forEach((relation) => {
2106
+ if (relation.status === "draft") {
2107
+ localDraftRelations.add(relation.id);
2254
2108
  }
2255
- }
2256
- ),
2257
- /* @__PURE__ */ jsx(
2258
- DescriptionComponentRenderer,
2259
- {
2260
- props: {
2261
- activeTab: status,
2262
- model,
2263
- documentId: id,
2264
- document: isCloning ? void 0 : document,
2265
- meta: isCloning ? void 0 : meta,
2266
- collectionType
2267
- },
2268
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2269
- children: (actions2) => {
2270
- const headerActions = actions2.filter((action) => {
2271
- const positions = Array.isArray(action.position) ? action.position : [action.position];
2272
- return positions.includes("header");
2273
- });
2274
- return /* @__PURE__ */ jsx(
2275
- DocumentActionsMenu,
2276
- {
2277
- actions: headerActions,
2278
- label: formatMessage({
2279
- id: "content-manager.containers.edit.header.more-actions",
2280
- defaultMessage: "More actions"
2281
- }),
2282
- children: /* @__PURE__ */ jsx(Information, { activeTab: status })
2283
- }
2284
- );
2109
+ });
2110
+ };
2111
+ const traverseAndExtract = (data) => {
2112
+ Object.entries(data).forEach(([key, value]) => {
2113
+ if (key === "connect" && Array.isArray(value)) {
2114
+ extractDraftRelations({ connect: value });
2115
+ } else if (typeof value === "object" && value !== null) {
2116
+ traverseAndExtract(value);
2285
2117
  }
2118
+ });
2119
+ };
2120
+ if (!documentId || modified) {
2121
+ traverseAndExtract(formValues);
2122
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2123
+ }
2124
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2125
+ React.useEffect(() => {
2126
+ if (!document || !document.documentId || isListView) {
2127
+ return;
2128
+ }
2129
+ const fetchDraftRelationsCount = async () => {
2130
+ const { data, error } = await countDraftRelations({
2131
+ collectionType,
2132
+ model,
2133
+ documentId,
2134
+ params
2135
+ });
2136
+ if (error) {
2137
+ throw error;
2286
2138
  }
2287
- )
2288
- ] });
2289
- };
2290
- const Information = ({ activeTab }) => {
2291
- const { formatMessage } = useIntl();
2292
- const { document, meta } = useDoc();
2293
- if (!document || !document.id) {
2139
+ if (data) {
2140
+ setServerCountOfDraftRelations(data.data);
2141
+ }
2142
+ };
2143
+ fetchDraftRelationsCount();
2144
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
2145
+ const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
2146
+ if (!schema?.options?.draftAndPublish) {
2294
2147
  return null;
2295
2148
  }
2296
- const createAndUpdateDocument = activeTab === "draft" ? document : meta?.availableStatus.find((status) => status.publishedAt === null);
2297
- const publishDocument = activeTab === "published" ? document : meta?.availableStatus.find((status) => status.publishedAt !== null);
2298
- const creator = createAndUpdateDocument?.[CREATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[CREATED_BY_ATTRIBUTE_NAME]) : null;
2299
- const updator = createAndUpdateDocument?.[UPDATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[UPDATED_BY_ATTRIBUTE_NAME]) : null;
2300
- const information = [
2301
- {
2302
- isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2303
- label: formatMessage({
2304
- id: "content-manager.containers.edit.information.last-published.label",
2305
- defaultMessage: "Last published"
2306
- }),
2307
- value: formatMessage(
2149
+ const performPublish = async () => {
2150
+ setSubmitting(true);
2151
+ try {
2152
+ const { errors } = await validate(true, {
2153
+ status: "published"
2154
+ });
2155
+ if (errors) {
2156
+ toggleNotification({
2157
+ type: "danger",
2158
+ message: formatMessage({
2159
+ id: "content-manager.validation.error",
2160
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2161
+ })
2162
+ });
2163
+ return;
2164
+ }
2165
+ const res = await publish(
2308
2166
  {
2309
- id: "content-manager.containers.edit.information.last-published.value",
2310
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2167
+ collectionType,
2168
+ model,
2169
+ documentId,
2170
+ params
2311
2171
  },
2312
- {
2313
- time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
2314
- isAnonymous: !publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME],
2315
- author: publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME] ? getDisplayName(publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME]) : null
2316
- }
2317
- )
2172
+ formValues
2173
+ );
2174
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2175
+ navigate({
2176
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2177
+ search: rawQuery
2178
+ });
2179
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2180
+ setErrors(formatValidationErrors(res.error));
2181
+ }
2182
+ } finally {
2183
+ setSubmitting(false);
2184
+ }
2185
+ };
2186
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2187
+ const enableDraftRelationsCount = false;
2188
+ const hasDraftRelations = enableDraftRelationsCount;
2189
+ return {
2190
+ /**
2191
+ * Disabled when:
2192
+ * - currently if you're cloning a document we don't support publish & clone at the same time.
2193
+ * - the form is submitting
2194
+ * - the active tab is the published tab
2195
+ * - the document is already published & not modified
2196
+ * - the document is being created & not modified
2197
+ * - the user doesn't have the permission to publish
2198
+ */
2199
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
2200
+ label: formatMessage({
2201
+ id: "app.utils.publish",
2202
+ defaultMessage: "Publish"
2203
+ }),
2204
+ onClick: async () => {
2205
+ await performPublish();
2318
2206
  },
2319
- {
2320
- isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2321
- label: formatMessage({
2322
- id: "content-manager.containers.edit.information.last-draft.label",
2323
- defaultMessage: "Last draft"
2207
+ dialog: hasDraftRelations ? {
2208
+ type: "dialog",
2209
+ variant: "danger",
2210
+ footer: null,
2211
+ title: formatMessage({
2212
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2213
+ defaultMessage: "Confirmation"
2324
2214
  }),
2325
- value: formatMessage(
2215
+ content: formatMessage(
2326
2216
  {
2327
- id: "content-manager.containers.edit.information.last-draft.value",
2328
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2217
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2218
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2329
2219
  },
2330
2220
  {
2331
- time: /* @__PURE__ */ jsx(
2332
- RelativeTime,
2333
- {
2334
- timestamp: new Date(createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME])
2335
- }
2336
- ),
2337
- isAnonymous: !updator,
2338
- author: updator
2221
+ count: totalDraftRelations
2339
2222
  }
2340
- )
2341
- },
2342
- {
2343
- isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2344
- label: formatMessage({
2345
- id: "content-manager.containers.edit.information.document.label",
2346
- defaultMessage: "Document"
2347
- }),
2348
- value: formatMessage(
2349
- {
2350
- id: "content-manager.containers.edit.information.document.value",
2351
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2352
- },
2353
- {
2354
- time: /* @__PURE__ */ jsx(
2355
- RelativeTime,
2356
- {
2357
- timestamp: new Date(createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME])
2358
- }
2359
- ),
2360
- isAnonymous: !creator,
2361
- author: creator
2223
+ ),
2224
+ onConfirm: async () => {
2225
+ await performPublish();
2226
+ }
2227
+ } : void 0
2228
+ };
2229
+ };
2230
+ PublishAction$1.type = "publish";
2231
+ const UpdateAction = ({
2232
+ activeTab,
2233
+ documentId,
2234
+ model,
2235
+ collectionType
2236
+ }) => {
2237
+ const navigate = useNavigate();
2238
+ const { toggleNotification } = useNotification();
2239
+ const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2240
+ const cloneMatch = useMatch(CLONE_PATH);
2241
+ const isCloning = cloneMatch !== null;
2242
+ const { formatMessage } = useIntl();
2243
+ const { create, update, clone } = useDocumentActions();
2244
+ const [{ query, rawQuery }] = useQueryParams();
2245
+ const params = React.useMemo(() => buildValidParams(query), [query]);
2246
+ const isSubmitting = useForm("UpdateAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
2247
+ const modified = useForm("UpdateAction", ({ modified: modified2 }) => modified2);
2248
+ const setSubmitting = useForm("UpdateAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
2249
+ const document = useForm("UpdateAction", ({ values }) => values);
2250
+ const validate = useForm("UpdateAction", (state) => state.validate);
2251
+ const setErrors = useForm("UpdateAction", (state) => state.setErrors);
2252
+ const resetForm = useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
2253
+ return {
2254
+ /**
2255
+ * Disabled when:
2256
+ * - the form is submitting
2257
+ * - the document is not modified & we're not cloning (you can save a clone entity straight away)
2258
+ * - the active tab is the published tab
2259
+ */
2260
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
2261
+ label: formatMessage({
2262
+ id: "content-manager.containers.Edit.save",
2263
+ defaultMessage: "Save"
2264
+ }),
2265
+ onClick: async () => {
2266
+ setSubmitting(true);
2267
+ try {
2268
+ const { errors } = await validate(true, {
2269
+ status: "draft"
2270
+ });
2271
+ if (errors) {
2272
+ toggleNotification({
2273
+ type: "danger",
2274
+ message: formatMessage({
2275
+ id: "content-manager.validation.error",
2276
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2277
+ })
2278
+ });
2279
+ return;
2362
2280
  }
2363
- )
2364
- }
2365
- ].filter((info) => info.isDisplayed);
2366
- return /* @__PURE__ */ jsx(
2367
- Flex,
2368
- {
2369
- borderWidth: "1px 0 0 0",
2370
- borderStyle: "solid",
2371
- borderColor: "neutral150",
2372
- direction: "column",
2373
- marginTop: 2,
2374
- tag: "dl",
2375
- padding: 5,
2376
- gap: 3,
2377
- alignItems: "flex-start",
2378
- marginLeft: "-0.4rem",
2379
- marginRight: "-0.4rem",
2380
- width: "calc(100% + 8px)",
2381
- children: information.map((info) => /* @__PURE__ */ jsxs(Flex, { gap: 1, direction: "column", alignItems: "flex-start", children: [
2382
- /* @__PURE__ */ jsx(Typography, { tag: "dt", variant: "pi", fontWeight: "bold", children: info.label }),
2383
- /* @__PURE__ */ jsx(Typography, { tag: "dd", variant: "pi", textColor: "neutral600", children: info.value })
2384
- ] }, info.label))
2281
+ if (isCloning) {
2282
+ const res = await clone(
2283
+ {
2284
+ model,
2285
+ documentId: cloneMatch.params.origin,
2286
+ params
2287
+ },
2288
+ document
2289
+ );
2290
+ if ("data" in res) {
2291
+ navigate(
2292
+ {
2293
+ pathname: `../${res.data.documentId}`,
2294
+ search: rawQuery
2295
+ },
2296
+ { relative: "path" }
2297
+ );
2298
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2299
+ setErrors(formatValidationErrors(res.error));
2300
+ }
2301
+ } else if (documentId || collectionType === SINGLE_TYPES) {
2302
+ const res = await update(
2303
+ {
2304
+ collectionType,
2305
+ model,
2306
+ documentId,
2307
+ params
2308
+ },
2309
+ document
2310
+ );
2311
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2312
+ setErrors(formatValidationErrors(res.error));
2313
+ } else {
2314
+ resetForm();
2315
+ }
2316
+ } else {
2317
+ const res = await create(
2318
+ {
2319
+ model,
2320
+ params
2321
+ },
2322
+ document
2323
+ );
2324
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2325
+ navigate(
2326
+ {
2327
+ pathname: `../${res.data.documentId}`,
2328
+ search: rawQuery
2329
+ },
2330
+ { replace: true, relative: "path" }
2331
+ );
2332
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2333
+ setErrors(formatValidationErrors(res.error));
2334
+ }
2335
+ }
2336
+ } finally {
2337
+ setSubmitting(false);
2338
+ }
2385
2339
  }
2386
- );
2340
+ };
2387
2341
  };
2388
- const HeaderActions = ({ actions: actions2 }) => {
2389
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2390
- if ("options" in action) {
2391
- return /* @__PURE__ */ jsx(
2392
- SingleSelect,
2393
- {
2394
- size: "S",
2395
- disabled: action.disabled,
2396
- "aria-label": action.label,
2397
- onChange: action.onSelect,
2398
- value: action.value,
2399
- children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2400
- },
2401
- action.id
2402
- );
2403
- } else {
2404
- return null;
2405
- }
2406
- }) });
2342
+ UpdateAction.type = "update";
2343
+ const UNPUBLISH_DRAFT_OPTIONS = {
2344
+ KEEP: "keep",
2345
+ DISCARD: "discard"
2407
2346
  };
2408
- const ConfigureTheViewAction = ({ collectionType, model }) => {
2409
- const navigate = useNavigate();
2347
+ const UnpublishAction$1 = ({
2348
+ activeTab,
2349
+ documentId,
2350
+ model,
2351
+ collectionType,
2352
+ document
2353
+ }) => {
2410
2354
  const { formatMessage } = useIntl();
2411
- return {
2412
- label: formatMessage({
2413
- id: "app.links.configure-view",
2414
- defaultMessage: "Configure the view"
2415
- }),
2416
- icon: /* @__PURE__ */ jsx(ListPlus, {}),
2417
- onClick: () => {
2418
- navigate(`../${collectionType}/${model}/configurations/edit`);
2419
- },
2420
- position: "header"
2355
+ const { schema } = useDoc();
2356
+ const canPublish = useDocumentRBAC("UnpublishAction", ({ canPublish: canPublish2 }) => canPublish2);
2357
+ const { unpublish } = useDocumentActions();
2358
+ const [{ query }] = useQueryParams();
2359
+ const params = React.useMemo(() => buildValidParams(query), [query]);
2360
+ const { toggleNotification } = useNotification();
2361
+ const [shouldKeepDraft, setShouldKeepDraft] = React.useState(true);
2362
+ const isDocumentModified = document?.status === "modified";
2363
+ const handleChange = (value) => {
2364
+ setShouldKeepDraft(value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
2421
2365
  };
2422
- };
2423
- ConfigureTheViewAction.type = "configure-the-view";
2424
- const EditTheModelAction = ({ model }) => {
2425
- const navigate = useNavigate();
2426
- const { formatMessage } = useIntl();
2366
+ if (!schema?.options?.draftAndPublish) {
2367
+ return null;
2368
+ }
2427
2369
  return {
2370
+ disabled: !canPublish || activeTab === "published" || document?.status !== "published" && document?.status !== "modified",
2428
2371
  label: formatMessage({
2429
- id: "content-manager.link-to-ctb",
2430
- defaultMessage: "Edit the model"
2372
+ id: "app.utils.unpublish",
2373
+ defaultMessage: "Unpublish"
2431
2374
  }),
2432
- icon: /* @__PURE__ */ jsx(Pencil, {}),
2433
- onClick: () => {
2434
- navigate(`/plugins/content-type-builder/content-types/${model}`);
2375
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2376
+ onClick: async () => {
2377
+ if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
2378
+ if (!documentId) {
2379
+ console.error(
2380
+ "You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
2381
+ );
2382
+ toggleNotification({
2383
+ message: formatMessage({
2384
+ id: "content-manager.actions.unpublish.error",
2385
+ defaultMessage: "An error occurred while trying to unpublish the document."
2386
+ }),
2387
+ type: "danger"
2388
+ });
2389
+ }
2390
+ return;
2391
+ }
2392
+ await unpublish({
2393
+ collectionType,
2394
+ model,
2395
+ documentId,
2396
+ params
2397
+ });
2435
2398
  },
2436
- position: "header"
2399
+ dialog: isDocumentModified ? {
2400
+ type: "dialog",
2401
+ title: formatMessage({
2402
+ id: "app.components.ConfirmDialog.title",
2403
+ defaultMessage: "Confirmation"
2404
+ }),
2405
+ content: /* @__PURE__ */ jsxs(Flex, { alignItems: "flex-start", direction: "column", gap: 6, children: [
2406
+ /* @__PURE__ */ jsxs(Flex, { width: "100%", direction: "column", gap: 2, children: [
2407
+ /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2408
+ /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2409
+ id: "content-manager.actions.unpublish.dialog.body",
2410
+ defaultMessage: "Are you sure?"
2411
+ }) })
2412
+ ] }),
2413
+ /* @__PURE__ */ jsxs(
2414
+ Radio.Group,
2415
+ {
2416
+ defaultValue: UNPUBLISH_DRAFT_OPTIONS.KEEP,
2417
+ name: "discard-options",
2418
+ "aria-label": formatMessage({
2419
+ id: "content-manager.actions.unpublish.dialog.radio-label",
2420
+ defaultMessage: "Choose an option to unpublish the document."
2421
+ }),
2422
+ onValueChange: handleChange,
2423
+ children: [
2424
+ /* @__PURE__ */ jsx(Radio.Item, { checked: shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.KEEP, children: formatMessage({
2425
+ id: "content-manager.actions.unpublish.dialog.option.keep-draft",
2426
+ defaultMessage: "Keep draft"
2427
+ }) }),
2428
+ /* @__PURE__ */ jsx(Radio.Item, { checked: !shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.DISCARD, children: formatMessage({
2429
+ id: "content-manager.actions.unpublish.dialog.option.replace-draft",
2430
+ defaultMessage: "Replace draft"
2431
+ }) })
2432
+ ]
2433
+ }
2434
+ )
2435
+ ] }),
2436
+ onConfirm: async () => {
2437
+ if (!documentId && collectionType !== SINGLE_TYPES) {
2438
+ console.error(
2439
+ "You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
2440
+ );
2441
+ toggleNotification({
2442
+ message: formatMessage({
2443
+ id: "content-manager.actions.unpublish.error",
2444
+ defaultMessage: "An error occurred while trying to unpublish the document."
2445
+ }),
2446
+ type: "danger"
2447
+ });
2448
+ }
2449
+ await unpublish(
2450
+ {
2451
+ collectionType,
2452
+ model,
2453
+ documentId,
2454
+ params
2455
+ },
2456
+ !shouldKeepDraft
2457
+ );
2458
+ }
2459
+ } : void 0,
2460
+ variant: "danger",
2461
+ position: ["panel", "table-row"]
2437
2462
  };
2438
2463
  };
2439
- EditTheModelAction.type = "edit-the-model";
2440
- const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2441
- const navigate = useNavigate();
2464
+ UnpublishAction$1.type = "unpublish";
2465
+ const DiscardAction = ({
2466
+ activeTab,
2467
+ documentId,
2468
+ model,
2469
+ collectionType,
2470
+ document
2471
+ }) => {
2442
2472
  const { formatMessage } = useIntl();
2443
- const listViewPathMatch = useMatch(LIST_PATH);
2444
- const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2445
- const { delete: deleteAction } = useDocumentActions();
2446
- const { toggleNotification } = useNotification();
2447
- const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2473
+ const { schema } = useDoc();
2474
+ const canUpdate = useDocumentRBAC("DiscardAction", ({ canUpdate: canUpdate2 }) => canUpdate2);
2475
+ const { discard } = useDocumentActions();
2476
+ const [{ query }] = useQueryParams();
2477
+ const params = React.useMemo(() => buildValidParams(query), [query]);
2478
+ if (!schema?.options?.draftAndPublish) {
2479
+ return null;
2480
+ }
2448
2481
  return {
2449
- disabled: !canDelete || !document,
2482
+ disabled: !canUpdate || activeTab === "published" || document?.status !== "modified",
2450
2483
  label: formatMessage({
2451
- id: "content-manager.actions.delete.label",
2452
- defaultMessage: "Delete document"
2484
+ id: "content-manager.actions.discard.label",
2485
+ defaultMessage: "Discard changes"
2453
2486
  }),
2454
- icon: /* @__PURE__ */ jsx(Trash, {}),
2487
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2488
+ position: ["panel", "table-row"],
2489
+ variant: "danger",
2455
2490
  dialog: {
2456
2491
  type: "dialog",
2457
2492
  title: formatMessage({
@@ -2461,92 +2496,90 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2461
2496
  content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
2462
2497
  /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2463
2498
  /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2464
- id: "content-manager.actions.delete.dialog.body",
2499
+ id: "content-manager.actions.discard.dialog.body",
2465
2500
  defaultMessage: "Are you sure?"
2466
2501
  }) })
2467
2502
  ] }),
2468
2503
  onConfirm: async () => {
2469
- if (!listViewPathMatch) {
2470
- setSubmitting(true);
2471
- }
2472
- try {
2473
- if (!documentId && collectionType !== SINGLE_TYPES) {
2474
- console.error(
2475
- "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2476
- );
2477
- toggleNotification({
2478
- message: formatMessage({
2479
- id: "content-manager.actions.delete.error",
2480
- defaultMessage: "An error occurred while trying to delete the document."
2481
- }),
2482
- type: "danger"
2483
- });
2484
- return;
2485
- }
2486
- const res = await deleteAction({
2487
- documentId,
2488
- model,
2489
- collectionType,
2490
- params: {
2491
- locale: "*"
2492
- }
2493
- });
2494
- if (!("error" in res)) {
2495
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2496
- }
2497
- } finally {
2498
- if (!listViewPathMatch) {
2499
- setSubmitting(false);
2500
- }
2501
- }
2504
+ await discard({
2505
+ collectionType,
2506
+ model,
2507
+ documentId,
2508
+ params
2509
+ });
2510
+ }
2511
+ }
2512
+ };
2513
+ };
2514
+ DiscardAction.type = "discard";
2515
+ const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2516
+ const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2517
+ const RelativeTime = React.forwardRef(
2518
+ ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
2519
+ const { formatRelativeTime, formatDate, formatTime } = useIntl();
2520
+ const interval = intervalToDuration({
2521
+ start: timestamp,
2522
+ end: Date.now()
2523
+ // see https://github.com/date-fns/date-fns/issues/2891 – No idea why it's all partial it returns it every time.
2524
+ });
2525
+ const unit = intervals.find((intervalUnit) => {
2526
+ return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2527
+ });
2528
+ const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2529
+ const customInterval = customIntervals.find(
2530
+ (custom) => interval[custom.unit] < custom.threshold
2531
+ );
2532
+ const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, { numeric: "auto" });
2533
+ return /* @__PURE__ */ jsx(
2534
+ "time",
2535
+ {
2536
+ ref: forwardedRef,
2537
+ dateTime: timestamp.toISOString(),
2538
+ role: "time",
2539
+ title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
2540
+ ...restProps,
2541
+ children: displayText
2502
2542
  }
2503
- },
2504
- variant: "danger",
2505
- position: ["header", "table-row"]
2506
- };
2543
+ );
2544
+ }
2545
+ );
2546
+ const getDisplayName = ({
2547
+ firstname,
2548
+ lastname,
2549
+ username,
2550
+ email
2551
+ } = {}) => {
2552
+ if (username) {
2553
+ return username;
2554
+ }
2555
+ if (firstname) {
2556
+ return `${firstname} ${lastname ?? ""}`.trim();
2557
+ }
2558
+ return email ?? "";
2507
2559
  };
2508
- DeleteAction$1.type = "delete";
2509
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2510
- const Panels = () => {
2511
- const isCloning = useMatch(CLONE_PATH) !== null;
2512
- const [
2513
- {
2514
- query: { status }
2515
- }
2516
- ] = useQueryParams({
2517
- status: "draft"
2518
- });
2519
- const { model, id, document, meta, collectionType } = useDoc();
2520
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2521
- const props = {
2522
- activeTab: status,
2523
- model,
2524
- documentId: id,
2525
- document: isCloning ? void 0 : document,
2526
- meta: isCloning ? void 0 : meta,
2527
- collectionType
2528
- };
2529
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2530
- DescriptionComponentRenderer,
2531
- {
2532
- props,
2533
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2534
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2535
- }
2536
- ) });
2560
+ const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2561
+ const DocumentStatus = ({ status = "draft", ...restProps }) => {
2562
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2563
+ return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2537
2564
  };
2538
- const ActionsPanel = () => {
2565
+ const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2539
2566
  const { formatMessage } = useIntl();
2540
- return {
2541
- title: formatMessage({
2542
- id: "content-manager.containers.edit.panels.default.title",
2543
- defaultMessage: "Document"
2544
- }),
2545
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2546
- };
2567
+ const isCloning = useMatch(CLONE_PATH) !== null;
2568
+ const title = isCreating ? formatMessage({
2569
+ id: "content-manager.containers.edit.title.new",
2570
+ defaultMessage: "Create an entry"
2571
+ }) : documentTitle;
2572
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2573
+ /* @__PURE__ */ jsx(BackButton, {}),
2574
+ /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2575
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2576
+ /* @__PURE__ */ jsx(HeaderToolbar, {})
2577
+ ] }),
2578
+ status ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2579
+ ] });
2547
2580
  };
2548
- ActionsPanel.type = "actions";
2549
- const ActionsPanelContent = () => {
2581
+ const HeaderToolbar = () => {
2582
+ const { formatMessage } = useIntl();
2550
2583
  const isCloning = useMatch(CLONE_PATH) !== null;
2551
2584
  const [
2552
2585
  {
@@ -2554,355 +2587,432 @@ const ActionsPanelContent = () => {
2554
2587
  }
2555
2588
  ] = useQueryParams();
2556
2589
  const { model, id, document, meta, collectionType } = useDoc();
2557
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2558
- const props = {
2559
- activeTab: status,
2560
- model,
2561
- documentId: id,
2562
- document: isCloning ? void 0 : document,
2563
- meta: isCloning ? void 0 : meta,
2564
- collectionType
2565
- };
2566
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2590
+ const plugins = useStrapiApp("HeaderToolbar", (state) => state.plugins);
2591
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
2567
2592
  /* @__PURE__ */ jsx(
2568
2593
  DescriptionComponentRenderer,
2569
2594
  {
2570
- props,
2571
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2572
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2595
+ props: {
2596
+ activeTab: status,
2597
+ model,
2598
+ documentId: id,
2599
+ document: isCloning ? void 0 : document,
2600
+ meta: isCloning ? void 0 : meta,
2601
+ collectionType
2602
+ },
2603
+ descriptions: plugins["content-manager"].apis.getHeaderActions(),
2604
+ children: (actions2) => {
2605
+ if (actions2.length > 0) {
2606
+ return /* @__PURE__ */ jsx(HeaderActions, { actions: actions2 });
2607
+ } else {
2608
+ return null;
2609
+ }
2610
+ }
2573
2611
  }
2574
2612
  ),
2575
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2613
+ /* @__PURE__ */ jsx(
2614
+ DescriptionComponentRenderer,
2615
+ {
2616
+ props: {
2617
+ activeTab: status,
2618
+ model,
2619
+ documentId: id,
2620
+ document: isCloning ? void 0 : document,
2621
+ meta: isCloning ? void 0 : meta,
2622
+ collectionType
2623
+ },
2624
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
2625
+ children: (actions2) => {
2626
+ const headerActions = actions2.filter((action) => {
2627
+ const positions = Array.isArray(action.position) ? action.position : [action.position];
2628
+ return positions.includes("header");
2629
+ });
2630
+ return /* @__PURE__ */ jsx(
2631
+ DocumentActionsMenu,
2632
+ {
2633
+ actions: headerActions,
2634
+ label: formatMessage({
2635
+ id: "content-manager.containers.edit.header.more-actions",
2636
+ defaultMessage: "More actions"
2637
+ }),
2638
+ children: /* @__PURE__ */ jsx(Information, { activeTab: status })
2639
+ }
2640
+ );
2641
+ }
2642
+ }
2643
+ )
2576
2644
  ] });
2577
2645
  };
2578
- const Panel = React.forwardRef(({ children, title }, ref) => {
2579
- return /* @__PURE__ */ jsxs(
2580
- Flex,
2646
+ const Information = ({ activeTab }) => {
2647
+ const { formatMessage } = useIntl();
2648
+ const { document, meta } = useDoc();
2649
+ if (!document || !document.id) {
2650
+ return null;
2651
+ }
2652
+ const createAndUpdateDocument = activeTab === "draft" ? document : meta?.availableStatus.find((status) => status.publishedAt === null);
2653
+ const publishDocument = activeTab === "published" ? document : meta?.availableStatus.find((status) => status.publishedAt !== null);
2654
+ const creator = createAndUpdateDocument?.[CREATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[CREATED_BY_ATTRIBUTE_NAME]) : null;
2655
+ const updator = createAndUpdateDocument?.[UPDATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[UPDATED_BY_ATTRIBUTE_NAME]) : null;
2656
+ const information = [
2581
2657
  {
2582
- ref,
2583
- tag: "aside",
2584
- "aria-labelledby": "additional-information",
2585
- background: "neutral0",
2586
- borderColor: "neutral150",
2587
- hasRadius: true,
2588
- paddingBottom: 4,
2589
- paddingLeft: 4,
2590
- paddingRight: 4,
2591
- paddingTop: 4,
2592
- shadow: "tableShadow",
2593
- gap: 3,
2594
- direction: "column",
2595
- justifyContent: "stretch",
2596
- alignItems: "flex-start",
2597
- children: [
2598
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2599
- children
2600
- ]
2601
- }
2602
- );
2603
- });
2604
- const HOOKS = {
2605
- /**
2606
- * Hook that allows to mutate the displayed headers of the list view table
2607
- * @constant
2608
- * @type {string}
2609
- */
2610
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2611
- /**
2612
- * Hook that allows to mutate the CM's collection types links pre-set filters
2613
- * @constant
2614
- * @type {string}
2615
- */
2616
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2617
- /**
2618
- * Hook that allows to mutate the CM's edit view layout
2619
- * @constant
2620
- * @type {string}
2621
- */
2622
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2623
- /**
2624
- * Hook that allows to mutate the CM's single types links pre-set filters
2625
- * @constant
2626
- * @type {string}
2627
- */
2628
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2629
- };
2630
- const contentTypesApi = contentManagerApi.injectEndpoints({
2631
- endpoints: (builder) => ({
2632
- getContentTypeConfiguration: builder.query({
2633
- query: (uid) => ({
2634
- url: `/content-manager/content-types/${uid}/configuration`,
2635
- method: "GET"
2658
+ isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2659
+ label: formatMessage({
2660
+ id: "content-manager.containers.edit.information.last-published.label",
2661
+ defaultMessage: "Published"
2636
2662
  }),
2637
- transformResponse: (response) => response.data,
2638
- providesTags: (_result, _error, uid) => [
2639
- { type: "ContentTypesConfiguration", id: uid },
2640
- { type: "ContentTypeSettings", id: "LIST" }
2641
- ]
2642
- }),
2643
- getAllContentTypeSettings: builder.query({
2644
- query: () => "/content-manager/content-types-settings",
2645
- transformResponse: (response) => response.data,
2646
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2647
- }),
2648
- updateContentTypeConfiguration: builder.mutation({
2649
- query: ({ uid, ...body }) => ({
2650
- url: `/content-manager/content-types/${uid}/configuration`,
2651
- method: "PUT",
2652
- data: body
2663
+ value: formatMessage(
2664
+ {
2665
+ id: "content-manager.containers.edit.information.last-published.value",
2666
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2667
+ },
2668
+ {
2669
+ time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
2670
+ isAnonymous: !publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME],
2671
+ author: publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME] ? getDisplayName(publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME]) : null
2672
+ }
2673
+ )
2674
+ },
2675
+ {
2676
+ isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2677
+ label: formatMessage({
2678
+ id: "content-manager.containers.edit.information.last-draft.label",
2679
+ defaultMessage: "Updated"
2680
+ }),
2681
+ value: formatMessage(
2682
+ {
2683
+ id: "content-manager.containers.edit.information.last-draft.value",
2684
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2685
+ },
2686
+ {
2687
+ time: /* @__PURE__ */ jsx(
2688
+ RelativeTime,
2689
+ {
2690
+ timestamp: new Date(createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME])
2691
+ }
2692
+ ),
2693
+ isAnonymous: !updator,
2694
+ author: updator
2695
+ }
2696
+ )
2697
+ },
2698
+ {
2699
+ isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2700
+ label: formatMessage({
2701
+ id: "content-manager.containers.edit.information.document.label",
2702
+ defaultMessage: "Created"
2653
2703
  }),
2654
- transformResponse: (response) => response.data,
2655
- invalidatesTags: (_result, _error, { uid }) => [
2656
- { type: "ContentTypesConfiguration", id: uid },
2657
- { type: "ContentTypeSettings", id: "LIST" },
2658
- // Is this necessary?
2659
- { type: "InitialData" }
2660
- ]
2661
- })
2662
- })
2663
- });
2664
- const {
2665
- useGetContentTypeConfigurationQuery,
2666
- useGetAllContentTypeSettingsQuery,
2667
- useUpdateContentTypeConfigurationMutation
2668
- } = contentTypesApi;
2669
- const checkIfAttributeIsDisplayable = (attribute) => {
2670
- const { type } = attribute;
2671
- if (type === "relation") {
2672
- return !attribute.relation.toLowerCase().includes("morph");
2673
- }
2674
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2675
- };
2676
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2677
- if (!mainFieldName) {
2678
- return void 0;
2679
- }
2680
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2681
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2682
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2704
+ value: formatMessage(
2705
+ {
2706
+ id: "content-manager.containers.edit.information.document.value",
2707
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2708
+ },
2709
+ {
2710
+ time: /* @__PURE__ */ jsx(
2711
+ RelativeTime,
2712
+ {
2713
+ timestamp: new Date(createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME])
2714
+ }
2715
+ ),
2716
+ isAnonymous: !creator,
2717
+ author: creator
2718
+ }
2719
+ )
2720
+ }
2721
+ ].filter((info) => info.isDisplayed);
2722
+ return /* @__PURE__ */ jsx(
2723
+ Flex,
2724
+ {
2725
+ borderWidth: "1px 0 0 0",
2726
+ borderStyle: "solid",
2727
+ borderColor: "neutral150",
2728
+ direction: "column",
2729
+ marginTop: 2,
2730
+ tag: "dl",
2731
+ padding: 5,
2732
+ gap: 3,
2733
+ alignItems: "flex-start",
2734
+ marginLeft: "-0.4rem",
2735
+ marginRight: "-0.4rem",
2736
+ width: "calc(100% + 8px)",
2737
+ children: information.map((info) => /* @__PURE__ */ jsxs(Flex, { gap: 1, direction: "column", alignItems: "flex-start", children: [
2738
+ /* @__PURE__ */ jsx(Typography, { tag: "dt", variant: "pi", fontWeight: "bold", children: info.label }),
2739
+ /* @__PURE__ */ jsx(Typography, { tag: "dd", variant: "pi", textColor: "neutral600", children: info.value })
2740
+ ] }, info.label))
2741
+ }
2683
2742
  );
2684
- return {
2685
- name: mainFieldName,
2686
- type: mainFieldType ?? "string"
2687
- };
2688
- };
2689
- const DEFAULT_SETTINGS = {
2690
- bulkable: false,
2691
- filterable: false,
2692
- searchable: false,
2693
- pagination: false,
2694
- defaultSortBy: "",
2695
- defaultSortOrder: "asc",
2696
- mainField: "id",
2697
- pageSize: 10
2698
2743
  };
2699
- const useDocumentLayout = (model) => {
2700
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2701
- const [{ query }] = useQueryParams();
2702
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2703
- const { toggleNotification } = useNotification();
2704
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2705
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2706
- const {
2707
- data,
2708
- isLoading: isLoadingConfigs,
2709
- error,
2710
- isFetching: isFetchingConfigs
2711
- } = useGetContentTypeConfigurationQuery(model);
2712
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2713
- React.useEffect(() => {
2714
- if (error) {
2715
- toggleNotification({
2716
- type: "danger",
2717
- message: formatAPIError(error)
2718
- });
2744
+ const HeaderActions = ({ actions: actions2 }) => {
2745
+ const [dialogId, setDialogId] = React.useState(null);
2746
+ const handleClick = (action) => async (e) => {
2747
+ if (!("options" in action)) {
2748
+ const { onClick = () => false, dialog, id } = action;
2749
+ const muteDialog = await onClick(e);
2750
+ if (dialog && !muteDialog) {
2751
+ e.preventDefault();
2752
+ setDialogId(id);
2753
+ }
2719
2754
  }
2720
- }, [error, formatAPIError, toggleNotification]);
2721
- const editLayout = React.useMemo(
2722
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2723
- layout: [],
2724
- components: {},
2725
- metadatas: {},
2726
- options: {},
2727
- settings: DEFAULT_SETTINGS
2728
- },
2729
- [data, isLoading, schemas, schema, components]
2730
- );
2731
- const listLayout = React.useMemo(() => {
2732
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2733
- layout: [],
2734
- metadatas: {},
2735
- options: {},
2736
- settings: DEFAULT_SETTINGS
2737
- };
2738
- }, [data, isLoading, schemas, schema, components]);
2739
- const { layout: edit } = React.useMemo(
2740
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2741
- layout: editLayout,
2742
- query
2743
- }),
2744
- [editLayout, query, runHookWaterfall]
2745
- );
2746
- return {
2747
- error,
2748
- isLoading,
2749
- edit,
2750
- list: listLayout
2751
2755
  };
2752
- };
2753
- const useDocLayout = () => {
2754
- const { model } = useDoc();
2755
- return useDocumentLayout(model);
2756
- };
2757
- const formatEditLayout = (data, {
2758
- schemas,
2759
- schema,
2760
- components
2761
- }) => {
2762
- let currentPanelIndex = 0;
2763
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2764
- data.contentType.layouts.edit,
2765
- schema?.attributes,
2766
- data.contentType.metadatas,
2767
- { configurations: data.components, schemas: components },
2768
- schemas
2769
- ).reduce((panels, row) => {
2770
- if (row.some((field) => field.type === "dynamiczone")) {
2771
- panels.push([row]);
2772
- currentPanelIndex += 2;
2756
+ const handleClose = () => {
2757
+ setDialogId(null);
2758
+ };
2759
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2760
+ if (action.options) {
2761
+ return /* @__PURE__ */ jsx(
2762
+ SingleSelect,
2763
+ {
2764
+ size: "S",
2765
+ onChange: action.onSelect,
2766
+ "aria-label": action.label,
2767
+ ...action,
2768
+ children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2769
+ },
2770
+ action.id
2771
+ );
2773
2772
  } else {
2774
- if (!panels[currentPanelIndex]) {
2775
- panels.push([]);
2773
+ if (action.type === "icon") {
2774
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
2775
+ /* @__PURE__ */ jsx(
2776
+ IconButton,
2777
+ {
2778
+ disabled: action.disabled,
2779
+ label: action.label,
2780
+ size: "S",
2781
+ onClick: handleClick(action),
2782
+ children: action.icon
2783
+ }
2784
+ ),
2785
+ action.dialog ? /* @__PURE__ */ jsx(
2786
+ HeaderActionDialog,
2787
+ {
2788
+ ...action.dialog,
2789
+ isOpen: dialogId === action.id,
2790
+ onClose: handleClose
2791
+ }
2792
+ ) : null
2793
+ ] }, action.id);
2776
2794
  }
2777
- panels[currentPanelIndex].push(row);
2778
2795
  }
2779
- return panels;
2780
- }, []);
2781
- const componentEditAttributes = Object.entries(data.components).reduce(
2782
- (acc, [uid, configuration]) => {
2783
- acc[uid] = {
2784
- layout: convertEditLayoutToFieldLayouts(
2785
- configuration.layouts.edit,
2786
- components[uid].attributes,
2787
- configuration.metadatas
2788
- ),
2789
- settings: {
2790
- ...configuration.settings,
2791
- icon: components[uid].info.icon,
2792
- displayName: components[uid].info.displayName
2793
- }
2794
- };
2795
- return acc;
2796
- },
2797
- {}
2798
- );
2799
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2800
- (acc, [attribute, metadata]) => {
2801
- return {
2802
- ...acc,
2803
- [attribute]: metadata.edit
2804
- };
2796
+ }) });
2797
+ };
2798
+ const HeaderActionDialog = ({
2799
+ onClose,
2800
+ onCancel,
2801
+ title,
2802
+ content: Content,
2803
+ isOpen
2804
+ }) => {
2805
+ const handleClose = async () => {
2806
+ if (onCancel) {
2807
+ await onCancel();
2808
+ }
2809
+ onClose();
2810
+ };
2811
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2812
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2813
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
2814
+ ] }) });
2815
+ };
2816
+ const ConfigureTheViewAction = ({ collectionType, model }) => {
2817
+ const navigate = useNavigate();
2818
+ const { formatMessage } = useIntl();
2819
+ return {
2820
+ label: formatMessage({
2821
+ id: "app.links.configure-view",
2822
+ defaultMessage: "Configure the view"
2823
+ }),
2824
+ icon: /* @__PURE__ */ jsx(ListPlus, {}),
2825
+ onClick: () => {
2826
+ navigate(`../${collectionType}/${model}/configurations/edit`);
2805
2827
  },
2806
- {}
2807
- );
2828
+ position: "header"
2829
+ };
2830
+ };
2831
+ ConfigureTheViewAction.type = "configure-the-view";
2832
+ const EditTheModelAction = ({ model }) => {
2833
+ const navigate = useNavigate();
2834
+ const { formatMessage } = useIntl();
2808
2835
  return {
2809
- layout: panelledEditAttributes,
2810
- components: componentEditAttributes,
2811
- metadatas: editMetadatas,
2812
- settings: {
2813
- ...data.contentType.settings,
2814
- displayName: schema?.info.displayName
2836
+ label: formatMessage({
2837
+ id: "content-manager.link-to-ctb",
2838
+ defaultMessage: "Edit the model"
2839
+ }),
2840
+ icon: /* @__PURE__ */ jsx(Pencil, {}),
2841
+ onClick: () => {
2842
+ navigate(`/plugins/content-type-builder/content-types/${model}`);
2815
2843
  },
2816
- options: {
2817
- ...schema?.options,
2818
- ...schema?.pluginOptions,
2819
- ...data.contentType.options
2820
- }
2844
+ position: "header"
2821
2845
  };
2822
2846
  };
2823
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2824
- return rows.map(
2825
- (row) => row.map((field) => {
2826
- const attribute = attributes[field.name];
2827
- if (!attribute) {
2828
- return null;
2847
+ EditTheModelAction.type = "edit-the-model";
2848
+ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2849
+ const navigate = useNavigate();
2850
+ const { formatMessage } = useIntl();
2851
+ const listViewPathMatch = useMatch(LIST_PATH);
2852
+ const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2853
+ const { delete: deleteAction } = useDocumentActions();
2854
+ const { toggleNotification } = useNotification();
2855
+ const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2856
+ const isLocalized = document?.locale != null;
2857
+ return {
2858
+ disabled: !canDelete || !document,
2859
+ label: formatMessage(
2860
+ {
2861
+ id: "content-manager.actions.delete.label",
2862
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2863
+ },
2864
+ { isLocalized }
2865
+ ),
2866
+ icon: /* @__PURE__ */ jsx(Trash, {}),
2867
+ dialog: {
2868
+ type: "dialog",
2869
+ title: formatMessage({
2870
+ id: "app.components.ConfirmDialog.title",
2871
+ defaultMessage: "Confirmation"
2872
+ }),
2873
+ content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
2874
+ /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2875
+ /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2876
+ id: "content-manager.actions.delete.dialog.body",
2877
+ defaultMessage: "Are you sure?"
2878
+ }) })
2879
+ ] }),
2880
+ onConfirm: async () => {
2881
+ if (!listViewPathMatch) {
2882
+ setSubmitting(true);
2883
+ }
2884
+ try {
2885
+ if (!documentId && collectionType !== SINGLE_TYPES) {
2886
+ console.error(
2887
+ "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2888
+ );
2889
+ toggleNotification({
2890
+ message: formatMessage({
2891
+ id: "content-manager.actions.delete.error",
2892
+ defaultMessage: "An error occurred while trying to delete the document."
2893
+ }),
2894
+ type: "danger"
2895
+ });
2896
+ return;
2897
+ }
2898
+ const res = await deleteAction({
2899
+ documentId,
2900
+ model,
2901
+ collectionType,
2902
+ params: {
2903
+ locale: "*"
2904
+ }
2905
+ });
2906
+ if (!("error" in res)) {
2907
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2908
+ }
2909
+ } finally {
2910
+ if (!listViewPathMatch) {
2911
+ setSubmitting(false);
2912
+ }
2913
+ }
2829
2914
  }
2830
- const { edit: metadata } = metadatas[field.name];
2831
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2832
- return {
2833
- attribute,
2834
- disabled: !metadata.editable,
2835
- hint: metadata.description,
2836
- label: metadata.label ?? "",
2837
- name: field.name,
2838
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2839
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2840
- schemas,
2841
- components: components?.schemas ?? {}
2842
- }),
2843
- placeholder: metadata.placeholder ?? "",
2844
- required: attribute.required ?? false,
2845
- size: field.size,
2846
- unique: "unique" in attribute ? attribute.unique : false,
2847
- visible: metadata.visible ?? true,
2848
- type: attribute.type
2849
- };
2850
- }).filter((field) => field !== null)
2851
- );
2852
- };
2853
- const formatListLayout = (data, {
2854
- schemas,
2855
- schema,
2856
- components
2857
- }) => {
2858
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2859
- (acc, [attribute, metadata]) => {
2860
- return {
2861
- ...acc,
2862
- [attribute]: metadata.list
2863
- };
2864
2915
  },
2865
- {}
2866
- );
2867
- const listAttributes = convertListLayoutToFieldLayouts(
2868
- data.contentType.layouts.list,
2869
- schema?.attributes,
2870
- listMetadatas,
2871
- { configurations: data.components, schemas: components },
2872
- schemas
2873
- );
2874
- return {
2875
- layout: listAttributes,
2876
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2877
- metadatas: listMetadatas,
2878
- options: {
2879
- ...schema?.options,
2880
- ...schema?.pluginOptions,
2881
- ...data.contentType.options
2916
+ variant: "danger",
2917
+ position: ["header", "table-row"]
2918
+ };
2919
+ };
2920
+ DeleteAction$1.type = "delete";
2921
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2922
+ const Panels = () => {
2923
+ const isCloning = useMatch(CLONE_PATH) !== null;
2924
+ const [
2925
+ {
2926
+ query: { status }
2927
+ }
2928
+ ] = useQueryParams({
2929
+ status: "draft"
2930
+ });
2931
+ const { model, id, document, meta, collectionType } = useDoc();
2932
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
2933
+ const props = {
2934
+ activeTab: status,
2935
+ model,
2936
+ documentId: id,
2937
+ document: isCloning ? void 0 : document,
2938
+ meta: isCloning ? void 0 : meta,
2939
+ collectionType
2940
+ };
2941
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2942
+ DescriptionComponentRenderer,
2943
+ {
2944
+ props,
2945
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2946
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2882
2947
  }
2948
+ ) });
2949
+ };
2950
+ const ActionsPanel = () => {
2951
+ const { formatMessage } = useIntl();
2952
+ return {
2953
+ title: formatMessage({
2954
+ id: "content-manager.containers.edit.panels.default.title",
2955
+ defaultMessage: "Entry"
2956
+ }),
2957
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2883
2958
  };
2884
2959
  };
2885
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2886
- return columns.map((name) => {
2887
- const attribute = attributes[name];
2888
- if (!attribute) {
2889
- return null;
2960
+ ActionsPanel.type = "actions";
2961
+ const ActionsPanelContent = () => {
2962
+ const isCloning = useMatch(CLONE_PATH) !== null;
2963
+ const [
2964
+ {
2965
+ query: { status = "draft" }
2890
2966
  }
2891
- const metadata = metadatas[name];
2892
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2893
- return {
2894
- attribute,
2895
- label: metadata.label ?? "",
2896
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2897
- schemas,
2898
- components: components?.schemas ?? {}
2899
- }),
2900
- name,
2901
- searchable: metadata.searchable ?? true,
2902
- sortable: metadata.sortable ?? true
2903
- };
2904
- }).filter((field) => field !== null);
2967
+ ] = useQueryParams();
2968
+ const { model, id, document, meta, collectionType } = useDoc();
2969
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2970
+ const props = {
2971
+ activeTab: status,
2972
+ model,
2973
+ documentId: id,
2974
+ document: isCloning ? void 0 : document,
2975
+ meta: isCloning ? void 0 : meta,
2976
+ collectionType
2977
+ };
2978
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2979
+ /* @__PURE__ */ jsx(
2980
+ DescriptionComponentRenderer,
2981
+ {
2982
+ props,
2983
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
2984
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2985
+ }
2986
+ ),
2987
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2988
+ ] });
2905
2989
  };
2990
+ const Panel = React.forwardRef(({ children, title }, ref) => {
2991
+ return /* @__PURE__ */ jsxs(
2992
+ Flex,
2993
+ {
2994
+ ref,
2995
+ tag: "aside",
2996
+ "aria-labelledby": "additional-information",
2997
+ background: "neutral0",
2998
+ borderColor: "neutral150",
2999
+ hasRadius: true,
3000
+ paddingBottom: 4,
3001
+ paddingLeft: 4,
3002
+ paddingRight: 4,
3003
+ paddingTop: 4,
3004
+ shadow: "tableShadow",
3005
+ gap: 3,
3006
+ direction: "column",
3007
+ justifyContent: "stretch",
3008
+ alignItems: "flex-start",
3009
+ children: [
3010
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
3011
+ children
3012
+ ]
3013
+ }
3014
+ );
3015
+ });
2906
3016
  const ConfirmBulkActionDialog = ({
2907
3017
  onToggleDialog,
2908
3018
  isOpen = false,
@@ -2941,6 +3051,7 @@ const ConfirmDialogPublishAll = ({
2941
3051
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2942
3052
  const { model, schema } = useDoc();
2943
3053
  const [{ query }] = useQueryParams();
3054
+ const enableDraftRelationsCount = false;
2944
3055
  const {
2945
3056
  data: countDraftRelations = 0,
2946
3057
  isLoading,
@@ -2952,7 +3063,7 @@ const ConfirmDialogPublishAll = ({
2952
3063
  locale: query?.plugins?.i18n?.locale
2953
3064
  },
2954
3065
  {
2955
- skip: selectedEntries.length === 0
3066
+ skip: !enableDraftRelationsCount
2956
3067
  }
2957
3068
  );
2958
3069
  React.useEffect(() => {
@@ -3137,7 +3248,7 @@ const SelectedEntriesTableContent = ({
3137
3248
  status: row.status
3138
3249
  }
3139
3250
  ) }),
3140
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3251
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3141
3252
  IconButton,
3142
3253
  {
3143
3254
  tag: Link,
@@ -3160,9 +3271,10 @@ const SelectedEntriesTableContent = ({
3160
3271
  ),
3161
3272
  target: "_blank",
3162
3273
  marginLeft: "auto",
3163
- children: /* @__PURE__ */ jsx(Pencil, {})
3274
+ variant: "ghost",
3275
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3164
3276
  }
3165
- ) })
3277
+ ) }) })
3166
3278
  ] }, row.id)) })
3167
3279
  ] });
3168
3280
  };
@@ -3555,7 +3667,7 @@ const TableActions = ({ document }) => {
3555
3667
  DescriptionComponentRenderer,
3556
3668
  {
3557
3669
  props,
3558
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3670
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3559
3671
  children: (actions2) => {
3560
3672
  const tableRowActions = actions2.filter((action) => {
3561
3673
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3666,7 +3778,7 @@ const CloneAction = ({ model, documentId }) => {
3666
3778
  }),
3667
3779
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3668
3780
  footer: ({ onClose }) => {
3669
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3781
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3670
3782
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3671
3783
  id: "cancel",
3672
3784
  defaultMessage: "Cancel"
@@ -3897,7 +4009,7 @@ const index = {
3897
4009
  app.router.addRoute({
3898
4010
  path: "content-manager/*",
3899
4011
  lazy: async () => {
3900
- const { Layout } = await import("./layout-LOFMsdWo.mjs");
4012
+ const { Layout } = await import("./layout-2CfjL0T9.mjs");
3901
4013
  return {
3902
4014
  Component: Layout
3903
4015
  };
@@ -3914,7 +4026,7 @@ const index = {
3914
4026
  async registerTrads({ locales }) {
3915
4027
  const importedTrads = await Promise.all(
3916
4028
  locales.map((locale) => {
3917
- 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 }) => {
4029
+ 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-DKV44jRb.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 }) => {
3918
4030
  return {
3919
4031
  data: prefixPluginTranslations(data, PLUGIN_ID),
3920
4032
  locale
@@ -3935,13 +4047,15 @@ export {
3935
4047
  BulkActionsRenderer as B,
3936
4048
  COLLECTION_TYPES as C,
3937
4049
  DocumentStatus as D,
3938
- DEFAULT_SETTINGS as E,
3939
- convertEditLayoutToFieldLayouts as F,
3940
- useDocument as G,
4050
+ extractContentTypeComponents as E,
4051
+ DEFAULT_SETTINGS as F,
4052
+ convertEditLayoutToFieldLayouts as G,
3941
4053
  HOOKS as H,
3942
4054
  InjectionZone as I,
3943
- index as J,
3944
- useDocumentActions as K,
4055
+ useDocument as J,
4056
+ index as K,
4057
+ useContentManagerContext as L,
4058
+ useDocumentActions as M,
3945
4059
  Panels as P,
3946
4060
  RelativeTime as R,
3947
4061
  SINGLE_TYPES as S,
@@ -3959,18 +4073,18 @@ export {
3959
4073
  PERMISSIONS as k,
3960
4074
  DocumentRBAC as l,
3961
4075
  DOCUMENT_META_FIELDS as m,
3962
- useDocLayout as n,
3963
- useGetContentTypeConfigurationQuery as o,
3964
- CREATOR_FIELDS as p,
3965
- getMainField as q,
3966
- getDisplayName as r,
4076
+ CLONE_PATH as n,
4077
+ useDocLayout as o,
4078
+ useGetContentTypeConfigurationQuery as p,
4079
+ CREATOR_FIELDS as q,
4080
+ getMainField as r,
3967
4081
  setInitialData as s,
3968
- checkIfAttributeIsDisplayable as t,
4082
+ getDisplayName as t,
3969
4083
  useContentTypeSchema as u,
3970
- useGetAllDocumentsQuery as v,
3971
- convertListLayoutToFieldLayouts as w,
3972
- capitalise as x,
3973
- useUpdateContentTypeConfigurationMutation as y,
3974
- extractContentTypeComponents as z
4084
+ checkIfAttributeIsDisplayable as v,
4085
+ useGetAllDocumentsQuery as w,
4086
+ convertListLayoutToFieldLayouts as x,
4087
+ capitalise as y,
4088
+ useUpdateContentTypeConfigurationMutation as z
3975
4089
  };
3976
- //# sourceMappingURL=index-BgiNQ7Q9.mjs.map
4090
+ //# sourceMappingURL=index-wnqzm4Q8.mjs.map