@strapi/content-manager 0.0.0-experimental.f6c00790e260ea5a9b6b86abac5fea02b05d569c → 0.0.0-experimental.f6f58027a6338ed795382f2d1c8882158b242361

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 (210) 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-CnL10QYC.mjs → ComponentConfigurationPage-D4H-v0et.mjs} +4 -4
  4. package/dist/_chunks/{ComponentConfigurationPage-CnL10QYC.mjs.map → ComponentConfigurationPage-D4H-v0et.mjs.map} +1 -1
  5. package/dist/_chunks/{ComponentConfigurationPage-G4EIirP8.js → ComponentConfigurationPage-DdkVGfXC.js} +5 -6
  6. package/dist/_chunks/{ComponentConfigurationPage-G4EIirP8.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-I2kKh9dx.mjs → EditConfigurationPage-D1nvB7Br.mjs} +4 -4
  11. package/dist/_chunks/{EditConfigurationPage-I2kKh9dx.mjs.map → EditConfigurationPage-D1nvB7Br.mjs.map} +1 -1
  12. package/dist/_chunks/{EditConfigurationPage-B2AA1kVF.js → EditConfigurationPage-LYEvR4fW.js} +5 -6
  13. package/dist/_chunks/{EditConfigurationPage-B2AA1kVF.js.map → EditConfigurationPage-LYEvR4fW.js.map} +1 -1
  14. package/dist/_chunks/{EditViewPage-zFjJK0s8.mjs → EditViewPage-Bcnff6Vd.mjs} +32 -55
  15. package/dist/_chunks/EditViewPage-Bcnff6Vd.mjs.map +1 -0
  16. package/dist/_chunks/{EditViewPage-CHgoNwlc.js → EditViewPage-DqelJ9UK.js} +34 -58
  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-DPm-KZ1A.js → Form-CnHfBeiB.js} +6 -7
  21. package/dist/_chunks/Form-CnHfBeiB.js.map +1 -0
  22. package/dist/_chunks/{Form-CEkENbkF.mjs → Form-CzPCJi3B.mjs} +4 -4
  23. package/dist/_chunks/Form-CzPCJi3B.mjs.map +1 -0
  24. package/dist/_chunks/{History-utls71em.mjs → History-CcmSn3Mj.mjs} +71 -104
  25. package/dist/_chunks/History-CcmSn3Mj.mjs.map +1 -0
  26. package/dist/_chunks/{History-DXSbTWez.js → History-zArjENzj.js} +81 -115
  27. package/dist/_chunks/History-zArjENzj.js.map +1 -0
  28. package/dist/_chunks/{Field-9DePZh-0.js → Input-CDHKQd7o.js} +1304 -1237
  29. package/dist/_chunks/Input-CDHKQd7o.js.map +1 -0
  30. package/dist/_chunks/{Field-DPAzUS1M.mjs → Input-aV8SSoTa.mjs} +1288 -1221
  31. package/dist/_chunks/Input-aV8SSoTa.mjs.map +1 -0
  32. package/dist/_chunks/{ListConfigurationPage-CuMXWWqb.mjs → ListConfigurationPage-BPvzENJJ.mjs} +7 -6
  33. package/dist/_chunks/ListConfigurationPage-BPvzENJJ.mjs.map +1 -0
  34. package/dist/_chunks/{ListConfigurationPage-D5C7ACZ_.js → ListConfigurationPage-ByZAO_9H.js} +7 -7
  35. package/dist/_chunks/ListConfigurationPage-ByZAO_9H.js.map +1 -0
  36. package/dist/_chunks/{ListViewPage-DfuwH1tt.js → ListViewPage-BVKBeQAA.js} +74 -49
  37. package/dist/_chunks/ListViewPage-BVKBeQAA.js.map +1 -0
  38. package/dist/_chunks/{ListViewPage-CdKd-PS_.mjs → ListViewPage-HljQVnFH.mjs} +68 -42
  39. package/dist/_chunks/ListViewPage-HljQVnFH.mjs.map +1 -0
  40. package/dist/_chunks/{NoContentTypePage-BIxlkWWi.js → NoContentTypePage-BV5zfDxr.js} +2 -2
  41. package/dist/_chunks/{NoContentTypePage-BIxlkWWi.js.map → NoContentTypePage-BV5zfDxr.js.map} +1 -1
  42. package/dist/_chunks/{NoContentTypePage-DkToTT7u.mjs → NoContentTypePage-BfHaSM-K.mjs} +2 -2
  43. package/dist/_chunks/{NoContentTypePage-DkToTT7u.mjs.map → NoContentTypePage-BfHaSM-K.mjs.map} +1 -1
  44. package/dist/_chunks/{NoPermissionsPage-DlWi4BAH.mjs → NoPermissionsPage-D6ze2nQL.mjs} +2 -2
  45. package/dist/_chunks/{NoPermissionsPage-DlWi4BAH.mjs.map → NoPermissionsPage-D6ze2nQL.mjs.map} +1 -1
  46. package/dist/_chunks/{NoPermissionsPage-Bu4GWYb-.js → NoPermissionsPage-vdNpc6jb.js} +2 -2
  47. package/dist/_chunks/{NoPermissionsPage-Bu4GWYb-.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-QP5yn9_z.mjs → Relations-B7_hbF0w.mjs} +78 -43
  53. package/dist/_chunks/Relations-B7_hbF0w.mjs.map +1 -0
  54. package/dist/_chunks/{Relations-CFjTESWQ.js → Relations-DcoOBejP.js} +78 -44
  55. package/dist/_chunks/Relations-DcoOBejP.js.map +1 -0
  56. package/dist/_chunks/{en-BVzUkPxZ.js → en-BR48D_RH.js} +30 -11
  57. package/dist/_chunks/{en-BVzUkPxZ.js.map → en-BR48D_RH.js.map} +1 -1
  58. package/dist/_chunks/{en-CPTj6CjC.mjs → en-D65uIF6Y.mjs} +30 -11
  59. package/dist/_chunks/{en-CPTj6CjC.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-DXiHxy70.js → index-CxLSGwnk.js} +1162 -706
  70. package/dist/_chunks/index-CxLSGwnk.js.map +1 -0
  71. package/dist/_chunks/{index-BHfS6_D5.mjs → index-EH8ZtHd5.mjs} +1178 -722
  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-DX_52HSH.mjs → layout-CxDMdJ13.mjs} +4 -4
  78. package/dist/_chunks/{layout-DX_52HSH.mjs.map → layout-CxDMdJ13.mjs.map} +1 -1
  79. package/dist/_chunks/{layout-bE-WUnQ0.js → layout-DSeUTfMv.js} +5 -6
  80. package/dist/_chunks/{layout-bE-WUnQ0.js.map → layout-DSeUTfMv.js.map} +1 -1
  81. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  82. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  83. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  84. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  85. package/dist/_chunks/{relations-SCVAL_aJ.mjs → relations-B8_Uu38Q.mjs} +21 -8
  86. package/dist/_chunks/relations-B8_Uu38Q.mjs.map +1 -0
  87. package/dist/_chunks/{relations-D706vblp.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/{useDebounce-CtcjDB3L.js → usePrev-B9w_-eYc.js} +1 -14
  94. package/dist/_chunks/usePrev-B9w_-eYc.js.map +1 -0
  95. package/dist/_chunks/usePrev-DH6iah0A.mjs +16 -0
  96. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +1 -0
  97. package/dist/admin/index.js +3 -1
  98. package/dist/admin/index.js.map +1 -1
  99. package/dist/admin/index.mjs +6 -4
  100. package/dist/admin/src/content-manager.d.ts +3 -2
  101. package/dist/admin/src/exports.d.ts +2 -1
  102. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  103. package/dist/admin/src/hooks/useDocument.d.ts +49 -1
  104. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  105. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -1
  106. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +3 -3
  107. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Code.d.ts +7 -0
  108. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/utils/prismLanguages.d.ts +49 -0
  109. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +1 -0
  110. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.d.ts +4 -1
  111. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +4 -1
  112. package/dist/admin/src/pages/EditView/components/FormLayout.d.ts +27 -0
  113. package/dist/admin/src/pages/EditView/components/Header.d.ts +1 -0
  114. package/dist/admin/src/pages/EditView/utils/data.d.ts +1 -0
  115. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  116. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  117. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  118. package/dist/admin/src/preview/index.d.ts +4 -0
  119. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  120. package/dist/admin/src/preview/routes.d.ts +3 -0
  121. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  122. package/dist/admin/src/router.d.ts +1 -1
  123. package/dist/admin/src/services/api.d.ts +1 -1
  124. package/dist/admin/src/services/components.d.ts +2 -2
  125. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  126. package/dist/admin/src/services/documents.d.ts +16 -19
  127. package/dist/admin/src/services/init.d.ts +1 -1
  128. package/dist/admin/src/services/relations.d.ts +2 -2
  129. package/dist/admin/src/services/uid.d.ts +3 -3
  130. package/dist/server/index.js +590 -333
  131. package/dist/server/index.js.map +1 -1
  132. package/dist/server/index.mjs +591 -333
  133. package/dist/server/index.mjs.map +1 -1
  134. package/dist/server/src/bootstrap.d.ts.map +1 -1
  135. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  136. package/dist/server/src/controllers/index.d.ts.map +1 -1
  137. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  138. package/dist/server/src/controllers/utils/metadata.d.ts +16 -1
  139. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  140. package/dist/server/src/history/controllers/history-version.d.ts +1 -1
  141. package/dist/server/src/history/controllers/history-version.d.ts.map +1 -1
  142. package/dist/server/src/history/services/history.d.ts +3 -3
  143. package/dist/server/src/history/services/history.d.ts.map +1 -1
  144. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  145. package/dist/server/src/history/services/utils.d.ts +6 -11
  146. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  147. package/dist/server/src/index.d.ts +7 -6
  148. package/dist/server/src/index.d.ts.map +1 -1
  149. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  150. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  151. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  152. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  153. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  154. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  155. package/dist/server/src/preview/index.d.ts +4 -0
  156. package/dist/server/src/preview/index.d.ts.map +1 -0
  157. package/dist/server/src/preview/routes/index.d.ts +8 -0
  158. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  159. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  160. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  161. package/dist/server/src/preview/services/index.d.ts +16 -0
  162. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  163. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  164. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  165. package/dist/server/src/preview/services/preview.d.ts +12 -0
  166. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  167. package/dist/server/src/preview/utils.d.ts +19 -0
  168. package/dist/server/src/preview/utils.d.ts.map +1 -0
  169. package/dist/server/src/register.d.ts.map +1 -1
  170. package/dist/server/src/routes/index.d.ts.map +1 -1
  171. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  172. package/dist/server/src/services/document-metadata.d.ts +12 -10
  173. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  174. package/dist/server/src/services/index.d.ts +7 -6
  175. package/dist/server/src/services/index.d.ts.map +1 -1
  176. package/dist/server/src/services/utils/populate.d.ts +2 -2
  177. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  178. package/dist/server/src/utils/index.d.ts +2 -0
  179. package/dist/server/src/utils/index.d.ts.map +1 -1
  180. package/dist/shared/contracts/index.d.ts +1 -0
  181. package/dist/shared/contracts/index.d.ts.map +1 -1
  182. package/dist/shared/contracts/preview.d.ts +27 -0
  183. package/dist/shared/contracts/preview.d.ts.map +1 -0
  184. package/dist/shared/index.js +4 -0
  185. package/dist/shared/index.js.map +1 -1
  186. package/dist/shared/index.mjs +4 -0
  187. package/dist/shared/index.mjs.map +1 -1
  188. package/package.json +16 -15
  189. package/dist/_chunks/EditViewPage-CHgoNwlc.js.map +0 -1
  190. package/dist/_chunks/EditViewPage-zFjJK0s8.mjs.map +0 -1
  191. package/dist/_chunks/Field-9DePZh-0.js.map +0 -1
  192. package/dist/_chunks/Field-DPAzUS1M.mjs.map +0 -1
  193. package/dist/_chunks/Form-CEkENbkF.mjs.map +0 -1
  194. package/dist/_chunks/Form-DPm-KZ1A.js.map +0 -1
  195. package/dist/_chunks/History-DXSbTWez.js.map +0 -1
  196. package/dist/_chunks/History-utls71em.mjs.map +0 -1
  197. package/dist/_chunks/ListConfigurationPage-CuMXWWqb.mjs.map +0 -1
  198. package/dist/_chunks/ListConfigurationPage-D5C7ACZ_.js.map +0 -1
  199. package/dist/_chunks/ListViewPage-CdKd-PS_.mjs.map +0 -1
  200. package/dist/_chunks/ListViewPage-DfuwH1tt.js.map +0 -1
  201. package/dist/_chunks/Relations-CFjTESWQ.js.map +0 -1
  202. package/dist/_chunks/Relations-QP5yn9_z.mjs.map +0 -1
  203. package/dist/_chunks/index-BHfS6_D5.mjs.map +0 -1
  204. package/dist/_chunks/index-DXiHxy70.js.map +0 -1
  205. package/dist/_chunks/relations-D706vblp.js.map +0 -1
  206. package/dist/_chunks/relations-SCVAL_aJ.mjs.map +0 -1
  207. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +0 -1
  208. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +0 -29
  209. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +0 -1
  210. package/strapi-server.js +0 -3
@@ -1,25 +1,34 @@
1
- import { More, Cross, WarningCircle, ListPlus, Pencil, Trash, Check, CrossCircle, 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, Typography, Dialog, Modal, Radio, Status, Box, SingleSelect, SingleSelectOption, IconButton, Loader, 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
9
  import { useParams, useNavigate, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
10
+ import { styled } from "styled-components";
9
11
  import * as yup from "yup";
10
12
  import { ValidationError } from "yup";
13
+ import { generateNKeysBetween } from "fractional-indexing";
11
14
  import pipe from "lodash/fp/pipe";
12
- import { intervalToDuration, isPast } from "date-fns";
13
- import { styled } from "styled-components";
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",
@@ -159,7 +283,8 @@ const contentManagerApi = adminApi.enhanceEndpoints({
159
283
  "InitialData",
160
284
  "HistoryVersion",
161
285
  "Relations",
162
- "UidAvailability"
286
+ "UidAvailability",
287
+ "RecentDocumentList"
163
288
  ]
164
289
  });
165
290
  const documentApi = contentManagerApi.injectEndpoints({
@@ -177,7 +302,7 @@ const documentApi = contentManagerApi.injectEndpoints({
177
302
  if (error) {
178
303
  return [];
179
304
  }
180
- return [{ type: "Document", id: `${model}_LIST` }];
305
+ return [{ type: "Document", id: `${model}_LIST` }, "RecentDocumentList"];
181
306
  }
182
307
  }),
183
308
  cloneDocument: builder.mutation({
@@ -191,7 +316,8 @@ const documentApi = contentManagerApi.injectEndpoints({
191
316
  }),
192
317
  invalidatesTags: (_result, _error, { model }) => [
193
318
  { type: "Document", id: `${model}_LIST` },
194
- { type: "UidAvailability", id: model }
319
+ { type: "UidAvailability", id: model },
320
+ "RecentDocumentList"
195
321
  ]
196
322
  }),
197
323
  /**
@@ -210,8 +336,21 @@ const documentApi = contentManagerApi.injectEndpoints({
210
336
  invalidatesTags: (result, _error, { model }) => [
211
337
  { type: "Document", id: `${model}_LIST` },
212
338
  "Relations",
213
- { type: "UidAvailability", id: model }
214
- ]
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
+ }
215
354
  }),
216
355
  deleteDocument: builder.mutation({
217
356
  query: ({ collectionType, model, documentId, params }) => ({
@@ -222,7 +361,8 @@ const documentApi = contentManagerApi.injectEndpoints({
222
361
  }
223
362
  }),
224
363
  invalidatesTags: (_result, _error, { collectionType, model }) => [
225
- { type: "Document", id: collectionType !== SINGLE_TYPES ? `${model}_LIST` : model }
364
+ { type: "Document", id: collectionType !== SINGLE_TYPES ? `${model}_LIST` : model },
365
+ "RecentDocumentList"
226
366
  ]
227
367
  }),
228
368
  deleteManyDocuments: builder.mutation({
@@ -234,7 +374,10 @@ const documentApi = contentManagerApi.injectEndpoints({
234
374
  params
235
375
  }
236
376
  }),
237
- invalidatesTags: (_res, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
377
+ invalidatesTags: (_res, _error, { model }) => [
378
+ { type: "Document", id: `${model}_LIST` },
379
+ "RecentDocumentList"
380
+ ]
238
381
  }),
239
382
  discardDocument: builder.mutation({
240
383
  query: ({ collectionType, model, documentId, params }) => ({
@@ -252,7 +395,8 @@ const documentApi = contentManagerApi.injectEndpoints({
252
395
  },
253
396
  { type: "Document", id: `${model}_LIST` },
254
397
  "Relations",
255
- { type: "UidAvailability", id: model }
398
+ { type: "UidAvailability", id: model },
399
+ "RecentDocumentList"
256
400
  ];
257
401
  }
258
402
  }),
@@ -265,7 +409,7 @@ const documentApi = contentManagerApi.injectEndpoints({
265
409
  url: `/content-manager/collection-types/${model}`,
266
410
  method: "GET",
267
411
  config: {
268
- params
412
+ params: stringify(params, { encode: true })
269
413
  }
270
414
  }),
271
415
  providesTags: (result, _error, arg) => {
@@ -347,7 +491,8 @@ const documentApi = contentManagerApi.injectEndpoints({
347
491
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
348
492
  },
349
493
  { type: "Document", id: `${model}_LIST` },
350
- "Relations"
494
+ "Relations",
495
+ "RecentDocumentList"
351
496
  ];
352
497
  }
353
498
  }),
@@ -378,7 +523,9 @@ const documentApi = contentManagerApi.injectEndpoints({
378
523
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
379
524
  },
380
525
  "Relations",
381
- { type: "UidAvailability", id: model }
526
+ { type: "UidAvailability", id: model },
527
+ "RecentDocumentList",
528
+ "RecentDocumentList"
382
529
  ];
383
530
  },
384
531
  async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
@@ -408,7 +555,8 @@ const documentApi = contentManagerApi.injectEndpoints({
408
555
  {
409
556
  type: "Document",
410
557
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
411
- }
558
+ },
559
+ "RecentDocumentList"
412
560
  ];
413
561
  }
414
562
  }),
@@ -421,7 +569,10 @@ const documentApi = contentManagerApi.injectEndpoints({
421
569
  params
422
570
  }
423
571
  }),
424
- 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
+ ]
425
576
  })
426
577
  })
427
578
  });
@@ -444,8 +595,7 @@ const {
444
595
  useUnpublishManyDocumentsMutation
445
596
  } = documentApi;
446
597
  const buildValidParams = (query) => {
447
- if (!query)
448
- return query;
598
+ if (!query) return query;
449
599
  const { plugins: _, ...validQueryParams } = {
450
600
  ...query,
451
601
  ...Object.values(query?.plugins ?? {}).reduce(
@@ -453,14 +603,29 @@ const buildValidParams = (query) => {
453
603
  {}
454
604
  )
455
605
  };
456
- if ("_q" in validQueryParams) {
457
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
458
- }
459
606
  return validQueryParams;
460
607
  };
461
608
  const isBaseQueryError = (error) => {
462
609
  return error.name !== void 0;
463
610
  };
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
+ });
464
629
  const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
465
630
  const createModelSchema = (attributes2) => yup.object().shape(
466
631
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
@@ -468,6 +633,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
468
633
  return acc;
469
634
  }
470
635
  const validations = [
636
+ addNullableValidation,
471
637
  addRequiredValidation,
472
638
  addMinLengthValidation,
473
639
  addMaxLengthValidation,
@@ -484,12 +650,12 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
484
650
  ...acc,
485
651
  [name]: transformSchema(
486
652
  yup.array().of(createModelSchema(attributes3).nullable(false))
487
- )
653
+ ).test(arrayValidator(attribute, options))
488
654
  };
489
655
  } else {
490
656
  return {
491
657
  ...acc,
492
- [name]: transformSchema(createModelSchema(attributes3))
658
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
493
659
  };
494
660
  }
495
661
  }
@@ -511,7 +677,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
511
677
  }
512
678
  )
513
679
  )
514
- )
680
+ ).test(arrayValidator(attribute, options))
515
681
  };
516
682
  case "relation":
517
683
  return {
@@ -523,7 +689,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
523
689
  } else if (Array.isArray(value)) {
524
690
  return yup.array().of(
525
691
  yup.object().shape({
526
- id: yup.string().required()
692
+ id: yup.number().required()
527
693
  })
528
694
  );
529
695
  } else if (typeof value === "object") {
@@ -609,17 +775,17 @@ const nullableSchema = (schema) => {
609
775
  schema
610
776
  );
611
777
  };
778
+ const addNullableValidation = () => (schema) => {
779
+ return nullableSchema(schema);
780
+ };
612
781
  const addRequiredValidation = (attribute, options) => (schema) => {
613
- if (options.status === "draft") {
614
- return nullableSchema(schema);
615
- }
616
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
617
- return schema.min(1, translatedErrors.required);
782
+ if (options.status === "draft" || !attribute.required) {
783
+ return schema;
618
784
  }
619
- if (attribute.required && attribute.type !== "relation") {
785
+ if (attribute.required && "required" in schema) {
620
786
  return schema.required(translatedErrors.required);
621
787
  }
622
- return nullableSchema(schema);
788
+ return schema;
623
789
  };
624
790
  const addMinLengthValidation = (attribute, options) => (schema) => {
625
791
  if (options.status === "draft") {
@@ -647,31 +813,12 @@ const addMaxLengthValidation = (attribute) => (schema) => {
647
813
  return schema;
648
814
  };
649
815
  const addMinValidation = (attribute, options) => (schema) => {
650
- if ("min" in attribute) {
816
+ if (options.status === "draft") {
817
+ return schema;
818
+ }
819
+ if ("min" in attribute && "min" in schema) {
651
820
  const min = toInteger(attribute.min);
652
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
653
- if (options.status !== "draft" && !attribute.required && "test" in schema && min) {
654
- return schema.test(
655
- "custom-min",
656
- {
657
- ...translatedErrors.min,
658
- values: {
659
- min: attribute.min
660
- }
661
- },
662
- (value) => {
663
- if (!value) {
664
- return true;
665
- }
666
- if (Array.isArray(value) && value.length === 0) {
667
- return true;
668
- }
669
- return value.length >= min;
670
- }
671
- );
672
- }
673
- }
674
- if ("min" in schema && min) {
821
+ if (min) {
675
822
  return schema.min(min, {
676
823
  ...translatedErrors.min,
677
824
  values: {
@@ -789,19 +936,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
789
936
  }, {});
790
937
  return componentsByKey;
791
938
  };
792
- 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);
793
1038
  const { toggleNotification } = useNotification();
794
1039
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1040
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
795
1041
  const {
796
- currentData: data,
797
- isLoading: isLoadingDocument,
798
- isFetching: isFetchingDocument,
799
- error
800
- } = useGetDocumentQuery(args, {
801
- ...opts,
802
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
803
- });
804
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
1042
+ data,
1043
+ isLoading: isLoadingConfigs,
1044
+ error,
1045
+ isFetching: isFetchingConfigs
1046
+ } = useGetContentTypeConfigurationQuery(model);
1047
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
805
1048
  React.useEffect(() => {
806
1049
  if (error) {
807
1050
  toggleNotification({
@@ -809,40 +1052,284 @@ const useDocument = (args, opts) => {
809
1052
  message: formatAPIError(error)
810
1053
  });
811
1054
  }
812
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
813
- const validationSchema = React.useMemo(() => {
814
- if (!schema) {
815
- return null;
816
- }
817
- return createYupSchema(schema.attributes, components);
818
- }, [schema, components]);
819
- const validate = React.useCallback(
820
- (document) => {
821
- if (!validationSchema) {
822
- throw new Error(
823
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
824
- );
825
- }
826
- try {
827
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
828
- return null;
829
- } catch (error2) {
830
- if (error2 instanceof ValidationError) {
831
- return getYupValidationErrors(error2);
832
- }
833
- throw error2;
834
- }
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
835
1063
  },
836
- [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]
837
1080
  );
838
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
839
1081
  return {
840
- components,
841
- document: data?.data,
842
- meta: data?.meta,
1082
+ error,
843
1083
  isLoading,
844
- schema,
845
- validate
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,
1326
+ isLoading,
1327
+ hasError,
1328
+ schema,
1329
+ schemas,
1330
+ validate,
1331
+ getTitle,
1332
+ getInitialFormValues
846
1333
  };
847
1334
  };
848
1335
  const useDoc = () => {
@@ -855,22 +1342,60 @@ const useDoc = () => {
855
1342
  if (!slug) {
856
1343
  throw new Error("Could not find model in url params");
857
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;
858
1352
  return {
859
1353
  collectionType,
860
1354
  model: slug,
861
- id: origin || id === "create" ? void 0 : id,
862
- ...useDocument(
863
- { documentId: origin || id, model: slug, collectionType, params },
864
- {
865
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
866
- }
867
- )
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
868
1396
  };
869
1397
  };
870
1398
  const prefixPluginTranslations = (trad, pluginId) => {
871
- if (!pluginId) {
872
- throw new TypeError("pluginId can't be empty");
873
- }
874
1399
  return Object.keys(trad).reduce((acc, current) => {
875
1400
  acc[`${pluginId}.${current}`] = trad[current];
876
1401
  return acc;
@@ -887,6 +1412,7 @@ const useDocumentActions = () => {
887
1412
  const { trackUsage } = useTracking();
888
1413
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
889
1414
  const navigate = useNavigate();
1415
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
890
1416
  const [deleteDocument] = useDeleteDocumentMutation();
891
1417
  const _delete = React.useCallback(
892
1418
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1201,6 +1727,7 @@ const useDocumentActions = () => {
1201
1727
  defaultMessage: "Saved document"
1202
1728
  })
1203
1729
  });
1730
+ setCurrentStep("contentManager.success");
1204
1731
  return res.data;
1205
1732
  } catch (err) {
1206
1733
  toggleNotification({
@@ -1302,10 +1829,10 @@ const useDocumentActions = () => {
1302
1829
  update
1303
1830
  };
1304
1831
  };
1305
- const ProtectedHistoryPage = lazy(
1306
- () => import("./History-utls71em.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1832
+ const ProtectedHistoryPage = React.lazy(
1833
+ () => import("./History-CcmSn3Mj.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1307
1834
  );
1308
- const routes$1 = [
1835
+ const routes$2 = [
1309
1836
  {
1310
1837
  path: ":collectionType/:slug/:id/history",
1311
1838
  Component: ProtectedHistoryPage
@@ -1315,32 +1842,45 @@ const routes$1 = [
1315
1842
  Component: ProtectedHistoryPage
1316
1843
  }
1317
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
+ ];
1318
1858
  const ProtectedEditViewPage = lazy(
1319
- () => import("./EditViewPage-zFjJK0s8.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1859
+ () => import("./EditViewPage-Bcnff6Vd.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1320
1860
  );
1321
1861
  const ProtectedListViewPage = lazy(
1322
- () => import("./ListViewPage-CdKd-PS_.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1862
+ () => import("./ListViewPage-HljQVnFH.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1323
1863
  );
1324
1864
  const ProtectedListConfiguration = lazy(
1325
- () => import("./ListConfigurationPage-CuMXWWqb.mjs").then((mod) => ({
1865
+ () => import("./ListConfigurationPage-BPvzENJJ.mjs").then((mod) => ({
1326
1866
  default: mod.ProtectedListConfiguration
1327
1867
  }))
1328
1868
  );
1329
1869
  const ProtectedEditConfigurationPage = lazy(
1330
- () => import("./EditConfigurationPage-I2kKh9dx.mjs").then((mod) => ({
1870
+ () => import("./EditConfigurationPage-D1nvB7Br.mjs").then((mod) => ({
1331
1871
  default: mod.ProtectedEditConfigurationPage
1332
1872
  }))
1333
1873
  );
1334
1874
  const ProtectedComponentConfigurationPage = lazy(
1335
- () => import("./ComponentConfigurationPage-CnL10QYC.mjs").then((mod) => ({
1875
+ () => import("./ComponentConfigurationPage-D4H-v0et.mjs").then((mod) => ({
1336
1876
  default: mod.ProtectedComponentConfigurationPage
1337
1877
  }))
1338
1878
  );
1339
1879
  const NoPermissions = lazy(
1340
- () => import("./NoPermissionsPage-DlWi4BAH.mjs").then((mod) => ({ default: mod.NoPermissions }))
1880
+ () => import("./NoPermissionsPage-D6ze2nQL.mjs").then((mod) => ({ default: mod.NoPermissions }))
1341
1881
  );
1342
1882
  const NoContentType = lazy(
1343
- () => import("./NoContentTypePage-DkToTT7u.mjs").then((mod) => ({ default: mod.NoContentType }))
1883
+ () => import("./NoContentTypePage-BfHaSM-K.mjs").then((mod) => ({ default: mod.NoContentType }))
1344
1884
  );
1345
1885
  const CollectionTypePages = () => {
1346
1886
  const { collectionType } = useParams();
@@ -1352,7 +1892,7 @@ const CollectionTypePages = () => {
1352
1892
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1353
1893
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1354
1894
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1355
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1895
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1356
1896
  const routes = [
1357
1897
  {
1358
1898
  path: LIST_RELATIVE_PATH,
@@ -1386,6 +1926,7 @@ const routes = [
1386
1926
  path: "no-content-types",
1387
1927
  Component: NoContentType
1388
1928
  },
1929
+ ...routes$2,
1389
1930
  ...routes$1
1390
1931
  ];
1391
1932
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1484,6 +2025,11 @@ const DocumentActionButton = (action) => {
1484
2025
  ) : null
1485
2026
  ] });
1486
2027
  };
2028
+ const MenuItem = styled(Menu.Item)`
2029
+ &:hover {
2030
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
2031
+ }
2032
+ `;
1487
2033
  const DocumentActionsMenu = ({
1488
2034
  actions: actions2,
1489
2035
  children,
@@ -1542,48 +2088,32 @@ const DocumentActionsMenu = ({
1542
2088
  /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1543
2089
  actions2.map((action) => {
1544
2090
  return /* @__PURE__ */ jsx(
1545
- Menu.Item,
2091
+ MenuItem,
1546
2092
  {
1547
2093
  disabled: action.disabled,
1548
2094
  onSelect: handleClick(action),
1549
2095
  display: "block",
1550
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1551
- /* @__PURE__ */ jsxs(
1552
- Flex,
1553
- {
1554
- color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1555
- gap: 2,
1556
- tag: "span",
1557
- children: [
1558
- /* @__PURE__ */ jsx(
1559
- Flex,
1560
- {
1561
- tag: "span",
1562
- color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1563
- children: action.icon
1564
- }
1565
- ),
1566
- action.label
1567
- ]
1568
- }
1569
- ),
1570
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1571
- Flex,
1572
- {
1573
- alignItems: "center",
1574
- background: "alternative100",
1575
- borderStyle: "solid",
1576
- borderColor: "alternative200",
1577
- borderWidth: "1px",
1578
- height: 5,
1579
- paddingLeft: 2,
1580
- paddingRight: 2,
1581
- hasRadius: true,
1582
- color: "alternative600",
1583
- children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1584
- }
1585
- )
1586
- ] })
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
+ ) })
1587
2117
  },
