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

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 (213) 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-5ukroXAh.js → ComponentConfigurationPage-BlzvDpbX.js} +5 -6
  4. package/dist/_chunks/{ComponentConfigurationPage-5ukroXAh.js.map → ComponentConfigurationPage-BlzvDpbX.js.map} +1 -1
  5. package/dist/_chunks/{ComponentConfigurationPage-BAgyHiMm.mjs → ComponentConfigurationPage-DaPOlQaD.mjs} +4 -4
  6. package/dist/_chunks/{ComponentConfigurationPage-BAgyHiMm.mjs.map → ComponentConfigurationPage-DaPOlQaD.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-DmoXawIh.mjs → EditConfigurationPage-BZPXItXo.mjs} +4 -4
  11. package/dist/_chunks/{EditConfigurationPage-DmoXawIh.mjs.map → EditConfigurationPage-BZPXItXo.mjs.map} +1 -1
  12. package/dist/_chunks/{EditConfigurationPage-Xp7lun0f.js → EditConfigurationPage-uy-v43AR.js} +5 -6
  13. package/dist/_chunks/{EditConfigurationPage-Xp7lun0f.js.map → EditConfigurationPage-uy-v43AR.js.map} +1 -1
  14. package/dist/_chunks/{EditViewPage-C-ukDOB7.js → EditViewPage-DT6A4ayX.js} +63 -13
  15. package/dist/_chunks/EditViewPage-DT6A4ayX.js.map +1 -0
  16. package/dist/_chunks/{EditViewPage-BLsjc5F-.mjs → EditViewPage-oOLeTySr.mjs} +63 -12
  17. package/dist/_chunks/EditViewPage-oOLeTySr.mjs.map +1 -0
  18. package/dist/_chunks/{Field-Cs7duwWd.mjs → Field-D7dv2aUX.mjs} +335 -211
  19. package/dist/_chunks/Field-D7dv2aUX.mjs.map +1 -0
  20. package/dist/_chunks/{Field-Bfph5SOd.js → Field-kYFVIGiP.js} +339 -215
  21. package/dist/_chunks/Field-kYFVIGiP.js.map +1 -0
  22. package/dist/_chunks/FieldTypeIcon-CMlNO8PE.mjs.map +1 -1
  23. package/dist/_chunks/FieldTypeIcon-Dnwq_IRF.js.map +1 -1
  24. package/dist/_chunks/{Form-Dg_GS5TQ.mjs → Form-BxR6sc29.mjs} +37 -18
  25. package/dist/_chunks/Form-BxR6sc29.mjs.map +1 -0
  26. package/dist/_chunks/{Form-CPYqIWDG.js → Form-CCijSg3V.js} +39 -21
  27. package/dist/_chunks/Form-CCijSg3V.js.map +1 -0
  28. package/dist/_chunks/{History-DNQkXANT.js → History-BMndx49M.js} +42 -100
  29. package/dist/_chunks/History-BMndx49M.js.map +1 -0
  30. package/dist/_chunks/{History-wrnHqf09.mjs → History-D8F7aYQU.mjs} +43 -100
  31. package/dist/_chunks/History-D8F7aYQU.mjs.map +1 -0
  32. package/dist/_chunks/{ListConfigurationPage-CUQxfpjT.js → ListConfigurationPage-DouY1EWM.js} +19 -9
  33. package/dist/_chunks/ListConfigurationPage-DouY1EWM.js.map +1 -0
  34. package/dist/_chunks/{ListConfigurationPage-DScmJVkW.mjs → ListConfigurationPage-DqAdSPwC.mjs} +19 -8
  35. package/dist/_chunks/ListConfigurationPage-DqAdSPwC.mjs.map +1 -0
  36. package/dist/_chunks/{ListViewPage-BsLiH2-2.js → ListViewPage-BPVmh9pq.js} +109 -78
  37. package/dist/_chunks/ListViewPage-BPVmh9pq.js.map +1 -0
  38. package/dist/_chunks/{ListViewPage-C4IvrMgY.mjs → ListViewPage-C73F0jPh.mjs} +106 -74
  39. package/dist/_chunks/ListViewPage-C73F0jPh.mjs.map +1 -0
  40. package/dist/_chunks/{NoContentTypePage-Djg8nPlj.mjs → NoContentTypePage-B5w7iJOF.mjs} +2 -2
  41. package/dist/_chunks/{NoContentTypePage-Djg8nPlj.mjs.map → NoContentTypePage-B5w7iJOF.mjs.map} +1 -1
  42. package/dist/_chunks/{NoContentTypePage-BZ-PnGAf.js → NoContentTypePage-BwcL--4H.js} +2 -2
  43. package/dist/_chunks/{NoContentTypePage-BZ-PnGAf.js.map → NoContentTypePage-BwcL--4H.js.map} +1 -1
  44. package/dist/_chunks/{NoPermissionsPage-_lUqjGW3.js → NoPermissionsPage-BMFKVcwJ.js} +2 -2
  45. package/dist/_chunks/{NoPermissionsPage-_lUqjGW3.js.map → NoPermissionsPage-BMFKVcwJ.js.map} +1 -1
  46. package/dist/_chunks/{NoPermissionsPage-DSP7R-hv.mjs → NoPermissionsPage-UnEgMGK4.mjs} +2 -2
  47. package/dist/_chunks/{NoPermissionsPage-DSP7R-hv.mjs.map → NoPermissionsPage-UnEgMGK4.mjs.map} +1 -1
  48. package/dist/_chunks/Preview-B7PR3Ok_.js +312 -0
  49. package/dist/_chunks/Preview-B7PR3Ok_.js.map +1 -0
  50. package/dist/_chunks/Preview-DECOhK0D.mjs +294 -0
  51. package/dist/_chunks/Preview-DECOhK0D.mjs.map +1 -0
  52. package/dist/_chunks/{Relations-BZr8tL0R.mjs → Relations-DinMQJ4B.mjs} +76 -42
  53. package/dist/_chunks/Relations-DinMQJ4B.mjs.map +1 -0
  54. package/dist/_chunks/{Relations-CtELXYIK.js → Relations-lndx3aQk.js} +76 -43
  55. package/dist/_chunks/Relations-lndx3aQk.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-c_5DdJi-.mjs → index-C2SagWVW.mjs} +1107 -777
  70. package/dist/_chunks/index-C2SagWVW.mjs.map +1 -0
  71. package/dist/_chunks/{index-OerGjbAN.js → index-Cnw4gqee.js} +1089 -759
  72. package/dist/_chunks/index-Cnw4gqee.js.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-oPBiO7RY.mjs → layout-DY_D9MGA.mjs} +23 -10
  78. package/dist/_chunks/layout-DY_D9MGA.mjs.map +1 -0
  79. package/dist/_chunks/{layout-Ci7qHlFb.js → layout-ivwIVPnV.js} +23 -11
  80. package/dist/_chunks/layout-ivwIVPnV.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-COBpStiF.js → relations-B7C7O_Pv.js} +6 -7
  86. package/dist/_chunks/relations-B7C7O_Pv.js.map +1 -0
  87. package/dist/_chunks/{relations-BIdWFjdq.mjs → relations-Boc5Y9kX.mjs} +6 -7
  88. package/dist/_chunks/relations-Boc5Y9kX.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 +648 -333
  131. package/dist/server/index.js.map +1 -1
  132. package/dist/server/index.mjs +649 -333
  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 +16 -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 +4 -4
  143. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  144. package/dist/server/src/index.d.ts +7 -6
  145. package/dist/server/src/index.d.ts.map +1 -1
  146. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  147. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  148. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  149. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  150. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  151. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  152. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  153. package/dist/server/src/preview/index.d.ts +4 -0
  154. package/dist/server/src/preview/index.d.ts.map +1 -0
  155. package/dist/server/src/preview/routes/index.d.ts +8 -0
  156. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  157. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  158. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  159. package/dist/server/src/preview/services/index.d.ts +16 -0
  160. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  161. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  162. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  163. package/dist/server/src/preview/services/preview.d.ts +12 -0
  164. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  165. package/dist/server/src/preview/utils.d.ts +19 -0
  166. package/dist/server/src/preview/utils.d.ts.map +1 -0
  167. package/dist/server/src/register.d.ts.map +1 -1
  168. package/dist/server/src/routes/index.d.ts.map +1 -1
  169. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  170. package/dist/server/src/services/document-metadata.d.ts +12 -10
  171. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  172. package/dist/server/src/services/index.d.ts +7 -6
  173. package/dist/server/src/services/index.d.ts.map +1 -1
  174. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  175. package/dist/server/src/services/utils/populate.d.ts +2 -2
  176. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  177. package/dist/server/src/utils/index.d.ts +2 -0
  178. package/dist/server/src/utils/index.d.ts.map +1 -1
  179. package/dist/shared/contracts/collection-types.d.ts +3 -1
  180. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  181. package/dist/shared/contracts/index.d.ts +1 -0
  182. package/dist/shared/contracts/index.d.ts.map +1 -1
  183. package/dist/shared/contracts/preview.d.ts +27 -0
  184. package/dist/shared/contracts/preview.d.ts.map +1 -0
  185. package/dist/shared/index.js +4 -0
  186. package/dist/shared/index.js.map +1 -1
  187. package/dist/shared/index.mjs +4 -0
  188. package/dist/shared/index.mjs.map +1 -1
  189. package/package.json +17 -15
  190. package/dist/_chunks/EditViewPage-BLsjc5F-.mjs.map +0 -1
  191. package/dist/_chunks/EditViewPage-C-ukDOB7.js.map +0 -1
  192. package/dist/_chunks/Field-Bfph5SOd.js.map +0 -1
  193. package/dist/_chunks/Field-Cs7duwWd.mjs.map +0 -1
  194. package/dist/_chunks/Form-CPYqIWDG.js.map +0 -1
  195. package/dist/_chunks/Form-Dg_GS5TQ.mjs.map +0 -1
  196. package/dist/_chunks/History-DNQkXANT.js.map +0 -1
  197. package/dist/_chunks/History-wrnHqf09.mjs.map +0 -1
  198. package/dist/_chunks/ListConfigurationPage-CUQxfpjT.js.map +0 -1
  199. package/dist/_chunks/ListConfigurationPage-DScmJVkW.mjs.map +0 -1
  200. package/dist/_chunks/ListViewPage-BsLiH2-2.js.map +0 -1
  201. package/dist/_chunks/ListViewPage-C4IvrMgY.mjs.map +0 -1
  202. package/dist/_chunks/Relations-BZr8tL0R.mjs.map +0 -1
  203. package/dist/_chunks/Relations-CtELXYIK.js.map +0 -1
  204. package/dist/_chunks/index-OerGjbAN.js.map +0 -1
  205. package/dist/_chunks/index-c_5DdJi-.mjs.map +0 -1
  206. package/dist/_chunks/layout-Ci7qHlFb.js.map +0 -1
  207. package/dist/_chunks/layout-oPBiO7RY.mjs.map +0 -1
  208. package/dist/_chunks/relations-BIdWFjdq.mjs.map +0 -1
  209. package/dist/_chunks/relations-COBpStiF.js.map +0 -1
  210. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  211. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  212. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  213. 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, Box, Typography, Dialog, Modal, Radio, Status, 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,11 +281,12 @@ 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) => {
266
288
  return [
289
+ { type: "Document", id: `ALL_LIST` },
267
290
  { type: "Document", id: `${arg.model}_LIST` },
268
291
  ...result?.results.map(({ documentId }) => ({
269
292
  type: "Document",
@@ -302,6 +325,11 @@ const documentApi = contentManagerApi.injectEndpoints({
302
325
  {
303
326
  type: "Document",
304
327
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
328
+ },
329
+ // Make it easy to invalidate all individual documents queries for a model
330
+ {
331
+ type: "Document",
332
+ id: `${model}_ALL_ITEMS`
305
333
  }
306
334
  ];
307
335
  }
@@ -365,7 +393,8 @@ const documentApi = contentManagerApi.injectEndpoints({
365
393
  type: "Document",
366
394
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
367
395
  },
368
- "Relations"
396
+ "Relations",
397
+ { type: "UidAvailability", id: model }
369
398
  ];
370
399
  },
371
400
  async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
@@ -431,8 +460,7 @@ const {
431
460
  useUnpublishManyDocumentsMutation
432
461
  } = documentApi;
433
462
  const buildValidParams = (query) => {
434
- if (!query)
435
- return query;
463
+ if (!query) return query;
436
464
  const { plugins: _, ...validQueryParams } = {
437
465
  ...query,
438
466
  ...Object.values(query?.plugins ?? {}).reduce(
@@ -440,28 +468,44 @@ const buildValidParams = (query) => {
440
468
  {}
441
469
  )
442
470
  };
443
- if ("_q" in validQueryParams) {
444
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
445
- }
446
471
  return validQueryParams;
447
472
  };
448
473
  const isBaseQueryError = (error) => {
449
474
  return error.name !== void 0;
450
475
  };
451
- 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 }) => {
452
495
  const createModelSchema = (attributes2) => yup.object().shape(
453
496
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
454
497
  if (DOCUMENT_META_FIELDS.includes(name)) {
455
498
  return acc;
456
499
  }
457
500
  const validations = [
501
+ addNullableValidation,
458
502
  addRequiredValidation,
459
503
  addMinLengthValidation,
460
504
  addMaxLengthValidation,
461
505
  addMinValidation,
462
506
  addMaxValidation,
463
507
  addRegexValidation
464
- ].map((fn) => fn(attribute));
508
+ ].map((fn) => fn(attribute, options));
465
509
  const transformSchema = pipe(...validations);
466
510
  switch (attribute.type) {
467
511
  case "component": {
@@ -471,12 +515,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
471
515
  ...acc,
472
516
  [name]: transformSchema(
473
517
  yup.array().of(createModelSchema(attributes3).nullable(false))
474
- )
518
+ ).test(arrayValidator(attribute, options))
475
519
  };
476
520
  } else {
477
521
  return {
478
522
  ...acc,
479
- [name]: transformSchema(createModelSchema(attributes3))
523
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
480
524
  };
481
525
  }
482
526
  }
@@ -498,7 +542,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
498
542
  }
499
543
  )
