@strapi/content-manager 0.0.0-experimental.a53a4b1c8f7981a689823cdd719105671e1c6392 → 0.0.0-experimental.a6728ad43ac70ae19dabb624dbfca1f2d9610a86

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 (130) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-DmwmiFQy.mjs → ComponentConfigurationPage-DJ5voqEK.mjs} +3 -3
  3. package/dist/_chunks/{ComponentConfigurationPage-DmwmiFQy.mjs.map → ComponentConfigurationPage-DJ5voqEK.mjs.map} +1 -1
  4. package/dist/_chunks/{ComponentConfigurationPage-C-49MccQ.js → ComponentConfigurationPage-_6osrv39.js} +3 -3
  5. package/dist/_chunks/{ComponentConfigurationPage-C-49MccQ.js.map → ComponentConfigurationPage-_6osrv39.js.map} +1 -1
  6. package/dist/_chunks/{EditConfigurationPage-JT3E7NZy.mjs → EditConfigurationPage-CZofxSLy.mjs} +3 -3
  7. package/dist/_chunks/{EditConfigurationPage-JT3E7NZy.mjs.map → EditConfigurationPage-CZofxSLy.mjs.map} +1 -1
  8. package/dist/_chunks/{EditConfigurationPage-DjFJw56M.js → EditConfigurationPage-ZN3s568V.js} +3 -3
  9. package/dist/_chunks/{EditConfigurationPage-DjFJw56M.js.map → EditConfigurationPage-ZN3s568V.js.map} +1 -1
  10. package/dist/_chunks/{EditViewPage-zT3fBr4Y.js → EditViewPage-Co2IKQZH.js} +19 -8
  11. package/dist/_chunks/EditViewPage-Co2IKQZH.js.map +1 -0
  12. package/dist/_chunks/{EditViewPage-CPj61RMh.mjs → EditViewPage-HYljoEY7.mjs} +19 -8
  13. package/dist/_chunks/EditViewPage-HYljoEY7.mjs.map +1 -0
  14. package/dist/_chunks/{Field-dha5VnIQ.mjs → Field-BOPUMZ1u.mjs} +194 -141
  15. package/dist/_chunks/Field-BOPUMZ1u.mjs.map +1 -0
  16. package/dist/_chunks/{Field-Boxf9Ajp.js → Field-G9CkFUtP.js} +196 -143
  17. package/dist/_chunks/Field-G9CkFUtP.js.map +1 -0
  18. package/dist/_chunks/{Form-DHrru2AV.mjs → Form-CDwNp7pU.mjs} +35 -16
  19. package/dist/_chunks/Form-CDwNp7pU.mjs.map +1 -0
  20. package/dist/_chunks/{Form-y5g1SRsh.js → Form-crsbkGxI.js} +35 -16
  21. package/dist/_chunks/Form-crsbkGxI.js.map +1 -0
  22. package/dist/_chunks/{History-Bru_KoeP.mjs → History-BDZrgfZ3.mjs} +44 -19
  23. package/dist/_chunks/History-BDZrgfZ3.mjs.map +1 -0
  24. package/dist/_chunks/{History-CqN6K7SX.js → History-CWcM9HnW.js} +44 -19
  25. package/dist/_chunks/History-CWcM9HnW.js.map +1 -0
  26. package/dist/_chunks/{ListConfigurationPage-D8wGABj0.mjs → ListConfigurationPage-BZ3ScUna.mjs} +20 -8
  27. package/dist/_chunks/ListConfigurationPage-BZ3ScUna.mjs.map +1 -0
  28. package/dist/_chunks/{ListConfigurationPage-R_p-SbHZ.js → ListConfigurationPage-DGzoQD_I.js} +20 -8
  29. package/dist/_chunks/ListConfigurationPage-DGzoQD_I.js.map +1 -0
  30. package/dist/_chunks/{ListViewPage-pEw_zug9.js → ListViewPage-BBAC9aPu.js} +60 -42
  31. package/dist/_chunks/ListViewPage-BBAC9aPu.js.map +1 -0
  32. package/dist/_chunks/{ListViewPage-SID6TRb9.mjs → ListViewPage-CsX7tWx-.mjs} +58 -40
  33. package/dist/_chunks/ListViewPage-CsX7tWx-.mjs.map +1 -0
  34. package/dist/_chunks/{NoContentTypePage-C5dcQojD.js → NoContentTypePage-CwVDx_YC.js} +2 -2
  35. package/dist/_chunks/{NoContentTypePage-C5dcQojD.js.map → NoContentTypePage-CwVDx_YC.js.map} +1 -1
  36. package/dist/_chunks/{NoContentTypePage-CJ7UXwrQ.mjs → NoContentTypePage-LClTUPWs.mjs} +2 -2
  37. package/dist/_chunks/{NoContentTypePage-CJ7UXwrQ.mjs.map → NoContentTypePage-LClTUPWs.mjs.map} +1 -1
  38. package/dist/_chunks/{NoPermissionsPage-BtPrImPP.js → NoPermissionsPage-D2iWw-sn.js} +2 -2
  39. package/dist/_chunks/{NoPermissionsPage-BtPrImPP.js.map → NoPermissionsPage-D2iWw-sn.js.map} +1 -1
  40. package/dist/_chunks/{NoPermissionsPage-B7syEq5E.mjs → NoPermissionsPage-S4Re3FwO.mjs} +2 -2
  41. package/dist/_chunks/{NoPermissionsPage-B7syEq5E.mjs.map → NoPermissionsPage-S4Re3FwO.mjs.map} +1 -1
  42. package/dist/_chunks/{Relations-B9Crnhnn.mjs → Relations-Dmv0Tpe5.mjs} +4 -4
  43. package/dist/_chunks/Relations-Dmv0Tpe5.mjs.map +1 -0
  44. package/dist/_chunks/{Relations-DjTQ5kGB.js → Relations-jwuTFGOV.js} +4 -4
  45. package/dist/_chunks/Relations-jwuTFGOV.js.map +1 -0
  46. package/dist/_chunks/{en-fbKQxLGn.js → en-BlhnxQfj.js} +11 -9
  47. package/dist/_chunks/{en-fbKQxLGn.js.map → en-BlhnxQfj.js.map} +1 -1
  48. package/dist/_chunks/{en-Ux26r5pl.mjs → en-C8YBvRrK.mjs} +11 -9
  49. package/dist/_chunks/{en-Ux26r5pl.mjs.map → en-C8YBvRrK.mjs.map} +1 -1
  50. package/dist/_chunks/{index-DJXJw9V5.mjs → index-BmUAydCA.mjs} +977 -680
  51. package/dist/_chunks/index-BmUAydCA.mjs.map +1 -0
  52. package/dist/_chunks/{index-DVPWZkbS.js → index-CBX6KyXv.js} +958 -661
  53. package/dist/_chunks/index-CBX6KyXv.js.map +1 -0
  54. package/dist/_chunks/{layout-Bau7ZfLV.mjs → layout-ClP-DC72.mjs} +25 -12
  55. package/dist/_chunks/layout-ClP-DC72.mjs.map +1 -0
  56. package/dist/_chunks/{layout-Dm6fbiQj.js → layout-CxxkX9jY.js} +24 -11
  57. package/dist/_chunks/layout-CxxkX9jY.js.map +1 -0
  58. package/dist/_chunks/{relations-CKnpRgrN.js → relations-DIjTADIu.js} +2 -2
  59. package/dist/_chunks/{relations-CKnpRgrN.js.map → relations-DIjTADIu.js.map} +1 -1
  60. package/dist/_chunks/{relations-BH_kBSJ0.mjs → relations-op89RClB.mjs} +2 -2
  61. package/dist/_chunks/{relations-BH_kBSJ0.mjs.map → relations-op89RClB.mjs.map} +1 -1
  62. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  63. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  64. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  65. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  66. package/dist/admin/index.js +2 -1
  67. package/dist/admin/index.js.map +1 -1
  68. package/dist/admin/index.mjs +5 -4
  69. package/dist/admin/src/exports.d.ts +1 -1
  70. package/dist/admin/src/history/index.d.ts +3 -0
  71. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  72. package/dist/admin/src/hooks/useDocument.d.ts +30 -1
  73. package/dist/admin/src/index.d.ts +1 -0
  74. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -0
  75. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  76. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  77. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  78. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  79. package/dist/admin/src/pages/EditView/components/Header.d.ts +10 -11
  80. package/dist/admin/src/services/api.d.ts +1 -1
  81. package/dist/admin/src/services/components.d.ts +2 -2
  82. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  83. package/dist/admin/src/services/documents.d.ts +19 -17
  84. package/dist/admin/src/services/init.d.ts +1 -1
  85. package/dist/admin/src/services/relations.d.ts +2 -2
  86. package/dist/admin/src/services/uid.d.ts +3 -3
  87. package/dist/admin/src/utils/validation.d.ts +4 -1
  88. package/dist/server/index.js +181 -107
  89. package/dist/server/index.js.map +1 -1
  90. package/dist/server/index.mjs +182 -108
  91. package/dist/server/index.mjs.map +1 -1
  92. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  93. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  94. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  95. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  96. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  97. package/dist/server/src/history/services/history.d.ts.map +1 -1
  98. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  99. package/dist/server/src/history/services/utils.d.ts +2 -1
  100. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  101. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  102. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  103. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  104. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  105. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  106. package/dist/shared/contracts/collection-types.d.ts +3 -1
  107. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  108. package/package.json +11 -11
  109. package/dist/_chunks/EditViewPage-CPj61RMh.mjs.map +0 -1
  110. package/dist/_chunks/EditViewPage-zT3fBr4Y.js.map +0 -1
  111. package/dist/_chunks/Field-Boxf9Ajp.js.map +0 -1
  112. package/dist/_chunks/Field-dha5VnIQ.mjs.map +0 -1
  113. package/dist/_chunks/Form-DHrru2AV.mjs.map +0 -1
  114. package/dist/_chunks/Form-y5g1SRsh.js.map +0 -1
  115. package/dist/_chunks/History-Bru_KoeP.mjs.map +0 -1
  116. package/dist/_chunks/History-CqN6K7SX.js.map +0 -1
  117. package/dist/_chunks/ListConfigurationPage-D8wGABj0.mjs.map +0 -1
  118. package/dist/_chunks/ListConfigurationPage-R_p-SbHZ.js.map +0 -1
  119. package/dist/_chunks/ListViewPage-SID6TRb9.mjs.map +0 -1
  120. package/dist/_chunks/ListViewPage-pEw_zug9.js.map +0 -1
  121. package/dist/_chunks/Relations-B9Crnhnn.mjs.map +0 -1
  122. package/dist/_chunks/Relations-DjTQ5kGB.js.map +0 -1
  123. package/dist/_chunks/index-DJXJw9V5.mjs.map +0 -1
  124. package/dist/_chunks/index-DVPWZkbS.js.map +0 -1
  125. package/dist/_chunks/layout-Bau7ZfLV.mjs.map +0 -1
  126. package/dist/_chunks/layout-Dm6fbiQj.js.map +0 -1
  127. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  128. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  129. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  130. package/strapi-server.js +0 -3
