@strapi/content-manager 0.0.0-experimental.cb311d9fcfbd8e441f790aea232f0a39bdd90e16 → 0.0.0-experimental.cfda358b7f27015e34e739b8742a2962ae2e7aee

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 (212) 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-DL1MHO8i.js → ComponentConfigurationPage-BTR_hQow.js} +5 -6
  4. package/dist/_chunks/{ComponentConfigurationPage-DL1MHO8i.js.map → ComponentConfigurationPage-BTR_hQow.js.map} +1 -1
  5. package/dist/_chunks/{ComponentConfigurationPage-D0dyDTwq.mjs → ComponentConfigurationPage-bLQr82ce.mjs} +4 -4
  6. package/dist/_chunks/{ComponentConfigurationPage-D0dyDTwq.mjs.map → ComponentConfigurationPage-bLQr82ce.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-13b7S5Cq.mjs → EditConfigurationPage-BhRSnUsL.mjs} +4 -4
  11. package/dist/_chunks/{EditConfigurationPage-13b7S5Cq.mjs.map → EditConfigurationPage-BhRSnUsL.mjs.map} +1 -1
  12. package/dist/_chunks/{EditConfigurationPage-CMaOf-A-.js → EditConfigurationPage-z39Wv3E6.js} +5 -6
  13. package/dist/_chunks/{EditConfigurationPage-CMaOf-A-.js.map → EditConfigurationPage-z39Wv3E6.js.map} +1 -1
  14. package/dist/_chunks/{EditViewPage-C3tIZ8F5.mjs → EditViewPage-BCjNxNlY.mjs} +63 -12
  15. package/dist/_chunks/EditViewPage-BCjNxNlY.mjs.map +1 -0
  16. package/dist/_chunks/{EditViewPage-BSVmMpRd.js → EditViewPage-wujOq90c.js} +63 -13
  17. package/dist/_chunks/EditViewPage-wujOq90c.js.map +1 -0
  18. package/dist/_chunks/{Field-DUCVth4C.js → Field-B5QXnctJ.js} +320 -169
  19. package/dist/_chunks/Field-B5QXnctJ.js.map +1 -0
  20. package/dist/_chunks/{Field-BvuT8cGL.mjs → Field-Byr3mPTl.mjs} +316 -165
  21. package/dist/_chunks/Field-Byr3mPTl.mjs.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-BZmDNVr9.mjs → Form-BZgvE8C8.mjs} +37 -18
  25. package/dist/_chunks/Form-BZgvE8C8.mjs.map +1 -0
  26. package/dist/_chunks/{Form-Cpl4W1ak.js → Form-D7mexvm3.js} +39 -21
  27. package/dist/_chunks/Form-D7mexvm3.js.map +1 -0
  28. package/dist/_chunks/{History-Cq_Hrzuu.mjs → History-CqNgxkqK.mjs} +43 -100
  29. package/dist/_chunks/History-CqNgxkqK.mjs.map +1 -0
  30. package/dist/_chunks/{History-D4U2YISB.js → History-DYl2A8Z_.js} +42 -100
  31. package/dist/_chunks/History-DYl2A8Z_.js.map +1 -0
  32. package/dist/_chunks/{ListConfigurationPage-Bny6CdWe.js → ListConfigurationPage-BXnu_OoY.js} +19 -9
  33. package/dist/_chunks/ListConfigurationPage-BXnu_OoY.js.map +1 -0
  34. package/dist/_chunks/{ListConfigurationPage-W-KQHmBv.mjs → ListConfigurationPage-BbQjzKkQ.mjs} +19 -8
  35. package/dist/_chunks/ListConfigurationPage-BbQjzKkQ.mjs.map +1 -0
  36. package/dist/_chunks/{ListViewPage-O8F1pBJo.js → ListViewPage-BtSi8C1l.js} +103 -77
  37. package/dist/_chunks/ListViewPage-BtSi8C1l.js.map +1 -0
  38. package/dist/_chunks/{ListViewPage-HBBnJa8K.mjs → ListViewPage-D4ofkbjR.mjs} +99 -72
  39. package/dist/_chunks/ListViewPage-D4ofkbjR.mjs.map +1 -0
  40. package/dist/_chunks/{NoContentTypePage-CQWChGPw.js → NoContentTypePage-CitJeOq4.js} +2 -2
  41. package/dist/_chunks/{NoContentTypePage-CQWChGPw.js.map → NoContentTypePage-CitJeOq4.js.map} +1 -1
  42. package/dist/_chunks/{NoContentTypePage-B-gIhHWM.mjs → NoContentTypePage-DyUx5mXh.mjs} +2 -2
  43. package/dist/_chunks/{NoContentTypePage-B-gIhHWM.mjs.map → NoContentTypePage-DyUx5mXh.mjs.map} +1 -1
  44. package/dist/_chunks/{NoPermissionsPage-XhOPl8wx.mjs → NoPermissionsPage-DhIiyWkk.mjs} +2 -2
  45. package/dist/_chunks/{NoPermissionsPage-XhOPl8wx.mjs.map → NoPermissionsPage-DhIiyWkk.mjs.map} +1 -1
  46. package/dist/_chunks/{NoPermissionsPage-CY46zxnM.js → NoPermissionsPage-DzgWz0M-.js} +2 -2
  47. package/dist/_chunks/{NoPermissionsPage-CY46zxnM.js.map → NoPermissionsPage-DzgWz0M-.js.map} +1 -1
  48. package/dist/_chunks/Preview-BaYGJ0nb.mjs +293 -0
  49. package/dist/_chunks/Preview-BaYGJ0nb.mjs.map +1 -0
  50. package/dist/_chunks/Preview-DfNx8Ke-.js +311 -0
  51. package/dist/_chunks/Preview-DfNx8Ke-.js.map +1 -0
  52. package/dist/_chunks/{Relations-vFZ6Wasg.mjs → Relations-DM2yUTST.mjs} +76 -42
  53. package/dist/_chunks/Relations-DM2yUTST.mjs.map +1 -0
  54. package/dist/_chunks/{Relations-C4gGfZRv.js → Relations-DuKCaXrv.js} +76 -43
  55. package/dist/_chunks/Relations-DuKCaXrv.js.map +1 -0
  56. package/dist/_chunks/{en-uOUIxfcQ.js → en-BK8Xyl5I.js} +28 -15
  57. package/dist/_chunks/{en-uOUIxfcQ.js.map → en-BK8Xyl5I.js.map} +1 -1
  58. package/dist/_chunks/{en-BrCTWlZv.mjs → en-Dtk_ot79.mjs} +28 -15
  59. package/dist/_chunks/{en-BrCTWlZv.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-5EMXLEM_.js → index-BUWEmX8m.js} +1320 -996
  70. package/dist/_chunks/index-BUWEmX8m.js.map +1 -0
  71. package/dist/_chunks/{index-Dpxg3ctD.mjs → index-DVAIIsOs.mjs} +1338 -1014
  72. package/dist/_chunks/index-DVAIIsOs.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-C0INpKap.mjs → layout-Bxsv5mP7.mjs} +9 -8
  78. package/dist/_chunks/layout-Bxsv5mP7.mjs.map +1 -0
  79. package/dist/_chunks/{layout-P3eKO1Qy.js → layout-C3fN7Ejz.js} +10 -10
  80. package/dist/_chunks/layout-C3fN7Ejz.js.map +1 -0
  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-B1y0K6LE.js → relations-BPZKAoEY.js} +6 -7
  86. package/dist/_chunks/relations-BPZKAoEY.js.map +1 -0
  87. package/dist/_chunks/{relations-FBRRBWeO.mjs → relations-o3pPhzY4.mjs} +6 -7
  88. package/dist/_chunks/relations-o3pPhzY4.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 +5 -4
  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/history/services/historyVersion.d.ts +1 -1
  103. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  104. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  105. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -1
  106. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +2 -2
  107. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Code.d.ts +7 -0
  108. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/utils/prismLanguages.d.ts +49 -0
  109. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +1 -0
  110. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  111. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  112. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  113. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  114. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  115. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  116. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  117. package/dist/admin/src/preview/index.d.ts +4 -0
  118. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  119. package/dist/admin/src/preview/routes.d.ts +3 -0
  120. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  121. package/dist/admin/src/router.d.ts +1 -1
  122. package/dist/admin/src/services/api.d.ts +1 -1
  123. package/dist/admin/src/services/components.d.ts +2 -2
  124. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  125. package/dist/admin/src/services/documents.d.ts +19 -20
  126. package/dist/admin/src/services/init.d.ts +1 -1
  127. package/dist/admin/src/services/relations.d.ts +2 -2
  128. package/dist/admin/src/services/uid.d.ts +3 -3
  129. package/dist/admin/src/utils/validation.d.ts +4 -1
  130. package/dist/server/index.js +482 -219
  131. package/dist/server/index.js.map +1 -1
  132. package/dist/server/index.mjs +482 -218
  133. package/dist/server/index.mjs.map +1 -1
  134. package/dist/server/src/bootstrap.d.ts.map +1 -1
  135. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  136. package/dist/server/src/controllers/index.d.ts.map +1 -1
  137. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  138. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  139. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  140. package/dist/server/src/history/services/history.d.ts.map +1 -1
  141. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  142. package/dist/server/src/history/services/utils.d.ts +3 -3
  143. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  144. package/dist/server/src/index.d.ts +4 -4
  145. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  146. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  147. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  148. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  149. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  150. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  151. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  152. package/dist/server/src/preview/index.d.ts +4 -0
  153. package/dist/server/src/preview/index.d.ts.map +1 -0
  154. package/dist/server/src/preview/routes/index.d.ts +8 -0
  155. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  156. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  157. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  158. package/dist/server/src/preview/services/index.d.ts +16 -0
  159. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  160. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  161. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  162. package/dist/server/src/preview/services/preview.d.ts +12 -0
  163. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  164. package/dist/server/src/preview/utils.d.ts +19 -0
  165. package/dist/server/src/preview/utils.d.ts.map +1 -0
  166. package/dist/server/src/register.d.ts.map +1 -1
  167. package/dist/server/src/routes/index.d.ts.map +1 -1
  168. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  169. package/dist/server/src/services/document-metadata.d.ts +8 -8
  170. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  171. package/dist/server/src/services/index.d.ts +4 -4
  172. package/dist/server/src/services/index.d.ts.map +1 -1
  173. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  174. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  175. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  176. package/dist/server/src/utils/index.d.ts +2 -0
  177. package/dist/server/src/utils/index.d.ts.map +1 -1
  178. package/dist/shared/contracts/collection-types.d.ts +3 -1
  179. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  180. package/dist/shared/contracts/index.d.ts +1 -0
  181. package/dist/shared/contracts/index.d.ts.map +1 -1
  182. package/dist/shared/contracts/preview.d.ts +27 -0
  183. package/dist/shared/contracts/preview.d.ts.map +1 -0
  184. package/dist/shared/index.js +4 -0
  185. package/dist/shared/index.js.map +1 -1
  186. package/dist/shared/index.mjs +4 -0
  187. package/dist/shared/index.mjs.map +1 -1
  188. package/package.json +17 -15
  189. package/dist/_chunks/EditViewPage-BSVmMpRd.js.map +0 -1
  190. package/dist/_chunks/EditViewPage-C3tIZ8F5.mjs.map +0 -1
  191. package/dist/_chunks/Field-BvuT8cGL.mjs.map +0 -1
  192. package/dist/_chunks/Field-DUCVth4C.js.map +0 -1
  193. package/dist/_chunks/Form-BZmDNVr9.mjs.map +0 -1
  194. package/dist/_chunks/Form-Cpl4W1ak.js.map +0 -1
  195. package/dist/_chunks/History-Cq_Hrzuu.mjs.map +0 -1
  196. package/dist/_chunks/History-D4U2YISB.js.map +0 -1
  197. package/dist/_chunks/ListConfigurationPage-Bny6CdWe.js.map +0 -1
  198. package/dist/_chunks/ListConfigurationPage-W-KQHmBv.mjs.map +0 -1
  199. package/dist/_chunks/ListViewPage-HBBnJa8K.mjs.map +0 -1
  200. package/dist/_chunks/ListViewPage-O8F1pBJo.js.map +0 -1
  201. package/dist/_chunks/Relations-C4gGfZRv.js.map +0 -1
  202. package/dist/_chunks/Relations-vFZ6Wasg.mjs.map +0 -1
  203. package/dist/_chunks/index-5EMXLEM_.js.map +0 -1
  204. package/dist/_chunks/index-Dpxg3ctD.mjs.map +0 -1
  205. package/dist/_chunks/layout-C0INpKap.mjs.map +0 -1
  206. package/dist/_chunks/layout-P3eKO1Qy.js.map +0 -1
  207. package/dist/_chunks/relations-B1y0K6LE.js.map +0 -1
  208. package/dist/_chunks/relations-FBRRBWeO.mjs.map +0 -1
  209. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  210. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  211. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  212. package/strapi-server.js +0 -3
