@strapi/content-manager 0.0.0-experimental.32c4b04580cc12400710050c8198e46b3644cfd4 → 0.0.0-experimental.332a7d5b6b1d23635d7e205657f0ff39ec133c9e

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 (215) 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-BAgyHiMm.mjs → ComponentConfigurationPage-D4H-v0et.mjs} +4 -4
  4. package/dist/_chunks/{ComponentConfigurationPage-BAgyHiMm.mjs.map → ComponentConfigurationPage-D4H-v0et.mjs.map} +1 -1
  5. package/dist/_chunks/{ComponentConfigurationPage-5ukroXAh.js → ComponentConfigurationPage-DdkVGfXC.js} +5 -6
  6. package/dist/_chunks/{ComponentConfigurationPage-5ukroXAh.js.map → ComponentConfigurationPage-DdkVGfXC.js.map} +1 -1
  7. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js → ComponentIcon-CRbtQEUV.js} +2 -3
  8. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js.map → ComponentIcon-CRbtQEUV.js.map} +1 -1
  9. package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -1
  10. package/dist/_chunks/{EditConfigurationPage-DmoXawIh.mjs → EditConfigurationPage-D1nvB7Br.mjs} +4 -4
  11. package/dist/_chunks/{EditConfigurationPage-DmoXawIh.mjs.map → EditConfigurationPage-D1nvB7Br.mjs.map} +1 -1
  12. package/dist/_chunks/{EditConfigurationPage-Xp7lun0f.js → EditConfigurationPage-LYEvR4fW.js} +5 -6
  13. package/dist/_chunks/{EditConfigurationPage-Xp7lun0f.js.map → EditConfigurationPage-LYEvR4fW.js.map} +1 -1
  14. package/dist/_chunks/{EditViewPage-BLsjc5F-.mjs → EditViewPage-Bcnff6Vd.mjs} +34 -46
  15. package/dist/_chunks/EditViewPage-Bcnff6Vd.mjs.map +1 -0
  16. package/dist/_chunks/{EditViewPage-C-ukDOB7.js → EditViewPage-DqelJ9UK.js} +36 -49
  17. package/dist/_chunks/EditViewPage-DqelJ9UK.js.map +1 -0
  18. package/dist/_chunks/FieldTypeIcon-CMlNO8PE.mjs.map +1 -1
  19. package/dist/_chunks/FieldTypeIcon-Dnwq_IRF.js.map +1 -1
  20. package/dist/_chunks/{Form-CPYqIWDG.js → Form-CnHfBeiB.js} +39 -21
  21. package/dist/_chunks/Form-CnHfBeiB.js.map +1 -0
  22. package/dist/_chunks/{Form-Dg_GS5TQ.mjs → Form-CzPCJi3B.mjs} +37 -18
  23. package/dist/_chunks/Form-CzPCJi3B.mjs.map +1 -0
  24. package/dist/_chunks/{History-wrnHqf09.mjs → History-CcmSn3Mj.mjs} +71 -104
  25. package/dist/_chunks/History-CcmSn3Mj.mjs.map +1 -0
  26. package/dist/_chunks/{History-DNQkXANT.js → History-zArjENzj.js} +81 -115
  27. package/dist/_chunks/History-zArjENzj.js.map +1 -0
  28. package/dist/_chunks/{Field-Bfph5SOd.js → Input-CDHKQd7o.js} +1334 -1239
  29. package/dist/_chunks/Input-CDHKQd7o.js.map +1 -0
  30. package/dist/_chunks/{Field-Cs7duwWd.mjs → Input-aV8SSoTa.mjs} +1192 -1097
  31. package/dist/_chunks/Input-aV8SSoTa.mjs.map +1 -0
  32. package/dist/_chunks/{ListConfigurationPage-DScmJVkW.mjs → ListConfigurationPage-BPvzENJJ.mjs} +19 -8
  33. package/dist/_chunks/ListConfigurationPage-BPvzENJJ.mjs.map +1 -0
  34. package/dist/_chunks/{ListConfigurationPage-CUQxfpjT.js → ListConfigurationPage-ByZAO_9H.js} +19 -9
  35. package/dist/_chunks/ListConfigurationPage-ByZAO_9H.js.map +1 -0
  36. package/dist/_chunks/{ListViewPage-BsLiH2-2.js → ListViewPage-BVKBeQAA.js} +108 -74
  37. package/dist/_chunks/ListViewPage-BVKBeQAA.js.map +1 -0
  38. package/dist/_chunks/{ListViewPage-C4IvrMgY.mjs → ListViewPage-HljQVnFH.mjs} +109 -74
  39. package/dist/_chunks/ListViewPage-HljQVnFH.mjs.map +1 -0
  40. package/dist/_chunks/{NoContentTypePage-BZ-PnGAf.js → NoContentTypePage-BV5zfDxr.js} +2 -2
  41. package/dist/_chunks/{NoContentTypePage-BZ-PnGAf.js.map → NoContentTypePage-BV5zfDxr.js.map} +1 -1
  42. package/dist/_chunks/{NoContentTypePage-Djg8nPlj.mjs → NoContentTypePage-BfHaSM-K.mjs} +2 -2
  43. package/dist/_chunks/{NoContentTypePage-Djg8nPlj.mjs.map → NoContentTypePage-BfHaSM-K.mjs.map} +1 -1
  44. package/dist/_chunks/{NoPermissionsPage-DSP7R-hv.mjs → NoPermissionsPage-D6ze2nQL.mjs} +2 -2
  45. package/dist/_chunks/{NoPermissionsPage-DSP7R-hv.mjs.map → NoPermissionsPage-D6ze2nQL.mjs.map} +1 -1
  46. package/dist/_chunks/{NoPermissionsPage-_lUqjGW3.js → NoPermissionsPage-vdNpc6jb.js} +2 -2
  47. package/dist/_chunks/{NoPermissionsPage-_lUqjGW3.js.map → NoPermissionsPage-vdNpc6jb.js.map} +1 -1
  48. package/dist/_chunks/Preview-DEHdENT1.js +305 -0
  49. package/dist/_chunks/Preview-DEHdENT1.js.map +1 -0
  50. package/dist/_chunks/Preview-vfWOtPG5.mjs +287 -0
  51. package/dist/_chunks/Preview-vfWOtPG5.mjs.map +1 -0
  52. package/dist/_chunks/{Relations-BZr8tL0R.mjs → Relations-B7_hbF0w.mjs} +79 -44
  53. package/dist/_chunks/Relations-B7_hbF0w.mjs.map +1 -0
  54. package/dist/_chunks/{Relations-CtELXYIK.js → Relations-DcoOBejP.js} +79 -45
  55. package/dist/_chunks/Relations-DcoOBejP.js.map +1 -0
  56. package/dist/_chunks/{en-uOUIxfcQ.js → en-BR48D_RH.js} +35 -15
  57. package/dist/_chunks/{en-uOUIxfcQ.js.map → en-BR48D_RH.js.map} +1 -1
  58. package/dist/_chunks/{en-BrCTWlZv.mjs → en-D65uIF6Y.mjs} +35 -15
  59. package/dist/_chunks/{en-BrCTWlZv.mjs.map → en-D65uIF6Y.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-B7kGGg3E.js → fr-C43IbhA_.js} +16 -3
  65. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-C43IbhA_.js.map} +1 -1
  66. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr-DBseuRuB.mjs} +16 -3
  67. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr-DBseuRuB.mjs.map} +1 -1
  68. package/dist/_chunks/hooks-BAaaKPS_.js.map +1 -1
  69. package/dist/_chunks/{index-OerGjbAN.js → index-CxLSGwnk.js} +1304 -750
  70. package/dist/_chunks/index-CxLSGwnk.js.map +1 -0
  71. package/dist/_chunks/{index-c_5DdJi-.mjs → index-EH8ZtHd5.mjs} +1323 -769
  72. package/dist/_chunks/index-EH8ZtHd5.mjs.map +1 -0
  73. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  74. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  75. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  76. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  77. package/dist/_chunks/{layout-oPBiO7RY.mjs → layout-CxDMdJ13.mjs} +23 -10
  78. package/dist/_chunks/layout-CxDMdJ13.mjs.map +1 -0
  79. package/dist/_chunks/{layout-Ci7qHlFb.js → layout-DSeUTfMv.js} +23 -11
  80. package/dist/_chunks/layout-DSeUTfMv.js.map +1 -0
  81. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  82. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  83. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  84. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  85. package/dist/_chunks/{relations-BIdWFjdq.mjs → relations-B8_Uu38Q.mjs} +21 -8
  86. package/dist/_chunks/relations-B8_Uu38Q.mjs.map +1 -0
  87. package/dist/_chunks/{relations-COBpStiF.js → relations-S5nNKdN3.js} +20 -7
  88. package/dist/_chunks/relations-S5nNKdN3.js.map +1 -0
  89. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js → useDragAndDrop-BMtgCYzL.js} +5 -9
  90. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js.map → useDragAndDrop-BMtgCYzL.js.map} +1 -1
  91. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs → useDragAndDrop-DJ6jqvZN.mjs} +4 -7
  92. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs.map → useDragAndDrop-DJ6jqvZN.mjs.map} +1 -1
  93. package/dist/_chunks/usePrev-B9w_-eYc.js.map +1 -1
  94. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +1 -1
  95. package/dist/admin/index.js +3 -1
  96. package/dist/admin/index.js.map +1 -1
  97. package/dist/admin/index.mjs +6 -4
  98. package/dist/admin/src/content-manager.d.ts +3 -2
  99. package/dist/admin/src/exports.d.ts +2 -1
  100. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  101. package/dist/admin/src/hooks/useDocument.d.ts +49 -1
  102. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  103. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -1
  104. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +3 -3
  105. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Code.d.ts +7 -0
  106. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/utils/prismLanguages.d.ts +49 -0
  107. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +1 -0
  108. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.d.ts +4 -1
  109. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +4 -1
  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/FormLayout.d.ts +27 -0
  114. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  115. package/dist/admin/src/pages/EditView/utils/data.d.ts +1 -0
  116. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  117. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  118. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  119. package/dist/admin/src/preview/index.d.ts +4 -0
  120. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  121. package/dist/admin/src/preview/routes.d.ts +3 -0
  122. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  123. package/dist/admin/src/router.d.ts +1 -1
  124. package/dist/admin/src/services/api.d.ts +1 -1
  125. package/dist/admin/src/services/components.d.ts +2 -2
  126. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  127. package/dist/admin/src/services/documents.d.ts +19 -20
  128. package/dist/admin/src/services/init.d.ts +1 -1
  129. package/dist/admin/src/services/relations.d.ts +2 -2
  130. package/dist/admin/src/services/uid.d.ts +3 -3
  131. package/dist/admin/src/utils/validation.d.ts +4 -1
  132. package/dist/server/index.js +727 -406
  133. package/dist/server/index.js.map +1 -1
  134. package/dist/server/index.mjs +728 -406
  135. package/dist/server/index.mjs.map +1 -1
  136. package/dist/server/src/bootstrap.d.ts.map +1 -1
  137. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  138. package/dist/server/src/controllers/index.d.ts.map +1 -1
  139. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  140. package/dist/server/src/controllers/utils/metadata.d.ts +16 -1
  141. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  142. package/dist/server/src/history/controllers/history-version.d.ts +1 -1
  143. package/dist/server/src/history/controllers/history-version.d.ts.map +1 -1
  144. package/dist/server/src/history/services/history.d.ts +3 -3
  145. package/dist/server/src/history/services/history.d.ts.map +1 -1
  146. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  147. package/dist/server/src/history/services/utils.d.ts +8 -12
  148. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  149. package/dist/server/src/index.d.ts +7 -6
  150. package/dist/server/src/index.d.ts.map +1 -1
  151. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  152. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  153. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  154. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  155. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  156. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  157. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  158. package/dist/server/src/preview/index.d.ts +4 -0
  159. package/dist/server/src/preview/index.d.ts.map +1 -0
  160. package/dist/server/src/preview/routes/index.d.ts +8 -0
  161. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  162. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  163. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  164. package/dist/server/src/preview/services/index.d.ts +16 -0
  165. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  166. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  167. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  168. package/dist/server/src/preview/services/preview.d.ts +12 -0
  169. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  170. package/dist/server/src/preview/utils.d.ts +19 -0
  171. package/dist/server/src/preview/utils.d.ts.map +1 -0
  172. package/dist/server/src/register.d.ts.map +1 -1
  173. package/dist/server/src/routes/index.d.ts.map +1 -1
  174. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  175. package/dist/server/src/services/document-metadata.d.ts +12 -10
  176. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  177. package/dist/server/src/services/index.d.ts +7 -6
  178. package/dist/server/src/services/index.d.ts.map +1 -1
  179. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  180. package/dist/server/src/services/utils/populate.d.ts +2 -2
  181. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  182. package/dist/server/src/utils/index.d.ts +2 -0
  183. package/dist/server/src/utils/index.d.ts.map +1 -1
  184. package/dist/shared/contracts/collection-types.d.ts +3 -1
  185. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  186. package/dist/shared/contracts/index.d.ts +1 -0
  187. package/dist/shared/contracts/index.d.ts.map +1 -1
  188. package/dist/shared/contracts/preview.d.ts +27 -0
  189. package/dist/shared/contracts/preview.d.ts.map +1 -0
  190. package/dist/shared/index.js +4 -0
  191. package/dist/shared/index.js.map +1 -1
  192. package/dist/shared/index.mjs +4 -0
  193. package/dist/shared/index.mjs.map +1 -1
  194. package/package.json +17 -16
  195. package/dist/_chunks/EditViewPage-BLsjc5F-.mjs.map +0 -1
  196. package/dist/_chunks/EditViewPage-C-ukDOB7.js.map +0 -1
  197. package/dist/_chunks/Field-Bfph5SOd.js.map +0 -1
  198. package/dist/_chunks/Field-Cs7duwWd.mjs.map +0 -1
  199. package/dist/_chunks/Form-CPYqIWDG.js.map +0 -1
  200. package/dist/_chunks/Form-Dg_GS5TQ.mjs.map +0 -1
  201. package/dist/_chunks/History-DNQkXANT.js.map +0 -1
  202. package/dist/_chunks/History-wrnHqf09.mjs.map +0 -1
  203. package/dist/_chunks/ListConfigurationPage-CUQxfpjT.js.map +0 -1
  204. package/dist/_chunks/ListConfigurationPage-DScmJVkW.mjs.map +0 -1
  205. package/dist/_chunks/ListViewPage-BsLiH2-2.js.map +0 -1
  206. package/dist/_chunks/ListViewPage-C4IvrMgY.mjs.map +0 -1
  207. package/dist/_chunks/Relations-BZr8tL0R.mjs.map +0 -1
  208. package/dist/_chunks/Relations-CtELXYIK.js.map +0 -1
  209. package/dist/_chunks/index-OerGjbAN.js.map +0 -1
  210. package/dist/_chunks/index-c_5DdJi-.mjs.map +0 -1
  211. package/dist/_chunks/layout-Ci7qHlFb.js.map +0 -1
  212. package/dist/_chunks/layout-oPBiO7RY.mjs.map +0 -1
  213. package/dist/_chunks/relations-BIdWFjdq.mjs.map +0 -1
  214. package/dist/_chunks/relations-COBpStiF.js.map +0 -1
  215. package/strapi-server.js +0 -3
