@strapi/content-manager 0.0.0-experimental.545ccead2ee1717bbc7ab950455dbb0ddb9924a3 → 0.0.0-experimental.550e739e36d7678bd8317b48acab2f24b1f4dcd6

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 (216) 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-Bqgx7Mes.js → ComponentConfigurationPage-CJPoOvy3.js} +5 -6
  4. package/dist/_chunks/{ComponentConfigurationPage-Bqgx7Mes.js.map → ComponentConfigurationPage-CJPoOvy3.js.map} +1 -1
  5. package/dist/_chunks/{ComponentConfigurationPage-B1bIXVuX.mjs → ComponentConfigurationPage-CcRDqD0e.mjs} +4 -4
  6. package/dist/_chunks/{ComponentConfigurationPage-B1bIXVuX.mjs.map → ComponentConfigurationPage-CcRDqD0e.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-ZO0vOO8q.mjs → EditConfigurationPage-C1ddZ_zf.mjs} +4 -4
  11. package/dist/_chunks/{EditConfigurationPage-ZO0vOO8q.mjs.map → EditConfigurationPage-C1ddZ_zf.mjs.map} +1 -1
  12. package/dist/_chunks/{EditConfigurationPage-BFEwvdMW.js → EditConfigurationPage-CF3lxOy2.js} +5 -6
  13. package/dist/_chunks/{EditConfigurationPage-BFEwvdMW.js.map → EditConfigurationPage-CF3lxOy2.js.map} +1 -1
  14. package/dist/_chunks/{EditViewPage-DlLEyUL6.mjs → EditViewPage-BPFcUbqi.mjs} +63 -12
  15. package/dist/_chunks/EditViewPage-BPFcUbqi.mjs.map +1 -0
  16. package/dist/_chunks/{EditViewPage-DA95Ha6J.js → EditViewPage-CDyTC6aU.js} +63 -13
  17. package/dist/_chunks/EditViewPage-CDyTC6aU.js.map +1 -0
  18. package/dist/_chunks/{Field-CnK8dO8N.js → Field-DuxAW9q2.js} +344 -220
  19. package/dist/_chunks/Field-DuxAW9q2.js.map +1 -0
  20. package/dist/_chunks/{Field-Dq7bDnuh.mjs → Field-fBnTwgU4.mjs} +340 -216
  21. package/dist/_chunks/Field-fBnTwgU4.mjs.map +1 -0
  22. package/dist/_chunks/FieldTypeIcon-CMlNO8PE.mjs.map +1 -1
  23. package/dist/_chunks/FieldTypeIcon-Dnwq_IRF.js.map +1 -1
  24. package/dist/_chunks/{Form-B_JE0dbz.mjs → Form-BGl7PhlZ.mjs} +37 -18
  25. package/dist/_chunks/Form-BGl7PhlZ.mjs.map +1 -0
  26. package/dist/_chunks/{Form-BpiR4piS.js → Form-DSGh_zkz.js} +39 -21
  27. package/dist/_chunks/Form-DSGh_zkz.js.map +1 -0
  28. package/dist/_chunks/{History-CBNGU7a-.mjs → History-DTYB9CSB.mjs} +59 -106
  29. package/dist/_chunks/History-DTYB9CSB.mjs.map +1 -0
  30. package/dist/_chunks/{History-DdIstl8b.js → History-DrDJv698.js} +58 -106
  31. package/dist/_chunks/History-DrDJv698.js.map +1 -0
  32. package/dist/_chunks/{ListConfigurationPage-5dr4qpue.mjs → ListConfigurationPage-qWx8r4D_.mjs} +19 -8
  33. package/dist/_chunks/ListConfigurationPage-qWx8r4D_.mjs.map +1 -0
  34. package/dist/_chunks/{ListConfigurationPage-DkKRparB.js → ListConfigurationPage-zurIlUZ7.js} +19 -9
  35. package/dist/_chunks/ListConfigurationPage-zurIlUZ7.js.map +1 -0
  36. package/dist/_chunks/{ListViewPage-wE0lXqoD.js → ListViewPage-DTM2uO_S.js} +109 -78
  37. package/dist/_chunks/ListViewPage-DTM2uO_S.js.map +1 -0
  38. package/dist/_chunks/{ListViewPage-DecLrYV6.mjs → ListViewPage-GKpL5p8A.mjs} +106 -74
  39. package/dist/_chunks/ListViewPage-GKpL5p8A.mjs.map +1 -0
  40. package/dist/_chunks/{NoContentTypePage-CiIcfYsd.mjs → NoContentTypePage-B5Vc5Cal.mjs} +2 -2
  41. package/dist/_chunks/{NoContentTypePage-CiIcfYsd.mjs.map → NoContentTypePage-B5Vc5Cal.mjs.map} +1 -1
  42. package/dist/_chunks/{NoContentTypePage-DEKR6tf9.js → NoContentTypePage-BuZlNroO.js} +2 -2
  43. package/dist/_chunks/{NoContentTypePage-DEKR6tf9.js.map → NoContentTypePage-BuZlNroO.js.map} +1 -1
  44. package/dist/_chunks/{NoPermissionsPage-CM5UD8ee.mjs → NoPermissionsPage-BAZlWgJ4.mjs} +2 -2
  45. package/dist/_chunks/{NoPermissionsPage-CM5UD8ee.mjs.map → NoPermissionsPage-BAZlWgJ4.mjs.map} +1 -1
  46. package/dist/_chunks/{NoPermissionsPage-DmNfF2Bb.js → NoPermissionsPage-DLzkS4Hy.js} +2 -2
  47. package/dist/_chunks/{NoPermissionsPage-DmNfF2Bb.js.map → NoPermissionsPage-DLzkS4Hy.js.map} +1 -1
  48. package/dist/_chunks/Preview-VOJ8RuQp.js +312 -0
  49. package/dist/_chunks/Preview-VOJ8RuQp.js.map +1 -0
  50. package/dist/_chunks/Preview-Zzjg2_K_.mjs +294 -0
  51. package/dist/_chunks/Preview-Zzjg2_K_.mjs.map +1 -0
  52. package/dist/_chunks/{Relations-Dqz0C1fz.mjs → Relations-BVdRfDkW.mjs} +76 -42
  53. package/dist/_chunks/Relations-BVdRfDkW.mjs.map +1 -0
  54. package/dist/_chunks/{Relations-L0xYRoSQ.js → Relations-Dsj0boFJ.js} +76 -43
  55. package/dist/_chunks/Relations-Dsj0boFJ.js.map +1 -0
  56. package/dist/_chunks/{en-uOUIxfcQ.js → en-BzQmavmK.js} +33 -15
  57. package/dist/_chunks/{en-uOUIxfcQ.js.map → en-BzQmavmK.js.map} +1 -1
  58. package/dist/_chunks/{en-BrCTWlZv.mjs → en-CSxLmrh1.mjs} +33 -15
  59. package/dist/_chunks/{en-BrCTWlZv.mjs.map → en-CSxLmrh1.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-BSn97i8U.mjs → index-Bu_-B7ZA.mjs} +1143 -767
  70. package/dist/_chunks/index-Bu_-B7ZA.mjs.map +1 -0
  71. package/dist/_chunks/{index-DyvUPg1a.js → index-Ct-GZ0iV.js} +1125 -749
  72. package/dist/_chunks/index-Ct-GZ0iV.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-TPqF2oJ5.js → layout-CDBEgRsM.js} +23 -11
  78. package/dist/_chunks/layout-CDBEgRsM.js.map +1 -0
  79. package/dist/_chunks/{layout-DPaHUusj.mjs → layout-COzAvgJh.mjs} +23 -10
  80. package/dist/_chunks/layout-COzAvgJh.mjs.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-BWYS9gkn.js → relations-BjiF1Aad.js} +6 -7
  86. package/dist/_chunks/relations-BjiF1Aad.js.map +1 -0
  87. package/dist/_chunks/{relations-Ck7-ecDT.mjs → relations-BtmMFBpM.mjs} +6 -7
  88. package/dist/_chunks/relations-BtmMFBpM.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 +3 -1
  98. package/dist/admin/index.js.map +1 -1
  99. package/dist/admin/index.mjs +6 -4
  100. package/dist/admin/src/content-manager.d.ts +3 -2
  101. package/dist/admin/src/exports.d.ts +2 -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 +3 -3
  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 +682 -360
  131. package/dist/server/index.js.map +1 -1
  132. package/dist/server/index.mjs +683 -360
  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/uid.d.ts.map +1 -1
  139. package/dist/server/src/controllers/utils/metadata.d.ts +16 -1
  140. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  141. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  142. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  143. package/dist/server/src/history/services/history.d.ts.map +1 -1
  144. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  145. package/dist/server/src/history/services/utils.d.ts +4 -4
  146. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  147. package/dist/server/src/index.d.ts +7 -6
  148. package/dist/server/src/index.d.ts.map +1 -1
  149. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  150. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  151. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  152. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  153. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  154. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  155. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  156. package/dist/server/src/preview/index.d.ts +4 -0
  157. package/dist/server/src/preview/index.d.ts.map +1 -0
  158. package/dist/server/src/preview/routes/index.d.ts +8 -0
  159. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  160. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  161. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  162. package/dist/server/src/preview/services/index.d.ts +16 -0
  163. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  164. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  165. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  166. package/dist/server/src/preview/services/preview.d.ts +12 -0
  167. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  168. package/dist/server/src/preview/utils.d.ts +19 -0
  169. package/dist/server/src/preview/utils.d.ts.map +1 -0
  170. package/dist/server/src/register.d.ts.map +1 -1
  171. package/dist/server/src/routes/index.d.ts.map +1 -1
  172. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  173. package/dist/server/src/services/document-metadata.d.ts +12 -10
  174. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  175. package/dist/server/src/services/index.d.ts +7 -6
  176. package/dist/server/src/services/index.d.ts.map +1 -1
  177. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  178. package/dist/server/src/services/utils/populate.d.ts +2 -2
  179. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  180. package/dist/server/src/utils/index.d.ts +2 -0
  181. package/dist/server/src/utils/index.d.ts.map +1 -1
  182. package/dist/shared/contracts/collection-types.d.ts +3 -1
  183. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  184. package/dist/shared/contracts/index.d.ts +1 -0
  185. package/dist/shared/contracts/index.d.ts.map +1 -1
  186. package/dist/shared/contracts/preview.d.ts +27 -0
  187. package/dist/shared/contracts/preview.d.ts.map +1 -0
  188. package/dist/shared/index.js +4 -0
  189. package/dist/shared/index.js.map +1 -1
  190. package/dist/shared/index.mjs +4 -0
  191. package/dist/shared/index.mjs.map +1 -1
  192. package/package.json +17 -15
  193. package/dist/_chunks/EditViewPage-DA95Ha6J.js.map +0 -1
  194. package/dist/_chunks/EditViewPage-DlLEyUL6.mjs.map +0 -1
  195. package/dist/_chunks/Field-CnK8dO8N.js.map +0 -1
  196. package/dist/_chunks/Field-Dq7bDnuh.mjs.map +0 -1
  197. package/dist/_chunks/Form-B_JE0dbz.mjs.map +0 -1
  198. package/dist/_chunks/Form-BpiR4piS.js.map +0 -1
  199. package/dist/_chunks/History-CBNGU7a-.mjs.map +0 -1
  200. package/dist/_chunks/History-DdIstl8b.js.map +0 -1
  201. package/dist/_chunks/ListConfigurationPage-5dr4qpue.mjs.map +0 -1
  202. package/dist/_chunks/ListConfigurationPage-DkKRparB.js.map +0 -1
  203. package/dist/_chunks/ListViewPage-DecLrYV6.mjs.map +0 -1
  204. package/dist/_chunks/ListViewPage-wE0lXqoD.js.map +0 -1
  205. package/dist/_chunks/Relations-Dqz0C1fz.mjs.map +0 -1
  206. package/dist/_chunks/Relations-L0xYRoSQ.js.map +0 -1
  207. package/dist/_chunks/index-BSn97i8U.mjs.map +0 -1
  208. package/dist/_chunks/index-DyvUPg1a.js.map +0 -1
  209. package/dist/_chunks/layout-DPaHUusj.mjs.map +0 -1
  210. package/dist/_chunks/layout-TPqF2oJ5.js.map +0 -1
  211. package/dist/_chunks/relations-BWYS9gkn.js.map +0 -1
  212. package/dist/_chunks/relations-Ck7-ecDT.mjs.map +0 -1
  213. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  214. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  215. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  216. 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,9 @@ const contentManagerApi = adminApi.enhanceEndpoints({
158
174
  "Document",
159
175
  "InitialData",
160
176
  "HistoryVersion",
161
- "Relations"
177
+ "Relations",
178
+ "UidAvailability",
179
+ "RecentDocumentList"
162
180
  ]
163
181
  });