@@ -1,17 +1,17 @@
1
- import { ClockCounterClockwise, CrossCircle, More, WarningCircle, ListPlus, Pencil, Trash, Check, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, 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, useQueryParams, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useTracking, useForm, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
4
- import { stringify } from "qs";
5
- import { useIntl } from "react-intl";
6
- import { useNavigate, useParams, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
3
+ import { useStrapiApp, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, useQueryParams, getYupValidationErrors, useForm, useTracking, useGuidedTour, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
7
4
  import * as React from "react";
8
5
  import { lazy } from "react";
9
- import { Button, Menu, VisuallyHidden, Flex, Box, Typography, Dialog, Modal, Radio, Status, SingleSelect, SingleSelectOption, Loader, IconButton, Tooltip, LinkButton } from "@strapi/design-system";
10
- import { styled } from "styled-components";
6
+ import { Button, Menu, VisuallyHidden, Flex, Typography, Dialog, Modal, Radio, Status, Box, SingleSelect, SingleSelectOption, IconButton, Loader, Tooltip, LinkButton } from "@strapi/design-system";
7
+ import { useIntl } from "react-intl";
8
+ import { useParams, useNavigate, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
11
9
  import * as yup from "yup";
12
10
  import { ValidationError } from "yup";
13
11
  import pipe from "lodash/fp/pipe";
14
12
  import { intervalToDuration, isPast } from "date-fns";
13
+ import { styled } from "styled-components";
14
+ import { stringify } from "qs";
15
15
  import { createSlice, combineReducers } from "@reduxjs/toolkit";
16
16
  const __variableDynamicImportRuntimeHelper = (glob, path) => {
17
17
  const v = glob[path];
@@ -49,42 +49,6 @@ const useInjectionZone = (area) => {
49
49
  const [page, position] = area.split(".");
50
50
  return contentManagerPlugin.getInjectedComponents(page, position);
51
51
  };
52
- const HistoryAction = ({ model, document }) => {
53
- const { formatMessage } = useIntl();
54
- const [{ query }] = useQueryParams();
55
- const navigate = useNavigate();
56
- const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
57
- if (!window.strapi.features.isEnabled("cms-content-history")) {
58
- return null;
59
- }
60
- return {
61
- icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
62
- label: formatMessage({
63
- id: "content-manager.history.document-action",
64
- defaultMessage: "Content History"
65
- }),
66
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
67
- disabled: (
68
- /**
69
- * The user is creating a new document.
70
- * It hasn't been saved yet, so there's no history to go to
71
- */
72
- !document || /**
73
- * The document has been created but the current dimension has never been saved.
74
- * For example, the user is creating a new locale in an existing document,
75
- * so there's no history for the document in that locale
76
- */
77
- !document.id || /**
78
- * History is only available for content types created by the user.
79
- * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
80
- * which start with `admin::` or `plugin::`
81
- */
82
- !model.startsWith("api::")
83
- ),
84
- position: "header"
85
- };
86
- };
87
- HistoryAction.type = "history";
88
52
  const ID = "id";
89
53
  const CREATED_BY_ATTRIBUTE_NAME = "createdBy";
90
54
  const UPDATED_BY_ATTRIBUTE_NAME = "updatedBy";
@@ -194,7 +158,8 @@ const contentManagerApi = adminApi.enhanceEndpoints({
194
158
  "Document",
195
159
  "InitialData",
196
160
  "HistoryVersion",
197
- "Relations"
161
+ "Relations",
162
+ "UidAvailability"
198
163
  ]
199
164
  });
200
165
  const documentApi = contentManagerApi.injectEndpoints({
@@ -208,7 +173,12 @@ const documentApi = contentManagerApi.injectEndpoints({
208
173
  params: query
209
174
  }
210
175
  }),
211
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
176
+ invalidatesTags: (_result, error, { model }) => {
177
+ if (error) {
178
+ return [];
179
+ }
180
+ return [{ type: "Document", id: `${model}_LIST` }];
181
+ }
212
182
  }),
213
183
  cloneDocument: builder.mutation({
214
184
  query: ({ model, sourceId, data, params }) => ({
@@ -219,7 +189,10 @@ const documentApi = contentManagerApi.injectEndpoints({
219
189
  params
220
190
  }
221
191
  }),
222
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
192
+ invalidatesTags: (_result, _error, { model }) => [
193
+ { type: "Document", id: `${model}_LIST` },
194
+ { type: "UidAvailability", id: model }
195
+ ]
223
196
  }),
224
197
  /**
225
198
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -236,7 +209,8 @@ const documentApi = contentManagerApi.injectEndpoints({
236
209
  }),
237
210
  invalidatesTags: (result, _error, { model }) => [
238
211
  { type: "Document", id: `${model}_LIST` },
239
- "Relations"
212
+ "Relations",
213
+ { type: "UidAvailability", id: model }
240
214
  ]
241
215
  }),
242
216
  deleteDocument: builder.mutation({
@@ -277,7 +251,8 @@ const documentApi = contentManagerApi.injectEndpoints({
277
251
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
278
252
  },
279
253
  { type: "Document", id: `${model}_LIST` },
280
- "Relations"
254
+ "Relations",
255
+ { type: "UidAvailability", id: model }
281
256
  ];
282
257
  }
283
258
  }),
@@ -295,6 +270,7 @@ const documentApi = contentManagerApi.injectEndpoints({
295
270
  }),
296
271
  providesTags: (result, _error, arg) => {
297
272
  return [
273
+ { type: "Document", id: `ALL_LIST` },
298
274
  { type: "Document", id: `${arg.model}_LIST` },
299
275
  ...result?.results.map(({ documentId }) => ({
300
276
  type: "Document",
@@ -333,6 +309,11 @@ const documentApi = contentManagerApi.injectEndpoints({
333
309
  {
334
310
  type: "Document",
335
311
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
312
+ },
313
+ // Make it easy to invalidate all individual documents queries for a model
314
+ {
315
+ type: "Document",
316
+ id: `${model}_ALL_ITEMS`
336
317
  }
337
318
  ];
338
319
  }
@@ -396,8 +377,21 @@ const documentApi = contentManagerApi.injectEndpoints({
396
377
  type: "Document",
397
378
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
398
379
  },
399
- "Relations"
380
+ "Relations",
381
+ { type: "UidAvailability", id: model }
400
382
  ];
383
+ },
384
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
385
+ const patchResult = dispatch(
386
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
387
+ Object.assign(draft.data, data);
388
+ })
389
+ );
390
+ try {
391
+ await queryFulfilled;
392
+ } catch {
393
+ patchResult.undo();
394
+ }
401
395
  }
402
396
  }),
403
397
  unpublishDocument: builder.mutation({
@@ -467,7 +461,7 @@ const buildValidParams = (query) => {
467
461
  const isBaseQueryError = (error) => {
468
462
  return error.name !== void 0;
469
463
  };
470
- const createYupSchema = (attributes = {}, components = {}) => {
464
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
471
465
  const createModelSchema = (attributes2) => yup.object().shape(
472
466
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
473
467
  if (DOCUMENT_META_FIELDS.includes(name)) {
@@ -480,7 +474,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
480
474
  addMinValidation,
481
475
  addMaxValidation,
482
476
  addRegexValidation
483
- ].map((fn) => fn(attribute));
477
+ ].map((fn) => fn(attribute, options));
484
478
  const transformSchema = pipe(...validations);
485
479
  switch (attribute.type) {
486
480
  case "component": {
@@ -581,6 +575,14 @@ const createAttributeSchema = (attribute) => {
581
575
  if (!value || typeof value === "string" && value.length === 0) {
582
576
  return true;
583
577
  }
578
+ if (typeof value === "object") {
579
+ try {
580
+ JSON.stringify(value);
581
+ return true;
582
+ } catch (err) {
583
+ return false;
584
+ }
585
+ }
584
586
  try {
585
587
  JSON.parse(value);
586
588
  return true;
@@ -599,13 +601,7 @@ const createAttributeSchema = (attribute) => {
599
601
  return yup.mixed();
600
602
  }
601
603
  };
602
- const addRequiredValidation = (attribute) => (schema) => {
603
- if (attribute.required) {
604
- return schema.required({
605
- id: translatedErrors.required.id,
606
- defaultMessage: "This field is required."
607
- });
608
- }
604
+ const nullableSchema = (schema) => {
609
605
  return schema?.nullable ? schema.nullable() : (
610
606
  // In some cases '.nullable' will not be available on the schema.
611
607
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -613,7 +609,22 @@ const addRequiredValidation = (attribute) => (schema) => {
613
609
  schema
614
610
  );
615
611
  };
616
- const addMinLengthValidation = (attribute) => (schema) => {
612
+ const addRequiredValidation = (attribute, options) => (schema) => {
613
+ if (options.status === "draft") {
614
+ return nullableSchema(schema);
615
+ }
616
+ if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
617
+ return schema.min(1, translatedErrors.required);
618
+ }
619
+ if (attribute.required && attribute.type !== "relation") {
620
+ return schema.required(translatedErrors.required);
621
+ }
622
+ return nullableSchema(schema);
623
+ };
624
+ const addMinLengthValidation = (attribute, options) => (schema) => {
625
+ if (options.status === "draft") {
626
+ return schema;
627
+ }
617
628
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
618
629
  return schema.min(attribute.minLength, {
619
630
  ...translatedErrors.minLength,
@@ -635,9 +646,31 @@ const addMaxLengthValidation = (attribute) => (schema) => {
635
646
  }
636
647
  return schema;
637
648
  };
638
- const addMinValidation = (attribute) => (schema) => {
649
+ const addMinValidation = (attribute, options) => (schema) => {
639
650
  if ("min" in attribute) {
640
651
  const min = toInteger(attribute.min);
652
+ if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
653
+ if (options.status !== "draft" && !attribute.required && "test" in schema && min) {
654
+ return schema.test(
655
+ "custom-min",
656
+ {
657
+ ...translatedErrors.min,
658
+ values: {
659
+ min: attribute.min
660
+ }
661
+ },
662
+ (value) => {
663
+ if (!value) {
664
+ return true;
665
+ }
666
+ if (Array.isArray(value) && value.length === 0) {
667
+ return true;
668
+ }
669
+ return value.length >= min;
670
+ }
671
+ );
672
+ }
673
+ }
641
674
  if ("min" in schema && min) {
642
675
  return schema.min(min, {
643
676
  ...translatedErrors.min,
@@ -756,16 +789,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
756
789
  }, {});
757
790
  return componentsByKey;
758
791
  };
759
- const useDocument = (args, opts) => {
792
+ const HOOKS = {
793
+ /**
794
+ * Hook that allows to mutate the displayed headers of the list view table
795
+ * @constant
796
+ * @type {string}
797
+ */
798
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
799
+ /**
800
+ * Hook that allows to mutate the CM's collection types links pre-set filters
801
+ * @constant
802
+ * @type {string}
803
+ */
804
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
805
+ /**
806
+ * Hook that allows to mutate the CM's edit view layout
807
+ * @constant
808
+ * @type {string}
809
+ */
810
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
811
+ /**
812
+ * Hook that allows to mutate the CM's single types links pre-set filters
813
+ * @constant
814
+ * @type {string}
815
+ */
816
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
817
+ };
818
+ const contentTypesApi = contentManagerApi.injectEndpoints({
819
+ endpoints: (builder) => ({
820
+ getContentTypeConfiguration: builder.query({
821
+ query: (uid) => ({
822
+ url: `/content-manager/content-types/${uid}/configuration`,
823
+ method: "GET"
824
+ }),
825
+ transformResponse: (response) => response.data,
826
+ providesTags: (_result, _error, uid) => [
827
+ { type: "ContentTypesConfiguration", id: uid },
828
+ { type: "ContentTypeSettings", id: "LIST" }
829
+ ]
830
+ }),
831
+ getAllContentTypeSettings: builder.query({
832
+ query: () => "/content-manager/content-types-settings",
833
+ transformResponse: (response) => response.data,
834
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
835
+ }),
836
+ updateContentTypeConfiguration: builder.mutation({
837
+ query: ({ uid, ...body }) => ({
838
+ url: `/content-manager/content-types/${uid}/configuration`,
839
+ method: "PUT",
840
+ data: body
841
+ }),
842
+ transformResponse: (response) => response.data,
843
+ invalidatesTags: (_result, _error, { uid }) => [
844
+ { type: "ContentTypesConfiguration", id: uid },
845
+ { type: "ContentTypeSettings", id: "LIST" },
846
+ // Is this necessary?
847
+ { type: "InitialData" }
848
+ ]
849
+ })
850
+ })
851
+ });
852
+ const {
853
+ useGetContentTypeConfigurationQuery,
854
+ useGetAllContentTypeSettingsQuery,
855
+ useUpdateContentTypeConfigurationMutation
856
+ } = contentTypesApi;
857
+ const checkIfAttributeIsDisplayable = (attribute) => {
858
+ const { type } = attribute;
859
+ if (type === "relation") {
860
+ return !attribute.relation.toLowerCase().includes("morph");
861
+ }
862
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
863
+ };
864
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
865
+ if (!mainFieldName) {
866
+ return void 0;
867
+ }
868
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
869
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
870
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
871
+ );
872
+ return {
873
+ name: mainFieldName,
874
+ type: mainFieldType ?? "string"
875
+ };
876
+ };
877
+ const DEFAULT_SETTINGS = {
878
+ bulkable: false,
879
+ filterable: false,
880
+ searchable: false,
881
+ pagination: false,
882
+ defaultSortBy: "",
883
+ defaultSortOrder: "asc",
884
+ mainField: "id",
885
+ pageSize: 10
886
+ };
887
+ const useDocumentLayout = (model) => {
888
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
889
+ const [{ query }] = useQueryParams();
890
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
760
891
  const { toggleNotification } = useNotification();
761
892
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
893
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
762
894
  const {
763
- currentData: data,
764
- isLoading: isLoadingDocument,
765
- isFetching: isFetchingDocument,
766
- error
767
- } = useGetDocumentQuery(args, opts);
768
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
895
+ data,
896
+ isLoading: isLoadingConfigs,
897
+ error,
898
+ isFetching: isFetchingConfigs
899
+ } = useGetContentTypeConfigurationQuery(model);
900
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
769
901
  React.useEffect(() => {
770
902
  if (error) {
771
903
  toggleNotification({
@@ -773,56 +905,269 @@ const useDocument = (args, opts) => {
773
905
  message: formatAPIError(error)
774
906
  });
775
907
  }
776
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
777
- const validationSchema = React.useMemo(() => {
778
- if (!schema) {
779
- return null;
780
- }
781
- return createYupSchema(schema.attributes, components);
782
- }, [schema, components]);
783
- const validate = React.useCallback(
784
- (document) => {
785
- if (!validationSchema) {
786
- throw new Error(
787
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
788
- );
789
- }
790
- try {
791
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
792
- return null;
793
- } catch (error2) {
794
- if (error2 instanceof ValidationError) {
795
- return getYupValidationErrors(error2);
796
- }
797
- throw error2;
798
- }
908
+ }, [error, formatAPIError, toggleNotification]);
909
+ const editLayout = React.useMemo(
910
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
911
+ layout: [],
912
+ components: {},
913
+ metadatas: {},
914
+ options: {},
915
+ settings: DEFAULT_SETTINGS
799
916
  },
800
- [validationSchema]
917
+ [data, isLoading, schemas, schema, components]
918
+ );
919
+ const listLayout = React.useMemo(() => {
920
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
921
+ layout: [],
922
+ metadatas: {},
923
+ options: {},
924
+ settings: DEFAULT_SETTINGS
925
+ };
926
+ }, [data, isLoading, schemas, schema, components]);
927
+ const { layout: edit } = React.useMemo(
928
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
929
+ layout: editLayout,
930
+ query
931
+ }),
932
+ [editLayout, query, runHookWaterfall]
801
933
  );