@@ -1,25 +1,34 @@
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, CheckCircle, ArrowsCounterClockwise, CrossCircle, 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, RawTable, Loader, Tbody, Tr, Td, 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 { generateNKeysBetween } from "fractional-indexing";
12
14
  import pipe from "lodash/fp/pipe";
13
- import { intervalToDuration, isPast } from "date-fns";
14
15
  import { stringify } from "qs";
16
+ import { intervalToDuration, isPast } from "date-fns";
15
17
  import { createSlice, combineReducers } from "@reduxjs/toolkit";
16
- const __variableDynamicImportRuntimeHelper = (glob, path) => {
18
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
17
19
  const v = glob[path];
18
20
  if (v) {
19
21
  return typeof v === "function" ? v() : Promise.resolve(v);
20
22
  }
21
23
  return new Promise((_, reject) => {
22
- (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(reject.bind(null, new Error("Unknown variable dynamic import: " + path)));
24
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
25
+ reject.bind(
26
+ null,
27
+ new Error(
28
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
29
+ )
30
+ )
31
+ );
23
32
  });
24
33
  };
25
34
  const PLUGIN_ID = "content-manager";
@@ -100,6 +109,7 @@ const DocumentRBAC = ({ children, permissions }) => {
100
109
  if (!slug) {
101
110
  throw new Error("Cannot find the slug param in the URL");
102
111
  }
112
+ const [{ rawQuery }] = useQueryParams();
103
113
  const userPermissions = useAuth("DocumentRBAC", (state) => state.permissions);
104
114
  const contentTypePermissions = React.useMemo(() => {
105
115
  const contentTypePermissions2 = userPermissions.filter(
@@ -110,7 +120,14 @@ const DocumentRBAC = ({ children, permissions }) => {
110
120
  return { ...acc, [action]: [permission] };
111
121
  }, {});
112
122
  }, [slug, userPermissions]);
113
- const { isLoading, allowedActions } = useRBAC(contentTypePermissions, permissions ?? void 0);
123
+ const { isLoading, allowedActions } = useRBAC(
124
+ contentTypePermissions,
125
+ permissions ?? void 0,
126
+ // TODO: useRBAC context should be typed and built differently
127
+ // We are passing raw query as context to the hook so that it can
128
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
129
+ rawQuery
130
+ );
114
131
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
115
132
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
116
133
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -150,6 +167,113 @@ const extractAndDedupeFields = (permissions = []) => permissions.flatMap((permis
150
167
  (field, index2, arr) => arr.indexOf(field) === index2 && typeof field === "string"
151
168
  );
152
169
  const removeNumericalStrings = (arr) => arr.filter((item) => isNaN(Number(item)));
170
+ const BLOCK_LIST_ATTRIBUTE_KEYS = ["__component", "__temp_key__"];
171
+ const traverseData = (predicate, transform) => (schema, components = {}) => (data = {}) => {
172
+ const traverse = (datum, attributes) => {
173
+ return Object.entries(datum).reduce((acc, [key, value]) => {
174
+ const attribute = attributes[key];
175
+ if (BLOCK_LIST_ATTRIBUTE_KEYS.includes(key) || value === null || value === void 0) {
176
+ acc[key] = value;
177
+ return acc;
178
+ }
179
+ if (attribute.type === "component") {
180
+ if (attribute.repeatable) {
181
+ const componentValue = predicate(attribute, value) ? transform(value, attribute) : value;
182
+ acc[key] = componentValue.map(
183
+ (componentData) => traverse(componentData, components[attribute.component]?.attributes ?? {})
184
+ );
185
+ } else {
186
+ const componentValue = predicate(attribute, value) ? transform(value, attribute) : value;
187
+ acc[key] = traverse(componentValue, components[attribute.component]?.attributes ?? {});
188
+ }
189
+ } else if (attribute.type === "dynamiczone") {
190
+ const dynamicZoneValue = predicate(attribute, value) ? transform(value, attribute) : value;
191
+ acc[key] = dynamicZoneValue.map(
192
+ (componentData) => traverse(componentData, components[componentData.__component]?.attributes ?? {})
193
+ );
194
+ } else if (predicate(attribute, value)) {
195
+ acc[key] = transform(value, attribute);
196
+ } else {
197
+ acc[key] = value;
198
+ }
199
+ return acc;
200
+ }, {});
201
+ };
202
+ return traverse(data, schema.attributes);
203
+ };
204
+ const removeProhibitedFields = (prohibitedFields) => traverseData(
205
+ (attribute) => prohibitedFields.includes(attribute.type),
206
+ () => ""
207
+ );
208
+ const prepareRelations = traverseData(
209
+ (attribute) => attribute.type === "relation",
210
+ () => ({
211
+ connect: [],
212
+ disconnect: []
213
+ })
214
+ );
215
+ const prepareTempKeys = traverseData(
216
+ (attribute) => attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone",
217
+ (data) => {
218
+ if (Array.isArray(data) && data.length > 0) {
219
+ const keys = generateNKeysBetween(void 0, void 0, data.length);
220
+ return data.map((datum, index2) => ({
221
+ ...datum,
222
+ __temp_key__: keys[index2]
223
+ }));
224
+ }
225
+ return data;
226
+ }
227
+ );
228
+ const removeFieldsThatDontExistOnSchema = (schema) => (data) => {
229
+ const schemaKeys = Object.keys(schema.attributes);
230
+ const dataKeys = Object.keys(data);
231
+ const keysToRemove = dataKeys.filter((key) => !schemaKeys.includes(key));
232
+ const revisedData = [...keysToRemove, ...DOCUMENT_META_FIELDS].reduce((acc, key) => {
233
+ delete acc[key];
234
+ return acc;
235
+ }, structuredClone(data));
236
+ return revisedData;
237
+ };
238
+ const removeNullValues = (data) => {
239
+ return Object.entries(data).reduce((acc, [key, value]) => {
240
+ if (value === null) {
241
+ return acc;
242
+ }
243
+ acc[key] = value;
244
+ return acc;
245
+ }, {});
246
+ };
247
+ const transformDocument = (schema, components = {}) => (document) => {
248
+ const transformations = pipe(
249
+ removeFieldsThatDontExistOnSchema(schema),
250
+ removeProhibitedFields(["password"])(schema, components),
251
+ removeNullValues,
252
+ prepareRelations(schema, components),
253
+ prepareTempKeys(schema, components)
254
+ );
255
+ return transformations(document);
256
+ };
257
+ const createDefaultForm = (contentType, components = {}) => {
258
+ const traverseSchema = (attributes) => {
259
+ return Object.entries(attributes).reduce((acc, [key, attribute]) => {
260
+ if ("default" in attribute) {
261
+ acc[key] = attribute.default;
262
+ } else if (attribute.type === "component" && attribute.required) {
263
+ const defaultComponentForm = traverseSchema(components[attribute.component].attributes);
264
+ if (attribute.repeatable) {
265
+ acc[key] = attribute.min ? [...Array(attribute.min).fill(defaultComponentForm)] : [];
266
+ } else {
267
+ acc[key] = defaultComponentForm;
268
+ }
269
+ } else if (attribute.type === "dynamiczone" && attribute.required) {
270
+ acc[key] = [];
271
+ }
272
+ return acc;
273
+ }, {});
274
+ };
275
+ return traverseSchema(contentType.attributes);
276
+ };
153
277
  const contentManagerApi = adminApi.enhanceEndpoints({
154
278
  addTagTypes: [
155
279
  "ComponentConfiguration",
@@ -158,7 +282,9 @@ const contentManagerApi = adminApi.enhanceEndpoints({
158
282
  "Document",
159
283
  "InitialData",
160
284
  "HistoryVersion",
161
- "Relations"
285
+ "Relations",
286
+ "UidAvailability",
287
+ "RecentDocumentList"
162
288
  ]
163
289
  });
164
290
  const documentApi = contentManagerApi.injectEndpoints({
@@ -176,7 +302,7 @@ const documentApi = contentManagerApi.injectEndpoints({
176
302
  if (error) {
177
303
  return [];
178
304
  }
179
- return [{ type: "Document", id: `${model}_LIST` }];
305
+ return [{ type: "Document", id: `${model}_LIST` }, "RecentDocumentList"];
180
306
  }
181
307
  }),
182
308
  cloneDocument: builder.mutation({
@@ -188,7 +314,11 @@ const documentApi = contentManagerApi.injectEndpoints({
188
314
  params
189
315
  }
190
316
  }),
191
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
317
+ invalidatesTags: (_result, _error, { model }) => [
318
+ { type: "Document", id: `${model}_LIST` },
319
+ { type: "UidAvailability", id: model },
320
+ "RecentDocumentList"
321
+ ]
192
322
  }),
193
323
  /**
194
324
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -205,8 +335,22 @@ const documentApi = contentManagerApi.injectEndpoints({
205
335
  }),
206
336
  invalidatesTags: (result, _error, { model }) => [
207
337
  { type: "Document", id: `${model}_LIST` },
208
- "Relations"
209
- ]
338
+ "Relations",
339
+ { type: "UidAvailability", id: model },
340
+ "RecentDocumentList"
341
+ ],
342
+ transformResponse: (response, meta, arg) => {
343
+ if (!("data" in response) && arg.model === "plugin::users-permissions.user") {
344
+ return {
345
+ data: response,
346
+ meta: {
347
+ availableStatus: [],
348
+ availableLocales: []
349
+ }
350
+ };
351
+ }
352
+ return response;
353
+ }
210
354
  }),
211
355
  deleteDocument: builder.mutation({
212
356
  query: ({ collectionType, model, documentId, params }) => ({
@@ -217,7 +361,8 @@ const documentApi = contentManagerApi.injectEndpoints({
217
361
  }
218
362
  }),
219
363
  invalidatesTags: (_result, _error, { collectionType, model }) => [
220
- { type: "Document", id: collectionType !== SINGLE_TYPES ? `${model}_LIST` : model }
364
+ { type: "Document", id: collectionType !== SINGLE_TYPES ? `${model}_LIST` : model },
365
+ "RecentDocumentList"
221
366
  ]
222
367
  }),
223
368
  deleteManyDocuments: builder.mutation({
@@ -229,7 +374,10 @@ const documentApi = contentManagerApi.injectEndpoints({
229
374
  params
230
375
  }
231
376
  }),
232
- invalidatesTags: (_res, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
377
+ invalidatesTags: (_res, _error, { model }) => [
378
+ { type: "Document", id: `${model}_LIST` },
379
+ "RecentDocumentList"
380
+ ]
233
381
  }),
234
382
  discardDocument: builder.mutation({
235
383
  query: ({ collectionType, model, documentId, params }) => ({
@@ -246,7 +394,9 @@ const documentApi = contentManagerApi.injectEndpoints({
246
394
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
247
395
  },
248
396
  { type: "Document", id: `${model}_LIST` },
249
- "Relations"
397
+ "Relations",
398
+ { type: "UidAvailability", id: model },
399
+ "RecentDocumentList"
250
400
  ];
251
401
  }
252
402
  }),
@@ -259,11 +409,12 @@ const documentApi = contentManagerApi.injectEndpoints({
259
409
  url: `/content-manager/collection-types/${model}`,
260
410
  method: "GET",
261
411
  config: {
262
- params
412
+ params: stringify(params, { encode: true })
263
413
  }
264
414
  }),
265
415
  providesTags: (result, _error, arg) => {
266
416
  return [
417
+ { type: "Document", id: `ALL_LIST` },
267
418
  { type: "Document", id: `${arg.model}_LIST` },
268
419
  ...result?.results.map(({ documentId }) => ({
269
420
  type: "Document",
@@ -302,6 +453,11 @@ const documentApi = contentManagerApi.injectEndpoints({
302
453
  {
303
454
  type: "Document",
304
455
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
456
+ },
457
+ // Make it easy to invalidate all individual documents queries for a model
458
+ {
459
+ type: "Document",
460
+ id: `${model}_ALL_ITEMS`
305
461
  }
306
462
  ];
307
463
  }
@@ -335,7 +491,8 @@ const documentApi = contentManagerApi.injectEndpoints({
335
491
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
336
492
  },
337
493
  { type: "Document", id: `${model}_LIST` },
338
- "Relations"
494
+ "Relations",
495
+ "RecentDocumentList"
339
496
  ];
340
497
  }
341
498
  }),
@@ -365,7 +522,10 @@ const documentApi = contentManagerApi.injectEndpoints({
365
522
  type: "Document",
366
523
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
367
524
  },
368
- "Relations"
525
+ "Relations",
526
+ { type: "UidAvailability", id: model },
527
+ "RecentDocumentList",
528
+ "RecentDocumentList"
369
529
  ];
370
530
  },
371
531
  async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
@@ -395,7 +555,8 @@ const documentApi = contentManagerApi.injectEndpoints({
395
555
  {
396
556
  type: "Document",
397
557
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
398
- }
558
+ },
559
+ "RecentDocumentList"
399
560
  ];
400
561
  }
401
562
  }),
@@ -408,7 +569,10 @@ const documentApi = contentManagerApi.injectEndpoints({
408
569
  params
409
570
  }
410
571
  }),
411
- invalidatesTags: (_res, _error, { model, documentIds }) => documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` }))
572
+ invalidatesTags: (_res, _error, { model, documentIds }) => [
573
+ ...documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` })),
574
+ "RecentDocumentList"
575
+ ]
412
576
  })
413
577
  })
414
578
  });
@@ -431,8 +595,7 @@ const {
431
595
  useUnpublishManyDocumentsMutation
432
596
  } = documentApi;
433
597
  const buildValidParams = (query) => {
434
- if (!query)
435
- return query;
598
+ if (!query) return query;
436
599
  const { plugins: _, ...validQueryParams } = {
437
600
  ...query,
438
601
  ...Object.values(query?.plugins ?? {}).reduce(
@@ -440,28 +603,44 @@ const buildValidParams = (query) => {
440
603
  {}
441
604
  )
442
605
  };
443
- if ("_q" in validQueryParams) {
444
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
445
- }
446
606
  return validQueryParams;
447
607
  };
448
608
  const isBaseQueryError = (error) => {
449
609
  return error.name !== void 0;
450
610
  };
451
- const createYupSchema = (attributes = {}, components = {}) => {
611
+ const arrayValidator = (attribute, options) => ({
612
+ message: translatedErrors.required,
613
+ test(value) {
614
+ if (options.status === "draft") {
615
+ return true;
616
+ }
617
+ if (!attribute.required) {
618
+ return true;
619
+ }
620
+ if (!value) {
621
+ return false;
622
+ }
623
+ if (Array.isArray(value) && value.length === 0) {
624
+ return false;
625
+ }
626
+ return true;
627
+ }
628
+ });
629
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
452
630
  const createModelSchema = (attributes2) => yup.object().shape(
453
631
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
454
632
  if (DOCUMENT_META_FIELDS.includes(name)) {
455
633
  return acc;
456
634
  }
457
635
  const validations = [
636
+ addNullableValidation,
458
637
  addRequiredValidation,
459
638
  addMinLengthValidation,
460
639
  addMaxLengthValidation,
461
640
  addMinValidation,
462
641
  addMaxValidation,
463
642
  addRegexValidation
464
- ].map((fn) => fn(attribute));
643
+ ].map((fn) => fn(attribute, options));
465
644
  const transformSchema = pipe(...validations);
466
645
  switch (attribute.type) {
467
646
  case "component": {
@@ -471,12 +650,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
471
650
  ...acc,
472
651
  [name]: transformSchema(
473
652
  yup.array().of(createModelSchema(attributes3).nullable(false))
474
- )
653
+ ).test(arrayValidator(attribute, options))
475
654
  };
476
655
  } else {
477
656
  return {
478
657
  ...acc,
479
- [name]: transformSchema(createModelSchema(attributes3))
658
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
480
659
  };
481
660
  }
482
661
  }
@@ -498,7 +677,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
498
677
  }
499
678
  )
500
679
  )
501
- )
680
+ ).test(arrayValidator(attribute, options))
502
681
  };
503
682
  case "relation":
504
683
  return {
@@ -510,7 +689,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
510
689
  } else if (Array.isArray(value)) {
511
690
  return yup.array().of(
512
691
  yup.object().shape({
513
- id: yup.string().required()
692
+ id: yup.number().required()
514
693
  })
515
694
  );
516
695
  } else if (typeof value === "object") {
@@ -562,6 +741,14 @@ const createAttributeSchema = (attribute) => {
562
741
  if (!value || typeof value === "string" && value.length === 0) {
563
742
  return true;
564
743
  }
744
+ if (typeof value === "object") {
745
+ try {
746
+ JSON.stringify(value);
747
+ return true;
748
+ } catch (err) {
749
+ return false;
750
+ }
751
+ }
565
752
  try {
566
753
  JSON.parse(value);
567
754
  return true;
@@ -580,13 +767,7 @@ const createAttributeSchema = (attribute) => {
580
767
  return yup.mixed();
581
768
  }
582
769
  };
583
- const addRequiredValidation = (attribute) => (schema) => {
584
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
585
- return schema.min(1, translatedErrors.required);
586
- }
587
- if (attribute.required && attribute.type !== "relation") {
588
- return schema.required(translatedErrors.required);
589
- }
770
+ const nullableSchema = (schema) => {
590
771
  return schema?.nullable ? schema.nullable() : (
591
772
  // In some cases '.nullable' will not be available on the schema.
592
773
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -594,7 +775,22 @@ const addRequiredValidation = (attribute) => (schema) => {
594
775
  schema
595
776
  );
596
777
  };
597
- const addMinLengthValidation = (attribute) => (schema) => {
778
+ const addNullableValidation = () => (schema) => {
779
+ return nullableSchema(schema);
780
+ };
781
+ const addRequiredValidation = (attribute, options) => (schema) => {
782
+ if (options.status === "draft" || !attribute.required) {
783
+ return schema;
784
+ }
785
+ if (attribute.required && "required" in schema) {
786
+ return schema.required(translatedErrors.required);
787
+ }
788
+ return schema;
789
+ };
790
+ const addMinLengthValidation = (attribute, options) => (schema) => {
791
+ if (options.status === "draft") {
792
+ return schema;
793
+ }
598
794
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
599
795
  return schema.min(attribute.minLength, {
600
796
  ...translatedErrors.minLength,
@@ -616,32 +812,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
616
812
  }
617
813
  return schema;
618
814
  };
619
- const addMinValidation = (attribute) => (schema) => {
620
- if ("min" in attribute) {
815
+ const addMinValidation = (attribute, options) => (schema) => {
816
+ if (options.status === "draft") {
817
+ return schema;
818
+ }
819
+ if ("min" in attribute && "min" in schema) {
621
820
  const min = toInteger(attribute.min);
622
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
623
- if (!attribute.required && "test" in schema && min) {
624
- return schema.test(
625
- "custom-min",
626
- {
627
- ...translatedErrors.min,
628
- values: {
629
- min: attribute.min
630
- }
631
- },
632
- (value) => {
633
- if (!value) {
634
- return true;
635
- }
636
- if (Array.isArray(value) && value.length === 0) {
637
- return true;
638
- }
639
- return value.length >= min;
640
- }
641
- );
642
- }
643
- }
644
- if ("min" in schema && min) {
821
+ if (min) {
645
822
  return schema.min(min, {
646
823
  ...translatedErrors.min,
647
824
  values: {
@@ -759,19 +936,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
759
936
  }, {});
760
937
  return componentsByKey;
761
938
  };
762
- const useDocument = (args, opts) => {
939
+ const HOOKS = {
940
+ /**
941
+ * Hook that allows to mutate the displayed headers of the list view table
942
+ * @constant
943
+ * @type {string}
944
+ */
945
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
946
+ /**
947
+ * Hook that allows to mutate the CM's collection types links pre-set filters
948
+ * @constant
949
+ * @type {string}
950
+ */
951
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
952
+ /**
953
+ * Hook that allows to mutate the CM's edit view layout
954
+ * @constant
955
+ * @type {string}
956
+ */
957
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
958
+ /**
959
+ * Hook that allows to mutate the CM's single types links pre-set filters
960
+ * @constant
961
+ * @type {string}
962
+ */
963
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
964
+ };
965
+ const contentTypesApi = contentManagerApi.injectEndpoints({
966
+ endpoints: (builder) => ({
967
+ getContentTypeConfiguration: builder.query({
968
+ query: (uid) => ({
969
+ url: `/content-manager/content-types/${uid}/configuration`,
970
+ method: "GET"
971
+ }),
972
+ transformResponse: (response) => response.data,
973
+ providesTags: (_result, _error, uid) => [
974
+ { type: "ContentTypesConfiguration", id: uid },
975
+ { type: "ContentTypeSettings", id: "LIST" }
976
+ ]
977
+ }),
978
+ getAllContentTypeSettings: builder.query({
979
+ query: () => "/content-manager/content-types-settings",
980
+ transformResponse: (response) => response.data,
981
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
982
+ }),
983
+ updateContentTypeConfiguration: builder.mutation({
984
+ query: ({ uid, ...body }) => ({
985
+ url: `/content-manager/content-types/${uid}/configuration`,
986
+ method: "PUT",
987
+ data: body
988
+ }),
989
+ transformResponse: (response) => response.data,
990
+ invalidatesTags: (_result, _error, { uid }) => [
991
+ { type: "ContentTypesConfiguration", id: uid },
992
+ { type: "ContentTypeSettings", id: "LIST" },
993
+ // Is this necessary?
994
+ { type: "InitialData" }
995
+ ]
996
+ })
997
+ })
998
+ });
999
+ const {
1000
+ useGetContentTypeConfigurationQuery,
1001
+ useGetAllContentTypeSettingsQuery,
1002
+ useUpdateContentTypeConfigurationMutation
1003
+ } = contentTypesApi;
1004
+ const checkIfAttributeIsDisplayable = (attribute) => {
1005
+ const { type } = attribute;
1006
+ if (type === "relation") {
1007
+ return !attribute.relation.toLowerCase().includes("morph");
1008
+ }
1009
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
1010
+ };
1011
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
1012
+ if (!mainFieldName) {
1013
+ return void 0;
1014
+ }
1015
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
1016
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
1017
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
1018
+ );
1019
+ return {
1020
+ name: mainFieldName,
1021
+ type: mainFieldType ?? "string"
1022
+ };
1023
+ };
1024
+ const DEFAULT_SETTINGS = {
1025
+ bulkable: false,
1026
+ filterable: false,
1027
+ searchable: false,
1028
+ pagination: false,
1029
+ defaultSortBy: "",
1030
+ defaultSortOrder: "asc",
1031
+ mainField: "id",
1032
+ pageSize: 10
1033
+ };
1034
+ const useDocumentLayout = (model) => {
1035
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
1036
+ const [{ query }] = useQueryParams();
1037
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
763
1038
  const { toggleNotification } = useNotification();
764
1039
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1040
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
765
1041
  const {
766
- currentData: data,
767
- isLoading: isLoadingDocument,
768
- isFetching: isFetchingDocument,
769
- error
770
- } = useGetDocumentQuery(args, {
771
- ...opts,
772
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
773
- });
774
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
1042
+ data,
1043
+ isLoading: isLoadingConfigs,
1044
+ error,
1045
+ isFetching: isFetchingConfigs
1046
+ } = useGetContentTypeConfigurationQuery(model);
1047
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
775
1048
  React.useEffect(() => {
776
1049
  if (error) {
777
1050
  toggleNotification({
@@ -779,40 +1052,284 @@ const useDocument = (args, opts) => {
779
1052
  message: formatAPIError(error)
780
1053
  });
781
1054
  }
782
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
783
- const validationSchema = React.useMemo(() => {
784
- if (!schema) {
785
- return null;
786
- }
787
- return createYupSchema(schema.attributes, components);
788
- }, [schema, components]);
789
- const validate = React.useCallback(
790
- (document) => {
791
- if (!validationSchema) {
792
- throw new Error(
793
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
794
- );
795
- }
796
- try {
797
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
798
- return null;
799
- } catch (error2) {
800
- if (error2 instanceof ValidationError) {
801
- return getYupValidationErrors(error2);
802
- }
803
- throw error2;
804
- }
1055
+ }, [error, formatAPIError, toggleNotification]);
1056
+ const editLayout = React.useMemo(
1057
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
1058
+ layout: [],
1059
+ components: {},
1060
+ metadatas: {},
1061
+ options: {},
1062
+ settings: DEFAULT_SETTINGS
805
1063
  },
806
- [validationSchema]
1064
+ [data, isLoading, schemas, schema, components]
1065
+ );
1066
+ const listLayout = React.useMemo(() => {
1067
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
1068
+ layout: [],
1069
+ metadatas: {},
1070
+ options: {},
1071
+ settings: DEFAULT_SETTINGS
1072
+ };
1073
+ }, [data, isLoading, schemas, schema, components]);
1074
+ const { layout: edit } = React.useMemo(
1075
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
1076
+ layout: editLayout,
1077
+ query
1078
+ }),
1079
+ [editLayout, query, runHookWaterfall]
807
1080
  );