164
182
  const documentApi = contentManagerApi.injectEndpoints({
@@ -172,7 +190,12 @@ const documentApi = contentManagerApi.injectEndpoints({
172
190
  params: query
173
191
  }
174
192
  }),
175
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
193
+ invalidatesTags: (_result, error, { model }) => {
194
+ if (error) {
195
+ return [];
196
+ }
197
+ return [{ type: "Document", id: `${model}_LIST` }, "RecentDocumentList"];
198
+ }
176
199
  }),
177
200
  cloneDocument: builder.mutation({
178
201
  query: ({ model, sourceId, data, params }) => ({
@@ -183,7 +206,11 @@ const documentApi = contentManagerApi.injectEndpoints({
183
206
  params
184
207
  }
185
208
  }),
186
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
209
+ invalidatesTags: (_result, _error, { model }) => [
210
+ { type: "Document", id: `${model}_LIST` },
211
+ { type: "UidAvailability", id: model },
212
+ "RecentDocumentList"
213
+ ]
187
214
  }),
188
215
  /**
189
216
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -200,8 +227,22 @@ const documentApi = contentManagerApi.injectEndpoints({
200
227
  }),
201
228
  invalidatesTags: (result, _error, { model }) => [
202
229
  { type: "Document", id: `${model}_LIST` },
203
- "Relations"
204
- ]
230
+ "Relations",
231
+ { type: "UidAvailability", id: model },
232
+ "RecentDocumentList"
233
+ ],
234
+ transformResponse: (response, meta, arg) => {
235
+ if (!("data" in response) && arg.model === "plugin::users-permissions.user") {
236
+ return {
237
+ data: response,
238
+ meta: {
239
+ availableStatus: [],
240
+ availableLocales: []
241
+ }
242
+ };
243
+ }
244
+ return response;
245
+ }
205
246
  }),
206
247
  deleteDocument: builder.mutation({
207
248
  query: ({ collectionType, model, documentId, params }) => ({
@@ -212,7 +253,8 @@ const documentApi = contentManagerApi.injectEndpoints({
212
253
  }
213
254
  }),
214
255
  invalidatesTags: (_result, _error, { collectionType, model }) => [
215
- { type: "Document", id: collectionType !== SINGLE_TYPES ? `${model}_LIST` : model }
256
+ { type: "Document", id: collectionType !== SINGLE_TYPES ? `${model}_LIST` : model },
257
+ "RecentDocumentList"
216
258
  ]
217
259
  }),
218
260
  deleteManyDocuments: builder.mutation({
@@ -224,7 +266,10 @@ const documentApi = contentManagerApi.injectEndpoints({
224
266
  params
225
267
  }
226
268
  }),
227
- invalidatesTags: (_res, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
269
+ invalidatesTags: (_res, _error, { model }) => [
270
+ { type: "Document", id: `${model}_LIST` },
271
+ "RecentDocumentList"
272
+ ]
228
273
  }),
229
274
  discardDocument: builder.mutation({
230
275
  query: ({ collectionType, model, documentId, params }) => ({
@@ -241,7 +286,9 @@ const documentApi = contentManagerApi.injectEndpoints({
241
286
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
242
287
  },
243
288
  { type: "Document", id: `${model}_LIST` },
244
- "Relations"
289
+ "Relations",
290
+ { type: "UidAvailability", id: model },
291
+ "RecentDocumentList"
245
292
  ];
246
293
  }
247
294
  }),
@@ -254,11 +301,12 @@ const documentApi = contentManagerApi.injectEndpoints({
254
301
  url: `/content-manager/collection-types/${model}`,
255
302
  method: "GET",
256
303
  config: {
257
- params
304
+ params: stringify(params, { encode: true })
258
305
  }
259
306
  }),
260
307
  providesTags: (result, _error, arg) => {
261
308
  return [
309
+ { type: "Document", id: `ALL_LIST` },
262
310
  { type: "Document", id: `${arg.model}_LIST` },
263
311
  ...result?.results.map(({ documentId }) => ({
264
312
  type: "Document",
@@ -297,6 +345,11 @@ const documentApi = contentManagerApi.injectEndpoints({
297
345
  {
298
346
  type: "Document",
299
347
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
348
+ },
349
+ // Make it easy to invalidate all individual documents queries for a model
350
+ {
351
+ type: "Document",
352
+ id: `${model}_ALL_ITEMS`
300
353
  }
301
354
  ];
302
355
  }
@@ -330,7 +383,8 @@ const documentApi = contentManagerApi.injectEndpoints({
330
383
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
331
384
  },
332
385
  { type: "Document", id: `${model}_LIST` },
333
- "Relations"
386
+ "Relations",
387
+ "RecentDocumentList"
334
388
  ];
335
389
  }
336
390
  }),
@@ -360,8 +414,23 @@ const documentApi = contentManagerApi.injectEndpoints({
360
414
  type: "Document",
361
415
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
362
416
  },
363
- "Relations"
417
+ "Relations",
418
+ { type: "UidAvailability", id: model },
419
+ "RecentDocumentList",
420
+ "RecentDocumentList"
364
421
  ];
422
+ },
423
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
424
+ const patchResult = dispatch(
425
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
426
+ Object.assign(draft.data, data);
427
+ })
428
+ );
429
+ try {
430
+ await queryFulfilled;
431
+ } catch {
432
+ patchResult.undo();
433
+ }
365
434
  }
366
435
  }),
367
436
  unpublishDocument: builder.mutation({
@@ -378,7 +447,8 @@ const documentApi = contentManagerApi.injectEndpoints({
378
447
  {
379
448
  type: "Document",
380
449
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
381
- }
450
+ },
451
+ "RecentDocumentList"
382
452
  ];
383
453
  }
384
454
  }),
@@ -391,7 +461,10 @@ const documentApi = contentManagerApi.injectEndpoints({
391
461
  params
392
462
  }
393
463
  }),
394
- invalidatesTags: (_res, _error, { model, documentIds }) => documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` }))
464
+ invalidatesTags: (_res, _error, { model, documentIds }) => [
465
+ ...documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` })),
466
+ "RecentDocumentList"
467
+ ]
395
468
  })
396
469
  })
397
470
  });
@@ -414,8 +487,7 @@ const {
414
487
  useUnpublishManyDocumentsMutation
415
488
  } = documentApi;
416
489
  const buildValidParams = (query) => {
417
- if (!query)
418
- return query;
490
+ if (!query) return query;
419
491
  const { plugins: _, ...validQueryParams } = {
420
492
  ...query,
421
493
  ...Object.values(query?.plugins ?? {}).reduce(
@@ -423,28 +495,44 @@ const buildValidParams = (query) => {
423
495
  {}
424
496
  )
425
497
  };
426
- if ("_q" in validQueryParams) {
427
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
428
- }
429
498
  return validQueryParams;
430
499
  };
431
500
  const isBaseQueryError = (error) => {
432
501
  return error.name !== void 0;
433
502
  };
434
- const createYupSchema = (attributes = {}, components = {}) => {
503
+ const arrayValidator = (attribute, options) => ({
504
+ message: translatedErrors.required,
505
+ test(value) {
506
+ if (options.status === "draft") {
507
+ return true;
508
+ }
509
+ if (!attribute.required) {
510
+ return true;
511
+ }
512
+ if (!value) {
513
+ return false;
514
+ }
515
+ if (Array.isArray(value) && value.length === 0) {
516
+ return false;
517
+ }
518
+ return true;
519
+ }
520
+ });
521
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
435
522
  const createModelSchema = (attributes2) => yup.object().shape(
436
523
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
437
524
  if (DOCUMENT_META_FIELDS.includes(name)) {
438
525
  return acc;
439
526
  }
440
527
  const validations = [
528
+ addNullableValidation,
441
529
  addRequiredValidation,
442
530
  addMinLengthValidation,
443
531
  addMaxLengthValidation,
444
532
  addMinValidation,
445
533
  addMaxValidation,
446
534
  addRegexValidation
447
- ].map((fn) => fn(attribute));
535
+ ].map((fn) => fn(attribute, options));
448
536
  const transformSchema = pipe(...validations);
449
537
  switch (attribute.type) {
450
538
  case "component": {
@@ -454,12 +542,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
454
542
  ...acc,
455
543
  [name]: transformSchema(
456
544
  yup.array().of(createModelSchema(attributes3).nullable(false))
457
- )
545
+ ).test(arrayValidator(attribute, options))
458
546
  };
459
547
  } else {
460
548
  return {
461
549
  ...acc,
462
- [name]: transformSchema(createModelSchema(attributes3))
550
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
463
551
  };
464
552
  }
465
553
  }
@@ -481,7 +569,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
481
569
  }
482
570
  )
483
571
  )
484
- )
572
+ ).test(arrayValidator(attribute, options))
485
573
  };
486
574
  case "relation":
487
575
  return {
@@ -493,7 +581,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
493
581
  } else if (Array.isArray(value)) {
494
582
  return yup.array().of(
495
583
  yup.object().shape({
496
- id: yup.string().required()
584
+ id: yup.number().required()
497
585
  })
498
586
  );
499
587
  } else if (typeof value === "object") {
@@ -545,6 +633,14 @@ const createAttributeSchema = (attribute) => {
545
633
  if (!value || typeof value === "string" && value.length === 0) {
546
634
  return true;
547
635
  }
636
+ if (typeof value === "object") {
637
+ try {
638
+ JSON.stringify(value);
639
+ return true;
640
+ } catch (err) {
641
+ return false;
642
+ }
643
+ }
548
644
  try {
549
645
  JSON.parse(value);
550
646
  return true;
@@ -563,13 +659,7 @@ const createAttributeSchema = (attribute) => {
563
659
  return yup.mixed();
564
660
  }
565
661
  };
566
- const addRequiredValidation = (attribute) => (schema) => {
567
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
568
- return schema.min(1, translatedErrors.required);
569
- }
570
- if (attribute.required && attribute.type !== "relation") {
571
- return schema.required(translatedErrors.required);
572
- }
662
+ const nullableSchema = (schema) => {
573
663
  return schema?.nullable ? schema.nullable() : (
574
664
  // In some cases '.nullable' will not be available on the schema.
575
665
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -577,7 +667,22 @@ const addRequiredValidation = (attribute) => (schema) => {
577
667
  schema
578
668
  );
579
669
  };
580
- const addMinLengthValidation = (attribute) => (schema) => {
670
+ const addNullableValidation = () => (schema) => {
671
+ return nullableSchema(schema);
672
+ };
673
+ const addRequiredValidation = (attribute, options) => (schema) => {
674
+ if (options.status === "draft" || !attribute.required) {
675
+ return schema;
676
+ }
677
+ if (attribute.required && "required" in schema) {
678
+ return schema.required(translatedErrors.required);
679
+ }
680
+ return schema;
681
+ };
682
+ const addMinLengthValidation = (attribute, options) => (schema) => {
683
+ if (options.status === "draft") {
684
+ return schema;
685
+ }
581
686
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
582
687
  return schema.min(attribute.minLength, {
583
688
  ...translatedErrors.minLength,
@@ -599,32 +704,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
599
704
  }
600
705
  return schema;
601
706
  };
602
- const addMinValidation = (attribute) => (schema) => {
603
- if ("min" in attribute) {
707
+ const addMinValidation = (attribute, options) => (schema) => {
708
+ if (options.status === "draft") {
709
+ return schema;
710
+ }
711
+ if ("min" in attribute && "min" in schema) {
604
712
  const min = toInteger(attribute.min);
605
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
606
- if (!attribute.required && "test" in schema && min) {
607
- return schema.test(
608
- "custom-min",
609
- {
610
- ...translatedErrors.min,
611
- values: {
612
- min: attribute.min
613
- }
614
- },
615
- (value) => {
616
- if (!value) {
617
- return true;
618
- }
619
- if (Array.isArray(value) && value.length === 0) {
620
- return true;
621
- }
622
- return value.length >= min;
623
- }
624
- );
625
- }
626
- }
627
- if ("min" in schema && min) {
713
+ if (min) {
628
714
  return schema.min(min, {
629
715
  ...translatedErrors.min,
630
716
  values: {
@@ -742,19 +828,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
742
828
  }, {});
743
829
  return componentsByKey;
744
830
  };
745
- const useDocument = (args, opts) => {
831
+ const HOOKS = {
832
+ /**
833
+ * Hook that allows to mutate the displayed headers of the list view table
834
+ * @constant
835
+ * @type {string}
836
+ */
837
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
838
+ /**
839
+ * Hook that allows to mutate the CM's collection types links pre-set filters
840
+ * @constant
841
+ * @type {string}
842
+ */
843
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
844
+ /**
845
+ * Hook that allows to mutate the CM's edit view layout
846
+ * @constant
847
+ * @type {string}
848
+ */
849
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
850
+ /**
851
+ * Hook that allows to mutate the CM's single types links pre-set filters
852
+ * @constant
853
+ * @type {string}
854
+ */
855
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
856
+ };
857
+ const contentTypesApi = contentManagerApi.injectEndpoints({
858
+ endpoints: (builder) => ({
859
+ getContentTypeConfiguration: builder.query({
860
+ query: (uid) => ({
861
+ url: `/content-manager/content-types/${uid}/configuration`,
862
+ method: "GET"
863
+ }),
864
+ transformResponse: (response) => response.data,
865
+ providesTags: (_result, _error, uid) => [
866
+ { type: "ContentTypesConfiguration", id: uid },
867
+ { type: "ContentTypeSettings", id: "LIST" }
868
+ ]
869
+ }),
870
+ getAllContentTypeSettings: builder.query({
871
+ query: () => "/content-manager/content-types-settings",
872
+ transformResponse: (response) => response.data,
873
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
874
+ }),
875
+ updateContentTypeConfiguration: builder.mutation({
876
+ query: ({ uid, ...body }) => ({
877
+ url: `/content-manager/content-types/${uid}/configuration`,
878
+ method: "PUT",
879
+ data: body
880
+ }),
881
+ transformResponse: (response) => response.data,
882
+ invalidatesTags: (_result, _error, { uid }) => [
883
+ { type: "ContentTypesConfiguration", id: uid },
884
+ { type: "ContentTypeSettings", id: "LIST" },
885
+ // Is this necessary?
886
+ { type: "InitialData" }
887
+ ]
888
+ })
889
+ })
890
+ });
891
+ const {
892
+ useGetContentTypeConfigurationQuery,
893
+ useGetAllContentTypeSettingsQuery,
894
+ useUpdateContentTypeConfigurationMutation
895
+ } = contentTypesApi;
896
+ const checkIfAttributeIsDisplayable = (attribute) => {
897
+ const { type } = attribute;
898
+ if (type === "relation") {
899
+ return !attribute.relation.toLowerCase().includes("morph");
900
+ }
901
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
902
+ };
903
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
904
+ if (!mainFieldName) {
905
+ return void 0;
906
+ }
907
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
908
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
909
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
910
+ );
911
+ return {
912
+ name: mainFieldName,
913
+ type: mainFieldType ?? "string"
914
+ };
915
+ };
916
+ const DEFAULT_SETTINGS = {
917
+ bulkable: false,
918
+ filterable: false,
919
+ searchable: false,
920
+ pagination: false,
921
+ defaultSortBy: "",
922
+ defaultSortOrder: "asc",
923
+ mainField: "id",
924
+ pageSize: 10
925
+ };
926
+ const useDocumentLayout = (model) => {
927
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
928
+ const [{ query }] = useQueryParams();
929
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
746
930
  const { toggleNotification } = useNotification();
747
931
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
932
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
748
933
  const {
749
- currentData: data,
750
- isLoading: isLoadingDocument,
751
- isFetching: isFetchingDocument,
752
- error
753
- } = useGetDocumentQuery(args, {
754
- ...opts,
755
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
756
- });
757
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
934
+ data,
935
+ isLoading: isLoadingConfigs,
936
+ error,
937
+ isFetching: isFetchingConfigs
938
+ } = useGetContentTypeConfigurationQuery(model);
939
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
758
940
  React.useEffect(() => {
759
941
  if (error) {
760
942
  toggleNotification({
@@ -762,68 +944,322 @@ const useDocument = (args, opts) => {
762
944
  message: formatAPIError(error)
763
945
  });
764
946
  }
765
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
766
- const validationSchema = React.useMemo(() => {
767
- if (!schema) {
768
- return null;
769
- }
770
- return createYupSchema(schema.attributes, components);
771
- }, [schema, components]);
772
- const validate = React.useCallback(
773
- (document) => {
774
- if (!validationSchema) {
775
- throw new Error(
776
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
777
- );
778
- }
779
- try {
780
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
781
- return null;
782
- } catch (error2) {
783
- if (error2 instanceof ValidationError) {
784
- return getYupValidationErrors(error2);
785
- }
786
- throw error2;
787
- }
947
+ }, [error, formatAPIError, toggleNotification]);
948
+ const editLayout = React.useMemo(
949
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
950
+ layout: [],
951
+ components: {},
952
+ metadatas: {},
953
+ options: {},
954
+ settings: DEFAULT_SETTINGS
788
955
  },
789
- [validationSchema]
956
+ [data, isLoading, schemas, schema, components]
957
+ );
958
+ const listLayout = React.useMemo(() => {
959
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
960
+ layout: [],
961
+ metadatas: {},
962
+ options: {},
963
+ settings: DEFAULT_SETTINGS
964
+ };
965
+ }, [data, isLoading, schemas, schema, components]);
966
+ const { layout: edit } = React.useMemo(
967
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
968
+ layout: editLayout,
969
+ query
970
+ }),
971
+ [editLayout, query, runHookWaterfall]
790
972
  );