@@ -1,25 +1,33 @@
1
- import { CrossCircle, More, WarningCircle, ListPlus, Pencil, Trash, Check, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
1
+ import { More, Cross, WarningCircle, ListPlus, Pencil, Trash, Check, CrossCircle, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
2
2
  import { jsx, Fragment, jsxs } from "react/jsx-runtime";
3
- import { useStrapiApp, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useQueryParams, useTracking, useForm, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
3
+ import { useStrapiApp, createContext, useQueryParams, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useForm, useTracking, useGuidedTour, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
4
4
  import * as React from "react";
5
5
  import { lazy } from "react";
6
- import { Button, Menu, VisuallyHidden, Flex, 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
- import { useParams, Navigate, useNavigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
9
+ import { useParams, useNavigate, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
9
10
  import { styled } from "styled-components";
10
11
  import * as yup from "yup";
11
12
  import { ValidationError } from "yup";
13
+ import { stringify } from "qs";
12
14
  import pipe from "lodash/fp/pipe";
13
15
  import { intervalToDuration, isPast } from "date-fns";
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) : [];
@@ -158,7 +174,8 @@ const contentManagerApi = adminApi.enhanceEndpoints({
158
174
  "Document",
159
175
  "InitialData",
160
176
  "HistoryVersion",
161
- "Relations"
177
+ "Relations",
178
+ "UidAvailability"
162
179
  ]
163
180
  });
164
181
  const documentApi = contentManagerApi.injectEndpoints({
@@ -188,7 +205,10 @@ const documentApi = contentManagerApi.injectEndpoints({
188
205
  params
189
206
  }
190
207
  }),
191
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
208
+ invalidatesTags: (_result, _error, { model }) => [
209
+ { type: "Document", id: `${model}_LIST` },
210
+ { type: "UidAvailability", id: model }
211
+ ]
192
212
  }),
193
213
  /**
194
214
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -205,7 +225,8 @@ const documentApi = contentManagerApi.injectEndpoints({
205
225
  }),
206
226
  invalidatesTags: (result, _error, { model }) => [
207
227
  { type: "Document", id: `${model}_LIST` },
208
- "Relations"
228
+ "Relations",
229
+ { type: "UidAvailability", id: model }
209
230
  ]
210
231
  }),
211
232
  deleteDocument: builder.mutation({
@@ -246,7 +267,8 @@ const documentApi = contentManagerApi.injectEndpoints({
246
267
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
247
268
  },
248
269
  { type: "Document", id: `${model}_LIST` },
249
- "Relations"
270
+ "Relations",
271
+ { type: "UidAvailability", id: model }
250
272
  ];
251
273
  }
252
274
  }),
@@ -259,7 +281,7 @@ const documentApi = contentManagerApi.injectEndpoints({
259
281
  url: `/content-manager/collection-types/${model}`,
260
282
  method: "GET",
261
283
  config: {
262
- params
284
+ params: stringify(params, { encode: true })
263
285
  }
264
286
  }),
265
287
  providesTags: (result, _error, arg) => {
@@ -371,7 +393,8 @@ const documentApi = contentManagerApi.injectEndpoints({
371
393
  type: "Document",
372
394
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
373
395
  },
374
- "Relations"
396
+ "Relations",
397
+ { type: "UidAvailability", id: model }
375
398
  ];
376
399
  },
377
400
  async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
@@ -437,8 +460,7 @@ const {
437
460
  useUnpublishManyDocumentsMutation
438
461
  } = documentApi;
439
462
  const buildValidParams = (query) => {
440
- if (!query)
441
- return query;
463
+ if (!query) return query;
442
464
  const { plugins: _, ...validQueryParams } = {
443
465
  ...query,
444
466
  ...Object.values(query?.plugins ?? {}).reduce(
@@ -446,28 +468,44 @@ const buildValidParams = (query) => {
446
468
  {}
447
469
  )
448
470
  };
449
- if ("_q" in validQueryParams) {
450
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
451
- }
452
471
  return validQueryParams;
453
472
  };
454
473
  const isBaseQueryError = (error) => {
455
474
  return error.name !== void 0;
456
475
  };
457
- const createYupSchema = (attributes = {}, components = {}) => {
476
+ const arrayValidator = (attribute, options) => ({
477
+ message: translatedErrors.required,
478
+ test(value) {
479
+ if (options.status === "draft") {
480
+ return true;
481
+ }
482
+ if (!attribute.required) {
483
+ return true;
484
+ }
485
+ if (!value) {
486
+ return false;
487
+ }
488
+ if (Array.isArray(value) && value.length === 0) {
489
+ return false;
490
+ }
491
+ return true;
492
+ }
493
+ });
494
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
458
495
  const createModelSchema = (attributes2) => yup.object().shape(
459
496
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
460
497
  if (DOCUMENT_META_FIELDS.includes(name)) {
461
498
  return acc;
462
499
  }
463
500
  const validations = [
501
+ addNullableValidation,
464
502
  addRequiredValidation,
465
503
  addMinLengthValidation,
466
504
  addMaxLengthValidation,
467
505
  addMinValidation,
468
506
  addMaxValidation,
469
507
  addRegexValidation
470
- ].map((fn) => fn(attribute));
508
+ ].map((fn) => fn(attribute, options));
471
509
  const transformSchema = pipe(...validations);
472
510
  switch (attribute.type) {
473
511
  case "component": {
@@ -477,12 +515,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
477
515
  ...acc,
478
516
  [name]: transformSchema(
479
517
  yup.array().of(createModelSchema(attributes3).nullable(false))
480
- )
518
+ ).test(arrayValidator(attribute, options))
481
519
  };
482
520
  } else {
483
521
  return {
484
522
  ...acc,
485
- [name]: transformSchema(createModelSchema(attributes3))
523
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
486
524
  };
487
525
  }
488
526
  }
@@ -504,7 +542,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
504
542
  }
505
543
  )
506
544
  )
507
- )
545
+ ).test(arrayValidator(attribute, options))
508
546
  };
509
547
  case "relation":
510
548
  return {
@@ -516,7 +554,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
516
554
  } else if (Array.isArray(value)) {
517
555
  return yup.array().of(
518
556
  yup.object().shape({
519
- id: yup.string().required()
557
+ id: yup.number().required()
520
558
  })
521
559
  );
522
560
  } else if (typeof value === "object") {
@@ -594,13 +632,7 @@ const createAttributeSchema = (attribute) => {
594
632
  return yup.mixed();
595
633
  }
596
634
  };
597
- const addRequiredValidation = (attribute) => (schema) => {
598
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
599
- return schema.min(1, translatedErrors.required);
600
- }
601
- if (attribute.required && attribute.type !== "relation") {
602
- return schema.required(translatedErrors.required);
603
- }
635
+ const nullableSchema = (schema) => {
604
636
  return schema?.nullable ? schema.nullable() : (
605
637
  // In some cases '.nullable' will not be available on the schema.
606
638
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -608,7 +640,22 @@ const addRequiredValidation = (attribute) => (schema) => {
608
640
  schema
609
641
  );
610
642
  };
611
- const addMinLengthValidation = (attribute) => (schema) => {
643
+ const addNullableValidation = () => (schema) => {
644
+ return nullableSchema(schema);
645
+ };
646
+ const addRequiredValidation = (attribute, options) => (schema) => {
647
+ if (options.status === "draft" || !attribute.required) {
648
+ return schema;
649
+ }
650
+ if (attribute.required && "required" in schema) {
651
+ return schema.required(translatedErrors.required);
652
+ }
653
+ return schema;
654
+ };
655
+ const addMinLengthValidation = (attribute, options) => (schema) => {
656
+ if (options.status === "draft") {
657
+ return schema;
658
+ }
612
659
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
613
660
  return schema.min(attribute.minLength, {
614
661
  ...translatedErrors.minLength,
@@ -630,32 +677,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
630
677
  }
631
678
  return schema;
632
679
  };
633
- const addMinValidation = (attribute) => (schema) => {
634
- if ("min" in attribute) {
680
+ const addMinValidation = (attribute, options) => (schema) => {
681
+ if (options.status === "draft") {
682
+ return schema;
683
+ }
684
+ if ("min" in attribute && "min" in schema) {
635
685
  const min = toInteger(attribute.min);
636
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
637
- if (!attribute.required && "test" in schema && min) {
638
- return schema.test(
639
- "custom-min",
640
- {
641
- ...translatedErrors.min,
642
- values: {
643
- min: attribute.min
644
- }
645
- },
646
- (value) => {
647
- if (!value) {
648
- return true;
649
- }
650
- if (Array.isArray(value) && value.length === 0) {
651
- return true;
652
- }
653
- return value.length >= min;
654
- }
655
- );
656
- }
657
- }
658
- if ("min" in schema && min) {
686
+ if (min) {
659
687
  return schema.min(min, {
660
688
  ...translatedErrors.min,
661
689
  values: {
@@ -773,19 +801,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
773
801
  }, {});
774
802
  return componentsByKey;
775
803
  };
776
- const useDocument = (args, opts) => {
804
+ const HOOKS = {
805
+ /**
806
+ * Hook that allows to mutate the displayed headers of the list view table
807
+ * @constant
808
+ * @type {string}
809
+ */
810
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
811
+ /**
812
+ * Hook that allows to mutate the CM's collection types links pre-set filters
813
+ * @constant
814
+ * @type {string}
815
+ */
816
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
817
+ /**
818
+ * Hook that allows to mutate the CM's edit view layout
819
+ * @constant
820
+ * @type {string}
821
+ */
822
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
823
+ /**
824
+ * Hook that allows to mutate the CM's single types links pre-set filters
825
+ * @constant
826
+ * @type {string}
827
+ */
828
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
829
+ };
830
+ const contentTypesApi = contentManagerApi.injectEndpoints({
831
+ endpoints: (builder) => ({
832
+ getContentTypeConfiguration: builder.query({
833
+ query: (uid) => ({
834
+ url: `/content-manager/content-types/${uid}/configuration`,
835
+ method: "GET"
836
+ }),
837
+ transformResponse: (response) => response.data,
838
+ providesTags: (_result, _error, uid) => [
839
+ { type: "ContentTypesConfiguration", id: uid },
840
+ { type: "ContentTypeSettings", id: "LIST" }
841
+ ]
842
+ }),
843
+ getAllContentTypeSettings: builder.query({
844
+ query: () => "/content-manager/content-types-settings",
845
+ transformResponse: (response) => response.data,
846
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
847
+ }),
848
+ updateContentTypeConfiguration: builder.mutation({
849
+ query: ({ uid, ...body }) => ({
850
+ url: `/content-manager/content-types/${uid}/configuration`,
851
+ method: "PUT",
852
+ data: body
853
+ }),
854
+ transformResponse: (response) => response.data,
855
+ invalidatesTags: (_result, _error, { uid }) => [
856
+ { type: "ContentTypesConfiguration", id: uid },
857
+ { type: "ContentTypeSettings", id: "LIST" },
858
+ // Is this necessary?
859
+ { type: "InitialData" }
860
+ ]
861
+ })
862
+ })
863
+ });
864
+ const {
865
+ useGetContentTypeConfigurationQuery,
866
+ useGetAllContentTypeSettingsQuery,
867
+ useUpdateContentTypeConfigurationMutation
868
+ } = contentTypesApi;
869
+ const checkIfAttributeIsDisplayable = (attribute) => {
870
+ const { type } = attribute;
871
+ if (type === "relation") {
872
+ return !attribute.relation.toLowerCase().includes("morph");
873
+ }
874
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
875
+ };
876
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
877
+ if (!mainFieldName) {
878
+ return void 0;
879
+ }
880
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
881
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
882
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
883
+ );
884
+ return {
885
+ name: mainFieldName,
886
+ type: mainFieldType ?? "string"
887
+ };
888
+ };
889
+ const DEFAULT_SETTINGS = {
890
+ bulkable: false,
891
+ filterable: false,
892
+ searchable: false,
893
+ pagination: false,
894
+ defaultSortBy: "",
895
+ defaultSortOrder: "asc",
896
+ mainField: "id",
897
+ pageSize: 10
898
+ };
899
+ const useDocumentLayout = (model) => {
900
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
901
+ const [{ query }] = useQueryParams();
902
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
777
903
  const { toggleNotification } = useNotification();
778
904
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
905
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
779
906
  const {
780
- currentData: data,
781
- isLoading: isLoadingDocument,
782
- isFetching: isFetchingDocument,
783
- error
784
- } = useGetDocumentQuery(args, {
785
- ...opts,
786
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
787
- });
788
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
907
+ data,
908
+ isLoading: isLoadingConfigs,
909
+ error,
910
+ isFetching: isFetchingConfigs
911
+ } = useGetContentTypeConfigurationQuery(model);
912
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
789
913
  React.useEffect(() => {
790
914
  if (error) {
791
915
  toggleNotification({
@@ -793,397 +917,654 @@ const useDocument = (args, opts) => {
793
917
  message: formatAPIError(error)
794
918
  });
795
919
  }
796
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
797
- const validationSchema = React.useMemo(() => {
798
- if (!schema) {
799
- return null;
800
- }
801
- return createYupSchema(schema.attributes, components);
802
- }, [schema, components]);
803
- const validate = React.useCallback(
804
- (document) => {
805
- if (!validationSchema) {
806
- throw new Error(
807
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
808
- );
809
- }
810
- try {
811
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
812
- return null;
813
- } catch (error2) {
814
- if (error2 instanceof ValidationError) {
815
- return getYupValidationErrors(error2);
816
- }
817
- throw error2;
818
- }
920
+ }, [error, formatAPIError, toggleNotification]);
921
+ const editLayout = React.useMemo(
922
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
923
+ layout: [],
924
+ components: {},
925
+ metadatas: {},
926
+ options: {},
927
+ settings: DEFAULT_SETTINGS
819
928
  },
820
- [validationSchema]
929
+ [data, isLoading, schemas, schema, components]
930
+ );
931
+ const listLayout = React.useMemo(() => {
932
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
933
+ layout: [],
934
+ metadatas: {},
935
+ options: {},
936
+ settings: DEFAULT_SETTINGS
937
+ };
938
+ }, [data, isLoading, schemas, schema, components]);
939
+ const { layout: edit } = React.useMemo(
940
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
941
+ layout: editLayout,
942
+ query
943
+ }),
944
+ [editLayout, query, runHookWaterfall]
821
945
  );
822
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
823
946
  return {
824
- components,
825
- document: data?.data,
826
- meta: data?.meta,
947
+ error,
827
948
  isLoading,
828
- schema,
829
- validate
830
- };
831
- };
832
- const useDoc = () => {
833
- const { id, slug, collectionType, origin } = useParams();
834
- const [{ query }] = useQueryParams();
835
- const params = React.useMemo(() => buildValidParams(query), [query]);
836
- if (!collectionType) {
837
- throw new Error("Could not find collectionType in url params");
838
- }
839
- if (!slug) {
840
- throw new Error("Could not find model in url params");
841
- }
842
- return {
843
- collectionType,
844
- model: slug,
845
- id: origin || id === "create" ? void 0 : id,
846
- ...useDocument(
847
- { documentId: origin || id, model: slug, collectionType, params },
848
- {
849
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
850
- }
851
- )
949
+ edit,
950
+ list: listLayout
852
951
  };
853
952
  };