1588
2118
  action.id
1589
2119
  );
@@ -1694,6 +2224,18 @@ const DocumentActionModal = ({
1694
2224
  typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1695
2225
  ] }) });
1696
2226
  };
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
+ };
1697
2239
  const PublishAction$1 = ({
1698
2240
  activeTab,
1699
2241
  documentId,
@@ -1708,6 +2250,7 @@ const PublishAction$1 = ({
1708
2250
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
1709
2251
  const isListView = useMatch(LIST_PATH) !== null;
1710
2252
  const isCloning = useMatch(CLONE_PATH) !== null;
2253
+ const { id } = useParams();
1711
2254
  const { formatMessage } = useIntl();
1712
2255
  const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1713
2256
  const { publish } = useDocumentActions();
@@ -1787,7 +2330,9 @@ const PublishAction$1 = ({
1787
2330
  const performPublish = async () => {
1788
2331
  setSubmitting(true);
1789
2332
  try {
1790
- const { errors } = await validate();
2333
+ const { errors } = await validate(true, {
2334
+ status: "published"
2335
+ });
1791
2336
  if (errors) {
1792
2337
  toggleNotification({
1793
2338
  type: "danger",
@@ -1805,13 +2350,15 @@ const PublishAction$1 = ({
1805
2350
  documentId,
1806
2351
  params
1807
2352
  },
1808
- formValues
2353
+ transformData(formValues)
1809
2354
  );
1810
2355
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1811
- navigate({
1812
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1813
- search: rawQuery
1814
- });
2356
+ if (id === "create") {
2357
+ navigate({
2358
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2359
+ search: rawQuery
2360
+ });
2361
+ }
1815
2362
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1816
2363
  setErrors(formatValidationErrors(res.error));
1817
2364
  }
@@ -1864,6 +2411,7 @@ const PublishAction$1 = ({
1864
2411
  };
1865
2412
  };
1866
2413
  PublishAction$1.type = "publish";
2414
+ PublishAction$1.position = "panel";
1867
2415
  const UpdateAction = ({
1868
2416
  activeTab,
1869
2417
  documentId,
@@ -1886,6 +2434,117 @@ const UpdateAction = ({
1886
2434
  const validate = useForm("UpdateAction", (state) => state.validate);
1887
2435
  const setErrors = useForm("UpdateAction", (state) => state.setErrors);
1888
2436
  const resetForm = useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
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(
2467
+ {
2468
+ pathname: `../${res.data.documentId}`,
2469
+ search: rawQuery
2470
+ },
2471
+ { relative: "path" }
2472
+ );
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));
2488
+ } else {
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(
2501
+ {
2502
+ pathname: `../${res.data.documentId}`,
2503
+ search: rawQuery
2504
+ },
2505
+ { replace: true, relative: "path" }
2506
+ );
2507
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2508
+ setErrors(formatValidationErrors(res.error));
2509
+ }
2510
+ }
2511
+ } finally {
2512
+ setSubmitting(false);
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]);
1889
2548
  return {
1890
2549
  /**
1891
2550
  * Disabled when:
@@ -1895,87 +2554,14 @@ const UpdateAction = ({
1895
2554
  */
1896
2555
  disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1897
2556
  label: formatMessage({
1898
- id: "content-manager.containers.Edit.save",
2557
+ id: "global.save",
1899
2558
  defaultMessage: "Save"
1900
2559
  }),
1901
- onClick: async () => {
1902
- setSubmitting(true);
1903
- try {
1904
- if (activeTab !== "draft") {
1905
- const { errors } = await validate();
1906
- if (errors) {
1907
- toggleNotification({
1908
- type: "danger",
1909
- message: formatMessage({
1910
- id: "content-manager.validation.error",
1911
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1912
- })
1913
- });
1914
- return;
1915
- }
1916
- }
1917
- if (isCloning) {
1918
- const res = await clone(
1919
- {
1920
- model,
1921
- documentId: cloneMatch.params.origin,
1922
- params
1923
- },
1924
- document
1925
- );
1926
- if ("data" in res) {
1927
- navigate(
1928
- {
1929
- pathname: `../${res.data.documentId}`,
1930
- search: rawQuery
1931
- },
1932
- { relative: "path" }
1933
- );
1934
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1935
- setErrors(formatValidationErrors(res.error));
1936
- }
1937
- } else if (documentId || collectionType === SINGLE_TYPES) {
1938
- const res = await update(
1939
- {
1940
- collectionType,
1941
- model,
1942
- documentId,
1943
- params
1944
- },
1945
- document
1946
- );
1947
- if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1948
- setErrors(formatValidationErrors(res.error));
1949
- } else {
1950
- resetForm();
1951
- }
1952
- } else {
1953
- const res = await create(
1954
- {
1955
- model,
1956
- params
1957
- },
1958
- document
1959
- );
1960
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1961
- navigate(
1962
- {
1963
- pathname: `../${res.data.documentId}`,
1964
- search: rawQuery
1965
- },
1966
- { replace: true, relative: "path" }
1967
- );
1968
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1969
- setErrors(formatValidationErrors(res.error));
1970
- }
1971
- }
1972
- } finally {
1973
- setSubmitting(false);
1974
- }
1975
- }
2560
+ onClick: handleUpdate
1976
2561
  };
1977
2562
  };
1978
2563
  UpdateAction.type = "update";
2564
+ UpdateAction.position = "panel";
1979
2565
  const UNPUBLISH_DRAFT_OPTIONS = {
1980
2566
  KEEP: "keep",
1981
2567
  DISCARD: "discard"
@@ -2098,6 +2684,7 @@ const UnpublishAction$1 = ({
2098
2684
  };
2099
2685
  };
2100
2686
  UnpublishAction$1.type = "unpublish";
2687
+ UnpublishAction$1.position = "panel";
2101
2688
  const DiscardAction = ({
2102
2689
  activeTab,
2103
2690
  documentId,
@@ -2148,6 +2735,7 @@ const DiscardAction = ({
2148
2735
  };
2149
2736
  };
2150
2737
  DiscardAction.type = "discard";
2738
+ DiscardAction.position = "panel";
2151
2739
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2152
2740
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2153
2741
  const RelativeTime = React.forwardRef(
@@ -2160,7 +2748,7 @@ const RelativeTime = React.forwardRef(
2160
2748
  });
2161
2749
  const unit = intervals.find((intervalUnit) => {
2162
2750
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2163
- });
2751
+ }) ?? "seconds";
2164
2752
  const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2165
2753
  const customInterval = customIntervals.find(
2166
2754
  (custom) => interval[custom.unit] < custom.threshold
@@ -2194,19 +2782,29 @@ const getDisplayName = ({
2194
2782
  return email ?? "";
2195
2783
  };
2196
2784
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2197
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2785
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2198
2786
  const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2199
- return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
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
+ }) }) });
2200
2792
  };
2201
2793
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2202
2794
  const { formatMessage } = useIntl();
2203
2795
  const isCloning = useMatch(CLONE_PATH) !== null;
2796
+ const params = useParams();
2204
2797
  const title = isCreating ? formatMessage({
2205
2798
  id: "content-manager.containers.edit.title.new",
2206
2799
  defaultMessage: "Create an entry"
2207
2800
  }) : documentTitle;
2208
2801
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2209
- /* @__PURE__ */ jsx(BackButton, {}),
2802
+ /* @__PURE__ */ jsx(
2803
+ BackButton,
2804
+ {
2805
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2806
+ }
2807
+ ),
2210
2808
  /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2211
2809
  /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2212
2810
  /* @__PURE__ */ jsx(HeaderToolbar, {})
@@ -2257,7 +2855,7 @@ const HeaderToolbar = () => {
2257
2855
  meta: isCloning ? void 0 : meta,
2258
2856
  collectionType
2259
2857
  },
2260
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2858
+ descriptions: plugins["content-manager"].apis.getDocumentActions("header"),
2261
2859
  children: (actions2) => {
2262
2860
  const headerActions = actions2.filter((action) => {
2263
2861
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2294,12 +2892,12 @@ const Information = ({ activeTab }) => {
2294
2892
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2295
2893
  label: formatMessage({
2296
2894
  id: "content-manager.containers.edit.information.last-published.label",
2297
- defaultMessage: "Last published"
2895
+ defaultMessage: "Published"
2298
2896
  }),
2299
2897
  value: formatMessage(
2300
2898
  {
2301
2899
  id: "content-manager.containers.edit.information.last-published.value",
2302
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2900
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2303
2901
  },
2304
2902
  {
2305
2903
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2312,12 +2910,12 @@ const Information = ({ activeTab }) => {
2312
2910
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2313
2911
  label: formatMessage({
2314
2912
  id: "content-manager.containers.edit.information.last-draft.label",
2315
- defaultMessage: "Last draft"
2913
+ defaultMessage: "Updated"
2316
2914
  }),
2317
2915
  value: formatMessage(
2318
2916
  {
2319
2917
  id: "content-manager.containers.edit.information.last-draft.value",
2320
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2918
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2321
2919
  },
2322
2920
  {
2323
2921
  time: /* @__PURE__ */ jsx(
@@ -2335,12 +2933,12 @@ const Information = ({ activeTab }) => {
2335
2933
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2336
2934
  label: formatMessage({
2337
2935
  id: "content-manager.containers.edit.information.document.label",
2338
- defaultMessage: "Document"
2936
+ defaultMessage: "Created"
2339
2937
  }),
2340
2938
  value: formatMessage(
2341
2939
  {
2342
2940
  id: "content-manager.containers.edit.information.document.value",
2343
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2941
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2344
2942
  },
2345
2943
  {
2346
2944
  time: /* @__PURE__ */ jsx(
@@ -2398,10 +2996,9 @@ const HeaderActions = ({ actions: actions2 }) => {
2398
2996
  SingleSelect,
2399
2997
  {
2400
2998
  size: "S",
2401
- disabled: action.disabled,
2402
- "aria-label": action.label,
2403
2999
  onChange: action.onSelect,
2404
- value: action.value,
3000
+ "aria-label": action.label,
3001
+ ...action,
2405
3002
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2406
3003
  },
2407
3004
  action.id
@@ -2466,6 +3063,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2466
3063
  };
2467
3064
  };
2468
3065
  ConfigureTheViewAction.type = "configure-the-view";
3066
+ ConfigureTheViewAction.position = "header";
2469
3067
  const EditTheModelAction = ({ model }) => {
2470
3068
  const navigate = useNavigate();
2471
3069
  const { formatMessage } = useIntl();
@@ -2482,6 +3080,7 @@ const EditTheModelAction = ({ model }) => {
2482
3080
  };
2483
3081
  };
2484
3082
  EditTheModelAction.type = "edit-the-model";
3083
+ EditTheModelAction.position = "header";
2485
3084
  const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2486
3085
  const navigate = useNavigate();
2487
3086
  const { formatMessage } = useIntl();
@@ -2490,12 +3089,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2490
3089
  const { delete: deleteAction } = useDocumentActions();
2491
3090
  const { toggleNotification } = useNotification();
2492
3091
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
3092
+ const isLocalized = document?.locale != null;
2493
3093
  return {
2494
3094
  disabled: !canDelete || !document,
2495
- label: formatMessage({
2496
- id: "content-manager.actions.delete.label",
2497
- defaultMessage: "Delete document"
2498
- }),
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
+ ),
2499
3102
  icon: /* @__PURE__ */ jsx(Trash, {}),
2500
3103
  dialog: {
2501
3104
  type: "dialog",
@@ -2551,6 +3154,7 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2551
3154
  };
2552
3155
  };
2553
3156
  DeleteAction$1.type = "delete";
3157
+ DeleteAction$1.position = ["header", "table-row"];
2554
3158
  const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2555
3159
  const Panels = () => {
2556
3160
  const isCloning = useMatch(CLONE_PATH) !== null;
@@ -2562,393 +3166,90 @@ const Panels = () => {
2562
3166
  status: "draft"
2563
3167
  });
2564
3168
  const { model, id, document, meta, collectionType } = useDoc();
2565
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2566
- const props = {
2567
- activeTab: status,
2568
- model,
2569
- documentId: id,
2570
- document: isCloning ? void 0 : document,
2571
- meta: isCloning ? void 0 : meta,
2572
- collectionType
2573
- };
2574
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2575
- DescriptionComponentRenderer,
2576
- {
2577
- props,
2578
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2579
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2580
- }
2581
- ) });
2582
- };
2583
- const ActionsPanel = () => {
2584
- const { formatMessage } = useIntl();
2585
- return {
2586
- title: formatMessage({
2587
- id: "content-manager.containers.edit.panels.default.title",
2588
- defaultMessage: "Entry"
2589
- }),
2590
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2591
- };
2592
- };
2593
- ActionsPanel.type = "actions";
2594
- const ActionsPanelContent = () => {
2595
- const isCloning = useMatch(CLONE_PATH) !== null;
2596
- const [
2597
- {
2598
- query: { status = "draft" }
2599
- }
2600
- ] = useQueryParams();
2601
- const { model, id, document, meta, collectionType } = useDoc();
2602
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2603
- const props = {
2604
- activeTab: status,
2605
- model,
2606
- documentId: id,
2607
- document: isCloning ? void 0 : document,
2608
- meta: isCloning ? void 0 : meta,
2609
- collectionType
2610
- };
2611
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2612
- /* @__PURE__ */ jsx(
2613
- DescriptionComponentRenderer,
2614
- {
2615
- props,
2616
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2617
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2618
- }
2619
- ),
2620
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2621
- ] });
2622
- };
2623
- const Panel = React.forwardRef(({ children, title }, ref) => {
2624
- return /* @__PURE__ */ jsxs(
2625
- Flex,
2626
- {
2627
- ref,
2628
- tag: "aside",
2629
- "aria-labelledby": "additional-information",
2630
- background: "neutral0",
2631
- borderColor: "neutral150",
2632
- hasRadius: true,
2633
- paddingBottom: 4,
2634
- paddingLeft: 4,
2635
- paddingRight: 4,
2636
- paddingTop: 4,
2637
- shadow: "tableShadow",
2638
- gap: 3,
2639
- direction: "column",
2640
- justifyContent: "stretch",
2641
- alignItems: "flex-start",
2642
- children: [
2643
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2644
- children
2645
- ]
2646
- }
2647
- );
2648
- });
2649
- const HOOKS = {
2650
- /**
2651
- * Hook that allows to mutate the displayed headers of the list view table
2652
- * @constant
2653
- * @type {string}
2654
- */
2655
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2656
- /**
2657
- * Hook that allows to mutate the CM's collection types links pre-set filters
2658
- * @constant
2659
- * @type {string}
2660
- */
2661
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2662
- /**
2663
- * Hook that allows to mutate the CM's edit view layout
2664
- * @constant
2665
- * @type {string}
2666
- */
2667
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2668
- /**
2669
- * Hook that allows to mutate the CM's single types links pre-set filters
2670
- * @constant
2671
- * @type {string}
2672
- */
2673
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2674
- };
2675
- const contentTypesApi = contentManagerApi.injectEndpoints({
2676
- endpoints: (builder) => ({
2677
- getContentTypeConfiguration: builder.query({
2678
- query: (uid) => ({
2679
- url: `/content-manager/content-types/${uid}/configuration`,
2680
- method: "GET"
2681
- }),
2682
- transformResponse: (response) => response.data,
2683
- providesTags: (_result, _error, uid) => [
2684
- { type: "ContentTypesConfiguration", id: uid },
2685
- { type: "ContentTypeSettings", id: "LIST" }
2686
- ]
2687
- }),
2688
- getAllContentTypeSettings: builder.query({
2689
- query: () => "/content-manager/content-types-settings",
2690
- transformResponse: (response) => response.data,
2691
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2692
- }),
2693
- updateContentTypeConfiguration: builder.mutation({
2694
- query: ({ uid, ...body }) => ({
2695
- url: `/content-manager/content-types/${uid}/configuration`,
2696
- method: "PUT",
2697
- data: body
2698
- }),
2699
- transformResponse: (response) => response.data,
2700
- invalidatesTags: (_result, _error, { uid }) => [
2701
- { type: "ContentTypesConfiguration", id: uid },
2702
- { type: "ContentTypeSettings", id: "LIST" },
2703
- // Is this necessary?
2704
- { type: "InitialData" }
2705
- ]
2706
- })
2707
- })
2708
- });
2709
- const {
2710
- useGetContentTypeConfigurationQuery,
2711
- useGetAllContentTypeSettingsQuery,
2712
- useUpdateContentTypeConfigurationMutation
2713
- } = contentTypesApi;
2714
- const checkIfAttributeIsDisplayable = (attribute) => {
2715
- const { type } = attribute;
2716
- if (type === "relation") {
2717
- return !attribute.relation.toLowerCase().includes("morph");
2718
- }
2719
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2720
- };
2721
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2722
- if (!mainFieldName) {
2723
- return void 0;
2724
- }
2725
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2726
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2727
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2728
- );
2729
- return {
2730
- name: mainFieldName,
2731
- type: mainFieldType ?? "string"
2732
- };
2733
- };
2734
- const DEFAULT_SETTINGS = {
2735
- bulkable: false,
2736
- filterable: false,
2737
- searchable: false,
2738
- pagination: false,
2739
- defaultSortBy: "",
2740
- defaultSortOrder: "asc",
2741
- mainField: "id",
2742
- pageSize: 10
2743
- };
2744
- const useDocumentLayout = (model) => {
2745
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2746
- const [{ query }] = useQueryParams();
2747
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2748
- const { toggleNotification } = useNotification();
2749
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2750
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2751
- const {
2752
- data,
2753
- isLoading: isLoadingConfigs,
2754
- error,
2755
- isFetching: isFetchingConfigs
2756
- } = useGetContentTypeConfigurationQuery(model);
2757
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2758
- React.useEffect(() => {
2759
- if (error) {
2760
- toggleNotification({
2761
- type: "danger",
2762
- message: formatAPIError(error)
2763
- });
2764
- }
2765
- }, [error, formatAPIError, toggleNotification]);
2766
- const editLayout = React.useMemo(
2767
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2768
- layout: [],
2769
- components: {},
2770
- metadatas: {},
2771
- options: {},
2772
- settings: DEFAULT_SETTINGS
2773
- },
2774
- [data, isLoading, schemas, schema, components]
2775
- );
2776
- const listLayout = React.useMemo(() => {
2777
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2778
- layout: [],
2779
- metadatas: {},
2780
- options: {},
2781
- settings: DEFAULT_SETTINGS
2782
- };
2783
- }, [data, isLoading, schemas, schema, components]);
2784
- const { layout: edit } = React.useMemo(
2785
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2786
- layout: editLayout,
2787
- query
2788
- }),
2789
- [editLayout, query, runHookWaterfall]
2790
- );
2791
- return {
2792
- error,
2793
- isLoading,
2794
- edit,
2795
- list: listLayout
2796
- };
2797
- };
2798
- const useDocLayout = () => {
2799
- const { model } = useDoc();
2800
- return useDocumentLayout(model);
2801
- };
2802
- const formatEditLayout = (data, {
2803
- schemas,
2804
- schema,
2805
- components
2806
- }) => {
2807
- let currentPanelIndex = 0;
2808
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2809
- data.contentType.layouts.edit,
2810
- schema?.attributes,
2811
- data.contentType.metadatas,
2812
- { configurations: data.components, schemas: components },
2813
- schemas
2814
- ).reduce((panels, row) => {
2815
- if (row.some((field) => field.type === "dynamiczone")) {
2816
- panels.push([row]);
2817
- currentPanelIndex += 2;
2818
- } else {
2819
- if (!panels[currentPanelIndex]) {
2820
- panels.push([]);
2821
- }
2822
- panels[currentPanelIndex].push(row);
2823
- }
2824
- return panels;
2825
- }, []);
2826
- const componentEditAttributes = Object.entries(data.components).reduce(
2827
- (acc, [uid, configuration]) => {
2828
- acc[uid] = {
2829
- layout: convertEditLayoutToFieldLayouts(
2830
- configuration.layouts.edit,
2831
- components[uid].attributes,
2832
- configuration.metadatas,
2833
- { configurations: data.components, schemas: components }
2834
- ),
2835
- settings: {
2836
- ...configuration.settings,
2837
- icon: components[uid].info.icon,
2838
- displayName: components[uid].info.displayName
2839
- }
2840
- };
2841
- return acc;
2842
- },
2843
- {}
2844
- );
2845
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2846
- (acc, [attribute, metadata]) => {
2847
- return {
2848
- ...acc,
2849
- [attribute]: metadata.edit
2850
- };
2851
- },
2852
- {}
2853
- );
2854
- return {
2855
- layout: panelledEditAttributes,
2856
- components: componentEditAttributes,
2857
- metadatas: editMetadatas,
2858
- settings: {
2859
- ...data.contentType.settings,
2860
- displayName: schema?.info.displayName
2861
- },
2862
- options: {
2863
- ...schema?.options,
2864
- ...schema?.pluginOptions,
2865
- ...data.contentType.options
2866
- }
3169
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
3170
+ const props = {
3171
+ activeTab: status,
3172
+ model,
3173
+ documentId: id,
3174
+ document: isCloning ? void 0 : document,
3175
+ meta: isCloning ? void 0 : meta,
3176
+ collectionType
2867
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
+ ) });
2868
3186
  };
