@strapi/content-manager 0.0.0-experimental.145e7d7ddefd1aef71aaf3d9bb86440d013035bf → 0.0.0-experimental.146a31b564bc8232686331f6b28f7ff966817963

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 (196) hide show
  1. package/dist/_chunks/CardDragPreview-C0QyJgRA.js.map +1 -1
  2. package/dist/_chunks/CardDragPreview-DOxamsuj.mjs.map +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-D_g11bYV.js → ComponentConfigurationPage-BSEZcJVB.js} +5 -6
  4. package/dist/_chunks/{ComponentConfigurationPage-D_g11bYV.js.map → ComponentConfigurationPage-BSEZcJVB.js.map} +1 -1
  5. package/dist/_chunks/{ComponentConfigurationPage-DJEJ49QD.mjs → ComponentConfigurationPage-BiASGi7x.mjs} +4 -4
  6. package/dist/_chunks/{ComponentConfigurationPage-DJEJ49QD.mjs.map → ComponentConfigurationPage-BiASGi7x.mjs.map} +1 -1
  7. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js → ComponentIcon-CRbtQEUV.js} +2 -3
  8. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js.map → ComponentIcon-CRbtQEUV.js.map} +1 -1
  9. package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -1
  10. package/dist/_chunks/{EditConfigurationPage-CeL712KW.js → EditConfigurationPage-D2rtvneE.js} +5 -6
  11. package/dist/_chunks/{EditConfigurationPage-CeL712KW.js.map → EditConfigurationPage-D2rtvneE.js.map} +1 -1
  12. package/dist/_chunks/{EditConfigurationPage-QBZdUYyG.mjs → EditConfigurationPage-vN4zupij.mjs} +4 -4
  13. package/dist/_chunks/{EditConfigurationPage-QBZdUYyG.mjs.map → EditConfigurationPage-vN4zupij.mjs.map} +1 -1
  14. package/dist/_chunks/{EditViewPage-g5TwrgRY.js → EditViewPage-BwisF04Q.js} +50 -11
  15. package/dist/_chunks/EditViewPage-BwisF04Q.js.map +1 -0
  16. package/dist/_chunks/{EditViewPage-CvRUUpVh.mjs → EditViewPage-_A31Cl4g.mjs} +50 -10
  17. package/dist/_chunks/EditViewPage-_A31Cl4g.mjs.map +1 -0
  18. package/dist/_chunks/{Field-reyvfnop.mjs → Field-CvIunNOj.mjs} +238 -152
  19. package/dist/_chunks/Field-CvIunNOj.mjs.map +1 -0
  20. package/dist/_chunks/{Field-ncdInvxS.js → Field-Dsu6-FrM.js} +244 -158
  21. package/dist/_chunks/Field-Dsu6-FrM.js.map +1 -0
  22. package/dist/_chunks/FieldTypeIcon-CMlNO8PE.mjs.map +1 -1
  23. package/dist/_chunks/FieldTypeIcon-Dnwq_IRF.js.map +1 -1
  24. package/dist/_chunks/{Form-DoMGsYxH.mjs → Form-DK0fG0Gj.mjs} +15 -9
  25. package/dist/_chunks/Form-DK0fG0Gj.mjs.map +1 -0
  26. package/dist/_chunks/{Form-BJ7bYiUx.js → Form-DUwWcCmA.js} +17 -12
  27. package/dist/_chunks/Form-DUwWcCmA.js.map +1 -0
  28. package/dist/_chunks/{History-pbhkxIrf.js → History-CeCDhoJG.js} +42 -100
  29. package/dist/_chunks/History-CeCDhoJG.js.map +1 -0
  30. package/dist/_chunks/{History-BseDJOrj.mjs → History-DP8gmXpm.mjs} +43 -100
  31. package/dist/_chunks/History-DP8gmXpm.mjs.map +1 -0
  32. package/dist/_chunks/{ListConfigurationPage-DWE_fr5B.mjs → ListConfigurationPage-BCkO5iuN.mjs} +7 -6
  33. package/dist/_chunks/ListConfigurationPage-BCkO5iuN.mjs.map +1 -0
  34. package/dist/_chunks/{ListConfigurationPage-Bna8zfjr.js → ListConfigurationPage-C-bAd44a.js} +7 -7
  35. package/dist/_chunks/ListConfigurationPage-C-bAd44a.js.map +1 -0
  36. package/dist/_chunks/{ListViewPage-Dymsvnv6.js → ListViewPage-BKTZFhsM.js} +88 -54
  37. package/dist/_chunks/ListViewPage-BKTZFhsM.js.map +1 -0
  38. package/dist/_chunks/{ListViewPage-lQ-VLV2G.mjs → ListViewPage-Cf_DgaFV.mjs} +83 -48
  39. package/dist/_chunks/ListViewPage-Cf_DgaFV.mjs.map +1 -0
  40. package/dist/_chunks/{NoContentTypePage-B4t_OsDR.js → NoContentTypePage-D3Cm3v3q.js} +2 -2
  41. package/dist/_chunks/{NoContentTypePage-B4t_OsDR.js.map → NoContentTypePage-D3Cm3v3q.js.map} +1 -1
  42. package/dist/_chunks/{NoContentTypePage-VCQOMwlf.mjs → NoContentTypePage-nHIyvJcB.mjs} +2 -2
  43. package/dist/_chunks/{NoContentTypePage-VCQOMwlf.mjs.map → NoContentTypePage-nHIyvJcB.mjs.map} +1 -1
  44. package/dist/_chunks/{NoPermissionsPage-TV830k4P.mjs → NoPermissionsPage-BALVSJ7x.mjs} +2 -2
  45. package/dist/_chunks/{NoPermissionsPage-TV830k4P.mjs.map → NoPermissionsPage-BALVSJ7x.mjs.map} +1 -1
  46. package/dist/_chunks/{NoPermissionsPage-BOwB6hki.js → NoPermissionsPage-CChGWBj5.js} +2 -2
  47. package/dist/_chunks/{NoPermissionsPage-BOwB6hki.js.map → NoPermissionsPage-CChGWBj5.js.map} +1 -1
  48. package/dist/_chunks/Preview-C4NBzKUV.mjs +294 -0
  49. package/dist/_chunks/Preview-C4NBzKUV.mjs.map +1 -0
  50. package/dist/_chunks/Preview-CT28Ckpg.js +312 -0
  51. package/dist/_chunks/Preview-CT28Ckpg.js.map +1 -0
  52. package/dist/_chunks/{Relations-D6NAlnsl.mjs → Relations-C8uC89cT.mjs} +75 -41
  53. package/dist/_chunks/Relations-C8uC89cT.mjs.map +1 -0
  54. package/dist/_chunks/{Relations-DdlstXTu.js → Relations-CvkPCng_.js} +75 -42
  55. package/dist/_chunks/Relations-CvkPCng_.js.map +1 -0
  56. package/dist/_chunks/{en-Cf41pH5f.js → en-BK8Xyl5I.js} +24 -12
  57. package/dist/_chunks/{en-Cf41pH5f.js.map → en-BK8Xyl5I.js.map} +1 -1
  58. package/dist/_chunks/{en-DCszE74t.mjs → en-Dtk_ot79.mjs} +24 -12
  59. package/dist/_chunks/{en-DCszE74t.mjs.map → en-Dtk_ot79.mjs.map} +1 -1
  60. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  61. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  62. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  63. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  64. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  65. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  66. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  67. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  68. package/dist/_chunks/hooks-BAaaKPS_.js.map +1 -1
  69. package/dist/_chunks/{index-CQos-KS0.js → index-CnX_j5h-.js} +1239 -942
  70. package/dist/_chunks/index-CnX_j5h-.js.map +1 -0
  71. package/dist/_chunks/{index-BYSWwHBJ.mjs → index-Dh2aGTGJ.mjs} +1241 -944
  72. package/dist/_chunks/index-Dh2aGTGJ.mjs.map +1 -0
  73. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  74. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  75. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  76. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  77. package/dist/_chunks/{layout-0TY7UtKO.mjs → layout-B5qsPihj.mjs} +6 -5
  78. package/dist/_chunks/{layout-0TY7UtKO.mjs.map → layout-B5qsPihj.mjs.map} +1 -1
  79. package/dist/_chunks/{layout-B4XAqu1v.js → layout-B_qdWGny.js} +7 -7
  80. package/dist/_chunks/{layout-B4XAqu1v.js.map → layout-B_qdWGny.js.map} +1 -1
  81. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  82. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  83. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  84. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  85. package/dist/_chunks/{relations-xZ2tMj1G.js → relations-ChcieiF5.js} +6 -7
  86. package/dist/_chunks/relations-ChcieiF5.js.map +1 -0
  87. package/dist/_chunks/{relations-DFDWfa0s.mjs → relations-DMXpNY-e.mjs} +6 -7
  88. package/dist/_chunks/relations-DMXpNY-e.mjs.map +1 -0
  89. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  90. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  91. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  92. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  93. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js → useDragAndDrop-BMtgCYzL.js} +5 -9
  94. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js.map → useDragAndDrop-BMtgCYzL.js.map} +1 -1
  95. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs → useDragAndDrop-DJ6jqvZN.mjs} +4 -7
  96. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs.map → useDragAndDrop-DJ6jqvZN.mjs.map} +1 -1
  97. package/dist/admin/index.js +2 -1
  98. package/dist/admin/index.js.map +1 -1
  99. package/dist/admin/index.mjs +4 -3
  100. package/dist/admin/src/content-manager.d.ts +3 -2
  101. package/dist/admin/src/exports.d.ts +1 -1
  102. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  103. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  104. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -1
  105. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +2 -2
  106. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Code.d.ts +7 -0
  107. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/utils/prismLanguages.d.ts +49 -0
  108. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +1 -0
  109. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  110. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  111. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  112. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  113. package/dist/admin/src/preview/index.d.ts +4 -0
  114. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  115. package/dist/admin/src/preview/routes.d.ts +3 -0
  116. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  117. package/dist/admin/src/router.d.ts +1 -1
  118. package/dist/admin/src/services/documents.d.ts +0 -3
  119. package/dist/server/index.js +513 -260
  120. package/dist/server/index.js.map +1 -1
  121. package/dist/server/index.mjs +514 -260
  122. package/dist/server/index.mjs.map +1 -1
  123. package/dist/server/src/bootstrap.d.ts.map +1 -1
  124. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  125. package/dist/server/src/controllers/index.d.ts.map +1 -1
  126. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  127. package/dist/server/src/controllers/utils/metadata.d.ts +16 -1
  128. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  129. package/dist/server/src/history/services/history.d.ts.map +1 -1
  130. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  131. package/dist/server/src/history/services/utils.d.ts +2 -3
  132. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  133. package/dist/server/src/index.d.ts +7 -6
  134. package/dist/server/src/index.d.ts.map +1 -1
  135. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  136. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  137. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  138. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  139. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  140. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  141. package/dist/server/src/preview/index.d.ts +4 -0
  142. package/dist/server/src/preview/index.d.ts.map +1 -0
  143. package/dist/server/src/preview/routes/index.d.ts +8 -0
  144. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  145. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  146. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  147. package/dist/server/src/preview/services/index.d.ts +16 -0
  148. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  149. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  150. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  151. package/dist/server/src/preview/services/preview.d.ts +12 -0
  152. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  153. package/dist/server/src/preview/utils.d.ts +19 -0
  154. package/dist/server/src/preview/utils.d.ts.map +1 -0
  155. package/dist/server/src/register.d.ts.map +1 -1
  156. package/dist/server/src/routes/index.d.ts.map +1 -1
  157. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  158. package/dist/server/src/services/document-metadata.d.ts +12 -10
  159. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  160. package/dist/server/src/services/index.d.ts +7 -6
  161. package/dist/server/src/services/index.d.ts.map +1 -1
  162. package/dist/server/src/services/utils/populate.d.ts +2 -2
  163. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  164. package/dist/server/src/utils/index.d.ts +2 -0
  165. package/dist/server/src/utils/index.d.ts.map +1 -1
  166. package/dist/shared/contracts/index.d.ts +1 -0
  167. package/dist/shared/contracts/index.d.ts.map +1 -1
  168. package/dist/shared/contracts/preview.d.ts +27 -0
  169. package/dist/shared/contracts/preview.d.ts.map +1 -0
  170. package/dist/shared/index.js +4 -0
  171. package/dist/shared/index.js.map +1 -1
  172. package/dist/shared/index.mjs +4 -0
  173. package/dist/shared/index.mjs.map +1 -1
  174. package/package.json +17 -15
  175. package/dist/_chunks/EditViewPage-CvRUUpVh.mjs.map +0 -1
  176. package/dist/_chunks/EditViewPage-g5TwrgRY.js.map +0 -1
  177. package/dist/_chunks/Field-ncdInvxS.js.map +0 -1
  178. package/dist/_chunks/Field-reyvfnop.mjs.map +0 -1
  179. package/dist/_chunks/Form-BJ7bYiUx.js.map +0 -1
  180. package/dist/_chunks/Form-DoMGsYxH.mjs.map +0 -1
  181. package/dist/_chunks/History-BseDJOrj.mjs.map +0 -1
  182. package/dist/_chunks/History-pbhkxIrf.js.map +0 -1
  183. package/dist/_chunks/ListConfigurationPage-Bna8zfjr.js.map +0 -1
  184. package/dist/_chunks/ListConfigurationPage-DWE_fr5B.mjs.map +0 -1
  185. package/dist/_chunks/ListViewPage-Dymsvnv6.js.map +0 -1
  186. package/dist/_chunks/ListViewPage-lQ-VLV2G.mjs.map +0 -1
  187. package/dist/_chunks/Relations-D6NAlnsl.mjs.map +0 -1
  188. package/dist/_chunks/Relations-DdlstXTu.js.map +0 -1
  189. package/dist/_chunks/index-BYSWwHBJ.mjs.map +0 -1
  190. package/dist/_chunks/index-CQos-KS0.js.map +0 -1
  191. package/dist/_chunks/relations-DFDWfa0s.mjs.map +0 -1
  192. package/dist/_chunks/relations-xZ2tMj1G.js.map +0 -1
  193. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  194. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  195. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  196. package/strapi-server.js +0 -3