500
544
  )
501
- )
545
+ ).test(arrayValidator(attribute, options))
502
546
  };
503
547
  case "relation":
504
548
  return {
@@ -510,7 +554,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
510
554
  } else if (Array.isArray(value)) {
511
555
  return yup.array().of(
512
556
  yup.object().shape({
513
- id: yup.string().required()
557
+ id: yup.number().required()
514
558
  })
515
559
  );
516
560
  } else if (typeof value === "object") {
@@ -562,6 +606,14 @@ const createAttributeSchema = (attribute) => {
562
606
  if (!value || typeof value === "string" && value.length === 0) {
563
607
  return true;
564
608
  }
609
+ if (typeof value === "object") {
610
+ try {
611
+ JSON.stringify(value);
612
+ return true;
613
+ } catch (err) {
614
+ return false;
615
+ }
616
+ }
565
617
  try {
566
618
  JSON.parse(value);
567
619
  return true;
@@ -580,13 +632,7 @@ const createAttributeSchema = (attribute) => {
580
632
  return yup.mixed();
581
633
  }
582
634
  };
583
- const addRequiredValidation = (attribute) => (schema) => {
584
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
585
- return schema.min(1, translatedErrors.required);
586
- }
587
- if (attribute.required && attribute.type !== "relation") {
588
- return schema.required(translatedErrors.required);
589
- }
635
+ const nullableSchema = (schema) => {
590
636
  return schema?.nullable ? schema.nullable() : (
591
637
  // In some cases '.nullable' will not be available on the schema.
592
638
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -594,7 +640,22 @@ const addRequiredValidation = (attribute) => (schema) => {
594
640
  schema
595
641
  );
596
642
  };
597
- 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
+ }
598
659
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
599
660
  return schema.min(attribute.minLength, {
600
661
  ...translatedErrors.minLength,
@@ -616,32 +677,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
616
677
  }
617
678
  return schema;
618
679
  };
619
- const addMinValidation = (attribute) => (schema) => {
620
- 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) {
621
685
  const min = toInteger(attribute.min);
622
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
623
- if (!attribute.required && "test" in schema && min) {
624
- return schema.test(
625
- "custom-min",
626
- {
627
- ...translatedErrors.min,
628
- values: {
629
- min: attribute.min
630
- }
631
- },
632
- (value) => {
633
- if (!value) {
634
- return true;
635
- }
636
- if (Array.isArray(value) && value.length === 0) {
637
- return true;
638
- }
639
- return value.length >= min;
640
- }
641
- );
642
- }
643
- }
644
- if ("min" in schema && min) {
686
+ if (min) {
645
687
  return schema.min(min, {
646
688
  ...translatedErrors.min,
647
689
  values: {
@@ -759,19 +801,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
759
801
  }, {});
760
802
  return componentsByKey;
761
803
  };
762
- 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);
763
903
  const { toggleNotification } = useNotification();
764
904
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
905
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
765
906
  const {
766
- currentData: data,
767
- isLoading: isLoadingDocument,
768
- isFetching: isFetchingDocument,
769
- error
770
- } = useGetDocumentQuery(args, {
771
- ...opts,
772
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
773
- });
774
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
907
+ data,
908
+ isLoading: isLoadingConfigs,
909
+ error,
910
+ isFetching: isFetchingConfigs
911
+ } = useGetContentTypeConfigurationQuery(model);
912
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
775
913
  React.useEffect(() => {
776
914
  if (error) {
777
915
  toggleNotification({
@@ -779,83 +917,339 @@ const useDocument = (args, opts) => {
779
917
  message: formatAPIError(error)
780
918
  });
781
919
  }
782
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
783
- const validationSchema = React.useMemo(() => {
784
- if (!schema) {
785
- return null;
786
- }
787
- return createYupSchema(schema.attributes, components);
788
- }, [schema, components]);
789
- const validate = React.useCallback(
790
- (document) => {
791
- if (!validationSchema) {
792
- throw new Error(
793
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
794
- );
795
- }
796
- try {
797
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
798
- return null;
799
- } catch (error2) {
800
- if (error2 instanceof ValidationError) {
801
- return getYupValidationErrors(error2);
802
- }
803
- throw error2;
804
- }
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
805
928
  },
806
- [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]
807
945
  );
808
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
809
946
  return {
810
- components,
811
- document: data?.data,
812
- meta: data?.meta,
947
+ error,
813
948
  isLoading,
814
- schema,
815
- validate
816
- };
817
- };
818
- const useDoc = () => {
819
- const { id, slug, collectionType, origin } = useParams();
820
- const [{ query }] = useQueryParams();
821
- const params = React.useMemo(() => buildValidParams(query), [query]);
822
- if (!collectionType) {
823
- throw new Error("Could not find collectionType in url params");
824
- }
825
- if (!slug) {
826
- throw new Error("Could not find model in url params");
827
- }
828
- return {
829
- collectionType,
830
- model: slug,
831
- id: origin || id === "create" ? void 0 : id,
832
- ...useDocument(
833
- { documentId: origin || id, model: slug, collectionType, params },
834
- {
835
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
836
- }
837
- )
949
+ edit,
950
+ list: listLayout
838
951
  };
839
952
  };
840
- const prefixPluginTranslations = (trad, pluginId) => {
841
- if (!pluginId) {
842
- throw new TypeError("pluginId can't be empty");
843
- }
844
- return Object.keys(trad).reduce((acc, current) => {
845
- acc[`${pluginId}.${current}`] = trad[current];
846
- return acc;
847
- }, {});
848
- };
849
- const getTranslation = (id) => `content-manager.${id}`;
850
- const DEFAULT_UNEXPECTED_ERROR_MSG = {
851
- id: "notification.error",
852
- defaultMessage: "An error occurred, please try again"
953
+ const useDocLayout = () => {
954
+ const { model } = useDoc();
955
+ return useDocumentLayout(model);
853
956
  };
854
- const useDocumentActions = () => {
855
- const { toggleNotification } = useNotification();
856
- const { formatMessage } = useIntl();
857
- const { trackUsage } = useTracking();
858
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
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);
978
+ }
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
995
+ }
996
+ };
997
+ return acc;
998
+ },
999
+ {}
1000
+ );
1001
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1002
+ (acc, [attribute, metadata]) => {
1003
+ return {
1004
+ ...acc,
1005
+ [attribute]: metadata.edit
1006
+ };
1007
+ },
1008
+ {}
1009
+ );
1010
+ return {
1011
+ layout: panelledEditAttributes,
1012
+ components: componentEditAttributes,
1013
+ metadatas: editMetadatas,
1014
+ settings: {
1015
+ ...data.contentType.settings,
1016
+ displayName: schema?.info.displayName
1017
+ },
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;
1031
+ }
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)
1053
+ );
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
+ }
1147
+ try {
1148
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1149
+ return null;
1150
+ } catch (error2) {
1151
+ if (error2 instanceof ValidationError) {
1152
+ return getYupValidationErrors(error2);
1153
+ }
1154
+ throw error2;
1155
+ }
1156
+ },
1157
+ [validationSchema]
1158
+ );
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);
859
1253
  const [deleteDocument] = useDeleteDocumentMutation();