802
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
803
934
  return {
804
- components,
805
- document: data?.data,
806
- meta: data?.meta,
935
+ error,
807
936
  isLoading,
808
- schema,
809
- validate
937
+ edit,
938
+ list: listLayout
810
939
  };
811
940
  };
812
- const useDoc = () => {
813
- const { id, slug, collectionType, origin } = useParams();
814
- const [{ query }] = useQueryParams();
815
- const params = React.useMemo(() => buildValidParams(query), [query]);
816
- if (!collectionType) {
817
- throw new Error("Could not find collectionType in url params");
818
- }
819
- if (!slug) {
820
- throw new Error("Could not find model in url params");
821
- }
822
- return {
823
- collectionType,
824
- model: slug,
825
- id: origin || id === "create" ? void 0 : id,
941
+ const useDocLayout = () => {
942
+ const { model } = useDoc();
943
+ return useDocumentLayout(model);
944
+ };
945
+ const formatEditLayout = (data, {
946
+ schemas,
947
+ schema,
948
+ components
949
+ }) => {
950
+ let currentPanelIndex = 0;
951
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
952
+ data.contentType.layouts.edit,
953
+ schema?.attributes,
954
+ data.contentType.metadatas,
955
+ { configurations: data.components, schemas: components },
956
+ schemas
957
+ ).reduce((panels, row) => {
958
+ if (row.some((field) => field.type === "dynamiczone")) {
959
+ panels.push([row]);
960
+ currentPanelIndex += 2;
961
+ } else {
962
+ if (!panels[currentPanelIndex]) {
963
+ panels.push([]);
964
+ }
965
+ panels[currentPanelIndex].push(row);
966
+ }
967
+ return panels;
968
+ }, []);
969
+ const componentEditAttributes = Object.entries(data.components).reduce(
970
+ (acc, [uid, configuration]) => {
971
+ acc[uid] = {
972
+ layout: convertEditLayoutToFieldLayouts(
973
+ configuration.layouts.edit,
974
+ components[uid].attributes,
975
+ configuration.metadatas,
976
+ { configurations: data.components, schemas: components }
977
+ ),
978
+ settings: {
979
+ ...configuration.settings,
980
+ icon: components[uid].info.icon,
981
+ displayName: components[uid].info.displayName
982
+ }
983
+ };
984
+ return acc;
985
+ },
986
+ {}
987
+ );
988
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
989
+ (acc, [attribute, metadata]) => {
990
+ return {
991
+ ...acc,
992
+ [attribute]: metadata.edit
993
+ };
994
+ },
995
+ {}
996
+ );
997
+ return {
998
+ layout: panelledEditAttributes,
999
+ components: componentEditAttributes,
1000
+ metadatas: editMetadatas,
1001
+ settings: {
1002
+ ...data.contentType.settings,
1003
+ displayName: schema?.info.displayName
1004
+ },
1005
+ options: {
1006
+ ...schema?.options,
1007
+ ...schema?.pluginOptions,
1008
+ ...data.contentType.options
1009
+ }
1010
+ };
1011
+ };
1012
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1013
+ return rows.map(
1014
+ (row) => row.map((field) => {
1015
+ const attribute = attributes[field.name];
1016
+ if (!attribute) {
1017
+ return null;
1018
+ }
1019
+ const { edit: metadata } = metadatas[field.name];
1020
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1021
+ return {
1022
+ attribute,
1023
+ disabled: !metadata.editable,
1024
+ hint: metadata.description,
1025
+ label: metadata.label ?? "",
1026
+ name: field.name,
1027
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1028
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1029
+ schemas,
1030
+ components: components?.schemas ?? {}
1031
+ }),
1032
+ placeholder: metadata.placeholder ?? "",
1033
+ required: attribute.required ?? false,
1034
+ size: field.size,
1035
+ unique: "unique" in attribute ? attribute.unique : false,
1036
+ visible: metadata.visible ?? true,
1037
+ type: attribute.type
1038
+ };
1039
+ }).filter((field) => field !== null)
1040
+ );
1041
+ };
1042
+ const formatListLayout = (data, {
1043
+ schemas,
1044
+ schema,
1045
+ components
1046
+ }) => {
1047
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1048
+ (acc, [attribute, metadata]) => {
1049
+ return {
1050
+ ...acc,
1051
+ [attribute]: metadata.list
1052
+ };
1053
+ },
1054
+ {}
1055
+ );
1056
+ const listAttributes = convertListLayoutToFieldLayouts(
1057
+ data.contentType.layouts.list,
1058
+ schema?.attributes,
1059
+ listMetadatas,
1060
+ { configurations: data.components, schemas: components },
1061
+ schemas
1062
+ );
1063
+ return {
1064
+ layout: listAttributes,
1065
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1066
+ metadatas: listMetadatas,
1067
+ options: {
1068
+ ...schema?.options,
1069
+ ...schema?.pluginOptions,
1070
+ ...data.contentType.options
1071
+ }
1072
+ };
1073
+ };
1074
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1075
+ return columns.map((name) => {
1076
+ const attribute = attributes[name];
1077
+ if (!attribute) {
1078
+ return null;
1079
+ }
1080
+ const metadata = metadatas[name];
1081
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1082
+ return {
1083
+ attribute,
1084
+ label: metadata.label ?? "",
1085
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1086
+ schemas,
1087
+ components: components?.schemas ?? {}
1088
+ }),
1089
+ name,
1090
+ searchable: metadata.searchable ?? true,
1091
+ sortable: metadata.sortable ?? true
1092
+ };
1093
+ }).filter((field) => field !== null);
1094
+ };
1095
+ const useDocument = (args, opts) => {
1096
+ const { toggleNotification } = useNotification();
1097
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1098
+ const {
1099
+ currentData: data,
1100
+ isLoading: isLoadingDocument,
1101
+ isFetching: isFetchingDocument,
1102
+ error
1103
+ } = useGetDocumentQuery(args, {
1104
+ ...opts,
1105
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1106
+ });
1107
+ const {
1108
+ components,
1109
+ schema,
1110
+ schemas,
1111
+ isLoading: isLoadingSchema
1112
+ } = useContentTypeSchema(args.model);
1113
+ React.useEffect(() => {
1114
+ if (error) {
1115
+ toggleNotification({
1116
+ type: "danger",
1117
+ message: formatAPIError(error)
1118
+ });
1119
+ }
1120
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1121
+ const validationSchema = React.useMemo(() => {
1122
+ if (!schema) {
1123
+ return null;
1124
+ }
1125
+ return createYupSchema(schema.attributes, components);
1126
+ }, [schema, components]);
1127
+ const validate = React.useCallback(
1128
+ (document) => {
1129
+ if (!validationSchema) {
1130
+ throw new Error(
1131
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1132
+ );
1133
+ }
1134
+ try {
1135
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1136
+ return null;
1137
+ } catch (error2) {
1138
+ if (error2 instanceof ValidationError) {
1139
+ return getYupValidationErrors(error2);
1140
+ }
1141
+ throw error2;
1142
+ }
1143
+ },
1144
+ [validationSchema]
1145
+ );
1146
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1147
+ return {
1148
+ components,
1149
+ document: data?.data,
1150
+ meta: data?.meta,
1151
+ isLoading,
1152
+ schema,
1153
+ schemas,
1154
+ validate
1155
+ };
1156
+ };
1157
+ const useDoc = () => {
1158
+ const { id, slug, collectionType, origin } = useParams();
1159
+ const [{ query }] = useQueryParams();
1160
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1161
+ if (!collectionType) {
1162
+ throw new Error("Could not find collectionType in url params");
1163
+ }
1164
+ if (!slug) {
1165
+ throw new Error("Could not find model in url params");
1166
+ }
1167
+ return {
1168
+ collectionType,
1169
+ model: slug,
1170
+ id: origin || id === "create" ? void 0 : id,
826
1171
  ...useDocument(
827
1172
  { documentId: origin || id, model: slug, collectionType, params },
828
1173
  {
@@ -831,6 +1176,45 @@ const useDoc = () => {
831
1176
  )
832
1177
  };
833
1178
  };
1179
+ const useContentManagerContext = () => {
1180
+ const {
1181
+ collectionType,
1182
+ model,
1183
+ id,
1184
+ components,
1185
+ isLoading: isLoadingDoc,
1186
+ schema,
1187
+ schemas
1188
+ } = useDoc();
1189
+ const layout = useDocumentLayout(model);
1190
+ const form = useForm("useContentManagerContext", (state) => state);
1191
+ const isSingleType = collectionType === SINGLE_TYPES;
1192
+ const slug = model;
1193
+ const isCreatingEntry = id === "create";
1194
+ useContentTypeSchema();
1195
+ const isLoading = isLoadingDoc || layout.isLoading;
1196
+ const error = layout.error;
1197
+ return {
1198
+ error,
1199
+ isLoading,
1200
+ // Base metadata
1201
+ model,
1202
+ collectionType,
1203
+ id,
1204
+ slug,
1205
+ isCreatingEntry,
1206
+ isSingleType,
1207
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1208
+ // All schema infos
1209
+ components,
1210
+ contentType: schema,
1211
+ contentTypes: schemas,
1212
+ // Form state
1213
+ form,
1214
+ // layout infos
1215
+ layout
1216
+ };
1217
+ };
834
1218
  const prefixPluginTranslations = (trad, pluginId) => {
835
1219
  if (!pluginId) {
836
1220
  throw new TypeError("pluginId can't be empty");
@@ -850,6 +1234,8 @@ const useDocumentActions = () => {
850
1234
  const { formatMessage } = useIntl();
851
1235
  const { trackUsage } = useTracking();
852
1236
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1237
+ const navigate = useNavigate();
1238
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
853
1239
  const [deleteDocument] = useDeleteDocumentMutation();
854
1240
  const _delete = React.useCallback(
855
1241
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1164,6 +1550,7 @@ const useDocumentActions = () => {
1164
1550
  defaultMessage: "Saved document"
1165
1551
  })
1166
1552
  });
1553
+ setCurrentStep("contentManager.success");
1167
1554
  return res.data;
1168
1555
  } catch (err) {
1169
1556
  toggleNotification({
@@ -1185,7 +1572,6 @@ const useDocumentActions = () => {
1185
1572
  sourceId
1186
1573
  });
1187
1574
  if ("error" in res) {
1188
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1189
1575
  return { error: res.error };
1190
1576
  }
1191
1577
  toggleNotification({
@@ -1204,7 +1590,7 @@ const useDocumentActions = () => {
1204
1590
  throw err;
1205
1591
  }
1206
1592
  },
1207
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1593
+ [autoCloneDocument, formatMessage, toggleNotification]
1208
1594
  );
1209
1595
  const [cloneDocument] = useCloneDocumentMutation();
1210
1596
  const clone = React.useCallback(
@@ -1230,6 +1616,7 @@ const useDocumentActions = () => {
1230
1616
  defaultMessage: "Cloned document"
1231
1617
  })
1232
1618
  });
1619
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1233
1620
  return res.data;
1234
1621
  } catch (err) {
1235
1622
  toggleNotification({
@@ -1240,7 +1627,7 @@ const useDocumentActions = () => {
1240
1627
  throw err;
1241
1628
  }
1242
1629
  },
1243
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1630
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1244
1631
  );
1245
1632
  const [getDoc] = useLazyGetDocumentQuery();
1246
1633
  const getDocument = React.useCallback(
@@ -1266,7 +1653,7 @@ const useDocumentActions = () => {
1266
1653
  };
1267
1654
  };
1268
1655
  const ProtectedHistoryPage = lazy(
1269
- () => import("./History-Bru_KoeP.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1656
+ () => import("./History-BDZrgfZ3.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1270
1657
  );
1271
1658
  const routes$1 = [
1272
1659
  {
@@ -1279,31 +1666,31 @@ const routes$1 = [
1279
1666
  }
1280
1667
  ];
1281
1668
  const ProtectedEditViewPage = lazy(
1282
- () => import("./EditViewPage-CPj61RMh.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1669
+ () => import("./EditViewPage-HYljoEY7.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1283
1670
  );
1284
1671
  const ProtectedListViewPage = lazy(
1285
- () => import("./ListViewPage-SID6TRb9.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1672
+ () => import("./ListViewPage-CsX7tWx-.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1286
1673
  );
1287
1674
  const ProtectedListConfiguration = lazy(
1288
- () => import("./ListConfigurationPage-D8wGABj0.mjs").then((mod) => ({
1675
+ () => import("./ListConfigurationPage-BZ3ScUna.mjs").then((mod) => ({
1289
1676
  default: mod.ProtectedListConfiguration
1290
1677
  }))
1291
1678
  );
1292
1679
  const ProtectedEditConfigurationPage = lazy(
1293
- () => import("./EditConfigurationPage-JT3E7NZy.mjs").then((mod) => ({
1680
+ () => import("./EditConfigurationPage-CZofxSLy.mjs").then((mod) => ({
1294
1681
  default: mod.ProtectedEditConfigurationPage
1295
1682
  }))
1296
1683
  );
1297
1684
  const ProtectedComponentConfigurationPage = lazy(
1298
- () => import("./ComponentConfigurationPage-DmwmiFQy.mjs").then((mod) => ({
1685
+ () => import("./ComponentConfigurationPage-DJ5voqEK.mjs").then((mod) => ({
1299
1686
  default: mod.ProtectedComponentConfigurationPage
1300
1687
  }))
1301
1688
  );
1302
1689
  const NoPermissions = lazy(
1303
- () => import("./NoPermissionsPage-B7syEq5E.mjs").then((mod) => ({ default: mod.NoPermissions }))
1690
+ () => import("./NoPermissionsPage-S4Re3FwO.mjs").then((mod) => ({ default: mod.NoPermissions }))
1304
1691
  );
1305
1692
  const NoContentType = lazy(
1306
- () => import("./NoContentTypePage-CJ7UXwrQ.mjs").then((mod) => ({ default: mod.NoContentType }))
1693
+ () => import("./NoContentTypePage-LClTUPWs.mjs").then((mod) => ({ default: mod.NoContentType }))
1307
1694
  );
1308
1695
  const CollectionTypePages = () => {
1309
1696
  const { collectionType } = useParams();
@@ -1417,12 +1804,14 @@ const DocumentActionButton = (action) => {
1417
1804
  /* @__PURE__ */ jsx(
1418
1805
  Button,
1419
1806
  {
1420
- flex: 1,
1807
+ flex: "auto",
1421
1808
  startIcon: action.icon,
1422
1809
  disabled: action.disabled,
1423
1810
  onClick: handleClick(action),
1424
1811
  justifyContent: "center",
1425
1812
  variant: action.variant || "default",
1813
+ paddingTop: "7px",
1814
+ paddingBottom: "7px",
1426
1815
  children: action.label
1427
1816
  }
1428
1817
  ),
@@ -1430,7 +1819,7 @@ const DocumentActionButton = (action) => {
1430
1819
  DocumentActionConfirmDialog,
1431
1820
  {
1432
1821
  ...action.dialog,
1433
- variant: action.variant,
1822
+ variant: action.dialog?.variant ?? action.variant,
1434
1823
  isOpen: dialogId === action.id,
1435
1824
  onClose: handleClose
1436
1825
  }
@@ -1487,9 +1876,9 @@ const DocumentActionsMenu = ({
1487
1876
  disabled: isDisabled,
1488
1877
  size: "S",
1489
1878
  endIcon: null,
1490
- paddingTop: "7px",
1491
- paddingLeft: "9px",
1492
- paddingRight: "9px",
1879
+ paddingTop: "4px",
1880
+ paddingLeft: "7px",
1881
+ paddingRight: "7px",
1493
1882
  variant,
1494
1883
  children: [
1495
1884
  /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
@@ -1500,7 +1889,7 @@ const DocumentActionsMenu = ({
1500
1889
  ]
1501
1890
  }
1502
1891
  ),
1503
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1892
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1504
1893
  actions2.map((action) => {
1505
1894
  return /* @__PURE__ */ jsx(
1506
1895
  Menu.Item,
@@ -1509,10 +1898,25 @@ const DocumentActionsMenu = ({
1509
1898
  onSelect: handleClick(action),
1510
1899
  display: "block",
1511
1900
  children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1512
- /* @__PURE__ */ jsxs(Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1513
- /* @__PURE__ */ jsx(Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1514
- action.label
1515
- ] }),
1901
+ /* @__PURE__ */ jsxs(
1902
+ Flex,
1903
+ {
1904
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1905
+ gap: 2,
1906
+ tag: "span",
1907
+ children: [
1908
+ /* @__PURE__ */ jsx(
1909
+ Flex,
1910
+ {
1911
+ tag: "span",
1912
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1913
+ children: action.icon
1914
+ }
1915
+ ),
1916
+ action.label
1917
+ ]
1918
+ }
1919
+ ),
1516
1920
  action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1517
1921
  Flex,
1518
1922
  {
@@ -1609,11 +2013,11 @@ const DocumentActionConfirmDialog = ({
1609
2013
  /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1610
2014
  /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1611
2015
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1612
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
2016
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1613
2017
  id: "app.components.Button.cancel",
1614
2018
  defaultMessage: "Cancel"
1615
2019
  }) }) }),
1616
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
2020
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1617
2021
  id: "app.components.Button.confirm",
1618
2022
  defaultMessage: "Confirm"
1619
2023
  }) })
@@ -1652,13 +2056,17 @@ const PublishAction$1 = ({
1652
2056
  const navigate = useNavigate();
1653
2057
  const { toggleNotification } = useNotification();
1654
2058
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2059
+ const isListView = useMatch(LIST_PATH) !== null;
1655
2060
  const isCloning = useMatch(CLONE_PATH) !== null;
1656
2061
  const { formatMessage } = useIntl();
1657
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1658
- "PublishAction",
1659
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1660
- );
2062
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1661
2063
  const { publish } = useDocumentActions();
2064
+ const [
2065
+ countDraftRelations,
2066
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2067
+ ] = useLazyGetDraftRelationCountQuery();
2068
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React.useState(0);
2069
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React.useState(0);
1662
2070
  const [{ query, rawQuery }] = useQueryParams();
1663
2071
  const params = React.useMemo(() => buildValidParams(query), [query]);
1664
2072
  const modified = useForm("PublishAction", ({ modified: modified2 }) => modified2);
@@ -1667,62 +2075,142 @@ const PublishAction$1 = ({
1667
2075
  const validate = useForm("PublishAction", (state) => state.validate);
1668
2076
  const setErrors = useForm("PublishAction", (state) => state.setErrors);
1669
2077
  const formValues = useForm("PublishAction", ({ values }) => values);
1670
- const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1671
- if (!schema?.options?.draftAndPublish) {
1672
- return null;
1673
- }
1674
- return {
1675
- /**
1676
- * Disabled when:
1677
- * - currently if you're cloning a document we don't support publish & clone at the same time.
1678
- * - the form is submitting
1679
- * - the active tab is the published tab
1680
- * - the document is already published & not modified
1681
- * - the document is being created & not modified
2078
+ React.useEffect(() => {
2079
+ if (isErrorDraftRelations) {
2080
+ toggleNotification({
2081
+ type: "danger",
2082
+ message: formatMessage({
2083
+ id: getTranslation("error.records.fetch-draft-relatons"),
2084
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2085
+ })
2086
+ });
2087
+ }
2088
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2089
+ React.useEffect(() => {
2090
+ const localDraftRelations = /* @__PURE__ */ new Set();
2091
+ const extractDraftRelations = (data) => {
2092
+ const relations = data.connect || [];
2093
+ relations.forEach((relation) => {
2094
+ if (relation.status === "draft") {
2095
+ localDraftRelations.add(relation.id);
2096
+ }
2097
+ });
2098
+ };
2099
+ const traverseAndExtract = (data) => {
2100
+ Object.entries(data).forEach(([key, value]) => {
2101
+ if (key === "connect" && Array.isArray(value)) {
2102
+ extractDraftRelations({ connect: value });
2103
+ } else if (typeof value === "object" && value !== null) {
2104
+ traverseAndExtract(value);
2105
+ }
2106
+ });
2107
+ };
2108
+ if (!documentId || modified) {
2109
+ traverseAndExtract(formValues);
2110
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2111
+ }
2112
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2113
+ React.useEffect(() => {
2114
+ if (!document || !document.documentId || isListView) {
2115
+ return;
2116
+ }
2117
+ const fetchDraftRelationsCount = async () => {
2118
+ const { data, error } = await countDraftRelations({
2119
+ collectionType,
2120
+ model,
2121
+ documentId,
2122
+ params
2123
+ });
2124
+ if (error) {
2125
+ throw error;
2126
+ }
2127
+ if (data) {
2128
+ setServerCountOfDraftRelations(data.data);
2129
+ }
2130
+ };
2131
+ fetchDraftRelationsCount();
2132
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
2133
+ const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
2134
+ if (!schema?.options?.draftAndPublish) {
2135
+ return null;
2136
+ }
2137
+ const performPublish = async () => {
2138
+ setSubmitting(true);
2139
+ try {
2140
+ const { errors } = await validate();
2141
+ if (errors) {
2142
+ toggleNotification({
2143
+ type: "danger",
2144
+ message: formatMessage({
2145
+ id: "content-manager.validation.error",
2146
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2147
+ })
2148
+ });
2149
+ return;
2150
+ }
2151
+ const res = await publish(
2152
+ {
2153
+ collectionType,
2154
+ model,
2155
+ documentId,
2156
+ params
2157
+ },
2158
+ formValues
2159
+ );
2160
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2161
+ navigate({
2162
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2163
+ search: rawQuery
2164
+ });
2165
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2166
+ setErrors(formatValidationErrors(res.error));
2167
+ }
2168
+ } finally {
2169
+ setSubmitting(false);
2170
+ }
2171
+ };
2172
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2173
+ const enableDraftRelationsCount = false;
2174
+ const hasDraftRelations = enableDraftRelationsCount;
2175
+ return {
2176
+ /**
2177
+ * Disabled when:
2178
+ * - currently if you're cloning a document we don't support publish & clone at the same time.
2179
+ * - the form is submitting
2180
+ * - the active tab is the published tab
2181
+ * - the document is already published & not modified
2182
+ * - the document is being created & not modified
1682
2183
  * - the user doesn't have the permission to publish
1683
- * - the user doesn't have the permission to create a new document
1684
- * - the user doesn't have the permission to update the document
1685
2184
  */
1686
- disabled: isCloning || isSubmitting || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2185
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1687
2186
  label: formatMessage({
1688
2187
  id: "app.utils.publish",
1689
2188
  defaultMessage: "Publish"
1690
2189
  }),
1691
2190
  onClick: async () => {
1692
- setSubmitting(true);
1693
- try {
1694
- const { errors } = await validate();
1695
- if (errors) {
1696
- toggleNotification({
1697
- type: "danger",
1698
- message: formatMessage({
1699
- id: "content-manager.validation.error",
1700
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1701
- })
1702
- });
1703
- return;
1704
- }
1705
- const res = await publish(
1706
- {
1707
- collectionType,
1708
- model,
1709
- documentId,
1710
- params
1711
- },
1712
- formValues
1713
- );
1714
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1715
- navigate({
1716
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1717
- search: rawQuery
1718
- });
1719
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1720
- setErrors(formatValidationErrors(res.error));
2191
+ await performPublish();
2192
+ },
2193
+ dialog: hasDraftRelations ? {
2194
+ type: "dialog",
2195
+ variant: "danger",
2196
+ footer: null,
2197
+ title: formatMessage({
2198
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2199
+ defaultMessage: "Confirmation"
2200
+ }),
2201
+ content: formatMessage(
2202
+ {
2203
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2204
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2205
+ },
2206
+ {
2207
+ count: totalDraftRelations
1721
2208
  }
1722
- } finally {
1723
- setSubmitting(false);
2209
+ ),
2210
+ onConfirm: async () => {
2211
+ await performPublish();
1724
2212
  }
1725
- }
2213
+ } : void 0
1726
2214
  };
1727
2215
  };
1728
2216
  PublishAction$1.type = "publish";
@@ -1738,10 +2226,6 @@ const UpdateAction = ({
1738
2226
  const cloneMatch = useMatch(CLONE_PATH);
1739
2227
  const isCloning = cloneMatch !== null;
1740
2228
  const { formatMessage } = useIntl();
1741
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1742
- canCreate: canCreate2,
1743
- canUpdate: canUpdate2
1744
- }));
1745
2229
  const { create, update, clone } = useDocumentActions();
1746
2230
  const [{ query, rawQuery }] = useQueryParams();
1747
2231
  const params = React.useMemo(() => buildValidParams(query), [query]);
@@ -1758,10 +2242,8 @@ const UpdateAction = ({
1758
2242
  * - the form is submitting
1759
2243
  * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1760
2244
  * - the active tab is the published tab
1761
- * - the user doesn't have the permission to create a new document
1762
- * - the user doesn't have the permission to update the document
1763
2245
  */
1764
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
2246
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1765
2247
  label: formatMessage({
1766
2248
  id: "content-manager.containers.Edit.save",
1767
2249
  defaultMessage: "Save"
@@ -1769,16 +2251,18 @@ const UpdateAction = ({
1769
2251
  onClick: async () => {
1770
2252
  setSubmitting(true);
1771
2253
  try {
1772
- const { errors } = await validate();
1773
- if (errors) {
1774
- toggleNotification({
1775
- type: "danger",
1776
- message: formatMessage({
1777
- id: "content-manager.validation.error",
1778
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1779
- })
1780
- });
1781
- return;
2254
+ if (activeTab !== "draft") {
2255
+ const { errors } = await validate();
2256
+ if (errors) {
2257
+ toggleNotification({
2258
+ type: "danger",
2259
+ message: formatMessage({
2260
+ id: "content-manager.validation.error",
2261
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2262
+ })
2263
+ });
2264
+ return;
2265
+ }
1782
2266
  }
1783
2267
  if (isCloning) {
1784
2268
  const res = await clone(
@@ -1790,10 +2274,13 @@ const UpdateAction = ({
1790
2274
  document
1791
2275
  );
1792
2276
  if ("data" in res) {
1793
- navigate({
1794
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1795
- search: rawQuery
1796
- });
2277
+ navigate(
2278
+ {
2279
+ pathname: `../${res.data.documentId}`,
2280
+ search: rawQuery
2281
+ },
2282
+ { relative: "path" }
2283
+ );
1797
2284
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1798
2285
  setErrors(formatValidationErrors(res.error));
1799
2286
  }
@@ -1821,10 +2308,13 @@ const UpdateAction = ({
1821
2308
  document
1822
2309
  );
1823
2310
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1824
- navigate({
1825
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1826
- search: rawQuery
1827
- });
2311
+ navigate(
2312
+ {
2313
+ pathname: `../${res.data.documentId}`,
2314
+ search: rawQuery
2315
+ },
2316
+ { replace: true, relative: "path" }
2317
+ );
1828
2318
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1829
2319
  setErrors(formatValidationErrors(res.error));
1830
2320
  }
@@ -1868,7 +2358,7 @@ const UnpublishAction$1 = ({
1868
2358
  id: "app.utils.unpublish",
1869
2359
  defaultMessage: "Unpublish"
1870
2360
  }),
1871
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2361
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1872
2362
  onClick: async () => {
1873
2363
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1874
2364
  if (!documentId) {
@@ -1980,7 +2470,7 @@ const DiscardAction = ({
1980
2470
  id: "content-manager.actions.discard.label",
1981
2471
  defaultMessage: "Discard changes"
1982
2472
  }),
1983
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2473
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1984
2474
  position: ["panel", "table-row"],
1985
2475
  variant: "danger",
1986
2476
  dialog: {
@@ -2008,11 +2498,6 @@ const DiscardAction = ({
2008
2498
  };
2009
2499
  };
2010
2500
  DiscardAction.type = "discard";
2011
- const StyledCrossCircle = styled(CrossCircle)`
2012
- path {
2013
- fill: currentColor;
2014
- }
2015
- `;
2016
2501
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2017
2502
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2018
2503
  const RelativeTime = React.forwardRef(
@@ -2060,7 +2545,7 @@ const getDisplayName = ({
2060
2545
  };
2061
2546
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2062
2547
  const DocumentStatus = ({ status = "draft", ...restProps }) => {
2063
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2548
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2064
2549
  return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2065
2550
  };
2066
2551
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
@@ -2070,23 +2555,13 @@ const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2070
2555
  id: "content-manager.containers.edit.title.new",
2071
2556
  defaultMessage: "Create an entry"
2072
2557
  }) : documentTitle;
2073
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 8, paddingBottom: 4, gap: 3, children: [
2558
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2074
2559
  /* @__PURE__ */ jsx(BackButton, {}),
2075
- /* @__PURE__ */ jsxs(
2076
- Flex,
2077
- {
2078
- width: "100%",
2079
- justifyContent: "space-between",
2080
- paddingTop: 1,
2081
- gap: "80px",
2082
- alignItems: "flex-start",
2083
- children: [
2084
- /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2085
- /* @__PURE__ */ jsx(HeaderToolbar, {})
2086
- ]
2087
- }
2088
- ),
2089
- status ? /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) : null
2560
+ /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2561
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2562
+ /* @__PURE__ */ jsx(HeaderToolbar, {})
2563
+ ] }),
2564
+ status ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2090
2565
  ] });
2091
2566
  };
2092
2567
  const HeaderToolbar = () => {
@@ -2253,8 +2728,22 @@ const Information = ({ activeTab }) => {
2253
2728
  );
2254
2729
  };
2255
2730
  const HeaderActions = ({ actions: actions2 }) => {
2256
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2257
- if ("options" in action) {
2731
+ const [dialogId, setDialogId] = React.useState(null);
2732
+ const handleClick = (action) => async (e) => {
2733
+ if (!("options" in action)) {
2734
+ const { onClick = () => false, dialog, id } = action;
2735
+ const muteDialog = await onClick(e);
2736
+ if (dialog && !muteDialog) {
2737
+ e.preventDefault();
2738
+ setDialogId(id);
2739
+ }
2740
+ }
2741
+ };
2742
+ const handleClose = () => {
2743
+ setDialogId(null);
2744
+ };
2745
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2746
+ if (action.options) {
2258
2747
  return /* @__PURE__ */ jsx(
2259
2748
  SingleSelect,
2260
2749
  {
@@ -2268,10 +2757,49 @@ const HeaderActions = ({ actions: actions2 }) => {
2268
2757
  action.id
2269
2758
  );
2270
2759
  } else {
2271
- return null;
2760
+ if (action.type === "icon") {
2761
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
2762
+ /* @__PURE__ */ jsx(
2763
+ IconButton,
2764
+ {
2765
+ disabled: action.disabled,
2766
+ label: action.label,
2767
+ size: "S",
2768
+ onClick: handleClick(action),
2769
+ children: action.icon
2770
+ }
2771
+ ),
2772
+ action.dialog ? /* @__PURE__ */ jsx(
2773
+ HeaderActionDialog,
2774
+ {
2775
+ ...action.dialog,
2776
+ isOpen: dialogId === action.id,
2777
+ onClose: handleClose
2778
+ }
2779
+ ) : null
2780
+ ] }, action.id);
2781
+ }
2272
2782
  }
2273
2783
  }) });
2274
2784
  };
2785
+ const HeaderActionDialog = ({
2786
+ onClose,
2787
+ onCancel,
2788
+ title,
2789
+ content: Content,
2790
+ isOpen
2791
+ }) => {
2792
+ const handleClose = async () => {
2793
+ if (onCancel) {
2794
+ await onCancel();
2795
+ }
2796
+ onClose();
2797
+ };
2798
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2799
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2800
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
2801
+ ] }) });
2802
+ };
2275
2803
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2276
2804
  const navigate = useNavigate();
2277
2805
  const { formatMessage } = useIntl();
@@ -2312,12 +2840,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2312
2840
  const { delete: deleteAction } = useDocumentActions();
2313
2841
  const { toggleNotification } = useNotification();
2314
2842
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2843
+ const isLocalized = document?.locale != null;
2315
2844
  return {
2316
2845
  disabled: !canDelete || !document,
2317
- label: formatMessage({
2318
- id: "content-manager.actions.delete.label",
2319
- defaultMessage: "Delete document"
2320
- }),
2846
+ label: formatMessage(
2847
+ {
2848
+ id: "content-manager.actions.delete.label",
2849
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2850
+ },
2851
+ { isLocalized }
2852
+ ),
2321
2853
  icon: /* @__PURE__ */ jsx(Trash, {}),
2322
2854
  dialog: {
2323
2855
  type: "dialog",
@@ -2351,425 +2883,123 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2351
2883
  return;
2352
2884
  }
2353
2885
  const res = await deleteAction({
2354
- documentId,
2355
- model,
2356
- collectionType,
2357
- params: {
2358
- locale: "*"
2359
- }
2360
- });
2361
- if (!("error" in res)) {
2362
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2363
- }
2364
- } finally {
2365
- if (!listViewPathMatch) {
2366
- setSubmitting(false);
2367
- }
2368
- }
2369
- }
2370
- },
2371
- variant: "danger",
2372
- position: ["header", "table-row"]
2373
- };
2374
- };
2375
- DeleteAction$1.type = "delete";
2376
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2377
- const Panels = () => {
2378
- const isCloning = useMatch(CLONE_PATH) !== null;
2379
- const [
2380
- {
2381
- query: { status }
2382
- }
2383
- ] = useQueryParams({
2384
- status: "draft"
2385
- });
2386
- const { model, id, document, meta, collectionType } = useDoc();
2387
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2388
- const props = {
2389
- activeTab: status,
2390
- model,
2391
- documentId: id,
2392
- document: isCloning ? void 0 : document,
2393
- meta: isCloning ? void 0 : meta,
2394
- collectionType
2395
- };
2396
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2397
- DescriptionComponentRenderer,
2398
- {
2399
- props,
2400
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2401
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2402
- }
2403
- ) });
2404
- };
2405
- const ActionsPanel = () => {
2406
- const { formatMessage } = useIntl();
2407
- return {
2408
- title: formatMessage({
2409
- id: "content-manager.containers.edit.panels.default.title",
2410
- defaultMessage: "Document"
2411
- }),
2412
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2413
- };
2414
- };
2415
- ActionsPanel.type = "actions";
2416
- const ActionsPanelContent = () => {
2417
- const isCloning = useMatch(CLONE_PATH) !== null;
2418
- const [
2419
- {
2420
- query: { status = "draft" }
2421
- }
2422
- ] = useQueryParams();
2423
- const { model, id, document, meta, collectionType } = useDoc();
2424
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2425
- const props = {
2426
- activeTab: status,
2427
- model,
2428
- documentId: id,
2429
- document: isCloning ? void 0 : document,
2430
- meta: isCloning ? void 0 : meta,
2431
- collectionType
2432
- };
2433
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2434
- /* @__PURE__ */ jsx(
2435
- DescriptionComponentRenderer,
2436
- {
2437
- props,
2438
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2439
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2440
- }
2441
- ),
2442
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2443
- ] });
2444
- };
2445
- const Panel = React.forwardRef(({ children, title }, ref) => {
2446
- return /* @__PURE__ */ jsxs(
2447
- Flex,
2448
- {
2449
- ref,
2450
- tag: "aside",
2451
- "aria-labelledby": "additional-information",
2452
- background: "neutral0",
2453
- borderColor: "neutral150",
2454
- hasRadius: true,
2455
- paddingBottom: 4,
2456
- paddingLeft: 4,
2457
- paddingRight: 4,
2458
- paddingTop: 4,
2459
- shadow: "tableShadow",
2460
- gap: 3,
2461
- direction: "column",
2462
- justifyContent: "stretch",
2463
- alignItems: "flex-start",
2464
- children: [
2465
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2466
- children
2467
- ]
2468
- }
2469
- );
2470
- });
2471
- const HOOKS = {
2472
- /**
2473
- * Hook that allows to mutate the displayed headers of the list view table
2474
- * @constant
2475
- * @type {string}
2476
- */
2477
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2478
- /**
2479
- * Hook that allows to mutate the CM's collection types links pre-set filters
2480
- * @constant
2481
- * @type {string}
2482
- */
2483
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2484
- /**
2485
- * Hook that allows to mutate the CM's edit view layout
2486
- * @constant
2487
- * @type {string}
2488
- */
2489
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2490
- /**
2491
- * Hook that allows to mutate the CM's single types links pre-set filters
2492
- * @constant
2493
- * @type {string}
2494
- */
2495
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2496
- };
2497
- const contentTypesApi = contentManagerApi.injectEndpoints({
2498
- endpoints: (builder) => ({
2499
- getContentTypeConfiguration: builder.query({
2500
- query: (uid) => ({
2501
- url: `/content-manager/content-types/${uid}/configuration`,
2502
- method: "GET"
2503
- }),
2504
- transformResponse: (response) => response.data,
2505
- providesTags: (_result, _error, uid) => [
2506
- { type: "ContentTypesConfiguration", id: uid },
2507
- { type: "ContentTypeSettings", id: "LIST" }
2508
- ]
2509
- }),
2510
- getAllContentTypeSettings: builder.query({
2511
- query: () => "/content-manager/content-types-settings",
2512
- transformResponse: (response) => response.data,
2513
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2514
- }),
2515
- updateContentTypeConfiguration: builder.mutation({
2516
- query: ({ uid, ...body }) => ({
2517
- url: `/content-manager/content-types/${uid}/configuration`,
2518
- method: "PUT",
2519
- data: body
2520
- }),
2521
- transformResponse: (response) => response.data,
2522
- invalidatesTags: (_result, _error, { uid }) => [
2523
- { type: "ContentTypesConfiguration", id: uid },
2524
- { type: "ContentTypeSettings", id: "LIST" },
2525
- // Is this necessary?
2526
- { type: "InitialData" }
2527
- ]
2528
- })
2529
- })
2530
- });
2531
- const {
2532
- useGetContentTypeConfigurationQuery,
2533
- useGetAllContentTypeSettingsQuery,
2534
- useUpdateContentTypeConfigurationMutation
2535
- } = contentTypesApi;
2536
- const checkIfAttributeIsDisplayable = (attribute) => {
2537
- const { type } = attribute;
2538
- if (type === "relation") {
2539
- return !attribute.relation.toLowerCase().includes("morph");
2540
- }
2541
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2542
- };
2543
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2544
- if (!mainFieldName) {
2545
- return void 0;
2546
- }
2547
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2548
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2549
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2550
- );
2551
- return {
2552
- name: mainFieldName,
2553
- type: mainFieldType ?? "string"
2554
- };
2555
- };
2556
- const DEFAULT_SETTINGS = {
2557
- bulkable: false,
2558
- filterable: false,
2559
- searchable: false,
2560
- pagination: false,
2561
- defaultSortBy: "",
2562
- defaultSortOrder: "asc",
2563
- mainField: "id",
2564
- pageSize: 10
2565
- };
2566
- const useDocumentLayout = (model) => {
2567
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2568
- const [{ query }] = useQueryParams();
2569
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2570
- const { toggleNotification } = useNotification();
2571
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2572
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2573
- const {
2574
- data,
2575
- isLoading: isLoadingConfigs,
2576
- error,
2577
- isFetching: isFetchingConfigs
2578
- } = useGetContentTypeConfigurationQuery(model);
2579
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2580
- React.useEffect(() => {
2581
- if (error) {
2582
- toggleNotification({
2583
- type: "danger",
2584
- message: formatAPIError(error)
2585
- });
2586
- }
2587
- }, [error, formatAPIError, toggleNotification]);
2588
- const editLayout = React.useMemo(
2589
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2590
- layout: [],
2591
- components: {},
2592
- metadatas: {},
2593
- options: {},
2594
- settings: DEFAULT_SETTINGS
2595
- },
2596
- [data, isLoading, schemas, schema, components]
2597
- );
2598
- const listLayout = React.useMemo(() => {
2599
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2600
- layout: [],
2601
- metadatas: {},
2602
- options: {},
2603
- settings: DEFAULT_SETTINGS
2604
- };
2605
- }, [data, isLoading, schemas, schema, components]);
2606
- const { layout: edit } = React.useMemo(
2607
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2608
- layout: editLayout,
2609
- query
2610
- }),
2611
- [editLayout, query, runHookWaterfall]
2612
- );
2613
- return {
2614
- error,
2615
- isLoading,
2616
- edit,
2617
- list: listLayout
2618
- };
2619
- };
2620
- const useDocLayout = () => {
2621
- const { model } = useDoc();
2622
- return useDocumentLayout(model);
2623
- };
2624
- const formatEditLayout = (data, {
2625
- schemas,
2626
- schema,
2627
- components
2628
- }) => {
2629
- let currentPanelIndex = 0;
2630
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2631
- data.contentType.layouts.edit,
2632
- schema?.attributes,
2633
- data.contentType.metadatas,
2634
- { configurations: data.components, schemas: components },
2635
- schemas
2636
- ).reduce((panels, row) => {
2637
- if (row.some((field) => field.type === "dynamiczone")) {
2638
- panels.push([row]);
2639
- currentPanelIndex += 2;
2640
- } else {
2641
- if (!panels[currentPanelIndex]) {
2642
- panels.push([]);
2643
- }
2644
- panels[currentPanelIndex].push(row);
2645
- }
2646
- return panels;
2647
- }, []);
2648
- const componentEditAttributes = Object.entries(data.components).reduce(
2649
- (acc, [uid, configuration]) => {
2650
- acc[uid] = {
2651
- layout: convertEditLayoutToFieldLayouts(
2652
- configuration.layouts.edit,
2653
- components[uid].attributes,
2654
- configuration.metadatas
2655
- ),
2656
- settings: {
2657
- ...configuration.settings,
2658
- icon: components[uid].info.icon,
2659
- displayName: components[uid].info.displayName
2886
+ documentId,
2887
+ model,
2888
+ collectionType,
2889
+ params: {
2890
+ locale: "*"
2891
+ }
2892
+ });
2893
+ if (!("error" in res)) {
2894
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2895
+ }
2896
+ } finally {
2897
+ if (!listViewPathMatch) {
2898
+ setSubmitting(false);
2899
+ }
2660
2900
  }
2661
- };
2662
- return acc;
2663
- },
2664
- {}
2665
- );
2666
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2667
- (acc, [attribute, metadata]) => {
2668
- return {
2669
- ...acc,
2670
- [attribute]: metadata.edit
2671
- };
2672
- },
2673
- {}
2674
- );
2675
- return {
2676
- layout: panelledEditAttributes,
2677
- components: componentEditAttributes,
2678
- metadatas: editMetadatas,
2679
- settings: {
2680
- ...data.contentType.settings,
2681
- displayName: schema?.info.displayName
2901
+ }
2682
2902
  },
2683
- options: {
2684
- ...schema?.options,
2685
- ...schema?.pluginOptions,
2686
- ...data.contentType.options
2687
- }
2903
+ variant: "danger",
2904
+ position: ["header", "table-row"]
2688
2905
  };
2689
2906
  };
2690
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2691
- return rows.map(
2692
- (row) => row.map((field) => {
2693
- const attribute = attributes[field.name];
2694
- if (!attribute) {
2695
- return null;
2696
- }
2697
- const { edit: metadata } = metadatas[field.name];
2698
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2699
- return {
2700
- attribute,
2701
- disabled: !metadata.editable,
2702
- hint: metadata.description,
2703
- label: metadata.label ?? "",
2704
- name: field.name,
2705
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2706
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2707
- schemas,
2708
- components: components?.schemas ?? {}
2709
- }),
2710
- placeholder: metadata.placeholder ?? "",
2711
- required: attribute.required ?? false,
2712
- size: field.size,
2713
- unique: "unique" in attribute ? attribute.unique : false,
2714
- visible: metadata.visible ?? true,
2715
- type: attribute.type
2716
- };
2717
- }).filter((field) => field !== null)
2718
- );
2907
+ DeleteAction$1.type = "delete";
2908
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2909
+ const Panels = () => {
2910
+ const isCloning = useMatch(CLONE_PATH) !== null;
2911
+ const [
2912
+ {
2913
+ query: { status }
2914
+ }
2915
+ ] = useQueryParams({
2916
+ status: "draft"
2917
+ });
2918
+ const { model, id, document, meta, collectionType } = useDoc();
2919
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
2920
+ const props = {
2921
+ activeTab: status,
2922
+ model,
2923
+ documentId: id,
2924
+ document: isCloning ? void 0 : document,
2925
+ meta: isCloning ? void 0 : meta,
2926
+ collectionType
2927
+ };
2928
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2929
+ DescriptionComponentRenderer,
2930
+ {
2931
+ props,
2932
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2933
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2934
+ }
2935
+ ) });
2719
2936
  };
