@strapi/content-manager 0.0.0-experimental.a9a5a36dd73072c19eadeff5f387e8286b2a4d22 → 0.0.0-experimental.ae69a6ec6a65b1061cd6a00b2608abeeff436042

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 (147) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-B1bIXVuX.mjs → ComponentConfigurationPage-DH3rgf1K.mjs} +4 -4
  2. package/dist/_chunks/{ComponentConfigurationPage-B1bIXVuX.mjs.map → ComponentConfigurationPage-DH3rgf1K.mjs.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-Bqgx7Mes.js → ComponentConfigurationPage-DRh2GoZx.js} +4 -4
  4. package/dist/_chunks/{ComponentConfigurationPage-Bqgx7Mes.js.map → ComponentConfigurationPage-DRh2GoZx.js.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-BFEwvdMW.js → EditConfigurationPage-BOAqRJUV.js} +4 -4
  6. package/dist/_chunks/{EditConfigurationPage-BFEwvdMW.js.map → EditConfigurationPage-BOAqRJUV.js.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-ZO0vOO8q.mjs → EditConfigurationPage-C1HHivAz.mjs} +4 -4
  8. package/dist/_chunks/{EditConfigurationPage-ZO0vOO8q.mjs.map → EditConfigurationPage-C1HHivAz.mjs.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-DA95Ha6J.js → EditViewPage-CgfL33yw.js} +30 -9
  10. package/dist/_chunks/EditViewPage-CgfL33yw.js.map +1 -0
  11. package/dist/_chunks/{EditViewPage-DlLEyUL6.mjs → EditViewPage-DBV7CZVf.mjs} +30 -9
  12. package/dist/_chunks/EditViewPage-DBV7CZVf.mjs.map +1 -0
  13. package/dist/_chunks/{Field-Dq7bDnuh.mjs → Field-BQFK_H87.mjs} +178 -106
  14. package/dist/_chunks/Field-BQFK_H87.mjs.map +1 -0
  15. package/dist/_chunks/{Field-CnK8dO8N.js → Field-C647fIuP.js} +180 -108
  16. package/dist/_chunks/Field-C647fIuP.js.map +1 -0
  17. package/dist/_chunks/{Form-B_JE0dbz.mjs → Form--31reETM.mjs} +36 -17
  18. package/dist/_chunks/Form--31reETM.mjs.map +1 -0
  19. package/dist/_chunks/{Form-BpiR4piS.js → Form-CsYUrVEA.js} +36 -17
  20. package/dist/_chunks/Form-CsYUrVEA.js.map +1 -0
  21. package/dist/_chunks/{History-CBNGU7a-.mjs → History-8tQaor_-.mjs} +42 -19
  22. package/dist/_chunks/History-8tQaor_-.mjs.map +1 -0
  23. package/dist/_chunks/{History-DdIstl8b.js → History-Dl6wOm0V.js} +41 -18
  24. package/dist/_chunks/History-Dl6wOm0V.js.map +1 -0
  25. package/dist/_chunks/{ListConfigurationPage-DkKRparB.js → ListConfigurationPage-BedUJnmo.js} +15 -5
  26. package/dist/_chunks/ListConfigurationPage-BedUJnmo.js.map +1 -0
  27. package/dist/_chunks/{ListConfigurationPage-5dr4qpue.mjs → ListConfigurationPage-_WWIGpVT.mjs} +15 -5
  28. package/dist/_chunks/ListConfigurationPage-_WWIGpVT.mjs.map +1 -0
  29. package/dist/_chunks/{ListViewPage-wE0lXqoD.js → ListViewPage-5QzoAtAo.js} +61 -41
  30. package/dist/_chunks/ListViewPage-5QzoAtAo.js.map +1 -0
  31. package/dist/_chunks/{ListViewPage-DecLrYV6.mjs → ListViewPage-CZfw9OW9.mjs} +59 -39
  32. package/dist/_chunks/ListViewPage-CZfw9OW9.mjs.map +1 -0
  33. package/dist/_chunks/{NoContentTypePage-CiIcfYsd.mjs → NoContentTypePage-D-RqshUI.mjs} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-CiIcfYsd.mjs.map → NoContentTypePage-D-RqshUI.mjs.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-DEKR6tf9.js → NoContentTypePage-DBIyA7hd.js} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-DEKR6tf9.js.map → NoContentTypePage-DBIyA7hd.js.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-DmNfF2Bb.js → NoPermissionsPage-CCAreseM.js} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-DmNfF2Bb.js.map → NoPermissionsPage-CCAreseM.js.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-CM5UD8ee.mjs → NoPermissionsPage-m7GAd26r.mjs} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-CM5UD8ee.mjs.map → NoPermissionsPage-m7GAd26r.mjs.map} +1 -1
  41. package/dist/_chunks/{Relations-Dqz0C1fz.mjs → Relations-Dq52hb_u.mjs} +70 -37
  42. package/dist/_chunks/Relations-Dq52hb_u.mjs.map +1 -0
  43. package/dist/_chunks/{Relations-L0xYRoSQ.js → Relations-SRwPhmo7.js} +69 -36
  44. package/dist/_chunks/Relations-SRwPhmo7.js.map +1 -0
  45. package/dist/_chunks/{en-uOUIxfcQ.js → en-Bm0D0IWz.js} +13 -12
  46. package/dist/_chunks/{en-uOUIxfcQ.js.map → en-Bm0D0IWz.js.map} +1 -1
  47. package/dist/_chunks/{en-BrCTWlZv.mjs → en-DKV44jRb.mjs} +13 -12
  48. package/dist/_chunks/{en-BrCTWlZv.mjs.map → en-DKV44jRb.mjs.map} +1 -1
  49. package/dist/_chunks/{index-BSn97i8U.mjs → index-1Bdj-6ls.mjs} +914 -710
  50. package/dist/_chunks/index-1Bdj-6ls.mjs.map +1 -0
  51. package/dist/_chunks/{index-DyvUPg1a.js → index-CsrCnNa3.js} +895 -690
  52. package/dist/_chunks/index-CsrCnNa3.js.map +1 -0
  53. package/dist/_chunks/{layout-TPqF2oJ5.js → layout-B33V9Tdu.js} +21 -8
  54. package/dist/_chunks/layout-B33V9Tdu.js.map +1 -0
  55. package/dist/_chunks/{layout-DPaHUusj.mjs → layout-ByD1cQSW.mjs} +22 -9
  56. package/dist/_chunks/layout-ByD1cQSW.mjs.map +1 -0
  57. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  58. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  59. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  60. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  61. package/dist/_chunks/{relations-BWYS9gkn.js → relations-BBHlo3qQ.js} +3 -7
  62. package/dist/_chunks/relations-BBHlo3qQ.js.map +1 -0
  63. package/dist/_chunks/{relations-Ck7-ecDT.mjs → relations-DFwbNLZ0.mjs} +3 -7
  64. package/dist/_chunks/relations-DFwbNLZ0.mjs.map +1 -0
  65. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  66. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  67. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  68. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  69. package/dist/admin/index.js +2 -1
  70. package/dist/admin/index.js.map +1 -1
  71. package/dist/admin/index.mjs +5 -4
  72. package/dist/admin/src/exports.d.ts +1 -1
  73. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  74. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  75. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  76. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  77. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  78. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  79. package/dist/admin/src/preview/constants.d.ts +1 -0
  80. package/dist/admin/src/preview/index.d.ts +4 -0
  81. package/dist/admin/src/services/api.d.ts +1 -1
  82. package/dist/admin/src/services/components.d.ts +2 -2
  83. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  84. package/dist/admin/src/services/documents.d.ts +19 -17
  85. package/dist/admin/src/services/init.d.ts +1 -1
  86. package/dist/admin/src/services/relations.d.ts +2 -2
  87. package/dist/admin/src/services/uid.d.ts +3 -3
  88. package/dist/admin/src/utils/validation.d.ts +4 -1
  89. package/dist/server/index.js +256 -134
  90. package/dist/server/index.js.map +1 -1
  91. package/dist/server/index.mjs +257 -135
  92. package/dist/server/index.mjs.map +1 -1
  93. package/dist/server/src/bootstrap.d.ts.map +1 -1
  94. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  95. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  96. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  97. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  98. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  99. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  100. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  101. package/dist/server/src/history/services/history.d.ts.map +1 -1
  102. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  103. package/dist/server/src/history/services/utils.d.ts +2 -1
  104. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  105. package/dist/server/src/index.d.ts +4 -4
  106. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  107. package/dist/server/src/preview/constants.d.ts +2 -0
  108. package/dist/server/src/preview/constants.d.ts.map +1 -0
  109. package/dist/server/src/preview/index.d.ts +4 -0
  110. package/dist/server/src/preview/index.d.ts.map +1 -0
  111. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  112. package/dist/server/src/services/document-metadata.d.ts +8 -8
  113. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  114. package/dist/server/src/services/index.d.ts +4 -4
  115. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  116. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  117. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  118. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  119. package/dist/server/src/utils/index.d.ts +2 -0
  120. package/dist/server/src/utils/index.d.ts.map +1 -1
  121. package/dist/shared/contracts/collection-types.d.ts +3 -1
  122. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  123. package/package.json +13 -13
  124. package/dist/_chunks/EditViewPage-DA95Ha6J.js.map +0 -1
  125. package/dist/_chunks/EditViewPage-DlLEyUL6.mjs.map +0 -1
  126. package/dist/_chunks/Field-CnK8dO8N.js.map +0 -1
  127. package/dist/_chunks/Field-Dq7bDnuh.mjs.map +0 -1
  128. package/dist/_chunks/Form-B_JE0dbz.mjs.map +0 -1
  129. package/dist/_chunks/Form-BpiR4piS.js.map +0 -1
  130. package/dist/_chunks/History-CBNGU7a-.mjs.map +0 -1
  131. package/dist/_chunks/History-DdIstl8b.js.map +0 -1
  132. package/dist/_chunks/ListConfigurationPage-5dr4qpue.mjs.map +0 -1
  133. package/dist/_chunks/ListConfigurationPage-DkKRparB.js.map +0 -1
  134. package/dist/_chunks/ListViewPage-DecLrYV6.mjs.map +0 -1
  135. package/dist/_chunks/ListViewPage-wE0lXqoD.js.map +0 -1
  136. package/dist/_chunks/Relations-Dqz0C1fz.mjs.map +0 -1
  137. package/dist/_chunks/Relations-L0xYRoSQ.js.map +0 -1
  138. package/dist/_chunks/index-BSn97i8U.mjs.map +0 -1
  139. package/dist/_chunks/index-DyvUPg1a.js.map +0 -1
  140. package/dist/_chunks/layout-DPaHUusj.mjs.map +0 -1
  141. package/dist/_chunks/layout-TPqF2oJ5.js.map +0 -1
  142. package/dist/_chunks/relations-BWYS9gkn.js.map +0 -1
  143. package/dist/_chunks/relations-Ck7-ecDT.mjs.map +0 -1
  144. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  145. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  146. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  147. package/strapi-server.js +0 -3