808
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
809
1081
  return {
810
- components,
811
- document: data?.data,
812
- meta: data?.meta,
1082
+ error,
1083
+ isLoading,
1084
+ edit,
1085
+ list: listLayout
1086
+ };
1087
+ };
1088
+ const useDocLayout = () => {
1089
+ const { model } = useDoc();
1090
+ return useDocumentLayout(model);
1091
+ };
1092
+ const formatEditLayout = (data, {
1093
+ schemas,
1094
+ schema,
1095
+ components
1096
+ }) => {
1097
+ let currentPanelIndex = 0;
1098
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
1099
+ data.contentType.layouts.edit,
1100
+ schema?.attributes,
1101
+ data.contentType.metadatas,
1102
+ { configurations: data.components, schemas: components },
1103
+ schemas
1104
+ ).reduce((panels, row) => {
1105
+ if (row.some((field) => field.type === "dynamiczone")) {
1106
+ panels.push([row]);
1107
+ currentPanelIndex += 2;
1108
+ } else {
1109
+ if (!panels[currentPanelIndex]) {
1110
+ panels.push([row]);
1111
+ } else {
1112
+ panels[currentPanelIndex].push(row);
1113
+ }
1114
+ }
1115
+ return panels;
1116
+ }, []);
1117
+ const componentEditAttributes = Object.entries(data.components).reduce(
1118
+ (acc, [uid, configuration]) => {
1119
+ acc[uid] = {
1120
+ layout: convertEditLayoutToFieldLayouts(
1121
+ configuration.layouts.edit,
1122
+ components[uid].attributes,
1123
+ configuration.metadatas,
1124
+ { configurations: data.components, schemas: components }
1125
+ ),
1126
+ settings: {
1127
+ ...configuration.settings,
1128
+ icon: components[uid].info.icon,
1129
+ displayName: components[uid].info.displayName
1130
+ }
1131
+ };
1132
+ return acc;
1133
+ },
1134
+ {}
1135
+ );
1136
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1137
+ (acc, [attribute, metadata]) => {
1138
+ return {
1139
+ ...acc,
1140
+ [attribute]: metadata.edit
1141
+ };
1142
+ },
1143
+ {}
1144
+ );
1145
+ return {
1146
+ layout: panelledEditAttributes,
1147
+ components: componentEditAttributes,
1148
+ metadatas: editMetadatas,
1149
+ settings: {
1150
+ ...data.contentType.settings,
1151
+ displayName: schema?.info.displayName
1152
+ },
1153
+ options: {
1154
+ ...schema?.options,
1155
+ ...schema?.pluginOptions,
1156
+ ...data.contentType.options
1157
+ }
1158
+ };
1159
+ };
1160
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1161
+ return rows.map(
1162
+ (row) => row.map((field) => {
1163
+ const attribute = attributes[field.name];
1164
+ if (!attribute) {
1165
+ return null;
1166
+ }
1167
+ const { edit: metadata } = metadatas[field.name];
1168
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1169
+ return {
1170
+ attribute,
1171
+ disabled: !metadata.editable,
1172
+ hint: metadata.description,
1173
+ label: metadata.label ?? "",
1174
+ name: field.name,
1175
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1176
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1177
+ schemas,
1178
+ components: components?.schemas ?? {}
1179
+ }),
1180
+ placeholder: metadata.placeholder ?? "",
1181
+ required: attribute.required ?? false,
1182
+ size: field.size,
1183
+ unique: "unique" in attribute ? attribute.unique : false,
1184
+ visible: metadata.visible ?? true,
1185
+ type: attribute.type
1186
+ };
1187
+ }).filter((field) => field !== null)
1188
+ );
1189
+ };
1190
+ const formatListLayout = (data, {
1191
+ schemas,
1192
+ schema,
1193
+ components
1194
+ }) => {
1195
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1196
+ (acc, [attribute, metadata]) => {
1197
+ return {
1198
+ ...acc,
1199
+ [attribute]: metadata.list
1200
+ };
1201
+ },
1202
+ {}
1203
+ );
1204
+ const listAttributes = convertListLayoutToFieldLayouts(
1205
+ data.contentType.layouts.list,
1206
+ schema?.attributes,
1207
+ listMetadatas,
1208
+ { configurations: data.components, schemas: components },
1209
+ schemas
1210
+ );
1211
+ return {
1212
+ layout: listAttributes,
1213
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1214
+ metadatas: listMetadatas,
1215
+ options: {
1216
+ ...schema?.options,
1217
+ ...schema?.pluginOptions,
1218
+ ...data.contentType.options
1219
+ }
1220
+ };
1221
+ };
1222
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1223
+ return columns.map((name) => {
1224
+ const attribute = attributes[name];
1225
+ if (!attribute) {
1226
+ return null;
1227
+ }
1228
+ const metadata = metadatas[name];
1229
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1230
+ return {
1231
+ attribute,
1232
+ label: metadata.label ?? "",
1233
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1234
+ schemas,
1235
+ components: components?.schemas ?? {}
1236
+ }),
1237
+ name,
1238
+ searchable: metadata.searchable ?? true,
1239
+ sortable: metadata.sortable ?? true
1240
+ };
1241
+ }).filter((field) => field !== null);
1242
+ };
1243
+ const useDocument = (args, opts) => {
1244
+ const { toggleNotification } = useNotification();
1245
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1246
+ const { formatMessage } = useIntl();
1247
+ const {
1248
+ currentData: data,
1249
+ isLoading: isLoadingDocument,
1250
+ isFetching: isFetchingDocument,
1251
+ error
1252
+ } = useGetDocumentQuery(args, {
1253
+ ...opts,
1254
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1255
+ });
1256
+ const document = data?.data;
1257
+ const meta = data?.meta;
1258
+ const {
1259
+ components,
1260
+ schema,
1261
+ schemas,
1262
+ isLoading: isLoadingSchema
1263
+ } = useContentTypeSchema(args.model);
1264
+ const isSingleType = schema?.kind === "singleType";
1265
+ const getTitle = (mainField) => {
1266
+ if (mainField !== "id" && document?.[mainField]) {
1267
+ return document[mainField];
1268
+ }
1269
+ if (isSingleType && schema?.info.displayName) {
1270
+ return schema.info.displayName;
1271
+ }
1272
+ return formatMessage({
1273
+ id: "content-manager.containers.untitled",
1274
+ defaultMessage: "Untitled"
1275
+ });
1276
+ };
1277
+ React.useEffect(() => {
1278
+ if (error) {
1279
+ toggleNotification({
1280
+ type: "danger",
1281
+ message: formatAPIError(error)
1282
+ });
1283
+ }
1284
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1285
+ const validationSchema = React.useMemo(() => {
1286
+ if (!schema) {
1287
+ return null;
1288
+ }
1289
+ return createYupSchema(schema.attributes, components);
1290
+ }, [schema, components]);
1291
+ const validate = React.useCallback(
1292
+ (document2) => {
1293
+ if (!validationSchema) {
1294
+ throw new Error(
1295
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1296
+ );
1297
+ }
1298
+ try {
1299
+ validationSchema.validateSync(document2, { abortEarly: false, strict: true });
1300
+ return null;
1301
+ } catch (error2) {
1302
+ if (error2 instanceof ValidationError) {
1303
+ return getYupValidationErrors(error2);
1304
+ }
1305
+ throw error2;
1306
+ }
1307
+ },
1308
+ [validationSchema]
1309
+ );
1310
+ const getInitialFormValues = React.useCallback(
1311
+ (isCreatingDocument = false) => {
1312
+ if (!document && !isCreatingDocument && !isSingleType || !schema) {
1313
+ return void 0;
1314
+ }
1315
+ const form = document?.id ? document : createDefaultForm(schema, components);
1316
+ return transformDocument(schema, components)(form);
1317
+ },
1318
+ [document, isSingleType, schema, components]
1319
+ );
1320
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1321
+ const hasError = !!error;
1322
+ return {
1323
+ components,
1324
+ document,
1325
+ meta,
813
1326
  isLoading,
1327
+ hasError,
814
1328
  schema,
815
- validate
1329
+ schemas,
1330
+ validate,
1331
+ getTitle,
1332
+ getInitialFormValues
816
1333
  };
817
1334
  };
818
1335
  const useDoc = () => {
@@ -825,22 +1342,60 @@ const useDoc = () => {
825
1342
  if (!slug) {
826
1343
  throw new Error("Could not find model in url params");
827
1344
  }
1345
+ const document = useDocument(
1346
+ { documentId: origin || id, model: slug, collectionType, params },
1347
+ {
1348
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1349
+ }
1350
+ );
1351
+ const returnId = origin || id === "create" ? void 0 : id;
828
1352
  return {
829
1353
  collectionType,
830
1354
  model: slug,
831
- id: origin || id === "create" ? void 0 : id,
832
- ...useDocument(
833
- { documentId: origin || id, model: slug, collectionType, params },
834
- {
835
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
836
- }
837
- )
1355
+ id: returnId,
1356
+ ...document
1357
+ };
1358
+ };
1359
+ const useContentManagerContext = () => {
1360
+ const {
1361
+ collectionType,
1362
+ model,
1363
+ id,
1364
+ components,
1365
+ isLoading: isLoadingDoc,
1366
+ schema,
1367
+ schemas
1368
+ } = useDoc();
1369
+ const layout = useDocumentLayout(model);
1370
+ const form = useForm("useContentManagerContext", (state) => state);
1371
+ const isSingleType = collectionType === SINGLE_TYPES;
1372
+ const slug = model;
1373
+ const isCreatingEntry = id === "create";
1374
+ useContentTypeSchema();
1375
+ const isLoading = isLoadingDoc || layout.isLoading;
1376
+ const error = layout.error;
1377
+ return {
1378
+ error,
1379
+ isLoading,
1380
+ // Base metadata
1381
+ model,
1382
+ collectionType,
1383
+ id,
1384
+ slug,
1385
+ isCreatingEntry,
1386
+ isSingleType,
1387
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1388
+ // All schema infos
1389
+ components,
1390
+ contentType: schema,
1391
+ contentTypes: schemas,
1392
+ // Form state
1393
+ form,
1394
+ // layout infos
1395
+ layout
838
1396
  };
839
1397
  };
840
1398
  const prefixPluginTranslations = (trad, pluginId) => {
841
- if (!pluginId) {
842
- throw new TypeError("pluginId can't be empty");
843
- }
844
1399
  return Object.keys(trad).reduce((acc, current) => {
845
1400
  acc[`${pluginId}.${current}`] = trad[current];
846
1401
  return acc;
@@ -856,6 +1411,8 @@ const useDocumentActions = () => {
856
1411
  const { formatMessage } = useIntl();
857
1412
  const { trackUsage } = useTracking();
858
1413
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1414
+ const navigate = useNavigate();
1415
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
859
1416
  const [deleteDocument] = useDeleteDocumentMutation();
860
1417
  const _delete = React.useCallback(
861
1418
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1170,6 +1727,7 @@ const useDocumentActions = () => {
1170
1727
  defaultMessage: "Saved document"
1171
1728
  })
1172
1729
  });
1730
+ setCurrentStep("contentManager.success");
1173
1731
  return res.data;
1174
1732
  } catch (err) {
1175
1733
  toggleNotification({
@@ -1209,7 +1767,7 @@ const useDocumentActions = () => {
1209
1767
  throw err;
1210
1768
  }
1211
1769
  },
1212
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1770
+ [autoCloneDocument, formatMessage, toggleNotification]
1213
1771
  );
1214
1772
  const [cloneDocument] = useCloneDocumentMutation();
1215
1773
  const clone = React.useCallback(
@@ -1235,6 +1793,7 @@ const useDocumentActions = () => {
1235
1793
  defaultMessage: "Cloned document"
1236
1794
  })
1237
1795
  });
1796
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1238
1797
  return res.data;
1239
1798
  } catch (err) {
1240
1799
  toggleNotification({
@@ -1245,7 +1804,7 @@ const useDocumentActions = () => {
1245
1804
  throw err;
1246
1805
  }
1247
1806
  },
1248
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1807
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1249
1808
  );
1250
1809
  const [getDoc] = useLazyGetDocumentQuery();
1251
1810
  const getDocument = React.useCallback(
@@ -1270,10 +1829,10 @@ const useDocumentActions = () => {
1270
1829
  update
1271
1830
  };
1272
1831
  };
1273
- const ProtectedHistoryPage = lazy(
1274
- () => import("./History-wrnHqf09.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1832
+ const ProtectedHistoryPage = React.lazy(
1833
+ () => import("./History-CcmSn3Mj.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1275
1834
  );
1276
- const routes$1 = [
1835
+ const routes$2 = [
1277
1836
  {
1278
1837
  path: ":collectionType/:slug/:id/history",
1279
1838
  Component: ProtectedHistoryPage
@@ -1283,32 +1842,45 @@ const routes$1 = [
1283
1842
  Component: ProtectedHistoryPage
1284
1843
  }
1285
1844
  ];
1845
+ const ProtectedPreviewPage = React.lazy(
1846
+ () => import("./Preview-vfWOtPG5.mjs").then((mod) => ({ default: mod.ProtectedPreviewPage }))
1847
+ );
1848
+ const routes$1 = [
1849
+ {
1850
+ path: ":collectionType/:slug/:id/preview",
1851
+ Component: ProtectedPreviewPage
1852
+ },
1853
+ {
1854
+ path: ":collectionType/:slug/preview",
1855
+ Component: ProtectedPreviewPage
1856
+ }
1857
+ ];
1286
1858
  const ProtectedEditViewPage = lazy(
1287
- () => import("./EditViewPage-BLsjc5F-.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1859
+ () => import("./EditViewPage-Bcnff6Vd.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1288
1860
  );
1289
1861
  const ProtectedListViewPage = lazy(
1290
- () => import("./ListViewPage-C4IvrMgY.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1862
+ () => import("./ListViewPage-HljQVnFH.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1291
1863
  );
1292
1864
  const ProtectedListConfiguration = lazy(
1293
- () => import("./ListConfigurationPage-DScmJVkW.mjs").then((mod) => ({
1865
+ () => import("./ListConfigurationPage-BPvzENJJ.mjs").then((mod) => ({
1294
1866
  default: mod.ProtectedListConfiguration
1295
1867
  }))
1296
1868
  );
1297
1869
  const ProtectedEditConfigurationPage = lazy(
1298
- () => import("./EditConfigurationPage-DmoXawIh.mjs").then((mod) => ({
1870
+ () => import("./EditConfigurationPage-D1nvB7Br.mjs").then((mod) => ({
1299
1871
  default: mod.ProtectedEditConfigurationPage
1300
1872
  }))
1301
1873
  );
1302
1874
  const ProtectedComponentConfigurationPage = lazy(
1303
- () => import("./ComponentConfigurationPage-BAgyHiMm.mjs").then((mod) => ({
1875
+ () => import("./ComponentConfigurationPage-D4H-v0et.mjs").then((mod) => ({
1304
1876
  default: mod.ProtectedComponentConfigurationPage
1305
1877
  }))
1306
1878
  );
1307
1879
  const NoPermissions = lazy(
1308
- () => import("./NoPermissionsPage-DSP7R-hv.mjs").then((mod) => ({ default: mod.NoPermissions }))
1880
+ () => import("./NoPermissionsPage-D6ze2nQL.mjs").then((mod) => ({ default: mod.NoPermissions }))
1309
1881
  );
1310
1882
  const NoContentType = lazy(
1311
- () => import("./NoContentTypePage-Djg8nPlj.mjs").then((mod) => ({ default: mod.NoContentType }))
1883
+ () => import("./NoContentTypePage-BfHaSM-K.mjs").then((mod) => ({ default: mod.NoContentType }))
1312
1884
  );
1313
1885
  const CollectionTypePages = () => {
1314
1886
  const { collectionType } = useParams();
@@ -1320,7 +1892,7 @@ const CollectionTypePages = () => {
1320
1892
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1321
1893
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1322
1894
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1323
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1895
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1324
1896
  const routes = [
1325
1897
  {
1326
1898
  path: LIST_RELATIVE_PATH,
@@ -1354,6 +1926,7 @@ const routes = [
1354
1926
  path: "no-content-types",
1355
1927
  Component: NoContentType
1356
1928
  },
1929
+ ...routes$2,
1357
1930
  ...routes$1
1358
1931
  ];
1359
1932
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1422,12 +1995,14 @@ const DocumentActionButton = (action) => {
1422
1995
  /* @__PURE__ */ jsx(
1423
1996
  Button,
1424
1997
  {
1425
- flex: 1,
1998
+ flex: "auto",
1426
1999
  startIcon: action.icon,
1427
2000
  disabled: action.disabled,
1428
2001
  onClick: handleClick(action),
1429
2002
  justifyContent: "center",
1430
2003
  variant: action.variant || "default",
2004
+ paddingTop: "7px",
2005
+ paddingBottom: "7px",
1431
2006
  children: action.label
1432
2007
  }
1433
2008
  ),
@@ -1450,6 +2025,11 @@ const DocumentActionButton = (action) => {
1450
2025
  ) : null
1451
2026
  ] });
1452
2027
  };
2028
+ const MenuItem = styled(Menu.Item)`
2029
+ &:hover {
2030
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
2031
+ }
2032
+ `;
1453
2033
  const DocumentActionsMenu = ({
1454
2034
  actions: actions2,
1455
2035
  children,
@@ -1492,9 +2072,9 @@ const DocumentActionsMenu = ({
1492
2072
  disabled: isDisabled,
1493
2073
  size: "S",
1494
2074
  endIcon: null,
1495
- paddingTop: "7px",
1496
- paddingLeft: "9px",
1497
- paddingRight: "9px",
2075
+ paddingTop: "4px",
2076
+ paddingLeft: "7px",
2077
+ paddingRight: "7px",
1498
2078
  variant,
1499
2079
  children: [
1500
2080
  /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
@@ -1505,36 +2085,35 @@ const DocumentActionsMenu = ({
1505
2085
  ]
1506
2086
  }
1507
2087
  ),
1508
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
2088
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1509
2089
  actions2.map((action) => {
1510
2090
  return /* @__PURE__ */ jsx(
1511
- Menu.Item,
2091
+ MenuItem,
1512
2092
  {
1513
2093
  disabled: action.disabled,
1514
2094
  onSelect: handleClick(action),
1515
2095
  display: "block",
1516
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1517
- /* @__PURE__ */ jsxs(Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1518
- /* @__PURE__ */ jsx(Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1519
- action.label
1520
- ] }),
1521
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1522
- Flex,
1523
- {
1524
- alignItems: "center",
1525
- background: "alternative100",
1526
- borderStyle: "solid",
1527
- borderColor: "alternative200",
1528
- borderWidth: "1px",
1529
- height: 5,
1530
- paddingLeft: 2,
1531
- paddingRight: 2,
1532
- hasRadius: true,
1533
- color: "alternative600",
1534
- children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1535
- }
1536
- )
1537
- ] })
2096
+ isVariantDanger: action.variant === "danger",
2097
+ isDisabled: action.disabled,
2098
+ children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxs(
2099
+ Flex,
2100
+ {
2101
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
2102
+ gap: 2,
2103
+ tag: "span",
2104
+ children: [
2105
+ /* @__PURE__ */ jsx(
2106
+ Flex,
2107
+ {
2108
+ tag: "span",
2109
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
2110
+ children: action.icon
2111
+ }
2112
+ ),
2113
+ action.label
2114
+ ]
2115
+ }
2116
+ ) })
1538
2117
  },
1539
2118
  action.id
1540
2119
  );
@@ -1614,11 +2193,11 @@ const DocumentActionConfirmDialog = ({
1614
2193
  /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1615
2194
  /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1616
2195
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1617
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
2196
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1618
2197
  id: "app.components.Button.cancel",
1619
2198
  defaultMessage: "Cancel"
1620
2199
  }) }) }),
1621
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
2200
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1622
2201
  id: "app.components.Button.confirm",
1623
2202
  defaultMessage: "Confirm"
1624
2203
  }) })
@@ -1645,8 +2224,20 @@ const DocumentActionModal = ({
1645
2224
  typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1646
2225
  ] }) });
1647
2226
  };
1648
- const PublishAction$1 = ({
1649
- activeTab,
2227
+ const transformData = (data) => {
2228
+ if (Array.isArray(data)) {
2229
+ return data.map(transformData);
2230
+ }
2231
+ if (typeof data === "object" && data !== null) {
2232
+ if ("apiData" in data) {
2233
+ return data.apiData;
2234
+ }
2235
+ return mapValues(transformData)(data);
2236
+ }
2237
+ return data;
2238
+ };
2239
+ const PublishAction$1 = ({
2240
+ activeTab,
1650
2241
  documentId,
1651
2242
  model,
1652
2243
  collectionType,
@@ -1657,12 +2248,11 @@ const PublishAction$1 = ({
1657
2248
  const navigate = useNavigate();
1658
2249
  const { toggleNotification } = useNotification();
1659
2250
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2251
+ const isListView = useMatch(LIST_PATH) !== null;
1660
2252
  const isCloning = useMatch(CLONE_PATH) !== null;
2253
+ const { id } = useParams();
1661
2254
  const { formatMessage } = useIntl();
1662
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1663
- "PublishAction",
1664
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1665
- );
2255
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1666
2256
  const { publish } = useDocumentActions();
1667
2257
  const [
1668
2258
  countDraftRelations,
@@ -1714,24 +2304,25 @@ const PublishAction$1 = ({
1714
2304
  }
1715
2305
  }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1716
2306
  React.useEffect(() => {
1717
- if (documentId) {
1718
- const fetchDraftRelationsCount = async () => {
1719
- const { data, error } = await countDraftRelations({
1720
- collectionType,
1721
- model,
1722
- documentId,
1723
- params
1724
- });
1725
- if (error) {
1726
- throw error;
1727
- }
1728
- if (data) {
1729
- setServerCountOfDraftRelations(data.data);
1730
- }
1731
- };
1732
- fetchDraftRelationsCount();
2307
+ if (!document || !document.documentId || isListView) {
2308
+ return;
1733
2309
  }
1734
- }, [documentId, countDraftRelations, collectionType, model, params]);
2310
+ const fetchDraftRelationsCount = async () => {
2311
+ const { data, error } = await countDraftRelations({
2312
+ collectionType,
2313
+ model,
2314
+ documentId,
2315
+ params
2316
+ });
2317
+ if (error) {
2318
+ throw error;
2319
+ }
2320
+ if (data) {
2321
+ setServerCountOfDraftRelations(data.data);
2322
+ }
2323
+ };
2324
+ fetchDraftRelationsCount();
2325
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1735
2326
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1736
2327
  if (!schema?.options?.draftAndPublish) {
1737
2328
  return null;
@@ -1739,7 +2330,9 @@ const PublishAction$1 = ({
1739
2330
  const performPublish = async () => {
1740
2331
  setSubmitting(true);
1741
2332
  try {
1742
- const { errors } = await validate();
2333
+ const { errors } = await validate(true, {
2334
+ status: "published"
2335
+ });
1743
2336
  if (errors) {
1744
2337
  toggleNotification({
1745
2338
  type: "danger",
@@ -1757,13 +2350,15 @@ const PublishAction$1 = ({
1757
2350
  documentId,
1758
2351
  params
1759
2352
  },
1760
- formValues
2353
+ transformData(formValues)
1761
2354
  );
1762
2355
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1763
- navigate({
1764
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1765
- search: rawQuery
1766
- });
2356
+ if (id === "create") {
2357
+ navigate({
2358
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2359
+ search: rawQuery
2360
+ });
2361
+ }
1767
2362
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1768
2363
  setErrors(formatValidationErrors(res.error));
1769
2364
  }
@@ -1772,7 +2367,8 @@ const PublishAction$1 = ({
1772
2367
  }
1773
2368
  };
1774
2369
  const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1775
- const hasDraftRelations = totalDraftRelations > 0;
2370
+ const enableDraftRelationsCount = false;
2371
+ const hasDraftRelations = enableDraftRelationsCount;
1776
2372
  return {
1777
2373
  /**
1778
2374
  * Disabled when:
@@ -1782,18 +2378,13 @@ const PublishAction$1 = ({
1782
2378
  * - the document is already published & not modified
1783
2379
  * - the document is being created & not modified
1784
2380
  * - the user doesn't have the permission to publish
1785
- * - the user doesn't have the permission to create a new document
1786
- * - the user doesn't have the permission to update the document
1787
2381
  */
1788
- disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2382
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1789
2383
  label: formatMessage({
1790
2384
  id: "app.utils.publish",
1791
2385
  defaultMessage: "Publish"
1792
2386
  }),
1793
2387
  onClick: async () => {
1794
- if (hasDraftRelations) {
1795
- return;
1796
- }
1797
2388
  await performPublish();
1798
2389
  },
1799
2390
  dialog: hasDraftRelations ? {
@@ -1820,6 +2411,7 @@ const PublishAction$1 = ({
1820
2411
  };
1821
2412
  };
1822
2413
  PublishAction$1.type = "publish";
2414
+ PublishAction$1.position = "panel";
1823
2415
  const UpdateAction = ({
1824
2416
  activeTab,
1825
2417
  documentId,
@@ -1832,10 +2424,6 @@ const UpdateAction = ({
1832
2424
  const cloneMatch = useMatch(CLONE_PATH);
1833
2425
  const isCloning = cloneMatch !== null;
1834
2426
  const { formatMessage } = useIntl();
1835
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1836
- canCreate: canCreate2,
1837
- canUpdate: canUpdate2
1838
- }));
1839
2427
  const { create, update, clone } = useDocumentActions();
1840
2428
  const [{ query, rawQuery }] = useQueryParams();
1841
2429
  const params = React.useMemo(() => buildValidParams(query), [query]);
@@ -1846,96 +2434,134 @@ const UpdateAction = ({
1846
2434
  const validate = useForm("UpdateAction", (state) => state.validate);
1847
2435
  const setErrors = useForm("UpdateAction", (state) => state.setErrors);
1848
2436
  const resetForm = useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
1849
- return {
1850
- /**
1851
- * Disabled when:
1852
- * - the form is submitting
1853
- * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1854
- * - the active tab is the published tab
1855
- * - the user doesn't have the permission to create a new document
1856
- * - the user doesn't have the permission to update the document
1857
- */
1858
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
1859
- label: formatMessage({
1860
- id: "content-manager.containers.Edit.save",
1861
- defaultMessage: "Save"
1862
- }),
1863
- onClick: async () => {
1864
- setSubmitting(true);
1865
- try {
1866
- const { errors } = await validate();
1867
- if (errors) {
1868
- toggleNotification({
1869
- type: "danger",
1870
- message: formatMessage({
1871
- id: "content-manager.validation.error",
1872
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1873
- })
1874
- });
1875
- return;
1876
- }
1877
- if (isCloning) {
1878
- const res = await clone(
1879
- {
1880
- model,
1881
- documentId: cloneMatch.params.origin,
1882
- params
1883
- },
1884
- document
1885
- );
1886
- if ("data" in res) {
1887
- navigate(
1888
- {
1889
- pathname: `../${res.data.documentId}`,
1890
- search: rawQuery
1891
- },
1892
- { relative: "path" }
1893
- );
1894
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1895
- setErrors(formatValidationErrors(res.error));
1896
- }
1897
- } else if (documentId || collectionType === SINGLE_TYPES) {
1898
- const res = await update(
2437
+ const handleUpdate = React.useCallback(async () => {
2438
+ setSubmitting(true);
2439
+ try {
2440
+ if (!modified) {
2441
+ return;
2442
+ }
2443
+ const { errors } = await validate(true, {
2444
+ status: "draft"
2445
+ });
2446
+ if (errors) {
2447
+ toggleNotification({
2448
+ type: "danger",
2449
+ message: formatMessage({
2450
+ id: "content-manager.validation.error",
2451
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2452
+ })
2453
+ });
2454
+ return;
2455
+ }
2456
+ if (isCloning) {
2457
+ const res = await clone(
2458
+ {
2459
+ model,
2460
+ documentId: cloneMatch.params.origin,
2461
+ params
2462
+ },
2463
+ transformData(document)
2464
+ );
2465
+ if ("data" in res) {
2466
+ navigate(
1899
2467
  {
1900
- collectionType,
1901
- model,
1902
- documentId,
1903
- params
2468
+ pathname: `../${res.data.documentId}`,
2469
+ search: rawQuery
1904
2470
  },
1905
- document
2471
+ { relative: "path" }
1906
2472
  );
1907
- if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1908
- setErrors(formatValidationErrors(res.error));
1909
- } else {
1910
- resetForm();
1911
- }
2473
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2474
+ setErrors(formatValidationErrors(res.error));
2475
+ }
2476
+ } else if (documentId || collectionType === SINGLE_TYPES) {
2477
+ const res = await update(
2478
+ {
2479
+ collectionType,
2480
+ model,
2481
+ documentId,
2482
+ params
2483
+ },
2484
+ transformData(document)
2485
+ );
2486
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2487
+ setErrors(formatValidationErrors(res.error));
1912
2488
  } else {
1913
- const res = await create(
2489
+ resetForm();
2490
+ }
2491
+ } else {
2492
+ const res = await create(
2493
+ {
2494
+ model,
2495
+ params
2496
+ },
2497
+ transformData(document)
2498
+ );
2499
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2500
+ navigate(
1914
2501
  {
1915
- model,
1916
- params
2502
+ pathname: `../${res.data.documentId}`,
2503
+ search: rawQuery
1917
2504
  },
1918
- document
2505
+ { replace: true, relative: "path" }
1919
2506
  );
1920
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1921
- navigate(
1922
- {
1923
- pathname: `../${res.data.documentId}`,
1924
- search: rawQuery
1925
- },
1926
- { replace: true, relative: "path" }
1927
- );
1928
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1929
- setErrors(formatValidationErrors(res.error));
1930
- }
2507
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2508
+ setErrors(formatValidationErrors(res.error));
1931
2509
  }
1932
- } finally {
1933
- setSubmitting(false);
1934
2510
  }
2511
+ } finally {
2512
+ setSubmitting(false);
1935
2513
  }
2514
+ }, [
2515
+ clone,
2516
+ cloneMatch?.params.origin,
2517
+ collectionType,
2518
+ create,
2519
+ document,
2520
+ documentId,
2521
+ formatMessage,
2522
+ formatValidationErrors,
2523
+ isCloning,
2524
+ model,
2525
+ modified,
2526
+ navigate,
2527
+ params,
2528
+ rawQuery,
2529
+ resetForm,
2530
+ setErrors,
2531
+ setSubmitting,
2532
+ toggleNotification,
2533
+ update,
2534
+ validate
2535
+ ]);
2536
+ React.useEffect(() => {
2537
+ const handleKeyDown = (e) => {
2538
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
2539
+ e.preventDefault();
2540
+ handleUpdate();
2541
+ }
2542
+ };
2543
+ window.addEventListener("keydown", handleKeyDown);
2544
+ return () => {
2545
+ window.removeEventListener("keydown", handleKeyDown);
2546
+ };
2547
+ }, [handleUpdate]);
2548
+ return {
2549
+ /**
2550
+ * Disabled when:
2551
+ * - the form is submitting
2552
+ * - the document is not modified & we're not cloning (you can save a clone entity straight away)
2553
+ * - the active tab is the published tab
2554
+ */
2555
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
2556
+ label: formatMessage({
2557
+ id: "global.save",
2558
+ defaultMessage: "Save"
2559
+ }),
2560
+ onClick: handleUpdate
1936
2561
  };
1937
2562
  };
1938
2563
  UpdateAction.type = "update";
2564
+ UpdateAction.position = "panel";
1939
2565
  const UNPUBLISH_DRAFT_OPTIONS = {
1940
2566
  KEEP: "keep",
1941
2567
  DISCARD: "discard"
@@ -1968,7 +2594,7 @@ const UnpublishAction$1 = ({
1968
2594
  id: "app.utils.unpublish",
1969
2595
  defaultMessage: "Unpublish"
1970
2596
  }),
1971
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2597
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1972
2598
  onClick: async () => {
1973
2599
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1974
2600
  if (!documentId) {
@@ -2058,6 +2684,7 @@ const UnpublishAction$1 = ({
2058
2684
  };
2059
2685
  };
2060
2686
  UnpublishAction$1.type = "unpublish";
2687
+ UnpublishAction$1.position = "panel";
2061
2688
  const DiscardAction = ({
2062
2689
  activeTab,
2063
2690
  documentId,
@@ -2080,7 +2707,7 @@ const DiscardAction = ({
2080
2707
  id: "content-manager.actions.discard.label",
2081
2708
  defaultMessage: "Discard changes"
2082
2709
  }),
2083
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2710
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2084
2711
  position: ["panel", "table-row"],
2085
2712
  variant: "danger",
2086
2713
  dialog: {
@@ -2108,11 +2735,7 @@ const DiscardAction = ({
2108
2735
  };
2109
2736
  };
2110
2737
  DiscardAction.type = "discard";
2111
- const StyledCrossCircle = styled(CrossCircle)`
2112
- path {
2113
- fill: currentColor;
2114
- }
2115
- `;
2738
+ DiscardAction.position = "panel";
2116
2739
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2117
2740
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2118
2741
  const RelativeTime = React.forwardRef(
@@ -2125,7 +2748,7 @@ const RelativeTime = React.forwardRef(
2125
2748
  });
2126
2749
  const unit = intervals.find((intervalUnit) => {
2127
2750
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2128
- });
2751
+ }) ?? "seconds";
2129
2752
  const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2130
2753
  const customInterval = customIntervals.find(
2131
2754
  (custom) => interval[custom.unit] < custom.threshold
@@ -2159,19 +2782,29 @@ const getDisplayName = ({
2159
2782
  return email ?? "";
2160
2783
  };
2161
2784
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2162
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2163
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2164
- return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2785
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2786
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2787
+ const { formatMessage } = useIntl();
2788
+ return /* @__PURE__ */ jsx(Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2789
+ id: `content-manager.containers.List.${status}`,
2790
+ defaultMessage: capitalise(status)
2791
+ }) }) });
2165
2792
  };
2166
2793
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2167
2794
  const { formatMessage } = useIntl();
2168
2795
  const isCloning = useMatch(CLONE_PATH) !== null;
2796
+ const params = useParams();
2169
2797
  const title = isCreating ? formatMessage({
2170
2798
  id: "content-manager.containers.edit.title.new",
2171
2799
  defaultMessage: "Create an entry"
2172
2800
  }) : documentTitle;
2173
2801
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2174
- /* @__PURE__ */ jsx(BackButton, {}),
2802
+ /* @__PURE__ */ jsx(
2803
+ BackButton,
2804
+ {
2805
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2806
+ }
2807
+ ),
2175
2808
  /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2176
2809
  /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2177
2810
  /* @__PURE__ */ jsx(HeaderToolbar, {})
@@ -2222,7 +2855,7 @@ const HeaderToolbar = () => {
2222
2855
  meta: isCloning ? void 0 : meta,
2223
2856
  collectionType
2224
2857
  },
2225
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2858
+ descriptions: plugins["content-manager"].apis.getDocumentActions("header"),
2226
2859
  children: (actions2) => {
2227
2860
  const headerActions = actions2.filter((action) => {
2228
2861
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2259,12 +2892,12 @@ const Information = ({ activeTab }) => {
2259
2892
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2260
2893
  label: formatMessage({
2261
2894
  id: "content-manager.containers.edit.information.last-published.label",
2262
- defaultMessage: "Last published"
2895
+ defaultMessage: "Published"
2263
2896
  }),
2264
2897
  value: formatMessage(
2265
2898
  {
2266
2899
  id: "content-manager.containers.edit.information.last-published.value",
2267
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2900
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2268
2901
  },
2269
2902
  {
2270
2903
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2277,12 +2910,12 @@ const Information = ({ activeTab }) => {
2277
2910
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2278
2911
  label: formatMessage({
2279
2912
  id: "content-manager.containers.edit.information.last-draft.label",
2280
- defaultMessage: "Last draft"
2913
+ defaultMessage: "Updated"
2281
2914
  }),
2282
2915
  value: formatMessage(
2283
2916
  {
2284
2917
  id: "content-manager.containers.edit.information.last-draft.value",
2285
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2918
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2286
2919
  },
2287
2920
  {
2288
2921
  time: /* @__PURE__ */ jsx(
@@ -2300,12 +2933,12 @@ const Information = ({ activeTab }) => {
2300
2933
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2301
2934
  label: formatMessage({
2302
2935
  id: "content-manager.containers.edit.information.document.label",
2303
- defaultMessage: "Document"
2936
+ defaultMessage: "Created"
2304
2937
  }),
2305
2938
  value: formatMessage(
2306
2939
  {
2307
2940
  id: "content-manager.containers.edit.information.document.value",
2308
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2941
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2309
2942
  },
2310
2943
  {
2311
2944
  time: /* @__PURE__ */ jsx(
@@ -2343,25 +2976,77 @@ const Information = ({ activeTab }) => {
2343
2976
  );
2344
2977
  };
2345
2978
  const HeaderActions = ({ actions: actions2 }) => {
2346
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2347
- if ("options" in action) {
2979
+ const [dialogId, setDialogId] = React.useState(null);
2980
+ const handleClick = (action) => async (e) => {
2981
+ if (!("options" in action)) {
2982
+ const { onClick = () => false, dialog, id } = action;
2983
+ const muteDialog = await onClick(e);
2984
+ if (dialog && !muteDialog) {
2985
+ e.preventDefault();
2986
+ setDialogId(id);
2987
+ }
2988
+ }
2989
+ };
2990
+ const handleClose = () => {
2991
+ setDialogId(null);
2992
+ };
2993
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2994
+ if (action.options) {
2348
2995
  return /* @__PURE__ */ jsx(
2349
2996
  SingleSelect,
2350
2997
  {
2351
2998
  size: "S",
2352
- disabled: action.disabled,
2353
- "aria-label": action.label,
2354
2999
  onChange: action.onSelect,
2355
- value: action.value,
3000
+ "aria-label": action.label,
3001
+ ...action,
2356
3002
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2357
3003
  },
2358
3004
  action.id
2359
3005
  );
2360
3006
  } else {
2361
- return null;
3007
+ if (action.type === "icon") {
3008
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
3009
+ /* @__PURE__ */ jsx(
3010
+ IconButton,
3011
+ {
3012
+ disabled: action.disabled,
3013
+ label: action.label,
3014
+ size: "S",
3015
+ onClick: handleClick(action),
3016
+ children: action.icon
3017
+ }
3018
+ ),
3019
+ action.dialog ? /* @__PURE__ */ jsx(
3020
+ HeaderActionDialog,
3021
+ {
3022
+ ...action.dialog,
3023
+ isOpen: dialogId === action.id,
3024
+ onClose: handleClose
3025
+ }
3026
+ ) : null
3027
+ ] }, action.id);
3028
+ }
2362
3029
  }
2363
3030
  }) });
2364
3031
  };
3032
+ const HeaderActionDialog = ({
3033
+ onClose,
3034
+ onCancel,
3035
+ title,
3036
+ content: Content,
3037
+ isOpen
3038
+ }) => {
3039
+ const handleClose = async () => {
3040
+ if (onCancel) {
3041
+ await onCancel();
3042
+ }
3043
+ onClose();
3044
+ };
3045
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
3046
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
3047
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
3048
+ ] }) });
3049
+ };
2365
3050
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2366
3051
  const navigate = useNavigate();
2367
3052
  const { formatMessage } = useIntl();
@@ -2378,6 +3063,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2378
3063
  };
2379
3064
  };
2380
3065
  ConfigureTheViewAction.type = "configure-the-view";
3066
+ ConfigureTheViewAction.position = "header";
2381
3067
  const EditTheModelAction = ({ model }) => {
2382
3068
  const navigate = useNavigate();
2383
3069
  const { formatMessage } = useIntl();
@@ -2394,6 +3080,7 @@ const EditTheModelAction = ({ model }) => {
2394
3080
  };
2395
3081
  };
2396
3082
  EditTheModelAction.type = "edit-the-model";
3083
+ EditTheModelAction.position = "header";
2397
3084
  const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2398
3085
  const navigate = useNavigate();
2399
3086
  const { formatMessage } = useIntl();
@@ -2402,12 +3089,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2402
3089
  const { delete: deleteAction } = useDocumentActions();
2403
3090
  const { toggleNotification } = useNotification();
2404
3091
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
3092
+ const isLocalized = document?.locale != null;
2405
3093
  return {
2406
3094
  disabled: !canDelete || !document,
2407
- label: formatMessage({
2408
- id: "content-manager.actions.delete.label",
2409
- defaultMessage: "Delete document"
2410
- }),
3095
+ label: formatMessage(
3096
+ {
3097
+ id: "content-manager.actions.delete.label",
3098
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
3099
+ },
3100
+ { isLocalized }
3101
+ ),
2411
3102
  icon: /* @__PURE__ */ jsx(Trash, {}),
2412
3103
  dialog: {
2413
3104
  type: "dialog",
@@ -2463,6 +3154,7 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2463
3154
  };
2464
3155
  };
2465
3156
  DeleteAction$1.type = "delete";
3157
+ DeleteAction$1.position = ["header", "table-row"];
2466
3158
  const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2467
3159
  const Panels = () => {
2468
3160
  const isCloning = useMatch(CLONE_PATH) !== null;
@@ -2477,389 +3169,87 @@ const Panels = () => {
2477
3169
  const plugins = useStrapiApp("Panels", (state) => state.plugins);
2478
3170
  const props = {
2479
3171
  activeTab: status,
2480
- model,
2481
- documentId: id,
2482
- document: isCloning ? void 0 : document,
2483
- meta: isCloning ? void 0 : meta,
2484
- collectionType
2485
- };
2486
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2487
- DescriptionComponentRenderer,
2488
- {
2489
- props,
2490
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2491
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2492
- }
2493
- ) });
2494
- };
2495
- const ActionsPanel = () => {
2496
- const { formatMessage } = useIntl();
2497
- return {
2498
- title: formatMessage({
2499
- id: "content-manager.containers.edit.panels.default.title",
2500
- defaultMessage: "Document"
2501
- }),
2502
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2503
- };
2504
- };
2505
- ActionsPanel.type = "actions";
2506
- const ActionsPanelContent = () => {
2507
- const isCloning = useMatch(CLONE_PATH) !== null;
2508
- const [
2509
- {
2510
- query: { status = "draft" }
2511
- }
2512
- ] = useQueryParams();
2513
- const { model, id, document, meta, collectionType } = useDoc();
2514
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2515
- const props = {
2516
- activeTab: status,
2517
- model,
2518
- documentId: id,
2519
- document: isCloning ? void 0 : document,
2520
- meta: isCloning ? void 0 : meta,
2521
- collectionType
2522
- };
2523
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2524
- /* @__PURE__ */ jsx(
2525
- DescriptionComponentRenderer,
2526
- {
2527
- props,
2528
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2529
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2530
- }
2531
- ),
2532
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2533
- ] });
2534
- };
2535
- const Panel = React.forwardRef(({ children, title }, ref) => {
2536
- return /* @__PURE__ */ jsxs(
2537
- Flex,
2538
- {
2539
- ref,
2540
- tag: "aside",
2541
- "aria-labelledby": "additional-information",
2542
- background: "neutral0",
2543
- borderColor: "neutral150",
2544
- hasRadius: true,
2545
- paddingBottom: 4,
2546
- paddingLeft: 4,
2547
- paddingRight: 4,
2548
- paddingTop: 4,
2549
- shadow: "tableShadow",
2550
- gap: 3,
2551
- direction: "column",
2552
- justifyContent: "stretch",
2553
- alignItems: "flex-start",
2554
- children: [
2555
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2556
- children
2557
- ]
2558
- }
2559
- );
2560
- });
2561
- const HOOKS = {
2562
- /**
2563
- * Hook that allows to mutate the displayed headers of the list view table
2564
- * @constant
2565
- * @type {string}
2566
- */
2567
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2568
- /**
2569
- * Hook that allows to mutate the CM's collection types links pre-set filters
2570
- * @constant
2571
- * @type {string}
2572
- */
2573
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2574
- /**
2575
- * Hook that allows to mutate the CM's edit view layout
2576
- * @constant
2577
- * @type {string}
2578
- */
2579
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2580
- /**
2581
- * Hook that allows to mutate the CM's single types links pre-set filters
2582
- * @constant
2583
- * @type {string}
2584
- */
2585
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2586
- };
2587
- const contentTypesApi = contentManagerApi.injectEndpoints({
2588
- endpoints: (builder) => ({
2589
- getContentTypeConfiguration: builder.query({
2590
- query: (uid) => ({
2591
- url: `/content-manager/content-types/${uid}/configuration`,
2592
- method: "GET"
2593
- }),
2594
- transformResponse: (response) => response.data,
2595
- providesTags: (_result, _error, uid) => [
2596
- { type: "ContentTypesConfiguration", id: uid },
2597
- { type: "ContentTypeSettings", id: "LIST" }
2598
- ]
2599
- }),
2600
- getAllContentTypeSettings: builder.query({
2601
- query: () => "/content-manager/content-types-settings",
2602
- transformResponse: (response) => response.data,
2603
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2604
- }),
2605
- updateContentTypeConfiguration: builder.mutation({
2606
- query: ({ uid, ...body }) => ({
2607
- url: `/content-manager/content-types/${uid}/configuration`,
2608
- method: "PUT",
2609
- data: body
2610
- }),
2611
- transformResponse: (response) => response.data,
2612
- invalidatesTags: (_result, _error, { uid }) => [
2613
- { type: "ContentTypesConfiguration", id: uid },
2614
- { type: "ContentTypeSettings", id: "LIST" },
2615
- // Is this necessary?
2616
- { type: "InitialData" }
2617
- ]
2618
- })
2619
- })
2620
- });
2621
- const {
2622
- useGetContentTypeConfigurationQuery,
2623
- useGetAllContentTypeSettingsQuery,
2624
- useUpdateContentTypeConfigurationMutation
2625
- } = contentTypesApi;
2626
- const checkIfAttributeIsDisplayable = (attribute) => {
2627
- const { type } = attribute;
2628
- if (type === "relation") {
2629
- return !attribute.relation.toLowerCase().includes("morph");
2630
- }
2631
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2632
- };
2633
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2634
- if (!mainFieldName) {
2635
- return void 0;
2636
- }
2637
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2638
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2639
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2640
- );
2641
- return {
2642
- name: mainFieldName,
2643
- type: mainFieldType ?? "string"
2644
- };
2645
- };
2646
- const DEFAULT_SETTINGS = {
2647
- bulkable: false,
2648
- filterable: false,
2649
- searchable: false,
2650
- pagination: false,
2651
- defaultSortBy: "",
2652
- defaultSortOrder: "asc",
2653
- mainField: "id",
2654
- pageSize: 10
2655
- };
2656
- const useDocumentLayout = (model) => {
2657
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2658
- const [{ query }] = useQueryParams();
2659
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2660
- const { toggleNotification } = useNotification();
2661
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2662
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2663
- const {
2664
- data,
2665
- isLoading: isLoadingConfigs,
2666
- error,
2667
- isFetching: isFetchingConfigs
2668
- } = useGetContentTypeConfigurationQuery(model);
2669
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2670
- React.useEffect(() => {
2671
- if (error) {
2672
- toggleNotification({
2673
- type: "danger",
2674
- message: formatAPIError(error)
2675
- });
2676
- }
2677
- }, [error, formatAPIError, toggleNotification]);
2678
- const editLayout = React.useMemo(
2679
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2680
- layout: [],
2681
- components: {},
2682
- metadatas: {},
2683
- options: {},
2684
- settings: DEFAULT_SETTINGS
2685
- },
2686
- [data, isLoading, schemas, schema, components]
2687
- );
2688
- const listLayout = React.useMemo(() => {
2689
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2690
- layout: [],
2691
- metadatas: {},
2692
- options: {},
2693
- settings: DEFAULT_SETTINGS
2694
- };
2695
- }, [data, isLoading, schemas, schema, components]);
2696
- const { layout: edit } = React.useMemo(
2697
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2698
- layout: editLayout,
2699
- query
2700
- }),
2701
- [editLayout, query, runHookWaterfall]
2702
- );
2703
- return {
2704
- error,
2705
- isLoading,
2706
- edit,
2707
- list: listLayout
2708
- };
2709
- };
2710
- const useDocLayout = () => {
2711
- const { model } = useDoc();
2712
- return useDocumentLayout(model);
2713
- };
2714
- const formatEditLayout = (data, {
2715
- schemas,
2716
- schema,
2717
- components
2718
- }) => {
2719
- let currentPanelIndex = 0;
2720
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2721
- data.contentType.layouts.edit,
2722
- schema?.attributes,
2723
- data.contentType.metadatas,
2724
- { configurations: data.components, schemas: components },
2725
- schemas
2726
- ).reduce((panels, row) => {
2727
- if (row.some((field) => field.type === "dynamiczone")) {
2728
- panels.push([row]);
2729
- currentPanelIndex += 2;
2730
- } else {
2731
- if (!panels[currentPanelIndex]) {
2732
- panels.push([]);
2733
- }
2734
- panels[currentPanelIndex].push(row);
2735
- }
2736
- return panels;
2737
- }, []);
2738
- const componentEditAttributes = Object.entries(data.components).reduce(
2739
- (acc, [uid, configuration]) => {
2740
- acc[uid] = {
2741
- layout: convertEditLayoutToFieldLayouts(
2742
- configuration.layouts.edit,
2743
- components[uid].attributes,
2744
- configuration.metadatas
2745
- ),
2746
- settings: {
2747
- ...configuration.settings,
2748
- icon: components[uid].info.icon,
2749
- displayName: components[uid].info.displayName
2750
- }
2751
- };
2752
- return acc;
2753
- },
2754
- {}
2755
- );
2756
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2757
- (acc, [attribute, metadata]) => {
2758
- return {
2759
- ...acc,
2760
- [attribute]: metadata.edit
2761
- };
2762
- },
2763
- {}
2764
- );
2765
- return {
2766
- layout: panelledEditAttributes,
2767
- components: componentEditAttributes,
2768
- metadatas: editMetadatas,
2769
- settings: {
2770
- ...data.contentType.settings,
2771
- displayName: schema?.info.displayName
2772
- },
2773
- options: {
2774
- ...schema?.options,
2775
- ...schema?.pluginOptions,
2776
- ...data.contentType.options
2777
- }
3172
+ model,
3173
+ documentId: id,
3174
+ document: isCloning ? void 0 : document,
3175
+ meta: isCloning ? void 0 : meta,
3176
+ collectionType
2778
3177
  };
3178
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
3179
+ DescriptionComponentRenderer,
3180
+ {
3181
+ props,
3182
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
3183
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
3184
+ }
3185
+ ) });
2779
3186
  };
2780
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2781
- return rows.map(
2782
- (row) => row.map((field) => {
2783
- const attribute = attributes[field.name];
2784
- if (!attribute) {
2785
- return null;
2786
- }
2787
- const { edit: metadata } = metadatas[field.name];
2788
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2789
- return {
2790
- attribute,
2791
- disabled: !metadata.editable,
2792
- hint: metadata.description,
2793
- label: metadata.label ?? "",
2794
- name: field.name,
2795
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2796
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2797
- schemas,
2798
- components: components?.schemas ?? {}
2799
- }),
2800
- placeholder: metadata.placeholder ?? "",
2801
- required: attribute.required ?? false,
2802
- size: field.size,
2803
- unique: "unique" in attribute ? attribute.unique : false,
2804
- visible: metadata.visible ?? true,
2805
- type: attribute.type
2806
- };
2807
- }).filter((field) => field !== null)
2808
- );
2809
- };
2810
- const formatListLayout = (data, {
2811
- schemas,
2812
- schema,
2813
- components
2814
- }) => {
2815
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2816
- (acc, [attribute, metadata]) => {
2817
- return {
2818
- ...acc,
2819
- [attribute]: metadata.list
2820
- };
2821
- },
2822
- {}
2823
- );
2824
- const listAttributes = convertListLayoutToFieldLayouts(
2825
- data.contentType.layouts.list,
2826
- schema?.attributes,
2827
- listMetadatas,
2828
- { configurations: data.components, schemas: components },
2829
- schemas
2830
- );
3187
+ const ActionsPanel = () => {
3188
+ const { formatMessage } = useIntl();
2831
3189
  return {
2832
- layout: listAttributes,
2833
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2834
- metadatas: listMetadatas,
2835
- options: {
2836
- ...schema?.options,
2837
- ...schema?.pluginOptions,
2838
- ...data.contentType.options
2839
- }
3190
+ title: formatMessage({
3191
+ id: "content-manager.containers.edit.panels.default.title",
3192
+ defaultMessage: "Entry"
3193
+ }),
3194
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2840
3195
  };
2841
3196
  };
2842
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2843
- return columns.map((name) => {
2844
- const attribute = attributes[name];
2845
- if (!attribute) {
2846
- return null;
3197
+ ActionsPanel.type = "actions";
3198
+ const ActionsPanelContent = () => {
3199
+ const isCloning = useMatch(CLONE_PATH) !== null;
3200
+ const [
3201
+ {
3202
+ query: { status = "draft" }
2847
3203
  }
2848
- const metadata = metadatas[name];
2849
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2850
- return {
2851
- attribute,
2852
- label: metadata.label ?? "",
2853
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2854
- schemas,
2855
- components: components?.schemas ?? {}
2856
- }),
2857
- name,
2858
- searchable: metadata.searchable ?? true,
2859
- sortable: metadata.sortable ?? true
2860
- };
2861
- }).filter((field) => field !== null);
3204
+ ] = useQueryParams();
3205
+ const { model, id, document, meta, collectionType } = useDoc();
3206
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
3207
+ const props = {
3208
+ activeTab: status,
3209
+ model,
3210
+ documentId: id,
3211
+ document: isCloning ? void 0 : document,
3212
+ meta: isCloning ? void 0 : meta,
3213
+ collectionType
3214
+ };
3215
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
3216
+ /* @__PURE__ */ jsx(
3217
+ DescriptionComponentRenderer,
3218
+ {
3219
+ props,
3220
+ descriptions: plugins["content-manager"].apis.getDocumentActions("panel"),
3221
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
3222
+ }
3223
+ ),
3224
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
3225
+ ] });
2862
3226
  };
3227
+ const Panel = React.forwardRef(({ children, title }, ref) => {
3228
+ return /* @__PURE__ */ jsxs(
3229
+ Flex,
3230
+ {
3231
+ ref,
3232
+ tag: "aside",
3233
+ "aria-labelledby": "additional-information",
3234
+ background: "neutral0",
3235
+ borderColor: "neutral150",
3236
+ hasRadius: true,
3237
+ paddingBottom: 4,
3238
+ paddingLeft: 4,
3239
+ paddingRight: 4,
3240
+ paddingTop: 4,
3241
+ shadow: "tableShadow",
3242
+ gap: 3,
3243
+ direction: "column",
3244
+ justifyContent: "stretch",
3245
+ alignItems: "flex-start",
3246
+ children: [
3247
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3248
+ children
3249
+ ]
3250
+ }
3251
+ );
3252
+ });
2863
3253
  const ConfirmBulkActionDialog = ({
2864
3254
  onToggleDialog,
2865
3255
  isOpen = false,
@@ -2885,7 +3275,7 @@ const ConfirmBulkActionDialog = ({
2885
3275
  ] })
2886
3276
  ] }) });
