@strapi/content-manager 0.0.0-experimental.edc24aaa3bb5a90fa5fd4fee208167dd4e2e38d4 → 0.0.0-experimental.f0d4afee92a0d386f80385590c87955656f995ce

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