@@ -1,16 +1,17 @@
1
- import { CrossCircle, More, WarningCircle, ListPlus, Pencil, Trash, Check, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
1
+ import { More, Cross, WarningCircle, ListPlus, Pencil, Trash, Check, CrossCircle, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
2
2
  import { jsx, Fragment, jsxs } from "react/jsx-runtime";
3
- import { useStrapiApp, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useQueryParams, useTracking, useForm, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
3
+ import { useStrapiApp, createContext, useQueryParams, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useForm, useTracking, useGuidedTour, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
4
4
  import * as React from "react";
5
5
  import { lazy } from "react";
6
- import { Button, Menu, VisuallyHidden, Flex, Box, Typography, Dialog, Modal, Radio, Status, SingleSelect, SingleSelectOption, Loader, IconButton, Tooltip, LinkButton } from "@strapi/design-system";
6
+ import { Button, Menu, VisuallyHidden, Flex, Typography, Dialog, Modal, Radio, Status, Box, SingleSelect, SingleSelectOption, IconButton, Loader, Tooltip, LinkButton } from "@strapi/design-system";
7
+ import mapValues from "lodash/fp/mapValues";
7
8
  import { useIntl } from "react-intl";
8
- import { useParams, Navigate, useNavigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
9
- import { styled } from "styled-components";
9
+ import { useParams, useNavigate, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
10
10
  import * as yup from "yup";
11
11
  import { ValidationError } from "yup";
12
12
  import pipe from "lodash/fp/pipe";
13
13
  import { intervalToDuration, isPast } from "date-fns";
14
+ import { styled } from "styled-components";
14
15
  import { stringify } from "qs";
15
16
  import { createSlice, combineReducers } from "@reduxjs/toolkit";
16
17
  const __variableDynamicImportRuntimeHelper = (glob, path) => {
@@ -100,6 +101,7 @@ const DocumentRBAC = ({ children, permissions }) => {
100
101
  if (!slug) {
101
102
  throw new Error("Cannot find the slug param in the URL");
102
103
  }
104
+ const [{ rawQuery }] = useQueryParams();
103
105
  const userPermissions = useAuth("DocumentRBAC", (state) => state.permissions);
104
106
  const contentTypePermissions = React.useMemo(() => {
105
107
  const contentTypePermissions2 = userPermissions.filter(
@@ -110,7 +112,14 @@ const DocumentRBAC = ({ children, permissions }) => {
110
112
  return { ...acc, [action]: [permission] };
111
113
  }, {});
112
114
  }, [slug, userPermissions]);
113
- const { isLoading, allowedActions } = useRBAC(contentTypePermissions, permissions ?? void 0);
115
+ const { isLoading, allowedActions } = useRBAC(
116
+ contentTypePermissions,
117
+ permissions ?? void 0,
118
+ // TODO: useRBAC context should be typed and built differently
119
+ // We are passing raw query as context to the hook so that it can
120
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
121
+ rawQuery
122
+ );
114
123
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
115
124
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
116
125
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -158,7 +167,8 @@ const contentManagerApi = adminApi.enhanceEndpoints({
158
167
  "Document",
159
168
  "InitialData",
160
169
  "HistoryVersion",
161
- "Relations"
170
+ "Relations",
171
+ "UidAvailability"
162
172
  ]
163
173
  });
164
174
  const documentApi = contentManagerApi.injectEndpoints({
@@ -172,7 +182,12 @@ const documentApi = contentManagerApi.injectEndpoints({
172
182
  params: query
173
183
  }
174
184
  }),
175
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
185
+ invalidatesTags: (_result, error, { model }) => {
186
+ if (error) {
187
+ return [];
188
+ }
189
+ return [{ type: "Document", id: `${model}_LIST` }];
190
+ }
176
191
  }),
177
192
  cloneDocument: builder.mutation({
178
193
  query: ({ model, sourceId, data, params }) => ({
@@ -183,7 +198,10 @@ const documentApi = contentManagerApi.injectEndpoints({
183
198
  params
184
199
  }
185
200
  }),
186
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
201
+ invalidatesTags: (_result, _error, { model }) => [
202
+ { type: "Document", id: `${model}_LIST` },
203
+ { type: "UidAvailability", id: model }
204
+ ]
187
205
  }),
188
206
  /**
189
207
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -200,7 +218,8 @@ const documentApi = contentManagerApi.injectEndpoints({
200
218
  }),
201
219
  invalidatesTags: (result, _error, { model }) => [
202
220
  { type: "Document", id: `${model}_LIST` },
203
- "Relations"
221
+ "Relations",
222
+ { type: "UidAvailability", id: model }
204
223
  ]
205
224
  }),
206
225
  deleteDocument: builder.mutation({
@@ -241,7 +260,8 @@ const documentApi = contentManagerApi.injectEndpoints({
241
260
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
242
261
  },
243
262
  { type: "Document", id: `${model}_LIST` },
244
- "Relations"
263
+ "Relations",
264
+ { type: "UidAvailability", id: model }
245
265
  ];
246
266
  }
247
267
  }),
@@ -251,7 +271,7 @@ const documentApi = contentManagerApi.injectEndpoints({
251
271
  */
252
272
  getAllDocuments: builder.query({
253
273
  query: ({ model, params }) => ({
254
- url: `/content-manager/collection-types/${model}`,
274
+ url: `/content-manager/collection-types/${model}${params ? `?${params}` : ""}`,
255
275
  method: "GET",
256
276
  config: {
257
277
  params
@@ -259,6 +279,7 @@ const documentApi = contentManagerApi.injectEndpoints({
259
279
  }),
260
280
  providesTags: (result, _error, arg) => {
261
281
  return [
282
+ { type: "Document", id: `ALL_LIST` },
262
283
  { type: "Document", id: `${arg.model}_LIST` },
263
284
  ...result?.results.map(({ documentId }) => ({
264
285
  type: "Document",
@@ -297,6 +318,11 @@ const documentApi = contentManagerApi.injectEndpoints({
297
318
  {
298
319
  type: "Document",
299
320
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
321
+ },
322
+ // Make it easy to invalidate all individual documents queries for a model
323
+ {
324
+ type: "Document",
325
+ id: `${model}_ALL_ITEMS`
300
326
  }
301
327
  ];
302
328
  }
@@ -360,8 +386,21 @@ const documentApi = contentManagerApi.injectEndpoints({
360
386
  type: "Document",
361
387
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
362
388
  },
363
- "Relations"
389
+ "Relations",
390
+ { type: "UidAvailability", id: model }
364
391
  ];
392
+ },
393
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
394
+ const patchResult = dispatch(
395
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
396
+ Object.assign(draft.data, data);
397
+ })
398
+ );
399
+ try {
400
+ await queryFulfilled;
401
+ } catch {
402
+ patchResult.undo();
403
+ }
365
404
  }
366
405
  }),
367
406
  unpublishDocument: builder.mutation({
@@ -431,20 +470,39 @@ const buildValidParams = (query) => {
431
470
  const isBaseQueryError = (error) => {
432
471
  return error.name !== void 0;
433
472
  };
434
- const createYupSchema = (attributes = {}, components = {}) => {
473
+ const arrayValidator = (attribute, options) => ({
474
+ message: translatedErrors.required,
475
+ test(value) {
476
+ if (options.status === "draft") {
477
+ return true;
478
+ }
479
+ if (!attribute.required) {
480
+ return true;
481
+ }
482
+ if (!value) {
483
+ return false;
484
+ }
485
+ if (Array.isArray(value) && value.length === 0) {
486
+ return false;
487
+ }
488
+ return true;
489
+ }
490
+ });
491
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
435
492
  const createModelSchema = (attributes2) => yup.object().shape(
436
493
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
437
494
  if (DOCUMENT_META_FIELDS.includes(name)) {
438
495
  return acc;
439
496
  }
440
497
  const validations = [
498
+ addNullableValidation,
441
499
  addRequiredValidation,
442
500
  addMinLengthValidation,
443
501
  addMaxLengthValidation,
444
502
  addMinValidation,
445
503
  addMaxValidation,
446
504
  addRegexValidation
447
- ].map((fn) => fn(attribute));
505
+ ].map((fn) => fn(attribute, options));
448
506
  const transformSchema = pipe(...validations);
449
507
  switch (attribute.type) {
450
508
  case "component": {
@@ -454,12 +512,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
454
512
  ...acc,
455
513
  [name]: transformSchema(
456
514
  yup.array().of(createModelSchema(attributes3).nullable(false))
457
- )
515
+ ).test(arrayValidator(attribute, options))
458
516
  };
459
517
  } else {
460
518
  return {
461
519
  ...acc,
462
- [name]: transformSchema(createModelSchema(attributes3))
520
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
463
521
  };
464
522
  }
465
523
  }
@@ -481,7 +539,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
481
539
  }
482
540
  )
483
541
  )
484
- )
542
+ ).test(arrayValidator(attribute, options))
485
543
  };
486
544
  case "relation":
487
545
  return {
@@ -493,7 +551,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
493
551
  } else if (Array.isArray(value)) {
494
552
  return yup.array().of(
495
553
  yup.object().shape({
496
- id: yup.string().required()
554
+ id: yup.number().required()
497
555
  })
498
556
  );
499
557
  } else if (typeof value === "object") {
@@ -545,6 +603,14 @@ const createAttributeSchema = (attribute) => {
545
603
  if (!value || typeof value === "string" && value.length === 0) {
546
604
  return true;
547
605
  }
606
+ if (typeof value === "object") {
607
+ try {
608
+ JSON.stringify(value);
609
+ return true;
610
+ } catch (err) {
611
+ return false;
612
+ }
613
+ }
548
614
  try {
549
615
  JSON.parse(value);
550
616
  return true;
@@ -563,13 +629,7 @@ const createAttributeSchema = (attribute) => {
563
629
  return yup.mixed();
564
630
  }
565
631
  };
566
- const addRequiredValidation = (attribute) => (schema) => {
567
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
568
- return schema.min(1, translatedErrors.required);
569
- }
570
- if (attribute.required && attribute.type !== "relation") {
571
- return schema.required(translatedErrors.required);
572
- }
632
+ const nullableSchema = (schema) => {
573
633
  return schema?.nullable ? schema.nullable() : (
574
634
  // In some cases '.nullable' will not be available on the schema.
575
635
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -577,7 +637,22 @@ const addRequiredValidation = (attribute) => (schema) => {
577
637
  schema
578
638
  );
579
639
  };
580
- const addMinLengthValidation = (attribute) => (schema) => {
640
+ const addNullableValidation = () => (schema) => {
641
+ return nullableSchema(schema);
642
+ };
643
+ const addRequiredValidation = (attribute, options) => (schema) => {
644
+ if (options.status === "draft" || !attribute.required) {
645
+ return schema;
646
+ }
647
+ if (attribute.required && "required" in schema) {
648
+ return schema.required(translatedErrors.required);
649
+ }
650
+ return schema;
651
+ };
652
+ const addMinLengthValidation = (attribute, options) => (schema) => {
653
+ if (options.status === "draft") {
654
+ return schema;
655
+ }
581
656
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
582
657
  return schema.min(attribute.minLength, {
583
658
  ...translatedErrors.minLength,
@@ -599,32 +674,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
599
674
  }
600
675
  return schema;
601
676
  };
602
- const addMinValidation = (attribute) => (schema) => {
603
- if ("min" in attribute) {
677
+ const addMinValidation = (attribute, options) => (schema) => {
678
+ if (options.status === "draft") {
679
+ return schema;
680
+ }
681
+ if ("min" in attribute && "min" in schema) {
604
682
  const min = toInteger(attribute.min);
605
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
606
- if (!attribute.required && "test" in schema && min) {
607
- return schema.test(
608
- "custom-min",
609
- {
610
- ...translatedErrors.min,
611
- values: {
612
- min: attribute.min
613
- }
614
- },
615
- (value) => {
616
- if (!value) {
617
- return true;
618
- }
619
- if (Array.isArray(value) && value.length === 0) {
620
- return true;
621
- }
622
- return value.length >= min;
623
- }
624
- );
625
- }
626
- }
627
- if ("min" in schema && min) {
683
+ if (min) {
628
684
  return schema.min(min, {
629
685
  ...translatedErrors.min,
630
686
  values: {
@@ -742,19 +798,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
742
798
  }, {});
743
799
  return componentsByKey;
744
800
  };
745
- const useDocument = (args, opts) => {
801
+ const HOOKS = {
802
+ /**
803
+ * Hook that allows to mutate the displayed headers of the list view table
804
+ * @constant
805
+ * @type {string}
806
+ */
807
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
808
+ /**
809
+ * Hook that allows to mutate the CM's collection types links pre-set filters
810
+ * @constant
811
+ * @type {string}
812
+ */
813
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
814
+ /**
815
+ * Hook that allows to mutate the CM's edit view layout
816
+ * @constant
817
+ * @type {string}
818
+ */
819
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
820
+ /**
821
+ * Hook that allows to mutate the CM's single types links pre-set filters
822
+ * @constant
823
+ * @type {string}
824
+ */
825
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
826
+ };
827
+ const contentTypesApi = contentManagerApi.injectEndpoints({
828
+ endpoints: (builder) => ({
829
+ getContentTypeConfiguration: builder.query({
830
+ query: (uid) => ({
831
+ url: `/content-manager/content-types/${uid}/configuration`,
832
+ method: "GET"
833
+ }),
834
+ transformResponse: (response) => response.data,
835
+ providesTags: (_result, _error, uid) => [
836
+ { type: "ContentTypesConfiguration", id: uid },
837
+ { type: "ContentTypeSettings", id: "LIST" }
838
+ ]
839
+ }),
840
+ getAllContentTypeSettings: builder.query({
841
+ query: () => "/content-manager/content-types-settings",
842
+ transformResponse: (response) => response.data,
843
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
844
+ }),
845
+ updateContentTypeConfiguration: builder.mutation({
846
+ query: ({ uid, ...body }) => ({
847
+ url: `/content-manager/content-types/${uid}/configuration`,
848
+ method: "PUT",
849
+ data: body
850
+ }),
851
+ transformResponse: (response) => response.data,
852
+ invalidatesTags: (_result, _error, { uid }) => [
853
+ { type: "ContentTypesConfiguration", id: uid },
854
+ { type: "ContentTypeSettings", id: "LIST" },
855
+ // Is this necessary?
856
+ { type: "InitialData" }
857
+ ]
858
+ })
859
+ })
860
+ });
861
+ const {
862
+ useGetContentTypeConfigurationQuery,
863
+ useGetAllContentTypeSettingsQuery,
864
+ useUpdateContentTypeConfigurationMutation
865
+ } = contentTypesApi;
866
+ const checkIfAttributeIsDisplayable = (attribute) => {
867
+ const { type } = attribute;
868
+ if (type === "relation") {
869
+ return !attribute.relation.toLowerCase().includes("morph");
870
+ }
871
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
872
+ };
873
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
874
+ if (!mainFieldName) {
875
+ return void 0;
876
+ }
877
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
878
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
879
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
880
+ );
881
+ return {
882
+ name: mainFieldName,
883
+ type: mainFieldType ?? "string"
884
+ };
885
+ };
886
+ const DEFAULT_SETTINGS = {
887
+ bulkable: false,
888
+ filterable: false,
889
+ searchable: false,
890
+ pagination: false,
891
+ defaultSortBy: "",
892
+ defaultSortOrder: "asc",
893
+ mainField: "id",
894
+ pageSize: 10
895
+ };
896
+ const useDocumentLayout = (model) => {
897
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
898
+ const [{ query }] = useQueryParams();
899
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
746
900
  const { toggleNotification } = useNotification();
747
901
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
902
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
748
903
  const {
749
- currentData: data,
750
- isLoading: isLoadingDocument,
751
- isFetching: isFetchingDocument,
752
- error
753
- } = useGetDocumentQuery(args, {
754
- ...opts,
755
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
756
- });
757
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
904
+ data,
905
+ isLoading: isLoadingConfigs,
906
+ error,
907
+ isFetching: isFetchingConfigs
908
+ } = useGetContentTypeConfigurationQuery(model);
909
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
758
910
  React.useEffect(() => {
759
911
  if (error) {
760
912
  toggleNotification({
@@ -762,83 +914,338 @@ const useDocument = (args, opts) => {
762
914
  message: formatAPIError(error)
763
915
  });
764
916
  }
765
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
766
- const validationSchema = React.useMemo(() => {
767
- if (!schema) {
768
- return null;
769
- }
770
- return createYupSchema(schema.attributes, components);
771
- }, [schema, components]);
772
- const validate = React.useCallback(
773
- (document) => {
774
- if (!validationSchema) {
775
- throw new Error(
776
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
777
- );
778
- }
779
- try {
780
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
781
- return null;
782
- } catch (error2) {
783
- if (error2 instanceof ValidationError) {
784
- return getYupValidationErrors(error2);
785
- }
786
- throw error2;
787
- }
917
+ }, [error, formatAPIError, toggleNotification]);
918
+ const editLayout = React.useMemo(
919
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
920
+ layout: [],
921
+ components: {},
922
+ metadatas: {},
923
+ options: {},
924
+ settings: DEFAULT_SETTINGS
788
925
  },
789
- [validationSchema]
926
+ [data, isLoading, schemas, schema, components]
927
+ );
928
+ const listLayout = React.useMemo(() => {
929
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
930
+ layout: [],
931
+ metadatas: {},
932
+ options: {},
933
+ settings: DEFAULT_SETTINGS
934
+ };
935
+ }, [data, isLoading, schemas, schema, components]);
936
+ const { layout: edit } = React.useMemo(
937
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
938
+ layout: editLayout,
939
+ query
940
+ }),
941
+ [editLayout, query, runHookWaterfall]
790
942
  );
791
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
792
943
  return {
793
- components,
794
- document: data?.data,
795
- meta: data?.meta,
944
+ error,
796
945
  isLoading,
797
- schema,
798
- validate
799
- };
800
- };
801
- const useDoc = () => {
802
- const { id, slug, collectionType, origin } = useParams();
803
- const [{ query }] = useQueryParams();
804
- const params = React.useMemo(() => buildValidParams(query), [query]);
805
- if (!collectionType) {
806
- throw new Error("Could not find collectionType in url params");
807
- }
808
- if (!slug) {
809
- throw new Error("Could not find model in url params");
810
- }
811
- return {
812
- collectionType,
813
- model: slug,
814
- id: origin || id === "create" ? void 0 : id,
815
- ...useDocument(
816
- { documentId: origin || id, model: slug, collectionType, params },
817
- {
818
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
819
- }
820
- )
946
+ edit,
947
+ list: listLayout
821
948
  };
822
949
  };
823
- const prefixPluginTranslations = (trad, pluginId) => {
824
- if (!pluginId) {
825
- throw new TypeError("pluginId can't be empty");
826
- }
827
- return Object.keys(trad).reduce((acc, current) => {
828
- acc[`${pluginId}.${current}`] = trad[current];
829
- return acc;
830
- }, {});
831
- };
832
- const getTranslation = (id) => `content-manager.${id}`;
833
- const DEFAULT_UNEXPECTED_ERROR_MSG = {
834
- id: "notification.error",
835
- defaultMessage: "An error occurred, please try again"
950
+ const useDocLayout = () => {
951
+ const { model } = useDoc();
952
+ return useDocumentLayout(model);
836
953
  };
837
- const useDocumentActions = () => {
954
+ const formatEditLayout = (data, {
955
+ schemas,
956
+ schema,
957
+ components
958
+ }) => {
959
+ let currentPanelIndex = 0;
960
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
961
+ data.contentType.layouts.edit,
962
+ schema?.attributes,
963
+ data.contentType.metadatas,
964
+ { configurations: data.components, schemas: components },
965
+ schemas
966
+ ).reduce((panels, row) => {
967
+ if (row.some((field) => field.type === "dynamiczone")) {
968
+ panels.push([row]);
969
+ currentPanelIndex += 2;
970
+ } else {
971
+ if (!panels[currentPanelIndex]) {
972
+ panels.push([]);
973
+ }
974
+ panels[currentPanelIndex].push(row);
975
+ }
976
+ return panels;
977
+ }, []);
978
+ const componentEditAttributes = Object.entries(data.components).reduce(
979
+ (acc, [uid, configuration]) => {
980
+ acc[uid] = {
981
+ layout: convertEditLayoutToFieldLayouts(
982
+ configuration.layouts.edit,
983
+ components[uid].attributes,
984
+ configuration.metadatas,
985
+ { configurations: data.components, schemas: components }
986
+ ),
987
+ settings: {
988
+ ...configuration.settings,
989
+ icon: components[uid].info.icon,
990
+ displayName: components[uid].info.displayName
991
+ }
992
+ };
993
+ return acc;
994
+ },
995
+ {}
996
+ );
997
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
998
+ (acc, [attribute, metadata]) => {
999
+ return {
1000
+ ...acc,
1001
+ [attribute]: metadata.edit
1002
+ };
1003
+ },
1004
+ {}
1005
+ );
1006
+ return {
1007
+ layout: panelledEditAttributes,
1008
+ components: componentEditAttributes,
1009
+ metadatas: editMetadatas,
1010
+ settings: {
1011
+ ...data.contentType.settings,
1012
+ displayName: schema?.info.displayName
1013
+ },
1014
+ options: {
1015
+ ...schema?.options,
1016
+ ...schema?.pluginOptions,
1017
+ ...data.contentType.options
1018
+ }
1019
+ };
1020
+ };
1021
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1022
+ return rows.map(
1023
+ (row) => row.map((field) => {
1024
+ const attribute = attributes[field.name];
1025
+ if (!attribute) {
1026
+ return null;
1027
+ }
1028
+ const { edit: metadata } = metadatas[field.name];
1029
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1030
+ return {
1031
+ attribute,
1032
+ disabled: !metadata.editable,
1033
+ hint: metadata.description,
1034
+ label: metadata.label ?? "",
1035
+ name: field.name,
1036
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1037
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1038
+ schemas,
1039
+ components: components?.schemas ?? {}
1040
+ }),
1041
+ placeholder: metadata.placeholder ?? "",
1042
+ required: attribute.required ?? false,
1043
+ size: field.size,
1044
+ unique: "unique" in attribute ? attribute.unique : false,
1045
+ visible: metadata.visible ?? true,
1046
+ type: attribute.type
1047
+ };
1048
+ }).filter((field) => field !== null)
1049
+ );
1050
+ };
1051
+ const formatListLayout = (data, {
1052
+ schemas,
1053
+ schema,
1054
+ components
1055
+ }) => {
1056
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1057
+ (acc, [attribute, metadata]) => {
1058
+ return {
1059
+ ...acc,
1060
+ [attribute]: metadata.list
1061
+ };
1062
+ },
1063
+ {}
1064
+ );
1065
+ const listAttributes = convertListLayoutToFieldLayouts(
1066
+ data.contentType.layouts.list,
1067
+ schema?.attributes,
1068
+ listMetadatas,
1069
+ { configurations: data.components, schemas: components },
1070
+ schemas
1071
+ );
1072
+ return {
1073
+ layout: listAttributes,
1074
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1075
+ metadatas: listMetadatas,
1076
+ options: {
1077
+ ...schema?.options,
1078
+ ...schema?.pluginOptions,
1079
+ ...data.contentType.options
1080
+ }
1081
+ };
1082
+ };
1083
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1084
+ return columns.map((name) => {
1085
+ const attribute = attributes[name];
1086
+ if (!attribute) {
1087
+ return null;
1088
+ }
1089
+ const metadata = metadatas[name];
1090
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1091
+ return {
1092
+ attribute,
1093
+ label: metadata.label ?? "",
1094
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1095
+ schemas,
1096
+ components: components?.schemas ?? {}
1097
+ }),
1098
+ name,
1099
+ searchable: metadata.searchable ?? true,
1100
+ sortable: metadata.sortable ?? true
1101
+ };
1102
+ }).filter((field) => field !== null);
1103
+ };
1104
+ const useDocument = (args, opts) => {
1105
+ const { toggleNotification } = useNotification();
1106
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1107
+ const {
1108
+ currentData: data,
1109
+ isLoading: isLoadingDocument,
1110
+ isFetching: isFetchingDocument,
1111
+ error
1112
+ } = useGetDocumentQuery(args, {
1113
+ ...opts,
1114
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1115
+ });
1116
+ const {
1117
+ components,
1118
+ schema,
1119
+ schemas,
1120
+ isLoading: isLoadingSchema
1121
+ } = useContentTypeSchema(args.model);
1122
+ React.useEffect(() => {
1123
+ if (error) {
1124
+ toggleNotification({
1125
+ type: "danger",
1126
+ message: formatAPIError(error)
1127
+ });
1128
+ }
1129
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1130
+ const validationSchema = React.useMemo(() => {
1131
+ if (!schema) {
1132
+ return null;
1133
+ }
1134
+ return createYupSchema(schema.attributes, components);
1135
+ }, [schema, components]);
1136
+ const validate = React.useCallback(
1137
+ (document) => {
1138
+ if (!validationSchema) {
1139
+ throw new Error(
1140
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1141
+ );
1142
+ }
1143
+ try {
1144
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1145
+ return null;
1146
+ } catch (error2) {
1147
+ if (error2 instanceof ValidationError) {
1148
+ return getYupValidationErrors(error2);
1149
+ }
1150
+ throw error2;
1151
+ }
1152
+ },
1153
+ [validationSchema]
1154
+ );
1155
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1156
+ const hasError = !!error;
1157
+ return {
1158
+ components,
1159
+ document: data?.data,
1160
+ meta: data?.meta,
1161
+ isLoading,
1162
+ hasError,
1163
+ schema,
1164
+ schemas,
1165
+ validate
1166
+ };
1167
+ };
1168
+ const useDoc = () => {
1169
+ const { id, slug, collectionType, origin } = useParams();
1170
+ const [{ query }] = useQueryParams();
1171
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1172
+ if (!collectionType) {
1173
+ throw new Error("Could not find collectionType in url params");
1174
+ }
1175
+ if (!slug) {
1176
+ throw new Error("Could not find model in url params");
1177
+ }
1178
+ const document = useDocument(
1179
+ { documentId: origin || id, model: slug, collectionType, params },
1180
+ {
1181
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1182
+ }
1183
+ );
1184
+ const returnId = origin || id === "create" ? void 0 : id;
1185
+ return {
1186
+ collectionType,
1187
+ model: slug,
1188
+ id: returnId,
1189
+ ...document
1190
+ };
1191
+ };
1192
+ const useContentManagerContext = () => {
1193
+ const {
1194
+ collectionType,
1195
+ model,
1196
+ id,
1197
+ components,
1198
+ isLoading: isLoadingDoc,
1199
+ schema,
1200
+ schemas
1201
+ } = useDoc();
1202
+ const layout = useDocumentLayout(model);
1203
+ const form = useForm("useContentManagerContext", (state) => state);
1204
+ const isSingleType = collectionType === SINGLE_TYPES;
1205
+ const slug = model;
1206
+ const isCreatingEntry = id === "create";
1207
+ useContentTypeSchema();
1208
+ const isLoading = isLoadingDoc || layout.isLoading;
1209
+ const error = layout.error;
1210
+ return {
1211
+ error,
1212
+ isLoading,
1213
+ // Base metadata
1214
+ model,
1215
+ collectionType,
1216
+ id,
1217
+ slug,
1218
+ isCreatingEntry,
1219
+ isSingleType,
1220
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1221
+ // All schema infos
1222
+ components,
1223
+ contentType: schema,
1224
+ contentTypes: schemas,
1225
+ // Form state
1226
+ form,
1227
+ // layout infos
1228
+ layout
1229
+ };
1230
+ };
1231
+ const prefixPluginTranslations = (trad, pluginId) => {
1232
+ return Object.keys(trad).reduce((acc, current) => {
1233
+ acc[`${pluginId}.${current}`] = trad[current];
1234
+ return acc;
1235
+ }, {});
1236
+ };
1237
+ const getTranslation = (id) => `content-manager.${id}`;
1238
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1239
+ id: "notification.error",
1240
+ defaultMessage: "An error occurred, please try again"
1241
+ };
1242
+ const useDocumentActions = () => {
838
1243
  const { toggleNotification } = useNotification();
839
1244
  const { formatMessage } = useIntl();
840
1245
  const { trackUsage } = useTracking();
841
1246
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1247
+ const navigate = useNavigate();
1248
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
842
1249
  const [deleteDocument] = useDeleteDocumentMutation();
843
1250
  const _delete = React.useCallback(
844
1251
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1153,6 +1560,7 @@ const useDocumentActions = () => {
1153
1560
  defaultMessage: "Saved document"
1154
1561
  })
1155
1562
  });
1563
+ setCurrentStep("contentManager.success");
1156
1564
  return res.data;
1157
1565
  } catch (err) {
1158
1566
  toggleNotification({
@@ -1174,7 +1582,6 @@ const useDocumentActions = () => {
1174
1582
  sourceId
1175
1583
  });
1176
1584
  if ("error" in res) {
1177
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1178
1585
  return { error: res.error };
1179
1586
  }
1180
1587
  toggleNotification({
@@ -1193,7 +1600,7 @@ const useDocumentActions = () => {
1193
1600
  throw err;
1194
1601
  }
1195
1602
  },
1196
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1603
+ [autoCloneDocument, formatMessage, toggleNotification]
1197
1604
  );
1198
1605
  const [cloneDocument] = useCloneDocumentMutation();
1199
1606
  const clone = React.useCallback(
@@ -1219,6 +1626,7 @@ const useDocumentActions = () => {
1219
1626
  defaultMessage: "Cloned document"
1220
1627
  })
1221
1628
  });
1629
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1222
1630
  return res.data;
1223
1631
  } catch (err) {
1224
1632
  toggleNotification({
@@ -1229,7 +1637,7 @@ const useDocumentActions = () => {
1229
1637
  throw err;
1230
1638
  }
1231
1639
  },
1232
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1640
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1233
1641
  );
1234
1642
  const [getDoc] = useLazyGetDocumentQuery();
1235
1643
  const getDocument = React.useCallback(
@@ -1255,7 +1663,7 @@ const useDocumentActions = () => {
1255
1663
  };
1256
1664
  };
1257
1665
  const ProtectedHistoryPage = lazy(
1258
- () => import("./History-CBNGU7a-.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1666
+ () => import("./History-8tQaor_-.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1259
1667
  );
1260
1668
  const routes$1 = [
1261
1669
  {
@@ -1268,31 +1676,31 @@ const routes$1 = [
1268
1676
  }
1269
1677
  ];
1270
1678
  const ProtectedEditViewPage = lazy(
1271
- () => import("./EditViewPage-DlLEyUL6.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1679
+ () => import("./EditViewPage-DBV7CZVf.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1272
1680
  );
1273
1681
  const ProtectedListViewPage = lazy(
1274
- () => import("./ListViewPage-DecLrYV6.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1682
+ () => import("./ListViewPage-CZfw9OW9.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1275
1683
  );
1276
1684
  const ProtectedListConfiguration = lazy(
1277
- () => import("./ListConfigurationPage-5dr4qpue.mjs").then((mod) => ({
1685
+ () => import("./ListConfigurationPage-_WWIGpVT.mjs").then((mod) => ({
1278
1686
  default: mod.ProtectedListConfiguration
1279
1687
  }))
1280
1688
  );
1281
1689
  const ProtectedEditConfigurationPage = lazy(
1282
- () => import("./EditConfigurationPage-ZO0vOO8q.mjs").then((mod) => ({
1690
+ () => import("./EditConfigurationPage-C1HHivAz.mjs").then((mod) => ({
1283
1691
  default: mod.ProtectedEditConfigurationPage
1284
1692
  }))
1285
1693
  );
1286
1694
  const ProtectedComponentConfigurationPage = lazy(
1287
- () => import("./ComponentConfigurationPage-B1bIXVuX.mjs").then((mod) => ({
1695
+ () => import("./ComponentConfigurationPage-DH3rgf1K.mjs").then((mod) => ({
1288
1696
  default: mod.ProtectedComponentConfigurationPage
1289
1697
  }))
1290
1698
  );
1291
1699
  const NoPermissions = lazy(
1292
- () => import("./NoPermissionsPage-CM5UD8ee.mjs").then((mod) => ({ default: mod.NoPermissions }))
1700
+ () => import("./NoPermissionsPage-m7GAd26r.mjs").then((mod) => ({ default: mod.NoPermissions }))
1293
1701
  );
1294
1702
  const NoContentType = lazy(
1295
- () => import("./NoContentTypePage-CiIcfYsd.mjs").then((mod) => ({ default: mod.NoContentType }))
1703
+ () => import("./NoContentTypePage-D-RqshUI.mjs").then((mod) => ({ default: mod.NoContentType }))
1296
1704
  );
1297
1705
  const CollectionTypePages = () => {
1298
1706
  const { collectionType } = useParams();
@@ -1406,12 +1814,14 @@ const DocumentActionButton = (action) => {
1406
1814
  /* @__PURE__ */ jsx(
1407
1815
  Button,
1408
1816
  {
1409
- flex: 1,
1817
+ flex: "auto",
1410
1818
  startIcon: action.icon,
1411
1819
  disabled: action.disabled,
1412
1820
  onClick: handleClick(action),
1413
1821
  justifyContent: "center",
1414
1822
  variant: action.variant || "default",
1823
+ paddingTop: "7px",
1824
+ paddingBottom: "7px",
1415
1825
  children: action.label
1416
1826
  }
1417
1827
  ),
@@ -1476,9 +1886,9 @@ const DocumentActionsMenu = ({
1476
1886
  disabled: isDisabled,
1477
1887
  size: "S",
1478
1888
  endIcon: null,
1479
- paddingTop: "7px",
1480
- paddingLeft: "9px",
1481
- paddingRight: "9px",
1889
+ paddingTop: "4px",
1890
+ paddingLeft: "7px",
1891
+ paddingRight: "7px",
1482
1892
  variant,
1483
1893
  children: [
1484
1894
  /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
@@ -1489,7 +1899,7 @@ const DocumentActionsMenu = ({
1489
1899
  ]
1490
1900
  }
1491
1901
  ),
1492
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1902
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1493
1903
  actions2.map((action) => {
1494
1904
  return /* @__PURE__ */ jsx(
1495
1905
  Menu.Item,
@@ -1498,14 +1908,29 @@ const DocumentActionsMenu = ({
1498
1908
  onSelect: handleClick(action),
1499
1909
  display: "block",
1500
1910
  children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1501
- /* @__PURE__ */ jsxs(Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1502
- /* @__PURE__ */ jsx(Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1503
- action.label
1504
- ] }),
1505
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1911
+ /* @__PURE__ */ jsxs(
1506
1912
  Flex,
1507
1913
  {
1508
- alignItems: "center",
1914
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1915
+ gap: 2,
1916
+ tag: "span",
1917
+ children: [
1918
+ /* @__PURE__ */ jsx(
1919
+ Flex,
1920
+ {
1921
+ tag: "span",
1922
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1923
+ children: action.icon
1924
+ }
1925
+ ),
1926
+ action.label
1927
+ ]
1928
+ }
1929
+ ),
1930
+ action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1931
+ Flex,
1932
+ {
1933
+ alignItems: "center",
1509
1934
  background: "alternative100",
1510
1935
  borderStyle: "solid",
1511
1936
  borderColor: "alternative200",
@@ -1598,11 +2023,11 @@ const DocumentActionConfirmDialog = ({
1598
2023
  /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1599
2024
  /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1600
2025
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1601
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
2026
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1602
2027
  id: "app.components.Button.cancel",
1603
2028
  defaultMessage: "Cancel"
1604
2029
  }) }) }),
1605
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
2030
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1606
2031
  id: "app.components.Button.confirm",
1607
2032
  defaultMessage: "Confirm"
1608
2033
  }) })
@@ -1625,10 +2050,22 @@ const DocumentActionModal = ({
1625
2050
  };
1626
2051
  return /* @__PURE__ */ jsx(Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
1627
2052
  /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: title }) }),
1628
- /* @__PURE__ */ jsx(Modal.Body, { children: typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content }),
1629
- /* @__PURE__ */ jsx(Modal.Footer, { children: typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer })
2053
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsx(Modal.Body, { children: Content }),
2054
+ typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1630
2055
  ] }) });
1631
2056
  };
2057
+ const transformData = (data) => {
2058
+ if (Array.isArray(data)) {
2059
+ return data.map(transformData);
2060
+ }
2061
+ if (typeof data === "object" && data !== null) {
2062
+ if ("apiData" in data) {
2063
+ return data.apiData;
2064
+ }
2065
+ return mapValues(transformData)(data);
2066
+ }
2067
+ return data;
2068
+ };
1632
2069
  const PublishAction$1 = ({
1633
2070
  activeTab,
1634
2071
  documentId,
@@ -1641,12 +2078,10 @@ const PublishAction$1 = ({
1641
2078
  const navigate = useNavigate();
1642
2079
  const { toggleNotification } = useNotification();
1643
2080
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2081
+ const isListView = useMatch(LIST_PATH) !== null;
1644
2082
  const isCloning = useMatch(CLONE_PATH) !== null;
1645
2083
  const { formatMessage } = useIntl();
1646
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1647
- "PublishAction",
1648
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1649
- );
2084
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1650
2085
  const { publish } = useDocumentActions();
1651
2086
  const [
1652
2087
  countDraftRelations,
@@ -1698,24 +2133,25 @@ const PublishAction$1 = ({
1698
2133
  }
1699
2134
  }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1700
2135
  React.useEffect(() => {
1701
- if (documentId) {
1702
- const fetchDraftRelationsCount = async () => {
1703
- const { data, error } = await countDraftRelations({
1704
- collectionType,
1705
- model,
1706
- documentId,
1707
- params
1708
- });
1709
- if (error) {
1710
- throw error;
1711
- }
1712
- if (data) {
1713
- setServerCountOfDraftRelations(data.data);
1714
- }
1715
- };
1716
- fetchDraftRelationsCount();
2136
+ if (!document || !document.documentId || isListView) {
2137
+ return;
1717
2138
  }
1718
- }, [documentId, countDraftRelations, collectionType, model, params]);
2139
+ const fetchDraftRelationsCount = async () => {
2140
+ const { data, error } = await countDraftRelations({
2141
+ collectionType,
2142
+ model,
2143
+ documentId,
2144
+ params
2145
+ });
2146
+ if (error) {
2147
+ throw error;
2148
+ }
2149
+ if (data) {
2150
+ setServerCountOfDraftRelations(data.data);
2151
+ }
2152
+ };
2153
+ fetchDraftRelationsCount();
2154
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1719
2155
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1720
2156
  if (!schema?.options?.draftAndPublish) {
1721
2157
  return null;
@@ -1723,7 +2159,9 @@ const PublishAction$1 = ({
1723
2159
  const performPublish = async () => {
1724
2160
  setSubmitting(true);
1725
2161
  try {
1726
- const { errors } = await validate();
2162
+ const { errors } = await validate(true, {
2163
+ status: "published"
2164
+ });
1727
2165
  if (errors) {
1728
2166
  toggleNotification({
1729
2167
  type: "danger",
@@ -1741,7 +2179,7 @@ const PublishAction$1 = ({
1741
2179
  documentId,
1742
2180
  params
1743
2181
  },
1744
- formValues
2182
+ transformData(formValues)
1745
2183
  );
1746
2184
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1747
2185
  navigate({
@@ -1756,7 +2194,8 @@ const PublishAction$1 = ({
1756
2194
  }
1757
2195
  };
1758
2196
  const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1759
- const hasDraftRelations = totalDraftRelations > 0;
2197
+ const enableDraftRelationsCount = false;
2198
+ const hasDraftRelations = enableDraftRelationsCount;
1760
2199
  return {
1761
2200
  /**
1762
2201
  * Disabled when:
@@ -1766,18 +2205,13 @@ const PublishAction$1 = ({
1766
2205
  * - the document is already published & not modified
1767
2206
  * - the document is being created & not modified
1768
2207
  * - the user doesn't have the permission to publish
1769
- * - the user doesn't have the permission to create a new document
1770
- * - the user doesn't have the permission to update the document
1771
2208
  */
1772
- disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2209
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1773
2210
  label: formatMessage({
1774
2211
  id: "app.utils.publish",
1775
2212
  defaultMessage: "Publish"
1776
2213
  }),
1777
2214
  onClick: async () => {
1778
- if (hasDraftRelations) {
1779
- return;
1780
- }
1781
2215
  await performPublish();
1782
2216
  },
1783
2217
  dialog: hasDraftRelations ? {
@@ -1816,10 +2250,6 @@ const UpdateAction = ({
1816
2250
  const cloneMatch = useMatch(CLONE_PATH);
1817
2251
  const isCloning = cloneMatch !== null;
1818
2252
  const { formatMessage } = useIntl();
1819
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1820
- canCreate: canCreate2,
1821
- canUpdate: canUpdate2
1822
- }));
1823
2253
  const { create, update, clone } = useDocumentActions();
1824
2254
  const [{ query, rawQuery }] = useQueryParams();
1825
2255
  const params = React.useMemo(() => buildValidParams(query), [query]);
@@ -1836,10 +2266,8 @@ const UpdateAction = ({
1836
2266
  * - the form is submitting
1837
2267
  * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1838
2268
  * - the active tab is the published tab
1839
- * - the user doesn't have the permission to create a new document
1840
- * - the user doesn't have the permission to update the document
1841
2269
  */
1842
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
2270
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1843
2271
  label: formatMessage({
1844
2272
  id: "content-manager.containers.Edit.save",
1845
2273
  defaultMessage: "Save"
@@ -1847,7 +2275,9 @@ const UpdateAction = ({
1847
2275
  onClick: async () => {
1848
2276
  setSubmitting(true);
1849
2277
  try {
1850
- const { errors } = await validate();
2278
+ const { errors } = await validate(true, {
2279
+ status: "draft"
2280
+ });
1851
2281
  if (errors) {
1852
2282
  toggleNotification({
1853
2283
  type: "danger",
@@ -1865,13 +2295,16 @@ const UpdateAction = ({
1865
2295
  documentId: cloneMatch.params.origin,
1866
2296
  params
1867
2297
  },
1868
- document
2298
+ transformData(document)
1869
2299
  );
1870
2300
  if ("data" in res) {
1871
- navigate({
1872
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1873
- search: rawQuery
1874
- });
2301
+ navigate(
2302
+ {
2303
+ pathname: `../${res.data.documentId}`,
2304
+ search: rawQuery
2305
+ },
2306
+ { relative: "path" }
2307
+ );
1875
2308
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1876
2309
  setErrors(formatValidationErrors(res.error));
1877
2310
  }
@@ -1883,7 +2316,7 @@ const UpdateAction = ({
1883
2316
  documentId,
1884
2317
  params
1885
2318
  },
1886
- document
2319
+ transformData(document)
1887
2320
  );
1888
2321
  if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1889
2322
  setErrors(formatValidationErrors(res.error));
@@ -1896,15 +2329,15 @@ const UpdateAction = ({
1896
2329
  model,
1897
2330
  params
1898
2331
  },
1899
- document
2332
+ transformData(document)
1900
2333
  );
1901
2334
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1902
2335
  navigate(
1903
2336
  {
1904
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2337
+ pathname: `../${res.data.documentId}`,
1905
2338
  search: rawQuery
1906
2339
  },
1907
- { replace: true }
2340
+ { replace: true, relative: "path" }
1908
2341
  );
1909
2342
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1910
2343
  setErrors(formatValidationErrors(res.error));
@@ -1949,7 +2382,7 @@ const UnpublishAction$1 = ({
1949
2382
  id: "app.utils.unpublish",
1950
2383
  defaultMessage: "Unpublish"
1951
2384
  }),
1952
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2385
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1953
2386
  onClick: async () => {
1954
2387
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1955
2388
  if (!documentId) {
@@ -2061,7 +2494,7 @@ const DiscardAction = ({
2061
2494
  id: "content-manager.actions.discard.label",
2062
2495
  defaultMessage: "Discard changes"
2063
2496
  }),
2064
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2497
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2065
2498
  position: ["panel", "table-row"],
2066
2499
  variant: "danger",
2067
2500
  dialog: {
@@ -2089,11 +2522,6 @@ const DiscardAction = ({
2089
2522
  };
2090
2523
  };
2091
2524
  DiscardAction.type = "discard";
2092
- const StyledCrossCircle = styled(CrossCircle)`
2093
- path {
2094
- fill: currentColor;
2095
- }
2096
- `;
2097
2525
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2098
2526
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2099
2527
  const RelativeTime = React.forwardRef(
@@ -2141,7 +2569,7 @@ const getDisplayName = ({
2141
2569
  };
2142
2570
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2143
2571
  const DocumentStatus = ({ status = "draft", ...restProps }) => {
2144
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2572
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2145
2573
  return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2146
2574
  };
2147
2575
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
@@ -2240,12 +2668,12 @@ const Information = ({ activeTab }) => {
2240
2668
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2241
2669
  label: formatMessage({
2242
2670
  id: "content-manager.containers.edit.information.last-published.label",
2243
- defaultMessage: "Last published"
2671
+ defaultMessage: "Published"
2244
2672
  }),
2245
2673
  value: formatMessage(
2246
2674
  {
2247
2675
  id: "content-manager.containers.edit.information.last-published.value",
2248
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2676
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2249
2677
  },
2250
2678
  {
2251
2679
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2258,12 +2686,12 @@ const Information = ({ activeTab }) => {
2258
2686
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2259
2687
  label: formatMessage({
2260
2688
  id: "content-manager.containers.edit.information.last-draft.label",
2261
- defaultMessage: "Last draft"
2689
+ defaultMessage: "Updated"
2262
2690
  }),
2263
2691
  value: formatMessage(
2264
2692
  {
2265
2693
  id: "content-manager.containers.edit.information.last-draft.value",
2266
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2694
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2267
2695
  },
2268
2696
  {
2269
2697
  time: /* @__PURE__ */ jsx(
@@ -2281,12 +2709,12 @@ const Information = ({ activeTab }) => {
2281
2709
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2282
2710
  label: formatMessage({
2283
2711
  id: "content-manager.containers.edit.information.document.label",
2284
- defaultMessage: "Document"
2712
+ defaultMessage: "Created"
2285
2713
  }),
2286
2714
  value: formatMessage(
2287
2715
  {
2288
2716
  id: "content-manager.containers.edit.information.document.value",
2289
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2717
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2290
2718
  },
2291
2719
  {
2292
2720
  time: /* @__PURE__ */ jsx(
@@ -2324,25 +2752,77 @@ const Information = ({ activeTab }) => {
2324
2752
  );
2325
2753
  };
2326
2754
  const HeaderActions = ({ actions: actions2 }) => {
2327
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2328
- if ("options" in action) {
2755
+ const [dialogId, setDialogId] = React.useState(null);
2756
+ const handleClick = (action) => async (e) => {
2757
+ if (!("options" in action)) {
2758
+ const { onClick = () => false, dialog, id } = action;
2759
+ const muteDialog = await onClick(e);
2760
+ if (dialog && !muteDialog) {
2761
+ e.preventDefault();
2762
+ setDialogId(id);
2763
+ }
2764
+ }
2765
+ };
2766
+ const handleClose = () => {
2767
+ setDialogId(null);
2768
+ };
2769
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2770
+ if (action.options) {
2329
2771
  return /* @__PURE__ */ jsx(
2330
2772
  SingleSelect,
2331
2773
  {
2332
2774
  size: "S",
2333
- disabled: action.disabled,
2334
- "aria-label": action.label,
2335
2775
  onChange: action.onSelect,
2336
- value: action.value,
2776
+ "aria-label": action.label,
2777
+ ...action,
2337
2778
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2338
2779
  },
2339
2780
  action.id
2340
2781
  );
2341
2782
  } else {
2342
- return null;
2783
+ if (action.type === "icon") {
2784
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
2785
+ /* @__PURE__ */ jsx(
2786
+ IconButton,
2787
+ {
2788
+ disabled: action.disabled,
2789
+ label: action.label,
2790
+ size: "S",
2791
+ onClick: handleClick(action),
2792
+ children: action.icon
2793
+ }
2794
+ ),
2795
+ action.dialog ? /* @__PURE__ */ jsx(
2796
+ HeaderActionDialog,
2797
+ {
2798
+ ...action.dialog,
2799
+ isOpen: dialogId === action.id,
2800
+ onClose: handleClose
2801
+ }
2802
+ ) : null
2803
+ ] }, action.id);
2804
+ }
2343
2805
  }
2344
2806
  }) });
2345
2807
  };
2808
+ const HeaderActionDialog = ({
2809
+ onClose,
2810
+ onCancel,
2811
+ title,
2812
+ content: Content,
2813
+ isOpen
2814
+ }) => {
2815
+ const handleClose = async () => {
2816
+ if (onCancel) {
2817
+ await onCancel();
2818
+ }
2819
+ onClose();
2820
+ };
2821
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2822
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2823
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
2824
+ ] }) });
2825
+ };
2346
2826
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2347
2827
  const navigate = useNavigate();
2348
2828
  const { formatMessage } = useIntl();
@@ -2367,480 +2847,182 @@ const EditTheModelAction = ({ model }) => {
2367
2847
  id: "content-manager.link-to-ctb",
2368
2848
  defaultMessage: "Edit the model"
2369
2849
  }),
2370
- icon: /* @__PURE__ */ jsx(Pencil, {}),
2371
- onClick: () => {
2372
- navigate(`/plugins/content-type-builder/content-types/${model}`);
2373
- },
2374
- position: "header"
2375
- };
2376
- };
2377
- EditTheModelAction.type = "edit-the-model";
2378
- const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2379
- const navigate = useNavigate();
2380
- const { formatMessage } = useIntl();
2381
- const listViewPathMatch = useMatch(LIST_PATH);
2382
- const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2383
- const { delete: deleteAction } = useDocumentActions();
2384
- const { toggleNotification } = useNotification();
2385
- const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2386
- return {
2387
- disabled: !canDelete || !document,
2388
- label: formatMessage({
2389
- id: "content-manager.actions.delete.label",
2390
- defaultMessage: "Delete document"
2391
- }),
2392
- icon: /* @__PURE__ */ jsx(Trash, {}),
2393
- dialog: {
2394
- type: "dialog",
2395
- title: formatMessage({
2396
- id: "app.components.ConfirmDialog.title",
2397
- defaultMessage: "Confirmation"
2398
- }),
2399
- content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
2400
- /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2401
- /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2402
- id: "content-manager.actions.delete.dialog.body",
2403
- defaultMessage: "Are you sure?"
2404
- }) })
2405
- ] }),
2406
- onConfirm: async () => {
2407
- if (!listViewPathMatch) {
2408
- setSubmitting(true);
2409
- }
2410
- try {
2411
- if (!documentId && collectionType !== SINGLE_TYPES) {
2412
- console.error(
2413
- "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2414
- );
2415
- toggleNotification({
2416
- message: formatMessage({
2417
- id: "content-manager.actions.delete.error",
2418
- defaultMessage: "An error occurred while trying to delete the document."
2419
- }),
2420
- type: "danger"
2421
- });
2422
- return;
2423
- }
2424
- const res = await deleteAction({
2425
- documentId,
2426
- model,
2427
- collectionType,
2428
- params: {
2429
- locale: "*"
2430
- }
2431
- });
2432
- if (!("error" in res)) {
2433
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2434
- }
2435
- } finally {
2436
- if (!listViewPathMatch) {
2437
- setSubmitting(false);
2438
- }
2439
- }
2440
- }
2441
- },
2442
- variant: "danger",
2443
- position: ["header", "table-row"]
2444
- };
2445
- };
2446
- DeleteAction$1.type = "delete";
2447
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2448
- const Panels = () => {
2449
- const isCloning = useMatch(CLONE_PATH) !== null;
2450
- const [
2451
- {
2452
- query: { status }
2453
- }
2454
- ] = useQueryParams({
2455
- status: "draft"
2456
- });
2457
- const { model, id, document, meta, collectionType } = useDoc();
2458
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2459
- const props = {
2460
- activeTab: status,
2461
- model,
2462
- documentId: id,
2463
- document: isCloning ? void 0 : document,
2464
- meta: isCloning ? void 0 : meta,
2465
- collectionType
2466
- };
2467
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2468
- DescriptionComponentRenderer,
2469
- {
2470
- props,
2471
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2472
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2473
- }
2474
- ) });
2475
- };
2476
- const ActionsPanel = () => {
2477
- const { formatMessage } = useIntl();
2478
- return {
2479
- title: formatMessage({
2480
- id: "content-manager.containers.edit.panels.default.title",
2481
- defaultMessage: "Document"
2482
- }),
2483
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2484
- };
2485
- };
2486
- ActionsPanel.type = "actions";
2487
- const ActionsPanelContent = () => {
2488
- const isCloning = useMatch(CLONE_PATH) !== null;
2489
- const [
2490
- {
2491
- query: { status = "draft" }
2492
- }
2493
- ] = useQueryParams();
2494
- const { model, id, document, meta, collectionType } = useDoc();
2495
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2496
- const props = {
2497
- activeTab: status,
2498
- model,
2499
- documentId: id,
2500
- document: isCloning ? void 0 : document,
2501
- meta: isCloning ? void 0 : meta,
2502
- collectionType
2503
- };
2504
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2505
- /* @__PURE__ */ jsx(
2506
- DescriptionComponentRenderer,
2507
- {
2508
- props,
2509
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2510
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2511
- }
2512
- ),
2513
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2514
- ] });
2515
- };
2516
- const Panel = React.forwardRef(({ children, title }, ref) => {
2517
- return /* @__PURE__ */ jsxs(
2518
- Flex,
2519
- {
2520
- ref,
2521
- tag: "aside",
2522
- "aria-labelledby": "additional-information",
2523
- background: "neutral0",
2524
- borderColor: "neutral150",
2525
- hasRadius: true,
2526
- paddingBottom: 4,
2527
- paddingLeft: 4,
2528
- paddingRight: 4,
2529
- paddingTop: 4,
2530
- shadow: "tableShadow",
2531
- gap: 3,
2532
- direction: "column",
2533
- justifyContent: "stretch",
2534
- alignItems: "flex-start",
2535
- children: [
2536
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2537
- children
2538
- ]
2539
- }
2540
- );
2541
- });
2542
- const HOOKS = {
2543
- /**
2544
- * Hook that allows to mutate the displayed headers of the list view table
2545
- * @constant
2546
- * @type {string}
2547
- */
2548
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2549
- /**
2550
- * Hook that allows to mutate the CM's collection types links pre-set filters
2551
- * @constant
2552
- * @type {string}
2553
- */
2554
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2555
- /**
2556
- * Hook that allows to mutate the CM's edit view layout
2557
- * @constant
2558
- * @type {string}
2559
- */
2560
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2561
- /**
2562
- * Hook that allows to mutate the CM's single types links pre-set filters
2563
- * @constant
2564
- * @type {string}
2565
- */
2566
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2567
- };
2568
- const contentTypesApi = contentManagerApi.injectEndpoints({
2569
- endpoints: (builder) => ({
2570
- getContentTypeConfiguration: builder.query({
2571
- query: (uid) => ({
2572
- url: `/content-manager/content-types/${uid}/configuration`,
2573
- method: "GET"
2574
- }),
2575
- transformResponse: (response) => response.data,
2576
- providesTags: (_result, _error, uid) => [
2577
- { type: "ContentTypesConfiguration", id: uid },
2578
- { type: "ContentTypeSettings", id: "LIST" }
2579
- ]
2580
- }),
2581
- getAllContentTypeSettings: builder.query({
2582
- query: () => "/content-manager/content-types-settings",
2583
- transformResponse: (response) => response.data,
2584
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2585
- }),
2586
- updateContentTypeConfiguration: builder.mutation({
2587
- query: ({ uid, ...body }) => ({
2588
- url: `/content-manager/content-types/${uid}/configuration`,
2589
- method: "PUT",
2590
- data: body
2591
- }),
2592
- transformResponse: (response) => response.data,
2593
- invalidatesTags: (_result, _error, { uid }) => [
2594
- { type: "ContentTypesConfiguration", id: uid },
2595
- { type: "ContentTypeSettings", id: "LIST" },
2596
- // Is this necessary?
2597
- { type: "InitialData" }
2598
- ]
2599
- })
2600
- })
2601
- });
2602
- const {
2603
- useGetContentTypeConfigurationQuery,
2604
- useGetAllContentTypeSettingsQuery,
2605
- useUpdateContentTypeConfigurationMutation
2606
- } = contentTypesApi;
2607
- const checkIfAttributeIsDisplayable = (attribute) => {
2608
- const { type } = attribute;
2609
- if (type === "relation") {
2610
- return !attribute.relation.toLowerCase().includes("morph");
2611
- }
2612
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2613
- };
2614
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2615
- if (!mainFieldName) {
2616
- return void 0;
2617
- }
2618
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2619
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2620
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2621
- );
2622
- return {
2623
- name: mainFieldName,
2624
- type: mainFieldType ?? "string"
2625
- };
2626
- };
2627
- const DEFAULT_SETTINGS = {
2628
- bulkable: false,
2629
- filterable: false,
2630
- searchable: false,
2631
- pagination: false,
2632
- defaultSortBy: "",
2633
- defaultSortOrder: "asc",
2634
- mainField: "id",
2635
- pageSize: 10
2636
- };
2637
- const useDocumentLayout = (model) => {
2638
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2639
- const [{ query }] = useQueryParams();
2640
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2641
- const { toggleNotification } = useNotification();
2642
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2643
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2644
- const {
2645
- data,
2646
- isLoading: isLoadingConfigs,
2647
- error,
2648
- isFetching: isFetchingConfigs
2649
- } = useGetContentTypeConfigurationQuery(model);
2650
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2651
- React.useEffect(() => {
2652
- if (error) {
2653
- toggleNotification({
2654
- type: "danger",
2655
- message: formatAPIError(error)
2656
- });
2657
- }
2658
- }, [error, formatAPIError, toggleNotification]);
2659
- const editLayout = React.useMemo(
2660
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2661
- layout: [],
2662
- components: {},
2663
- metadatas: {},
2664
- options: {},
2665
- settings: DEFAULT_SETTINGS
2666
- },
2667
- [data, isLoading, schemas, schema, components]
2668
- );
2669
- const listLayout = React.useMemo(() => {
2670
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2671
- layout: [],
2672
- metadatas: {},
2673
- options: {},
2674
- settings: DEFAULT_SETTINGS
2675
- };
2676
- }, [data, isLoading, schemas, schema, components]);
2677
- const { layout: edit } = React.useMemo(
2678
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2679
- layout: editLayout,
2680
- query
2681
- }),
2682
- [editLayout, query, runHookWaterfall]
2683
- );
2684
- return {
2685
- error,
2686
- isLoading,
2687
- edit,
2688
- list: listLayout
2689
- };
2690
- };
2691
- const useDocLayout = () => {
2692
- const { model } = useDoc();
2693
- return useDocumentLayout(model);
2694
- };
2695
- const formatEditLayout = (data, {
2696
- schemas,
2697
- schema,
2698
- components
2699
- }) => {
2700
- let currentPanelIndex = 0;
2701
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2702
- data.contentType.layouts.edit,
2703
- schema?.attributes,
2704
- data.contentType.metadatas,
2705
- { configurations: data.components, schemas: components },
2706
- schemas
2707
- ).reduce((panels, row) => {
2708
- if (row.some((field) => field.type === "dynamiczone")) {
2709
- panels.push([row]);
2710
- currentPanelIndex += 2;
2711
- } else {
2712
- if (!panels[currentPanelIndex]) {
2713
- panels.push([]);
2714
- }
2715
- panels[currentPanelIndex].push(row);
2716
- }
2717
- return panels;
2718
- }, []);
2719
- const componentEditAttributes = Object.entries(data.components).reduce(
2720
- (acc, [uid, configuration]) => {
2721
- acc[uid] = {
2722
- layout: convertEditLayoutToFieldLayouts(
2723
- configuration.layouts.edit,
2724
- components[uid].attributes,
2725
- configuration.metadatas
2726
- ),
2727
- settings: {
2728
- ...configuration.settings,
2729
- icon: components[uid].info.icon,
2730
- displayName: components[uid].info.displayName
2731
- }
2732
- };
2733
- return acc;
2734
- },
2735
- {}
2736
- );
2737
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2738
- (acc, [attribute, metadata]) => {
2739
- return {
2740
- ...acc,
2741
- [attribute]: metadata.edit
2742
- };
2850
+ icon: /* @__PURE__ */ jsx(Pencil, {}),
2851
+ onClick: () => {
2852
+ navigate(`/plugins/content-type-builder/content-types/${model}`);
2743
2853
  },
2744
- {}
2745
- );
2854
+ position: "header"
2855
+ };
2856
+ };
2857
+ EditTheModelAction.type = "edit-the-model";
2858
+ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2859
+ const navigate = useNavigate();
2860
+ const { formatMessage } = useIntl();
2861
+ const listViewPathMatch = useMatch(LIST_PATH);
2862
+ const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
2863
+ const { delete: deleteAction } = useDocumentActions();
2864
+ const { toggleNotification } = useNotification();
2865
+ const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2866
+ const isLocalized = document?.locale != null;
2746
2867
  return {
2747
- layout: panelledEditAttributes,
2748
- components: componentEditAttributes,
2749
- metadatas: editMetadatas,
2750
- settings: {
2751
- ...data.contentType.settings,
2752
- displayName: schema?.info.displayName
2868
+ disabled: !canDelete || !document,
2869
+ label: formatMessage(
2870
+ {
2871
+ id: "content-manager.actions.delete.label",
2872
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2873
+ },
2874
+ { isLocalized }
2875
+ ),
2876
+ icon: /* @__PURE__ */ jsx(Trash, {}),
2877
+ dialog: {
2878
+ type: "dialog",
2879
+ title: formatMessage({
2880
+ id: "app.components.ConfirmDialog.title",
2881
+ defaultMessage: "Confirmation"
2882
+ }),
2883
+ content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
2884
+ /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
2885
+ /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
2886
+ id: "content-manager.actions.delete.dialog.body",
2887
+ defaultMessage: "Are you sure?"
2888
+ }) })
2889
+ ] }),
2890
+ onConfirm: async () => {
2891
+ if (!listViewPathMatch) {
2892
+ setSubmitting(true);
2893
+ }
2894
+ try {
2895
+ if (!documentId && collectionType !== SINGLE_TYPES) {
2896
+ console.error(
2897
+ "You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
2898
+ );
2899
+ toggleNotification({
2900
+ message: formatMessage({
2901
+ id: "content-manager.actions.delete.error",
2902
+ defaultMessage: "An error occurred while trying to delete the document."
2903
+ }),
2904
+ type: "danger"
2905
+ });
2906
+ return;
2907
+ }
2908
+ const res = await deleteAction({
2909
+ documentId,
2910
+ model,
2911
+ collectionType,
2912
+ params: {
2913
+ locale: "*"
2914
+ }
2915
+ });
2916
+ if (!("error" in res)) {
2917
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2918
+ }
2919
+ } finally {
2920
+ if (!listViewPathMatch) {
2921
+ setSubmitting(false);
2922
+ }
2923
+ }
2924
+ }
2753
2925
  },
2754
- options: {
2755
- ...schema?.options,
2756
- ...schema?.pluginOptions,
2757
- ...data.contentType.options
2758
- }
2926
+ variant: "danger",
2927
+ position: ["header", "table-row"]
2759
2928
  };
2760
2929
  };
2761
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2762
- return rows.map(
2763
- (row) => row.map((field) => {
2764
- const attribute = attributes[field.name];
2765
- if (!attribute) {
2766
- return null;
2767
- }
2768
- const { edit: metadata } = metadatas[field.name];
2769
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2770
- return {
2771
- attribute,
2772
- disabled: !metadata.editable,
2773
- hint: metadata.description,
2774
- label: metadata.label ?? "",
2775
- name: field.name,
2776
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2777
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2778
- schemas,
2779
- components: components?.schemas ?? {}
2780
- }),
2781
- placeholder: metadata.placeholder ?? "",
2782
- required: attribute.required ?? false,
2783
- size: field.size,
2784
- unique: "unique" in attribute ? attribute.unique : false,
2785
- visible: metadata.visible ?? true,
2786
- type: attribute.type
2787
- };
2788
- }).filter((field) => field !== null)
2789
- );
2930
+ DeleteAction$1.type = "delete";
2931
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2932
+ const Panels = () => {
2933
+ const isCloning = useMatch(CLONE_PATH) !== null;
2934
+ const [
2935
+ {
2936
+ query: { status }
2937
+ }
2938
+ ] = useQueryParams({
2939
+ status: "draft"
2940
+ });
2941
+ const { model, id, document, meta, collectionType } = useDoc();
2942
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
2943
+ const props = {
2944
+ activeTab: status,
2945
+ model,
2946
+ documentId: id,
2947
+ document: isCloning ? void 0 : document,
2948
+ meta: isCloning ? void 0 : meta,
2949
+ collectionType
2950
+ };
2951
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2952
+ DescriptionComponentRenderer,
2953
+ {
2954
+ props,
2955
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2956
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2957
+ }
2958
+ ) });
2790
2959
  };
2791
- const formatListLayout = (data, {
2792
- schemas,
2793
- schema,
2794
- components
2795
- }) => {
2796
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2797
- (acc, [attribute, metadata]) => {
2798
- return {
2799
- ...acc,
2800
- [attribute]: metadata.list
2801
- };
2802
- },
2803
- {}
2804
- );
2805
- const listAttributes = convertListLayoutToFieldLayouts(
2806
- data.contentType.layouts.list,
2807
- schema?.attributes,
2808
- listMetadatas,
2809
- { configurations: data.components, schemas: components },
2810
- schemas
2811
- );
2960
+ const ActionsPanel = () => {
2961
+ const { formatMessage } = useIntl();
2812
2962
  return {
2813
- layout: listAttributes,
2814
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2815
- metadatas: listMetadatas,
2816
- options: {
2817
- ...schema?.options,
2818
- ...schema?.pluginOptions,
2819
- ...data.contentType.options
2820
- }
2963
+ title: formatMessage({
2964
+ id: "content-manager.containers.edit.panels.default.title",
2965
+ defaultMessage: "Entry"
2966
+ }),
2967
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2821
2968
  };
2822
2969
  };
2823
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2824
- return columns.map((name) => {
2825
- const attribute = attributes[name];
2826
- if (!attribute) {
2827
- return null;
2970
+ ActionsPanel.type = "actions";
2971
+ const ActionsPanelContent = () => {
2972
+ const isCloning = useMatch(CLONE_PATH) !== null;
2973
+ const [
2974
+ {
2975
+ query: { status = "draft" }
2828
2976
  }
2829
- const metadata = metadatas[name];
2830
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2831
- return {
2832
- attribute,
2833
- label: metadata.label ?? "",
2834
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2835
- schemas,
2836
- components: components?.schemas ?? {}
2837
- }),
2838
- name,
2839
- searchable: metadata.searchable ?? true,
2840
- sortable: metadata.sortable ?? true
2841
- };
2842
- }).filter((field) => field !== null);
2977
+ ] = useQueryParams();
2978
+ const { model, id, document, meta, collectionType } = useDoc();
2979
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2980
+ const props = {
2981
+ activeTab: status,
2982
+ model,
2983
+ documentId: id,
2984
+ document: isCloning ? void 0 : document,
2985
+ meta: isCloning ? void 0 : meta,
2986
+ collectionType
2987
+ };
2988
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2989
+ /* @__PURE__ */ jsx(
2990
+ DescriptionComponentRenderer,
2991
+ {
2992
+ props,
2993
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
2994
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2995
+ }
2996
+ ),
2997
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2998
+ ] });
2843
2999
  };
3000
+ const Panel = React.forwardRef(({ children, title }, ref) => {
3001
+ return /* @__PURE__ */ jsxs(
3002
+ Flex,
3003
+ {
3004
+ ref,
3005
+ tag: "aside",
3006
+ "aria-labelledby": "additional-information",
3007
+ background: "neutral0",
3008
+ borderColor: "neutral150",
3009
+ hasRadius: true,
3010
+ paddingBottom: 4,
3011
+ paddingLeft: 4,
3012
+ paddingRight: 4,
3013
+ paddingTop: 4,
3014
+ shadow: "tableShadow",
3015
+ gap: 3,
3016
+ direction: "column",
3017
+ justifyContent: "stretch",
3018
+ alignItems: "flex-start",
3019
+ children: [
3020
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3021
+ children
3022
+ ]
3023
+ }
3024
+ );
3025
+ });
2844
3026
  const ConfirmBulkActionDialog = ({
2845
3027
  onToggleDialog,
2846
3028
  isOpen = false,
@@ -2879,6 +3061,7 @@ const ConfirmDialogPublishAll = ({
2879
3061
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2880
3062
  const { model, schema } = useDoc();
2881
3063
  const [{ query }] = useQueryParams();
3064
+ const enableDraftRelationsCount = false;
2882
3065
  const {
2883
3066
  data: countDraftRelations = 0,
2884
3067
  isLoading,
@@ -2890,7 +3073,7 @@ const ConfirmDialogPublishAll = ({
2890
3073
  locale: query?.plugins?.i18n?.locale
2891
3074
  },
2892
3075
  {
2893
- skip: selectedEntries.length === 0
3076
+ skip: !enableDraftRelationsCount
2894
3077
  }
2895
3078
  );
2896
3079
  React.useEffect(() => {
@@ -3075,7 +3258,7 @@ const SelectedEntriesTableContent = ({
3075
3258
  status: row.status
3076
3259
  }
3077
3260
  ) }),
3078
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3261
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3079
3262
  IconButton,
3080
3263
  {
3081
3264
  tag: Link,
@@ -3098,9 +3281,10 @@ const SelectedEntriesTableContent = ({
3098
3281
  ),
3099
3282
  target: "_blank",
3100
3283
  marginLeft: "auto",
3101
- children: /* @__PURE__ */ jsx(Pencil, {})
3284
+ variant: "ghost",
3285
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3102
3286
  }
3103
- ) })
3287
+ ) }) })
3104
3288
  ] }, row.id)) })