2869
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2870
- return rows.map(
2871
- (row) => row.map((field) => {
2872
- const attribute = attributes[field.name];
2873
- if (!attribute) {
2874
- return null;
2875
- }
2876
- const { edit: metadata } = metadatas[field.name];
2877
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2878
- return {
2879
- attribute,
2880
- disabled: !metadata.editable,
2881
- hint: metadata.description,
2882
- label: metadata.label ?? "",
2883
- name: field.name,
2884
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2885
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2886
- schemas,
2887
- components: components?.schemas ?? {}
2888
- }),
2889
- placeholder: metadata.placeholder ?? "",
2890
- required: attribute.required ?? false,
2891
- size: field.size,
2892
- unique: "unique" in attribute ? attribute.unique : false,
2893
- visible: metadata.visible ?? true,
2894
- type: attribute.type
2895
- };
2896
- }).filter((field) => field !== null)
2897
- );
2898
- };
2899
- const formatListLayout = (data, {
2900
- schemas,
2901
- schema,
2902
- components
2903
- }) => {
2904
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2905
- (acc, [attribute, metadata]) => {
2906
- return {
2907
- ...acc,
2908
- [attribute]: metadata.list
2909
- };
2910
- },
2911
- {}
2912
- );
2913
- const listAttributes = convertListLayoutToFieldLayouts(
2914
- data.contentType.layouts.list,
2915
- schema?.attributes,
2916
- listMetadatas,
2917
- { configurations: data.components, schemas: components },
2918
- schemas
2919
- );
3187
+ const ActionsPanel = () => {
3188
+ const { formatMessage } = useIntl();
2920
3189
  return {
2921
- layout: listAttributes,
2922
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2923
- metadatas: listMetadatas,
2924
- options: {
2925
- ...schema?.options,
2926
- ...schema?.pluginOptions,
2927
- ...data.contentType.options
2928
- }
3190
+ title: formatMessage({
3191
+ id: "content-manager.containers.edit.panels.default.title",
3192
+ defaultMessage: "Entry"
3193
+ }),
3194
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2929
3195
  };
2930
3196
  };