791
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
792
973
  return {
793
- components,
794
- document: data?.data,
795
- meta: data?.meta,
974
+ error,
796
975
  isLoading,
797
- schema,
798
- validate
799
- };
800
- };
801
- const useDoc = () => {
802
- const { id, slug, collectionType, origin } = useParams();
803
- const [{ query }] = useQueryParams();
804
- const params = React.useMemo(() => buildValidParams(query), [query]);
805
- if (!collectionType) {
806
- throw new Error("Could not find collectionType in url params");
807
- }
808
- if (!slug) {
809
- throw new Error("Could not find model in url params");
810
- }
811
- return {
812
- collectionType,
813
- model: slug,
814
- id: origin || id === "create" ? void 0 : id,
815
- ...useDocument(
816
- { documentId: origin || id, model: slug, collectionType, params },
817
- {
818
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
819
- }
820
- )
976
+ edit,
977
+ list: listLayout
821
978
  };
822
979
  };
823
- const prefixPluginTranslations = (trad, pluginId) => {
824
- if (!pluginId) {
825
- throw new TypeError("pluginId can't be empty");
980
+ const useDocLayout = () => {
981
+ const { model } = useDoc();
982
+ return useDocumentLayout(model);
983
+ };
984
+ const formatEditLayout = (data, {
985
+ schemas,
986
+ schema,
987
+ components
988
+ }) => {
989
+ let currentPanelIndex = 0;
990
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
991
+ data.contentType.layouts.edit,
992
+ schema?.attributes,
993
+ data.contentType.metadatas,
994
+ { configurations: data.components, schemas: components },
995
+ schemas
996
+ ).reduce((panels, row) => {
997
+ if (row.some((field) => field.type === "dynamiczone")) {
998
+ panels.push([row]);
999
+ currentPanelIndex += 2;
1000
+ } else {
1001
+ if (!panels[currentPanelIndex]) {
1002
+ panels.push([row]);
1003
+ } else {
1004
+ panels[currentPanelIndex].push(row);
1005
+ }
1006
+ }
1007
+ return panels;
1008
+ }, []);
1009
+ const componentEditAttributes = Object.entries(data.components).reduce(
1010
+ (acc, [uid, configuration]) => {
1011
+ acc[uid] = {
1012
+ layout: convertEditLayoutToFieldLayouts(
1013
+ configuration.layouts.edit,
1014
+ components[uid].attributes,
1015
+ configuration.metadatas,
1016
+ { configurations: data.components, schemas: components }
1017
+ ),
1018
+ settings: {
1019
+ ...configuration.settings,
1020
+ icon: components[uid].info.icon,
1021
+ displayName: components[uid].info.displayName
1022
+ }
1023
+ };
1024
+ return acc;
1025
+ },
1026
+ {}
1027
+ );
1028
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1029
+ (acc, [attribute, metadata]) => {
1030
+ return {
1031
+ ...acc,
1032
+ [attribute]: metadata.edit
1033
+ };
1034
+ },
1035
+ {}
1036
+ );
1037
+ return {
1038
+ layout: panelledEditAttributes,
1039
+ components: componentEditAttributes,
1040
+ metadatas: editMetadatas,
1041
+ settings: {
1042
+ ...data.contentType.settings,
1043
+ displayName: schema?.info.displayName
1044
+ },
1045
+ options: {
1046
+ ...schema?.options,
1047
+ ...schema?.pluginOptions,
1048
+ ...data.contentType.options
1049
+ }
1050
+ };
1051
+ };
1052
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1053
+ return rows.map(
1054
+ (row) => row.map((field) => {
1055
+ const attribute = attributes[field.name];
1056
+ if (!attribute) {
1057
+ return null;
1058
+ }
1059
+ const { edit: metadata } = metadatas[field.name];
1060
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1061
+ return {
1062
+ attribute,
1063
+ disabled: !metadata.editable,
1064
+ hint: metadata.description,
1065
+ label: metadata.label ?? "",
1066
+ name: field.name,
1067
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1068
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1069
+ schemas,
1070
+ components: components?.schemas ?? {}
1071
+ }),
1072
+ placeholder: metadata.placeholder ?? "",
1073
+ required: attribute.required ?? false,
1074
+ size: field.size,
1075
+ unique: "unique" in attribute ? attribute.unique : false,
1076
+ visible: metadata.visible ?? true,
1077
+ type: attribute.type
1078
+ };
1079
+ }).filter((field) => field !== null)
1080
+ );
1081
+ };
1082
+ const formatListLayout = (data, {
1083
+ schemas,
1084
+ schema,
1085
+ components
1086
+ }) => {
1087
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1088
+ (acc, [attribute, metadata]) => {
1089
+ return {
1090
+ ...acc,
1091
+ [attribute]: metadata.list
1092
+ };
1093
+ },
1094
+ {}
1095
+ );
1096
+ const listAttributes = convertListLayoutToFieldLayouts(
1097
+ data.contentType.layouts.list,
1098
+ schema?.attributes,
1099
+ listMetadatas,
1100
+ { configurations: data.components, schemas: components },
1101
+ schemas
1102
+ );
1103
+ return {
1104
+ layout: listAttributes,
1105
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1106
+ metadatas: listMetadatas,
1107
+ options: {
1108
+ ...schema?.options,
1109
+ ...schema?.pluginOptions,
1110
+ ...data.contentType.options
1111
+ }
1112
+ };
1113
+ };
1114
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1115
+ return columns.map((name) => {
1116
+ const attribute = attributes[name];
1117
+ if (!attribute) {
1118
+ return null;
1119
+ }
1120
+ const metadata = metadatas[name];
1121
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1122
+ return {
1123
+ attribute,
1124
+ label: metadata.label ?? "",
1125
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1126
+ schemas,
1127
+ components: components?.schemas ?? {}
1128
+ }),
1129
+ name,
1130
+ searchable: metadata.searchable ?? true,
1131
+ sortable: metadata.sortable ?? true
1132
+ };
1133
+ }).filter((field) => field !== null);
1134
+ };
1135
+ const useDocument = (args, opts) => {
1136
+ const { toggleNotification } = useNotification();
1137
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1138
+ const {
1139
+ currentData: data,
1140
+ isLoading: isLoadingDocument,
1141
+ isFetching: isFetchingDocument,
1142
+ error
1143
+ } = useGetDocumentQuery(args, {
1144
+ ...opts,
1145
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1146
+ });
1147
+ const {
1148
+ components,
1149
+ schema,
1150
+ schemas,
1151
+ isLoading: isLoadingSchema
1152
+ } = useContentTypeSchema(args.model);
1153
+ React.useEffect(() => {
1154
+ if (error) {
1155
+ toggleNotification({
1156
+ type: "danger",
1157
+ message: formatAPIError(error)
1158
+ });
1159
+ }
1160
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1161
+ const validationSchema = React.useMemo(() => {
1162
+ if (!schema) {
1163
+ return null;
1164
+ }
1165
+ return createYupSchema(schema.attributes, components);
1166
+ }, [schema, components]);
1167
+ const validate = React.useCallback(
1168
+ (document) => {
1169
+ if (!validationSchema) {
1170
+ throw new Error(
1171
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1172
+ );
1173
+ }
1174
+ try {
1175
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1176
+ return null;
1177
+ } catch (error2) {
1178
+ if (error2 instanceof ValidationError) {
1179
+ return getYupValidationErrors(error2);
1180
+ }
1181
+ throw error2;
1182
+ }
1183
+ },
1184
+ [validationSchema]
1185
+ );
1186
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1187
+ const hasError = !!error;
1188
+ return {
1189
+ components,
1190
+ document: data?.data,
1191
+ meta: data?.meta,
1192
+ isLoading,
1193
+ hasError,
1194
+ schema,
1195
+ schemas,
1196
+ validate
1197
+ };
1198
+ };
1199
+ const useDoc = () => {
1200
+ const { id, slug, collectionType, origin } = useParams();
1201
+ const [{ query }] = useQueryParams();
1202
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1203
+ if (!collectionType) {
1204
+ throw new Error("Could not find collectionType in url params");
1205
+ }
1206
+ if (!slug) {
1207
+ throw new Error("Could not find model in url params");
826
1208
  }
1209
+ const document = useDocument(
1210
+ { documentId: origin || id, model: slug, collectionType, params },
1211
+ {
1212
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1213
+ }
1214
+ );
1215
+ const returnId = origin || id === "create" ? void 0 : id;
1216
+ return {
1217
+ collectionType,
1218
+ model: slug,
1219
+ id: returnId,
1220
+ ...document
1221
+ };
1222
+ };
1223
+ const useContentManagerContext = () => {
1224
+ const {
1225
+ collectionType,
1226
+ model,
1227
+ id,
1228
+ components,
1229
+ isLoading: isLoadingDoc,
1230
+ schema,
1231
+ schemas
1232
+ } = useDoc();
1233
+ const layout = useDocumentLayout(model);
1234
+ const form = useForm("useContentManagerContext", (state) => state);
1235
+ const isSingleType = collectionType === SINGLE_TYPES;
1236
+ const slug = model;
1237
+ const isCreatingEntry = id === "create";
1238
+ useContentTypeSchema();
1239
+ const isLoading = isLoadingDoc || layout.isLoading;
1240
+ const error = layout.error;
1241
+ return {
1242
+ error,
1243
+ isLoading,
1244
+ // Base metadata
1245
+ model,
1246
+ collectionType,
1247
+ id,
1248
+ slug,
1249
+ isCreatingEntry,
1250
+ isSingleType,
1251
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1252
+ // All schema infos
1253
+ components,
1254
+ contentType: schema,
1255
+ contentTypes: schemas,
1256
+ // Form state
1257
+ form,
1258
+ // layout infos
1259
+ layout
1260
+ };
1261
+ };
1262
+ const prefixPluginTranslations = (trad, pluginId) => {
827
1263
  return Object.keys(trad).reduce((acc, current) => {
828
1264
  acc[`${pluginId}.${current}`] = trad[current];
829
1265
  return acc;
@@ -839,6 +1275,8 @@ const useDocumentActions = () => {
839
1275
  const { formatMessage } = useIntl();
840
1276
  const { trackUsage } = useTracking();
841
1277
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1278
+ const navigate = useNavigate();
1279
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
842
1280
  const [deleteDocument] = useDeleteDocumentMutation();
843
1281
  const _delete = React.useCallback(
844
1282
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1153,6 +1591,7 @@ const useDocumentActions = () => {
1153
1591
  defaultMessage: "Saved document"
1154
1592
  })
1155
1593
  });
1594
+ setCurrentStep("contentManager.success");
1156
1595
  return res.data;
1157
1596
  } catch (err) {
1158
1597
  toggleNotification({
@@ -1174,7 +1613,6 @@ const useDocumentActions = () => {
1174
1613
  sourceId
1175
1614
  });
1176
1615
  if ("error" in res) {
1177
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1178
1616
  return { error: res.error };
1179
1617
  }
1180
1618
  toggleNotification({
@@ -1193,7 +1631,7 @@ const useDocumentActions = () => {
1193
1631
  throw err;
1194
1632
  }
1195
1633
  },
1196
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1634
+ [autoCloneDocument, formatMessage, toggleNotification]
1197
1635
  );
1198
1636
  const [cloneDocument] = useCloneDocumentMutation();
1199
1637
  const clone = React.useCallback(
@@ -1219,6 +1657,7 @@ const useDocumentActions = () => {
1219
1657
  defaultMessage: "Cloned document"
1220
1658
  })
1221
1659
  });
1660
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1222
1661
  return res.data;
1223
1662
  } catch (err) {
1224
1663
  toggleNotification({
@@ -1229,7 +1668,7 @@ const useDocumentActions = () => {
1229
1668
  throw err;
1230
1669
  }
1231
1670
  },
1232
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1671
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1233
1672
  );
1234
1673
  const [getDoc] = useLazyGetDocumentQuery();
1235
1674
  const getDocument = React.useCallback(
@@ -1254,10 +1693,10 @@ const useDocumentActions = () => {
1254
1693
  update
1255
1694
  };
1256
1695
  };
1257
- const ProtectedHistoryPage = lazy(
1258
- () => import("./History-CBNGU7a-.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1696
+ const ProtectedHistoryPage = React.lazy(
1697
+ () => import("./History-DTYB9CSB.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1259
1698
  );
1260
- const routes$1 = [
1699
+ const routes$2 = [
1261
1700
  {
1262
1701
  path: ":collectionType/:slug/:id/history",
1263
1702
  Component: ProtectedHistoryPage
@@ -1267,32 +1706,45 @@ const routes$1 = [
1267
1706
  Component: ProtectedHistoryPage
1268
1707
  }
1269
1708
  ];
1709
+ const ProtectedPreviewPage = React.lazy(
1710
+ () => import("./Preview-Zzjg2_K_.mjs").then((mod) => ({ default: mod.ProtectedPreviewPage }))
1711
+ );
1712
+ const routes$1 = [
1713
+ {
1714
+ path: ":collectionType/:slug/:id/preview",
1715
+ Component: ProtectedPreviewPage
1716
+ },
1717
+ {
1718
+ path: ":collectionType/:slug/preview",
1719
+ Component: ProtectedPreviewPage
1720
+ }
1721
+ ];
1270
1722
  const ProtectedEditViewPage = lazy(
1271
- () => import("./EditViewPage-DlLEyUL6.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1723
+ () => import("./EditViewPage-BPFcUbqi.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1272
1724
  );
1273
1725
  const ProtectedListViewPage = lazy(
1274
- () => import("./ListViewPage-DecLrYV6.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1726
+ () => import("./ListViewPage-GKpL5p8A.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1275
1727
  );
1276
1728
  const ProtectedListConfiguration = lazy(
1277
- () => import("./ListConfigurationPage-5dr4qpue.mjs").then((mod) => ({
1729
+ () => import("./ListConfigurationPage-qWx8r4D_.mjs").then((mod) => ({
1278
1730
  default: mod.ProtectedListConfiguration
1279
1731
  }))
1280
1732
  );
1281
1733
  const ProtectedEditConfigurationPage = lazy(
1282
- () => import("./EditConfigurationPage-ZO0vOO8q.mjs").then((mod) => ({
1734
+ () => import("./EditConfigurationPage-C1ddZ_zf.mjs").then((mod) => ({
1283
1735
  default: mod.ProtectedEditConfigurationPage
1284
1736
  }))
1285
1737
  );
1286
1738
  const ProtectedComponentConfigurationPage = lazy(
1287
- () => import("./ComponentConfigurationPage-B1bIXVuX.mjs").then((mod) => ({
1739
+ () => import("./ComponentConfigurationPage-CcRDqD0e.mjs").then((mod) => ({
1288
1740
  default: mod.ProtectedComponentConfigurationPage
1289
1741
  }))
1290
1742
  );
1291
1743
  const NoPermissions = lazy(
1292
- () => import("./NoPermissionsPage-CM5UD8ee.mjs").then((mod) => ({ default: mod.NoPermissions }))
1744
+ () => import("./NoPermissionsPage-BAZlWgJ4.mjs").then((mod) => ({ default: mod.NoPermissions }))
1293
1745
  );
1294
1746
  const NoContentType = lazy(
1295
- () => import("./NoContentTypePage-CiIcfYsd.mjs").then((mod) => ({ default: mod.NoContentType }))
1747
+ () => import("./NoContentTypePage-B5Vc5Cal.mjs").then((mod) => ({ default: mod.NoContentType }))
1296
1748
  );
1297
1749
  const CollectionTypePages = () => {
1298
1750
  const { collectionType } = useParams();
@@ -1304,7 +1756,7 @@ const CollectionTypePages = () => {
1304
1756
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1305
1757
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1306
1758
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1307
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1759
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1308
1760
  const routes = [
1309
1761
  {
1310
1762
  path: LIST_RELATIVE_PATH,
@@ -1338,6 +1790,7 @@ const routes = [
1338
1790
  path: "no-content-types",
1339
1791
  Component: NoContentType
1340
1792
  },
1793
+ ...routes$2,
1341
1794
  ...routes$1
1342
1795
  ];
1343
1796
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1406,12 +1859,14 @@ const DocumentActionButton = (action) => {
1406
1859
  /* @__PURE__ */ jsx(
1407
1860
  Button,
1408
1861
  {
1409
- flex: 1,
1862
+ flex: "auto",
1410
1863
  startIcon: action.icon,
1411
1864
  disabled: action.disabled,
1412
1865
  onClick: handleClick(action),
1413
1866
  justifyContent: "center",
1414
1867
  variant: action.variant || "default",
1868
+ paddingTop: "7px",
1869
+ paddingBottom: "7px",
1415
1870
  children: action.label
1416
1871
  }
1417
1872
  ),
@@ -1434,6 +1889,11 @@ const DocumentActionButton = (action) => {
1434
1889
  ) : null
1435
1890
  ] });
1436
1891
  };
1892
+ const MenuItem = styled(Menu.Item)`
1893
+ &:hover {
1894
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
1895
+ }
1896
+ `;
1437
1897
  const DocumentActionsMenu = ({
1438
1898
  actions: actions2,
1439
1899
  children,
@@ -1476,9 +1936,9 @@ const DocumentActionsMenu = ({
1476
1936
  disabled: isDisabled,
1477
1937
  size: "S",
1478
1938
  endIcon: null,
1479
- paddingTop: "7px",
1480
- paddingLeft: "9px",
1481
- paddingRight: "9px",
1939
+ paddingTop: "4px",
1940
+ paddingLeft: "7px",
1941
+ paddingRight: "7px",
1482
1942
  variant,
1483
1943
  children: [
1484
1944
  /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
@@ -1489,36 +1949,35 @@ const DocumentActionsMenu = ({
1489
1949
  ]
1490
1950
  }
1491
1951
  ),
1492
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1952
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1493
1953
  actions2.map((action) => {
1494
1954
  return /* @__PURE__ */ jsx(
1495
- Menu.Item,
1955
+ MenuItem,
1496
1956
  {
1497
1957
  disabled: action.disabled,
1498
1958
  onSelect: handleClick(action),
1499
1959
  display: "block",
1500
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1501
- /* @__PURE__ */ jsxs(Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1502
- /* @__PURE__ */ jsx(Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1503
- action.label
1504
- ] }),
1505
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1506
- Flex,
1507
- {
1508
- alignItems: "center",
1509
- background: "alternative100",
1510
- borderStyle: "solid",
1511
- borderColor: "alternative200",
1512
- borderWidth: "1px",
1513
- height: 5,
1514
- paddingLeft: 2,
1515
- paddingRight: 2,
1516
- hasRadius: true,
1517
- color: "alternative600",
1518
- children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1519
- }
1520
- )
1521
- ] })
1960
+ isVariantDanger: action.variant === "danger",
1961
+ isDisabled: action.disabled,
1962
+ children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxs(
1963
+ Flex,
1964
+ {
1965
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1966
+ gap: 2,
1967
+ tag: "span",
1968
+ children: [
1969
+ /* @__PURE__ */ jsx(
1970
+ Flex,
1971
+ {
1972
+ tag: "span",
1973
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1974
+ children: action.icon
1975
+ }
1976
+ ),
1977
+ action.label
1978
+ ]
1979
+ }
1980
+ ) })
1522
1981
  },
1523
1982
  action.id
1524
1983
  );
@@ -1598,11 +2057,11 @@ const DocumentActionConfirmDialog = ({
1598
2057
  /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1599
2058
  /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1600
2059
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1601
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
2060
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1602
2061
  id: "app.components.Button.cancel",
1603
2062
  defaultMessage: "Cancel"
1604
2063
  }) }) }),
1605
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
2064
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1606
2065
  id: "app.components.Button.confirm",
1607
2066
  defaultMessage: "Confirm"
1608
2067
  }) })
@@ -1625,10 +2084,22 @@ const DocumentActionModal = ({
1625
2084
  };
1626
2085
  return /* @__PURE__ */ jsx(Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
1627
2086
  /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: title }) }),
1628
- /* @__PURE__ */ jsx(Modal.Body, { children: typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content }),
1629
- /* @__PURE__ */ jsx(Modal.Footer, { children: typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer })
2087
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsx(Modal.Body, { children: Content }),
2088
+ typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1630
2089
  ] }) });
1631
2090
  };
2091
+ const transformData = (data) => {
2092
+ if (Array.isArray(data)) {
2093
+ return data.map(transformData);
2094
+ }
2095
+ if (typeof data === "object" && data !== null) {
2096
+ if ("apiData" in data) {
2097
+ return data.apiData;
2098
+ }
2099
+ return mapValues(transformData)(data);
2100
+ }
2101
+ return data;
2102
+ };
1632
2103
  const PublishAction$1 = ({
1633
2104
  activeTab,
1634
2105
  documentId,
@@ -1641,12 +2112,11 @@ const PublishAction$1 = ({
1641
2112
  const navigate = useNavigate();
1642
2113
  const { toggleNotification } = useNotification();
1643
2114
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2115
+ const isListView = useMatch(LIST_PATH) !== null;
1644
2116
  const isCloning = useMatch(CLONE_PATH) !== null;
2117
+ const { id } = useParams();
1645
2118
  const { formatMessage } = useIntl();
1646
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1647
- "PublishAction",
1648
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1649
- );
2119
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1650
2120
  const { publish } = useDocumentActions();
1651
2121
  const [
1652
2122
  countDraftRelations,
@@ -1698,24 +2168,25 @@ const PublishAction$1 = ({
1698
2168
  }
1699
2169
  }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1700
2170
  React.useEffect(() => {
1701
- if (documentId) {
1702
- const fetchDraftRelationsCount = async () => {
1703
- const { data, error } = await countDraftRelations({
1704
- collectionType,
1705
- model,
1706
- documentId,
1707
- params
1708
- });
1709
- if (error) {
1710
- throw error;
1711
- }
1712
- if (data) {
1713
- setServerCountOfDraftRelations(data.data);
1714
- }
1715
- };
1716
- fetchDraftRelationsCount();
2171
+ if (!document || !document.documentId || isListView) {
2172
+ return;
1717
2173
  }
1718
- }, [documentId, countDraftRelations, collectionType, model, params]);
2174
+ const fetchDraftRelationsCount = async () => {
2175
+ const { data, error } = await countDraftRelations({
2176
+ collectionType,
2177
+ model,
2178
+ documentId,
2179
+ params
2180
+ });
2181
+ if (error) {
2182
+ throw error;
2183
+ }
2184
+ if (data) {
2185
+ setServerCountOfDraftRelations(data.data);
2186
+ }
2187
+ };
2188
+ fetchDraftRelationsCount();
2189
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1719
2190
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1720
2191
  if (!schema?.options?.draftAndPublish) {
1721
2192
  return null;
@@ -1723,7 +2194,9 @@ const PublishAction$1 = ({
1723
2194
  const performPublish = async () => {
1724
2195
  setSubmitting(true);
1725
2196
  try {
1726
- const { errors } = await validate();
2197
+ const { errors } = await validate(true, {
2198
+ status: "published"
2199
+ });
1727
2200
  if (errors) {
1728
2201
  toggleNotification({
1729
2202
  type: "danger",
@@ -1741,13 +2214,15 @@ const PublishAction$1 = ({
1741
2214
  documentId,
1742
2215
  params
1743
2216
  },
1744
- formValues
2217
+ transformData(formValues)
1745
2218
  );
1746
2219
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1747
- navigate({
1748
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1749
- search: rawQuery
1750
- });
2220
+ if (id === "create") {
2221
+ navigate({
2222
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2223
+ search: rawQuery
2224
+ });
2225
+ }
1751
2226
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1752
2227
  setErrors(formatValidationErrors(res.error));
1753
2228
  }
@@ -1756,7 +2231,8 @@ const PublishAction$1 = ({
1756
2231
  }
1757
2232
  };
1758
2233
  const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1759
- const hasDraftRelations = totalDraftRelations > 0;
2234
+ const enableDraftRelationsCount = false;
2235
+ const hasDraftRelations = enableDraftRelationsCount;
1760
2236
  return {
1761
2237
  /**
1762
2238
  * Disabled when:
@@ -1766,18 +2242,13 @@ const PublishAction$1 = ({
1766
2242
  * - the document is already published & not modified
1767
2243
  * - the document is being created & not modified
1768
2244
  * - the user doesn't have the permission to publish
1769
- * - the user doesn't have the permission to create a new document
1770
- * - the user doesn't have the permission to update the document
1771
2245
  */
1772
- disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2246
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1773
2247
  label: formatMessage({
1774
2248
  id: "app.utils.publish",
1775
2249
  defaultMessage: "Publish"
1776
2250
  }),
1777
2251
  onClick: async () => {
1778
- if (hasDraftRelations) {
1779
- return;
1780
- }
1781
2252
  await performPublish();
1782
2253
  },
1783
2254
  dialog: hasDraftRelations ? {
@@ -1804,6 +2275,7 @@ const PublishAction$1 = ({
1804
2275
  };
1805
2276
  };
1806
2277
  PublishAction$1.type = "publish";
2278
+ PublishAction$1.position = "panel";
1807
2279
  const UpdateAction = ({
1808
2280
  activeTab,
1809
2281
  documentId,
@@ -1816,10 +2288,6 @@ const UpdateAction = ({
1816
2288
  const cloneMatch = useMatch(CLONE_PATH);
1817
2289
  const isCloning = cloneMatch !== null;
1818
2290
  const { formatMessage } = useIntl();
1819
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1820
- canCreate: canCreate2,
1821
- canUpdate: canUpdate2
1822
- }));
1823
2291
  const { create, update, clone } = useDocumentActions();
1824
2292
  const [{ query, rawQuery }] = useQueryParams();
1825
2293
  const params = React.useMemo(() => buildValidParams(query), [query]);
@@ -1830,93 +2298,134 @@ const UpdateAction = ({
1830
2298
  const validate = useForm("UpdateAction", (state) => state.validate);
1831
2299
  const setErrors = useForm("UpdateAction", (state) => state.setErrors);
1832
2300
  const resetForm = useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
1833
- return {
1834
- /**
1835
- * Disabled when:
1836
- * - the form is submitting
1837
- * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1838
- * - the active tab is the published tab
1839
- * - the user doesn't have the permission to create a new document
1840
- * - the user doesn't have the permission to update the document
1841
- */
1842
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
1843
- label: formatMessage({
1844
- id: "content-manager.containers.Edit.save",
1845
- defaultMessage: "Save"
1846
- }),
1847
- onClick: async () => {
1848
- setSubmitting(true);
1849
- try {
1850
- const { errors } = await validate();
1851
- if (errors) {
1852
- toggleNotification({
1853
- type: "danger",
1854
- message: formatMessage({
1855
- id: "content-manager.validation.error",
1856
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1857
- })
1858
- });
1859
- return;
1860
- }
1861
- if (isCloning) {
1862
- const res = await clone(
2301
+ const handleUpdate = React.useCallback(async () => {
2302
+ setSubmitting(true);
2303
+ try {
2304
+ if (!modified) {
2305
+ return;
2306
+ }
2307
+ const { errors } = await validate(true, {
2308
+ status: "draft"
2309
+ });
2310
+ if (errors) {
2311
+ toggleNotification({
2312
+ type: "danger",
2313
+ message: formatMessage({
2314
+ id: "content-manager.validation.error",
2315
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2316
+ })
2317
+ });
2318
+ return;
2319
+ }
2320
+ if (isCloning) {
2321
+ const res = await clone(
2322
+ {
2323
+ model,
2324
+ documentId: cloneMatch.params.origin,
2325
+ params
2326
+ },
2327
+ transformData(document)
2328
+ );
2329
+ if ("data" in res) {
2330
+ navigate(
1863
2331
  {
1864
- model,
1865
- documentId: cloneMatch.params.origin,
1866
- params
1867
- },
1868
- document
1869
- );
1870
- if ("data" in res) {
1871
- navigate({
1872
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2332
+ pathname: `../${res.data.documentId}`,
1873
2333
  search: rawQuery
1874
- });
1875
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1876
- setErrors(formatValidationErrors(res.error));
1877
- }
1878
- } else if (documentId || collectionType === SINGLE_TYPES) {
1879
- const res = await update(
1880
- {
1881
- collectionType,
1882
- model,
1883
- documentId,
1884
- params
1885
2334
  },
1886
- document
2335
+ { relative: "path" }
1887
2336
  );
1888
- if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1889
- setErrors(formatValidationErrors(res.error));
1890
- } else {
1891
- resetForm();
1892
- }
2337
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2338
+ setErrors(formatValidationErrors(res.error));
2339
+ }
2340
+ } else if (documentId || collectionType === SINGLE_TYPES) {
2341
+ const res = await update(
2342
+ {
2343
+ collectionType,
2344
+ model,
2345
+ documentId,
2346
+ params
2347
+ },
2348
+ transformData(document)
2349
+ );
2350
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2351
+ setErrors(formatValidationErrors(res.error));
1893
2352
  } else {
1894
- const res = await create(
2353
+ resetForm();
2354
+ }
2355
+ } else {
2356
+ const res = await create(
2357
+ {
2358
+ model,
2359
+ params
2360
+ },
2361
+ transformData(document)
2362
+ );
2363
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2364
+ navigate(
1895
2365
  {
1896
- model,
1897
- params
2366
+ pathname: `../${res.data.documentId}`,
2367
+ search: rawQuery
1898
2368
  },
1899
- document
2369
+ { replace: true, relative: "path" }
1900
2370
  );
1901
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1902
- navigate(
1903
- {
1904
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1905
- search: rawQuery
1906
- },
1907
- { replace: true }
1908
- );
1909
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1910
- setErrors(formatValidationErrors(res.error));
1911
- }
2371
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2372
+ setErrors(formatValidationErrors(res.error));
1912
2373
  }
1913
- } finally {
1914
- setSubmitting(false);
1915
2374
  }
2375
+ } finally {
2376
+ setSubmitting(false);
1916
2377
  }
2378
+ }, [
2379
+ clone,
2380
+ cloneMatch?.params.origin,
2381
+ collectionType,
2382
+ create,
2383
+ document,
2384
+ documentId,
2385
+ formatMessage,
2386
+ formatValidationErrors,
2387
+ isCloning,
2388
+ model,
2389
+ modified,
2390
+ navigate,
2391
+ params,
2392
+ rawQuery,
2393
+ resetForm,
2394
+ setErrors,
2395
+ setSubmitting,
2396
+ toggleNotification,
2397
+ update,
2398
+ validate
2399
+ ]);
2400
+ React.useEffect(() => {
2401
+ const handleKeyDown = (e) => {
2402
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
2403
+ e.preventDefault();
2404
+ handleUpdate();
2405
+ }
2406
+ };
2407
+ window.addEventListener("keydown", handleKeyDown);
2408
+ return () => {
2409
+ window.removeEventListener("keydown", handleKeyDown);
2410
+ };
2411
+ }, [handleUpdate]);
2412
+ return {
2413
+ /**
2414
+ * Disabled when:
2415
+ * - the form is submitting
2416
+ * - the document is not modified & we're not cloning (you can save a clone entity straight away)
2417
+ * - the active tab is the published tab
2418
+ */
2419
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
2420
+ label: formatMessage({
2421
+ id: "global.save",
2422
+ defaultMessage: "Save"
2423
+ }),
2424
+ onClick: handleUpdate
1917
2425
  };
1918
2426
  };
1919
2427
  UpdateAction.type = "update";
2428
+ UpdateAction.position = "panel";
1920
2429
  const UNPUBLISH_DRAFT_OPTIONS = {
1921
2430
  KEEP: "keep",
1922
2431
  DISCARD: "discard"
@@ -1949,7 +2458,7 @@ const UnpublishAction$1 = ({
1949
2458
  id: "app.utils.unpublish",
1950
2459
  defaultMessage: "Unpublish"
1951
2460
  }),
1952
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2461
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1953
2462
  onClick: async () => {
1954
2463
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1955
2464
  if (!documentId) {
@@ -2039,6 +2548,7 @@ const UnpublishAction$1 = ({
2039
2548
  };
2040
2549
  };
2041
2550
  UnpublishAction$1.type = "unpublish";
2551
+ UnpublishAction$1.position = "panel";
2042
2552
  const DiscardAction = ({
2043
2553
  activeTab,
2044
2554
  documentId,
@@ -2061,7 +2571,7 @@ const DiscardAction = ({
2061
2571
  id: "content-manager.actions.discard.label",
2062
2572
  defaultMessage: "Discard changes"
2063
2573
  }),
2064
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2574
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2065
2575
  position: ["panel", "table-row"],
2066
2576
  variant: "danger",
2067
2577
  dialog: {
@@ -2089,11 +2599,7 @@ const DiscardAction = ({
2089
2599
  };
2090
2600
  };
2091
2601
  DiscardAction.type = "discard";
2092
- const StyledCrossCircle = styled(CrossCircle)`
2093
- path {
2094
- fill: currentColor;
2095
- }
2096
- `;
2602
+ DiscardAction.position = "panel";
2097
2603
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2098
2604
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2099
2605
  const RelativeTime = React.forwardRef(
@@ -2106,7 +2612,7 @@ const RelativeTime = React.forwardRef(
2106
2612
  });
2107
2613
  const unit = intervals.find((intervalUnit) => {
2108
2614
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2109
- });
2615
+ }) ?? "seconds";
2110
2616
  const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2111
2617
  const customInterval = customIntervals.find(
2112
2618
  (custom) => interval[custom.unit] < custom.threshold
@@ -2140,19 +2646,29 @@ const getDisplayName = ({
2140
2646
  return email ?? "";
2141
2647
  };
2142
2648
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2143
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2144
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2145
- return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2649
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2650
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2651
+ const { formatMessage } = useIntl();
2652
+ return /* @__PURE__ */ jsx(Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2653
+ id: `content-manager.containers.List.${status}`,
2654
+ defaultMessage: capitalise(status)
2655
+ }) }) });
2146
2656
  };
2147
2657
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2148
2658
  const { formatMessage } = useIntl();
2149
2659
  const isCloning = useMatch(CLONE_PATH) !== null;
2660
+ const params = useParams();
2150
2661
  const title = isCreating ? formatMessage({
2151
2662
  id: "content-manager.containers.edit.title.new",
2152
2663
  defaultMessage: "Create an entry"
2153
2664
  }) : documentTitle;
2154
2665
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2155
- /* @__PURE__ */ jsx(BackButton, {}),
2666
+ /* @__PURE__ */ jsx(
2667
+ BackButton,
2668
+ {
2669
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2670
+ }
2671
+ ),
2156
2672
  /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2157
2673
  /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2158
2674
  /* @__PURE__ */ jsx(HeaderToolbar, {})
@@ -2203,7 +2719,7 @@ const HeaderToolbar = () => {
2203
2719
  meta: isCloning ? void 0 : meta,
2204
2720
  collectionType
2205
2721
  },
2206
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2722
+ descriptions: plugins["content-manager"].apis.getDocumentActions("header"),
2207
2723
  children: (actions2) => {
2208
2724
  const headerActions = actions2.filter((action) => {
2209
2725
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2240,12 +2756,12 @@ const Information = ({ activeTab }) => {
2240
2756
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2241
2757
  label: formatMessage({
2242
2758
  id: "content-manager.containers.edit.information.last-published.label",
2243
- defaultMessage: "Last published"
2759
+ defaultMessage: "Published"
2244
2760
  }),
2245
2761
  value: formatMessage(
2246
2762
  {
2247
2763
  id: "content-manager.containers.edit.information.last-published.value",
2248
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2764
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2249
2765
  },
2250
2766
  {
2251
2767
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2258,12 +2774,12 @@ const Information = ({ activeTab }) => {
2258
2774
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2259
2775
  label: formatMessage({
2260
2776
  id: "content-manager.containers.edit.information.last-draft.label",
2261
- defaultMessage: "Last draft"
2777
+ defaultMessage: "Updated"
2262
2778
  }),
2263
2779
  value: formatMessage(
2264
2780
  {
2265
2781
  id: "content-manager.containers.edit.information.last-draft.value",
2266
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2782
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2267
2783
  },
2268
2784
  {
2269
2785
  time: /* @__PURE__ */ jsx(
@@ -2281,12 +2797,12 @@ const Information = ({ activeTab }) => {
2281
2797
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2282
2798
  label: formatMessage({
2283
2799
  id: "content-manager.containers.edit.information.document.label",
2284
- defaultMessage: "Document"
2800
+ defaultMessage: "Created"
2285
2801
  }),
2286
2802
  value: formatMessage(
2287
2803
  {
2288
2804
  id: "content-manager.containers.edit.information.document.value",
2289
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2805
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2290
2806
  },
2291
2807
  {
2292
2808
  time: /* @__PURE__ */ jsx(
@@ -2324,25 +2840,77 @@ const Information = ({ activeTab }) => {
2324
2840
  );
2325
2841
  };
2326
2842
  const HeaderActions = ({ actions: actions2 }) => {
2327
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2328
- if ("options" in action) {
2843
+ const [dialogId, setDialogId] = React.useState(null);
2844
+ const handleClick = (action) => async (e) => {
2845
+ if (!("options" in action)) {
2846
+ const { onClick = () => false, dialog, id } = action;
2847
+ const muteDialog = await onClick(e);
2848
+ if (dialog && !muteDialog) {
2849
+ e.preventDefault();
2850
+ setDialogId(id);
2851
+ }
2852
+ }
2853
+ };
2854
+ const handleClose = () => {
2855
+ setDialogId(null);
2856
+ };
2857
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2858
+ if (action.options) {
2329
2859
  return /* @__PURE__ */ jsx(
2330
2860
  SingleSelect,
2331
2861
  {
2332
2862
  size: "S",
2333
- disabled: action.disabled,
2334
- "aria-label": action.label,
2335
2863
  onChange: action.onSelect,
2336
- value: action.value,
2864
+ "aria-label": action.label,
2865
+ ...action,
2337
2866
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2338
2867
  },
2339
2868
  action.id
2340
2869
  );
2341
2870
  } else {
2342
- return null;
2871
+ if (action.type === "icon") {
2872
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
2873
+ /* @__PURE__ */ jsx(
2874
+ IconButton,
2875
+ {
2876
+ disabled: action.disabled,
2877
+ label: action.label,
2878
+ size: "S",
2879
+ onClick: handleClick(action),
2880
+ children: action.icon
2881
+ }
2882
+ ),
2883
+ action.dialog ? /* @__PURE__ */ jsx(
2884
+ HeaderActionDialog,
2885
+ {
2886
+ ...action.dialog,
2887
+ isOpen: dialogId === action.id,
2888
+ onClose: handleClose
2889
+ }
2890
+ ) : null
2891
+ ] }, action.id);
2892
+ }
2343
2893
  }
2344
2894
  }) });
2345
2895
  };
2896
+ const HeaderActionDialog = ({
2897
+ onClose,
2898
+ onCancel,
2899
+ title,
2900
+ content: Content,
2901
+ isOpen
2902
+ }) => {
2903
+ const handleClose = async () => {
2904
+ if (onCancel) {
2905
+ await onCancel();
2906
+ }
2907
+ onClose();
2908
+ };
2909
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2910
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2911
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
2912
+ ] }) });
2913
+ };
2346
2914
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2347
2915
  const navigate = useNavigate();
2348
2916
  const { formatMessage } = useIntl();
@@ -2359,6 +2927,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2359
2927
  };
2360
2928
  };
2361
2929
  ConfigureTheViewAction.type = "configure-the-view";
2930
+ ConfigureTheViewAction.position = "header";
2362
2931
  const EditTheModelAction = ({ model }) => {
2363
2932
  const navigate = useNavigate();
2364
2933
  const { formatMessage } = useIntl();
@@ -2375,6 +2944,7 @@ const EditTheModelAction = ({ model }) => {
2375
2944
  };
2376
2945
  };
2377
2946
  EditTheModelAction.type = "edit-the-model";
2947
+ EditTheModelAction.position = "header";
2378
2948
  const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2379
2949
  const navigate = useNavigate();
2380
2950
  const { formatMessage } = useIntl();
@@ -2383,12 +2953,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2383
2953
  const { delete: deleteAction } = useDocumentActions();
2384
2954
  const { toggleNotification } = useNotification();
2385
2955
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2956
+ const isLocalized = document?.locale != null;
2386
2957
  return {
2387
2958
  disabled: !canDelete || !document,
2388
- label: formatMessage({
2389
- id: "content-manager.actions.delete.label",
2390
- defaultMessage: "Delete document"
2391
- }),
2959
+ label: formatMessage(
2960
+ {
2961
+ id: "content-manager.actions.delete.label",
2962
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2963
+ },
2964
+ { isLocalized }
2965
+ ),
2392
2966
  icon: /* @__PURE__ */ jsx(Trash, {}),
2393
2967
  dialog: {
2394
2968
  type: "dialog",
@@ -2430,417 +3004,116 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2430
3004
  }
2431
3005
  });
2432
3006
  if (!("error" in res)) {
2433
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2434
- }
2435
- } finally {
2436
- if (!listViewPathMatch) {
2437
- setSubmitting(false);
2438
- }
2439
- }
2440
- }
2441
- },
2442
- variant: "danger",
2443
- position: ["header", "table-row"]
2444
- };
2445
- };
2446
- DeleteAction$1.type = "delete";
2447
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2448
- const Panels = () => {
2449
- const isCloning = useMatch(CLONE_PATH) !== null;
2450
- const [
2451
- {
2452
- query: { status }
2453
- }
2454
- ] = useQueryParams({
2455
- status: "draft"
2456
- });
2457
- const { model, id, document, meta, collectionType } = useDoc();
2458
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2459
- const props = {
2460
- activeTab: status,
2461
- model,
2462
- documentId: id,
2463
- document: isCloning ? void 0 : document,
2464
- meta: isCloning ? void 0 : meta,
2465
- collectionType
2466
- };
2467
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2468
- DescriptionComponentRenderer,
2469
- {
2470
- props,
2471
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2472
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2473
- }
2474
- ) });
2475
- };
2476
- const ActionsPanel = () => {
2477
- const { formatMessage } = useIntl();
2478
- return {
2479
- title: formatMessage({
2480
- id: "content-manager.containers.edit.panels.default.title",
2481
- defaultMessage: "Document"
2482
- }),
2483
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2484
- };
2485
- };
2486
- ActionsPanel.type = "actions";
2487
- const ActionsPanelContent = () => {
2488
- const isCloning = useMatch(CLONE_PATH) !== null;
2489
- const [
2490
- {
2491
- query: { status = "draft" }
2492
- }
2493
- ] = useQueryParams();
2494
- const { model, id, document, meta, collectionType } = useDoc();
2495
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2496
- const props = {
2497
- activeTab: status,
2498
- model,
2499
- documentId: id,
2500
- document: isCloning ? void 0 : document,
2501
- meta: isCloning ? void 0 : meta,
2502
- collectionType
2503
- };
2504
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2505
- /* @__PURE__ */ jsx(
2506
- DescriptionComponentRenderer,
2507
- {
2508
- props,
2509
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2510
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2511
- }
2512
- ),
2513
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2514
- ] });
2515
- };
2516
- const Panel = React.forwardRef(({ children, title }, ref) => {
2517
- return /* @__PURE__ */ jsxs(
2518
- Flex,
2519
- {
2520
- ref,
2521
- tag: "aside",
2522
- "aria-labelledby": "additional-information",
2523
- background: "neutral0",
2524
- borderColor: "neutral150",
2525
- hasRadius: true,
2526
- paddingBottom: 4,
2527
- paddingLeft: 4,
2528
- paddingRight: 4,
2529
- paddingTop: 4,
2530
- shadow: "tableShadow",
2531
- gap: 3,
2532
- direction: "column",
2533
- justifyContent: "stretch",
2534
- alignItems: "flex-start",
2535
- children: [
2536
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2537
- children
2538
- ]
2539
- }
2540
- );
2541
- });
2542
- const HOOKS = {
2543
- /**
2544
- * Hook that allows to mutate the displayed headers of the list view table
2545
- * @constant
2546
- * @type {string}
2547
- */
2548
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2549
- /**
2550
- * Hook that allows to mutate the CM's collection types links pre-set filters
2551
- * @constant
2552
- * @type {string}
2553
- */
2554
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2555
- /**
2556
- * Hook that allows to mutate the CM's edit view layout
2557
- * @constant
2558
- * @type {string}
2559
- */
2560
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2561
- /**
2562
- * Hook that allows to mutate the CM's single types links pre-set filters
2563
- * @constant
2564
- * @type {string}
2565
- */
2566
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2567
- };
2568
- const contentTypesApi = contentManagerApi.injectEndpoints({
2569
- endpoints: (builder) => ({
2570
- getContentTypeConfiguration: builder.query({
2571
- query: (uid) => ({
2572
- url: `/content-manager/content-types/${uid}/configuration`,
2573
- method: "GET"
2574
- }),
2575
- transformResponse: (response) => response.data,
2576
- providesTags: (_result, _error, uid) => [
2577
- { type: "ContentTypesConfiguration", id: uid },
2578
- { type: "ContentTypeSettings", id: "LIST" }
2579
- ]
2580
- }),
2581
- getAllContentTypeSettings: builder.query({
2582
- query: () => "/content-manager/content-types-settings",
2583
- transformResponse: (response) => response.data,
2584
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2585
- }),
2586
- updateContentTypeConfiguration: builder.mutation({
2587
- query: ({ uid, ...body }) => ({
2588
- url: `/content-manager/content-types/${uid}/configuration`,
2589
- method: "PUT",
2590
- data: body
2591
- }),
2592
- transformResponse: (response) => response.data,
2593
- invalidatesTags: (_result, _error, { uid }) => [
2594
- { type: "ContentTypesConfiguration", id: uid },
2595
- { type: "ContentTypeSettings", id: "LIST" },
2596
- // Is this necessary?
2597
- { type: "InitialData" }
2598
- ]
2599
- })
2600
- })
2601
- });
2602
- const {
2603
- useGetContentTypeConfigurationQuery,
2604
- useGetAllContentTypeSettingsQuery,
2605
- useUpdateContentTypeConfigurationMutation
2606
- } = contentTypesApi;
2607
- const checkIfAttributeIsDisplayable = (attribute) => {
2608
- const { type } = attribute;
2609
- if (type === "relation") {
2610
- return !attribute.relation.toLowerCase().includes("morph");
2611
- }
2612
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2613
- };
2614
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2615
- if (!mainFieldName) {
2616
- return void 0;
2617
- }
2618
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2619
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2620
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2621
- );
2622
- return {
2623
- name: mainFieldName,
2624
- type: mainFieldType ?? "string"
2625
- };
2626
- };
2627
- const DEFAULT_SETTINGS = {
2628
- bulkable: false,
2629
- filterable: false,
2630
- searchable: false,
2631
- pagination: false,
2632
- defaultSortBy: "",
2633
- defaultSortOrder: "asc",
2634
- mainField: "id",
2635
- pageSize: 10
2636
- };
2637
- const useDocumentLayout = (model) => {
2638
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2639
- const [{ query }] = useQueryParams();
2640
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2641
- const { toggleNotification } = useNotification();
2642
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2643
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2644
- const {
2645
- data,
2646
- isLoading: isLoadingConfigs,
2647
- error,
2648
- isFetching: isFetchingConfigs
2649
- } = useGetContentTypeConfigurationQuery(model);
2650
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2651
- React.useEffect(() => {
2652
- if (error) {
2653
- toggleNotification({
2654
- type: "danger",
2655
- message: formatAPIError(error)
2656
- });
2657
- }
2658
- }, [error, formatAPIError, toggleNotification]);
2659
- const editLayout = React.useMemo(
2660
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2661
- layout: [],
2662
- components: {},
2663
- metadatas: {},
2664
- options: {},
2665
- settings: DEFAULT_SETTINGS
2666
- },
2667
- [data, isLoading, schemas, schema, components]
2668
- );
2669
- const listLayout = React.useMemo(() => {
2670
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2671
- layout: [],
2672
- metadatas: {},
2673
- options: {},
2674
- settings: DEFAULT_SETTINGS
2675
- };
2676
- }, [data, isLoading, schemas, schema, components]);
2677
- const { layout: edit } = React.useMemo(
2678
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2679
- layout: editLayout,
2680
- query
2681
- }),
2682
- [editLayout, query, runHookWaterfall]
2683
- );
2684
- return {
2685
- error,
2686
- isLoading,
2687
- edit,
2688
- list: listLayout
2689
- };
2690
- };
2691
- const useDocLayout = () => {
2692
- const { model } = useDoc();
2693
- return useDocumentLayout(model);
2694
- };
2695
- const formatEditLayout = (data, {
2696
- schemas,
2697
- schema,
2698
- components
2699
- }) => {
2700
- let currentPanelIndex = 0;
2701
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2702
- data.contentType.layouts.edit,
2703
- schema?.attributes,
2704
- data.contentType.metadatas,
2705
- { configurations: data.components, schemas: components },
2706
- schemas
2707
- ).reduce((panels, row) => {
2708
- if (row.some((field) => field.type === "dynamiczone")) {
2709
- panels.push([row]);
2710
- currentPanelIndex += 2;
2711
- } else {
2712
- if (!panels[currentPanelIndex]) {
2713
- panels.push([]);
2714
- }
2715
- panels[currentPanelIndex].push(row);
2716
- }
2717
- return panels;
2718
- }, []);
2719
- const componentEditAttributes = Object.entries(data.components).reduce(
2720
- (acc, [uid, configuration]) => {
2721
- acc[uid] = {
2722
- layout: convertEditLayoutToFieldLayouts(
2723
- configuration.layouts.edit,
2724
- components[uid].attributes,
2725
- configuration.metadatas
2726
- ),
2727
- settings: {
2728
- ...configuration.settings,
2729
- icon: components[uid].info.icon,
2730
- displayName: components[uid].info.displayName
3007
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
3008
+ }
3009
+ } finally {
3010
+ if (!listViewPathMatch) {
3011
+ setSubmitting(false);
3012
+ }
2731
3013
  }
2732
- };
2733
- return acc;
2734
- },
2735
- {}
2736
- );
2737
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2738
- (acc, [attribute, metadata]) => {
2739
- return {
2740
- ...acc,
2741
- [attribute]: metadata.edit
2742
- };
2743
- },
2744
- {}
2745
- );
2746
- return {
2747
- layout: panelledEditAttributes,
2748
- components: componentEditAttributes,
2749
- metadatas: editMetadatas,
2750
- settings: {
2751
- ...data.contentType.settings,
2752
- displayName: schema?.info.displayName
3014
+ }
2753
3015
  },
2754
- options: {
2755
- ...schema?.options,
2756
- ...schema?.pluginOptions,
2757
- ...data.contentType.options
2758
- }
3016
+ variant: "danger",
3017
+ position: ["header", "table-row"]
2759
3018
  };