3105
3289
  ] });
3106
3290
  };
@@ -3137,7 +3321,13 @@ const SelectedEntriesModalContent = ({
3137
3321
  );
3138
3322
  const { rows, validationErrors } = React.useMemo(() => {
3139
3323
  if (data.length > 0 && schema) {
3140
- const validate = createYupSchema(schema.attributes, components);
3324
+ const validate = createYupSchema(
3325
+ schema.attributes,
3326
+ components,
3327
+ // Since this is the "Publish" action, the validation
3328
+ // schema must enforce the rules for published entities
3329
+ { status: "published" }
3330
+ );
3141
3331
  const validationErrors2 = {};
3142
3332
  const rows2 = data.map((entry) => {
3143
3333
  try {
@@ -3487,7 +3677,7 @@ const TableActions = ({ document }) => {
3487
3677
  DescriptionComponentRenderer,
3488
3678
  {
3489
3679
  props,
3490
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3680
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3491
3681
  children: (actions2) => {
3492
3682
  const tableRowActions = actions2.filter((action) => {
3493
3683
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3598,7 +3788,7 @@ const CloneAction = ({ model, documentId }) => {
3598
3788
  }),
3599
3789
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3600
3790
  footer: ({ onClose }) => {
3601
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3791
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3602
3792
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3603
3793
  id: "cancel",
3604
3794
  defaultMessage: "Cancel"
@@ -3810,6 +4000,15 @@ const { setInitialData } = actions;
3810
4000
  const reducer = combineReducers({
3811
4001
  app: reducer$1
3812
4002
  });
4003
+ const FEATURE_ID = "preview";
4004
+ const previewAdmin = {
4005
+ bootstrap(app) {
4006
+ if (!window.strapi.future.isEnabled(FEATURE_ID)) {
4007
+ return {};
4008
+ }
4009
+ console.log("Bootstrapping preview admin");
4010
+ }
4011
+ };
3813
4012
  const index = {
3814
4013
  register(app) {
3815
4014
  const cm = new ContentManagerPlugin();
@@ -3829,7 +4028,7 @@ const index = {
3829
4028
  app.router.addRoute({
3830
4029
  path: "content-manager/*",
3831
4030
  lazy: async () => {
3832
- const { Layout } = await import("./layout-DPaHUusj.mjs");
4031
+ const { Layout } = await import("./layout-ByD1cQSW.mjs");
3833
4032
  return {
3834
4033
  Component: Layout
3835
4034
  };
@@ -3842,11 +4041,14 @@ const index = {
3842
4041
  if (typeof historyAdmin.bootstrap === "function") {
3843
4042
  historyAdmin.bootstrap(app);
3844
4043
  }
4044
+ if (typeof previewAdmin.bootstrap === "function") {
4045
+ previewAdmin.bootstrap(app);
4046
+ }
3845
4047
  },
3846
4048
  async registerTrads({ locales }) {
3847
4049
  const importedTrads = await Promise.all(
3848
4050
  locales.map((locale) => {
3849
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-BrCTWlZv.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
4051
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-DKV44jRb.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
3850
4052
  return {
3851
4053
  data: prefixPluginTranslations(data, PLUGIN_ID),
3852
4054
  locale
@@ -3867,13 +4069,15 @@ export {
3867
4069
  BulkActionsRenderer as B,
3868
4070
  COLLECTION_TYPES as C,
3869
4071
  DocumentStatus as D,
3870
- DEFAULT_SETTINGS as E,
3871
- convertEditLayoutToFieldLayouts as F,
3872
- useDocument as G,
4072
+ extractContentTypeComponents as E,
4073
+ DEFAULT_SETTINGS as F,
4074
+ convertEditLayoutToFieldLayouts as G,
3873
4075
  HOOKS as H,
3874
4076
  InjectionZone as I,
3875
- index as J,
3876
- useDocumentActions as K,
4077
+ useDocument as J,
4078
+ index as K,
4079
+ useContentManagerContext as L,
4080
+ useDocumentActions as M,
3877
4081
  Panels as P,
3878
4082
  RelativeTime as R,
3879
4083
  SINGLE_TYPES as S,
@@ -3891,18 +4095,18 @@ export {
3891
4095
  PERMISSIONS as k,
3892
4096
  DocumentRBAC as l,
3893
4097
  DOCUMENT_META_FIELDS as m,
3894
- useDocLayout as n,
3895
- useGetContentTypeConfigurationQuery as o,
3896
- CREATOR_FIELDS as p,
3897
- getMainField as q,
3898
- getDisplayName as r,
4098
+ CLONE_PATH as n,
4099
+ useDocLayout as o,
4100
+ useGetContentTypeConfigurationQuery as p,
4101
+ CREATOR_FIELDS as q,
4102
+ getMainField as r,
3899
4103
  setInitialData as s,
3900
- checkIfAttributeIsDisplayable as t,
4104
+ getDisplayName as t,
3901
4105
  useContentTypeSchema as u,
3902
- useGetAllDocumentsQuery as v,
3903
- convertListLayoutToFieldLayouts as w,
3904
- capitalise as x,
3905
- useUpdateContentTypeConfigurationMutation as y,
3906
- extractContentTypeComponents as z
4106
+ checkIfAttributeIsDisplayable as v,
4107
+ useGetAllDocumentsQuery as w,
4108
+ convertListLayoutToFieldLayouts as x,
4109
+ capitalise as y,
4110
+ useUpdateContentTypeConfigurationMutation as z
3907
4111
  };
3908
- //# sourceMappingURL=index-BSn97i8U.mjs.map
4112
+ //# sourceMappingURL=index-1Bdj-6ls.mjs.map