860
1254
  const _delete = React.useCallback(
861
1255
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1170,6 +1564,7 @@ const useDocumentActions = () => {
1170
1564
  defaultMessage: "Saved document"
1171
1565
  })
1172
1566
  });
1567
+ setCurrentStep("contentManager.success");
1173
1568
  return res.data;
1174
1569
  } catch (err) {
1175
1570
  toggleNotification({
@@ -1209,7 +1604,7 @@ const useDocumentActions = () => {
1209
1604
  throw err;
1210
1605
  }
1211
1606
  },
1212
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1607
+ [autoCloneDocument, formatMessage, toggleNotification]
1213
1608
  );
1214
1609
  const [cloneDocument] = useCloneDocumentMutation();
1215
1610
  const clone = React.useCallback(
@@ -1235,6 +1630,7 @@ const useDocumentActions = () => {
1235
1630
  defaultMessage: "Cloned document"
1236
1631
  })
1237
1632
  });
1633
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1238
1634
  return res.data;
1239
1635
  } catch (err) {
1240
1636
  toggleNotification({
@@ -1245,7 +1641,7 @@ const useDocumentActions = () => {
1245
1641
  throw err;
1246
1642
  }
1247
1643
  },
1248
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1644
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1249
1645
  );
1250
1646
  const [getDoc] = useLazyGetDocumentQuery();
1251
1647
  const getDocument = React.useCallback(
@@ -1270,10 +1666,10 @@ const useDocumentActions = () => {
1270
1666
  update
1271
1667
  };
1272
1668
  };
1273
- const ProtectedHistoryPage = lazy(
1274
- () => import("./History-wrnHqf09.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1669
+ const ProtectedHistoryPage = React.lazy(
1670
+ () => import("./History-D8F7aYQU.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1275
1671
  );
1276
- const routes$1 = [
1672
+ const routes$2 = [
1277
1673
  {
1278
1674
  path: ":collectionType/:slug/:id/history",
1279
1675
  Component: ProtectedHistoryPage
@@ -1283,32 +1679,45 @@ const routes$1 = [
1283
1679
  Component: ProtectedHistoryPage
1284
1680
  }
1285
1681
  ];
1286
- const ProtectedEditViewPage = lazy(
1287
- () => import("./EditViewPage-BLsjc5F-.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1682
+ const ProtectedPreviewPage = React.lazy(
1683
+ () => import("./Preview-DECOhK0D.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
+ ];
1695
+ const ProtectedEditViewPage = lazy(
1696
+ () => import("./EditViewPage-oOLeTySr.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1288
1697
  );
1289
1698
  const ProtectedListViewPage = lazy(
1290
- () => import("./ListViewPage-C4IvrMgY.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1699
+ () => import("./ListViewPage-C73F0jPh.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1291
1700
  );
1292
1701
  const ProtectedListConfiguration = lazy(
1293
- () => import("./ListConfigurationPage-DScmJVkW.mjs").then((mod) => ({
1702
+ () => import("./ListConfigurationPage-DqAdSPwC.mjs").then((mod) => ({
1294
1703
  default: mod.ProtectedListConfiguration
1295
1704
  }))
1296
1705
  );
1297
1706
  const ProtectedEditConfigurationPage = lazy(
1298
- () => import("./EditConfigurationPage-DmoXawIh.mjs").then((mod) => ({
1707
+ () => import("./EditConfigurationPage-BZPXItXo.mjs").then((mod) => ({
1299
1708
  default: mod.ProtectedEditConfigurationPage
1300
1709
  }))
1301
1710
  );
1302
1711
  const ProtectedComponentConfigurationPage = lazy(
1303
- () => import("./ComponentConfigurationPage-BAgyHiMm.mjs").then((mod) => ({
1712
+ () => import("./ComponentConfigurationPage-DaPOlQaD.mjs").then((mod) => ({
1304
1713
  default: mod.ProtectedComponentConfigurationPage
1305
1714
  }))
1306
1715
  );
1307
1716
  const NoPermissions = lazy(
1308
- () => import("./NoPermissionsPage-DSP7R-hv.mjs").then((mod) => ({ default: mod.NoPermissions }))
1717
+ () => import("./NoPermissionsPage-UnEgMGK4.mjs").then((mod) => ({ default: mod.NoPermissions }))
1309
1718
  );
1310
1719
  const NoContentType = lazy(
1311
- () => import("./NoContentTypePage-Djg8nPlj.mjs").then((mod) => ({ default: mod.NoContentType }))
1720
+ () => import("./NoContentTypePage-B5w7iJOF.mjs").then((mod) => ({ default: mod.NoContentType }))
1312
1721
  );
1313
1722
  const CollectionTypePages = () => {
1314
1723
  const { collectionType } = useParams();
@@ -1320,7 +1729,7 @@ const CollectionTypePages = () => {
1320
1729
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1321
1730
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1322
1731
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1323
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1732
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1324
1733
  const routes = [
1325
1734
  {
1326
1735
  path: LIST_RELATIVE_PATH,
@@ -1354,6 +1763,7 @@ const routes = [
1354
1763
  path: "no-content-types",
1355
1764
  Component: NoContentType
1356
1765
  },
1766
+ ...routes$2,
1357
1767
  ...routes$1
1358
1768
  ];
1359
1769
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1422,12 +1832,14 @@ const DocumentActionButton = (action) => {
1422
1832
  /* @__PURE__ */ jsx(
1423
1833
  Button,
1424
1834
  {
1425
- flex: 1,
1835
+ flex: "auto",
1426
1836
  startIcon: action.icon,
1427
1837
  disabled: action.disabled,
1428
1838
  onClick: handleClick(action),
1429
1839
  justifyContent: "center",
1430
1840
  variant: action.variant || "default",
1841
+ paddingTop: "7px",
1842
+ paddingBottom: "7px",
1431
1843
  children: action.label
1432
1844
  }
1433
1845
  ),
@@ -1450,6 +1862,11 @@ const DocumentActionButton = (action) => {
1450
1862
  ) : null
1451
1863
  ] });
1452
1864
  };
1865
+ const MenuItem = styled(Menu.Item)`
1866
+ &:hover {
1867
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
1868
+ }
1869
+ `;
1453
1870
  const DocumentActionsMenu = ({
1454
1871
  actions: actions2,
1455
1872
  children,
@@ -1492,9 +1909,9 @@ const DocumentActionsMenu = ({
1492
1909
  disabled: isDisabled,
1493
1910
  size: "S",
1494
1911
  endIcon: null,
1495
- paddingTop: "7px",
1496
- paddingLeft: "9px",
1497
- paddingRight: "9px",
1912
+ paddingTop: "4px",
1913
+ paddingLeft: "7px",
1914
+ paddingRight: "7px",
1498
1915
  variant,
1499
1916
  children: [
1500
1917
  /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
@@ -1505,36 +1922,35 @@ const DocumentActionsMenu = ({
1505
1922
  ]
1506
1923
  }
1507
1924
  ),
1508
- /* @__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: [
1509
1926
  actions2.map((action) => {
1510
1927
  return /* @__PURE__ */ jsx(
1511
- Menu.Item,
1928
+ MenuItem,
1512
1929
  {
1513
1930
  disabled: action.disabled,
1514
1931
  onSelect: handleClick(action),
1515
1932
  display: "block",
1516
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1517
- /* @__PURE__ */ jsxs(Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1518
- /* @__PURE__ */ jsx(Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1519
- action.label
1520
- ] }),
1521
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1522
- Flex,
1523
- {
1524
- alignItems: "center",
1525
- background: "alternative100",
1526
- borderStyle: "solid",
1527
- borderColor: "alternative200",
1528
- borderWidth: "1px",
1529
- height: 5,
1530
- paddingLeft: 2,
1531
- paddingRight: 2,
1532
- hasRadius: true,
1533
- color: "alternative600",
1534
- children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1535
- }
1536
- )
1537
- ] })
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
+ ) })
1538
1954
  },
1539
1955
  action.id
1540
1956
  );
@@ -1614,11 +2030,11 @@ const DocumentActionConfirmDialog = ({
1614
2030
  /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1615
2031
  /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1616
2032
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1617
- /* @__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({
1618
2034
  id: "app.components.Button.cancel",
1619
2035
  defaultMessage: "Cancel"
1620
2036
  }) }) }),
1621
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
2037
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1622
2038
  id: "app.components.Button.confirm",
1623
2039
  defaultMessage: "Confirm"
1624
2040
  }) })
@@ -1645,6 +2061,18 @@ const DocumentActionModal = ({
1645
2061
  typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1646
2062
  ] }) });
1647
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
+ };
1648
2076
  const PublishAction$1 = ({
1649
2077
  activeTab,
1650
2078
  documentId,
@@ -1657,12 +2085,11 @@ const PublishAction$1 = ({
1657
2085
  const navigate = useNavigate();
1658
2086
  const { toggleNotification } = useNotification();
1659
2087
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2088
+ const isListView = useMatch(LIST_PATH) !== null;
1660
2089
  const isCloning = useMatch(CLONE_PATH) !== null;
2090
+ const { id } = useParams();
1661
2091
  const { formatMessage } = useIntl();
1662
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1663
- "PublishAction",
1664
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1665
- );
2092
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1666
2093
  const { publish } = useDocumentActions();
1667
2094
  const [
1668
2095
  countDraftRelations,
@@ -1714,24 +2141,25 @@ const PublishAction$1 = ({
1714
2141
  }
1715
2142
  }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1716
2143
  React.useEffect(() => {
1717
- if (documentId) {
1718
- const fetchDraftRelationsCount = async () => {
1719
- const { data, error } = await countDraftRelations({
1720
- collectionType,
1721
- model,
1722
- documentId,
1723
- params
1724
- });
1725
- if (error) {
1726
- throw error;
1727
- }
1728
- if (data) {
1729
- setServerCountOfDraftRelations(data.data);
1730
- }
1731
- };
1732
- fetchDraftRelationsCount();
2144
+ if (!document || !document.documentId || isListView) {
2145
+ return;
1733
2146
  }
1734
- }, [documentId, countDraftRelations, collectionType, model, params]);
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]);
1735
2163
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1736
2164
  if (!schema?.options?.draftAndPublish) {
1737
2165
  return null;
@@ -1739,7 +2167,9 @@ const PublishAction$1 = ({
1739
2167
  const performPublish = async () => {
1740
2168
  setSubmitting(true);
1741
2169
  try {
1742
- const { errors } = await validate();
2170
+ const { errors } = await validate(true, {
2171
+ status: "published"
2172
+ });
1743
2173
  if (errors) {
1744
2174
  toggleNotification({
1745
2175
  type: "danger",
@@ -1757,13 +2187,15 @@ const PublishAction$1 = ({
1757
2187
  documentId,
1758
2188
  params
1759
2189
  },
1760
- formValues
2190
+ transformData(formValues)
1761
2191
  );
1762
2192
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1763
- navigate({
1764
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1765
- search: rawQuery
1766
- });
2193
+ if (id === "create") {
2194
+ navigate({
2195
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2196
+ search: rawQuery
2197
+ });
2198
+ }
1767
2199
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1768
2200
  setErrors(formatValidationErrors(res.error));
1769
2201
  }
@@ -1772,7 +2204,8 @@ const PublishAction$1 = ({
1772
2204
  }
1773
2205
  };
1774
2206
  const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1775
- const hasDraftRelations = totalDraftRelations > 0;
2207
+ const enableDraftRelationsCount = false;
2208
+ const hasDraftRelations = enableDraftRelationsCount;
1776
2209
  return {
1777
2210
  /**
1778
2211
  * Disabled when:
@@ -1782,18 +2215,13 @@ const PublishAction$1 = ({
1782
2215
  * - the document is already published & not modified
1783
2216
  * - the document is being created & not modified
1784
2217
  * - the user doesn't have the permission to publish
1785
- * - the user doesn't have the permission to create a new document
1786
- * - the user doesn't have the permission to update the document
1787
2218
  */
1788
- disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2219
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1789
2220
  label: formatMessage({
1790
2221
  id: "app.utils.publish",
1791
2222
  defaultMessage: "Publish"
1792
2223
  }),
1793
2224
  onClick: async () => {
1794
- if (hasDraftRelations) {
1795
- return;
1796
- }
1797
2225
  await performPublish();
1798
2226
  },
1799
2227
  dialog: hasDraftRelations ? {
@@ -1820,6 +2248,7 @@ const PublishAction$1 = ({
1820
2248
  };
1821
2249
  };
1822
2250
  PublishAction$1.type = "publish";
2251
+ PublishAction$1.position = "panel";
1823
2252
  const UpdateAction = ({
1824
2253
  activeTab,
1825
2254
  documentId,
@@ -1832,10 +2261,6 @@ const UpdateAction = ({
1832
2261
  const cloneMatch = useMatch(CLONE_PATH);
1833
2262
  const isCloning = cloneMatch !== null;
1834
2263
  const { formatMessage } = useIntl();
1835
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1836
- canCreate: canCreate2,
1837
- canUpdate: canUpdate2
1838
- }));
1839
2264
  const { create, update, clone } = useDocumentActions();
1840
2265
  const [{ query, rawQuery }] = useQueryParams();
1841
2266
  const params = React.useMemo(() => buildValidParams(query), [query]);
@@ -1846,96 +2271,134 @@ const UpdateAction = ({
1846
2271
  const validate = useForm("UpdateAction", (state) => state.validate);
1847
2272
  const setErrors = useForm("UpdateAction", (state) => state.setErrors);
1848
2273
  const resetForm = useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
1849
- return {
1850
- /**
1851
- * Disabled when:
1852
- * - the form is submitting
1853
- * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1854
- * - the active tab is the published tab
1855
- * - the user doesn't have the permission to create a new document
1856
- * - the user doesn't have the permission to update the document
1857
- */
1858
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
1859
- label: formatMessage({
1860
- id: "content-manager.containers.Edit.save",
1861
- defaultMessage: "Save"
1862
- }),
1863
- onClick: async () => {
1864
- setSubmitting(true);
1865
- try {
1866
- const { errors } = await validate();
1867
- if (errors) {
1868
- toggleNotification({
1869
- type: "danger",
1870
- message: formatMessage({
1871
- id: "content-manager.validation.error",
1872
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1873
- })
1874
- });
1875
- return;
1876
- }
1877
- if (isCloning) {
1878
- const res = await clone(
1879
- {
1880
- model,
1881
- documentId: cloneMatch.params.origin,
1882
- params
1883
- },
1884
- document
1885
- );
1886
- if ("data" in res) {
1887
- navigate(
1888
- {
1889
- pathname: `../${res.data.documentId}`,
1890
- search: rawQuery
1891
- },
1892
- { relative: "path" }
1893
- );
1894
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1895
- setErrors(formatValidationErrors(res.error));
1896
- }
1897
- } else if (documentId || collectionType === SINGLE_TYPES) {
1898
- const res = await update(
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(
1899
2304
  {
1900
- collectionType,
1901
- model,
1902
- documentId,
1903
- params
2305
+ pathname: `../${res.data.documentId}`,
2306
+ search: rawQuery
1904
2307
  },
1905
- document
2308
+ { relative: "path" }
1906
2309
  );
1907
- if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1908
- setErrors(formatValidationErrors(res.error));
1909
- } else {
1910
- resetForm();
1911
- }
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));
1912
2325
  } else {
1913
- const res = await create(
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(
1914
2338
  {
1915
- model,
1916
- params
2339
+ pathname: `../${res.data.documentId}`,
2340
+ search: rawQuery
1917
2341
  },
1918
- document
2342
+ { replace: true, relative: "path" }
1919
2343
  );
1920
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1921
- navigate(
1922
- {
1923
- pathname: `../${res.data.documentId}`,
1924
- search: rawQuery
1925
- },
1926
- { replace: true, relative: "path" }
1927
- );
1928
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1929
- setErrors(formatValidationErrors(res.error));
1930
- }
2344
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2345
+ setErrors(formatValidationErrors(res.error));
1931
2346
  }
1932
- } finally {
1933
- setSubmitting(false);
1934
2347
  }
2348
+ } finally {
2349
+ setSubmitting(false);
1935
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]);
2385
+ return {
2386
+ /**
2387
+ * Disabled when:
2388
+ * - the form is submitting
2389
+ * - the document is not modified & we're not cloning (you can save a clone entity straight away)
2390
+ * - the active tab is the published tab
2391
+ */
2392
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
2393
+ label: formatMessage({
2394
+ id: "global.save",
2395
+ defaultMessage: "Save"
2396
+ }),
2397
+ onClick: handleUpdate
1936
2398
  };
1937
2399
  };
1938
2400
  UpdateAction.type = "update";
2401
+ UpdateAction.position = "panel";
1939
2402
  const UNPUBLISH_DRAFT_OPTIONS = {
1940
2403
  KEEP: "keep",
1941
2404
  DISCARD: "discard"
@@ -1968,7 +2431,7 @@ const UnpublishAction$1 = ({
1968
2431
  id: "app.utils.unpublish",
1969
2432
  defaultMessage: "Unpublish"
1970
2433
  }),
1971
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2434
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1972
2435
  onClick: async () => {
1973
2436
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1974
2437
  if (!documentId) {
@@ -2058,6 +2521,7 @@ const UnpublishAction$1 = ({
2058
2521
  };
2059
2522
  };
2060
2523
  UnpublishAction$1.type = "unpublish";
2524
+ UnpublishAction$1.position = "panel";
2061
2525
  const DiscardAction = ({
2062
2526
  activeTab,
2063
2527
  documentId,
@@ -2080,7 +2544,7 @@ const DiscardAction = ({
2080
2544
  id: "content-manager.actions.discard.label",
2081
2545
  defaultMessage: "Discard changes"
2082
2546
  }),
2083
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2547
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2084
2548
  position: ["panel", "table-row"],
2085
2549
  variant: "danger",
2086
2550
  dialog: {
@@ -2108,11 +2572,7 @@ const DiscardAction = ({
2108
2572
  };
2109
2573
  };
2110
2574
  DiscardAction.type = "discard";
2111
- const StyledCrossCircle = styled(CrossCircle)`
2112
- path {
2113
- fill: currentColor;
2114
- }
2115
- `;
2575
+ DiscardAction.position = "panel";
2116
2576
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2117
2577
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2118
2578
  const RelativeTime = React.forwardRef(
@@ -2125,7 +2585,7 @@ const RelativeTime = React.forwardRef(
2125
2585
  });
2126
2586
  const unit = intervals.find((intervalUnit) => {
2127
2587
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2128
- });
2588
+ }) ?? "seconds";
2129
2589
  const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2130
2590
  const customInterval = customIntervals.find(
2131
2591
  (custom) => interval[custom.unit] < custom.threshold
@@ -2159,19 +2619,29 @@ const getDisplayName = ({
2159
2619
  return email ?? "";
2160
2620
  };
2161
2621
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2162
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2163
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2164
- return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
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
+ }) }) });
2165
2629
  };
2166
2630
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2167
2631
  const { formatMessage } = useIntl();
2168
2632
  const isCloning = useMatch(CLONE_PATH) !== null;
2633
+ const params = useParams();
2169
2634
  const title = isCreating ? formatMessage({
2170
2635
  id: "content-manager.containers.edit.title.new",
2171
2636
  defaultMessage: "Create an entry"
2172
2637
  }) : documentTitle;
2173
2638
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2174
- /* @__PURE__ */ jsx(BackButton, {}),
2639
+ /* @__PURE__ */ jsx(
2640
+ BackButton,
2641
+ {
2642
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2643
+ }
2644
+ ),
2175
2645
  /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2176
2646
  /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2177
2647
  /* @__PURE__ */ jsx(HeaderToolbar, {})
@@ -2222,7 +2692,7 @@ const HeaderToolbar = () => {
2222
2692
  meta: isCloning ? void 0 : meta,
2223
2693
  collectionType
2224
2694
  },
2225
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2695
+ descriptions: plugins["content-manager"].apis.getDocumentActions("header"),
2226
2696
  children: (actions2) => {
2227
2697
  const headerActions = actions2.filter((action) => {
2228
2698
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2259,12 +2729,12 @@ const Information = ({ activeTab }) => {
2259
2729
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2260
2730
  label: formatMessage({
2261
2731
  id: "content-manager.containers.edit.information.last-published.label",
2262
- defaultMessage: "Last published"
2732
+ defaultMessage: "Published"
2263
2733
  }),
2264
2734
  value: formatMessage(
2265
2735
  {
2266
2736
  id: "content-manager.containers.edit.information.last-published.value",
2267
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2737
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2268
2738
  },
2269
2739
  {
2270
2740
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2277,12 +2747,12 @@ const Information = ({ activeTab }) => {
2277
2747
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2278
2748
  label: formatMessage({
2279
2749
  id: "content-manager.containers.edit.information.last-draft.label",
2280
- defaultMessage: "Last draft"
2750
+ defaultMessage: "Updated"
2281
2751
  }),
2282
2752
  value: formatMessage(
2283
2753
  {
2284
2754
  id: "content-manager.containers.edit.information.last-draft.value",
2285
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2755
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2286
2756
  },
2287
2757
  {
2288
2758
  time: /* @__PURE__ */ jsx(
@@ -2300,12 +2770,12 @@ const Information = ({ activeTab }) => {
2300
2770
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2301
2771
  label: formatMessage({
2302
2772
  id: "content-manager.containers.edit.information.document.label",
2303
- defaultMessage: "Document"
2773
+ defaultMessage: "Created"
2304
2774
  }),
2305
2775
  value: formatMessage(
2306
2776
  {
2307
2777
  id: "content-manager.containers.edit.information.document.value",
2308
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2778
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2309
2779
  },
2310
2780
  {
2311
2781
  time: /* @__PURE__ */ jsx(
@@ -2343,25 +2813,77 @@ const Information = ({ activeTab }) => {
2343
2813
  );
2344
2814
  };
2345
2815
  const HeaderActions = ({ actions: actions2 }) => {
2346
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2347
- 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) {
2348
2832
  return /* @__PURE__ */ jsx(
2349
2833
  SingleSelect,
2350
2834
  {
2351
2835
  size: "S",
2352
- disabled: action.disabled,
2353
- "aria-label": action.label,
2354
2836
  onChange: action.onSelect,
2355
- value: action.value,
2837
+ "aria-label": action.label,
2838
+ ...action,
2356
2839
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2357
2840
  },
2358
2841
  action.id
2359
2842
  );
2360
2843
  } else {
2361
- 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
+ }
2362
2866
  }
2363
2867
  }) });
2364
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
+ };
2365
2887
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2366
2888
  const navigate = useNavigate();
2367
2889
  const { formatMessage } = useIntl();
@@ -2378,6 +2900,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2378
2900
  };
2379
2901
  };
2380
2902
  ConfigureTheViewAction.type = "configure-the-view";
2903
+ ConfigureTheViewAction.position = "header";
2381
2904
  const EditTheModelAction = ({ model }) => {
2382
2905
  const navigate = useNavigate();
2383
2906
  const { formatMessage } = useIntl();
@@ -2394,6 +2917,7 @@ const EditTheModelAction = ({ model }) => {
2394
2917
  };
2395
2918
  };
2396
2919
  EditTheModelAction.type = "edit-the-model";
2920
+ EditTheModelAction.position = "header";
2397
2921
  const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2398
2922
  const navigate = useNavigate();
2399
2923
  const { formatMessage } = useIntl();
@@ -2402,12 +2926,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2402
2926
  const { delete: deleteAction } = useDocumentActions();
2403
2927
  const { toggleNotification } = useNotification();
2404
2928
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2929
+ const isLocalized = document?.locale != null;
2405
2930
  return {
2406
2931
  disabled: !canDelete || !document,
2407
- label: formatMessage({
2408
- id: "content-manager.actions.delete.label",
2409
- defaultMessage: "Delete document"
2410
- }),
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
+ ),
2411
2939
  icon: /* @__PURE__ */ jsx(Trash, {}),
2412
2940
  dialog: {
2413
2941
  type: "dialog",
@@ -2449,417 +2977,116 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2449
2977
  }
2450
2978
  });
2451
2979
  if (!("error" in res)) {
2452
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2453
- }
2454
- } finally {
2455
- if (!listViewPathMatch) {
2456
- setSubmitting(false);
2457
- }
2458
- }
2459
- }
2460
- },
2461
- variant: "danger",
2462
- position: ["header", "table-row"]
2463
- };
2464
- };
2465
- DeleteAction$1.type = "delete";
2466
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2467
- const Panels = () => {
2468
- const isCloning = useMatch(CLONE_PATH) !== null;
2469
- const [
2470
- {
2471
- query: { status }
2472
- }
2473
- ] = useQueryParams({
2474
- status: "draft"
2475
- });
2476
- const { model, id, document, meta, collectionType } = useDoc();
2477
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2478
- const props = {
2479
- activeTab: status,
2480
- model,
2481
- documentId: id,
2482
- document: isCloning ? void 0 : document,
2483
- meta: isCloning ? void 0 : meta,
2484
- collectionType
2485
- };
2486
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2487
- DescriptionComponentRenderer,
2488
- {
2489
- props,
2490
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2491
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2492
- }
2493
- ) });
2494
- };
2495
- const ActionsPanel = () => {
2496
- const { formatMessage } = useIntl();
2497
- return {
2498
- title: formatMessage({
2499
- id: "content-manager.containers.edit.panels.default.title",
2500
- defaultMessage: "Document"
2501
- }),
2502
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2503
- };
2504
- };
2505
- ActionsPanel.type = "actions";
2506
- const ActionsPanelContent = () => {
2507
- const isCloning = useMatch(CLONE_PATH) !== null;
2508
- const [
2509
- {
2510
- query: { status = "draft" }
2511
- }
2512
- ] = useQueryParams();
2513
- const { model, id, document, meta, collectionType } = useDoc();
2514
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2515
- const props = {
2516
- activeTab: status,
2517
- model,
2518
- documentId: id,
2519
- document: isCloning ? void 0 : document,
2520
- meta: isCloning ? void 0 : meta,
2521
- collectionType
2522
- };
2523
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2524
- /* @__PURE__ */ jsx(
2525
- DescriptionComponentRenderer,
2526
- {
2527
- props,
2528
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2529
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2530
- }
2531
- ),
2532
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2533
- ] });
2534
- };
2535
- const Panel = React.forwardRef(({ children, title }, ref) => {
2536
- return /* @__PURE__ */ jsxs(
2537
- Flex,
2538
- {
2539
- ref,
2540
- tag: "aside",
2541
- "aria-labelledby": "additional-information",
2542
- background: "neutral0",
2543
- borderColor: "neutral150",
2544
- hasRadius: true,
2545
- paddingBottom: 4,
2546
- paddingLeft: 4,
2547
- paddingRight: 4,
2548
- paddingTop: 4,
2549
- shadow: "tableShadow",
2550
- gap: 3,
2551
- direction: "column",
2552
- justifyContent: "stretch",
2553
- alignItems: "flex-start",
2554
- children: [
2555
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2556
- children
2557
- ]
2558
- }
2559
- );
2560
- });
2561
- const HOOKS = {
2562
- /**
2563
- * Hook that allows to mutate the displayed headers of the list view table
2564
- * @constant
2565
- * @type {string}
2566
- */
2567
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2568
- /**
2569
- * Hook that allows to mutate the CM's collection types links pre-set filters
2570
- * @constant
2571
- * @type {string}
2572
- */
2573
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2574
- /**
2575
- * Hook that allows to mutate the CM's edit view layout
2576
- * @constant
2577
- * @type {string}
2578
- */
2579
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2580
- /**
2581
- * Hook that allows to mutate the CM's single types links pre-set filters
2582
- * @constant
2583
- * @type {string}
2584
- */
2585
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2586
- };
2587
- const contentTypesApi = contentManagerApi.injectEndpoints({
2588
- endpoints: (builder) => ({
2589
- getContentTypeConfiguration: builder.query({
2590
- query: (uid) => ({
2591
- url: `/content-manager/content-types/${uid}/configuration`,
2592
- method: "GET"
2593
- }),
2594
- transformResponse: (response) => response.data,
2595
- providesTags: (_result, _error, uid) => [
2596
- { type: "ContentTypesConfiguration", id: uid },
2597
- { type: "ContentTypeSettings", id: "LIST" }
2598
- ]
2599
- }),
2600
- getAllContentTypeSettings: builder.query({
2601
- query: () => "/content-manager/content-types-settings",
2602
- transformResponse: (response) => response.data,
2603
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2604
- }),
2605
- updateContentTypeConfiguration: builder.mutation({
2606
- query: ({ uid, ...body }) => ({
2607
- url: `/content-manager/content-types/${uid}/configuration`,
2608
- method: "PUT",
2609
- data: body
2610
- }),
2611
- transformResponse: (response) => response.data,
2612
- invalidatesTags: (_result, _error, { uid }) => [
2613
- { type: "ContentTypesConfiguration", id: uid },
2614
- { type: "ContentTypeSettings", id: "LIST" },
2615
- // Is this necessary?
2616
- { type: "InitialData" }
2617
- ]
2618
- })
2619
- })
2620
- });
2621
- const {
2622
- useGetContentTypeConfigurationQuery,
2623
- useGetAllContentTypeSettingsQuery,
2624
- useUpdateContentTypeConfigurationMutation
2625
- } = contentTypesApi;
2626
- const checkIfAttributeIsDisplayable = (attribute) => {
2627
- const { type } = attribute;
2628
- if (type === "relation") {
2629
- return !attribute.relation.toLowerCase().includes("morph");
2630
- }
2631
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2632
- };
2633
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2634
- if (!mainFieldName) {
2635
- return void 0;
2636
- }
2637
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2638
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2639
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2640
- );
2641
- return {
2642
- name: mainFieldName,
2643
- type: mainFieldType ?? "string"
2644
- };
2645
- };
2646
- const DEFAULT_SETTINGS = {
2647
- bulkable: false,
2648
- filterable: false,
2649
- searchable: false,
2650
- pagination: false,
2651
- defaultSortBy: "",
2652
- defaultSortOrder: "asc",
2653
- mainField: "id",
2654
- pageSize: 10
2655
- };
2656
- const useDocumentLayout = (model) => {
2657
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2658
- const [{ query }] = useQueryParams();
2659
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2660
- const { toggleNotification } = useNotification();
2661
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2662
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2663
- const {
2664
- data,
2665
- isLoading: isLoadingConfigs,
2666
- error,
2667
- isFetching: isFetchingConfigs
2668
- } = useGetContentTypeConfigurationQuery(model);
2669
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2670
- React.useEffect(() => {
2671
- if (error) {
2672
- toggleNotification({
2673
- type: "danger",
2674
- message: formatAPIError(error)
2675
- });
2676
- }
2677
- }, [error, formatAPIError, toggleNotification]);
2678
- const editLayout = React.useMemo(
2679
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2680
- layout: [],
2681
- components: {},
2682
- metadatas: {},
2683
- options: {},
2684
- settings: DEFAULT_SETTINGS
2685
- },
2686
- [data, isLoading, schemas, schema, components]
2687
- );
2688
- const listLayout = React.useMemo(() => {
2689
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2690
- layout: [],
2691
- metadatas: {},
2692
- options: {},
2693
- settings: DEFAULT_SETTINGS
2694
- };
2695
- }, [data, isLoading, schemas, schema, components]);
2696
- const { layout: edit } = React.useMemo(
2697
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2698
- layout: editLayout,
2699
- query
2700
- }),
2701
- [editLayout, query, runHookWaterfall]
2702
- );
2703
- return {
2704
- error,
2705
- isLoading,
2706
- edit,
2707
- list: listLayout
2708
- };
2709
- };
2710
- const useDocLayout = () => {
2711
- const { model } = useDoc();
2712
- return useDocumentLayout(model);
2713
- };
2714
- const formatEditLayout = (data, {
2715
- schemas,
2716
- schema,
2717
- components
2718
- }) => {
2719
- let currentPanelIndex = 0;
2720
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2721
- data.contentType.layouts.edit,
2722
- schema?.attributes,
2723
- data.contentType.metadatas,
2724
- { configurations: data.components, schemas: components },
2725
- schemas
2726
- ).reduce((panels, row) => {
2727
- if (row.some((field) => field.type === "dynamiczone")) {
2728
- panels.push([row]);
2729
- currentPanelIndex += 2;
2730
- } else {
2731
- if (!panels[currentPanelIndex]) {
2732
- panels.push([]);
2733
- }
2734
- panels[currentPanelIndex].push(row);
2735
- }
2736
- return panels;
2737
- }, []);
2738
- const componentEditAttributes = Object.entries(data.components).reduce(
2739
- (acc, [uid, configuration]) => {
2740
- acc[uid] = {
2741
- layout: convertEditLayoutToFieldLayouts(
2742
- configuration.layouts.edit,
2743
- components[uid].attributes,
2744
- configuration.metadatas
2745
- ),
2746
- settings: {
2747
- ...configuration.settings,
2748
- icon: components[uid].info.icon,
2749
- displayName: components[uid].info.displayName
2980
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2981
+ }
2982
+ } finally {
2983
+ if (!listViewPathMatch) {
2984
+ setSubmitting(false);
2985
+ }
2750
2986
  }
2751
- };
2752
- return acc;
2753
- },
2754
- {}
2755
- );
2756
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2757
- (acc, [attribute, metadata]) => {
2758
- return {
2759
- ...acc,
2760
- [attribute]: metadata.edit
2761
- };
2762
- },
2763
- {}
2764
- );
2765
- return {
2766
- layout: panelledEditAttributes,
2767
- components: componentEditAttributes,
2768
- metadatas: editMetadatas,
2769
- settings: {
2770
- ...data.contentType.settings,
2771
- displayName: schema?.info.displayName
2987
+ }
2772
2988
  },
2773
- options: {
2774
- ...schema?.options,
2775
- ...schema?.pluginOptions,
2776
- ...data.contentType.options
2777
- }
2989
+ variant: "danger",
2990
+ position: ["header", "table-row"]
2778
2991
  };
2779
2992
  };
2780
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2781
- return rows.map(
2782
- (row) => row.map((field) => {
2783
- const attribute = attributes[field.name];
2784
- if (!attribute) {
2785
- return null;
2786
- }
2787
- const { edit: metadata } = metadatas[field.name];
2788
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2789
- return {
2790
- attribute,
2791
- disabled: !metadata.editable,
2792
- hint: metadata.description,
2793
- label: metadata.label ?? "",
2794
- name: field.name,
2795
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2796
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2797
- schemas,
2798
- components: components?.schemas ?? {}
2799
- }),
2800
- placeholder: metadata.placeholder ?? "",
2801
- required: attribute.required ?? false,
2802
- size: field.size,
2803
- unique: "unique" in attribute ? attribute.unique : false,
2804
- visible: metadata.visible ?? true,
2805
- type: attribute.type
2806
- };
2807
- }).filter((field) => field !== null)
2808
- );
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
+ ) });
2809
3023
  };