2720
- const formatListLayout = (data, {
2721
- schemas,
2722
- schema,
2723
- components
2724
- }) => {
2725
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2726
- (acc, [attribute, metadata]) => {
2727
- return {
2728
- ...acc,
2729
- [attribute]: metadata.list
2730
- };
2731
- },
2732
- {}
2733
- );
2734
- const listAttributes = convertListLayoutToFieldLayouts(
2735
- data.contentType.layouts.list,
2736
- schema?.attributes,
2737
- listMetadatas,
2738
- { configurations: data.components, schemas: components },
2739
- schemas
2740
- );
2937
+ const ActionsPanel = () => {
2938
+ const { formatMessage } = useIntl();
2741
2939
  return {
2742
- layout: listAttributes,
2743
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2744
- metadatas: listMetadatas,
2745
- options: {
2746
- ...schema?.options,
2747
- ...schema?.pluginOptions,
2748
- ...data.contentType.options
2749
- }
2940
+ title: formatMessage({
2941
+ id: "content-manager.containers.edit.panels.default.title",
2942
+ defaultMessage: "Entry"
2943
+ }),
2944
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2750
2945
  };
2751
2946
  };
2752
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2753
- return columns.map((name) => {
2754
- const attribute = attributes[name];
2755
- if (!attribute) {
2756
- return null;
2947
+ ActionsPanel.type = "actions";
2948
+ const ActionsPanelContent = () => {
2949
+ const isCloning = useMatch(CLONE_PATH) !== null;
2950
+ const [
2951
+ {
2952
+ query: { status = "draft" }
2757
2953
  }
2758
- const metadata = metadatas[name];
2759
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2760
- return {
2761
- attribute,
2762
- label: metadata.label ?? "",
2763
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2764
- schemas,
2765
- components: components?.schemas ?? {}
2766
- }),
2767
- name,
2768
- searchable: metadata.searchable ?? true,
2769
- sortable: metadata.sortable ?? true
2770
- };
2771
- }).filter((field) => field !== null);
2954
+ ] = useQueryParams();
2955
+ const { model, id, document, meta, collectionType } = useDoc();
2956
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2957
+ const props = {
2958
+ activeTab: status,
2959
+ model,
2960
+ documentId: id,
2961
+ document: isCloning ? void 0 : document,
2962
+ meta: isCloning ? void 0 : meta,
2963
+ collectionType
2964
+ };
2965
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2966
+ /* @__PURE__ */ jsx(
2967
+ DescriptionComponentRenderer,
2968
+ {
2969
+ props,
2970
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
2971
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2972
+ }
2973
+ ),
2974
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2975
+ ] });
2772
2976
  };