2760
3019
  };
2761
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2762
- return rows.map(
2763
- (row) => row.map((field) => {
2764
- const attribute = attributes[field.name];
2765
- if (!attribute) {
2766
- return null;
2767
- }
2768
- const { edit: metadata } = metadatas[field.name];
2769
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2770
- return {
2771
- attribute,
2772
- disabled: !metadata.editable,
2773
- hint: metadata.description,
2774
- label: metadata.label ?? "",
2775
- name: field.name,
2776
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2777
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2778
- schemas,
2779
- components: components?.schemas ?? {}
2780
- }),
2781
- placeholder: metadata.placeholder ?? "",
2782
- required: attribute.required ?? false,
2783
- size: field.size,
2784
- unique: "unique" in attribute ? attribute.unique : false,
2785
- visible: metadata.visible ?? true,
2786
- type: attribute.type
2787
- };
2788
- }).filter((field) => field !== null)
2789
- );
3020
+ DeleteAction$1.type = "delete";
3021
+ DeleteAction$1.position = ["header", "table-row"];
3022
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
3023
+ const Panels = () => {
3024
+ const isCloning = useMatch(CLONE_PATH) !== null;
3025
+ const [
3026
+ {
3027
+ query: { status }
3028
+ }
3029
+ ] = useQueryParams({
3030
+ status: "draft"
3031
+ });
3032
+ const { model, id, document, meta, collectionType } = useDoc();
3033
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
3034
+ const props = {
3035
+ activeTab: status,
3036
+ model,
3037
+ documentId: id,
3038
+ document: isCloning ? void 0 : document,
3039
+ meta: isCloning ? void 0 : meta,
3040
+ collectionType
3041
+ };
3042
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
3043
+ DescriptionComponentRenderer,
3044
+ {
3045
+ props,
3046
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
3047
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
3048
+ }
3049
+ ) });
2790
3050
  };