2810
- const formatListLayout = (data, {
2811
- schemas,
2812
- schema,
2813
- components
2814
- }) => {
2815
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2816
- (acc, [attribute, metadata]) => {
2817
- return {
2818
- ...acc,
2819
- [attribute]: metadata.list
2820
- };
2821
- },
2822
- {}
2823
- );
2824
- const listAttributes = convertListLayoutToFieldLayouts(
2825
- data.contentType.layouts.list,
2826
- schema?.attributes,
2827
- listMetadatas,
2828
- { configurations: data.components, schemas: components },
2829
- schemas
2830
- );
3024
+ const ActionsPanel = () => {
3025
+ const { formatMessage } = useIntl();
2831
3026
  return {
2832
- layout: listAttributes,
2833
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2834
- metadatas: listMetadatas,
2835
- options: {
2836
- ...schema?.options,
2837
- ...schema?.pluginOptions,
2838
- ...data.contentType.options
2839
- }
3027
+ title: formatMessage({
3028
+ id: "content-manager.containers.edit.panels.default.title",
3029
+ defaultMessage: "Entry"
3030
+ }),
3031
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2840
3032
  };
2841
3033
  };
2842
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2843
- return columns.map((name) => {
2844
- const attribute = attributes[name];
2845
- if (!attribute) {
2846
- return null;
3034
+ ActionsPanel.type = "actions";
3035
+ const ActionsPanelContent = () => {
3036
+ const isCloning = useMatch(CLONE_PATH) !== null;
3037
+ const [
3038
+ {
3039
+ query: { status = "draft" }
2847
3040
  }
2848
- const metadata = metadatas[name];
2849
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2850
- return {
2851
- attribute,
2852
- label: metadata.label ?? "",
2853
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2854
- schemas,
2855
- components: components?.schemas ?? {}
2856
- }),
2857
- name,
2858
- searchable: metadata.searchable ?? true,
2859
- sortable: metadata.sortable ?? true
2860
- };
2861
- }).filter((field) => field !== null);
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
+ ] });
2862
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
+ });
2863
3090
  const ConfirmBulkActionDialog = ({
2864
3091
  onToggleDialog,
2865
3092
  isOpen = false,
@@ -2898,6 +3125,7 @@ const ConfirmDialogPublishAll = ({
2898
3125
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2899
3126
  const { model, schema } = useDoc();
2900
3127
  const [{ query }] = useQueryParams();
3128
+ const enableDraftRelationsCount = false;
2901
3129
  const {
2902
3130
  data: countDraftRelations = 0,
2903
3131
  isLoading,
@@ -2909,7 +3137,7 @@ const ConfirmDialogPublishAll = ({
2909
3137
  locale: query?.plugins?.i18n?.locale
2910
3138
  },
2911
3139
  {
2912
- skip: selectedEntries.length === 0
3140
+ skip: !enableDraftRelationsCount
2913
3141
  }
2914
3142
  );
2915
3143
  React.useEffect(() => {
@@ -3094,7 +3322,7 @@ const SelectedEntriesTableContent = ({
3094
3322
  status: row.status
3095
3323
  }
3096
3324
  ) }),
3097
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3325
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3098
3326
  IconButton,
3099
3327
  {
3100
3328
  tag: Link,
@@ -3103,23 +3331,16 @@ const SelectedEntriesTableContent = ({
3103
3331
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3104
3332
  },
3105
3333
  state: { from: pathname },
3106
- label: formatMessage(
3107
- { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3108
- {
3109
- target: formatMessage(
3110
- {
3111
- id: "content-manager.components.ListViewHelperPluginTable.row-line",
3112
- defaultMessage: "item line {number}"
3113
- },
3114
- { number: index2 + 1 }
3115
- )
3116
- }
3117
- ),
3334
+ label: formatMessage({
3335
+ id: "content-manager.bulk-publish.edit",
3336
+ defaultMessage: "Edit"
3337
+ }),
3118
3338
  target: "_blank",
3119
3339
  marginLeft: "auto",
3120
- children: /* @__PURE__ */ jsx(Pencil, {})
3340
+ variant: "ghost",
3341
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3121
3342
  }
3122
- ) })
3343
+ ) }) })
3123
3344
  ] }, row.id)) })