@@ -1,25 +1,33 @@
1
1
  import { More, Cross, WarningCircle, ListPlus, Pencil, Trash, Check, CrossCircle, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
2
2
  import { jsx, Fragment, jsxs } from "react/jsx-runtime";
3
- import { useStrapiApp, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useQueryParams, useTracking, useForm, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
3
+ import { useStrapiApp, createContext, 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, Typography, Dialog, Modal, Radio, Status, Box, SingleSelect, SingleSelectOption, Loader, IconButton, Tooltip, LinkButton } from "@strapi/design-system";
6
+ import { Menu, Button, VisuallyHidden, Flex, Dialog, Modal, Typography, 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
9
  import { useParams, useNavigate, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
10
+ import { styled } from "styled-components";
9
11
  import * as yup from "yup";
10
12
  import { ValidationError } from "yup";
13
+ import { stringify } from "qs";
11
14
  import pipe from "lodash/fp/pipe";
12
15
  import { intervalToDuration, isPast } from "date-fns";
13
- import { styled } from "styled-components";
14
- import { stringify } from "qs";
15
16
  import { createSlice, combineReducers } from "@reduxjs/toolkit";
16
- const __variableDynamicImportRuntimeHelper = (glob, path) => {
17
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
17
18
  const v = glob[path];
18
19
  if (v) {
19
20
  return typeof v === "function" ? v() : Promise.resolve(v);
20
21
  }
21
22
  return new Promise((_, reject) => {
22
- (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(reject.bind(null, new Error("Unknown variable dynamic import: " + path)));
23
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
24
+ reject.bind(
25
+ null,
26
+ new Error(
27
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
28
+ )
29
+ )
30
+ );
23
31
  });
24
32
  };
25
33
  const PLUGIN_ID = "content-manager";
@@ -100,6 +108,7 @@ const DocumentRBAC = ({ children, permissions }) => {
100
108
  if (!slug) {
101
109
  throw new Error("Cannot find the slug param in the URL");
102
110
  }
111
+ const [{ rawQuery }] = useQueryParams();
103
112
  const userPermissions = useAuth("DocumentRBAC", (state) => state.permissions);
104
113
  const contentTypePermissions = React.useMemo(() => {
105
114
  const contentTypePermissions2 = userPermissions.filter(
@@ -110,7 +119,14 @@ const DocumentRBAC = ({ children, permissions }) => {
110
119
  return { ...acc, [action]: [permission] };
111
120
  }, {});
112
121
  }, [slug, userPermissions]);
113
- const { isLoading, allowedActions } = useRBAC(contentTypePermissions, permissions ?? void 0);
122
+ const { isLoading, allowedActions } = useRBAC(
123
+ contentTypePermissions,
124
+ permissions ?? void 0,
125
+ // TODO: useRBAC context should be typed and built differently
126
+ // We are passing raw query as context to the hook so that it can
127
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
128
+ rawQuery
129
+ );
114
130
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
115
131
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
116
132
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -211,7 +227,19 @@ const documentApi = contentManagerApi.injectEndpoints({
211
227
  { type: "Document", id: `${model}_LIST` },
212
228
  "Relations",
213
229
  { type: "UidAvailability", id: model }
214
- ]
230
+ ],
231
+ transformResponse: (response, meta, arg) => {
232
+ if (!("data" in response) && arg.model === "plugin::users-permissions.user") {
233
+ return {
234
+ data: response,
235
+ meta: {
236
+ availableStatus: [],
237
+ availableLocales: []
238
+ }
239
+ };
240
+ }
241
+ return response;
242
+ }
215
243
  }),
216
244
  deleteDocument: builder.mutation({
217
245
  query: ({ collectionType, model, documentId, params }) => ({
@@ -265,7 +293,7 @@ const documentApi = contentManagerApi.injectEndpoints({
265
293
  url: `/content-manager/collection-types/${model}`,
266
294
  method: "GET",
267
295
  config: {
268
- params
296
+ params: stringify(params, { encode: true })
269
297
  }
270
298
  }),
271
299
  providesTags: (result, _error, arg) => {
@@ -444,8 +472,7 @@ const {
444
472
  useUnpublishManyDocumentsMutation
445
473
  } = documentApi;
446
474
  const buildValidParams = (query) => {
447
- if (!query)
448
- return query;
475
+ if (!query) return query;
449
476
  const { plugins: _, ...validQueryParams } = {
450
477
  ...query,
451
478
  ...Object.values(query?.plugins ?? {}).reduce(
@@ -453,14 +480,29 @@ const buildValidParams = (query) => {
453
480
  {}
454
481
  )
455
482
  };
456
- if ("_q" in validQueryParams) {
457
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
458
- }
459
483
  return validQueryParams;
460
484
  };
461
485
  const isBaseQueryError = (error) => {
462
486
  return error.name !== void 0;
463
487
  };
488
+ const arrayValidator = (attribute, options) => ({
489
+ message: translatedErrors.required,
490
+ test(value) {
491
+ if (options.status === "draft") {
492
+ return true;
493
+ }
494
+ if (!attribute.required) {
495
+ return true;
496
+ }
497
+ if (!value) {
498
+ return false;
499
+ }
500
+ if (Array.isArray(value) && value.length === 0) {
501
+ return false;
502
+ }
503
+ return true;
504
+ }
505
+ });
464
506
  const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
465
507
  const createModelSchema = (attributes2) => yup.object().shape(
466
508
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
@@ -468,6 +510,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
468
510
  return acc;
469
511
  }
470
512
  const validations = [
513
+ addNullableValidation,
471
514
  addRequiredValidation,
472
515
  addMinLengthValidation,
473
516
  addMaxLengthValidation,
@@ -484,12 +527,12 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
484
527
  ...acc,
485
528
  [name]: transformSchema(
486
529
  yup.array().of(createModelSchema(attributes3).nullable(false))
487
- )
530
+ ).test(arrayValidator(attribute, options))
488
531
  };
489
532
  } else {
490
533
  return {
491
534
  ...acc,
492
- [name]: transformSchema(createModelSchema(attributes3))
535
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
493
536
  };
494
537
  }
495
538
  }
@@ -511,7 +554,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
511
554
  }
512
555
  )
513
556
  )
514
- )
557
+ ).test(arrayValidator(attribute, options))
515
558
  };
516
559
  case "relation":
517
560
  return {
@@ -523,7 +566,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
523
566
  } else if (Array.isArray(value)) {
524
567
  return yup.array().of(
525
568
  yup.object().shape({
526
- id: yup.string().required()
569
+ id: yup.number().required()
527
570
  })
528
571
  );
529
572
  } else if (typeof value === "object") {
@@ -609,17 +652,17 @@ const nullableSchema = (schema) => {
609
652
  schema
610
653
  );
611
654
  };