2791
- const formatListLayout = (data, {
2792
- schemas,
2793
- schema,
2794
- components
2795
- }) => {
2796
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2797
- (acc, [attribute, metadata]) => {
2798
- return {
2799
- ...acc,
2800
- [attribute]: metadata.list
2801
- };
2802
- },
2803
- {}
2804
- );
2805
- const listAttributes = convertListLayoutToFieldLayouts(
2806
- data.contentType.layouts.list,
2807
- schema?.attributes,
2808
- listMetadatas,
2809
- { configurations: data.components, schemas: components },
2810
- schemas
2811
- );
3051
+ const ActionsPanel = () => {
3052
+ const { formatMessage } = useIntl();
2812
3053
  return {
2813
- layout: listAttributes,
2814
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2815
- metadatas: listMetadatas,
2816
- options: {
2817
- ...schema?.options,
2818
- ...schema?.pluginOptions,
2819
- ...data.contentType.options
2820
- }
3054
+ title: formatMessage({
3055
+ id: "content-manager.containers.edit.panels.default.title",
3056
+ defaultMessage: "Entry"
3057
+ }),
3058
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2821
3059
  };
2822
3060
  };
2823
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2824
- return columns.map((name) => {
2825
- const attribute = attributes[name];
2826
- if (!attribute) {
2827
- return null;
3061
+ ActionsPanel.type = "actions";
3062
+ const ActionsPanelContent = () => {
3063
+ const isCloning = useMatch(CLONE_PATH) !== null;
3064
+ const [
3065
+ {
3066
+ query: { status = "draft" }
2828
3067
  }
2829
- const metadata = metadatas[name];
2830
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2831
- return {
2832
- attribute,
2833
- label: metadata.label ?? "",
2834
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2835
- schemas,
2836
- components: components?.schemas ?? {}
2837
- }),
2838
- name,
2839
- searchable: metadata.searchable ?? true,
2840
- sortable: metadata.sortable ?? true
2841
- };
2842
- }).filter((field) => field !== null);
3068
+ ] = useQueryParams();
3069
+ const { model, id, document, meta, collectionType } = useDoc();
3070
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
3071
+ const props = {
3072
+ activeTab: status,
3073
+ model,
3074
+ documentId: id,
3075
+ document: isCloning ? void 0 : document,
3076
+ meta: isCloning ? void 0 : meta,
3077
+ collectionType
3078
+ };
3079
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
3080
+ /* @__PURE__ */ jsx(
3081
+ DescriptionComponentRenderer,
3082
+ {
3083
+ props,
3084
+ descriptions: plugins["content-manager"].apis.getDocumentActions("panel"),
3085
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
3086
+ }
3087
+ ),
3088
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
3089
+ ] });
2843
3090
  };