854
- const prefixPluginTranslations = (trad, pluginId) => {
855
- if (!pluginId) {
856
- throw new TypeError("pluginId can't be empty");
857
- }
858
- return Object.keys(trad).reduce((acc, current) => {
859
- acc[`${pluginId}.${current}`] = trad[current];
860
- return acc;
861
- }, {});
862
- };
863
- const getTranslation = (id) => `content-manager.${id}`;
864
- const DEFAULT_UNEXPECTED_ERROR_MSG = {
865
- id: "notification.error",
866
- defaultMessage: "An error occurred, please try again"
953
+ const useDocLayout = () => {
954
+ const { model } = useDoc();
955
+ return useDocumentLayout(model);
867
956
  };
868
- const useDocumentActions = () => {
869
- const { toggleNotification } = useNotification();
870
- const { formatMessage } = useIntl();
871
- const { trackUsage } = useTracking();
872
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
873
- const [deleteDocument] = useDeleteDocumentMutation();
874
- const _delete = React.useCallback(
875
- async ({ collectionType, model, documentId, params }, trackerProperty) => {
876
- try {
877
- trackUsage("willDeleteEntry", trackerProperty);
878
- const res = await deleteDocument({
879
- collectionType,
880
- model,
881
- documentId,
882
- params
883
- });
884
- if ("error" in res) {
885
- toggleNotification({
886
- type: "danger",
887
- message: formatAPIError(res.error)
888
- });
889
- return { error: res.error };
890
- }
891
- toggleNotification({
892
- type: "success",
893
- message: formatMessage({
894
- id: getTranslation("success.record.delete"),
895
- defaultMessage: "Deleted document"
896
- })
897
- });
898
- trackUsage("didDeleteEntry", trackerProperty);
899
- return res.data;
900
- } catch (err) {
901
- toggleNotification({
902
- type: "danger",
903
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
904
- });
905
- trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
906
- throw err;
957
+ const formatEditLayout = (data, {
958
+ schemas,
959
+ schema,
960
+ components
961
+ }) => {
962
+ let currentPanelIndex = 0;
963
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
964
+ data.contentType.layouts.edit,
965
+ schema?.attributes,
966
+ data.contentType.metadatas,
967
+ { configurations: data.components, schemas: components },
968
+ schemas
969
+ ).reduce((panels, row) => {
970
+ if (row.some((field) => field.type === "dynamiczone")) {
971
+ panels.push([row]);
972
+ currentPanelIndex += 2;
973
+ } else {
974
+ if (!panels[currentPanelIndex]) {
975
+ panels.push([row]);
976
+ } else {
977
+ panels[currentPanelIndex].push(row);
907
978
  }
908
- },
909
- [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
910
- );
911
- const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
912
- const deleteMany = React.useCallback(
913
- async ({ model, documentIds, params }) => {
914
- try {
915
- trackUsage("willBulkDeleteEntries");
916
- const res = await deleteManyDocuments({
917
- model,
918
- documentIds,
919
- params
920
- });
921
- if ("error" in res) {
922
- toggleNotification({
923
- type: "danger",
924
- message: formatAPIError(res.error)
925
- });
926
- return { error: res.error };
979
+ }
980
+ return panels;
981
+ }, []);
982
+ const componentEditAttributes = Object.entries(data.components).reduce(
983
+ (acc, [uid, configuration]) => {
984
+ acc[uid] = {
985
+ layout: convertEditLayoutToFieldLayouts(
986
+ configuration.layouts.edit,
987
+ components[uid].attributes,
988
+ configuration.metadatas,
989
+ { configurations: data.components, schemas: components }
990
+ ),
991
+ settings: {
992
+ ...configuration.settings,
993
+ icon: components[uid].info.icon,
994
+ displayName: components[uid].info.displayName
927
995
  }
928
- toggleNotification({
929
- type: "success",
930
- title: formatMessage({
931
- id: getTranslation("success.records.delete"),
932
- defaultMessage: "Successfully deleted."
933
- }),
934
- message: ""
935
- });
936
- trackUsage("didBulkDeleteEntries");
937
- return res.data;
938
- } catch (err) {
939
- toggleNotification({
940
- type: "danger",
941
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
942
- });
943
- trackUsage("didNotBulkDeleteEntries");
944
- throw err;
945
- }
996
+ };
997
+ return acc;
946
998
  },
947
- [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
999
+ {}
948
1000
  );
949
- const [discardDocument] = useDiscardDocumentMutation();
950
- const discard = React.useCallback(
951
- async ({ collectionType, model, documentId, params }) => {
952
- try {
953
- const res = await discardDocument({
954
- collectionType,
955
- model,
956
- documentId,
957
- params
958
- });
959
- if ("error" in res) {
960
- toggleNotification({
961
- type: "danger",
962
- message: formatAPIError(res.error)
963
- });
964
- return { error: res.error };
965
- }
966
- toggleNotification({
967
- type: "success",
968
- message: formatMessage({
969
- id: "content-manager.success.record.discard",
970
- defaultMessage: "Changes discarded"
971
- })
972
- });
973
- return res.data;
974
- } catch (err) {
975
- toggleNotification({
976
- type: "danger",
977
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
978
- });
979
- throw err;
980
- }
1001
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1002
+ (acc, [attribute, metadata]) => {
1003
+ return {
1004
+ ...acc,
1005
+ [attribute]: metadata.edit
1006
+ };
981
1007
  },
982
- [discardDocument, formatAPIError, formatMessage, toggleNotification]
1008
+ {}
983
1009
  );
984
- const [publishDocument] = usePublishDocumentMutation();
985
- const publish = React.useCallback(
986
- async ({ collectionType, model, documentId, params }, data) => {
987
- try {
988
- trackUsage("willPublishEntry");
989
- const res = await publishDocument({
990
- collectionType,
991
- model,
992
- documentId,
993
- data,
994
- params
995
- });
996
- if ("error" in res) {
997
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
998
- return { error: res.error };
999
- }
1000
- trackUsage("didPublishEntry");
1001
- toggleNotification({
1002
- type: "success",
1003
- message: formatMessage({
1004
- id: getTranslation("success.record.publish"),
1005
- defaultMessage: "Published document"
1006
- })
1007
- });
1008
- return res.data;
1009
- } catch (err) {
1010
- toggleNotification({
1011
- type: "danger",
1012
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1013
- });
1014
- throw err;
1015
- }
1010
+ return {
1011
+ layout: panelledEditAttributes,
1012
+ components: componentEditAttributes,
1013
+ metadatas: editMetadatas,
1014
+ settings: {
1015
+ ...data.contentType.settings,
1016
+ displayName: schema?.info.displayName
1016
1017
  },
1017
- [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1018
- );
1019
- const [publishManyDocuments] = usePublishManyDocumentsMutation();
1020
- const publishMany = React.useCallback(
1021
- async ({ model, documentIds, params }) => {
1022
- try {
1023
- const res = await publishManyDocuments({
1024
- model,
1025
- documentIds,
1026
- params
1027
- });
1028
- if ("error" in res) {
1029
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1030
- return { error: res.error };
1031
- }
1032
- toggleNotification({
1033
- type: "success",
1034
- message: formatMessage({
1035
- id: getTranslation("success.record.publish"),
1036
- defaultMessage: "Published document"
1037
- })
1038
- });
1039
- return res.data;
1040
- } catch (err) {
1041
- toggleNotification({
1042
- type: "danger",
1043
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1044
- });
1045
- throw err;
1018
+ options: {
1019
+ ...schema?.options,
1020
+ ...schema?.pluginOptions,
1021
+ ...data.contentType.options
1022
+ }
1023
+ };
1024
+ };
1025
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1026
+ return rows.map(
1027
+ (row) => row.map((field) => {
1028
+ const attribute = attributes[field.name];
1029
+ if (!attribute) {
1030
+ return null;
1046
1031
  }
1047
- },
1048
- [
1049
- // trackUsage,
1050
- publishManyDocuments,
1051
- toggleNotification,
1052
- formatMessage,
1053
- formatAPIError
1054
- ]
1032
+ const { edit: metadata } = metadatas[field.name];
1033
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1034
+ return {
1035
+ attribute,
1036
+ disabled: !metadata.editable,
1037
+ hint: metadata.description,
1038
+ label: metadata.label ?? "",
1039
+ name: field.name,
1040
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1041
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1042
+ schemas,
1043
+ components: components?.schemas ?? {}
1044
+ }),
1045
+ placeholder: metadata.placeholder ?? "",
1046
+ required: attribute.required ?? false,
1047
+ size: field.size,
1048
+ unique: "unique" in attribute ? attribute.unique : false,
1049
+ visible: metadata.visible ?? true,
1050
+ type: attribute.type
1051
+ };
1052
+ }).filter((field) => field !== null)
1055
1053
  );
1056
- const [updateDocument] = useUpdateDocumentMutation();
1057
- const update = React.useCallback(
1058
- async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1054
+ };
1055
+ const formatListLayout = (data, {
1056
+ schemas,
1057
+ schema,
1058
+ components
1059
+ }) => {
1060
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1061
+ (acc, [attribute, metadata]) => {
1062
+ return {
1063
+ ...acc,
1064
+ [attribute]: metadata.list
1065
+ };
1066
+ },
1067
+ {}
1068
+ );
1069
+ const listAttributes = convertListLayoutToFieldLayouts(
1070
+ data.contentType.layouts.list,
1071
+ schema?.attributes,
1072
+ listMetadatas,
1073
+ { configurations: data.components, schemas: components },
1074
+ schemas
1075
+ );
1076
+ return {
1077
+ layout: listAttributes,
1078
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1079
+ metadatas: listMetadatas,
1080
+ options: {
1081
+ ...schema?.options,
1082
+ ...schema?.pluginOptions,
1083
+ ...data.contentType.options
1084
+ }
1085
+ };
1086
+ };
1087
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1088
+ return columns.map((name) => {
1089
+ const attribute = attributes[name];
1090
+ if (!attribute) {
1091
+ return null;
1092
+ }
1093
+ const metadata = metadatas[name];
1094
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1095
+ return {
1096
+ attribute,
1097
+ label: metadata.label ?? "",
1098
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1099
+ schemas,
1100
+ components: components?.schemas ?? {}
1101
+ }),
1102
+ name,
1103
+ searchable: metadata.searchable ?? true,
1104
+ sortable: metadata.sortable ?? true
1105
+ };
1106
+ }).filter((field) => field !== null);
1107
+ };
1108
+ const useDocument = (args, opts) => {
1109
+ const { toggleNotification } = useNotification();
1110
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1111
+ const {
1112
+ currentData: data,
1113
+ isLoading: isLoadingDocument,
1114
+ isFetching: isFetchingDocument,
1115
+ error
1116
+ } = useGetDocumentQuery(args, {
1117
+ ...opts,
1118
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1119
+ });
1120
+ const {
1121
+ components,
1122
+ schema,
1123
+ schemas,
1124
+ isLoading: isLoadingSchema
1125
+ } = useContentTypeSchema(args.model);
1126
+ React.useEffect(() => {
1127
+ if (error) {
1128
+ toggleNotification({
1129
+ type: "danger",
1130
+ message: formatAPIError(error)
1131
+ });
1132
+ }
1133
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1134
+ const validationSchema = React.useMemo(() => {
1135
+ if (!schema) {
1136
+ return null;
1137
+ }
1138
+ return createYupSchema(schema.attributes, components);
1139
+ }, [schema, components]);
1140
+ const validate = React.useCallback(
1141
+ (document) => {
1142
+ if (!validationSchema) {
1143
+ throw new Error(
1144
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1145
+ );
1146
+ }
1059
1147
  try {
1060
- trackUsage("willEditEntry", trackerProperty);
1061
- const res = await updateDocument({
1062
- collectionType,
1063
- model,
1064
- documentId,
1065
- data,
1066
- params
1067
- });
1068
- if ("error" in res) {
1069
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1070
- trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1071
- return { error: res.error };
1148
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1149
+ return null;
1150
+ } catch (error2) {
1151
+ if (error2 instanceof ValidationError) {
1152
+ return getYupValidationErrors(error2);
1072
1153
  }
1073
- trackUsage("didEditEntry", trackerProperty);
1074
- toggleNotification({
1075
- type: "success",
1076
- message: formatMessage({
1077
- id: getTranslation("success.record.save"),
1078
- defaultMessage: "Saved document"
1079
- })
1080
- });
1081
- return res.data;
1082
- } catch (err) {
1083
- trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1084
- toggleNotification({
1085
- type: "danger",
1086
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1087
- });
1088
- throw err;
1154
+ throw error2;
1089
1155
  }
1090
1156
  },
1091
- [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1157
+ [validationSchema]
1092
1158
  );
1093
- const [unpublishDocument] = useUnpublishDocumentMutation();
1094
- const unpublish = React.useCallback(
1095
- async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1159
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1160
+ const hasError = !!error;
1161
+ return {
1162
+ components,
1163
+ document: data?.data,
1164
+ meta: data?.meta,
1165
+ isLoading,
1166
+ hasError,
1167
+ schema,
1168
+ schemas,
1169
+ validate
1170
+ };
1171
+ };
1172
+ const useDoc = () => {
1173
+ const { id, slug, collectionType, origin } = useParams();
1174
+ const [{ query }] = useQueryParams();
1175
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1176
+ if (!collectionType) {
1177
+ throw new Error("Could not find collectionType in url params");
1178
+ }
1179
+ if (!slug) {
1180
+ throw new Error("Could not find model in url params");
1181
+ }
1182
+ const document = useDocument(
1183
+ { documentId: origin || id, model: slug, collectionType, params },
1184
+ {
1185
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1186
+ }
1187
+ );
1188
+ const returnId = origin || id === "create" ? void 0 : id;
1189
+ return {
1190
+ collectionType,
1191
+ model: slug,
1192
+ id: returnId,
1193
+ ...document
1194
+ };
1195
+ };
1196
+ const useContentManagerContext = () => {
1197
+ const {
1198
+ collectionType,
1199
+ model,
1200
+ id,
1201
+ components,
1202
+ isLoading: isLoadingDoc,
1203
+ schema,
1204
+ schemas
1205
+ } = useDoc();
1206
+ const layout = useDocumentLayout(model);
1207
+ const form = useForm("useContentManagerContext", (state) => state);
1208
+ const isSingleType = collectionType === SINGLE_TYPES;
1209
+ const slug = model;
1210
+ const isCreatingEntry = id === "create";
1211
+ useContentTypeSchema();
1212
+ const isLoading = isLoadingDoc || layout.isLoading;
1213
+ const error = layout.error;
1214
+ return {
1215
+ error,
1216
+ isLoading,
1217
+ // Base metadata
1218
+ model,
1219
+ collectionType,
1220
+ id,
1221
+ slug,
1222
+ isCreatingEntry,
1223
+ isSingleType,
1224
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1225
+ // All schema infos
1226
+ components,
1227
+ contentType: schema,
1228
+ contentTypes: schemas,
1229
+ // Form state
1230
+ form,
1231
+ // layout infos
1232
+ layout
1233
+ };
1234
+ };
1235
+ const prefixPluginTranslations = (trad, pluginId) => {
1236
+ return Object.keys(trad).reduce((acc, current) => {
1237
+ acc[`${pluginId}.${current}`] = trad[current];
1238
+ return acc;
1239
+ }, {});
1240
+ };
1241
+ const getTranslation = (id) => `content-manager.${id}`;
1242
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1243
+ id: "notification.error",
1244
+ defaultMessage: "An error occurred, please try again"
1245
+ };
1246
+ const useDocumentActions = () => {
1247
+ const { toggleNotification } = useNotification();
1248
+ const { formatMessage } = useIntl();
1249
+ const { trackUsage } = useTracking();
1250
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1251
+ const navigate = useNavigate();
1252
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
1253
+ const [deleteDocument] = useDeleteDocumentMutation();
1254
+ const _delete = React.useCallback(
1255
+ async ({ collectionType, model, documentId, params }, trackerProperty) => {
1096
1256
  try {
1097
- trackUsage("willUnpublishEntry");
1098
- const res = await unpublishDocument({
1257
+ trackUsage("willDeleteEntry", trackerProperty);
1258
+ const res = await deleteDocument({
1099
1259
  collectionType,
1100
1260
  model,
1101
1261
  documentId,
1102
- params,
1103
- data: {
1104
- discardDraft
1105
- }
1262
+ params
1106
1263
  });
1107
1264
  if ("error" in res) {
1108
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1265
+ toggleNotification({
1266
+ type: "danger",
1267
+ message: formatAPIError(res.error)
1268
+ });
1109
1269
  return { error: res.error };
1110
1270
  }
1111
- trackUsage("didUnpublishEntry");
1112
1271
  toggleNotification({
1113
1272
  type: "success",
1114
1273
  message: formatMessage({
1115
- id: getTranslation("success.record.unpublish"),
1116
- defaultMessage: "Unpublished document"
1274
+ id: getTranslation("success.record.delete"),
1275
+ defaultMessage: "Deleted document"
1117
1276
  })
1118
1277
  });
1278
+ trackUsage("didDeleteEntry", trackerProperty);
1119
1279
  return res.data;
1120
1280
  } catch (err) {
1121
1281
  toggleNotification({
1122
1282
  type: "danger",
1123
1283
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1124
1284
  });
1285
+ trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
1125
1286
  throw err;
1126
1287
  }
1127
1288
  },
1128
- [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1289
+ [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
1129
1290
  );
1130
- const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1131
- const unpublishMany = React.useCallback(
1291
+ const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
1292
+ const deleteMany = React.useCallback(
1132
1293
  async ({ model, documentIds, params }) => {
1133
1294
  try {
1134
- trackUsage("willBulkUnpublishEntries");
1135
- const res = await unpublishManyDocuments({
1295
+ trackUsage("willBulkDeleteEntries");
1296
+ const res = await deleteManyDocuments({
1136
1297
  model,
1137
1298
  documentIds,
1138
1299
  params
1139
1300
  });
1140
1301
  if ("error" in res) {
1141
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1302
+ toggleNotification({
1303
+ type: "danger",
1304
+ message: formatAPIError(res.error)
1305
+ });
1142
1306
  return { error: res.error };
1143
1307
  }
1144
- trackUsage("didBulkUnpublishEntries");
1145
1308
  toggleNotification({
1146
1309
  type: "success",
1147
1310
  title: formatMessage({
1148
- id: getTranslation("success.records.unpublish"),
1149
- defaultMessage: "Successfully unpublished."
1311
+ id: getTranslation("success.records.delete"),
1312
+ defaultMessage: "Successfully deleted."
1150
1313
  }),
1151
1314
  message: ""
1152
1315
  });
1316
+ trackUsage("didBulkDeleteEntries");
1153
1317
  return res.data;
1154
1318
  } catch (err) {
1155
1319
  toggleNotification({
1156
1320
  type: "danger",
1157
1321
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1158
1322
  });
1159
- trackUsage("didNotBulkUnpublishEntries");
1323
+ trackUsage("didNotBulkDeleteEntries");
1160
1324
  throw err;
1161
1325
  }
1162
1326
  },
1163
- [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1327
+ [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1164
1328
  );
1165
- const [createDocument] = useCreateDocumentMutation();
1166
- const create = React.useCallback(
1167
- async ({ model, params }, data, trackerProperty) => {
1329
+ const [discardDocument] = useDiscardDocumentMutation();
1330
+ const discard = React.useCallback(
1331
+ async ({ collectionType, model, documentId, params }) => {
1168
1332
  try {
1169
- const res = await createDocument({
1333
+ const res = await discardDocument({
1334
+ collectionType,
1170
1335
  model,
1171
- data,
1336
+ documentId,
1172
1337
  params
1173
1338
  });
1174
1339
  if ("error" in res) {
1175
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1176
- trackUsage("didNotCreateEntry", { error: res.error, ...trackerProperty });
1340
+ toggleNotification({
1341
+ type: "danger",
1342
+ message: formatAPIError(res.error)
1343
+ });
1177
1344
  return { error: res.error };
1178
1345
  }
1179
- trackUsage("didCreateEntry", trackerProperty);
1180
1346
  toggleNotification({
1181
1347
  type: "success",
1182
1348
  message: formatMessage({
1183
- id: getTranslation("success.record.save"),
1349
+ id: "content-manager.success.record.discard",
1350
+ defaultMessage: "Changes discarded"
1351
+ })
1352
+ });
1353
+ return res.data;
1354
+ } catch (err) {
1355
+ toggleNotification({
1356
+ type: "danger",
1357
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1358
+ });
1359
+ throw err;
1360
+ }
1361
+ },
1362
+ [discardDocument, formatAPIError, formatMessage, toggleNotification]
1363
+ );
1364
+ const [publishDocument] = usePublishDocumentMutation();
1365
+ const publish = React.useCallback(
1366
+ async ({ collectionType, model, documentId, params }, data) => {
1367
+ try {
1368
+ trackUsage("willPublishEntry");
1369
+ const res = await publishDocument({
1370
+ collectionType,
1371
+ model,
1372
+ documentId,
1373
+ data,
1374
+ params
1375
+ });
1376
+ if ("error" in res) {
1377
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1378
+ return { error: res.error };
1379
+ }
1380
+ trackUsage("didPublishEntry");
1381
+ toggleNotification({
1382
+ type: "success",
1383
+ message: formatMessage({
1384
+ id: getTranslation("success.record.publish"),
1385
+ defaultMessage: "Published document"
1386
+ })
1387
+ });
1388
+ return res.data;
1389
+ } catch (err) {
1390
+ toggleNotification({
1391
+ type: "danger",
1392
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1393
+ });
1394
+ throw err;
1395
+ }
1396
+ },
1397
+ [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1398
+ );
1399
+ const [publishManyDocuments] = usePublishManyDocumentsMutation();
1400
+ const publishMany = React.useCallback(
1401
+ async ({ model, documentIds, params }) => {
1402
+ try {
1403
+ const res = await publishManyDocuments({
1404
+ model,
1405
+ documentIds,
1406
+ params
1407
+ });
1408
+ if ("error" in res) {
1409
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1410
+ return { error: res.error };
1411
+ }
1412
+ toggleNotification({
1413
+ type: "success",
1414
+ message: formatMessage({
1415
+ id: getTranslation("success.record.publish"),
1416
+ defaultMessage: "Published document"
1417
+ })
1418
+ });
1419
+ return res.data;
1420
+ } catch (err) {
1421
+ toggleNotification({
1422
+ type: "danger",
1423
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1424
+ });
1425
+ throw err;
1426
+ }
1427
+ },
1428
+ [
1429
+ // trackUsage,
1430
+ publishManyDocuments,
1431
+ toggleNotification,
1432
+ formatMessage,
1433
+ formatAPIError
1434
+ ]
1435
+ );
1436
+ const [updateDocument] = useUpdateDocumentMutation();
1437
+ const update = React.useCallback(
1438
+ async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1439
+ try {
1440
+ trackUsage("willEditEntry", trackerProperty);
1441
+ const res = await updateDocument({
1442
+ collectionType,
1443
+ model,
1444
+ documentId,
1445
+ data,
1446
+ params
1447
+ });
1448
+ if ("error" in res) {
1449
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1450
+ trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1451
+ return { error: res.error };
1452
+ }
1453
+ trackUsage("didEditEntry", trackerProperty);
1454
+ toggleNotification({
1455
+ type: "success",
1456
+ message: formatMessage({
1457
+ id: getTranslation("success.record.save"),
1458
+ defaultMessage: "Saved document"
1459
+ })
1460
+ });
1461
+ return res.data;
1462
+ } catch (err) {
1463
+ trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1464
+ toggleNotification({
1465
+ type: "danger",
1466
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1467
+ });
1468
+ throw err;
1469
+ }
1470
+ },
1471
+ [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1472
+ );
1473
+ const [unpublishDocument] = useUnpublishDocumentMutation();
1474
+ const unpublish = React.useCallback(
1475
+ async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1476
+ try {
1477
+ trackUsage("willUnpublishEntry");
1478
+ const res = await unpublishDocument({
1479
+ collectionType,
1480
+ model,
1481
+ documentId,
1482
+ params,
1483
+ data: {
1484
+ discardDraft
1485
+ }
1486
+ });
1487
+ if ("error" in res) {
1488
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1489
+ return { error: res.error };
1490
+ }
1491
+ trackUsage("didUnpublishEntry");
1492
+ toggleNotification({
1493
+ type: "success",
1494
+ message: formatMessage({
1495
+ id: getTranslation("success.record.unpublish"),
1496
+ defaultMessage: "Unpublished document"
1497
+ })
1498
+ });
1499
+ return res.data;
1500
+ } catch (err) {
1501
+ toggleNotification({
1502
+ type: "danger",
1503
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1504
+ });
1505
+ throw err;
1506
+ }
1507
+ },
1508
+ [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1509
+ );
1510
+ const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1511
+ const unpublishMany = React.useCallback(
1512
+ async ({ model, documentIds, params }) => {
1513
+ try {
1514
+ trackUsage("willBulkUnpublishEntries");
1515
+ const res = await unpublishManyDocuments({
1516
+ model,
1517
+ documentIds,
1518
+ params
1519
+ });
1520
+ if ("error" in res) {
1521
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1522
+ return { error: res.error };
1523
+ }
1524
+ trackUsage("didBulkUnpublishEntries");
1525
+ toggleNotification({
1526
+ type: "success",
1527
+ title: formatMessage({
1528
+ id: getTranslation("success.records.unpublish"),
1529
+ defaultMessage: "Successfully unpublished."
1530
+ }),
1531
+ message: ""
1532
+ });
1533
+ return res.data;
1534
+ } catch (err) {
1535
+ toggleNotification({
1536
+ type: "danger",
1537
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1538
+ });
1539
+ trackUsage("didNotBulkUnpublishEntries");
1540
+ throw err;
1541
+ }
1542
+ },
1543
+ [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1544
+ );
1545
+ const [createDocument] = useCreateDocumentMutation();
1546
+ const create = React.useCallback(
1547
+ async ({ model, params }, data, trackerProperty) => {
1548
+ try {
1549
+ const res = await createDocument({
1550
+ model,
1551
+ data,
1552
+ params
1553
+ });
1554
+ if ("error" in res) {
1555
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1556
+ trackUsage("didNotCreateEntry", { error: res.error, ...trackerProperty });
1557
+ return { error: res.error };
1558
+ }
1559
+ trackUsage("didCreateEntry", trackerProperty);
1560
+ toggleNotification({
1561
+ type: "success",
1562
+ message: formatMessage({
1563
+ id: getTranslation("success.record.save"),
1184
1564
  defaultMessage: "Saved document"
1185
1565
  })
1186
1566
  });
1567
+ setCurrentStep("contentManager.success");
1187
1568
  return res.data;
1188
1569
  } catch (err) {
1189
1570
  toggleNotification({
@@ -1223,7 +1604,7 @@ const useDocumentActions = () => {
1223
1604
  throw err;
1224
1605
  }
1225
1606
  },
1226
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1607
+ [autoCloneDocument, formatMessage, toggleNotification]
1227
1608
  );
1228
1609
  const [cloneDocument] = useCloneDocumentMutation();
1229
1610
  const clone = React.useCallback(
@@ -1249,6 +1630,7 @@ const useDocumentActions = () => {
1249
1630
  defaultMessage: "Cloned document"
1250
1631
  })
1251
1632
  });
1633
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1252
1634
  return res.data;
1253
1635
  } catch (err) {
1254
1636
  toggleNotification({
@@ -1259,7 +1641,7 @@ const useDocumentActions = () => {
1259
1641
  throw err;
1260
1642
  }
1261
1643
  },
1262
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1644
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1263
1645
  );
1264
1646
  const [getDoc] = useLazyGetDocumentQuery();
1265
1647
  const getDocument = React.useCallback(
@@ -1284,10 +1666,10 @@ const useDocumentActions = () => {
1284
1666
  update
1285
1667
  };
1286
1668
  };
1287
- const ProtectedHistoryPage = lazy(
1288
- () => import("./History-Cq_Hrzuu.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1669
+ const ProtectedHistoryPage = React.lazy(
1670
+ () => import("./History-CqNgxkqK.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1289
1671
  );
1290
- const routes$1 = [
1672
+ const routes$2 = [
1291
1673
  {
1292
1674
  path: ":collectionType/:slug/:id/history",
1293
1675
  Component: ProtectedHistoryPage
@@ -1297,32 +1679,45 @@ const routes$1 = [
1297
1679
  Component: ProtectedHistoryPage
1298
1680
  }
1299
1681
  ];
1682
+ const ProtectedPreviewPage = React.lazy(
1683
+ () => import("./Preview-BaYGJ0nb.mjs").then((mod) => ({ default: mod.ProtectedPreviewPage }))
1684
+ );
1685
+ const routes$1 = [
1686
+ {
1687
+ path: ":collectionType/:slug/:id/preview",
1688
+ Component: ProtectedPreviewPage
1689
+ },
1690
+ {
1691
+ path: ":collectionType/:slug/preview",
1692
+ Component: ProtectedPreviewPage
1693
+ }
1694
+ ];
1300
1695
  const ProtectedEditViewPage = lazy(
1301
- () => import("./EditViewPage-C3tIZ8F5.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1696
+ () => import("./EditViewPage-BCjNxNlY.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1302
1697
  );
1303
1698
  const ProtectedListViewPage = lazy(
1304
- () => import("./ListViewPage-HBBnJa8K.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1699
+ () => import("./ListViewPage-D4ofkbjR.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1305
1700
  );
1306
1701
  const ProtectedListConfiguration = lazy(
1307
- () => import("./ListConfigurationPage-W-KQHmBv.mjs").then((mod) => ({
1702
+ () => import("./ListConfigurationPage-BbQjzKkQ.mjs").then((mod) => ({
1308
1703
  default: mod.ProtectedListConfiguration
1309
1704
  }))
1310
1705
  );
1311
1706
  const ProtectedEditConfigurationPage = lazy(
1312
- () => import("./EditConfigurationPage-13b7S5Cq.mjs").then((mod) => ({
1707
+ () => import("./EditConfigurationPage-BhRSnUsL.mjs").then((mod) => ({
1313
1708
  default: mod.ProtectedEditConfigurationPage
1314
1709
  }))
1315
1710
  );
1316
1711
  const ProtectedComponentConfigurationPage = lazy(
1317
- () => import("./ComponentConfigurationPage-D0dyDTwq.mjs").then((mod) => ({
1712
+ () => import("./ComponentConfigurationPage-bLQr82ce.mjs").then((mod) => ({
1318
1713
  default: mod.ProtectedComponentConfigurationPage
1319
1714
  }))
1320
1715
  );
1321
1716
  const NoPermissions = lazy(
1322
- () => import("./NoPermissionsPage-XhOPl8wx.mjs").then((mod) => ({ default: mod.NoPermissions }))
1717
+ () => import("./NoPermissionsPage-DhIiyWkk.mjs").then((mod) => ({ default: mod.NoPermissions }))
1323
1718
  );
1324
1719
  const NoContentType = lazy(
1325
- () => import("./NoContentTypePage-B-gIhHWM.mjs").then((mod) => ({ default: mod.NoContentType }))
1720
+ () => import("./NoContentTypePage-DyUx5mXh.mjs").then((mod) => ({ default: mod.NoContentType }))
1326
1721
  );
1327
1722
  const CollectionTypePages = () => {
1328
1723
  const { collectionType } = useParams();
@@ -1334,7 +1729,7 @@ const CollectionTypePages = () => {
1334
1729
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1335
1730
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1336
1731
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1337
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1732
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1338
1733
  const routes = [
1339
1734
  {
1340
1735
  path: LIST_RELATIVE_PATH,
@@ -1368,6 +1763,7 @@ const routes = [
1368
1763
  path: "no-content-types",
1369
1764
  Component: NoContentType
1370
1765
  },
1766
+ ...routes$2,
1371
1767
  ...routes$1
1372
1768
  ];
1373
1769
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1442,6 +1838,8 @@ const DocumentActionButton = (action) => {
1442
1838
  onClick: handleClick(action),
1443
1839
  justifyContent: "center",
1444
1840
  variant: action.variant || "default",
1841
+ paddingTop: "7px",
1842
+ paddingBottom: "7px",
1445
1843
  children: action.label
1446
1844
  }
1447
1845
  ),
@@ -1464,6 +1862,11 @@ const DocumentActionButton = (action) => {
1464
1862
  ) : null
1465
1863
  ] });
1466
1864
  };
1865
+ const MenuItem = styled(Menu.Item)`
1866
+ &:hover {
1867
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
1868
+ }
1869
+ `;
1467
1870
  const DocumentActionsMenu = ({
1468
1871
  actions: actions2,
1469
1872
  children,
@@ -1519,44 +1922,35 @@ const DocumentActionsMenu = ({
1519
1922
  ]
1520
1923
  }
1521
1924
  ),
1522
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1925
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1523
1926
  actions2.map((action) => {
1524
1927
  return /* @__PURE__ */ jsx(
1525
- Menu.Item,
1928
+ MenuItem,
1526
1929
  {
1527
1930
  disabled: action.disabled,
1528
1931
  onSelect: handleClick(action),
1529
1932
  display: "block",
1530
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1531
- /* @__PURE__ */ jsxs(
1532
- Flex,
1533
- {
1534
- color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1535
- gap: 2,
1536
- tag: "span",
1537
- children: [
1538
- /* @__PURE__ */ jsx("span", { children: action.icon }),
1539
- action.label
1540
- ]
1541
- }
1542
- ),
1543
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1544
- Flex,
1545
- {
1546
- alignItems: "center",
1547
- background: "alternative100",
1548
- borderStyle: "solid",
1549
- borderColor: "alternative200",
1550
- borderWidth: "1px",
1551
- height: 5,
1552
- paddingLeft: 2,
1553
- paddingRight: 2,
1554
- hasRadius: true,
1555
- color: "alternative600",
1556
- children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1557
- }
1558
- )
1559
- ] })
1933
+ isVariantDanger: action.variant === "danger",
1934
+ isDisabled: action.disabled,
1935
+ children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxs(
1936
+ Flex,
1937
+ {
1938
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1939
+ gap: 2,
1940
+ tag: "span",
1941
+ children: [
1942
+ /* @__PURE__ */ jsx(
1943
+ Flex,
1944
+ {
1945
+ tag: "span",
1946
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1947
+ children: action.icon
1948
+ }
1949
+ ),
1950
+ action.label
1951
+ ]
1952
+ }
1953
+ ) })
1560
1954
  },
1561
1955
  action.id
1562
1956
  );
@@ -1598,6 +1992,18 @@ const convertActionVariantToColor = (variant = "secondary") => {
1598
1992
  return "primary600";
1599
1993
  }
1600
1994
  };
1995
+ const convertActionVariantToIconColor = (variant = "secondary") => {
1996
+ switch (variant) {
1997
+ case "danger":
1998
+ return "danger600";
1999
+ case "secondary":
2000
+ return "neutral500";
2001
+ case "success":
2002
+ return "success600";
2003
+ default:
2004
+ return "primary600";
2005
+ }
2006
+ };
1601
2007
  const DocumentActionConfirmDialog = ({
1602
2008
  onClose,
1603
2009
  onCancel,
@@ -1624,11 +2030,11 @@ const DocumentActionConfirmDialog = ({
1624
2030
  /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1625
2031
  /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1626
2032
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1627
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
2033
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1628
2034
  id: "app.components.Button.cancel",
1629
2035
  defaultMessage: "Cancel"
1630
2036
  }) }) }),
1631
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
2037
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1632
2038
  id: "app.components.Button.confirm",
1633
2039
  defaultMessage: "Confirm"
1634
2040
  }) })
@@ -1655,6 +2061,18 @@ const DocumentActionModal = ({
1655
2061
  typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1656
2062
  ] }) });
1657
2063
  };
2064
+ const transformData = (data) => {
2065
+ if (Array.isArray(data)) {
2066
+ return data.map(transformData);
2067
+ }
2068
+ if (typeof data === "object" && data !== null) {
2069
+ if ("apiData" in data) {
2070
+ return data.apiData;
2071
+ }
2072
+ return mapValues(transformData)(data);
2073
+ }
2074
+ return data;
2075
+ };
1658
2076
  const PublishAction$1 = ({
1659
2077
  activeTab,
1660
2078
  documentId,
@@ -1667,12 +2085,11 @@ const PublishAction$1 = ({
1667
2085
  const navigate = useNavigate();
1668
2086
  const { toggleNotification } = useNotification();
1669
2087
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2088
+ const isListView = useMatch(LIST_PATH) !== null;
1670
2089
  const isCloning = useMatch(CLONE_PATH) !== null;
2090
+ const { id } = useParams();
1671
2091
  const { formatMessage } = useIntl();
1672
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1673
- "PublishAction",
1674
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1675
- );
2092
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1676
2093
  const { publish } = useDocumentActions();
1677
2094
  const [
1678
2095
  countDraftRelations,
@@ -1724,32 +2141,35 @@ const PublishAction$1 = ({
1724
2141
  }
1725
2142
  }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1726
2143
  React.useEffect(() => {
1727
- if (documentId) {
1728
- const fetchDraftRelationsCount = async () => {
1729
- const { data, error } = await countDraftRelations({
1730
- collectionType,
1731
- model,
1732
- documentId,
1733
- params
1734
- });
1735
- if (error) {
1736
- throw error;
1737
- }
1738
- if (data) {
1739
- setServerCountOfDraftRelations(data.data);
1740
- }
1741
- };
1742
- fetchDraftRelationsCount();
2144
+ if (!document || !document.documentId || isListView) {
2145
+ return;
1743
2146
  }
1744
- }, [documentId, countDraftRelations, collectionType, model, params]);
1745
- const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
2147
+ const fetchDraftRelationsCount = async () => {
2148
+ const { data, error } = await countDraftRelations({
2149
+ collectionType,
2150
+ model,
2151
+ documentId,
2152
+ params
2153
+ });
2154
+ if (error) {
2155
+ throw error;
2156
+ }
2157
+ if (data) {
2158
+ setServerCountOfDraftRelations(data.data);
2159
+ }
2160
+ };
2161
+ fetchDraftRelationsCount();
2162
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
2163
+ const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1746
2164
  if (!schema?.options?.draftAndPublish) {
1747
2165
  return null;
1748
2166
  }
1749
2167
  const performPublish = async () => {
1750
2168
  setSubmitting(true);
1751
2169
  try {
1752
- const { errors } = await validate();
2170
+ const { errors } = await validate(true, {
2171
+ status: "published"
2172
+ });
1753
2173
  if (errors) {
1754
2174
  toggleNotification({
1755
2175
  type: "danger",
@@ -1767,13 +2187,15 @@ const PublishAction$1 = ({
1767
2187
  documentId,
1768
2188
  params
1769
2189
  },
1770
- formValues
2190
+ transformData(formValues)
1771
2191
  );
1772
2192
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1773
- navigate({
1774
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1775
- search: rawQuery
1776
- });
2193
+ if (id === "create") {
2194
+ navigate({
2195
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2196
+ search: rawQuery
2197
+ });
2198
+ }
1777
2199
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1778
2200
  setErrors(formatValidationErrors(res.error));
1779
2201
  }
@@ -1782,7 +2204,8 @@ const PublishAction$1 = ({
1782
2204
  }
1783
2205
  };
1784
2206
  const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1785
- const hasDraftRelations = totalDraftRelations > 0;
2207
+ const enableDraftRelationsCount = false;
2208
+ const hasDraftRelations = enableDraftRelationsCount;
1786
2209
  return {
1787
2210
  /**
1788
2211
  * Disabled when:
@@ -1799,9 +2222,6 @@ const PublishAction$1 = ({
1799
2222
  defaultMessage: "Publish"
1800
2223
  }),
1801
2224
  onClick: async () => {
1802
- if (hasDraftRelations) {
1803
- return;
1804
- }
1805
2225
  await performPublish();
1806
2226
  },
1807
2227
  dialog: hasDraftRelations ? {
@@ -1828,6 +2248,7 @@ const PublishAction$1 = ({
1828
2248
  };
1829
2249
  };
1830
2250
  PublishAction$1.type = "publish";
2251
+ PublishAction$1.position = "panel";
1831
2252
  const UpdateAction = ({
1832
2253
  activeTab,
1833
2254
  documentId,
@@ -1840,10 +2261,6 @@ const UpdateAction = ({
1840
2261
  const cloneMatch = useMatch(CLONE_PATH);
1841
2262
  const isCloning = cloneMatch !== null;
1842
2263
  const { formatMessage } = useIntl();
1843
- useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1844
- canCreate: canCreate2,
1845
- canUpdate: canUpdate2
1846
- }));
1847
2264
  const { create, update, clone } = useDocumentActions();
1848
2265
  const [{ query, rawQuery }] = useQueryParams();
1849
2266
  const params = React.useMemo(() => buildValidParams(query), [query]);
@@ -1854,6 +2271,117 @@ const UpdateAction = ({
1854
2271
  const validate = useForm("UpdateAction", (state) => state.validate);
1855
2272
  const setErrors = useForm("UpdateAction", (state) => state.setErrors);
1856
2273
  const resetForm = useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
2274
+ const handleUpdate = React.useCallback(async () => {
2275
+ setSubmitting(true);
2276
+ try {
2277
+ if (!modified) {
2278
+ return;
2279
+ }
2280
+ const { errors } = await validate(true, {
2281
+ status: "draft"
2282
+ });
2283
+ if (errors) {
2284
+ toggleNotification({
2285
+ type: "danger",
2286
+ message: formatMessage({
2287
+ id: "content-manager.validation.error",
2288
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2289
+ })
2290
+ });
2291
+ return;
2292
+ }
2293
+ if (isCloning) {
2294
+ const res = await clone(
2295
+ {
2296
+ model,
2297
+ documentId: cloneMatch.params.origin,
2298
+ params
2299
+ },
2300
+ transformData(document)
2301
+ );
2302
+ if ("data" in res) {
2303
+ navigate(
2304
+ {
2305
+ pathname: `../${res.data.documentId}`,
2306
+ search: rawQuery
2307
+ },
2308
+ { relative: "path" }
2309
+ );
2310
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2311
+ setErrors(formatValidationErrors(res.error));
2312
+ }
2313
+ } else if (documentId || collectionType === SINGLE_TYPES) {
2314
+ const res = await update(
2315
+ {
2316
+ collectionType,
2317
+ model,
2318
+ documentId,
2319
+ params
2320
+ },
2321
+ transformData(document)
2322
+ );
2323
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2324
+ setErrors(formatValidationErrors(res.error));
2325
+ } else {
2326
+ resetForm();
2327
+ }
2328
+ } else {
2329
+ const res = await create(
2330
+ {
2331
+ model,
2332
+ params
2333
+ },
2334
+ transformData(document)
2335
+ );
2336
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2337
+ navigate(
2338
+ {
2339
+ pathname: `../${res.data.documentId}`,
2340
+ search: rawQuery
2341
+ },
2342
+ { replace: true, relative: "path" }
2343
+ );
2344
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2345
+ setErrors(formatValidationErrors(res.error));
2346
+ }
2347
+ }
2348
+ } finally {
2349
+ setSubmitting(false);
2350
+ }
2351
+ }, [
2352
+ clone,
2353
+ cloneMatch?.params.origin,
2354
+ collectionType,
2355
+ create,
2356
+ document,
2357
+ documentId,
2358
+ formatMessage,
2359
+ formatValidationErrors,
2360
+ isCloning,
2361
+ model,
2362
+ modified,
2363
+ navigate,
2364
+ params,
2365
+ rawQuery,
2366
+ resetForm,
2367
+ setErrors,
2368
+ setSubmitting,
2369
+ toggleNotification,
2370
+ update,
2371
+ validate
2372
+ ]);
2373
+ React.useEffect(() => {
2374
+ const handleKeyDown = (e) => {
2375
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
2376
+ e.preventDefault();
2377
+ handleUpdate();
2378
+ }
2379
+ };
2380
+ window.addEventListener("keydown", handleKeyDown);
2381
+ return () => {
2382
+ window.removeEventListener("keydown", handleKeyDown);
2383
+ };
2384
+ }, [handleUpdate]);
1857
2385
  return {
1858
2386
  /**
1859
2387
  * Disabled when:
@@ -1863,85 +2391,14 @@ const UpdateAction = ({
1863
2391
  */
1864
2392
  disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1865
2393
  label: formatMessage({
1866
- id: "content-manager.containers.Edit.save",
2394
+ id: "global.save",
1867
2395
  defaultMessage: "Save"
1868
2396
  }),
1869
- onClick: async () => {
1870
- setSubmitting(true);
1871
- try {
1872
- const { errors } = await validate();
1873
- if (errors) {
1874
- toggleNotification({
1875
- type: "danger",
1876
- message: formatMessage({
1877
- id: "content-manager.validation.error",
1878
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1879
- })
1880
- });
1881
- return;
1882
- }
1883
- if (isCloning) {
1884
- const res = await clone(
1885
- {
1886
- model,
1887
- documentId: cloneMatch.params.origin,
1888
- params
1889
- },
1890
- document
1891
- );
1892
- if ("data" in res) {
1893
- navigate(
1894
- {
1895
- pathname: `../${res.data.documentId}`,
1896
- search: rawQuery
1897
- },
1898
- { relative: "path" }
1899
- );
1900
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1901
- setErrors(formatValidationErrors(res.error));
1902
- }
1903
- } else if (documentId || collectionType === SINGLE_TYPES) {
1904
- const res = await update(
1905
- {
1906
- collectionType,
1907
- model,
1908
- documentId,
1909
- params
1910
- },
1911
- document
1912
- );
1913
- if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1914
- setErrors(formatValidationErrors(res.error));
1915
- } else {
1916
- resetForm();
1917
- }
1918
- } else {
1919
- const res = await create(
1920
- {
1921
- model,
1922
- params
1923
- },
1924
- document
1925
- );
1926
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1927
- navigate(
1928
- {
1929
- pathname: `../${res.data.documentId}`,
1930
- search: rawQuery
1931
- },
1932
- { replace: true, relative: "path" }
1933
- );
1934
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1935
- setErrors(formatValidationErrors(res.error));
1936
- }
1937
- }
1938
- } finally {
1939
- setSubmitting(false);
1940
- }
1941
- }
2397
+ onClick: handleUpdate
1942
2398
  };
1943
2399
  };
1944
2400
  UpdateAction.type = "update";
2401
+ UpdateAction.position = "panel";
1945
2402
  const UNPUBLISH_DRAFT_OPTIONS = {
1946
2403
  KEEP: "keep",
1947
2404
  DISCARD: "discard"
@@ -1974,7 +2431,7 @@ const UnpublishAction$1 = ({
1974
2431
  id: "app.utils.unpublish",
1975
2432
  defaultMessage: "Unpublish"
1976
2433
  }),
1977
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2434
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1978
2435
  onClick: async () => {
1979
2436
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1980
2437
  if (!documentId) {
@@ -2064,6 +2521,7 @@ const UnpublishAction$1 = ({
2064
2521
  };
2065
2522
  };
2066
2523
  UnpublishAction$1.type = "unpublish";
2524
+ UnpublishAction$1.position = "panel";
2067
2525
  const DiscardAction = ({
2068
2526
  activeTab,
2069
2527
  documentId,
@@ -2086,7 +2544,7 @@ const DiscardAction = ({
2086
2544
  id: "content-manager.actions.discard.label",
2087
2545
  defaultMessage: "Discard changes"
2088
2546
  }),
2089
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2547
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2090
2548
  position: ["panel", "table-row"],
2091
2549
  variant: "danger",
2092
2550
  dialog: {
@@ -2114,11 +2572,7 @@ const DiscardAction = ({
2114
2572
  };
2115
2573
  };
2116
2574
  DiscardAction.type = "discard";
2117
- const StyledCrossCircle = styled(CrossCircle)`
2118
- path {
2119
- fill: currentColor;
2120
- }
2121
- `;
2575
+ DiscardAction.position = "panel";
2122
2576
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2123
2577
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2124
2578
  const RelativeTime = React.forwardRef(
@@ -2131,7 +2585,7 @@ const RelativeTime = React.forwardRef(
2131
2585
  });
2132
2586
  const unit = intervals.find((intervalUnit) => {
2133
2587
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2134
- });
2588
+ }) ?? "seconds";
2135
2589
  const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2136
2590
  const customInterval = customIntervals.find(
2137
2591
  (custom) => interval[custom.unit] < custom.threshold
@@ -2165,19 +2619,29 @@ const getDisplayName = ({
2165
2619
  return email ?? "";
2166
2620
  };
2167
2621
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2168
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2169
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2170
- return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2622
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2623
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2624
+ const { formatMessage } = useIntl();
2625
+ return /* @__PURE__ */ jsx(Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2626
+ id: `content-manager.containers.List.${status}`,
2627
+ defaultMessage: capitalise(status)
2628
+ }) }) });
2171
2629
  };
2172
2630
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2173
2631
  const { formatMessage } = useIntl();
2174
2632
  const isCloning = useMatch(CLONE_PATH) !== null;
2633
+ const params = useParams();
2175
2634
  const title = isCreating ? formatMessage({
2176
2635
  id: "content-manager.containers.edit.title.new",
2177
2636
  defaultMessage: "Create an entry"
2178
2637
  }) : documentTitle;
2179
2638
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2180
- /* @__PURE__ */ jsx(BackButton, {}),
2639
+ /* @__PURE__ */ jsx(
2640
+ BackButton,
2641
+ {
2642
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2643
+ }
2644
+ ),
2181
2645
  /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2182
2646
  /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2183
2647
  /* @__PURE__ */ jsx(HeaderToolbar, {})
@@ -2228,7 +2692,7 @@ const HeaderToolbar = () => {
2228
2692
  meta: isCloning ? void 0 : meta,
2229
2693
  collectionType
2230
2694
  },
2231
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2695
+ descriptions: plugins["content-manager"].apis.getDocumentActions("header"),
2232
2696
  children: (actions2) => {
2233
2697
  const headerActions = actions2.filter((action) => {
2234
2698
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2265,12 +2729,12 @@ const Information = ({ activeTab }) => {
2265
2729
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2266
2730
  label: formatMessage({
2267
2731
  id: "content-manager.containers.edit.information.last-published.label",
2268
- defaultMessage: "Last published"
2732
+ defaultMessage: "Published"
2269
2733
  }),
2270
2734
  value: formatMessage(
2271
2735
  {
2272
2736
  id: "content-manager.containers.edit.information.last-published.value",
2273
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2737
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2274
2738
  },
2275
2739
  {
2276
2740
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2283,12 +2747,12 @@ const Information = ({ activeTab }) => {
2283
2747
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2284
2748
  label: formatMessage({
2285
2749
  id: "content-manager.containers.edit.information.last-draft.label",
2286
- defaultMessage: "Last draft"
2750
+ defaultMessage: "Updated"
2287
2751
  }),
2288
2752
  value: formatMessage(
2289
2753
  {
2290
2754
  id: "content-manager.containers.edit.information.last-draft.value",
2291
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2755
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2292
2756
  },
2293
2757
  {
2294
2758
  time: /* @__PURE__ */ jsx(
@@ -2306,12 +2770,12 @@ const Information = ({ activeTab }) => {
2306
2770
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2307
2771
  label: formatMessage({
2308
2772
  id: "content-manager.containers.edit.information.document.label",
2309
- defaultMessage: "Document"
2773
+ defaultMessage: "Created"
2310
2774
  }),
2311
2775
  value: formatMessage(
2312
2776
  {
2313
2777
  id: "content-manager.containers.edit.information.document.value",
2314
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2778
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2315
2779
  },
2316
2780
  {
2317
2781
  time: /* @__PURE__ */ jsx(
@@ -2349,25 +2813,77 @@ const Information = ({ activeTab }) => {
2349
2813
  );
2350
2814
  };
2351
2815
  const HeaderActions = ({ actions: actions2 }) => {
2352
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2353
- if ("options" in action) {
2816
+ const [dialogId, setDialogId] = React.useState(null);
2817
+ const handleClick = (action) => async (e) => {
2818
+ if (!("options" in action)) {
2819
+ const { onClick = () => false, dialog, id } = action;
2820
+ const muteDialog = await onClick(e);
2821
+ if (dialog && !muteDialog) {
2822
+ e.preventDefault();
2823
+ setDialogId(id);
2824
+ }
2825
+ }
2826
+ };
2827
+ const handleClose = () => {
2828
+ setDialogId(null);
2829
+ };
2830
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2831
+ if (action.options) {
2354
2832
  return /* @__PURE__ */ jsx(
2355
2833
  SingleSelect,
2356
2834
  {
2357
2835
  size: "S",
2358
- disabled: action.disabled,
2359
- "aria-label": action.label,
2360
2836
  onChange: action.onSelect,
2361
- value: action.value,
2837
+ "aria-label": action.label,
2838
+ ...action,
2362
2839
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2363
2840
  },
2364
2841
  action.id
2365
2842
  );
2366
2843
  } else {
2367
- return null;
2844
+ if (action.type === "icon") {
2845
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
2846
+ /* @__PURE__ */ jsx(
2847
+ IconButton,
2848
+ {
2849
+ disabled: action.disabled,
2850
+ label: action.label,
2851
+ size: "S",
2852
+ onClick: handleClick(action),
2853
+ children: action.icon
2854
+ }
2855
+ ),
2856
+ action.dialog ? /* @__PURE__ */ jsx(
2857
+ HeaderActionDialog,
2858
+ {
2859
+ ...action.dialog,
2860
+ isOpen: dialogId === action.id,
2861
+ onClose: handleClose
2862
+ }
2863
+ ) : null
2864
+ ] }, action.id);
2865
+ }
2368
2866
  }
2369
2867
  }) });
2370
2868
  };
2869
+ const HeaderActionDialog = ({
2870
+ onClose,
2871
+ onCancel,
2872
+ title,
2873
+ content: Content,
2874
+ isOpen
2875
+ }) => {
2876
+ const handleClose = async () => {
2877
+ if (onCancel) {
2878
+ await onCancel();
2879
+ }
2880
+ onClose();
2881
+ };
2882
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2883
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2884
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
2885
+ ] }) });
2886
+ };
2371
2887
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2372
2888
  const navigate = useNavigate();
2373
2889
  const { formatMessage } = useIntl();
@@ -2384,6 +2900,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2384
2900
  };
2385
2901
  };
2386
2902
  ConfigureTheViewAction.type = "configure-the-view";
2903
+ ConfigureTheViewAction.position = "header";
2387
2904
  const EditTheModelAction = ({ model }) => {
2388
2905
  const navigate = useNavigate();
2389
2906
  const { formatMessage } = useIntl();
@@ -2400,6 +2917,7 @@ const EditTheModelAction = ({ model }) => {
2400
2917
  };
2401
2918
  };
2402
2919
  EditTheModelAction.type = "edit-the-model";
2920
+ EditTheModelAction.position = "header";
2403
2921
  const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2404
2922
  const navigate = useNavigate();
2405
2923
  const { formatMessage } = useIntl();
@@ -2408,12 +2926,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2408
2926
  const { delete: deleteAction } = useDocumentActions();
2409
2927
  const { toggleNotification } = useNotification();
2410
2928
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2929
+ const isLocalized = document?.locale != null;
2411
2930
  return {
2412
2931
  disabled: !canDelete || !document,
2413
- label: formatMessage({
2414
- id: "content-manager.actions.delete.label",
2415
- defaultMessage: "Delete document"
2416
- }),
2932
+ label: formatMessage(
2933
+ {
2934
+ id: "content-manager.actions.delete.label",
2935
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2936
+ },
2937
+ { isLocalized }
2938
+ ),
2417
2939
  icon: /* @__PURE__ */ jsx(Trash, {}),
2418
2940
  dialog: {
2419
2941
  type: "dialog",
@@ -2455,417 +2977,116 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2455
2977
  }
2456
2978
  });
2457
2979
  if (!("error" in res)) {
2458
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2459
- }
2460
- } finally {
2461
- if (!listViewPathMatch) {
2462
- setSubmitting(false);
2463
- }
2464
- }
2465
- }
2466
- },
2467
- variant: "danger",
2468
- position: ["header", "table-row"]
2469
- };
2470
- };
2471
- DeleteAction$1.type = "delete";
2472
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2473
- const Panels = () => {
2474
- const isCloning = useMatch(CLONE_PATH) !== null;
2475
- const [
2476
- {
2477
- query: { status }
2478
- }
2479
- ] = useQueryParams({
2480
- status: "draft"
2481
- });
2482
- const { model, id, document, meta, collectionType } = useDoc();
2483
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2484
- const props = {
2485
- activeTab: status,
2486
- model,
2487
- documentId: id,
2488
- document: isCloning ? void 0 : document,
2489
- meta: isCloning ? void 0 : meta,
2490
- collectionType
2491
- };
2492
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2493
- DescriptionComponentRenderer,
2494
- {
2495
- props,
2496
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2497
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2498
- }
2499
- ) });
2500
- };
2501
- const ActionsPanel = () => {
2502
- const { formatMessage } = useIntl();
2503
- return {
2504
- title: formatMessage({
2505
- id: "content-manager.containers.edit.panels.default.title",
2506
- defaultMessage: "Document"
2507
- }),
2508
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2509
- };
2510
- };
2511
- ActionsPanel.type = "actions";
2512
- const ActionsPanelContent = () => {
2513
- const isCloning = useMatch(CLONE_PATH) !== null;
2514
- const [
2515
- {
2516
- query: { status = "draft" }
2517
- }
2518
- ] = useQueryParams();
2519
- const { model, id, document, meta, collectionType } = useDoc();
2520
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2521
- const props = {
2522
- activeTab: status,
2523
- model,
2524
- documentId: id,
2525
- document: isCloning ? void 0 : document,
2526
- meta: isCloning ? void 0 : meta,
2527
- collectionType
2528
- };
2529
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2530
- /* @__PURE__ */ jsx(
2531
- DescriptionComponentRenderer,
2532
- {
2533
- props,
2534
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2535
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2536
- }
2537
- ),
2538
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2539
- ] });
2540
- };
2541
- const Panel = React.forwardRef(({ children, title }, ref) => {
2542
- return /* @__PURE__ */ jsxs(
2543
- Flex,
2544
- {
2545
- ref,
2546
- tag: "aside",
2547
- "aria-labelledby": "additional-information",
2548
- background: "neutral0",
2549
- borderColor: "neutral150",
2550
- hasRadius: true,
2551
- paddingBottom: 4,
2552
- paddingLeft: 4,
2553
- paddingRight: 4,
2554
- paddingTop: 4,
2555
- shadow: "tableShadow",
2556
- gap: 3,
2557
- direction: "column",
2558
- justifyContent: "stretch",
2559
- alignItems: "flex-start",
2560
- children: [
2561
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2562
- children
2563
- ]
2564
- }
2565
- );
2566
- });
2567
- const HOOKS = {
2568
- /**
2569
- * Hook that allows to mutate the displayed headers of the list view table
2570
- * @constant
2571
- * @type {string}
2572
- */
2573
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2574
- /**
2575
- * Hook that allows to mutate the CM's collection types links pre-set filters
2576
- * @constant
2577
- * @type {string}
2578
- */
2579
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2580
- /**
2581
- * Hook that allows to mutate the CM's edit view layout
2582
- * @constant
2583
- * @type {string}
2584
- */
2585
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2586
- /**
2587
- * Hook that allows to mutate the CM's single types links pre-set filters
2588
- * @constant
2589
- * @type {string}
2590
- */
2591
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2592
- };
2593
- const contentTypesApi = contentManagerApi.injectEndpoints({
2594
- endpoints: (builder) => ({
2595
- getContentTypeConfiguration: builder.query({
2596
- query: (uid) => ({
2597
- url: `/content-manager/content-types/${uid}/configuration`,
2598
- method: "GET"
2599
- }),
2600
- transformResponse: (response) => response.data,
2601
- providesTags: (_result, _error, uid) => [
2602
- { type: "ContentTypesConfiguration", id: uid },
2603
- { type: "ContentTypeSettings", id: "LIST" }
2604
- ]
2605
- }),
2606
- getAllContentTypeSettings: builder.query({
2607
- query: () => "/content-manager/content-types-settings",
2608
- transformResponse: (response) => response.data,
2609
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2610
- }),
2611
- updateContentTypeConfiguration: builder.mutation({
2612
- query: ({ uid, ...body }) => ({
2613
- url: `/content-manager/content-types/${uid}/configuration`,
2614
- method: "PUT",
2615
- data: body
2616
- }),
2617
- transformResponse: (response) => response.data,
2618
- invalidatesTags: (_result, _error, { uid }) => [
2619
- { type: "ContentTypesConfiguration", id: uid },
2620
- { type: "ContentTypeSettings", id: "LIST" },
2621
- // Is this necessary?
2622
- { type: "InitialData" }
2623
- ]
2624
- })
2625
- })
2626
- });
2627
- const {
2628
- useGetContentTypeConfigurationQuery,
2629
- useGetAllContentTypeSettingsQuery,
2630
- useUpdateContentTypeConfigurationMutation
2631
- } = contentTypesApi;
2632
- const checkIfAttributeIsDisplayable = (attribute) => {
2633
- const { type } = attribute;
2634
- if (type === "relation") {
2635
- return !attribute.relation.toLowerCase().includes("morph");
2636
- }
2637
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2638
- };
2639
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2640
- if (!mainFieldName) {
2641
- return void 0;
2642
- }
2643
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2644
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2645
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2646
- );
2647
- return {
2648
- name: mainFieldName,
2649
- type: mainFieldType ?? "string"
2650
- };
2651
- };
2652
- const DEFAULT_SETTINGS = {
2653
- bulkable: false,
2654
- filterable: false,
2655
- searchable: false,
2656
- pagination: false,
2657
- defaultSortBy: "",
2658
- defaultSortOrder: "asc",
2659
- mainField: "id",
2660
- pageSize: 10
2661
- };
2662
- const useDocumentLayout = (model) => {
2663
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2664
- const [{ query }] = useQueryParams();
2665
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2666
- const { toggleNotification } = useNotification();
2667
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2668
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2669
- const {
2670
- data,
2671
- isLoading: isLoadingConfigs,
2672
- error,
2673
- isFetching: isFetchingConfigs
2674
- } = useGetContentTypeConfigurationQuery(model);
2675
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2676
- React.useEffect(() => {
2677
- if (error) {
2678
- toggleNotification({
2679
- type: "danger",
2680
- message: formatAPIError(error)
2681
- });
2682
- }
2683
- }, [error, formatAPIError, toggleNotification]);
2684
- const editLayout = React.useMemo(
2685
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2686
- layout: [],
2687
- components: {},
2688
- metadatas: {},
2689
- options: {},
2690
- settings: DEFAULT_SETTINGS
2691
- },
2692
- [data, isLoading, schemas, schema, components]
2693
- );
2694
- const listLayout = React.useMemo(() => {
2695
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2696
- layout: [],
2697
- metadatas: {},
2698
- options: {},
2699
- settings: DEFAULT_SETTINGS
2700
- };
2701
- }, [data, isLoading, schemas, schema, components]);
2702
- const { layout: edit } = React.useMemo(
2703
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2704
- layout: editLayout,
2705
- query
2706
- }),
2707
- [editLayout, query, runHookWaterfall]
2708
- );
2709
- return {
2710
- error,
2711
- isLoading,
2712
- edit,
2713
- list: listLayout
2714
- };
2715
- };
2716
- const useDocLayout = () => {
2717
- const { model } = useDoc();
2718
- return useDocumentLayout(model);
2719
- };
2720
- const formatEditLayout = (data, {
2721
- schemas,
2722
- schema,
2723
- components
2724
- }) => {
2725
- let currentPanelIndex = 0;
2726
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2727
- data.contentType.layouts.edit,
2728
- schema?.attributes,
2729
- data.contentType.metadatas,
2730
- { configurations: data.components, schemas: components },
2731
- schemas
2732
- ).reduce((panels, row) => {
2733
- if (row.some((field) => field.type === "dynamiczone")) {
2734
- panels.push([row]);
2735
- currentPanelIndex += 2;
2736
- } else {
2737
- if (!panels[currentPanelIndex]) {
2738
- panels.push([]);
2739
- }
2740
- panels[currentPanelIndex].push(row);
2741
- }
2742
- return panels;
2743
- }, []);
2744
- const componentEditAttributes = Object.entries(data.components).reduce(
2745
- (acc, [uid, configuration]) => {
2746
- acc[uid] = {
2747
- layout: convertEditLayoutToFieldLayouts(
2748
- configuration.layouts.edit,
2749
- components[uid].attributes,
2750
- configuration.metadatas
2751
- ),
2752
- settings: {
2753
- ...configuration.settings,
2754
- icon: components[uid].info.icon,
2755
- displayName: components[uid].info.displayName
2980
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2981
+ }
2982
+ } finally {
2983
+ if (!listViewPathMatch) {
2984
+ setSubmitting(false);
2985
+ }
2756
2986
  }
2757
- };
2758
- return acc;
2759
- },
2760
- {}
2761
- );
2762
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2763
- (acc, [attribute, metadata]) => {
2764
- return {
2765
- ...acc,
2766
- [attribute]: metadata.edit
2767
- };
2768
- },
2769
- {}
2770
- );
2771
- return {
2772
- layout: panelledEditAttributes,
2773
- components: componentEditAttributes,
2774
- metadatas: editMetadatas,
2775
- settings: {
2776
- ...data.contentType.settings,
2777
- displayName: schema?.info.displayName
2987
+ }
2778
2988
  },
2779
- options: {
2780
- ...schema?.options,
2781
- ...schema?.pluginOptions,
2782
- ...data.contentType.options
2783
- }
2989
+ variant: "danger",
2990
+ position: ["header", "table-row"]
2784
2991
  };
2785
2992
  };
2786
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2787
- return rows.map(
2788
- (row) => row.map((field) => {
2789
- const attribute = attributes[field.name];
2790
- if (!attribute) {
2791
- return null;
2792
- }
2793
- const { edit: metadata } = metadatas[field.name];
2794
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2795
- return {
2796
- attribute,
2797
- disabled: !metadata.editable,
2798
- hint: metadata.description,
2799
- label: metadata.label ?? "",
2800
- name: field.name,
2801
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2802
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2803
- schemas,
2804
- components: components?.schemas ?? {}
2805
- }),
2806
- placeholder: metadata.placeholder ?? "",
2807
- required: attribute.required ?? false,
2808
- size: field.size,
2809
- unique: "unique" in attribute ? attribute.unique : false,
2810
- visible: metadata.visible ?? true,
2811
- type: attribute.type
2812
- };
2813
- }).filter((field) => field !== null)
2814
- );
2993
+ DeleteAction$1.type = "delete";
2994
+ DeleteAction$1.position = ["header", "table-row"];
2995
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2996
+ const Panels = () => {
2997
+ const isCloning = useMatch(CLONE_PATH) !== null;
2998
+ const [
2999
+ {
3000
+ query: { status }
3001
+ }
3002
+ ] = useQueryParams({
3003
+ status: "draft"
3004
+ });
3005
+ const { model, id, document, meta, collectionType } = useDoc();
3006
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
3007
+ const props = {
3008
+ activeTab: status,
3009
+ model,
3010
+ documentId: id,
3011
+ document: isCloning ? void 0 : document,
3012
+ meta: isCloning ? void 0 : meta,
3013
+ collectionType
3014
+ };
3015
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
3016
+ DescriptionComponentRenderer,
3017
+ {
3018
+ props,
3019
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
3020
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
3021
+ }
3022
+ ) });
2815
3023
  };
2816
- const formatListLayout = (data, {
2817
- schemas,
2818
- schema,
2819
- components
2820
- }) => {
2821
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2822
- (acc, [attribute, metadata]) => {
2823
- return {
2824
- ...acc,
2825
- [attribute]: metadata.list
2826
- };
2827
- },
2828
- {}
2829
- );
2830
- const listAttributes = convertListLayoutToFieldLayouts(
2831
- data.contentType.layouts.list,
2832
- schema?.attributes,
2833
- listMetadatas,
2834
- { configurations: data.components, schemas: components },
2835
- schemas
2836
- );
3024
+ const ActionsPanel = () => {
3025
+ const { formatMessage } = useIntl();
2837
3026
  return {
2838
- layout: listAttributes,
2839
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2840
- metadatas: listMetadatas,
2841
- options: {
2842
- ...schema?.options,
2843
- ...schema?.pluginOptions,
2844
- ...data.contentType.options
2845
- }
3027
+ title: formatMessage({
3028
+ id: "content-manager.containers.edit.panels.default.title",
3029
+ defaultMessage: "Entry"
3030
+ }),
3031
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2846
3032
  };
2847
3033
  };
2848
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2849
- return columns.map((name) => {
2850
- const attribute = attributes[name];
2851
- if (!attribute) {
2852
- return null;
3034
+ ActionsPanel.type = "actions";
3035
+ const ActionsPanelContent = () => {
3036
+ const isCloning = useMatch(CLONE_PATH) !== null;
3037
+ const [
3038
+ {
3039
+ query: { status = "draft" }
2853
3040
  }
2854
- const metadata = metadatas[name];
2855
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2856
- return {
2857
- attribute,
2858
- label: metadata.label ?? "",
2859
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2860
- schemas,
2861
- components: components?.schemas ?? {}
2862
- }),
2863
- name,
2864
- searchable: metadata.searchable ?? true,
2865
- sortable: metadata.sortable ?? true
2866
- };
2867
- }).filter((field) => field !== null);
3041
+ ] = useQueryParams();
3042
+ const { model, id, document, meta, collectionType } = useDoc();
3043
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
3044
+ const props = {
3045
+ activeTab: status,
3046
+ model,
3047
+ documentId: id,
3048
+ document: isCloning ? void 0 : document,
3049
+ meta: isCloning ? void 0 : meta,
3050
+ collectionType
3051
+ };
3052
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
3053
+ /* @__PURE__ */ jsx(
3054
+ DescriptionComponentRenderer,
3055
+ {
3056
+ props,
3057
+ descriptions: plugins["content-manager"].apis.getDocumentActions("panel"),
3058
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
3059
+ }
3060
+ ),
3061
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
3062
+ ] });
2868
3063
  };
3064
+ const Panel = React.forwardRef(({ children, title }, ref) => {
3065
+ return /* @__PURE__ */ jsxs(
3066
+ Flex,
3067
+ {
3068
+ ref,
3069
+ tag: "aside",
3070
+ "aria-labelledby": "additional-information",
3071
+ background: "neutral0",
3072
+ borderColor: "neutral150",
3073
+ hasRadius: true,
3074
+ paddingBottom: 4,
3075
+ paddingLeft: 4,
3076
+ paddingRight: 4,
3077
+ paddingTop: 4,
3078
+ shadow: "tableShadow",
3079
+ gap: 3,
3080
+ direction: "column",
3081
+ justifyContent: "stretch",
3082
+ alignItems: "flex-start",
3083
+ children: [
3084
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3085
+ children
3086
+ ]
3087
+ }
3088
+ );
3089
+ });
2869
3090
  const ConfirmBulkActionDialog = ({
2870
3091
  onToggleDialog,
2871
3092
  isOpen = false,
@@ -2904,6 +3125,7 @@ const ConfirmDialogPublishAll = ({
2904
3125
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2905
3126
  const { model, schema } = useDoc();
2906
3127
  const [{ query }] = useQueryParams();
3128
+ const enableDraftRelationsCount = false;
2907
3129
  const {
2908
3130
  data: countDraftRelations = 0,
2909
3131
  isLoading,
@@ -2915,7 +3137,7 @@ const ConfirmDialogPublishAll = ({
2915
3137
  locale: query?.plugins?.i18n?.locale
2916
3138
  },
2917
3139
  {
2918
- skip: selectedEntries.length === 0
3140
+ skip: !enableDraftRelationsCount
2919
3141
  }
2920
3142
  );
2921
3143
  React.useEffect(() => {
@@ -3100,7 +3322,7 @@ const SelectedEntriesTableContent = ({
3100
3322
  status: row.status
3101
3323
  }
3102
3324
  ) }),
3103
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3325
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3104
3326
  IconButton,
3105
3327
  {
3106
3328
  tag: Link,
@@ -3109,23 +3331,16 @@ const SelectedEntriesTableContent = ({
3109
3331
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3110
3332
  },
3111
3333
  state: { from: pathname },
3112
- label: formatMessage(
3113
- { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3114
- {
3115
- target: formatMessage(
3116
- {
3117
- id: "content-manager.components.ListViewHelperPluginTable.row-line",
3118
- defaultMessage: "item line {number}"
3119
- },
3120
- { number: index2 + 1 }
3121
- )
3122
- }
3123
- ),
3334
+ label: formatMessage({
3335
+ id: "content-manager.bulk-publish.edit",
3336
+ defaultMessage: "Edit"
3337
+ }),
3124
3338
  target: "_blank",
3125
3339
  marginLeft: "auto",
3126
- children: /* @__PURE__ */ jsx(Pencil, {})
3340
+ variant: "ghost",
3341
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3127
3342
  }
3128
- ) })
3343
+ ) }) })
3129
3344
  ] }, row.id)) })