2887
3277
  };
2888
- const BoldChunk$1 = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
3278
+ const BoldChunk = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
2889
3279
  const ConfirmDialogPublishAll = ({
2890
3280
  isOpen,
2891
3281
  onToggleDialog,
@@ -2898,6 +3288,7 @@ const ConfirmDialogPublishAll = ({
2898
3288
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2899
3289
  const { model, schema } = useDoc();
2900
3290
  const [{ query }] = useQueryParams();
3291
+ const enableDraftRelationsCount = false;
2901
3292
  const {
2902
3293
  data: countDraftRelations = 0,
2903
3294
  isLoading,
@@ -2909,7 +3300,7 @@ const ConfirmDialogPublishAll = ({
2909
3300
  locale: query?.plugins?.i18n?.locale
2910
3301
  },
2911
3302
  {
2912
- skip: selectedEntries.length === 0
3303
+ skip: !enableDraftRelationsCount
2913
3304
  }
2914
3305
  );
2915
3306
  React.useEffect(() => {
@@ -2933,7 +3324,7 @@ const ConfirmDialogPublishAll = ({
2933
3324
  defaultMessage: "<b>{count} {count, plural, one { relation } other { relations } } out of {entities} { entities, plural, one { entry } other { entries } } {count, plural, one { is } other { are } }</b> not published yet and might lead to unexpected behavior. "
2934
3325
  },
2935
3326
  {
2936
- b: BoldChunk$1,
3327
+ b: BoldChunk,
2937
3328
  count: countDraftRelations,
2938
3329
  entities: selectedEntries.length
2939
3330
  }
@@ -2972,6 +3363,16 @@ const ConfirmDialogPublishAll = ({
2972
3363
  const TypographyMaxWidth = styled(Typography)`
2973
3364
  max-width: 300px;
2974
3365
  `;
3366
+ const TableComponent = styled(RawTable)`
3367
+ width: 100%;
3368
+ table-layout: fixed;
3369
+ td:first-child {
3370
+ border-right: 1px solid ${({ theme }) => theme.colors.neutral150};
3371
+ }
3372
+ td:first-of-type {
3373
+ padding: ${({ theme }) => theme.spaces[4]};
3374
+ }
3375
+ `;
2975
3376
  const formatErrorMessages = (errors, parentKey, formatMessage) => {
2976
3377
  const messages = [];
2977
3378
  Object.entries(errors).forEach(([key, value]) => {
@@ -3076,7 +3477,7 @@ const SelectedEntriesTableContent = ({
3076
3477
  )
3077
3478
  ] }),
3078
3479
  /* @__PURE__ */ jsx(Table.Loading, {}),
3079
- /* @__PURE__ */ jsx(Table.Body, { children: rowsToDisplay.map((row, index2) => /* @__PURE__ */ jsxs(Table.Row, { children: [
3480
+ /* @__PURE__ */ jsx(Table.Body, { children: rowsToDisplay.map((row) => /* @__PURE__ */ jsxs(Table.Row, { children: [
3080
3481
  /* @__PURE__ */ jsx(Table.CheckboxCell, { id: row.id }),
3081
3482
  /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Typography, { children: row.id }) }),
3082
3483
  shouldDisplayMainField && /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Typography, { children: row[mainField] }) }),
@@ -3094,7 +3495,7 @@ const SelectedEntriesTableContent = ({
3094
3495
  status: row.status
3095
3496
  }
3096
3497
  ) }),
3097
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3498
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3098
3499
  IconButton,
3099
3500
  {
3100
3501
  tag: Link,
@@ -3103,27 +3504,86 @@ const SelectedEntriesTableContent = ({
3103
3504
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3104
3505
  },
3105
3506
  state: { from: pathname },
3106
- label: formatMessage(
3107
- { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3108
- {
3109
- target: formatMessage(
3110
- {
3111
- id: "content-manager.components.ListViewHelperPluginTable.row-line",
3112
- defaultMessage: "item line {number}"
3113
- },
3114
- { number: index2 + 1 }
3115
- )
3116
- }
3117
- ),
3507
+ label: formatMessage({
3508
+ id: "content-manager.bulk-publish.edit",
3509
+ defaultMessage: "Edit"
3510
+ }),
3118
3511
  target: "_blank",
3119
3512
  marginLeft: "auto",
3120
- children: /* @__PURE__ */ jsx(Pencil, {})
3513
+ variant: "ghost",
3514
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3121
3515
  }
3122
- ) })
3516
+ ) }) })
3123
3517
  ] }, row.id)) })
3124
3518
  ] });