3091
+ const Panel = React.forwardRef(({ children, title }, ref) => {
3092
+ return /* @__PURE__ */ jsxs(
3093
+ Flex,
3094
+ {
3095
+ ref,
3096
+ tag: "aside",
3097
+ "aria-labelledby": "additional-information",
3098
+ background: "neutral0",
3099
+ borderColor: "neutral150",
3100
+ hasRadius: true,
3101
+ paddingBottom: 4,
3102
+ paddingLeft: 4,
3103
+ paddingRight: 4,
3104
+ paddingTop: 4,
3105
+ shadow: "tableShadow",
3106
+ gap: 3,
3107
+ direction: "column",
3108
+ justifyContent: "stretch",
3109
+ alignItems: "flex-start",
3110
+ children: [
3111
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3112
+ children
3113
+ ]
3114
+ }
3115
+ );
3116
+ });
2844
3117
  const ConfirmBulkActionDialog = ({
2845
3118
  onToggleDialog,
2846
3119
  isOpen = false,
@@ -2879,6 +3152,7 @@ const ConfirmDialogPublishAll = ({
2879
3152
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2880
3153
  const { model, schema } = useDoc();
2881
3154
  const [{ query }] = useQueryParams();
3155
+ const enableDraftRelationsCount = false;
2882
3156
  const {
2883
3157
  data: countDraftRelations = 0,
2884
3158
  isLoading,
@@ -2890,7 +3164,7 @@ const ConfirmDialogPublishAll = ({
2890
3164
  locale: query?.plugins?.i18n?.locale
2891
3165
  },
2892
3166
  {
2893
- skip: selectedEntries.length === 0
3167
+ skip: !enableDraftRelationsCount
2894
3168
  }
2895
3169
  );
2896
3170
  React.useEffect(() => {
@@ -3075,7 +3349,7 @@ const SelectedEntriesTableContent = ({
3075
3349
  status: row.status
3076
3350
  }
3077
3351
  ) }),
3078
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3352
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3079
3353
  IconButton,
3080
3354
  {
3081
3355
  tag: Link,
@@ -3084,23 +3358,16 @@ const SelectedEntriesTableContent = ({
3084
3358
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3085
3359
  },
3086
3360
  state: { from: pathname },
3087
- label: formatMessage(
3088
- { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3089
- {
3090
- target: formatMessage(
3091
- {
3092
- id: "content-manager.components.ListViewHelperPluginTable.row-line",
3093
- defaultMessage: "item line {number}"
3094
- },
3095
- { number: index2 + 1 }
3096
- )
3097
- }
3098
- ),
3361
+ label: formatMessage({
3362
+ id: "content-manager.bulk-publish.edit",
3363
+ defaultMessage: "Edit"
3364
+ }),
3099
3365
  target: "_blank",
3100
3366
  marginLeft: "auto",
3101
- children: /* @__PURE__ */ jsx(Pencil, {})
3367
+ variant: "ghost",
3368
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3102
3369
  }
3103
- ) })
3370
+ ) }) })
3104
3371
  ] }, row.id)) })