655
+ const addNullableValidation = () => (schema) => {
656
+ return nullableSchema(schema);
657
+ };
612
658
  const addRequiredValidation = (attribute, options) => (schema) => {
613
- if (options.status === "draft") {
614
- return nullableSchema(schema);
615
- }
616
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
617
- return schema.min(1, translatedErrors.required);
659
+ if (options.status === "draft" || !attribute.required) {
660
+ return schema;
618
661
  }
619
- if (attribute.required && attribute.type !== "relation") {
662
+ if (attribute.required && "required" in schema) {
620
663
  return schema.required(translatedErrors.required);
621
664
  }
622
- return nullableSchema(schema);
665
+ return schema;
623
666
  };
624
667
  const addMinLengthValidation = (attribute, options) => (schema) => {
625
668
  if (options.status === "draft") {
@@ -647,31 +690,12 @@ const addMaxLengthValidation = (attribute) => (schema) => {
647
690
  return schema;
648
691
  };
649
692
  const addMinValidation = (attribute, options) => (schema) => {
650
- if ("min" in attribute) {
693
+ if (options.status === "draft") {
694
+ return schema;
695
+ }
696
+ if ("min" in attribute && "min" in schema) {
651
697
  const min = toInteger(attribute.min);
652
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
653
- if (options.status !== "draft" && !attribute.required && "test" in schema && min) {
654
- return schema.test(
655
- "custom-min",
656
- {
657
- ...translatedErrors.min,
658
- values: {
659
- min: attribute.min
660
- }
661
- },
662
- (value) => {
663
- if (!value) {
664
- return true;
665
- }
666
- if (Array.isArray(value) && value.length === 0) {
667
- return true;
668
- }
669
- return value.length >= min;
670
- }
671
- );
672
- }
673
- }
674
- if ("min" in schema && min) {
698
+ if (min) {
675
699
  return schema.min(min, {
676
700
  ...translatedErrors.min,
677
701
  values: {
@@ -789,19 +813,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
789
813
  }, {});
790
814
  return componentsByKey;
791
815
  };
792
- const useDocument = (args, opts) => {
816
+ const HOOKS = {
817
+ /**
818
+ * Hook that allows to mutate the displayed headers of the list view table
819
+ * @constant
820
+ * @type {string}
821
+ */
822
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
823
+ /**
824
+ * Hook that allows to mutate the CM's collection types links pre-set filters
825
+ * @constant
826
+ * @type {string}
827
+ */
828
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
829
+ /**
830
+ * Hook that allows to mutate the CM's edit view layout
831
+ * @constant
832
+ * @type {string}
833
+ */
834
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
835
+ /**
836
+ * Hook that allows to mutate the CM's single types links pre-set filters
837
+ * @constant
838
+ * @type {string}
839
+ */
840
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
841
+ };
842
+ const contentTypesApi = contentManagerApi.injectEndpoints({
843
+ endpoints: (builder) => ({
844
+ getContentTypeConfiguration: builder.query({
845
+ query: (uid) => ({
846
+ url: `/content-manager/content-types/${uid}/configuration`,
847
+ method: "GET"
848
+ }),
849
+ transformResponse: (response) => response.data,
850
+ providesTags: (_result, _error, uid) => [
851
+ { type: "ContentTypesConfiguration", id: uid },
852
+ { type: "ContentTypeSettings", id: "LIST" }
853
+ ]
854
+ }),
855
+ getAllContentTypeSettings: builder.query({
856
+ query: () => "/content-manager/content-types-settings",
857
+ transformResponse: (response) => response.data,
858
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
859
+ }),
860
+ updateContentTypeConfiguration: builder.mutation({
861
+ query: ({ uid, ...body }) => ({
862
+ url: `/content-manager/content-types/${uid}/configuration`,
863
+ method: "PUT",
864
+ data: body
865
+ }),
866
+ transformResponse: (response) => response.data,
867
+ invalidatesTags: (_result, _error, { uid }) => [
868
+ { type: "ContentTypesConfiguration", id: uid },
869
+ { type: "ContentTypeSettings", id: "LIST" },
870
+ // Is this necessary?
871
+ { type: "InitialData" }
872
+ ]
873
+ })
874
+ })
875
+ });
876
+ const {
877
+ useGetContentTypeConfigurationQuery,
878
+ useGetAllContentTypeSettingsQuery,
879
+ useUpdateContentTypeConfigurationMutation
880
+ } = contentTypesApi;
881
+ const checkIfAttributeIsDisplayable = (attribute) => {
882
+ const { type } = attribute;
883
+ if (type === "relation") {
884
+ return !attribute.relation.toLowerCase().includes("morph");
885
+ }
886
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
887
+ };
888
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
889
+ if (!mainFieldName) {
890
+ return void 0;
891
+ }
892
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
893
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
894
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
895
+ );
896
+ return {
897
+ name: mainFieldName,
898
+ type: mainFieldType ?? "string"
899
+ };
900
+ };
901
+ const DEFAULT_SETTINGS = {
902
+ bulkable: false,
903
+ filterable: false,
904
+ searchable: false,
905
+ pagination: false,
906
+ defaultSortBy: "",
907
+ defaultSortOrder: "asc",
908
+ mainField: "id",
909
+ pageSize: 10
910
+ };
911
+ const useDocumentLayout = (model) => {
912
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
913
+ const [{ query }] = useQueryParams();
914
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
793
915
  const { toggleNotification } = useNotification();
794
916
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
917
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
795
918
  const {
796
- currentData: data,
797
- isLoading: isLoadingDocument,
798
- isFetching: isFetchingDocument,
799
- error
800
- } = useGetDocumentQuery(args, {
801
- ...opts,
802
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
803
- });
804
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
919
+ data,
920
+ isLoading: isLoadingConfigs,
921
+ error,
922
+ isFetching: isFetchingConfigs
923
+ } = useGetContentTypeConfigurationQuery(model);
924
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
805
925
  React.useEffect(() => {
806
926
  if (error) {
807
927
  toggleNotification({
@@ -809,363 +929,438 @@ const useDocument = (args, opts) => {
809
929
  message: formatAPIError(error)
810
930
  });
811
931
  }
812
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
813
- const validationSchema = React.useMemo(() => {
814
- if (!schema) {
815
- return null;
816
- }
817
- return createYupSchema(schema.attributes, components);
818
- }, [schema, components]);
819
- const validate = React.useCallback(
820
- (document) => {
821
- if (!validationSchema) {
822
- throw new Error(
823
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
824
- );
825
- }
826
- try {
827
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
828
- return null;
829
- } catch (error2) {
830
- if (error2 instanceof ValidationError) {
831
- return getYupValidationErrors(error2);
832
- }
833
- throw error2;
834
- }
932
+ }, [error, formatAPIError, toggleNotification]);
933
+ const editLayout = React.useMemo(
934
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
935
+ layout: [],
936
+ components: {},
937
+ metadatas: {},
938
+ options: {},
939
+ settings: DEFAULT_SETTINGS
835
940
  },
836
- [validationSchema]
941
+ [data, isLoading, schemas, schema, components]
942
+ );
943
+ const listLayout = React.useMemo(() => {
944
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
945
+ layout: [],
946
+ metadatas: {},
947
+ options: {},
948
+ settings: DEFAULT_SETTINGS
949
+ };
950
+ }, [data, isLoading, schemas, schema, components]);
951
+ const { layout: edit } = React.useMemo(
952
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
953
+ layout: editLayout,
954
+ query
955
+ }),
956
+ [editLayout, query, runHookWaterfall]
837
957
  );
838
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
839
958
  return {
840
- components,
841
- document: data?.data,
842
- meta: data?.meta,
959
+ error,
843
960
  isLoading,
844
- schema,
845
- validate
846
- };
847
- };
848
- const useDoc = () => {
849
- const { id, slug, collectionType, origin } = useParams();
850
- const [{ query }] = useQueryParams();
851
- const params = React.useMemo(() => buildValidParams(query), [query]);
852
- if (!collectionType) {
853
- throw new Error("Could not find collectionType in url params");
854
- }
855
- if (!slug) {
856
- throw new Error("Could not find model in url params");
857
- }
858
- return {
859
- collectionType,
860
- model: slug,
861
- id: origin || id === "create" ? void 0 : id,
862
- ...useDocument(
863
- { documentId: origin || id, model: slug, collectionType, params },
864
- {
865
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
866
- }
867
- )
961
+ edit,
962
+ list: listLayout
868
963
  };
869
964
  };
870
- const prefixPluginTranslations = (trad, pluginId) => {
871
- if (!pluginId) {
872
- throw new TypeError("pluginId can't be empty");
873
- }
874
- return Object.keys(trad).reduce((acc, current) => {
875
- acc[`${pluginId}.${current}`] = trad[current];
876
- return acc;
877
- }, {});
878
- };
879
- const getTranslation = (id) => `content-manager.${id}`;
880
- const DEFAULT_UNEXPECTED_ERROR_MSG = {
881
- id: "notification.error",
882
- defaultMessage: "An error occurred, please try again"
965
+ const useDocLayout = () => {
966
+ const { model } = useDoc();
967
+ return useDocumentLayout(model);
883
968
  };
884
- const useDocumentActions = () => {
885
- const { toggleNotification } = useNotification();
886
- const { formatMessage } = useIntl();
887
- const { trackUsage } = useTracking();
888
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
889
- const navigate = useNavigate();
890
- const [deleteDocument] = useDeleteDocumentMutation();
891
- const _delete = React.useCallback(
892
- async ({ collectionType, model, documentId, params }, trackerProperty) => {
893
- try {
894
- trackUsage("willDeleteEntry", trackerProperty);
895
- const res = await deleteDocument({
896
- collectionType,
897
- model,
898
- documentId,
899
- params
900
- });
901
- if ("error" in res) {
902
- toggleNotification({
903
- type: "danger",
904
- message: formatAPIError(res.error)
905
- });
906
- return { error: res.error };
907
- }
908
- toggleNotification({
909
- type: "success",
910
- message: formatMessage({
911
- id: getTranslation("success.record.delete"),
912
- defaultMessage: "Deleted document"
913
- })
914
- });
915
- trackUsage("didDeleteEntry", trackerProperty);
916
- return res.data;
917
- } catch (err) {
918
- toggleNotification({
919
- type: "danger",
920
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
921
- });
922
- trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
923
- throw err;
969
+ const formatEditLayout = (data, {
970
+ schemas,
971
+ schema,
972
+ components
973
+ }) => {
974
+ let currentPanelIndex = 0;
975
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
976
+ data.contentType.layouts.edit,
977
+ schema?.attributes,
978
+ data.contentType.metadatas,
979
+ { configurations: data.components, schemas: components },
980
+ schemas
981
+ ).reduce((panels, row) => {
982
+ if (row.some((field) => field.type === "dynamiczone")) {
983
+ panels.push([row]);
984
+ currentPanelIndex += 2;
985
+ } else {
986
+ if (!panels[currentPanelIndex]) {
987
+ panels.push([row]);
988
+ } else {
989
+ panels[currentPanelIndex].push(row);
924
990
  }
925
- },
926
- [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
927
- );
928
- const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
929
- const deleteMany = React.useCallback(
930
- async ({ model, documentIds, params }) => {
931
- try {
932
- trackUsage("willBulkDeleteEntries");
933
- const res = await deleteManyDocuments({
934
- model,
935
- documentIds,
936
- params
937
- });
938
- if ("error" in res) {
939
- toggleNotification({
940
- type: "danger",
941
- message: formatAPIError(res.error)
942
- });
943
- return { error: res.error };
991
+ }
992
+ return panels;
993
+ }, []);
994
+ const componentEditAttributes = Object.entries(data.components).reduce(
995
+ (acc, [uid, configuration]) => {
996
+ acc[uid] = {
997
+ layout: convertEditLayoutToFieldLayouts(
998
+ configuration.layouts.edit,
999
+ components[uid].attributes,
1000
+ configuration.metadatas,
1001
+ { configurations: data.components, schemas: components }
1002
+ ),
1003
+ settings: {
1004
+ ...configuration.settings,
1005
+ icon: components[uid].info.icon,
1006
+ displayName: components[uid].info.displayName
944
1007
  }
945
- toggleNotification({
946
- type: "success",
947
- title: formatMessage({
948
- id: getTranslation("success.records.delete"),
949
- defaultMessage: "Successfully deleted."
950
- }),
951
- message: ""
952
- });
953
- trackUsage("didBulkDeleteEntries");
954
- return res.data;
955
- } catch (err) {
956
- toggleNotification({
957
- type: "danger",
958
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
959
- });
960
- trackUsage("didNotBulkDeleteEntries");
961
- throw err;
962
- }
1008
+ };
1009
+ return acc;
963
1010
  },
964
- [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1011
+ {}
965
1012
  );
966
- const [discardDocument] = useDiscardDocumentMutation();
967
- const discard = React.useCallback(
968
- async ({ collectionType, model, documentId, params }) => {
969
- try {
970
- const res = await discardDocument({
971
- collectionType,
972
- model,
973
- documentId,
974
- params
975
- });
976
- if ("error" in res) {
977
- toggleNotification({
978
- type: "danger",
979
- message: formatAPIError(res.error)
980
- });
981
- return { error: res.error };
982
- }
983
- toggleNotification({
984
- type: "success",
985
- message: formatMessage({
986
- id: "content-manager.success.record.discard",
987
- defaultMessage: "Changes discarded"
988
- })
989
- });
990
- return res.data;
991
- } catch (err) {
992
- toggleNotification({
993
- type: "danger",
994
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
995
- });
996
- throw err;
997
- }
1013
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1014
+ (acc, [attribute, metadata]) => {
1015
+ return {
1016
+ ...acc,
1017
+ [attribute]: metadata.edit
1018
+ };
998
1019
  },
999
- [discardDocument, formatAPIError, formatMessage, toggleNotification]
1020
+ {}
1000
1021
  );
1001
- const [publishDocument] = usePublishDocumentMutation();
1002
- const publish = React.useCallback(
1003
- async ({ collectionType, model, documentId, params }, data) => {
1004
- try {
1005
- trackUsage("willPublishEntry");
1006
- const res = await publishDocument({
1007
- collectionType,
1008
- model,
1009
- documentId,
1010
- data,
1011
- params
1012
- });
1013
- if ("error" in res) {
1014
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1015
- return { error: res.error };
1016
- }
1017
- trackUsage("didPublishEntry");
1018
- toggleNotification({
1019
- type: "success",
1020
- message: formatMessage({
1021
- id: getTranslation("success.record.publish"),
1022
- defaultMessage: "Published document"
1023
- })
1024
- });
1025
- return res.data;
1026
- } catch (err) {
1027
- toggleNotification({
1028
- type: "danger",
1029
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1030
- });
1031
- throw err;
1022
+ return {
1023
+ layout: panelledEditAttributes,
1024
+ components: componentEditAttributes,
1025
+ metadatas: editMetadatas,
1026
+ settings: {
1027
+ ...data.contentType.settings,
1028
+ displayName: schema?.info.displayName
1029
+ },
1030
+ options: {
1031
+ ...schema?.options,
1032
+ ...schema?.pluginOptions,
1033
+ ...data.contentType.options
1034
+ }
1035
+ };
1036
+ };
1037
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1038
+ return rows.map(
1039
+ (row) => row.map((field) => {
1040
+ const attribute = attributes[field.name];
1041
+ if (!attribute) {
1042
+ return null;
1032
1043
  }
1044
+ const { edit: metadata } = metadatas[field.name];
1045
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1046
+ return {
1047
+ attribute,
1048
+ disabled: !metadata.editable,
1049
+ hint: metadata.description,
1050
+ label: metadata.label ?? "",
1051
+ name: field.name,
1052
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1053
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1054
+ schemas,
1055
+ components: components?.schemas ?? {}
1056
+ }),
1057
+ placeholder: metadata.placeholder ?? "",
1058
+ required: attribute.required ?? false,
1059
+ size: field.size,
1060
+ unique: "unique" in attribute ? attribute.unique : false,
1061
+ visible: metadata.visible ?? true,
1062
+ type: attribute.type
1063
+ };
1064
+ }).filter((field) => field !== null)
1065
+ );
1066
+ };
1067
+ const formatListLayout = (data, {
1068
+ schemas,
1069
+ schema,
1070
+ components
1071
+ }) => {
1072
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1073
+ (acc, [attribute, metadata]) => {
1074
+ return {
1075
+ ...acc,
1076
+ [attribute]: metadata.list
1077
+ };
1033
1078
  },
1034
- [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1079
+ {}
1035
1080
  );
1036
- const [publishManyDocuments] = usePublishManyDocumentsMutation();
1037
- const publishMany = React.useCallback(
1038
- async ({ model, documentIds, params }) => {
1039
- try {
1040
- const res = await publishManyDocuments({
1041
- model,
1042
- documentIds,
1043
- params
1044
- });
1045
- if ("error" in res) {
1046
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1047
- return { error: res.error };
1048
- }
1049
- toggleNotification({
1050
- type: "success",
1051
- message: formatMessage({
1052
- id: getTranslation("success.record.publish"),
1053
- defaultMessage: "Published document"
1054
- })
1055
- });
1056
- return res.data;
1057
- } catch (err) {
1058
- toggleNotification({
1059
- type: "danger",
1060
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1061
- });
1062
- throw err;
1081
+ const listAttributes = convertListLayoutToFieldLayouts(
1082
+ data.contentType.layouts.list,
1083
+ schema?.attributes,
1084
+ listMetadatas,
1085
+ { configurations: data.components, schemas: components },
1086
+ schemas
1087
+ );
1088
+ return {
1089
+ layout: listAttributes,
1090
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1091
+ metadatas: listMetadatas,
1092
+ options: {
1093
+ ...schema?.options,
1094
+ ...schema?.pluginOptions,
1095
+ ...data.contentType.options
1096
+ }
1097
+ };
1098
+ };
1099
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1100
+ return columns.map((name) => {
1101
+ const attribute = attributes[name];
1102
+ if (!attribute) {
1103
+ return null;
1104
+ }
1105
+ const metadata = metadatas[name];
1106
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1107
+ return {
1108
+ attribute,
1109
+ label: metadata.label ?? "",
1110
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1111
+ schemas,
1112
+ components: components?.schemas ?? {}
1113
+ }),
1114
+ name,
1115
+ searchable: metadata.searchable ?? true,
1116
+ sortable: metadata.sortable ?? true
1117
+ };
1118
+ }).filter((field) => field !== null);
1119
+ };
1120
+ const useDocument = (args, opts) => {
1121
+ const { toggleNotification } = useNotification();
1122
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1123
+ const {
1124
+ currentData: data,
1125
+ isLoading: isLoadingDocument,
1126
+ isFetching: isFetchingDocument,
1127
+ error
1128
+ } = useGetDocumentQuery(args, {
1129
+ ...opts,
1130
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1131
+ });
1132
+ const {
1133
+ components,
1134
+ schema,
1135
+ schemas,
1136
+ isLoading: isLoadingSchema
1137
+ } = useContentTypeSchema(args.model);
1138
+ React.useEffect(() => {
1139
+ if (error) {
1140
+ toggleNotification({
1141
+ type: "danger",
1142
+ message: formatAPIError(error)
1143
+ });
1144
+ }
1145
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1146
+ const validationSchema = React.useMemo(() => {
1147
+ if (!schema) {
1148
+ return null;
1149
+ }
1150
+ return createYupSchema(schema.attributes, components);
1151
+ }, [schema, components]);
1152
+ const validate = React.useCallback(
1153
+ (document) => {
1154
+ if (!validationSchema) {
1155
+ throw new Error(
1156
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1157
+ );
1158
+ }
1159
+ try {
1160
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1161
+ return null;
1162
+ } catch (error2) {
1163
+ if (error2 instanceof ValidationError) {
1164
+ return getYupValidationErrors(error2);
1165
+ }
1166
+ throw error2;
1063
1167
  }
1064
1168
  },
1065
- [
1066
- // trackUsage,
1067
- publishManyDocuments,
1068
- toggleNotification,
1069
- formatMessage,
1070
- formatAPIError
1071
- ]
1169
+ [validationSchema]
1072
1170
  );
1073
- const [updateDocument] = useUpdateDocumentMutation();
1074
- const update = React.useCallback(
1075
- async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1171
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1172
+ const hasError = !!error;
1173
+ return {
1174
+ components,
1175
+ document: data?.data,
1176
+ meta: data?.meta,
1177
+ isLoading,
1178
+ hasError,
1179
+ schema,
1180
+ schemas,
1181
+ validate
1182
+ };
1183
+ };
1184
+ const useDoc = () => {
1185
+ const { id, slug, collectionType, origin } = useParams();
1186
+ const [{ query }] = useQueryParams();
1187
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1188
+ if (!collectionType) {
1189
+ throw new Error("Could not find collectionType in url params");
1190
+ }
1191
+ if (!slug) {
1192
+ throw new Error("Could not find model in url params");
1193
+ }
1194
+ const document = useDocument(
1195
+ { documentId: origin || id, model: slug, collectionType, params },
1196
+ {
1197
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1198
+ }
1199
+ );
1200
+ const returnId = origin || id === "create" ? void 0 : id;
1201
+ return {
1202
+ collectionType,
1203
+ model: slug,
1204
+ id: returnId,
1205
+ ...document
1206
+ };
1207
+ };
1208
+ const useContentManagerContext = () => {
1209
+ const {
1210
+ collectionType,
1211
+ model,
1212
+ id,
1213
+ components,
1214
+ isLoading: isLoadingDoc,
1215
+ schema,
1216
+ schemas
1217
+ } = useDoc();
1218
+ const layout = useDocumentLayout(model);
1219
+ const form = useForm("useContentManagerContext", (state) => state);
1220
+ const isSingleType = collectionType === SINGLE_TYPES;
1221
+ const slug = model;
1222
+ const isCreatingEntry = id === "create";
1223
+ useContentTypeSchema();
1224
+ const isLoading = isLoadingDoc || layout.isLoading;
1225
+ const error = layout.error;
1226
+ return {
1227
+ error,
1228
+ isLoading,
1229
+ // Base metadata
1230
+ model,
1231
+ collectionType,
1232
+ id,
1233
+ slug,
1234
+ isCreatingEntry,
1235
+ isSingleType,
1236
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1237
+ // All schema infos
1238
+ components,
1239
+ contentType: schema,
1240
+ contentTypes: schemas,
1241
+ // Form state
1242
+ form,
1243
+ // layout infos
1244
+ layout
1245
+ };
1246
+ };
1247
+ const prefixPluginTranslations = (trad, pluginId) => {
1248
+ return Object.keys(trad).reduce((acc, current) => {
1249
+ acc[`${pluginId}.${current}`] = trad[current];
1250
+ return acc;
1251
+ }, {});
1252
+ };
1253
+ const getTranslation = (id) => `content-manager.${id}`;
1254
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1255
+ id: "notification.error",
1256
+ defaultMessage: "An error occurred, please try again"
1257
+ };
1258
+ const useDocumentActions = () => {
1259
+ const { toggleNotification } = useNotification();
1260
+ const { formatMessage } = useIntl();
1261
+ const { trackUsage } = useTracking();
1262
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1263
+ const navigate = useNavigate();
1264
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
1265
+ const [deleteDocument] = useDeleteDocumentMutation();
1266
+ const _delete = React.useCallback(
1267
+ async ({ collectionType, model, documentId, params }, trackerProperty) => {
1076
1268
  try {
1077
- trackUsage("willEditEntry", trackerProperty);
1078
- const res = await updateDocument({
1269
+ trackUsage("willDeleteEntry", trackerProperty);
1270
+ const res = await deleteDocument({
1079
1271
  collectionType,
1080
1272
  model,
1081
1273
  documentId,
1082
- data,
1083
1274
  params
1084
1275
  });
1085
1276
  if ("error" in res) {
1086
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1087
- trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1277
+ toggleNotification({
1278
+ type: "danger",
1279
+ message: formatAPIError(res.error)
1280
+ });
1088
1281
  return { error: res.error };
1089
1282
  }
1090
- trackUsage("didEditEntry", trackerProperty);
1091
1283
  toggleNotification({
1092
1284
  type: "success",
1093
1285
  message: formatMessage({
1094
- id: getTranslation("success.record.save"),
1095
- defaultMessage: "Saved document"
1286
+ id: getTranslation("success.record.delete"),
1287
+ defaultMessage: "Deleted document"
1096
1288
  })
1097
1289
  });
1290
+ trackUsage("didDeleteEntry", trackerProperty);
1098
1291
  return res.data;
1099
1292
  } catch (err) {
1100
- trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1101
1293
  toggleNotification({
1102
1294
  type: "danger",
1103
1295
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1104
1296
  });
1297
+ trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
1105
1298
  throw err;
1106
1299
  }
1107
1300
  },
1108
- [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1301
+ [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
1109
1302
  );
1110
- const [unpublishDocument] = useUnpublishDocumentMutation();
1111
- const unpublish = React.useCallback(
1112
- async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1303
+ const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
1304
+ const deleteMany = React.useCallback(
1305
+ async ({ model, documentIds, params }) => {
1113
1306
  try {
1114
- trackUsage("willUnpublishEntry");
1115
- const res = await unpublishDocument({
1116
- collectionType,
1307
+ trackUsage("willBulkDeleteEntries");
1308
+ const res = await deleteManyDocuments({
1117
1309
  model,
1118
- documentId,
1119
- params,
1120
- data: {
1121
- discardDraft
1122
- }
1310
+ documentIds,
1311
+ params
1123
1312
  });
1124
1313
  if ("error" in res) {
1125
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1314
+ toggleNotification({
1315
+ type: "danger",
1316
+ message: formatAPIError(res.error)
1317
+ });
1126
1318
  return { error: res.error };
1127
1319
  }
1128
- trackUsage("didUnpublishEntry");
1129
1320
  toggleNotification({
1130
1321
  type: "success",
1131
- message: formatMessage({
1132
- id: getTranslation("success.record.unpublish"),
1133
- defaultMessage: "Unpublished document"
1134
- })
1322
+ title: formatMessage({
1323
+ id: getTranslation("success.records.delete"),
1324
+ defaultMessage: "Successfully deleted."
1325
+ }),
1326
+ message: ""
1135
1327
  });
1328
+ trackUsage("didBulkDeleteEntries");
1136
1329
  return res.data;
1137
1330
  } catch (err) {
1138
1331
  toggleNotification({
1139
1332
  type: "danger",
1140
1333
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1141
1334
  });
1335
+ trackUsage("didNotBulkDeleteEntries");
1142
1336
  throw err;
1143
1337
  }
1144
1338
  },
1145
- [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1339
+ [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1146
1340
  );
1147
- const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1148
- const unpublishMany = React.useCallback(
1149
- async ({ model, documentIds, params }) => {
1341
+ const [discardDocument] = useDiscardDocumentMutation();
1342
+ const discard = React.useCallback(
1343
+ async ({ collectionType, model, documentId, params }) => {
1150
1344
  try {
1151
- trackUsage("willBulkUnpublishEntries");
1152
- const res = await unpublishManyDocuments({
1345
+ const res = await discardDocument({
1346
+ collectionType,
1153
1347
  model,
1154
- documentIds,
1348
+ documentId,
1155
1349
  params
1156
1350
  });
1157
1351
  if ("error" in res) {
1158
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1352
+ toggleNotification({
1353
+ type: "danger",
1354
+ message: formatAPIError(res.error)
1355
+ });
1159
1356
  return { error: res.error };
1160
1357
  }
1161
- trackUsage("didBulkUnpublishEntries");
1162
1358
  toggleNotification({
1163
1359
  type: "success",
1164
- title: formatMessage({
1165
- id: getTranslation("success.records.unpublish"),
1166
- defaultMessage: "Successfully unpublished."
1167
- }),
1168
- message: ""
1360
+ message: formatMessage({
1361
+ id: "content-manager.success.record.discard",
1362
+ defaultMessage: "Changes discarded"
1363
+ })
1169
1364
  });
1170
1365
  return res.data;
1171
1366
  } catch (err) {
@@ -1173,16 +1368,196 @@ const useDocumentActions = () => {
1173
1368
  type: "danger",
1174
1369
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1175
1370
  });
1176
- trackUsage("didNotBulkUnpublishEntries");
1177
1371
  throw err;
1178
1372
  }
1179
1373
  },
1180
- [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1374
+ [discardDocument, formatAPIError, formatMessage, toggleNotification]
1181
1375
  );
1182
- const [createDocument] = useCreateDocumentMutation();
1183
- const create = React.useCallback(
1184
- async ({ model, params }, data, trackerProperty) => {
1185
- try {
1376
+ const [publishDocument] = usePublishDocumentMutation();
1377
+ const publish = React.useCallback(
1378
+ async ({ collectionType, model, documentId, params }, data) => {
1379
+ try {
1380
+ trackUsage("willPublishEntry");
1381
+ const res = await publishDocument({
1382
+ collectionType,
1383
+ model,
1384
+ documentId,
1385
+ data,
1386
+ params
1387
+ });
1388
+ if ("error" in res) {
1389
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1390
+ return { error: res.error };
1391
+ }
1392
+ trackUsage("didPublishEntry");
1393
+ toggleNotification({
1394
+ type: "success",
1395
+ message: formatMessage({
1396
+ id: getTranslation("success.record.publish"),
1397
+ defaultMessage: "Published document"
1398
+ })
1399
+ });
1400
+ return res.data;
1401
+ } catch (err) {
1402
+ toggleNotification({
1403
+ type: "danger",
1404
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1405
+ });
1406
+ throw err;
1407
+ }
1408
+ },
1409
+ [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1410
+ );
1411
+ const [publishManyDocuments] = usePublishManyDocumentsMutation();
1412
+ const publishMany = React.useCallback(
1413
+ async ({ model, documentIds, params }) => {
1414
+ try {
1415
+ const res = await publishManyDocuments({
1416
+ model,
1417
+ documentIds,
1418
+ params
1419
+ });
1420
+ if ("error" in res) {
1421
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1422
+ return { error: res.error };
1423
+ }
1424
+ toggleNotification({
1425
+ type: "success",
1426
+ message: formatMessage({
1427
+ id: getTranslation("success.record.publish"),
1428
+ defaultMessage: "Published document"
1429
+ })
1430
+ });
1431
+ return res.data;
1432
+ } catch (err) {
1433
+ toggleNotification({
1434
+ type: "danger",
1435
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1436
+ });
1437
+ throw err;
1438
+ }
1439
+ },
1440
+ [
1441
+ // trackUsage,
1442
+ publishManyDocuments,
1443
+ toggleNotification,
1444
+ formatMessage,
1445
+ formatAPIError
1446
+ ]
1447
+ );
1448
+ const [updateDocument] = useUpdateDocumentMutation();
1449
+ const update = React.useCallback(
1450
+ async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1451
+ try {
1452
+ trackUsage("willEditEntry", trackerProperty);
1453
+ const res = await updateDocument({
1454
+ collectionType,
1455
+ model,
1456
+ documentId,
1457
+ data,
1458
+ params
1459
+ });
1460
+ if ("error" in res) {
1461
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1462
+ trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1463
+ return { error: res.error };
1464
+ }
1465
+ trackUsage("didEditEntry", trackerProperty);
1466
+ toggleNotification({
1467
+ type: "success",
1468
+ message: formatMessage({
1469
+ id: getTranslation("success.record.save"),
1470
+ defaultMessage: "Saved document"
1471
+ })
1472
+ });
1473
+ return res.data;
1474
+ } catch (err) {
1475
+ trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1476
+ toggleNotification({
1477
+ type: "danger",
1478
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1479
+ });
1480
+ throw err;
1481
+ }
1482
+ },
1483
+ [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1484
+ );
1485
+ const [unpublishDocument] = useUnpublishDocumentMutation();
1486
+ const unpublish = React.useCallback(
1487
+ async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1488
+ try {
1489
+ trackUsage("willUnpublishEntry");
1490
+ const res = await unpublishDocument({
1491
+ collectionType,
1492
+ model,
1493
+ documentId,
1494
+ params,
1495
+ data: {
1496
+ discardDraft
1497
+ }
1498
+ });
1499
+ if ("error" in res) {
1500
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1501
+ return { error: res.error };
1502
+ }
1503
+ trackUsage("didUnpublishEntry");
1504
+ toggleNotification({
1505
+ type: "success",
1506
+ message: formatMessage({
1507
+ id: getTranslation("success.record.unpublish"),
1508
+ defaultMessage: "Unpublished document"
1509
+ })
1510
+ });
1511
+ return res.data;
1512
+ } catch (err) {
1513
+ toggleNotification({
1514
+ type: "danger",
1515
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1516
+ });
1517
+ throw err;
1518
+ }
1519
+ },
1520
+ [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1521
+ );
1522
+ const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1523
+ const unpublishMany = React.useCallback(
1524
+ async ({ model, documentIds, params }) => {
1525
+ try {
1526
+ trackUsage("willBulkUnpublishEntries");
1527
+ const res = await unpublishManyDocuments({
1528
+ model,
1529
+ documentIds,
1530
+ params
1531
+ });
1532
+ if ("error" in res) {
1533
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1534
+ return { error: res.error };
1535
+ }
1536
+ trackUsage("didBulkUnpublishEntries");
1537
+ toggleNotification({
1538
+ type: "success",
1539
+ title: formatMessage({
1540
+ id: getTranslation("success.records.unpublish"),
1541
+ defaultMessage: "Successfully unpublished."
1542
+ }),
1543
+ message: ""
1544
+ });
1545
+ return res.data;
1546
+ } catch (err) {
1547
+ toggleNotification({
1548
+ type: "danger",
1549
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1550
+ });
1551
+ trackUsage("didNotBulkUnpublishEntries");
1552
+ throw err;
1553
+ }
1554
+ },
1555
+ [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1556
+ );
1557
+ const [createDocument] = useCreateDocumentMutation();
1558
+ const create = React.useCallback(
1559
+ async ({ model, params }, data, trackerProperty) => {
1560
+ try {
1186
1561
  const res = await createDocument({
1187
1562
  model,
1188
1563
  data,
@@ -1201,6 +1576,7 @@ const useDocumentActions = () => {
1201
1576
  defaultMessage: "Saved document"
1202
1577
  })
1203
1578
  });
1579
+ setCurrentStep("contentManager.success");
1204
1580
  return res.data;
1205
1581
  } catch (err) {
1206
1582
  toggleNotification({
@@ -1302,10 +1678,10 @@ const useDocumentActions = () => {
1302
1678
  update
1303
1679
  };
1304
1680
  };
1305
- const ProtectedHistoryPage = lazy(
1306
- () => import("./History-BseDJOrj.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1681
+ const ProtectedHistoryPage = React.lazy(
1682
+ () => import("./History-DP8gmXpm.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1307
1683
  );
1308
- const routes$1 = [
1684
+ const routes$2 = [
1309
1685
  {
1310
1686
  path: ":collectionType/:slug/:id/history",
1311
1687
  Component: ProtectedHistoryPage
@@ -1315,32 +1691,45 @@ const routes$1 = [
1315
1691
  Component: ProtectedHistoryPage
1316
1692
  }
1317
1693
  ];
1694
+ const ProtectedPreviewPage = React.lazy(
1695
+ () => import("./Preview-C4NBzKUV.mjs").then((mod) => ({ default: mod.ProtectedPreviewPage }))
1696
+ );
1697
+ const routes$1 = [
1698
+ {
1699
+ path: ":collectionType/:slug/:id/preview",
1700
+ Component: ProtectedPreviewPage
1701
+ },
1702
+ {
1703
+ path: ":collectionType/:slug/preview",
1704
+ Component: ProtectedPreviewPage
1705
+ }
1706
+ ];
1318
1707
  const ProtectedEditViewPage = lazy(
1319
- () => import("./EditViewPage-CvRUUpVh.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1708
+ () => import("./EditViewPage-_A31Cl4g.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1320
1709
  );
1321
1710
  const ProtectedListViewPage = lazy(
1322
- () => import("./ListViewPage-lQ-VLV2G.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1711
+ () => import("./ListViewPage-Cf_DgaFV.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1323
1712
  );
1324
1713
  const ProtectedListConfiguration = lazy(
1325
- () => import("./ListConfigurationPage-DWE_fr5B.mjs").then((mod) => ({
1714
+ () => import("./ListConfigurationPage-BCkO5iuN.mjs").then((mod) => ({
1326
1715
  default: mod.ProtectedListConfiguration
1327
1716
  }))
1328
1717
  );
1329
1718
  const ProtectedEditConfigurationPage = lazy(
1330
- () => import("./EditConfigurationPage-QBZdUYyG.mjs").then((mod) => ({
1719
+ () => import("./EditConfigurationPage-vN4zupij.mjs").then((mod) => ({
1331
1720
  default: mod.ProtectedEditConfigurationPage
1332
1721
  }))
1333
1722
  );
1334
1723
  const ProtectedComponentConfigurationPage = lazy(
1335
- () => import("./ComponentConfigurationPage-DJEJ49QD.mjs").then((mod) => ({
1724
+ () => import("./ComponentConfigurationPage-BiASGi7x.mjs").then((mod) => ({
1336
1725
  default: mod.ProtectedComponentConfigurationPage
1337
1726
  }))
1338
1727
  );
1339
1728
  const NoPermissions = lazy(
1340
- () => import("./NoPermissionsPage-TV830k4P.mjs").then((mod) => ({ default: mod.NoPermissions }))
1729
+ () => import("./NoPermissionsPage-BALVSJ7x.mjs").then((mod) => ({ default: mod.NoPermissions }))
1341
1730
  );
1342
1731
  const NoContentType = lazy(
1343
- () => import("./NoContentTypePage-VCQOMwlf.mjs").then((mod) => ({ default: mod.NoContentType }))
1732
+ () => import("./NoContentTypePage-nHIyvJcB.mjs").then((mod) => ({ default: mod.NoContentType }))
1344
1733
  );
1345
1734
  const CollectionTypePages = () => {
1346
1735
  const { collectionType } = useParams();
@@ -1352,7 +1741,7 @@ const CollectionTypePages = () => {
1352
1741
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1353
1742
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1354
1743
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1355
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1744
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1356
1745
  const routes = [
1357
1746
  {
1358
1747
  path: LIST_RELATIVE_PATH,
@@ -1386,6 +1775,7 @@ const routes = [
1386
1775
  path: "no-content-types",
1387
1776
  Component: NoContentType
1388
1777
  },
1778
+ ...routes$2,
1389
1779
  ...routes$1
1390
1780
  ];
1391
1781
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1484,6 +1874,11 @@ const DocumentActionButton = (action) => {
1484
1874
  ) : null
1485
1875
  ] });
1486
1876
  };
1877
+ const MenuItem = styled(Menu.Item)`
1878
+ &:hover {
1879
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
1880
+ }
1881
+ `;
1487
1882
  const DocumentActionsMenu = ({
1488
1883
  actions: actions2,
1489
1884
  children,
@@ -1539,51 +1934,35 @@ const DocumentActionsMenu = ({
1539
1934
  ]
1540
1935
  }
1541
1936
  ),
1542
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1937
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1543
1938
  actions2.map((action) => {
1544
1939
  return /* @__PURE__ */ jsx(
1545
- Menu.Item,
1940
+ MenuItem,
1546
1941
  {
1547
1942
  disabled: action.disabled,
1548
1943
  onSelect: handleClick(action),
1549
1944
  display: "block",
1550
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1551
- /* @__PURE__ */ jsxs(
1552
- Flex,
1553
- {
1554
- color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1555
- gap: 2,
1556
- tag: "span",
1557
- children: [
1558
- /* @__PURE__ */ jsx(
1559
- Flex,
1560
- {
1561
- tag: "span",
1562
- color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1563
- children: action.icon
1564
- }
1565
- ),
1566
- action.label
1567
- ]
1568
- }
1569
- ),
1570
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1571
- Flex,
1572
- {
1573
- alignItems: "center",
1574
- background: "alternative100",
1575
- borderStyle: "solid",
1576
- borderColor: "alternative200",
1577
- borderWidth: "1px",
1578
- height: 5,
1579
- paddingLeft: 2,
1580
- paddingRight: 2,
1581
- hasRadius: true,
1582
- color: "alternative600",
1583
- children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1584
- }
1585
- )
1586
- ] })
1945
+ isVariantDanger: action.variant === "danger",
1946
+ isDisabled: action.disabled,
1947
+ children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxs(
1948
+ Flex,
1949
+ {
1950
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1951
+ gap: 2,
1952
+ tag: "span",
1953
+ children: [
1954
+ /* @__PURE__ */ jsx(
1955
+ Flex,
1956
+ {
1957
+ tag: "span",
1958
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1959
+ children: action.icon
1960
+ }
1961
+ ),
1962
+ action.label
1963
+ ]
1964
+ }
1965
+ ) })
1587
1966
  },
1588
1967
  action.id
1589
1968
  );
@@ -1663,11 +2042,11 @@ const DocumentActionConfirmDialog = ({
1663
2042
  /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1664
2043
  /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1665
2044
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1666
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
2045
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1667
2046
  id: "app.components.Button.cancel",
1668
2047
  defaultMessage: "Cancel"
1669
2048
  }) }) }),
1670
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
2049
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1671
2050
  id: "app.components.Button.confirm",
1672
2051
  defaultMessage: "Confirm"
1673
2052
  }) })
@@ -1694,6 +2073,18 @@ const DocumentActionModal = ({
1694
2073
  typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1695
2074
  ] }) });
1696
2075
  };
2076
+ const transformData = (data) => {
2077
+ if (Array.isArray(data)) {
2078
+ return data.map(transformData);
2079
+ }
2080
+ if (typeof data === "object" && data !== null) {
2081
+ if ("apiData" in data) {
2082
+ return data.apiData;
2083
+ }
2084
+ return mapValues(transformData)(data);
2085
+ }
2086
+ return data;
2087
+ };
1697
2088
  const PublishAction$1 = ({
1698
2089
  activeTab,
1699
2090
  documentId,
@@ -1708,6 +2099,7 @@ const PublishAction$1 = ({
1708
2099
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
1709
2100
  const isListView = useMatch(LIST_PATH) !== null;
1710
2101
  const isCloning = useMatch(CLONE_PATH) !== null;
2102
+ const { id } = useParams();
1711
2103
  const { formatMessage } = useIntl();
1712
2104
  const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1713
2105
  const { publish } = useDocumentActions();
@@ -1787,7 +2179,9 @@ const PublishAction$1 = ({
1787
2179
  const performPublish = async () => {
1788
2180
  setSubmitting(true);
1789
2181
  try {
1790
- const { errors } = await validate();
2182
+ const { errors } = await validate(true, {
2183
+ status: "published"
2184
+ });
1791
2185
  if (errors) {
1792
2186
  toggleNotification({
1793
2187
  type: "danger",
@@ -1805,13 +2199,15 @@ const PublishAction$1 = ({
1805
2199
  documentId,
1806
2200
  params
1807
2201
  },
1808
- formValues
2202
+ transformData(formValues)
1809
2203
  );
1810
2204
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1811
- navigate({
1812
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1813
- search: rawQuery
1814
- });
2205
+ if (id === "create") {
2206
+ navigate({
2207
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2208
+ search: rawQuery
2209
+ });
2210
+ }
1815
2211
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1816
2212
  setErrors(formatValidationErrors(res.error));
1817
2213
  }
@@ -1864,6 +2260,7 @@ const PublishAction$1 = ({
1864
2260
  };
1865
2261
  };
1866
2262
  PublishAction$1.type = "publish";
2263
+ PublishAction$1.position = "panel";
1867
2264
  const UpdateAction = ({
1868
2265
  activeTab,
1869
2266
  documentId,
@@ -1886,96 +2283,134 @@ const UpdateAction = ({
1886
2283
  const validate = useForm("UpdateAction", (state) => state.validate);
1887
2284
  const setErrors = useForm("UpdateAction", (state) => state.setErrors);
1888
2285
  const resetForm = useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
1889
- return {
1890
- /**
1891
- * Disabled when:
1892
- * - the form is submitting
1893
- * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1894
- * - the active tab is the published tab
1895
- */
1896
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1897
- label: formatMessage({
1898
- id: "content-manager.containers.Edit.save",
1899
- defaultMessage: "Save"
1900
- }),
1901
- onClick: async () => {
1902
- setSubmitting(true);
1903
- try {
1904
- if (activeTab !== "draft") {
1905
- const { errors } = await validate();
1906
- if (errors) {
1907
- toggleNotification({
1908
- type: "danger",
1909
- message: formatMessage({
1910
- id: "content-manager.validation.error",
1911
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1912
- })
1913
- });
1914
- return;
1915
- }
1916
- }
1917
- if (isCloning) {
1918
- const res = await clone(
1919
- {
1920
- model,
1921
- documentId: cloneMatch.params.origin,
1922
- params
1923
- },
1924
- document
1925
- );
1926
- if ("data" in res) {
1927
- navigate(
1928
- {
1929
- pathname: `../${res.data.documentId}`,
1930
- search: rawQuery
1931
- },
1932
- { relative: "path" }
1933
- );
1934
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1935
- setErrors(formatValidationErrors(res.error));
1936
- }
1937
- } else if (documentId || collectionType === SINGLE_TYPES) {
1938
- const res = await update(
2286
+ const handleUpdate = React.useCallback(async () => {
2287
+ setSubmitting(true);
2288
+ try {
2289
+ if (!modified) {
2290
+ return;
2291
+ }
2292
+ const { errors } = await validate(true, {
2293
+ status: "draft"
2294
+ });
2295
+ if (errors) {
2296
+ toggleNotification({
2297
+ type: "danger",
2298
+ message: formatMessage({
2299
+ id: "content-manager.validation.error",
2300
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2301
+ })
2302
+ });
2303
+ return;
2304
+ }
2305
+ if (isCloning) {
2306
+ const res = await clone(
2307
+ {
2308
+ model,
2309
+ documentId: cloneMatch.params.origin,
2310
+ params
2311
+ },
2312
+ transformData(document)
2313
+ );
2314
+ if ("data" in res) {
2315
+ navigate(
1939
2316
  {
1940
- collectionType,
1941
- model,
1942
- documentId,
1943
- params
2317
+ pathname: `../${res.data.documentId}`,
2318
+ search: rawQuery
1944
2319
  },
1945
- document
2320
+ { relative: "path" }
1946
2321
  );
1947
- if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1948
- setErrors(formatValidationErrors(res.error));
1949
- } else {
1950
- resetForm();
1951
- }
2322
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2323
+ setErrors(formatValidationErrors(res.error));
2324
+ }
2325
+ } else if (documentId || collectionType === SINGLE_TYPES) {
2326
+ const res = await update(
2327
+ {
2328
+ collectionType,
2329
+ model,
2330
+ documentId,
2331
+ params
2332
+ },
2333
+ transformData(document)
2334
+ );
2335
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2336
+ setErrors(formatValidationErrors(res.error));
1952
2337
  } else {
1953
- const res = await create(
2338
+ resetForm();
2339
+ }
2340
+ } else {
2341
+ const res = await create(
2342
+ {
2343
+ model,
2344
+ params
2345
+ },
2346
+ transformData(document)
2347
+ );
2348
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2349
+ navigate(
1954
2350
  {
1955
- model,
1956
- params
2351
+ pathname: `../${res.data.documentId}`,
2352
+ search: rawQuery
1957
2353
  },
1958
- document
2354
+ { replace: true, relative: "path" }
1959
2355
  );
1960
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1961
- navigate(
1962
- {
1963
- pathname: `../${res.data.documentId}`,
1964
- search: rawQuery
1965
- },
1966
- { replace: true, relative: "path" }
1967
- );
1968
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1969
- setErrors(formatValidationErrors(res.error));
1970
- }
2356
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2357
+ setErrors(formatValidationErrors(res.error));
1971
2358
  }
1972
- } finally {
1973
- setSubmitting(false);
1974
2359
  }
2360
+ } finally {
2361
+ setSubmitting(false);
1975
2362
  }
2363
+ }, [
2364
+ clone,
2365
+ cloneMatch?.params.origin,
2366
+ collectionType,
2367
+ create,
2368
+ document,
2369
+ documentId,
2370
+ formatMessage,
2371
+ formatValidationErrors,
2372
+ isCloning,
2373
+ model,
2374
+ modified,
2375
+ navigate,
2376
+ params,
2377
+ rawQuery,
2378
+ resetForm,
2379
+ setErrors,
2380
+ setSubmitting,
2381
+ toggleNotification,
2382
+ update,
2383
+ validate
2384
+ ]);
2385
+ React.useEffect(() => {
2386
+ const handleKeyDown = (e) => {
2387
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
2388
+ e.preventDefault();
2389
+ handleUpdate();
2390
+ }
2391
+ };
2392
+ window.addEventListener("keydown", handleKeyDown);
2393
+ return () => {
2394
+ window.removeEventListener("keydown", handleKeyDown);
2395
+ };
2396
+ }, [handleUpdate]);
2397
+ return {
2398
+ /**
2399
+ * Disabled when:
2400
+ * - the form is submitting
2401
+ * - the document is not modified & we're not cloning (you can save a clone entity straight away)
2402
+ * - the active tab is the published tab
2403
+ */
2404
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
2405
+ label: formatMessage({
2406
+ id: "global.save",
2407
+ defaultMessage: "Save"
2408
+ }),
2409
+ onClick: handleUpdate
1976
2410
  };
1977
2411
  };
1978
2412
  UpdateAction.type = "update";
2413
+ UpdateAction.position = "panel";
1979
2414
  const UNPUBLISH_DRAFT_OPTIONS = {
1980
2415
  KEEP: "keep",
1981
2416
  DISCARD: "discard"
@@ -2098,6 +2533,7 @@ const UnpublishAction$1 = ({
2098
2533
  };
2099
2534
  };
2100
2535
  UnpublishAction$1.type = "unpublish";
2536
+ UnpublishAction$1.position = "panel";
2101
2537
  const DiscardAction = ({
2102
2538
  activeTab,
2103
2539
  documentId,
@@ -2148,6 +2584,7 @@ const DiscardAction = ({
2148
2584
  };
2149
2585
  };
2150
2586
  DiscardAction.type = "discard";
2587
+ DiscardAction.position = "panel";
2151
2588
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2152
2589
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2153
2590
  const RelativeTime = React.forwardRef(
@@ -2160,7 +2597,7 @@ const RelativeTime = React.forwardRef(
2160
2597
  });
2161
2598
  const unit = intervals.find((intervalUnit) => {
2162
2599
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2163
- });
2600
+ }) ?? "seconds";
2164
2601
  const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2165
2602
  const customInterval = customIntervals.find(
2166
2603
  (custom) => interval[custom.unit] < custom.threshold
@@ -2194,19 +2631,29 @@ const getDisplayName = ({
2194
2631
  return email ?? "";
2195
2632
  };
2196
2633
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2197
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2198
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2199
- return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2634
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2635
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2636
+ const { formatMessage } = useIntl();
2637
+ return /* @__PURE__ */ jsx(Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2638
+ id: `content-manager.containers.List.${status}`,
2639
+ defaultMessage: capitalise(status)
2640
+ }) }) });
2200
2641
  };
2201
2642
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2202
2643
  const { formatMessage } = useIntl();
2203
2644
  const isCloning = useMatch(CLONE_PATH) !== null;
2645
+ const params = useParams();
2204
2646
  const title = isCreating ? formatMessage({
2205
2647
  id: "content-manager.containers.edit.title.new",
2206
2648
  defaultMessage: "Create an entry"
2207
2649
  }) : documentTitle;
2208
2650
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2209
- /* @__PURE__ */ jsx(BackButton, {}),
2651
+ /* @__PURE__ */ jsx(
2652
+ BackButton,
2653
+ {
2654
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2655
+ }
2656
+ ),
2210
2657
  /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2211
2658
  /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2212
2659
  /* @__PURE__ */ jsx(HeaderToolbar, {})
@@ -2257,7 +2704,7 @@ const HeaderToolbar = () => {
2257
2704
  meta: isCloning ? void 0 : meta,
2258
2705
  collectionType
2259
2706
  },
2260
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2707
+ descriptions: plugins["content-manager"].apis.getDocumentActions("header"),
2261
2708
  children: (actions2) => {
2262
2709
  const headerActions = actions2.filter((action) => {
2263
2710
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2294,12 +2741,12 @@ const Information = ({ activeTab }) => {
2294
2741
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2295
2742
  label: formatMessage({
2296
2743
  id: "content-manager.containers.edit.information.last-published.label",
2297
- defaultMessage: "Last published"
2744
+ defaultMessage: "Published"
2298
2745
  }),
2299
2746
  value: formatMessage(
2300
2747
  {
2301
2748
  id: "content-manager.containers.edit.information.last-published.value",
2302
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2749
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2303
2750
  },
2304
2751
  {
2305
2752
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2312,12 +2759,12 @@ const Information = ({ activeTab }) => {
2312
2759
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2313
2760
  label: formatMessage({
2314
2761
  id: "content-manager.containers.edit.information.last-draft.label",
2315
- defaultMessage: "Last draft"
2762
+ defaultMessage: "Updated"
2316
2763
  }),
2317
2764
  value: formatMessage(
2318
2765
  {
2319
2766
  id: "content-manager.containers.edit.information.last-draft.value",
2320
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2767
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2321
2768
  },
2322
2769
  {
2323
2770
  time: /* @__PURE__ */ jsx(
@@ -2335,12 +2782,12 @@ const Information = ({ activeTab }) => {
2335
2782
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2336
2783
  label: formatMessage({
2337
2784
  id: "content-manager.containers.edit.information.document.label",
2338
- defaultMessage: "Document"
2785
+ defaultMessage: "Created"
2339
2786
  }),
2340
2787
  value: formatMessage(
2341
2788
  {
2342
2789
  id: "content-manager.containers.edit.information.document.value",
2343
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2790
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2344
2791
  },
2345
2792
  {
2346
2793
  time: /* @__PURE__ */ jsx(
@@ -2378,25 +2825,77 @@ const Information = ({ activeTab }) => {
2378
2825
  );
2379
2826
  };
2380
2827
  const HeaderActions = ({ actions: actions2 }) => {
2381
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2382
- if ("options" in action) {
2828
+ const [dialogId, setDialogId] = React.useState(null);
2829
+ const handleClick = (action) => async (e) => {
2830
+ if (!("options" in action)) {
2831
+ const { onClick = () => false, dialog, id } = action;
2832
+ const muteDialog = await onClick(e);
2833
+ if (dialog && !muteDialog) {
2834
+ e.preventDefault();
2835
+ setDialogId(id);
2836
+ }
2837
+ }
2838
+ };
2839
+ const handleClose = () => {
2840
+ setDialogId(null);
2841
+ };
2842
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2843
+ if (action.options) {
2383
2844
  return /* @__PURE__ */ jsx(
2384
2845
  SingleSelect,
2385
2846
  {
2386
2847
  size: "S",
2387
- disabled: action.disabled,
2388
- "aria-label": action.label,
2389
2848
  onChange: action.onSelect,
2390
- value: action.value,
2849
+ "aria-label": action.label,
2850
+ ...action,
2391
2851
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2392
2852
  },
2393
2853
  action.id
2394
2854
  );
2395
2855
  } else {
2396
- return null;
2856
+ if (action.type === "icon") {
2857
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
2858
+ /* @__PURE__ */ jsx(
2859
+ IconButton,
2860
+ {
2861
+ disabled: action.disabled,
2862
+ label: action.label,
2863
+ size: "S",
2864
+ onClick: handleClick(action),
2865
+ children: action.icon
2866
+ }
2867
+ ),
2868
+ action.dialog ? /* @__PURE__ */ jsx(
2869
+ HeaderActionDialog,
2870
+ {
2871
+ ...action.dialog,
2872
+ isOpen: dialogId === action.id,
2873
+ onClose: handleClose
2874
+ }
2875
+ ) : null
2876
+ ] }, action.id);
2877
+ }
2397
2878
  }
2398
2879
  }) });
2399
2880
  };
2881
+ const HeaderActionDialog = ({
2882
+ onClose,
2883
+ onCancel,
2884
+ title,
2885
+ content: Content,
2886
+ isOpen
2887
+ }) => {
2888
+ const handleClose = async () => {
2889
+ if (onCancel) {
2890
+ await onCancel();
2891
+ }
2892
+ onClose();
2893
+ };
2894
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2895
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2896
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
2897
+ ] }) });
2898
+ };
2400
2899
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2401
2900
  const navigate = useNavigate();
2402
2901
  const { formatMessage } = useIntl();
@@ -2413,6 +2912,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2413
2912
  };
2414
2913
  };
2415
2914
  ConfigureTheViewAction.type = "configure-the-view";
2915
+ ConfigureTheViewAction.position = "header";
2416
2916
  const EditTheModelAction = ({ model }) => {
2417
2917
  const navigate = useNavigate();
2418
2918
  const { formatMessage } = useIntl();
@@ -2429,6 +2929,7 @@ const EditTheModelAction = ({ model }) => {
2429
2929
  };
2430
2930
  };
2431
2931
  EditTheModelAction.type = "edit-the-model";
2932
+ EditTheModelAction.position = "header";
2432
2933
  const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2433
2934
  const navigate = useNavigate();
2434
2935
  const { formatMessage } = useIntl();
@@ -2437,12 +2938,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2437
2938
  const { delete: deleteAction } = useDocumentActions();
2438
2939
  const { toggleNotification } = useNotification();
2439
2940
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2941
+ const isLocalized = document?.locale != null;
2440
2942
  return {
2441
2943
  disabled: !canDelete || !document,
2442
- label: formatMessage({
2443
- id: "content-manager.actions.delete.label",
2444
- defaultMessage: "Delete document"
2445
- }),
2944
+ label: formatMessage(
2945
+ {
2946
+ id: "content-manager.actions.delete.label",
2947
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2948
+ },
2949
+ { isLocalized }
2950
+ ),
2446
2951
  icon: /* @__PURE__ */ jsx(Trash, {}),
2447
2952
  dialog: {
2448
2953
  type: "dialog",
@@ -2480,422 +2985,120 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2480
2985
  model,
2481
2986
  collectionType,
2482
2987
  params: {
2483
- locale: "*"
2484
- }
2485
- });
2486
- if (!("error" in res)) {
2487
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2488
- }
2489
- } finally {
2490
- if (!listViewPathMatch) {
2491
- setSubmitting(false);
2492
- }
2493
- }
2494
- }
2495
- },
2496
- variant: "danger",
2497
- position: ["header", "table-row"]
2498
- };
2499
- };
2500
- DeleteAction$1.type = "delete";
2501
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2502
- const Panels = () => {
2503
- const isCloning = useMatch(CLONE_PATH) !== null;
2504
- const [
2505
- {
2506
- query: { status }
2507
- }
2508
- ] = useQueryParams({
2509
- status: "draft"
2510
- });
2511
- const { model, id, document, meta, collectionType } = useDoc();
2512
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2513
- const props = {
2514
- activeTab: status,
2515
- model,
2516
- documentId: id,
2517
- document: isCloning ? void 0 : document,
2518
- meta: isCloning ? void 0 : meta,
2519
- collectionType
2520
- };
2521
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2522
- DescriptionComponentRenderer,
2523
- {
2524
- props,
2525
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2526
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2527
- }
2528
- ) });
2529
- };
2530
- const ActionsPanel = () => {
2531
- const { formatMessage } = useIntl();
2532
- return {
2533
- title: formatMessage({
2534
- id: "content-manager.containers.edit.panels.default.title",
2535
- defaultMessage: "Document"
2536
- }),
2537
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2538
- };
2539
- };
2540
- ActionsPanel.type = "actions";
2541
- const ActionsPanelContent = () => {
2542
- const isCloning = useMatch(CLONE_PATH) !== null;
2543
- const [
2544
- {
2545
- query: { status = "draft" }
2546
- }
2547
- ] = useQueryParams();
2548
- const { model, id, document, meta, collectionType } = useDoc();
2549
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2550
- const props = {
2551
- activeTab: status,
2552
- model,
2553
- documentId: id,
2554
- document: isCloning ? void 0 : document,
2555
- meta: isCloning ? void 0 : meta,
2556
- collectionType
2557
- };
2558
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2559
- /* @__PURE__ */ jsx(
2560
- DescriptionComponentRenderer,
2561
- {
2562
- props,
2563
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2564
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2565
- }
2566
- ),
2567
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2568
- ] });
2569
- };
2570
- const Panel = React.forwardRef(({ children, title }, ref) => {
2571
- return /* @__PURE__ */ jsxs(
2572
- Flex,
2573
- {
2574
- ref,
2575
- tag: "aside",
2576
- "aria-labelledby": "additional-information",
2577
- background: "neutral0",
2578
- borderColor: "neutral150",
2579
- hasRadius: true,
2580
- paddingBottom: 4,
2581
- paddingLeft: 4,
2582
- paddingRight: 4,
2583
- paddingTop: 4,
2584
- shadow: "tableShadow",
2585
- gap: 3,
2586
- direction: "column",
2587
- justifyContent: "stretch",
2588
- alignItems: "flex-start",
2589
- children: [
2590
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2591
- children
2592
- ]
2593
- }
2594
- );
2595
- });
2596
- const HOOKS = {
2597
- /**
2598
- * Hook that allows to mutate the displayed headers of the list view table
2599
- * @constant
2600
- * @type {string}
2601
- */
2602
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2603
- /**
2604
- * Hook that allows to mutate the CM's collection types links pre-set filters
2605
- * @constant
2606
- * @type {string}
2607
- */
2608
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2609
- /**
2610
- * Hook that allows to mutate the CM's edit view layout
2611
- * @constant
2612
- * @type {string}
2613
- */
2614
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2615
- /**
2616
- * Hook that allows to mutate the CM's single types links pre-set filters
2617
- * @constant
2618
- * @type {string}
2619
- */
2620
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2621
- };
2622
- const contentTypesApi = contentManagerApi.injectEndpoints({
2623
- endpoints: (builder) => ({
2624
- getContentTypeConfiguration: builder.query({
2625
- query: (uid) => ({
2626
- url: `/content-manager/content-types/${uid}/configuration`,
2627
- method: "GET"
2628
- }),
2629
- transformResponse: (response) => response.data,
2630
- providesTags: (_result, _error, uid) => [
2631
- { type: "ContentTypesConfiguration", id: uid },
2632
- { type: "ContentTypeSettings", id: "LIST" }
2633
- ]
2634
- }),
2635
- getAllContentTypeSettings: builder.query({
2636
- query: () => "/content-manager/content-types-settings",
2637
- transformResponse: (response) => response.data,
2638
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2639
- }),
2640
- updateContentTypeConfiguration: builder.mutation({
2641
- query: ({ uid, ...body }) => ({
2642
- url: `/content-manager/content-types/${uid}/configuration`,
2643
- method: "PUT",
2644
- data: body
2645
- }),
2646
- transformResponse: (response) => response.data,
2647
- invalidatesTags: (_result, _error, { uid }) => [
2648
- { type: "ContentTypesConfiguration", id: uid },
2649
- { type: "ContentTypeSettings", id: "LIST" },
2650
- // Is this necessary?
2651
- { type: "InitialData" }
2652
- ]
2653
- })
2654
- })
2655
- });
2656
- const {
2657
- useGetContentTypeConfigurationQuery,
2658
- useGetAllContentTypeSettingsQuery,
2659
- useUpdateContentTypeConfigurationMutation
2660
- } = contentTypesApi;
2661
- const checkIfAttributeIsDisplayable = (attribute) => {
2662
- const { type } = attribute;
2663
- if (type === "relation") {
2664
- return !attribute.relation.toLowerCase().includes("morph");
2665
- }
2666
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2667
- };
2668
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2669
- if (!mainFieldName) {
2670
- return void 0;
2671
- }
2672
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2673
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2674
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2675
- );
2676
- return {
2677
- name: mainFieldName,
2678
- type: mainFieldType ?? "string"
2679
- };
2680
- };
2681
- const DEFAULT_SETTINGS = {
2682
- bulkable: false,
2683
- filterable: false,
2684
- searchable: false,
2685
- pagination: false,
2686
- defaultSortBy: "",
2687
- defaultSortOrder: "asc",
2688
- mainField: "id",
2689
- pageSize: 10
2690
- };
2691
- const useDocumentLayout = (model) => {
2692
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2693
- const [{ query }] = useQueryParams();
2694
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2695
- const { toggleNotification } = useNotification();
2696
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2697
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2698
- const {
2699
- data,
2700
- isLoading: isLoadingConfigs,
2701
- error,
2702
- isFetching: isFetchingConfigs
2703
- } = useGetContentTypeConfigurationQuery(model);
2704
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2705
- React.useEffect(() => {
2706
- if (error) {
2707
- toggleNotification({
2708
- type: "danger",
2709
- message: formatAPIError(error)
2710
- });
2711
- }
2712
- }, [error, formatAPIError, toggleNotification]);
2713
- const editLayout = React.useMemo(
2714
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2715
- layout: [],
2716
- components: {},
2717
- metadatas: {},
2718
- options: {},
2719
- settings: DEFAULT_SETTINGS
2720
- },
2721
- [data, isLoading, schemas, schema, components]
2722
- );
2723
- const listLayout = React.useMemo(() => {
2724
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2725
- layout: [],
2726
- metadatas: {},
2727
- options: {},
2728
- settings: DEFAULT_SETTINGS
2729
- };
2730
- }, [data, isLoading, schemas, schema, components]);
2731
- const { layout: edit } = React.useMemo(
2732
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2733
- layout: editLayout,
2734
- query
2735
- }),
2736
- [editLayout, query, runHookWaterfall]
2737
- );
2738
- return {
2739
- error,
2740
- isLoading,
2741
- edit,
2742
- list: listLayout
2743
- };
2744
- };
2745
- const useDocLayout = () => {
2746
- const { model } = useDoc();
2747
- return useDocumentLayout(model);
2748
- };
2749
- const formatEditLayout = (data, {
2750
- schemas,
2751
- schema,
2752
- components
2753
- }) => {
2754
- let currentPanelIndex = 0;
2755
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2756
- data.contentType.layouts.edit,
2757
- schema?.attributes,
2758
- data.contentType.metadatas,
2759
- { configurations: data.components, schemas: components },
2760
- schemas
2761
- ).reduce((panels, row) => {
2762
- if (row.some((field) => field.type === "dynamiczone")) {
2763
- panels.push([row]);
2764
- currentPanelIndex += 2;
2765
- } else {
2766
- if (!panels[currentPanelIndex]) {
2767
- panels.push([]);
2768
- }
2769
- panels[currentPanelIndex].push(row);
2770
- }
2771
- return panels;
2772
- }, []);
2773
- const componentEditAttributes = Object.entries(data.components).reduce(
2774
- (acc, [uid, configuration]) => {
2775
- acc[uid] = {
2776
- layout: convertEditLayoutToFieldLayouts(
2777
- configuration.layouts.edit,
2778
- components[uid].attributes,
2779
- configuration.metadatas,
2780
- { configurations: data.components, schemas: components }
2781
- ),
2782
- settings: {
2783
- ...configuration.settings,
2784
- icon: components[uid].info.icon,
2785
- displayName: components[uid].info.displayName
2988
+ locale: "*"
2989
+ }
2990
+ });
2991
+ if (!("error" in res)) {
2992
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2993
+ }
2994
+ } finally {
2995
+ if (!listViewPathMatch) {
2996
+ setSubmitting(false);
2997
+ }
2786
2998
  }
2787
- };
2788
- return acc;
2789
- },
2790
- {}
2791
- );
2792
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2793
- (acc, [attribute, metadata]) => {
2794
- return {
2795
- ...acc,
2796
- [attribute]: metadata.edit
2797
- };
2798
- },
2799
- {}
2800
- );
2801
- return {
2802
- layout: panelledEditAttributes,
2803
- components: componentEditAttributes,
2804
- metadatas: editMetadatas,
2805
- settings: {
2806
- ...data.contentType.settings,
2807
- displayName: schema?.info.displayName
2999
+ }
2808
3000
  },
2809
- options: {
2810
- ...schema?.options,
2811
- ...schema?.pluginOptions,
2812
- ...data.contentType.options
2813
- }
3001
+ variant: "danger",
3002
+ position: ["header", "table-row"]
2814
3003
  };
2815
3004
  };
2816
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2817
- return rows.map(
2818
- (row) => row.map((field) => {
2819
- const attribute = attributes[field.name];
2820
- if (!attribute) {
2821
- return null;
2822
- }
2823
- const { edit: metadata } = metadatas[field.name];
2824
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2825
- return {
2826
- attribute,
2827
- disabled: !metadata.editable,
2828
- hint: metadata.description,
2829
- label: metadata.label ?? "",
2830
- name: field.name,
2831
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2832
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2833
- schemas,
2834
- components: components?.schemas ?? {}
2835
- }),
2836
- placeholder: metadata.placeholder ?? "",
2837
- required: attribute.required ?? false,
2838
- size: field.size,
2839
- unique: "unique" in attribute ? attribute.unique : false,
2840
- visible: metadata.visible ?? true,
2841
- type: attribute.type
2842
- };
2843
- }).filter((field) => field !== null)
2844
- );
3005
+ DeleteAction$1.type = "delete";
3006
+ DeleteAction$1.position = ["header", "table-row"];
3007
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
3008
+ const Panels = () => {
3009
+ const isCloning = useMatch(CLONE_PATH) !== null;
3010
+ const [
3011
+ {
3012
+ query: { status }
3013
+ }
3014
+ ] = useQueryParams({
3015
+ status: "draft"
3016
+ });
3017
+ const { model, id, document, meta, collectionType } = useDoc();
3018
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
3019
+ const props = {
3020
+ activeTab: status,
3021
+ model,
3022
+ documentId: id,
3023
+ document: isCloning ? void 0 : document,
3024
+ meta: isCloning ? void 0 : meta,
3025
+ collectionType
3026
+ };
3027
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
3028
+ DescriptionComponentRenderer,
3029
+ {
3030
+ props,
3031
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
3032
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
3033
+ }
3034
+ ) });
2845
3035
  };
2846
- const formatListLayout = (data, {
2847
- schemas,
2848
- schema,
2849
- components
2850
- }) => {
2851
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2852
- (acc, [attribute, metadata]) => {
2853
- return {
2854
- ...acc,
2855
- [attribute]: metadata.list
2856
- };
2857
- },
2858
- {}
2859
- );
2860
- const listAttributes = convertListLayoutToFieldLayouts(
2861
- data.contentType.layouts.list,
2862
- schema?.attributes,
2863
- listMetadatas,
2864
- { configurations: data.components, schemas: components },
2865
- schemas
2866
- );
3036
+ const ActionsPanel = () => {
3037
+ const { formatMessage } = useIntl();
2867
3038
  return {
2868
- layout: listAttributes,
2869
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2870
- metadatas: listMetadatas,
2871
- options: {
2872
- ...schema?.options,
2873
- ...schema?.pluginOptions,
2874
- ...data.contentType.options
2875
- }
3039
+ title: formatMessage({
3040
+ id: "content-manager.containers.edit.panels.default.title",
3041
+ defaultMessage: "Entry"
3042
+ }),
3043
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2876
3044
  };
2877
3045
  };
2878
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2879
- return columns.map((name) => {
2880
- const attribute = attributes[name];
2881
- if (!attribute) {
2882
- return null;
3046
+ ActionsPanel.type = "actions";
3047
+ const ActionsPanelContent = () => {
3048
+ const isCloning = useMatch(CLONE_PATH) !== null;
3049
+ const [
3050
+ {
3051
+ query: { status = "draft" }
2883
3052
  }
2884
- const metadata = metadatas[name];
2885
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2886
- return {
2887
- attribute,
2888
- label: metadata.label ?? "",
2889
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2890
- schemas,
2891
- components: components?.schemas ?? {}
2892
- }),
2893
- name,
2894
- searchable: metadata.searchable ?? true,
2895
- sortable: metadata.sortable ?? true
2896
- };
2897
- }).filter((field) => field !== null);
3053
+ ] = useQueryParams();
3054
+ const { model, id, document, meta, collectionType } = useDoc();
3055
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
3056
+ const props = {
3057
+ activeTab: status,
3058
+ model,
3059
+ documentId: id,
3060
+ document: isCloning ? void 0 : document,
3061
+ meta: isCloning ? void 0 : meta,
3062
+ collectionType
3063
+ };
3064
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
3065
+ /* @__PURE__ */ jsx(
3066
+ DescriptionComponentRenderer,
3067
+ {
3068
+ props,
3069
+ descriptions: plugins["content-manager"].apis.getDocumentActions("panel"),
3070
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
3071
+ }
3072
+ ),
3073
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
3074
+ ] });
2898
3075
  };
3076
+ const Panel = React.forwardRef(({ children, title }, ref) => {
3077
+ return /* @__PURE__ */ jsxs(
3078
+ Flex,
3079
+ {
3080
+ ref,
3081
+ tag: "aside",
3082
+ "aria-labelledby": "additional-information",
3083
+ background: "neutral0",
3084
+ borderColor: "neutral150",
3085
+ hasRadius: true,
3086
+ paddingBottom: 4,
3087
+ paddingLeft: 4,
3088
+ paddingRight: 4,
3089
+ paddingTop: 4,
3090
+ shadow: "tableShadow",
3091
+ gap: 3,
3092
+ direction: "column",
3093
+ justifyContent: "stretch",
3094
+ alignItems: "flex-start",
3095
+ children: [
3096
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3097
+ children
3098
+ ]
3099
+ }
3100
+ );
3101
+ });
2899
3102
  const ConfirmBulkActionDialog = ({
2900
3103
  onToggleDialog,
2901
3104
  isOpen = false,
@@ -3140,18 +3343,10 @@ const SelectedEntriesTableContent = ({
3140
3343
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3141
3344
  },
3142
3345
  state: { from: pathname },
3143
- label: formatMessage(
3144
- { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3145
- {
3146
- target: formatMessage(
3147
- {
3148
- id: "content-manager.components.ListViewHelperPluginTable.row-line",
3149
- defaultMessage: "item line {number}"
3150
- },
3151
- { number: index2 + 1 }
3152
- )
3153
- }
3154
- ),
3346
+ label: formatMessage({
3347
+ id: "content-manager.bulk-publish.edit",
3348
+ defaultMessage: "Edit"
3349
+ }),
3155
3350
  target: "_blank",
3156
3351
  marginLeft: "auto",
3157
3352
  variant: "ghost",
@@ -3325,8 +3520,7 @@ const PublishAction = ({ documents, model }) => {
3325
3520
  const refetchList = () => {
3326
3521
  contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3327
3522
  };
3328
- if (!showPublishButton)
3329
- return null;
3523
+ if (!showPublishButton) return null;
3330
3524
  return {
3331
3525
  actionType: "publish",
3332
3526
  variant: "tertiary",
@@ -3394,8 +3588,7 @@ const DeleteAction = ({ documents, model }) => {
3394
3588
  selectRow([]);
3395
3589
  }
3396
3590
  };
3397
- if (!hasDeletePermission)
3398
- return null;
3591
+ if (!hasDeletePermission) return null;
3399
3592
  return {
3400
3593
  variant: "danger-light",
3401
3594
  label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
@@ -3444,8 +3637,7 @@ const UnpublishAction = ({ documents, model }) => {
3444
3637
  }
3445
3638
  };
3446
3639
  const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3447
- if (!showUnpublishButton)
3448
- return null;
3640
+ if (!showUnpublishButton) return null;
3449
3641
  return {
3450
3642
  variant: "tertiary",
3451
3643
  label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
@@ -3550,7 +3742,7 @@ const TableActions = ({ document }) => {
3550
3742
  DescriptionComponentRenderer,
3551
3743
  {
3552
3744
  props,
3553
- descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3745
+ descriptions: plugins["content-manager"].apis.getDocumentActions("table-row").filter((action) => action.name !== "PublishAction"),
3554
3746
  children: (actions2) => {
3555
3747
  const tableRowActions = actions2.filter((action) => {
3556
3748
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3609,6 +3801,7 @@ const EditAction = ({ documentId }) => {
3609
3801
  };
3610
3802
  };
3611
3803
  EditAction.type = "edit";
3804
+ EditAction.position = "table-row";
3612
3805
  const StyledPencil = styled(Pencil)`
3613
3806
  path {
3614
3807
  fill: currentColor;
@@ -3685,6 +3878,7 @@ const CloneAction = ({ model, documentId }) => {
3685
3878
  };
3686
3879
  };
3687
3880
  CloneAction.type = "clone";
3881
+ CloneAction.position = "table-row";
3688
3882
  const StyledDuplicate = styled(Duplicate)`
3689
3883
  path {
3690
3884
  fill: currentColor;
@@ -3771,7 +3965,14 @@ class ContentManagerPlugin {
3771
3965
  addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3772
3966
  addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3773
3967
  getBulkActions: () => this.bulkActions,
3774
- getDocumentActions: () => this.documentActions,
3968
+ getDocumentActions: (position) => {
3969
+ if (position) {
3970
+ return this.documentActions.filter(
3971
+ (action) => action.position == void 0 || [action.position].flat().includes(position)
3972
+ );
3973
+ }
3974
+ return this.documentActions;
3975
+ },
3775
3976
  getEditViewSidePanels: () => this.editViewSidePanels,
3776
3977
  getHeaderActions: () => this.headerActions
3777
3978
  }
@@ -3781,10 +3982,8 @@ class ContentManagerPlugin {
3781
3982
  const getPrintableType = (value) => {
3782
3983
  const nativeType = typeof value;
3783
3984
  if (nativeType === "object") {
3784
- if (value === null)
3785
- return "null";
3786
- if (Array.isArray(value))
3787
- return "array";
3985
+ if (value === null) return "null";
3986
+ if (Array.isArray(value)) return "array";
3788
3987
  if (value instanceof Object && value.constructor.name !== "Object") {
3789
3988
  return value.constructor.name;
3790
3989
  }
@@ -3795,17 +3994,27 @@ const HistoryAction = ({ model, document }) => {
3795
3994
  const { formatMessage } = useIntl();
3796
3995
  const [{ query }] = useQueryParams();
3797
3996
  const navigate = useNavigate();
3997
+ const { trackUsage } = useTracking();
3998
+ const { pathname } = useLocation();
3798
3999
  const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
3799
4000
  if (!window.strapi.features.isEnabled("cms-content-history")) {
3800
4001
  return null;
3801
4002
  }
4003
+ const handleOnClick = () => {
4004
+ const destination = { pathname: "history", search: pluginsQueryParams };
4005
+ trackUsage("willNavigate", {
4006
+ from: pathname,
4007
+ to: `${pathname}/${destination.pathname}`
4008
+ });
4009
+ navigate(destination);
4010
+ };
3802
4011
  return {
3803
4012
  icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
3804
4013
  label: formatMessage({
3805
4014
  id: "content-manager.history.document-action",
3806
4015
  defaultMessage: "Content History"
3807
4016
  }),
3808
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
4017
+ onClick: handleOnClick,
3809
4018
  disabled: (
3810
4019
  /**
3811
4020
  * The user is creating a new document.
@@ -3827,6 +4036,7 @@ const HistoryAction = ({ model, document }) => {
3827
4036
  };
3828
4037
  };
3829
4038
  HistoryAction.type = "history";
4039
+ HistoryAction.position = "header";
3830
4040
  const historyAdmin = {
3831
4041
  bootstrap(app) {
3832
4042
  const { addDocumentAction } = app.getPlugin("content-manager").apis;
@@ -3873,6 +4083,88 @@ const { setInitialData } = actions;
3873
4083
  const reducer = combineReducers({
3874
4084
  app: reducer$1
3875
4085
  });
4086
+ const previewApi = contentManagerApi.injectEndpoints({
4087
+ endpoints: (builder) => ({
4088
+ getPreviewUrl: builder.query({
4089
+ query({ query, params }) {
4090
+ return {
4091
+ url: `/content-manager/preview/url/${params.contentType}`,
4092
+ method: "GET",
4093
+ config: {
4094
+ params: query
4095
+ }
4096
+ };
4097
+ }
4098
+ })
4099
+ })
4100
+ });
4101
+ const { useGetPreviewUrlQuery } = previewApi;
4102
+ const ConditionalTooltip = ({ isShown, label, children }) => {
4103
+ if (isShown) {
4104
+ return /* @__PURE__ */ jsx(Tooltip, { label, children });
4105
+ }
4106
+ return children;
4107
+ };
4108
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4109
+ const { formatMessage } = useIntl();
4110
+ const { trackUsage } = useTracking();
4111
+ const { pathname } = useLocation();
4112
+ const [{ query }] = useQueryParams();
4113
+ const isModified = useForm("PreviewSidePanel", (state) => state.modified);
4114
+ const { data, error } = useGetPreviewUrlQuery({
4115
+ params: {
4116
+ contentType: model
4117
+ },
4118
+ query: {
4119
+ documentId,
4120
+ locale: document?.locale,
4121
+ status: document?.status
4122
+ }
4123
+ });
4124
+ if (!data?.data?.url || error) {
4125
+ return null;
4126
+ }
4127
+ const trackNavigation = () => {
4128
+ const destinationPathname = pathname.replace(/\/$/, "") + "/preview";
4129
+ trackUsage("willNavigate", { from: pathname, to: destinationPathname });
4130
+ };
4131
+ return {
4132
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4133
+ content: /* @__PURE__ */ jsx(
4134
+ ConditionalTooltip,
4135
+ {
4136
+ label: formatMessage({
4137
+ id: "content-manager.preview.panel.button-disabled-tooltip",
4138
+ defaultMessage: "Please save to open the preview"
4139
+ }),
4140
+ isShown: isModified,
4141
+ children: /* @__PURE__ */ jsx(Box, { cursor: "not-allowed", width: "100%", children: /* @__PURE__ */ jsx(
4142
+ Button,
4143
+ {
4144
+ variant: "tertiary",
4145
+ tag: Link,
4146
+ to: { pathname: "preview", search: stringify(query, { encode: false }) },
4147
+ onClick: trackNavigation,
4148
+ width: "100%",
4149
+ disabled: isModified,
4150
+ pointerEvents: isModified ? "none" : void 0,
4151
+ tabIndex: isModified ? -1 : void 0,
4152
+ children: formatMessage({
4153
+ id: "content-manager.preview.panel.button",
4154
+ defaultMessage: "Open preview"
4155
+ })
4156
+ }
4157
+ ) })
4158
+ }
4159
+ )
4160
+ };
4161
+ };
4162
+ const previewAdmin = {
4163
+ bootstrap(app) {
4164
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4165
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4166
+ }
4167
+ };
3876
4168
  const index = {
3877
4169
  register(app) {
3878
4170
  const cm = new ContentManagerPlugin();
@@ -3892,7 +4184,7 @@ const index = {
3892
4184
  app.router.addRoute({
3893
4185
  path: "content-manager/*",
3894
4186
  lazy: async () => {
3895
- const { Layout } = await import("./layout-0TY7UtKO.mjs");
4187
+ const { Layout } = await import("./layout-B5qsPihj.mjs");
3896
4188
  return {
3897
4189
  Component: Layout
3898
4190
  };
@@ -3905,11 +4197,14 @@ const index = {
3905
4197
  if (typeof historyAdmin.bootstrap === "function") {
3906
4198
  historyAdmin.bootstrap(app);
3907
4199
  }
4200
+ if (typeof previewAdmin.bootstrap === "function") {
4201
+ previewAdmin.bootstrap(app);
4202
+ }
3908
4203
  },
3909
4204
  async registerTrads({ locales }) {
3910
4205
  const importedTrads = await Promise.all(
3911
4206
  locales.map((locale) => {
3912
- 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-DCszE74t.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 }) => {
4207
+ 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-Dtk_ot79.mjs"), "./translations/es.json": () => import("./es-D34tqjMw.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr--pg5jUbt.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-BHqhDq4V.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`, 3).then(({ default: data }) => {
3913
4208
  return {
3914
4209
  data: prefixPluginTranslations(data, PLUGIN_ID),
3915
4210
  locale
@@ -3936,8 +4231,10 @@ export {
3936
4231
  HOOKS as H,
3937
4232
  InjectionZone as I,
3938
4233
  useDocument as J,
3939
- index as K,
3940
- useDocumentActions as L,
4234
+ useGetPreviewUrlQuery as K,
4235
+ index as L,
4236
+ useContentManagerContext as M,
4237
+ useDocumentActions as N,
3941
4238
  Panels as P,
3942
4239
  RelativeTime as R,
3943
4240
  SINGLE_TYPES as S,
@@ -3969,4 +4266,4 @@ export {
3969
4266
  capitalise as y,
3970
4267
  useUpdateContentTypeConfigurationMutation as z
3971
4268
  };
3972
- //# sourceMappingURL=index-BYSWwHBJ.mjs.map
4269
+ //# sourceMappingURL=index-Dh2aGTGJ.mjs.map