3124
3345
  ] });
3125
3346
  };
@@ -3156,7 +3377,13 @@ const SelectedEntriesModalContent = ({
3156
3377
  );
3157
3378
  const { rows, validationErrors } = React.useMemo(() => {
3158
3379
  if (data.length > 0 && schema) {
3159
- 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
+ );
3160
3387
  const validationErrors2 = {};
3161
3388
  const rows2 = data.map((entry) => {
3162
3389
  try {
@@ -3281,8 +3508,7 @@ const PublishAction = ({ documents, model }) => {
3281
3508
  const refetchList = () => {
3282
3509
  contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3283
3510
  };
3284
- if (!showPublishButton)
3285
- return null;
3511
+ if (!showPublishButton) return null;
3286
3512
  return {
3287
3513
  actionType: "publish",
3288
3514
  variant: "tertiary",
@@ -3350,8 +3576,7 @@ const DeleteAction = ({ documents, model }) => {
3350
3576
  selectRow([]);
3351
3577
  }
3352
3578
  };
3353
- if (!hasDeletePermission)
3354
- return null;
3579
+ if (!hasDeletePermission) return null;
3355
3580
  return {
3356
3581
  variant: "danger-light",
3357
3582
  label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
@@ -3400,8 +3625,7 @@ const UnpublishAction = ({ documents, model }) => {
3400
3625
  }
3401
3626
  };
3402
3627
  const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3403
- if (!showUnpublishButton)
3404
- return null;
3628
+ if (!showUnpublishButton) return null;
3405
3629
  return {
3406
3630
  variant: "tertiary",
3407
3631
  label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
@@ -3506,7 +3730,7 @@ const TableActions = ({ document }) => {
3506
3730
  DescriptionComponentRenderer,
3507
3731
  {
3508
3732
  props,
3509
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3733
+ descriptions: plugins["content-manager"].apis.getDocumentActions("table-row").filter((action) => action.name !== "PublishAction"),
3510
3734
  children: (actions2) => {
3511
3735
  const tableRowActions = actions2.filter((action) => {
3512
3736
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3565,6 +3789,7 @@ const EditAction = ({ documentId }) => {
3565
3789
  };
3566
3790
  };
3567
3791
  EditAction.type = "edit";
3792
+ EditAction.position = "table-row";
3568
3793
  const StyledPencil = styled(Pencil)`
3569
3794
  path {
3570
3795
  fill: currentColor;
@@ -3617,7 +3842,7 @@ const CloneAction = ({ model, documentId }) => {
3617
3842
  }),
3618
3843
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3619
3844
  footer: ({ onClose }) => {
3620
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3845
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3621
3846
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3622
3847
  id: "cancel",
3623
3848
  defaultMessage: "Cancel"
@@ -3641,6 +3866,7 @@ const CloneAction = ({ model, documentId }) => {
3641
3866
  };
3642
3867
  };
3643
3868
  CloneAction.type = "clone";
3869
+ CloneAction.position = "table-row";
3644
3870
  const StyledDuplicate = styled(Duplicate)`
3645
3871
  path {
3646
3872
  fill: currentColor;
@@ -3727,7 +3953,14 @@ class ContentManagerPlugin {
3727
3953
  addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3728
3954
  addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3729
3955
  getBulkActions: () => this.bulkActions,
3730
- 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
+ },
3731
3964
  getEditViewSidePanels: () => this.editViewSidePanels,
3732
3965
  getHeaderActions: () => this.headerActions
3733
3966
  }
@@ -3737,10 +3970,8 @@ class ContentManagerPlugin {
3737
3970
  const getPrintableType = (value) => {
3738
3971
  const nativeType = typeof value;
3739
3972
  if (nativeType === "object") {
3740
- if (value === null)
3741
- return "null";
3742
- if (Array.isArray(value))
3743
- return "array";
3973
+ if (value === null) return "null";
3974
+ if (Array.isArray(value)) return "array";
3744
3975
  if (value instanceof Object && value.constructor.name !== "Object") {
3745
3976
  return value.constructor.name;
3746
3977
  }
@@ -3751,17 +3982,27 @@ const HistoryAction = ({ model, document }) => {
3751
3982
  const { formatMessage } = useIntl();
3752
3983
  const [{ query }] = useQueryParams();
3753
3984
  const navigate = useNavigate();
3985
+ const { trackUsage } = useTracking();
3986
+ const { pathname } = useLocation();
3754
3987
  const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
3755
3988
  if (!window.strapi.features.isEnabled("cms-content-history")) {
3756
3989
  return null;
3757
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
+ };
3758
3999
  return {
3759
4000
  icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
3760
4001
  label: formatMessage({
3761
4002
  id: "content-manager.history.document-action",
3762
4003
  defaultMessage: "Content History"
3763
4004
  }),
3764
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
4005
+ onClick: handleOnClick,
3765
4006
  disabled: (
3766
4007
  /**
3767
4008
  * The user is creating a new document.
@@ -3783,6 +4024,7 @@ const HistoryAction = ({ model, document }) => {
3783
4024
  };
3784
4025
  };
3785
4026
  HistoryAction.type = "history";
4027
+ HistoryAction.position = "header";
3786
4028
  const historyAdmin = {
3787
4029
  bootstrap(app) {
3788
4030
  const { addDocumentAction } = app.getPlugin("content-manager").apis;
@@ -3829,6 +4071,88 @@ const { setInitialData } = actions;
3829
4071
  const reducer = combineReducers({
3830
4072
  app: reducer$1
3831
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
+ };
3832
4156
  const index = {
3833
4157
  register(app) {
3834
4158
  const cm = new ContentManagerPlugin();
@@ -3848,7 +4172,7 @@ const index = {
3848
4172
  app.router.addRoute({
3849
4173
  path: "content-manager/*",
3850
4174
  lazy: async () => {
3851
- const { Layout } = await import("./layout-oPBiO7RY.mjs");
4175
+ const { Layout } = await import("./layout-DY_D9MGA.mjs");
3852
4176
  return {
3853
4177
  Component: Layout
3854
4178
  };
@@ -3861,11 +4185,14 @@ const index = {
3861
4185
  if (typeof historyAdmin.bootstrap === "function") {
3862
4186
  historyAdmin.bootstrap(app);
3863
4187
  }
4188
+ if (typeof previewAdmin.bootstrap === "function") {
4189
+ previewAdmin.bootstrap(app);
4190
+ }
3864
4191
  },
3865
4192
  async registerTrads({ locales }) {
3866
4193
  const importedTrads = await Promise.all(
3867
4194
  locales.map((locale) => {
3868
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-BrCTWlZv.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
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 }) => {
3869
4196
  return {
3870
4197
  data: prefixPluginTranslations(data, PLUGIN_ID),
3871
4198
  locale
@@ -3886,13 +4213,16 @@ export {
3886
4213
  BulkActionsRenderer as B,
3887
4214
  COLLECTION_TYPES as C,
3888
4215
  DocumentStatus as D,
3889
- DEFAULT_SETTINGS as E,
3890
- convertEditLayoutToFieldLayouts as F,
3891
- useDocument as G,
4216
+ extractContentTypeComponents as E,
4217
+ DEFAULT_SETTINGS as F,
4218
+ convertEditLayoutToFieldLayouts as G,
3892
4219
  HOOKS as H,
3893
4220
  InjectionZone as I,
3894
- index as J,
3895
- useDocumentActions as K,
4221
+ useDocument as J,
4222
+ useGetPreviewUrlQuery as K,
4223
+ index as L,
4224
+ useContentManagerContext as M,
4225
+ useDocumentActions as N,
3896
4226
  Panels as P,
3897
4227
  RelativeTime as R,
3898
4228
  SINGLE_TYPES as S,
@@ -3910,18 +4240,18 @@ export {
3910
4240
  PERMISSIONS as k,
3911
4241
  DocumentRBAC as l,
3912
4242
  DOCUMENT_META_FIELDS as m,
3913
- useDocLayout as n,
3914
- useGetContentTypeConfigurationQuery as o,
3915
- CREATOR_FIELDS as p,
3916
- getMainField as q,
3917
- getDisplayName as r,
4243
+ CLONE_PATH as n,
4244
+ useDocLayout as o,
4245
+ useGetContentTypeConfigurationQuery as p,
4246
+ CREATOR_FIELDS as q,
4247
+ getMainField as r,
3918
4248
  setInitialData as s,
3919
- checkIfAttributeIsDisplayable as t,
4249
+ getDisplayName as t,
3920
4250
  useContentTypeSchema as u,
3921
- useGetAllDocumentsQuery as v,
3922
- convertListLayoutToFieldLayouts as w,
3923
- capitalise as x,
3924
- useUpdateContentTypeConfigurationMutation as y,
3925
- extractContentTypeComponents as z
4251
+ checkIfAttributeIsDisplayable as v,
4252
+ useGetAllDocumentsQuery as w,
4253
+ convertListLayoutToFieldLayouts as x,
4254
+ capitalise as y,
4255
+ useUpdateContentTypeConfigurationMutation as z
3926
4256
  };
3927
- //# sourceMappingURL=index-c_5DdJi-.mjs.map
4257
+ //# sourceMappingURL=index-C2SagWVW.mjs.map