3105
3372
  ] });
3106
3373
  };
@@ -3137,7 +3404,13 @@ const SelectedEntriesModalContent = ({
3137
3404
  );
3138
3405
  const { rows, validationErrors } = React.useMemo(() => {
3139
3406
  if (data.length > 0 && schema) {
3140
- const validate = createYupSchema(schema.attributes, components);
3407
+ const validate = createYupSchema(
3408
+ schema.attributes,
3409
+ components,
3410
+ // Since this is the "Publish" action, the validation
3411
+ // schema must enforce the rules for published entities
3412
+ { status: "published" }
3413
+ );
3141
3414
  const validationErrors2 = {};
3142
3415
  const rows2 = data.map((entry) => {
3143
3416
  try {
@@ -3262,8 +3535,7 @@ const PublishAction = ({ documents, model }) => {
3262
3535
  const refetchList = () => {
3263
3536
  contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3264
3537
  };
3265
- if (!showPublishButton)
3266
- return null;
3538
+ if (!showPublishButton) return null;
3267
3539
  return {
3268
3540
  actionType: "publish",
3269
3541
  variant: "tertiary",
@@ -3331,8 +3603,7 @@ const DeleteAction = ({ documents, model }) => {
3331
3603
  selectRow([]);
3332
3604
  }
3333
3605
  };
3334
- if (!hasDeletePermission)
3335
- return null;
3606
+ if (!hasDeletePermission) return null;
3336
3607
  return {
3337
3608
  variant: "danger-light",
3338
3609
  label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
@@ -3381,8 +3652,7 @@ const UnpublishAction = ({ documents, model }) => {
3381
3652
  }
3382
3653
  };
3383
3654
  const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3384
- if (!showUnpublishButton)
3385
- return null;
3655
+ if (!showUnpublishButton) return null;
3386
3656
  return {
3387
3657
  variant: "tertiary",
3388
3658
  label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
@@ -3487,7 +3757,7 @@ const TableActions = ({ document }) => {
3487
3757
  DescriptionComponentRenderer,
3488
3758
  {
3489
3759
  props,
3490
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3760
+ descriptions: plugins["content-manager"].apis.getDocumentActions("table-row").filter((action) => action.name !== "PublishAction"),
3491
3761
  children: (actions2) => {
3492
3762
  const tableRowActions = actions2.filter((action) => {
3493
3763
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3546,6 +3816,7 @@ const EditAction = ({ documentId }) => {
3546
3816
  };
3547
3817
  };
3548
3818
  EditAction.type = "edit";
3819
+ EditAction.position = "table-row";
3549
3820
  const StyledPencil = styled(Pencil)`
3550
3821
  path {
3551
3822
  fill: currentColor;
@@ -3598,7 +3869,7 @@ const CloneAction = ({ model, documentId }) => {
3598
3869
  }),
3599
3870
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3600
3871
  footer: ({ onClose }) => {
3601
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3872
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3602
3873
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3603
3874
  id: "cancel",
3604
3875
  defaultMessage: "Cancel"
@@ -3622,6 +3893,7 @@ const CloneAction = ({ model, documentId }) => {
3622
3893
  };
3623
3894
  };
3624
3895
  CloneAction.type = "clone";
3896
+ CloneAction.position = "table-row";
3625
3897
  const StyledDuplicate = styled(Duplicate)`
3626
3898
  path {
3627
3899
  fill: currentColor;
@@ -3708,7 +3980,14 @@ class ContentManagerPlugin {
3708
3980
  addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3709
3981
  addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3710
3982
  getBulkActions: () => this.bulkActions,
3711
- getDocumentActions: () => this.documentActions,
3983
+ getDocumentActions: (position) => {
3984
+ if (position) {
3985
+ return this.documentActions.filter(
3986
+ (action) => action.position == void 0 || [action.position].flat().includes(position)
3987
+ );
3988
+ }
3989
+ return this.documentActions;
3990
+ },
3712
3991
  getEditViewSidePanels: () => this.editViewSidePanels,
3713
3992
  getHeaderActions: () => this.headerActions
3714
3993
  }
@@ -3718,10 +3997,8 @@ class ContentManagerPlugin {
3718
3997
  const getPrintableType = (value) => {
3719
3998
  const nativeType = typeof value;
3720
3999
  if (nativeType === "object") {
3721
- if (value === null)
3722
- return "null";
3723
- if (Array.isArray(value))
3724
- return "array";
4000
+ if (value === null) return "null";
4001
+ if (Array.isArray(value)) return "array";
3725
4002
  if (value instanceof Object && value.constructor.name !== "Object") {
3726
4003
  return value.constructor.name;
3727
4004
  }
@@ -3732,17 +4009,27 @@ const HistoryAction = ({ model, document }) => {
3732
4009
  const { formatMessage } = useIntl();
3733
4010
  const [{ query }] = useQueryParams();
3734
4011
  const navigate = useNavigate();
4012
+ const { trackUsage } = useTracking();
4013
+ const { pathname } = useLocation();
3735
4014
  const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
3736
4015
  if (!window.strapi.features.isEnabled("cms-content-history")) {
3737
4016
  return null;
3738
4017
  }
4018
+ const handleOnClick = () => {
4019
+ const destination = { pathname: "history", search: pluginsQueryParams };
4020
+ trackUsage("willNavigate", {
4021
+ from: pathname,
4022
+ to: `${pathname}/${destination.pathname}`
4023
+ });
4024
+ navigate(destination);
4025
+ };
3739
4026
  return {
3740
4027
  icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
3741
4028
  label: formatMessage({
3742
4029
  id: "content-manager.history.document-action",
3743
4030
  defaultMessage: "Content History"
3744
4031
  }),
3745
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
4032
+ onClick: handleOnClick,
3746
4033
  disabled: (
3747
4034
  /**
3748
4035
  * The user is creating a new document.
@@ -3764,6 +4051,7 @@ const HistoryAction = ({ model, document }) => {
3764
4051
  };
3765
4052
  };
3766
4053
  HistoryAction.type = "history";
4054
+ HistoryAction.position = "header";
3767
4055
  const historyAdmin = {
3768
4056
  bootstrap(app) {
3769
4057
  const { addDocumentAction } = app.getPlugin("content-manager").apis;
@@ -3810,6 +4098,88 @@ const { setInitialData } = actions;
3810
4098
  const reducer = combineReducers({
3811
4099
  app: reducer$1
3812
4100
  });
4101
+ const previewApi = contentManagerApi.injectEndpoints({
4102
+ endpoints: (builder) => ({
4103
+ getPreviewUrl: builder.query({
4104
+ query({ query, params }) {
4105
+ return {
4106
+ url: `/content-manager/preview/url/${params.contentType}`,
4107
+ method: "GET",
4108
+ config: {
4109
+ params: query
4110
+ }
4111
+ };
4112
+ }
4113
+ })
4114
+ })
4115
+ });
4116
+ const { useGetPreviewUrlQuery } = previewApi;
4117
+ const ConditionalTooltip = ({ isShown, label, children }) => {
4118
+ if (isShown) {
4119
+ return /* @__PURE__ */ jsx(Tooltip, { label, children });
4120
+ }
4121
+ return children;
4122
+ };
4123
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4124
+ const { formatMessage } = useIntl();
4125
+ const { trackUsage } = useTracking();
4126
+ const { pathname } = useLocation();
4127
+ const [{ query }] = useQueryParams();
4128
+ const isModified = useForm("PreviewSidePanel", (state) => state.modified);
4129
+ const { data, error } = useGetPreviewUrlQuery({
4130
+ params: {
4131
+ contentType: model
4132
+ },
4133
+ query: {
4134
+ documentId,
4135
+ locale: document?.locale,
4136
+ status: document?.status
4137
+ }
4138
+ });
4139
+ if (!data?.data?.url || error) {
4140
+ return null;
4141
+ }
4142
+ const trackNavigation = () => {
4143
+ const destinationPathname = pathname.replace(/\/$/, "") + "/preview";
4144
+ trackUsage("willNavigate", { from: pathname, to: destinationPathname });
4145
+ };
4146
+ return {
4147
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4148
+ content: /* @__PURE__ */ jsx(
4149
+ ConditionalTooltip,
4150
+ {
4151
+ label: formatMessage({
4152
+ id: "content-manager.preview.panel.button-disabled-tooltip",
4153
+ defaultMessage: "Please save to open the preview"
4154
+ }),
4155
+ isShown: isModified,
4156
+ children: /* @__PURE__ */ jsx(Box, { cursor: "not-allowed", width: "100%", children: /* @__PURE__ */ jsx(
4157
+ Button,
4158
+ {
4159
+ variant: "tertiary",
4160
+ tag: Link,
4161
+ to: { pathname: "preview", search: stringify(query, { encode: false }) },
4162
+ onClick: trackNavigation,
4163
+ width: "100%",
4164
+ disabled: isModified,
4165
+ pointerEvents: isModified ? "none" : void 0,
4166
+ tabIndex: isModified ? -1 : void 0,
4167
+ children: formatMessage({
4168
+ id: "content-manager.preview.panel.button",
4169
+ defaultMessage: "Open preview"
4170
+ })
4171
+ }
4172
+ ) })
4173
+ }
4174
+ )
4175
+ };
4176
+ };
4177
+ const previewAdmin = {
4178
+ bootstrap(app) {
4179
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4180
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4181
+ }
4182
+ };
3813
4183
  const index = {
3814
4184
  register(app) {
3815
4185
  const cm = new ContentManagerPlugin();
@@ -3829,7 +4199,7 @@ const index = {
3829
4199
  app.router.addRoute({
3830
4200
  path: "content-manager/*",
3831
4201
  lazy: async () => {
3832
- const { Layout } = await import("./layout-DPaHUusj.mjs");
4202
+ const { Layout } = await import("./layout-COzAvgJh.mjs");
3833
4203
  return {
3834
4204
  Component: Layout
3835
4205
  };
@@ -3842,11 +4212,14 @@ const index = {
3842
4212
  if (typeof historyAdmin.bootstrap === "function") {
3843
4213
  historyAdmin.bootstrap(app);
3844
4214
  }
4215
+ if (typeof previewAdmin.bootstrap === "function") {
4216
+ previewAdmin.bootstrap(app);
4217
+ }
3845
4218
  },
3846
4219
  async registerTrads({ locales }) {
3847
4220
  const importedTrads = await Promise.all(
3848
4221
  locales.map((locale) => {
3849
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-BrCTWlZv.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
4222
+ 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-CSxLmrh1.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 }) => {
3850
4223
  return {
3851
4224
  data: prefixPluginTranslations(data, PLUGIN_ID),
3852
4225
  locale
@@ -3867,13 +4240,16 @@ export {
3867
4240
  BulkActionsRenderer as B,
3868
4241
  COLLECTION_TYPES as C,
3869
4242
  DocumentStatus as D,
3870
- DEFAULT_SETTINGS as E,
3871
- convertEditLayoutToFieldLayouts as F,
3872
- useDocument as G,
4243
+ extractContentTypeComponents as E,
4244
+ DEFAULT_SETTINGS as F,
4245
+ convertEditLayoutToFieldLayouts as G,
3873
4246
  HOOKS as H,
3874
4247
  InjectionZone as I,
3875
- index as J,
3876
- useDocumentActions as K,
4248
+ useDocument as J,
4249
+ useGetPreviewUrlQuery as K,
4250
+ index as L,
4251
+ useContentManagerContext as M,
4252
+ useDocumentActions as N,
3877
4253
  Panels as P,
3878
4254
  RelativeTime as R,
3879
4255
  SINGLE_TYPES as S,
@@ -3891,18 +4267,18 @@ export {
3891
4267
  PERMISSIONS as k,
3892
4268
  DocumentRBAC as l,
3893
4269
  DOCUMENT_META_FIELDS as m,
3894
- useDocLayout as n,
3895
- useGetContentTypeConfigurationQuery as o,
3896
- CREATOR_FIELDS as p,
3897
- getMainField as q,
3898
- getDisplayName as r,
4270
+ CLONE_PATH as n,
4271
+ useDocLayout as o,
4272
+ useGetContentTypeConfigurationQuery as p,
4273
+ CREATOR_FIELDS as q,
4274
+ getMainField as r,
3899
4275
  setInitialData as s,
3900
- checkIfAttributeIsDisplayable as t,
4276
+ getDisplayName as t,
3901
4277
  useContentTypeSchema as u,
3902
- useGetAllDocumentsQuery as v,
3903
- convertListLayoutToFieldLayouts as w,
3904
- capitalise as x,
3905
- useUpdateContentTypeConfigurationMutation as y,
3906
- extractContentTypeComponents as z
4278
+ checkIfAttributeIsDisplayable as v,
4279
+ useGetAllDocumentsQuery as w,
4280
+ convertListLayoutToFieldLayouts as x,
4281
+ capitalise as y,
4282
+ useUpdateContentTypeConfigurationMutation as z
3907
4283
  };
3908
- //# sourceMappingURL=index-BSn97i8U.mjs.map
4284
+ //# sourceMappingURL=index-Bu_-B7ZA.mjs.map