3125
3519
  };
3126
- const BoldChunk = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
3520
+ const PublicationStatusSummary = ({ count, icon, message }) => {
3521
+ return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", flex: 1, gap: 3, children: [
3522
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3523
+ icon,
3524
+ /* @__PURE__ */ jsx(Typography, { children: message })
3525
+ ] }),
3526
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: count })
3527
+ ] });
3528
+ };
3529
+ const PublicationStatusGrid = ({
3530
+ entriesReadyToPublishCount,
3531
+ entriesPublishedCount,
3532
+ entriesModifiedCount,
3533
+ entriesWithErrorsCount
3534
+ }) => {
3535
+ const { formatMessage } = useIntl();
3536
+ return /* @__PURE__ */ jsx(Box, { hasRadius: true, borderColor: "neutral150", children: /* @__PURE__ */ jsx(TableComponent, { colCount: 2, rowCount: 2, children: /* @__PURE__ */ jsxs(Tbody, { children: [
3537
+ /* @__PURE__ */ jsxs(Tr, { children: [
3538
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
3539
+ PublicationStatusSummary,
3540
+ {
3541
+ count: entriesReadyToPublishCount,
3542
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
3543
+ message: formatMessage({
3544
+ id: "app.utils.ready-to-publish",
3545
+ defaultMessage: "Ready to publish"
3546
+ })
3547
+ }
3548
+ ) }),
3549
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
3550
+ PublicationStatusSummary,
3551
+ {
3552
+ count: entriesPublishedCount,
3553
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
3554
+ message: formatMessage({
3555
+ id: "app.utils.already-published",
3556
+ defaultMessage: "Already published"
3557
+ })
3558
+ }
3559
+ ) })
3560
+ ] }),
3561
+ /* @__PURE__ */ jsxs(Tr, { children: [
3562
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
3563
+ PublicationStatusSummary,
3564
+ {
3565
+ count: entriesModifiedCount,
3566
+ icon: /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
3567
+ message: formatMessage({
3568
+ id: "content-manager.bulk-publish.modified",
3569
+ defaultMessage: "Ready to publish changes"
3570
+ })
3571
+ }
3572
+ ) }),
3573
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(
3574
+ PublicationStatusSummary,
3575
+ {
3576
+ count: entriesWithErrorsCount,
3577
+ icon: /* @__PURE__ */ jsx(CrossCircle, { fill: "danger600" }),
3578
+ message: formatMessage({
3579
+ id: "content-manager.bulk-publish.waiting-for-action",
3580
+ defaultMessage: "Waiting for action"
3581
+ })
3582
+ }
3583
+ ) })
3584
+ ] })
3585
+ ] }) }) });
3586
+ };
3127
3587
  const SelectedEntriesModalContent = ({
3128
3588
  listViewSelectedEntries,
3129
3589
  toggleModal,
@@ -3156,7 +3616,13 @@ const SelectedEntriesModalContent = ({
3156
3616
  );
3157
3617
  const { rows, validationErrors } = React.useMemo(() => {
3158
3618
  if (data.length > 0 && schema) {
3159
- const validate = createYupSchema(schema.attributes, components);
3619
+ const validate = createYupSchema(
3620
+ schema.attributes,
3621
+ components,
3622
+ // Since this is the "Publish" action, the validation
3623
+ // schema must enforce the rules for published entities
3624
+ { status: "published" }
3625
+ );
3160
3626
  const validationErrors2 = {};
3161
3627
  const rows2 = data.map((entry) => {
3162
3628
  try {
@@ -3176,7 +3642,6 @@ const SelectedEntriesModalContent = ({
3176
3642
  validationErrors: {}
3177
3643
  };
3178
3644
  }, [components, data, schema]);
3179
- const [publishedCount, setPublishedCount] = React.useState(0);
3180
3645
  const [isDialogOpen, setIsDialogOpen] = React.useState(false);
3181
3646
  const { publishMany: bulkPublishAction } = useDocumentActions();
3182
3647
  const [, { isLoading: isSubmittingForm }] = usePublishManyDocumentsMutation();
@@ -3188,53 +3653,36 @@ const SelectedEntriesModalContent = ({
3188
3653
  const selectedEntriesWithErrorsCount = selectedEntries.filter(
3189
3654
  ({ documentId }) => validationErrors[documentId]
3190
3655
  ).length;
3191
- const selectedEntriesPublished = selectedEntries.filter(
3656
+ const selectedEntriesPublishedCount = selectedEntries.filter(
3192
3657
  ({ status }) => status === "published"
3193
3658
  ).length;
3194
- const selectedEntriesWithNoErrorsCount = selectedEntries.length - selectedEntriesWithErrorsCount - selectedEntriesPublished;
3659
+ const selectedEntriesModifiedCount = selectedEntries.filter(
3660
+ ({ status, documentId }) => status === "modified" && !validationErrors[documentId]
3661
+ ).length;
3662
+ const selectedEntriesWithNoErrorsCount = selectedEntries.length - selectedEntriesWithErrorsCount - selectedEntriesPublishedCount;
3195
3663
  const toggleDialog = () => setIsDialogOpen((prev) => !prev);
3196
3664
  const handleConfirmBulkPublish = async () => {
3197
3665
  toggleDialog();
3198
3666
  const res = await bulkPublishAction({ model, documentIds: entriesToPublish, params });
3199
3667
  if (!("error" in res)) {
3200
- setPublishedCount(res.count);
3201
3668
  const unpublishedEntries = rows.filter((row) => {
3202
3669
  return !entriesToPublish.includes(row.documentId);
3203
3670
  });
3204
3671
  setListViewSelectedDocuments(unpublishedEntries);
3205
3672
  }
3206
3673
  };
3207
- const getFormattedCountMessage = () => {
3208
- if (publishedCount) {
3209
- return formatMessage(
3210
- {
3211
- id: getTranslation("containers.list.selectedEntriesModal.publishedCount"),
3212
- defaultMessage: "<b>{publishedCount}</b> {publishedCount, plural, =0 {entries} one {entry} other {entries}} published. <b>{withErrorsCount}</b> {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action."
3213
- },
3214
- {
3215
- publishedCount,
3216
- withErrorsCount: selectedEntriesWithErrorsCount,
3217
- b: BoldChunk
3218
- }
3219
- );
3220
- }
3221
- return formatMessage(
3222
- {
3223
- id: getTranslation("containers.list.selectedEntriesModal.selectedCount"),
3224
- defaultMessage: "<b>{alreadyPublishedCount}</b> {alreadyPublishedCount, plural, =0 {entries} one {entry} other {entries}} already published. <b>{readyToPublishCount}</b> {readyToPublishCount, plural, =0 {entries} one {entry} other {entries}} ready to publish. <b>{withErrorsCount}</b> {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action."
3225
- },
3226
- {
3227
- readyToPublishCount: selectedEntriesWithNoErrorsCount,
3228
- withErrorsCount: selectedEntriesWithErrorsCount,
3229
- alreadyPublishedCount: selectedEntriesPublished,
3230
- b: BoldChunk
3231
- }
3232
- );
3233
- };
3234
3674
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3235
3675
  /* @__PURE__ */ jsxs(Modal.Body, { children: [
3236
- /* @__PURE__ */ jsx(Typography, { children: getFormattedCountMessage() }),
3237
- /* @__PURE__ */ jsx(Box, { marginTop: 5, children: /* @__PURE__ */ jsx(
3676
+ /* @__PURE__ */ jsx(
3677
+ PublicationStatusGrid,
3678
+ {
3679
+ entriesReadyToPublishCount: selectedEntriesWithNoErrorsCount - selectedEntriesModifiedCount,
3680
+ entriesPublishedCount: selectedEntriesPublishedCount,
3681
+ entriesModifiedCount: selectedEntriesModifiedCount,
3682
+ entriesWithErrorsCount: selectedEntriesWithErrorsCount
3683
+ }
3684
+ ),
3685
+ /* @__PURE__ */ jsx(Box, { marginTop: 7, children: /* @__PURE__ */ jsx(
3238
3686
  SelectedEntriesTableContent,
3239
3687
  {
3240
3688
  isPublishing: isSubmittingForm,
@@ -3255,7 +3703,7 @@ const SelectedEntriesModalContent = ({
3255
3703
  Button,
3256
3704
  {
3257
3705
  onClick: toggleDialog,
3258
- disabled: selectedEntries.length === 0 || selectedEntries.length === selectedEntriesWithErrorsCount || selectedEntriesPublished === selectedEntries.length || isLoading,
3706
+ disabled: selectedEntries.length === 0 || selectedEntries.length === selectedEntriesWithErrorsCount || selectedEntriesPublishedCount === selectedEntries.length || isLoading,
3259
3707
  loading: isSubmittingForm,
3260
3708
  children: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" })
3261
3709
  }
@@ -3281,8 +3729,7 @@ const PublishAction = ({ documents, model }) => {
3281
3729
  const refetchList = () => {
3282
3730
  contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3283
3731
  };
3284
- if (!showPublishButton)
3285
- return null;
3732
+ if (!showPublishButton) return null;
3286
3733
  return {
3287
3734
  actionType: "publish",
3288
3735
  variant: "tertiary",
@@ -3350,8 +3797,7 @@ const DeleteAction = ({ documents, model }) => {
3350
3797
  selectRow([]);
3351
3798
  }
3352
3799
  };
3353
- if (!hasDeletePermission)
3354
- return null;
3800
+ if (!hasDeletePermission) return null;
3355
3801
  return {
3356
3802
  variant: "danger-light",
3357
3803
  label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
@@ -3400,8 +3846,7 @@ const UnpublishAction = ({ documents, model }) => {
3400
3846
  }
3401
3847
  };
3402
3848
  const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3403
- if (!showUnpublishButton)
3404
- return null;
3849
+ if (!showUnpublishButton) return null;
3405
3850
  return {
3406
3851
  variant: "tertiary",
3407
3852
  label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
@@ -3506,7 +3951,7 @@ const TableActions = ({ document }) => {
3506
3951
  DescriptionComponentRenderer,
3507
3952
  {
3508
3953
  props,
3509
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3954
+ descriptions: plugins["content-manager"].apis.getDocumentActions("table-row").filter((action) => action.name !== "PublishAction"),
3510
3955
  children: (actions2) => {
3511
3956
  const tableRowActions = actions2.filter((action) => {
3512
3957
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3565,6 +4010,7 @@ const EditAction = ({ documentId }) => {
3565
4010
  };
3566
4011
  };
3567
4012
  EditAction.type = "edit";
4013
+ EditAction.position = "table-row";
3568
4014
  const StyledPencil = styled(Pencil)`
3569
4015
  path {
3570
4016
  fill: currentColor;
@@ -3617,7 +4063,7 @@ const CloneAction = ({ model, documentId }) => {
3617
4063
  }),
3618
4064
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3619
4065
  footer: ({ onClose }) => {
3620
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
4066
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3621
4067
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3622
4068
  id: "cancel",
3623
4069
  defaultMessage: "Cancel"
@@ -3641,6 +4087,7 @@ const CloneAction = ({ model, documentId }) => {
3641
4087
  };
3642
4088
  };
3643
4089
  CloneAction.type = "clone";
4090
+ CloneAction.position = "table-row";
3644
4091
  const StyledDuplicate = styled(Duplicate)`
3645
4092
  path {
3646
4093
  fill: currentColor;
@@ -3727,7 +4174,14 @@ class ContentManagerPlugin {
3727
4174
  addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3728
4175
  addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3729
4176
  getBulkActions: () => this.bulkActions,
3730
- getDocumentActions: () => this.documentActions,
4177
+ getDocumentActions: (position) => {
4178
+ if (position) {
4179
+ return this.documentActions.filter(
4180
+ (action) => action.position == void 0 || [action.position].flat().includes(position)
4181
+ );
4182
+ }
4183
+ return this.documentActions;
4184
+ },
3731
4185
  getEditViewSidePanels: () => this.editViewSidePanels,
3732
4186
  getHeaderActions: () => this.headerActions
3733
4187
  }
@@ -3737,10 +4191,8 @@ class ContentManagerPlugin {
3737
4191
  const getPrintableType = (value) => {
3738
4192
  const nativeType = typeof value;
3739
4193
  if (nativeType === "object") {
3740
- if (value === null)
3741
- return "null";
3742
- if (Array.isArray(value))
3743
- return "array";
4194
+ if (value === null) return "null";
4195
+ if (Array.isArray(value)) return "array";
3744
4196
  if (value instanceof Object && value.constructor.name !== "Object") {
3745
4197
  return value.constructor.name;
3746
4198
  }
@@ -3751,17 +4203,27 @@ const HistoryAction = ({ model, document }) => {
3751
4203
  const { formatMessage } = useIntl();
3752
4204
  const [{ query }] = useQueryParams();
3753
4205
  const navigate = useNavigate();
4206
+ const { trackUsage } = useTracking();
4207
+ const { pathname } = useLocation();
3754
4208
  const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
3755
4209
  if (!window.strapi.features.isEnabled("cms-content-history")) {
3756
4210
  return null;
3757
4211
  }
4212
+ const handleOnClick = () => {
4213
+ const destination = { pathname: "history", search: pluginsQueryParams };
4214
+ trackUsage("willNavigate", {
4215
+ from: pathname,
4216
+ to: `${pathname}/${destination.pathname}`
4217
+ });
4218
+ navigate(destination);
4219
+ };
3758
4220
  return {
3759
4221
  icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
3760
4222
  label: formatMessage({
3761
4223
  id: "content-manager.history.document-action",
3762
4224
  defaultMessage: "Content History"
3763
4225
  }),
3764
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
4226
+ onClick: handleOnClick,
3765
4227
  disabled: (
3766
4228
  /**
3767
4229
  * The user is creating a new document.
@@ -3783,6 +4245,7 @@ const HistoryAction = ({ model, document }) => {
3783
4245
  };
3784
4246
  };
3785
4247
  HistoryAction.type = "history";
4248
+ HistoryAction.position = "header";
3786
4249
  const historyAdmin = {
3787
4250
  bootstrap(app) {
3788
4251
  const { addDocumentAction } = app.getPlugin("content-manager").apis;
@@ -3829,6 +4292,88 @@ const { setInitialData } = actions;
3829
4292
  const reducer = combineReducers({
3830
4293
  app: reducer$1
3831
4294
  });
4295
+ const previewApi = contentManagerApi.injectEndpoints({
4296
+ endpoints: (builder) => ({
4297
+ getPreviewUrl: builder.query({
4298
+ query({ query, params }) {
4299
+ return {
4300
+ url: `/content-manager/preview/url/${params.contentType}`,
4301
+ method: "GET",
4302
+ config: {
4303
+ params: query
4304
+ }
4305
+ };
4306
+ }
4307
+ })
4308
+ })
4309
+ });
4310
+ const { useGetPreviewUrlQuery } = previewApi;
4311
+ const ConditionalTooltip = ({ isShown, label, children }) => {
4312
+ if (isShown) {
4313
+ return /* @__PURE__ */ jsx(Tooltip, { label, children });
4314
+ }
4315
+ return children;
4316
+ };
4317
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4318
+ const { formatMessage } = useIntl();
4319
+ const { trackUsage } = useTracking();
4320
+ const { pathname } = useLocation();
4321
+ const [{ query }] = useQueryParams();
4322
+ const isModified = useForm("PreviewSidePanel", (state) => state.modified);
4323
+ const { data, error } = useGetPreviewUrlQuery({
4324
+ params: {
4325
+ contentType: model
4326
+ },
4327
+ query: {
4328
+ documentId,
4329
+ locale: document?.locale,
4330
+ status: document?.status
4331
+ }
4332
+ });
4333
+ if (!data?.data?.url || error) {
4334
+ return null;
4335
+ }
4336
+ const trackNavigation = () => {
4337
+ const destinationPathname = pathname.replace(/\/$/, "") + "/preview";
4338
+ trackUsage("willNavigate", { from: pathname, to: destinationPathname });
4339
+ };
4340
+ return {
4341
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4342
+ content: /* @__PURE__ */ jsx(
4343
+ ConditionalTooltip,
4344
+ {
4345
+ label: formatMessage({
4346
+ id: "content-manager.preview.panel.button-disabled-tooltip",
4347
+ defaultMessage: "Please save to open the preview"
4348
+ }),
4349
+ isShown: isModified,
4350
+ children: /* @__PURE__ */ jsx(Box, { cursor: "not-allowed", width: "100%", children: /* @__PURE__ */ jsx(
4351
+ Button,
4352
+ {
4353
+ variant: "tertiary",
4354
+ tag: Link,
4355
+ to: { pathname: "preview", search: stringify(query, { encode: false }) },
4356
+ onClick: trackNavigation,
4357
+ width: "100%",
4358
+ disabled: isModified,
4359
+ pointerEvents: isModified ? "none" : void 0,
4360
+ tabIndex: isModified ? -1 : void 0,
4361
+ children: formatMessage({
4362
+ id: "content-manager.preview.panel.button",
4363
+ defaultMessage: "Open preview"
4364
+ })
4365
+ }
4366
+ ) })
4367
+ }
4368
+ )
4369
+ };
4370
+ };
4371
+ const previewAdmin = {
4372
+ bootstrap(app) {
4373
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4374
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4375
+ }
4376
+ };
3832
4377
  const index = {
3833
4378
  register(app) {
3834
4379
  const cm = new ContentManagerPlugin();
@@ -3848,7 +4393,7 @@ const index = {
3848
4393
  app.router.addRoute({
3849
4394
  path: "content-manager/*",
3850
4395
  lazy: async () => {
3851
- const { Layout } = await import("./layout-oPBiO7RY.mjs");
4396
+ const { Layout } = await import("./layout-CxDMdJ13.mjs");
3852
4397
  return {
3853
4398
  Component: Layout
3854
4399
  };
@@ -3861,11 +4406,14 @@ const index = {
3861
4406
  if (typeof historyAdmin.bootstrap === "function") {
3862
4407
  historyAdmin.bootstrap(app);
3863
4408
  }
4409
+ if (typeof previewAdmin.bootstrap === "function") {
4410
+ previewAdmin.bootstrap(app);
4411
+ }
3864
4412
  },
3865
4413
  async registerTrads({ locales }) {
3866
4414
  const importedTrads = await Promise.all(
3867
4415
  locales.map((locale) => {
3868
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-BrCTWlZv.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
4416
+ 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-D65uIF6Y.mjs"), "./translations/es.json": () => import("./es-D34tqjMw.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-DBseuRuB.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-BHqhDq4V.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`, 3).then(({ default: data }) => {
3869
4417
  return {
3870
4418
  data: prefixPluginTranslations(data, PLUGIN_ID),
3871
4419
  locale
@@ -3882,21 +4430,27 @@ const index = {
3882
4430
  }
3883
4431
  };
3884
4432
  export {
3885
- ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD as A,
4433
+ useUpdateContentTypeConfigurationMutation as A,
3886
4434
  BulkActionsRenderer as B,
3887
4435
  COLLECTION_TYPES as C,
3888
4436
  DocumentStatus as D,
3889
- DEFAULT_SETTINGS as E,
3890
- convertEditLayoutToFieldLayouts as F,
3891
- useDocument as G,
4437
+ ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD as E,
4438
+ extractContentTypeComponents as F,
4439
+ DEFAULT_SETTINGS as G,
3892
4440
  HOOKS as H,
3893
4441
  InjectionZone as I,
3894
- index as J,
3895
- useDocumentActions as K,
4442
+ convertEditLayoutToFieldLayouts as J,
4443
+ removeFieldsThatDontExistOnSchema as K,
4444
+ prepareTempKeys as L,
4445
+ useDocument as M,
4446
+ useGetPreviewUrlQuery as N,
4447
+ index as O,
3896
4448
  Panels as P,
4449
+ useContentManagerContext as Q,
3897
4450
  RelativeTime as R,
3898
4451
  SINGLE_TYPES as S,
3899
4452
  TableActions as T,
4453
+ useDocumentActions as U,
3900
4454
  useGetInitialDataQuery as a,
3901
4455
  useGetAllContentTypeSettingsQuery as b,
3902
4456
  useDoc as c,
@@ -3909,19 +4463,19 @@ export {
3909
4463
  Header as j,
3910
4464
  PERMISSIONS as k,
3911
4465
  DocumentRBAC as l,
3912
- DOCUMENT_META_FIELDS as m,
3913
- useDocLayout as n,
3914
- useGetContentTypeConfigurationQuery as o,
3915
- CREATOR_FIELDS as p,
3916
- getMainField as q,
3917
- getDisplayName as r,
4466
+ useDocLayout as m,
4467
+ createDefaultForm as n,
4468
+ CLONE_PATH as o,
4469
+ useGetContentTypeConfigurationQuery as p,
4470
+ CREATOR_FIELDS as q,
4471
+ getMainField as r,
3918
4472
  setInitialData as s,
3919
- checkIfAttributeIsDisplayable as t,
4473
+ transformDocument as t,
3920
4474
  useContentTypeSchema as u,
3921
- useGetAllDocumentsQuery as v,
3922
- convertListLayoutToFieldLayouts as w,
3923
- capitalise as x,
3924
- useUpdateContentTypeConfigurationMutation as y,
3925
- extractContentTypeComponents as z
4475
+ getDisplayName as v,
4476
+ checkIfAttributeIsDisplayable as w,
4477
+ useGetAllDocumentsQuery as x,
4478
+ convertListLayoutToFieldLayouts as y,
4479
+ capitalise as z
3926
4480
  };
3927
- //# sourceMappingURL=index-c_5DdJi-.mjs.map
4481
+ //# sourceMappingURL=index-EH8ZtHd5.mjs.map