2977
+ const Panel = React.forwardRef(({ children, title }, ref) => {
2978
+ return /* @__PURE__ */ jsxs(
2979
+ Flex,
2980
+ {
2981
+ ref,
2982
+ tag: "aside",
2983
+ "aria-labelledby": "additional-information",
2984
+ background: "neutral0",
2985
+ borderColor: "neutral150",
2986
+ hasRadius: true,
2987
+ paddingBottom: 4,
2988
+ paddingLeft: 4,
2989
+ paddingRight: 4,
2990
+ paddingTop: 4,
2991
+ shadow: "tableShadow",
2992
+ gap: 3,
2993
+ direction: "column",
2994
+ justifyContent: "stretch",
2995
+ alignItems: "flex-start",
2996
+ children: [
2997
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2998
+ children
2999
+ ]
3000
+ }
3001
+ );
3002
+ });
2773
3003
  const ConfirmBulkActionDialog = ({
2774
3004
  onToggleDialog,
2775
3005
  isOpen = false,
@@ -2777,7 +3007,7 @@ const ConfirmBulkActionDialog = ({
2777
3007
  endAction
2778
3008
  }) => {
2779
3009
  const { formatMessage } = useIntl();
2780
- return /* @__PURE__ */ jsx(Dialog.Root, { onOpenChange: onToggleDialog, open: isOpen, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
3010
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2781
3011
  /* @__PURE__ */ jsx(Dialog.Header, { children: formatMessage({
2782
3012
  id: "app.components.ConfirmDialog.title",
2783
3013
  defaultMessage: "Confirmation"
@@ -2808,6 +3038,7 @@ const ConfirmDialogPublishAll = ({
2808
3038
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2809
3039
  const { model, schema } = useDoc();
2810
3040
  const [{ query }] = useQueryParams();
3041
+ const enableDraftRelationsCount = false;
2811
3042
  const {
2812
3043
  data: countDraftRelations = 0,
2813
3044
  isLoading,
@@ -2819,7 +3050,7 @@ const ConfirmDialogPublishAll = ({
2819
3050
  locale: query?.plugins?.i18n?.locale
2820
3051
  },
2821
3052
  {
2822
- skip: selectedEntries.length === 0
3053
+ skip: !enableDraftRelationsCount
2823
3054
  }
2824
3055
  );
2825
3056
  React.useEffect(() => {
@@ -2898,7 +3129,14 @@ const formatErrorMessages = (errors, parentKey, formatMessage) => {
2898
3129
  )
2899
3130
  );
2900
3131
  } else {
2901
- messages.push(...formatErrorMessages(value, currentKey, formatMessage));
3132
+ messages.push(
3133
+ ...formatErrorMessages(
3134
+ // @ts-expect-error TODO: check why value is not compatible with FormErrors
3135
+ value,
3136
+ currentKey,
3137
+ formatMessage
3138
+ )
3139
+ );
2902
3140
  }
2903
3141
  } else {
2904
3142
  messages.push(
@@ -2997,7 +3235,7 @@ const SelectedEntriesTableContent = ({
2997
3235
  status: row.status
2998
3236
  }
2999
3237
  ) }),
3000
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3238
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3001
3239
  IconButton,
3002
3240
  {
3003
3241
  tag: Link,
@@ -3020,9 +3258,10 @@ const SelectedEntriesTableContent = ({
3020
3258
  ),
3021
3259
  target: "_blank",
3022
3260
  marginLeft: "auto",
3023
- children: /* @__PURE__ */ jsx(Pencil, {})
3261
+ variant: "ghost",
3262
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3024
3263
  }
3025
- ) })
3264
+ ) }) })
3026
3265
  ] }, row.id)) })