3130
3345
  ] });
3131
3346
  };
@@ -3162,7 +3377,13 @@ const SelectedEntriesModalContent = ({
3162
3377
  );
3163
3378
  const { rows, validationErrors } = React.useMemo(() => {
3164
3379
  if (data.length > 0 && schema) {
3165
- const validate = createYupSchema(schema.attributes, components);
3380
+ const validate = createYupSchema(
3381
+ schema.attributes,
3382
+ components,
3383
+ // Since this is the "Publish" action, the validation
3384
+ // schema must enforce the rules for published entities
3385
+ { status: "published" }
3386
+ );
3166
3387
  const validationErrors2 = {};
3167
3388
  const rows2 = data.map((entry) => {
3168
3389
  try {
@@ -3287,8 +3508,7 @@ const PublishAction = ({ documents, model }) => {
3287
3508
  const refetchList = () => {
3288
3509
  contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3289
3510
  };
3290
- if (!showPublishButton)
3291
- return null;
3511
+ if (!showPublishButton) return null;
3292
3512
  return {
3293
3513
  actionType: "publish",
3294
3514
  variant: "tertiary",
@@ -3356,8 +3576,7 @@ const DeleteAction = ({ documents, model }) => {
3356
3576
  selectRow([]);
3357
3577
  }
3358
3578
  };
3359
- if (!hasDeletePermission)
3360
- return null;
3579
+ if (!hasDeletePermission) return null;
3361
3580
  return {
3362
3581
  variant: "danger-light",
3363
3582
  label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
@@ -3406,8 +3625,7 @@ const UnpublishAction = ({ documents, model }) => {
3406
3625
  }
3407
3626
  };
3408
3627
  const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3409
- if (!showUnpublishButton)
3410
- return null;
3628
+ if (!showUnpublishButton) return null;
3411
3629
  return {
3412
3630
  variant: "tertiary",
3413
3631
  label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
@@ -3512,7 +3730,7 @@ const TableActions = ({ document }) => {
3512
3730
  DescriptionComponentRenderer,
3513
3731
  {
3514
3732
  props,
3515
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3733
+ descriptions: plugins["content-manager"].apis.getDocumentActions("table-row").filter((action) => action.name !== "PublishAction"),
3516
3734
  children: (actions2) => {
3517
3735
  const tableRowActions = actions2.filter((action) => {
3518
3736
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3571,6 +3789,7 @@ const EditAction = ({ documentId }) => {
3571
3789
  };
3572
3790
  };
3573
3791
  EditAction.type = "edit";
3792
+ EditAction.position = "table-row";
3574
3793
  const StyledPencil = styled(Pencil)`
3575
3794
  path {
3576
3795
  fill: currentColor;
@@ -3623,7 +3842,7 @@ const CloneAction = ({ model, documentId }) => {
3623
3842
  }),
3624
3843
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3625
3844
  footer: ({ onClose }) => {
3626
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3845
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3627
3846
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3628
3847
  id: "cancel",
3629
3848
  defaultMessage: "Cancel"
@@ -3647,6 +3866,7 @@ const CloneAction = ({ model, documentId }) => {
3647
3866
  };
3648
3867
  };
3649
3868
  CloneAction.type = "clone";
3869
+ CloneAction.position = "table-row";
3650
3870
  const StyledDuplicate = styled(Duplicate)`
3651
3871
  path {
3652
3872
  fill: currentColor;
@@ -3733,7 +3953,14 @@ class ContentManagerPlugin {
3733
3953
  addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3734
3954
  addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3735
3955
  getBulkActions: () => this.bulkActions,
3736
- getDocumentActions: () => this.documentActions,
3956
+ getDocumentActions: (position) => {
3957
+ if (position) {
3958
+ return this.documentActions.filter(
3959
+ (action) => action.position == void 0 || [action.position].flat().includes(position)
3960
+ );
3961
+ }
3962
+ return this.documentActions;
3963
+ },
3737
3964
  getEditViewSidePanels: () => this.editViewSidePanels,
3738
3965
  getHeaderActions: () => this.headerActions
3739
3966
  }
@@ -3743,10 +3970,8 @@ class ContentManagerPlugin {
3743
3970
  const getPrintableType = (value) => {
3744
3971
  const nativeType = typeof value;
3745
3972
  if (nativeType === "object") {
3746
- if (value === null)
3747
- return "null";
3748
- if (Array.isArray(value))
3749
- return "array";
3973
+ if (value === null) return "null";
3974
+ if (Array.isArray(value)) return "array";
3750
3975
  if (value instanceof Object && value.constructor.name !== "Object") {
3751
3976
  return value.constructor.name;
3752
3977
  }
@@ -3757,17 +3982,27 @@ const HistoryAction = ({ model, document }) => {
3757
3982
  const { formatMessage } = useIntl();
3758
3983
  const [{ query }] = useQueryParams();
3759
3984
  const navigate = useNavigate();
3985
+ const { trackUsage } = useTracking();
3986
+ const { pathname } = useLocation();
3760
3987
  const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
3761
3988
  if (!window.strapi.features.isEnabled("cms-content-history")) {
3762
3989
  return null;
3763
3990
  }
3991
+ const handleOnClick = () => {
3992
+ const destination = { pathname: "history", search: pluginsQueryParams };
3993
+ trackUsage("willNavigate", {
3994
+ from: pathname,
3995
+ to: `${pathname}/${destination.pathname}`
3996
+ });
3997
+ navigate(destination);
3998
+ };
3764
3999
  return {
3765
4000
  icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
3766
4001
  label: formatMessage({
3767
4002
  id: "content-manager.history.document-action",
3768
4003
  defaultMessage: "Content History"
3769
4004
  }),
3770
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
4005
+ onClick: handleOnClick,
3771
4006
  disabled: (
3772
4007
  /**
3773
4008
  * The user is creating a new document.
@@ -3789,6 +4024,7 @@ const HistoryAction = ({ model, document }) => {
3789
4024
  };
3790
4025
  };
3791
4026
  HistoryAction.type = "history";
4027
+ HistoryAction.position = "header";
3792
4028
  const historyAdmin = {
3793
4029
  bootstrap(app) {
3794
4030
  const { addDocumentAction } = app.getPlugin("content-manager").apis;
@@ -3835,6 +4071,88 @@ const { setInitialData } = actions;
3835
4071
  const reducer = combineReducers({
3836
4072
  app: reducer$1
3837
4073
  });
4074
+ const previewApi = contentManagerApi.injectEndpoints({
4075
+ endpoints: (builder) => ({
4076
+ getPreviewUrl: builder.query({
4077
+ query({ query, params }) {
4078
+ return {
4079
+ url: `/content-manager/preview/url/${params.contentType}`,
4080
+ method: "GET",
4081
+ config: {
4082
+ params: query
4083
+ }
4084
+ };
4085
+ }
4086
+ })
4087
+ })
4088
+ });
4089
+ const { useGetPreviewUrlQuery } = previewApi;
4090
+ const ConditionalTooltip = ({ isShown, label, children }) => {
4091
+ if (isShown) {
4092
+ return /* @__PURE__ */ jsx(Tooltip, { label, children });
4093
+ }
4094
+ return children;
4095
+ };
4096
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4097
+ const { formatMessage } = useIntl();
4098
+ const { trackUsage } = useTracking();
4099
+ const { pathname } = useLocation();
4100
+ const [{ query }] = useQueryParams();
4101
+ const isModified = useForm("PreviewSidePanel", (state) => state.modified);
4102
+ const { data, error } = useGetPreviewUrlQuery({
4103
+ params: {
4104
+ contentType: model
4105
+ },
4106
+ query: {
4107
+ documentId,
4108
+ locale: document?.locale,
4109
+ status: document?.status
4110
+ }
4111
+ });
4112
+ if (!data?.data?.url || error) {
4113
+ return null;
4114
+ }
4115
+ const trackNavigation = () => {
4116
+ const destinationPathname = pathname.replace(/\/$/, "") + "/preview";
4117
+ trackUsage("willNavigate", { from: pathname, to: destinationPathname });
4118
+ };
4119
+ return {
4120
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4121
+ content: /* @__PURE__ */ jsx(
4122
+ ConditionalTooltip,
4123
+ {
4124
+ label: formatMessage({
4125
+ id: "content-manager.preview.panel.button-disabled-tooltip",
4126
+ defaultMessage: "Please save to open the preview"
4127
+ }),
4128
+ isShown: isModified,
4129
+ children: /* @__PURE__ */ jsx(Box, { cursor: "not-allowed", width: "100%", children: /* @__PURE__ */ jsx(
4130
+ Button,
4131
+ {
4132
+ variant: "tertiary",
4133
+ tag: Link,
4134
+ to: { pathname: "preview", search: stringify(query, { encode: false }) },
4135
+ onClick: trackNavigation,
4136
+ width: "100%",
4137
+ disabled: isModified,
4138
+ pointerEvents: isModified ? "none" : void 0,
4139
+ tabIndex: isModified ? -1 : void 0,
4140
+ children: formatMessage({
4141
+ id: "content-manager.preview.panel.button",
4142
+ defaultMessage: "Open preview"
4143
+ })
4144
+ }
4145
+ ) })
4146
+ }
4147
+ )
4148
+ };
4149
+ };
4150
+ const previewAdmin = {
4151
+ bootstrap(app) {
4152
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4153
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4154
+ }
4155
+ };
3838
4156
  const index = {
3839
4157
  register(app) {
3840
4158
  const cm = new ContentManagerPlugin();
@@ -3854,7 +4172,7 @@ const index = {
3854
4172
  app.router.addRoute({
3855
4173
  path: "content-manager/*",
3856
4174
  lazy: async () => {
3857
- const { Layout } = await import("./layout-C0INpKap.mjs");
4175
+ const { Layout } = await import("./layout-Bxsv5mP7.mjs");
3858
4176
  return {
3859
4177
  Component: Layout
3860
4178
  };
@@ -3867,11 +4185,14 @@ const index = {
3867
4185
  if (typeof historyAdmin.bootstrap === "function") {
3868
4186
  historyAdmin.bootstrap(app);
3869
4187
  }
4188
+ if (typeof previewAdmin.bootstrap === "function") {
4189
+ previewAdmin.bootstrap(app);
4190
+ }
3870
4191
  },
3871
4192
  async registerTrads({ locales }) {
3872
4193
  const importedTrads = await Promise.all(
3873
4194
  locales.map((locale) => {
3874
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-BrCTWlZv.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
4195
+ 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 }) => {
3875
4196
  return {
3876
4197
  data: prefixPluginTranslations(data, PLUGIN_ID),
3877
4198
  locale
@@ -3892,13 +4213,16 @@ export {
3892
4213
  BulkActionsRenderer as B,
3893
4214
  COLLECTION_TYPES as C,
3894
4215
  DocumentStatus as D,
3895
- DEFAULT_SETTINGS as E,
3896
- convertEditLayoutToFieldLayouts as F,
3897
- useDocument as G,
4216
+ extractContentTypeComponents as E,
4217
+ DEFAULT_SETTINGS as F,
4218
+ convertEditLayoutToFieldLayouts as G,
3898
4219
  HOOKS as H,
3899
4220
  InjectionZone as I,
3900
- index as J,
3901
- useDocumentActions as K,
4221
+ useDocument as J,
4222
+ useGetPreviewUrlQuery as K,
4223
+ index as L,
4224
+ useContentManagerContext as M,
4225
+ useDocumentActions as N,
3902
4226
  Panels as P,
3903
4227
  RelativeTime as R,
3904
4228
  SINGLE_TYPES as S,
@@ -3916,18 +4240,18 @@ export {
3916
4240
  PERMISSIONS as k,
3917
4241
  DocumentRBAC as l,
3918
4242
  DOCUMENT_META_FIELDS as m,
3919
- useDocLayout as n,
3920
- useGetContentTypeConfigurationQuery as o,
3921
- CREATOR_FIELDS as p,
3922
- getMainField as q,
3923
- getDisplayName as r,
4243
+ CLONE_PATH as n,
4244
+ useDocLayout as o,
4245
+ useGetContentTypeConfigurationQuery as p,
4246
+ CREATOR_FIELDS as q,
4247
+ getMainField as r,
3924
4248
  setInitialData as s,
3925
- checkIfAttributeIsDisplayable as t,
4249
+ getDisplayName as t,
3926
4250
  useContentTypeSchema as u,
3927
- useGetAllDocumentsQuery as v,
3928
- convertListLayoutToFieldLayouts as w,
3929
- capitalise as x,
3930
- useUpdateContentTypeConfigurationMutation as y,
3931
- extractContentTypeComponents as z
4251
+ checkIfAttributeIsDisplayable as v,
4252
+ useGetAllDocumentsQuery as w,
4253
+ convertListLayoutToFieldLayouts as x,
4254
+ capitalise as y,
4255
+ useUpdateContentTypeConfigurationMutation as z
3932
4256
  };
3933
- //# sourceMappingURL=index-Dpxg3ctD.mjs.map
4257
+ //# sourceMappingURL=index-DVAIIsOs.mjs.map