2931
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2932
- return columns.map((name) => {
2933
- const attribute = attributes[name];
2934
- if (!attribute) {
2935
- return null;
3197
+ ActionsPanel.type = "actions";
3198
+ const ActionsPanelContent = () => {
3199
+ const isCloning = useMatch(CLONE_PATH) !== null;
3200
+ const [
3201
+ {
3202
+ query: { status = "draft" }
2936
3203
  }
2937
- const metadata = metadatas[name];
2938
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2939
- return {
2940
- attribute,
2941
- label: metadata.label ?? "",
2942
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2943
- schemas,
2944
- components: components?.schemas ?? {}
2945
- }),
2946
- name,
2947
- searchable: metadata.searchable ?? true,
2948
- sortable: metadata.sortable ?? true
2949
- };
2950
- }).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
+ ] });
2951
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
+ });
2952
3253
  const ConfirmBulkActionDialog = ({
2953
3254
  onToggleDialog,
2954
3255
  isOpen = false,
@@ -2974,7 +3275,7 @@ const ConfirmBulkActionDialog = ({
2974
3275
  ] })
2975
3276
  ] }) });
2976
3277
  };
2977
- const BoldChunk$1 = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
3278
+ const BoldChunk = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
2978
3279
  const ConfirmDialogPublishAll = ({
2979
3280
  isOpen,
2980
3281
  onToggleDialog,
@@ -3023,7 +3324,7 @@ const ConfirmDialogPublishAll = ({
3023
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. "
3024
3325
  },
3025
3326
  {
3026
- b: BoldChunk$1,
3327
+ b: BoldChunk,
3027
3328
  count: countDraftRelations,
3028
3329
  entities: selectedEntries.length
3029
3330
  }
@@ -3062,6 +3363,16 @@ const ConfirmDialogPublishAll = ({
3062
3363
  const TypographyMaxWidth = styled(Typography)`
3063
3364
  max-width: 300px;
3064
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
+ `;
3065
3376
  const formatErrorMessages = (errors, parentKey, formatMessage) => {
3066
3377
  const messages = [];
3067
3378
  Object.entries(errors).forEach(([key, value]) => {
@@ -3166,7 +3477,7 @@ const SelectedEntriesTableContent = ({
3166
3477
  )
3167
3478
  ] }),
3168
3479
  /* @__PURE__ */ jsx(Table.Loading, {}),
3169
- /* @__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: [
3170
3481
  /* @__PURE__ */ jsx(Table.CheckboxCell, { id: row.id }),
3171
3482
  /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Typography, { children: row.id }) }),
3172
3483
  shouldDisplayMainField && /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Typography, { children: row[mainField] }) }),