3027
3266
  ] });
3028
3267
  };
@@ -3059,7 +3298,13 @@ const SelectedEntriesModalContent = ({
3059
3298
  );
3060
3299
  const { rows, validationErrors } = React.useMemo(() => {
3061
3300
  if (data.length > 0 && schema) {
3062
- const validate = createYupSchema(schema.attributes, components);
3301
+ const validate = createYupSchema(
3302
+ schema.attributes,
3303
+ components,
3304
+ // Since this is the "Publish" action, the validation
3305
+ // schema must enforce the rules for published entities
3306
+ { status: "published" }
3307
+ );
3063
3308
  const validationErrors2 = {};
3064
3309
  const rows2 = data.map((entry) => {
3065
3310
  try {
@@ -3409,7 +3654,7 @@ const TableActions = ({ document }) => {
3409
3654
  DescriptionComponentRenderer,
3410
3655
  {
3411
3656
  props,
3412
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3657
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3413
3658
  children: (actions2) => {
3414
3659
  const tableRowActions = actions2.filter((action) => {
3415
3660
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3520,7 +3765,7 @@ const CloneAction = ({ model, documentId }) => {
3520
3765
  }),
3521
3766
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3522
3767
  footer: ({ onClose }) => {
3523
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3768
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3524
3769
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3525
3770
  id: "cancel",
3526
3771
  defaultMessage: "Cancel"
@@ -3561,8 +3806,7 @@ class ContentManagerPlugin {
3561
3806
  documentActions = [
3562
3807
  ...DEFAULT_ACTIONS,
3563
3808
  ...DEFAULT_TABLE_ROW_ACTIONS,
3564
- ...DEFAULT_HEADER_ACTIONS,
3565
- HistoryAction
3809
+ ...DEFAULT_HEADER_ACTIONS
3566
3810
  ];
3567
3811
  editViewSidePanels = [ActionsPanel];
3568
3812
  headerActions = [];
@@ -3651,6 +3895,52 @@ const getPrintableType = (value) => {
3651
3895
  }
3652
3896
  return nativeType;
3653
3897
  };
3898
+ const HistoryAction = ({ model, document }) => {
3899
+ const { formatMessage } = useIntl();
3900
+ const [{ query }] = useQueryParams();
3901
+ const navigate = useNavigate();
3902
+ const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
3903
+ if (!window.strapi.features.isEnabled("cms-content-history")) {
3904
+ return null;
3905
+ }
3906
+ return {
3907
+ icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
3908
+ label: formatMessage({
3909
+ id: "content-manager.history.document-action",
3910
+ defaultMessage: "Content History"
3911
+ }),
3912
+ onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
3913
+ disabled: (
3914
+ /**
3915
+ * The user is creating a new document.
3916
+ * It hasn't been saved yet, so there's no history to go to
3917
+ */
3918
+ !document || /**
3919
+ * The document has been created but the current dimension has never been saved.
3920
+ * For example, the user is creating a new locale in an existing document,
3921
+ * so there's no history for the document in that locale
3922
+ */
3923
+ !document.id || /**
3924
+ * History is only available for content types created by the user.
3925
+ * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
3926
+ * which start with `admin::` or `plugin::`
3927
+ */
3928
+ !model.startsWith("api::")
3929
+ ),
3930
+ position: "header"
3931
+ };
3932
+ };
3933
+ HistoryAction.type = "history";
3934
+ const historyAdmin = {
3935
+ bootstrap(app) {
3936
+ const { addDocumentAction } = app.getPlugin("content-manager").apis;
3937
+ addDocumentAction((actions2) => {
3938
+ const indexOfDeleteAction = actions2.findIndex((action) => action.type === "delete");
3939
+ actions2.splice(indexOfDeleteAction, 0, HistoryAction);
3940
+ return actions2;
3941
+ });
3942
+ }
3943
+ };
3654
3944
  const initialState = {
3655
3945
  collectionTypeLinks: [],
3656
3946
  components: [],
@@ -3706,7 +3996,7 @@ const index = {
3706
3996
  app.router.addRoute({
3707
3997
  path: "content-manager/*",
3708
3998
  lazy: async () => {
3709
- const { Layout } = await import("./layout-Bau7ZfLV.mjs");
3999
+ const { Layout } = await import("./layout-ClP-DC72.mjs");
3710
4000
  return {
3711
4001
  Component: Layout
3712
4002
  };
@@ -3715,10 +4005,15 @@ const index = {
3715
4005
  });
3716
4006
  app.registerPlugin(cm.config);
3717
4007
  },
4008
+ bootstrap(app) {
4009
+ if (typeof historyAdmin.bootstrap === "function") {
4010
+ historyAdmin.bootstrap(app);
4011
+ }
4012
+ },
3718
4013
  async registerTrads({ locales }) {
3719
4014
  const importedTrads = await Promise.all(
3720
4015
  locales.map((locale) => {
3721
- 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-Ux26r5pl.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
4016
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-C8YBvRrK.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
3722
4017
  return {
3723
4018
  data: prefixPluginTranslations(data, PLUGIN_ID),
3724
4019
  locale
@@ -3739,13 +4034,15 @@ export {
3739
4034
  BulkActionsRenderer as B,
3740
4035
  COLLECTION_TYPES as C,
3741
4036
  DocumentStatus as D,
3742
- DEFAULT_SETTINGS as E,
3743
- convertEditLayoutToFieldLayouts as F,
3744
- useDocument as G,
4037
+ extractContentTypeComponents as E,
4038
+ DEFAULT_SETTINGS as F,
4039
+ convertEditLayoutToFieldLayouts as G,
3745
4040
  HOOKS as H,
3746
4041
  InjectionZone as I,
3747
- index as J,
3748
- useDocumentActions as K,
4042
+ useDocument as J,
4043
+ index as K,
4044
+ useContentManagerContext as L,
4045
+ useDocumentActions as M,
3749
4046
  Panels as P,
3750
4047
  RelativeTime as R,
3751
4048
  SINGLE_TYPES as S,
@@ -3763,18 +4060,18 @@ export {
3763
4060
  PERMISSIONS as k,
3764
4061
  DocumentRBAC as l,
3765
4062
  DOCUMENT_META_FIELDS as m,
3766
- useDocLayout as n,
3767
- useGetContentTypeConfigurationQuery as o,
3768
- CREATOR_FIELDS as p,
3769
- getMainField as q,
3770
- getDisplayName as r,
4063
+ CLONE_PATH as n,
4064
+ useDocLayout as o,
4065
+ useGetContentTypeConfigurationQuery as p,
4066
+ CREATOR_FIELDS as q,
4067
+ getMainField as r,
3771
4068
  setInitialData as s,
3772
- checkIfAttributeIsDisplayable as t,
4069
+ getDisplayName as t,
3773
4070
  useContentTypeSchema as u,
3774
- useGetAllDocumentsQuery as v,
3775
- convertListLayoutToFieldLayouts as w,
3776
- capitalise as x,
3777
- useUpdateContentTypeConfigurationMutation as y,
3778
- extractContentTypeComponents as z
4071
+ checkIfAttributeIsDisplayable as v,
4072
+ useGetAllDocumentsQuery as w,
4073
+ convertListLayoutToFieldLayouts as x,
4074
+ capitalise as y,
4075
+ useUpdateContentTypeConfigurationMutation as z
3779
4076
  };
3780
- //# sourceMappingURL=index-DJXJw9V5.mjs.map
4077
+ //# sourceMappingURL=index-BmUAydCA.mjs.map