@@ -3193,18 +3504,10 @@ const SelectedEntriesTableContent = ({
3193
3504
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3194
3505
  },
3195
3506
  state: { from: pathname },
3196
- label: formatMessage(
3197
- { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3198
- {
3199
- target: formatMessage(
3200
- {
3201
- id: "content-manager.components.ListViewHelperPluginTable.row-line",
3202
- defaultMessage: "item line {number}"
3203
- },
3204
- { number: index2 + 1 }
3205
- )
3206
- }
3207
- ),
3507
+ label: formatMessage({
3508
+ id: "content-manager.bulk-publish.edit",
3509
+ defaultMessage: "Edit"
3510
+ }),
3208
3511
  target: "_blank",
3209
3512
  marginLeft: "auto",
3210
3513
  variant: "ghost",
@@ -3214,7 +3517,73 @@ const SelectedEntriesTableContent = ({
3214
3517
  ] }, row.id)) })
3215
3518
  ] });
3216
3519
  };
3217
- 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
+ };
3218
3587
  const SelectedEntriesModalContent = ({
3219
3588
  listViewSelectedEntries,
3220
3589
  toggleModal,
@@ -3273,7 +3642,6 @@ const SelectedEntriesModalContent = ({
3273
3642
  validationErrors: {}
3274
3643
  };
3275
3644
  }, [components, data, schema]);
3276
- const [publishedCount, setPublishedCount] = React.useState(0);
3277
3645
  const [isDialogOpen, setIsDialogOpen] = React.useState(false);
3278
3646
  const { publishMany: bulkPublishAction } = useDocumentActions();
3279
3647
  const [, { isLoading: isSubmittingForm }] = usePublishManyDocumentsMutation();
@@ -3285,53 +3653,36 @@ const SelectedEntriesModalContent = ({
3285
3653
  const selectedEntriesWithErrorsCount = selectedEntries.filter(
3286
3654
  ({ documentId }) => validationErrors[documentId]
3287
3655
  ).length;
3288
- const selectedEntriesPublished = selectedEntries.filter(
3656
+ const selectedEntriesPublishedCount = selectedEntries.filter(
3289
3657
  ({ status }) => status === "published"
3290
3658
  ).length;
3291
- 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;
3292
3663
  const toggleDialog = () => setIsDialogOpen((prev) => !prev);
3293
3664
  const handleConfirmBulkPublish = async () => {
3294
3665
  toggleDialog();
3295
3666
  const res = await bulkPublishAction({ model, documentIds: entriesToPublish, params });
3296
3667
  if (!("error" in res)) {
3297
- setPublishedCount(res.count);
3298
3668
  const unpublishedEntries = rows.filter((row) => {
3299
3669
  return !entriesToPublish.includes(row.documentId);
3300
3670
  });
3301
3671
  setListViewSelectedDocuments(unpublishedEntries);
3302
3672
  }
3303
3673
  };
3304
- const getFormattedCountMessage = () => {
3305
- if (publishedCount) {
3306
- return formatMessage(
3307
- {
3308
- id: getTranslation("containers.list.selectedEntriesModal.publishedCount"),
3309
- 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."
3310
- },
3311
- {
3312
- publishedCount,
3313
- withErrorsCount: selectedEntriesWithErrorsCount,
3314
- b: BoldChunk
3315
- }
3316
- );
3317
- }
3318
- return formatMessage(
3319
- {
3320
- id: getTranslation("containers.list.selectedEntriesModal.selectedCount"),
3321
- 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."
3322
- },
3323
- {
3324
- readyToPublishCount: selectedEntriesWithNoErrorsCount,
3325
- withErrorsCount: selectedEntriesWithErrorsCount,
3326
- alreadyPublishedCount: selectedEntriesPublished,
3327
- b: BoldChunk
3328
- }
3329
- );
3330
- };
3331
3674
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3332
3675
  /* @__PURE__ */ jsxs(Modal.Body, { children: [
3333
- /* @__PURE__ */ jsx(Typography, { children: getFormattedCountMessage() }),
3334
- /* @__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(
3335
3686
  SelectedEntriesTableContent,
3336
3687
  {
3337
3688
  isPublishing: isSubmittingForm,
@@ -3352,7 +3703,7 @@ const SelectedEntriesModalContent = ({
3352
3703
  Button,
3353
3704
  {
3354
3705
  onClick: toggleDialog,
3355
- disabled: selectedEntries.length === 0 || selectedEntries.length === selectedEntriesWithErrorsCount || selectedEntriesPublished === selectedEntries.length || isLoading,
3706
+ disabled: selectedEntries.length === 0 || selectedEntries.length === selectedEntriesWithErrorsCount || selectedEntriesPublishedCount === selectedEntries.length || isLoading,
3356
3707
  loading: isSubmittingForm,
3357
3708
  children: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" })
3358
3709
  }
@@ -3378,8 +3729,7 @@ const PublishAction = ({ documents, model }) => {
3378
3729
  const refetchList = () => {
3379
3730
  contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3380
3731
  };
3381
- if (!showPublishButton)
3382
- return null;
3732
+ if (!showPublishButton) return null;
3383
3733
  return {
3384
3734
  actionType: "publish",
3385
3735
  variant: "tertiary",
@@ -3447,8 +3797,7 @@ const DeleteAction = ({ documents, model }) => {
3447
3797
  selectRow([]);
3448
3798
  }
3449
3799
  };
3450
- if (!hasDeletePermission)
3451
- return null;
3800
+ if (!hasDeletePermission) return null;
3452
3801
  return {
3453
3802
  variant: "danger-light",
3454
3803
  label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
@@ -3497,8 +3846,7 @@ const UnpublishAction = ({ documents, model }) => {
3497
3846
  }
3498
3847
  };
3499
3848
  const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3500
- if (!showUnpublishButton)
3501
- return null;
3849
+ if (!showUnpublishButton) return null;
3502
3850
  return {
3503
3851
  variant: "tertiary",
3504
3852
  label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
@@ -3603,7 +3951,7 @@ const TableActions = ({ document }) => {
3603
3951
  DescriptionComponentRenderer,
3604
3952
  {
3605
3953
  props,
3606
- descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3954
+ descriptions: plugins["content-manager"].apis.getDocumentActions("table-row").filter((action) => action.name !== "PublishAction"),
3607
3955
  children: (actions2) => {
3608
3956
  const tableRowActions = actions2.filter((action) => {
3609
3957
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3662,6 +4010,7 @@ const EditAction = ({ documentId }) => {
3662
4010
  };
3663
4011
  };
3664
4012
  EditAction.type = "edit";
4013
+ EditAction.position = "table-row";
3665
4014
  const StyledPencil = styled(Pencil)`
3666
4015
  path {
3667
4016
  fill: currentColor;
@@ -3738,6 +4087,7 @@ const CloneAction = ({ model, documentId }) => {
3738
4087
  };
3739
4088
  };
3740
4089
  CloneAction.type = "clone";
4090
+ CloneAction.position = "table-row";
3741
4091
  const StyledDuplicate = styled(Duplicate)`
3742
4092
  path {
3743
4093
  fill: currentColor;
@@ -3824,7 +4174,14 @@ class ContentManagerPlugin {
3824
4174
  addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3825
4175
  addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3826
4176
  getBulkActions: () => this.bulkActions,
3827
- 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
+ },
3828
4185
  getEditViewSidePanels: () => this.editViewSidePanels,
3829
4186
  getHeaderActions: () => this.headerActions
3830
4187
  }
@@ -3834,10 +4191,8 @@ class ContentManagerPlugin {
3834
4191
  const getPrintableType = (value) => {
3835
4192
  const nativeType = typeof value;
3836
4193
  if (nativeType === "object") {
3837
- if (value === null)
3838
- return "null";
3839
- if (Array.isArray(value))
3840
- return "array";
4194
+ if (value === null) return "null";
4195
+ if (Array.isArray(value)) return "array";
3841
4196
  if (value instanceof Object && value.constructor.name !== "Object") {
3842
4197
  return value.constructor.name;
3843
4198
  }
@@ -3848,17 +4203,27 @@ const HistoryAction = ({ model, document }) => {
3848
4203
  const { formatMessage } = useIntl();
3849
4204
  const [{ query }] = useQueryParams();
3850
4205
  const navigate = useNavigate();
4206
+ const { trackUsage } = useTracking();
4207
+ const { pathname } = useLocation();
3851
4208
  const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
3852
4209
  if (!window.strapi.features.isEnabled("cms-content-history")) {
3853
4210
  return null;
3854
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
+ };
3855
4220
  return {
3856
4221
  icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
3857
4222
  label: formatMessage({
3858
4223
  id: "content-manager.history.document-action",
3859
4224
  defaultMessage: "Content History"
3860
4225
  }),
3861
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
4226
+ onClick: handleOnClick,
3862
4227
  disabled: (
3863
4228
  /**
3864
4229
  * The user is creating a new document.
@@ -3880,6 +4245,7 @@ const HistoryAction = ({ model, document }) => {
3880
4245
  };
3881
4246
  };
3882
4247
  HistoryAction.type = "history";
4248
+ HistoryAction.position = "header";
3883
4249
  const historyAdmin = {
3884
4250
  bootstrap(app) {
3885
4251
  const { addDocumentAction } = app.getPlugin("content-manager").apis;
@@ -3926,6 +4292,88 @@ const { setInitialData } = actions;
3926
4292
  const reducer = combineReducers({
3927
4293
  app: reducer$1
3928
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
+ };
3929
4377
  const index = {
3930
4378
  register(app) {
3931
4379
  const cm = new ContentManagerPlugin();
@@ -3945,7 +4393,7 @@ const index = {
3945
4393
  app.router.addRoute({
3946
4394
  path: "content-manager/*",
3947
4395
  lazy: async () => {
3948
- const { Layout } = await import("./layout-DX_52HSH.mjs");
4396
+ const { Layout } = await import("./layout-CxDMdJ13.mjs");
3949
4397
  return {
3950
4398
  Component: Layout
3951
4399
  };
@@ -3958,11 +4406,14 @@ const index = {
3958
4406
  if (typeof historyAdmin.bootstrap === "function") {
3959
4407
  historyAdmin.bootstrap(app);
3960
4408
  }
4409
+ if (typeof previewAdmin.bootstrap === "function") {
4410
+ previewAdmin.bootstrap(app);
4411
+ }
3961
4412
  },
3962
4413
  async registerTrads({ locales }) {
3963
4414
  const importedTrads = await Promise.all(
3964
4415
  locales.map((locale) => {
3965
- 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-CPTj6CjC.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 }) => {
3966
4417
  return {
3967
4418
  data: prefixPluginTranslations(data, PLUGIN_ID),
3968
4419
  locale
@@ -3979,22 +4430,27 @@ const index = {
3979
4430
  }
3980
4431
  };
3981
4432
  export {
3982
- ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD as A,
4433
+ useUpdateContentTypeConfigurationMutation as A,
3983
4434
  BulkActionsRenderer as B,
3984
4435
  COLLECTION_TYPES as C,
3985
4436
  DocumentStatus as D,
3986
- extractContentTypeComponents as E,
3987
- DEFAULT_SETTINGS as F,
3988
- convertEditLayoutToFieldLayouts as G,
4437
+ ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD as E,
4438
+ extractContentTypeComponents as F,
4439
+ DEFAULT_SETTINGS as G,
3989
4440
  HOOKS as H,
3990
4441
  InjectionZone as I,
3991
- useDocument as J,
3992
- index as K,
3993
- useDocumentActions as L,
4442
+ convertEditLayoutToFieldLayouts as J,
4443
+ removeFieldsThatDontExistOnSchema as K,
4444
+ prepareTempKeys as L,
4445
+ useDocument as M,
4446
+ useGetPreviewUrlQuery as N,
4447
+ index as O,
3994
4448
  Panels as P,
4449
+ useContentManagerContext as Q,
3995
4450
  RelativeTime as R,
3996
4451
  SINGLE_TYPES as S,
3997
4452
  TableActions as T,
4453
+ useDocumentActions as U,
3998
4454
  useGetInitialDataQuery as a,
3999
4455
  useGetAllContentTypeSettingsQuery as b,
4000
4456
  useDoc as c,
@@ -4007,19 +4463,19 @@ export {
4007
4463
  Header as j,
4008
4464
  PERMISSIONS as k,
4009
4465
  DocumentRBAC as l,
4010
- DOCUMENT_META_FIELDS as m,
4011
- CLONE_PATH as n,
4012
- useDocLayout as o,
4466
+ useDocLayout as m,
4467
+ createDefaultForm as n,
4468
+ CLONE_PATH as o,
4013
4469
  useGetContentTypeConfigurationQuery as p,
4014
4470
  CREATOR_FIELDS as q,
4015
4471
  getMainField as r,
4016
4472
  setInitialData as s,
4017
- getDisplayName as t,
4473
+ transformDocument as t,
4018
4474
  useContentTypeSchema as u,
4019
- checkIfAttributeIsDisplayable as v,
4020
- useGetAllDocumentsQuery as w,
4021
- convertListLayoutToFieldLayouts as x,
4022
- capitalise as y,
4023
- useUpdateContentTypeConfigurationMutation as z
4475
+ getDisplayName as v,
4476
+ checkIfAttributeIsDisplayable as w,
4477
+ useGetAllDocumentsQuery as x,
4478
+ convertListLayoutToFieldLayouts as y,
4479
+ capitalise as z
4024
4480
  };
4025
- //# sourceMappingURL=index-BHfS6_D5.mjs.map
4481
+ //# sourceMappingURL=index-EH8ZtHd5.mjs.map