@strapi/content-manager 0.0.0-experimental.d53e940834bf72ddc725f1d2fd36dac9abec30cb → 0.0.0-experimental.d6bba97c7ed8309e57888e84d4788fa279d473fd

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 (220) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/CardDragPreview-C0QyJgRA.js.map +1 -1
  3. package/dist/_chunks/CardDragPreview-DOxamsuj.mjs.map +1 -1
  4. package/dist/_chunks/{ComponentConfigurationPage-DmwmiFQy.mjs → ComponentConfigurationPage-D4J64ny7.mjs} +4 -4
  5. package/dist/_chunks/{ComponentConfigurationPage-DmwmiFQy.mjs.map → ComponentConfigurationPage-D4J64ny7.mjs.map} +1 -1
  6. package/dist/_chunks/{ComponentConfigurationPage-C-49MccQ.js → ComponentConfigurationPage-DHV8IFbd.js} +5 -6
  7. package/dist/_chunks/{ComponentConfigurationPage-C-49MccQ.js.map → ComponentConfigurationPage-DHV8IFbd.js.map} +1 -1
  8. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js → ComponentIcon-CRbtQEUV.js} +2 -3
  9. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js.map → ComponentIcon-CRbtQEUV.js.map} +1 -1
  10. package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -1
  11. package/dist/_chunks/{EditConfigurationPage-JT3E7NZy.mjs → EditConfigurationPage-DMnf8orh.mjs} +4 -4
  12. package/dist/_chunks/{EditConfigurationPage-JT3E7NZy.mjs.map → EditConfigurationPage-DMnf8orh.mjs.map} +1 -1
  13. package/dist/_chunks/{EditConfigurationPage-DjFJw56M.js → EditConfigurationPage-q76oeVU1.js} +5 -6
  14. package/dist/_chunks/{EditConfigurationPage-DjFJw56M.js.map → EditConfigurationPage-q76oeVU1.js.map} +1 -1
  15. package/dist/_chunks/{EditViewPage-zT3fBr4Y.js → EditViewPage-BXoY-ITh.js} +63 -13
  16. package/dist/_chunks/EditViewPage-BXoY-ITh.js.map +1 -0
  17. package/dist/_chunks/{EditViewPage-CPj61RMh.mjs → EditViewPage-COVXj9bh.mjs} +63 -12
  18. package/dist/_chunks/EditViewPage-COVXj9bh.mjs.map +1 -0
  19. package/dist/_chunks/{Field-dha5VnIQ.mjs → Field-CcppsFQR.mjs} +300 -203
  20. package/dist/_chunks/Field-CcppsFQR.mjs.map +1 -0
  21. package/dist/_chunks/{Field-Boxf9Ajp.js → Field-Dj1nOvt8.js} +303 -207
  22. package/dist/_chunks/Field-Dj1nOvt8.js.map +1 -0
  23. package/dist/_chunks/FieldTypeIcon-CMlNO8PE.mjs.map +1 -1
  24. package/dist/_chunks/FieldTypeIcon-Dnwq_IRF.js.map +1 -1
  25. package/dist/_chunks/{Form-DHrru2AV.mjs → Form-DbWwH0-A.mjs} +37 -18
  26. package/dist/_chunks/Form-DbWwH0-A.mjs.map +1 -0
  27. package/dist/_chunks/{Form-y5g1SRsh.js → Form-aTchNxab.js} +39 -21
  28. package/dist/_chunks/Form-aTchNxab.js.map +1 -0
  29. package/dist/_chunks/{History-CqN6K7SX.js → History-Cs6XM6EU.js} +81 -114
  30. package/dist/_chunks/History-Cs6XM6EU.js.map +1 -0
  31. package/dist/_chunks/{History-Bru_KoeP.mjs → History-tU567_hc.mjs} +82 -114
  32. package/dist/_chunks/History-tU567_hc.mjs.map +1 -0
  33. package/dist/_chunks/{ListConfigurationPage-R_p-SbHZ.js → ListConfigurationPage-7LeytuFD.js} +25 -13
  34. package/dist/_chunks/ListConfigurationPage-7LeytuFD.js.map +1 -0
  35. package/dist/_chunks/{ListConfigurationPage-D8wGABj0.mjs → ListConfigurationPage-DQryo_4i.mjs} +25 -12
  36. package/dist/_chunks/ListConfigurationPage-DQryo_4i.mjs.map +1 -0
  37. package/dist/_chunks/{ListViewPage-SID6TRb9.mjs → ListViewPage-B50esy_x.mjs} +118 -77
  38. package/dist/_chunks/ListViewPage-B50esy_x.mjs.map +1 -0
  39. package/dist/_chunks/{ListViewPage-pEw_zug9.js → ListViewPage-CaGBu5H5.js} +121 -81
  40. package/dist/_chunks/ListViewPage-CaGBu5H5.js.map +1 -0
  41. package/dist/_chunks/{NoContentTypePage-C5dcQojD.js → NoContentTypePage-BaBE00IO.js} +2 -2
  42. package/dist/_chunks/{NoContentTypePage-C5dcQojD.js.map → NoContentTypePage-BaBE00IO.js.map} +1 -1
  43. package/dist/_chunks/{NoContentTypePage-CJ7UXwrQ.mjs → NoContentTypePage-CiPP8cLx.mjs} +2 -2
  44. package/dist/_chunks/{NoContentTypePage-CJ7UXwrQ.mjs.map → NoContentTypePage-CiPP8cLx.mjs.map} +1 -1
  45. package/dist/_chunks/{NoPermissionsPage-B7syEq5E.mjs → NoPermissionsPage-0-CW106p.mjs} +2 -2
  46. package/dist/_chunks/{NoPermissionsPage-B7syEq5E.mjs.map → NoPermissionsPage-0-CW106p.mjs.map} +1 -1
  47. package/dist/_chunks/{NoPermissionsPage-BtPrImPP.js → NoPermissionsPage-DQn5cqZz.js} +2 -2
  48. package/dist/_chunks/{NoPermissionsPage-BtPrImPP.js.map → NoPermissionsPage-DQn5cqZz.js.map} +1 -1
  49. package/dist/_chunks/Preview-C1dBkhXf.mjs +272 -0
  50. package/dist/_chunks/Preview-C1dBkhXf.mjs.map +1 -0
  51. package/dist/_chunks/Preview-DH1h7kJ6.js +290 -0
  52. package/dist/_chunks/Preview-DH1h7kJ6.js.map +1 -0
  53. package/dist/_chunks/{Relations-B9Crnhnn.mjs → Relations-Bvne4TvU.mjs} +76 -42
  54. package/dist/_chunks/Relations-Bvne4TvU.mjs.map +1 -0
  55. package/dist/_chunks/{Relations-DjTQ5kGB.js → Relations-CkECnBOd.js} +76 -43
  56. package/dist/_chunks/Relations-CkECnBOd.js.map +1 -0
  57. package/dist/_chunks/{en-fbKQxLGn.js → en-BK8Xyl5I.js} +32 -18
  58. package/dist/_chunks/{en-fbKQxLGn.js.map → en-BK8Xyl5I.js.map} +1 -1
  59. package/dist/_chunks/{en-Ux26r5pl.mjs → en-Dtk_ot79.mjs} +32 -18
  60. package/dist/_chunks/{en-Ux26r5pl.mjs.map → en-Dtk_ot79.mjs.map} +1 -1
  61. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  62. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  63. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  64. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  65. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  66. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  67. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  68. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  69. package/dist/_chunks/hooks-BAaaKPS_.js.map +1 -1
  70. package/dist/_chunks/{index-DVPWZkbS.js → index-BN1pPa5v.js} +1135 -693
  71. package/dist/_chunks/index-BN1pPa5v.js.map +1 -0
  72. package/dist/_chunks/{index-DJXJw9V5.mjs → index-ByPZ754U.mjs} +1156 -714
  73. package/dist/_chunks/index-ByPZ754U.mjs.map +1 -0
  74. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  75. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  76. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  77. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  78. package/dist/_chunks/{layout-Bau7ZfLV.mjs → layout-CUTOYU8I.mjs} +26 -13
  79. package/dist/_chunks/layout-CUTOYU8I.mjs.map +1 -0
  80. package/dist/_chunks/{layout-Dm6fbiQj.js → layout-nBPDlXjr.js} +26 -14
  81. package/dist/_chunks/layout-nBPDlXjr.js.map +1 -0
  82. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  83. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  84. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  85. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  86. package/dist/_chunks/{relations-CKnpRgrN.js → relations-B6B-b7lI.js} +6 -7
  87. package/dist/_chunks/relations-B6B-b7lI.js.map +1 -0
  88. package/dist/_chunks/{relations-BH_kBSJ0.mjs → relations-CBc5HYHC.mjs} +6 -7
  89. package/dist/_chunks/relations-CBc5HYHC.mjs.map +1 -0
  90. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  91. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  92. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  93. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  94. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js → useDragAndDrop-BMtgCYzL.js} +5 -9
  95. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js.map → useDragAndDrop-BMtgCYzL.js.map} +1 -1
  96. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs → useDragAndDrop-DJ6jqvZN.mjs} +4 -7
  97. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs.map → useDragAndDrop-DJ6jqvZN.mjs.map} +1 -1
  98. package/dist/admin/index.js +2 -1
  99. package/dist/admin/index.js.map +1 -1
  100. package/dist/admin/index.mjs +5 -4
  101. package/dist/admin/src/content-manager.d.ts +3 -2
  102. package/dist/admin/src/exports.d.ts +1 -1
  103. package/dist/admin/src/history/index.d.ts +3 -0
  104. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  105. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  106. package/dist/admin/src/index.d.ts +1 -0
  107. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  108. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +2 -1
  109. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +2 -2
  110. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  111. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  112. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  113. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  114. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  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/constants.d.ts +1 -0
  119. package/dist/admin/src/preview/index.d.ts +4 -0
  120. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  121. package/dist/admin/src/preview/routes.d.ts +3 -0
  122. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  123. package/dist/admin/src/router.d.ts +1 -1
  124. package/dist/admin/src/services/api.d.ts +1 -1
  125. package/dist/admin/src/services/components.d.ts +2 -2
  126. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  127. package/dist/admin/src/services/documents.d.ts +19 -20
  128. package/dist/admin/src/services/init.d.ts +1 -1
  129. package/dist/admin/src/services/relations.d.ts +2 -2
  130. package/dist/admin/src/services/uid.d.ts +3 -3
  131. package/dist/admin/src/utils/validation.d.ts +4 -1
  132. package/dist/server/index.js +615 -299
  133. package/dist/server/index.js.map +1 -1
  134. package/dist/server/index.mjs +615 -298
  135. package/dist/server/index.mjs.map +1 -1
  136. package/dist/server/src/bootstrap.d.ts.map +1 -1
  137. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  138. package/dist/server/src/controllers/index.d.ts.map +1 -1
  139. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  140. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  141. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  142. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  143. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  144. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  145. package/dist/server/src/history/services/history.d.ts.map +1 -1
  146. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  147. package/dist/server/src/history/services/utils.d.ts +4 -4
  148. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  149. package/dist/server/src/index.d.ts +4 -4
  150. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  151. package/dist/server/src/preview/constants.d.ts +2 -0
  152. package/dist/server/src/preview/constants.d.ts.map +1 -0
  153. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  154. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  155. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  156. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  157. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  158. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  159. package/dist/server/src/preview/index.d.ts +4 -0
  160. package/dist/server/src/preview/index.d.ts.map +1 -0
  161. package/dist/server/src/preview/routes/index.d.ts +8 -0
  162. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  163. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  164. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  165. package/dist/server/src/preview/services/index.d.ts +16 -0
  166. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  167. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  168. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  169. package/dist/server/src/preview/services/preview.d.ts +12 -0
  170. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  171. package/dist/server/src/preview/utils.d.ts +19 -0
  172. package/dist/server/src/preview/utils.d.ts.map +1 -0
  173. package/dist/server/src/register.d.ts.map +1 -1
  174. package/dist/server/src/routes/index.d.ts.map +1 -1
  175. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  176. package/dist/server/src/services/document-metadata.d.ts +8 -8
  177. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  178. package/dist/server/src/services/index.d.ts +4 -4
  179. package/dist/server/src/services/index.d.ts.map +1 -1
  180. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  181. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  182. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  183. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  184. package/dist/server/src/utils/index.d.ts +2 -0
  185. package/dist/server/src/utils/index.d.ts.map +1 -1
  186. package/dist/shared/contracts/collection-types.d.ts +3 -1
  187. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  188. package/dist/shared/contracts/index.d.ts +1 -0
  189. package/dist/shared/contracts/index.d.ts.map +1 -1
  190. package/dist/shared/contracts/preview.d.ts +27 -0
  191. package/dist/shared/contracts/preview.d.ts.map +1 -0
  192. package/dist/shared/index.js +4 -0
  193. package/dist/shared/index.js.map +1 -1
  194. package/dist/shared/index.mjs +4 -0
  195. package/dist/shared/index.mjs.map +1 -1
  196. package/package.json +15 -15
  197. package/dist/_chunks/EditViewPage-CPj61RMh.mjs.map +0 -1
  198. package/dist/_chunks/EditViewPage-zT3fBr4Y.js.map +0 -1
  199. package/dist/_chunks/Field-Boxf9Ajp.js.map +0 -1
  200. package/dist/_chunks/Field-dha5VnIQ.mjs.map +0 -1
  201. package/dist/_chunks/Form-DHrru2AV.mjs.map +0 -1
  202. package/dist/_chunks/Form-y5g1SRsh.js.map +0 -1
  203. package/dist/_chunks/History-Bru_KoeP.mjs.map +0 -1
  204. package/dist/_chunks/History-CqN6K7SX.js.map +0 -1
  205. package/dist/_chunks/ListConfigurationPage-D8wGABj0.mjs.map +0 -1
  206. package/dist/_chunks/ListConfigurationPage-R_p-SbHZ.js.map +0 -1
  207. package/dist/_chunks/ListViewPage-SID6TRb9.mjs.map +0 -1
  208. package/dist/_chunks/ListViewPage-pEw_zug9.js.map +0 -1
  209. package/dist/_chunks/Relations-B9Crnhnn.mjs.map +0 -1
  210. package/dist/_chunks/Relations-DjTQ5kGB.js.map +0 -1
  211. package/dist/_chunks/index-DJXJw9V5.mjs.map +0 -1
  212. package/dist/_chunks/index-DVPWZkbS.js.map +0 -1
  213. package/dist/_chunks/layout-Bau7ZfLV.mjs.map +0 -1
  214. package/dist/_chunks/layout-Dm6fbiQj.js.map +0 -1
  215. package/dist/_chunks/relations-BH_kBSJ0.mjs.map +0 -1
  216. package/dist/_chunks/relations-CKnpRgrN.js.map +0 -1
  217. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  218. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  219. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  220. package/strapi-server.js +0 -3
@@ -1,25 +1,33 @@
1
- import { ClockCounterClockwise, CrossCircle, More, WarningCircle, ListPlus, Pencil, Trash, Check, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, Feather } from "@strapi/icons";
1
+ import { More, Cross, WarningCircle, ListPlus, Pencil, Trash, Check, CrossCircle, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
2
2
  import { jsx, Fragment, jsxs } from "react/jsx-runtime";
3
- import { useStrapiApp, useQueryParams, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useTracking, useForm, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
4
- import { stringify } from "qs";
5
- import { useIntl } from "react-intl";
6
- import { useNavigate, useParams, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
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";
7
4
  import * as React from "react";
8
5
  import { lazy } from "react";
9
- import { Button, Menu, VisuallyHidden, Flex, Box, Typography, Dialog, Modal, Radio, Status, SingleSelect, SingleSelectOption, Loader, IconButton, Tooltip, LinkButton } from "@strapi/design-system";
6
+ import { Menu, Button, VisuallyHidden, Flex, Dialog, Modal, Typography, Radio, Status, Box, SingleSelect, SingleSelectOption, IconButton, Loader, Tooltip, LinkButton } from "@strapi/design-system";
7
+ import mapValues from "lodash/fp/mapValues";
8
+ import { useIntl } from "react-intl";
9
+ import { useParams, useNavigate, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
10
10
  import { styled } from "styled-components";
11
11
  import * as yup from "yup";
12
12
  import { ValidationError } from "yup";
13
+ import { stringify } from "qs";
13
14
  import pipe from "lodash/fp/pipe";
14
15
  import { intervalToDuration, isPast } from "date-fns";
15
16
  import { createSlice, combineReducers } from "@reduxjs/toolkit";
16
- const __variableDynamicImportRuntimeHelper = (glob, path) => {
17
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
17
18
  const v = glob[path];
18
19
  if (v) {
19
20
  return typeof v === "function" ? v() : Promise.resolve(v);
20
21
  }
21
22
  return new Promise((_, reject) => {
22
- (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(reject.bind(null, new Error("Unknown variable dynamic import: " + path)));
23
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
24
+ reject.bind(
25
+ null,
26
+ new Error(
27
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
28
+ )
29
+ )
30
+ );
23
31
  });
24
32
  };
25
33
  const PLUGIN_ID = "content-manager";
@@ -49,42 +57,6 @@ const useInjectionZone = (area) => {
49
57
  const [page, position] = area.split(".");
50
58
  return contentManagerPlugin.getInjectedComponents(page, position);
51
59
  };
52
- const HistoryAction = ({ model, document }) => {
53
- const { formatMessage } = useIntl();
54
- const [{ query }] = useQueryParams();
55
- const navigate = useNavigate();
56
- const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
57
- if (!window.strapi.features.isEnabled("cms-content-history")) {
58
- return null;
59
- }
60
- return {
61
- icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
62
- label: formatMessage({
63
- id: "content-manager.history.document-action",
64
- defaultMessage: "Content History"
65
- }),
66
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
67
- disabled: (
68
- /**
69
- * The user is creating a new document.
70
- * It hasn't been saved yet, so there's no history to go to
71
- */
72
- !document || /**
73
- * The document has been created but the current dimension has never been saved.
74
- * For example, the user is creating a new locale in an existing document,
75
- * so there's no history for the document in that locale
76
- */
77
- !document.id || /**
78
- * History is only available for content types created by the user.
79
- * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
80
- * which start with `admin::` or `plugin::`
81
- */
82
- !model.startsWith("api::")
83
- ),
84
- position: "header"
85
- };
86
- };
87
- HistoryAction.type = "history";
88
60
  const ID = "id";
89
61
  const CREATED_BY_ATTRIBUTE_NAME = "createdBy";
90
62
  const UPDATED_BY_ATTRIBUTE_NAME = "updatedBy";
@@ -136,6 +108,7 @@ const DocumentRBAC = ({ children, permissions }) => {
136
108
  if (!slug) {
137
109
  throw new Error("Cannot find the slug param in the URL");
138
110
  }
111
+ const [{ rawQuery }] = useQueryParams();
139
112
  const userPermissions = useAuth("DocumentRBAC", (state) => state.permissions);
140
113
  const contentTypePermissions = React.useMemo(() => {
141
114
  const contentTypePermissions2 = userPermissions.filter(
@@ -146,7 +119,14 @@ const DocumentRBAC = ({ children, permissions }) => {
146
119
  return { ...acc, [action]: [permission] };
147
120
  }, {});
148
121
  }, [slug, userPermissions]);
149
- const { isLoading, allowedActions } = useRBAC(contentTypePermissions, permissions ?? void 0);
122
+ const { isLoading, allowedActions } = useRBAC(
123
+ contentTypePermissions,
124
+ permissions ?? void 0,
125
+ // TODO: useRBAC context should be typed and built differently
126
+ // We are passing raw query as context to the hook so that it can
127
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
128
+ rawQuery
129
+ );
150
130
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
151
131
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
152
132
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -194,7 +174,8 @@ const contentManagerApi = adminApi.enhanceEndpoints({
194
174
  "Document",
195
175
  "InitialData",
196
176
  "HistoryVersion",
197
- "Relations"
177
+ "Relations",
178
+ "UidAvailability"
198
179
  ]
199
180
  });
200
181
  const documentApi = contentManagerApi.injectEndpoints({
@@ -208,7 +189,12 @@ const documentApi = contentManagerApi.injectEndpoints({
208
189
  params: query
209
190
  }
210
191
  }),
211
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
192
+ invalidatesTags: (_result, error, { model }) => {
193
+ if (error) {
194
+ return [];
195
+ }
196
+ return [{ type: "Document", id: `${model}_LIST` }];
197
+ }
212
198
  }),
213
199
  cloneDocument: builder.mutation({
214
200
  query: ({ model, sourceId, data, params }) => ({
@@ -219,7 +205,10 @@ const documentApi = contentManagerApi.injectEndpoints({
219
205
  params
220
206
  }
221
207
  }),
222
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
208
+ invalidatesTags: (_result, _error, { model }) => [
209
+ { type: "Document", id: `${model}_LIST` },
210
+ { type: "UidAvailability", id: model }
211
+ ]
223
212
  }),
224
213
  /**
225
214
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -236,7 +225,8 @@ const documentApi = contentManagerApi.injectEndpoints({
236
225
  }),
237
226
  invalidatesTags: (result, _error, { model }) => [
238
227
  { type: "Document", id: `${model}_LIST` },
239
- "Relations"
228
+ "Relations",
229
+ { type: "UidAvailability", id: model }
240
230
  ]
241
231
  }),
242
232
  deleteDocument: builder.mutation({
@@ -277,7 +267,8 @@ const documentApi = contentManagerApi.injectEndpoints({
277
267
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
278
268
  },
279
269
  { type: "Document", id: `${model}_LIST` },
280
- "Relations"
270
+ "Relations",
271
+ { type: "UidAvailability", id: model }
281
272
  ];
282
273
  }
283
274
  }),
@@ -290,11 +281,12 @@ const documentApi = contentManagerApi.injectEndpoints({
290
281
  url: `/content-manager/collection-types/${model}`,
291
282
  method: "GET",
292
283
  config: {
293
- params
284
+ params: stringify(params, { encode: true })
294
285
  }
295
286
  }),
296
287
  providesTags: (result, _error, arg) => {
297
288
  return [
289
+ { type: "Document", id: `ALL_LIST` },
298
290
  { type: "Document", id: `${arg.model}_LIST` },
299
291
  ...result?.results.map(({ documentId }) => ({
300
292
  type: "Document",
@@ -333,6 +325,11 @@ const documentApi = contentManagerApi.injectEndpoints({
333
325
  {
334
326
  type: "Document",
335
327
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
328
+ },
329
+ // Make it easy to invalidate all individual documents queries for a model
330
+ {
331
+ type: "Document",
332
+ id: `${model}_ALL_ITEMS`
336
333
  }
337
334
  ];
338
335
  }
@@ -396,8 +393,21 @@ const documentApi = contentManagerApi.injectEndpoints({
396
393
  type: "Document",
397
394
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
398
395
  },
399
- "Relations"
396
+ "Relations",
397
+ { type: "UidAvailability", id: model }
400
398
  ];
399
+ },
400
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
401
+ const patchResult = dispatch(
402
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
403
+ Object.assign(draft.data, data);
404
+ })
405
+ );
406
+ try {
407
+ await queryFulfilled;
408
+ } catch {
409
+ patchResult.undo();
410
+ }
401
411
  }
402
412
  }),
403
413
  unpublishDocument: builder.mutation({
@@ -450,8 +460,7 @@ const {
450
460
  useUnpublishManyDocumentsMutation
451
461
  } = documentApi;
452
462
  const buildValidParams = (query) => {
453
- if (!query)
454
- return query;
463
+ if (!query) return query;
455
464
  const { plugins: _, ...validQueryParams } = {
456
465
  ...query,
457
466
  ...Object.values(query?.plugins ?? {}).reduce(
@@ -459,28 +468,44 @@ const buildValidParams = (query) => {
459
468
  {}
460
469
  )
461
470
  };
462
- if ("_q" in validQueryParams) {
463
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
464
- }
465
471
  return validQueryParams;
466
472
  };
467
473
  const isBaseQueryError = (error) => {
468
474
  return error.name !== void 0;
469
475
  };
470
- const createYupSchema = (attributes = {}, components = {}) => {
476
+ const arrayValidator = (attribute, options) => ({
477
+ message: translatedErrors.required,
478
+ test(value) {
479
+ if (options.status === "draft") {
480
+ return true;
481
+ }
482
+ if (!attribute.required) {
483
+ return true;
484
+ }
485
+ if (!value) {
486
+ return false;
487
+ }
488
+ if (Array.isArray(value) && value.length === 0) {
489
+ return false;
490
+ }
491
+ return true;
492
+ }
493
+ });
494
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
471
495
  const createModelSchema = (attributes2) => yup.object().shape(
472
496
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
473
497
  if (DOCUMENT_META_FIELDS.includes(name)) {
474
498
  return acc;
475
499
  }
476
500
  const validations = [
501
+ addNullableValidation,
477
502
  addRequiredValidation,
478
503
  addMinLengthValidation,
479
504
  addMaxLengthValidation,
480
505
  addMinValidation,
481
506
  addMaxValidation,
482
507
  addRegexValidation
483
- ].map((fn) => fn(attribute));
508
+ ].map((fn) => fn(attribute, options));
484
509
  const transformSchema = pipe(...validations);
485
510
  switch (attribute.type) {
486
511
  case "component": {
@@ -490,12 +515,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
490
515
  ...acc,
491
516
  [name]: transformSchema(
492
517
  yup.array().of(createModelSchema(attributes3).nullable(false))
493
- )
518
+ ).test(arrayValidator(attribute, options))
494
519
  };
495
520
  } else {
496
521
  return {
497
522
  ...acc,
498
- [name]: transformSchema(createModelSchema(attributes3))
523
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
499
524
  };
500
525
  }
501
526
  }
@@ -517,7 +542,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
517
542
  }
518
543
  )
519
544
  )
520
- )
545
+ ).test(arrayValidator(attribute, options))
521
546
  };
522
547
  case "relation":
523
548
  return {
@@ -529,7 +554,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
529
554
  } else if (Array.isArray(value)) {
530
555
  return yup.array().of(
531
556
  yup.object().shape({
532
- id: yup.string().required()
557
+ id: yup.number().required()
533
558
  })
534
559
  );
535
560
  } else if (typeof value === "object") {
@@ -581,6 +606,14 @@ const createAttributeSchema = (attribute) => {
581
606
  if (!value || typeof value === "string" && value.length === 0) {
582
607
  return true;
583
608
  }
609
+ if (typeof value === "object") {
610
+ try {
611
+ JSON.stringify(value);
612
+ return true;
613
+ } catch (err) {
614
+ return false;
615
+ }
616
+ }
584
617
  try {
585
618
  JSON.parse(value);
586
619
  return true;
@@ -599,13 +632,7 @@ const createAttributeSchema = (attribute) => {
599
632
  return yup.mixed();
600
633
  }
601
634
  };
602
- const addRequiredValidation = (attribute) => (schema) => {
603
- if (attribute.required) {
604
- return schema.required({
605
- id: translatedErrors.required.id,
606
- defaultMessage: "This field is required."
607
- });
608
- }
635
+ const nullableSchema = (schema) => {
609
636
  return schema?.nullable ? schema.nullable() : (
610
637
  // In some cases '.nullable' will not be available on the schema.
611
638
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -613,7 +640,22 @@ const addRequiredValidation = (attribute) => (schema) => {
613
640
  schema
614
641
  );
615
642
  };
616
- const addMinLengthValidation = (attribute) => (schema) => {
643
+ const addNullableValidation = () => (schema) => {
644
+ return nullableSchema(schema);
645
+ };
646
+ const addRequiredValidation = (attribute, options) => (schema) => {
647
+ if (options.status === "draft" || !attribute.required) {
648
+ return schema;
649
+ }
650
+ if (attribute.required && "required" in schema) {
651
+ return schema.required(translatedErrors.required);
652
+ }
653
+ return schema;
654
+ };
655
+ const addMinLengthValidation = (attribute, options) => (schema) => {
656
+ if (options.status === "draft") {
657
+ return schema;
658
+ }
617
659
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
618
660
  return schema.min(attribute.minLength, {
619
661
  ...translatedErrors.minLength,
@@ -635,10 +677,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
635
677
  }
636
678
  return schema;
637
679
  };
638
- const addMinValidation = (attribute) => (schema) => {
639
- if ("min" in attribute) {
680
+ const addMinValidation = (attribute, options) => (schema) => {
681
+ if (options.status === "draft") {
682
+ return schema;
683
+ }
684
+ if ("min" in attribute && "min" in schema) {
640
685
  const min = toInteger(attribute.min);
641
- if ("min" in schema && min) {
686
+ if (min) {
642
687
  return schema.min(min, {
643
688
  ...translatedErrors.min,
644
689
  values: {
@@ -756,16 +801,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
756
801
  }, {});
757
802
  return componentsByKey;
758
803
  };
759
- const useDocument = (args, opts) => {
804
+ const HOOKS = {
805
+ /**
806
+ * Hook that allows to mutate the displayed headers of the list view table
807
+ * @constant
808
+ * @type {string}
809
+ */
810
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
811
+ /**
812
+ * Hook that allows to mutate the CM's collection types links pre-set filters
813
+ * @constant
814
+ * @type {string}
815
+ */
816
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
817
+ /**
818
+ * Hook that allows to mutate the CM's edit view layout
819
+ * @constant
820
+ * @type {string}
821
+ */
822
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
823
+ /**
824
+ * Hook that allows to mutate the CM's single types links pre-set filters
825
+ * @constant
826
+ * @type {string}
827
+ */
828
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
829
+ };
830
+ const contentTypesApi = contentManagerApi.injectEndpoints({
831
+ endpoints: (builder) => ({
832
+ getContentTypeConfiguration: builder.query({
833
+ query: (uid) => ({
834
+ url: `/content-manager/content-types/${uid}/configuration`,
835
+ method: "GET"
836
+ }),
837
+ transformResponse: (response) => response.data,
838
+ providesTags: (_result, _error, uid) => [
839
+ { type: "ContentTypesConfiguration", id: uid },
840
+ { type: "ContentTypeSettings", id: "LIST" }
841
+ ]
842
+ }),
843
+ getAllContentTypeSettings: builder.query({
844
+ query: () => "/content-manager/content-types-settings",
845
+ transformResponse: (response) => response.data,
846
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
847
+ }),
848
+ updateContentTypeConfiguration: builder.mutation({
849
+ query: ({ uid, ...body }) => ({
850
+ url: `/content-manager/content-types/${uid}/configuration`,
851
+ method: "PUT",
852
+ data: body
853
+ }),
854
+ transformResponse: (response) => response.data,
855
+ invalidatesTags: (_result, _error, { uid }) => [
856
+ { type: "ContentTypesConfiguration", id: uid },
857
+ { type: "ContentTypeSettings", id: "LIST" },
858
+ // Is this necessary?
859
+ { type: "InitialData" }
860
+ ]
861
+ })
862
+ })
863
+ });
864
+ const {
865
+ useGetContentTypeConfigurationQuery,
866
+ useGetAllContentTypeSettingsQuery,
867
+ useUpdateContentTypeConfigurationMutation
868
+ } = contentTypesApi;
869
+ const checkIfAttributeIsDisplayable = (attribute) => {
870
+ const { type } = attribute;
871
+ if (type === "relation") {
872
+ return !attribute.relation.toLowerCase().includes("morph");
873
+ }
874
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
875
+ };
876
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
877
+ if (!mainFieldName) {
878
+ return void 0;
879
+ }
880
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
881
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
882
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
883
+ );
884
+ return {
885
+ name: mainFieldName,
886
+ type: mainFieldType ?? "string"
887
+ };
888
+ };
889
+ const DEFAULT_SETTINGS = {
890
+ bulkable: false,
891
+ filterable: false,
892
+ searchable: false,
893
+ pagination: false,
894
+ defaultSortBy: "",
895
+ defaultSortOrder: "asc",
896
+ mainField: "id",
897
+ pageSize: 10
898
+ };
899
+ const useDocumentLayout = (model) => {
900
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
901
+ const [{ query }] = useQueryParams();
902
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
760
903
  const { toggleNotification } = useNotification();
761
904
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
905
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
762
906
  const {
763
- currentData: data,
764
- isLoading: isLoadingDocument,
765
- isFetching: isFetchingDocument,
766
- error
767
- } = useGetDocumentQuery(args, opts);
768
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
907
+ data,
908
+ isLoading: isLoadingConfigs,
909
+ error,
910
+ isFetching: isFetchingConfigs
911
+ } = useGetContentTypeConfigurationQuery(model);
912
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
769
913
  React.useEffect(() => {
770
914
  if (error) {
771
915
  toggleNotification({
@@ -773,68 +917,322 @@ const useDocument = (args, opts) => {
773
917
  message: formatAPIError(error)
774
918
  });
775
919
  }
776
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
777
- const validationSchema = React.useMemo(() => {
778
- if (!schema) {
779
- return null;
780
- }
781
- return createYupSchema(schema.attributes, components);
782
- }, [schema, components]);
783
- const validate = React.useCallback(
784
- (document) => {
785
- if (!validationSchema) {
786
- throw new Error(
787
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
788
- );
789
- }
790
- try {
791
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
792
- return null;
793
- } catch (error2) {
794
- if (error2 instanceof ValidationError) {
795
- return getYupValidationErrors(error2);
796
- }
797
- throw error2;
798
- }
920
+ }, [error, formatAPIError, toggleNotification]);
921
+ const editLayout = React.useMemo(
922
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
923
+ layout: [],
924
+ components: {},
925
+ metadatas: {},
926
+ options: {},
927
+ settings: DEFAULT_SETTINGS
799
928
  },
800
- [validationSchema]
929
+ [data, isLoading, schemas, schema, components]
930
+ );
931
+ const listLayout = React.useMemo(() => {
932
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
933
+ layout: [],
934
+ metadatas: {},
935
+ options: {},
936
+ settings: DEFAULT_SETTINGS
937
+ };
938
+ }, [data, isLoading, schemas, schema, components]);
939
+ const { layout: edit } = React.useMemo(
940
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
941
+ layout: editLayout,
942
+ query
943
+ }),
944
+ [editLayout, query, runHookWaterfall]
801
945
  );
802
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
803
946
  return {
804
- components,
805
- document: data?.data,
806
- meta: data?.meta,
947
+ error,
807
948
  isLoading,
808
- schema,
809
- validate
949
+ edit,
950
+ list: listLayout
810
951
  };
811
952
  };
812
- const useDoc = () => {
813
- const { id, slug, collectionType, origin } = useParams();
814
- const [{ query }] = useQueryParams();
815
- const params = React.useMemo(() => buildValidParams(query), [query]);
816
- if (!collectionType) {
817
- throw new Error("Could not find collectionType in url params");
818
- }
819
- if (!slug) {
953
+ const useDocLayout = () => {
954
+ const { model } = useDoc();
955
+ return useDocumentLayout(model);
956
+ };
957
+ const formatEditLayout = (data, {
958
+ schemas,
959
+ schema,
960
+ components
961
+ }) => {
962
+ let currentPanelIndex = 0;
963
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
964
+ data.contentType.layouts.edit,
965
+ schema?.attributes,
966
+ data.contentType.metadatas,
967
+ { configurations: data.components, schemas: components },
968
+ schemas
969
+ ).reduce((panels, row) => {
970
+ if (row.some((field) => field.type === "dynamiczone")) {
971
+ panels.push([row]);
972
+ currentPanelIndex += 2;
973
+ } else {
974
+ if (!panels[currentPanelIndex]) {
975
+ panels.push([row]);
976
+ } else {
977
+ panels[currentPanelIndex].push(row);
978
+ }
979
+ }
980
+ return panels;
981
+ }, []);
982
+ const componentEditAttributes = Object.entries(data.components).reduce(
983
+ (acc, [uid, configuration]) => {
984
+ acc[uid] = {
985
+ layout: convertEditLayoutToFieldLayouts(
986
+ configuration.layouts.edit,
987
+ components[uid].attributes,
988
+ configuration.metadatas,
989
+ { configurations: data.components, schemas: components }
990
+ ),
991
+ settings: {
992
+ ...configuration.settings,
993
+ icon: components[uid].info.icon,
994
+ displayName: components[uid].info.displayName
995
+ }
996
+ };
997
+ return acc;
998
+ },
999
+ {}
1000
+ );
1001
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1002
+ (acc, [attribute, metadata]) => {
1003
+ return {
1004
+ ...acc,
1005
+ [attribute]: metadata.edit
1006
+ };
1007
+ },
1008
+ {}
1009
+ );
1010
+ return {
1011
+ layout: panelledEditAttributes,
1012
+ components: componentEditAttributes,
1013
+ metadatas: editMetadatas,
1014
+ settings: {
1015
+ ...data.contentType.settings,
1016
+ displayName: schema?.info.displayName
1017
+ },
1018
+ options: {
1019
+ ...schema?.options,
1020
+ ...schema?.pluginOptions,
1021
+ ...data.contentType.options
1022
+ }
1023
+ };
1024
+ };
1025
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1026
+ return rows.map(
1027
+ (row) => row.map((field) => {
1028
+ const attribute = attributes[field.name];
1029
+ if (!attribute) {
1030
+ return null;
1031
+ }
1032
+ const { edit: metadata } = metadatas[field.name];
1033
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1034
+ return {
1035
+ attribute,
1036
+ disabled: !metadata.editable,
1037
+ hint: metadata.description,
1038
+ label: metadata.label ?? "",
1039
+ name: field.name,
1040
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1041
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1042
+ schemas,
1043
+ components: components?.schemas ?? {}
1044
+ }),
1045
+ placeholder: metadata.placeholder ?? "",
1046
+ required: attribute.required ?? false,
1047
+ size: field.size,
1048
+ unique: "unique" in attribute ? attribute.unique : false,
1049
+ visible: metadata.visible ?? true,
1050
+ type: attribute.type
1051
+ };
1052
+ }).filter((field) => field !== null)
1053
+ );
1054
+ };
1055
+ const formatListLayout = (data, {
1056
+ schemas,
1057
+ schema,
1058
+ components
1059
+ }) => {
1060
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1061
+ (acc, [attribute, metadata]) => {
1062
+ return {
1063
+ ...acc,
1064
+ [attribute]: metadata.list
1065
+ };
1066
+ },
1067
+ {}
1068
+ );
1069
+ const listAttributes = convertListLayoutToFieldLayouts(
1070
+ data.contentType.layouts.list,
1071
+ schema?.attributes,
1072
+ listMetadatas,
1073
+ { configurations: data.components, schemas: components },
1074
+ schemas
1075
+ );
1076
+ return {
1077
+ layout: listAttributes,
1078
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1079
+ metadatas: listMetadatas,
1080
+ options: {
1081
+ ...schema?.options,
1082
+ ...schema?.pluginOptions,
1083
+ ...data.contentType.options
1084
+ }
1085
+ };
1086
+ };
1087
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1088
+ return columns.map((name) => {
1089
+ const attribute = attributes[name];
1090
+ if (!attribute) {
1091
+ return null;
1092
+ }
1093
+ const metadata = metadatas[name];
1094
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1095
+ return {
1096
+ attribute,
1097
+ label: metadata.label ?? "",
1098
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1099
+ schemas,
1100
+ components: components?.schemas ?? {}
1101
+ }),
1102
+ name,
1103
+ searchable: metadata.searchable ?? true,
1104
+ sortable: metadata.sortable ?? true
1105
+ };
1106
+ }).filter((field) => field !== null);
1107
+ };
1108
+ const useDocument = (args, opts) => {
1109
+ const { toggleNotification } = useNotification();
1110
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1111
+ const {
1112
+ currentData: data,
1113
+ isLoading: isLoadingDocument,
1114
+ isFetching: isFetchingDocument,
1115
+ error
1116
+ } = useGetDocumentQuery(args, {
1117
+ ...opts,
1118
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1119
+ });
1120
+ const {
1121
+ components,
1122
+ schema,
1123
+ schemas,
1124
+ isLoading: isLoadingSchema
1125
+ } = useContentTypeSchema(args.model);
1126
+ React.useEffect(() => {
1127
+ if (error) {
1128
+ toggleNotification({
1129
+ type: "danger",
1130
+ message: formatAPIError(error)
1131
+ });
1132
+ }
1133
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1134
+ const validationSchema = React.useMemo(() => {
1135
+ if (!schema) {
1136
+ return null;
1137
+ }
1138
+ return createYupSchema(schema.attributes, components);
1139
+ }, [schema, components]);
1140
+ const validate = React.useCallback(
1141
+ (document) => {
1142
+ if (!validationSchema) {
1143
+ throw new Error(
1144
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1145
+ );
1146
+ }
1147
+ try {
1148
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1149
+ return null;
1150
+ } catch (error2) {
1151
+ if (error2 instanceof ValidationError) {
1152
+ return getYupValidationErrors(error2);
1153
+ }
1154
+ throw error2;
1155
+ }
1156
+ },
1157
+ [validationSchema]
1158
+ );
1159
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1160
+ const hasError = !!error;
1161
+ return {
1162
+ components,
1163
+ document: data?.data,
1164
+ meta: data?.meta,
1165
+ isLoading,
1166
+ hasError,
1167
+ schema,
1168
+ schemas,
1169
+ validate
1170
+ };
1171
+ };
1172
+ const useDoc = () => {
1173
+ const { id, slug, collectionType, origin } = useParams();
1174
+ const [{ query }] = useQueryParams();
1175
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1176
+ if (!collectionType) {
1177
+ throw new Error("Could not find collectionType in url params");
1178
+ }
1179
+ if (!slug) {
820
1180
  throw new Error("Could not find model in url params");
821
1181
  }
1182
+ const document = useDocument(
1183
+ { documentId: origin || id, model: slug, collectionType, params },
1184
+ {
1185
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1186
+ }
1187
+ );
1188
+ const returnId = origin || id === "create" ? void 0 : id;
822
1189
  return {
823
1190
  collectionType,
824
1191
  model: slug,
825
- id: origin || id === "create" ? void 0 : id,
826
- ...useDocument(
827
- { documentId: origin || id, model: slug, collectionType, params },
828
- {
829
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
830
- }
831
- )
1192
+ id: returnId,
1193
+ ...document
1194
+ };
1195
+ };
1196
+ const useContentManagerContext = () => {
1197
+ const {
1198
+ collectionType,
1199
+ model,
1200
+ id,
1201
+ components,
1202
+ isLoading: isLoadingDoc,
1203
+ schema,
1204
+ schemas
1205
+ } = useDoc();
1206
+ const layout = useDocumentLayout(model);
1207
+ const form = useForm("useContentManagerContext", (state) => state);
1208
+ const isSingleType = collectionType === SINGLE_TYPES;
1209
+ const slug = model;
1210
+ const isCreatingEntry = id === "create";
1211
+ useContentTypeSchema();
1212
+ const isLoading = isLoadingDoc || layout.isLoading;
1213
+ const error = layout.error;
1214
+ return {
1215
+ error,
1216
+ isLoading,
1217
+ // Base metadata
1218
+ model,
1219
+ collectionType,
1220
+ id,
1221
+ slug,
1222
+ isCreatingEntry,
1223
+ isSingleType,
1224
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1225
+ // All schema infos
1226
+ components,
1227
+ contentType: schema,
1228
+ contentTypes: schemas,
1229
+ // Form state
1230
+ form,
1231
+ // layout infos
1232
+ layout
832
1233
  };
833
1234
  };
834
1235
  const prefixPluginTranslations = (trad, pluginId) => {
835
- if (!pluginId) {
836
- throw new TypeError("pluginId can't be empty");
837
- }
838
1236
  return Object.keys(trad).reduce((acc, current) => {
839
1237
  acc[`${pluginId}.${current}`] = trad[current];
840
1238
  return acc;
@@ -850,6 +1248,8 @@ const useDocumentActions = () => {
850
1248
  const { formatMessage } = useIntl();
851
1249
  const { trackUsage } = useTracking();
852
1250
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1251
+ const navigate = useNavigate();
1252
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
853
1253
  const [deleteDocument] = useDeleteDocumentMutation();
854
1254
  const _delete = React.useCallback(
855
1255
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1164,6 +1564,7 @@ const useDocumentActions = () => {
1164
1564
  defaultMessage: "Saved document"
1165
1565
  })
1166
1566
  });
1567
+ setCurrentStep("contentManager.success");
1167
1568
  return res.data;
1168
1569
  } catch (err) {
1169
1570
  toggleNotification({
@@ -1185,7 +1586,6 @@ const useDocumentActions = () => {
1185
1586
  sourceId
1186
1587
  });
1187
1588
  if ("error" in res) {
1188
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1189
1589
  return { error: res.error };
1190
1590
  }
1191
1591
  toggleNotification({
@@ -1204,7 +1604,7 @@ const useDocumentActions = () => {
1204
1604
  throw err;
1205
1605
  }
1206
1606
  },
1207
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1607
+ [autoCloneDocument, formatMessage, toggleNotification]
1208
1608
  );
1209
1609
  const [cloneDocument] = useCloneDocumentMutation();
1210
1610
  const clone = React.useCallback(
@@ -1230,6 +1630,7 @@ const useDocumentActions = () => {
1230
1630
  defaultMessage: "Cloned document"
1231
1631
  })
1232
1632
  });
1633
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1233
1634
  return res.data;
1234
1635
  } catch (err) {
1235
1636
  toggleNotification({
@@ -1240,7 +1641,7 @@ const useDocumentActions = () => {
1240
1641
  throw err;
1241
1642
  }
1242
1643
  },
1243
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1644
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1244
1645
  );
1245
1646
  const [getDoc] = useLazyGetDocumentQuery();
1246
1647
  const getDocument = React.useCallback(
@@ -1265,10 +1666,10 @@ const useDocumentActions = () => {
1265
1666
  update
1266
1667
  };
1267
1668
  };
1268
- const ProtectedHistoryPage = lazy(
1269
- () => import("./History-Bru_KoeP.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1669
+ const ProtectedHistoryPage = React.lazy(
1670
+ () => import("./History-tU567_hc.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1270
1671
  );
1271
- const routes$1 = [
1672
+ const routes$2 = [
1272
1673
  {
1273
1674
  path: ":collectionType/:slug/:id/history",
1274
1675
  Component: ProtectedHistoryPage
@@ -1278,32 +1679,45 @@ const routes$1 = [
1278
1679
  Component: ProtectedHistoryPage
1279
1680
  }
1280
1681
  ];
1682
+ const ProtectedPreviewPage = React.lazy(
1683
+ () => import("./Preview-C1dBkhXf.mjs").then((mod) => ({ default: mod.ProtectedPreviewPage }))
1684
+ );
1685
+ const routes$1 = [
1686
+ {
1687
+ path: ":collectionType/:slug/:id/preview",
1688
+ Component: ProtectedPreviewPage
1689
+ },
1690
+ {
1691
+ path: ":collectionType/:slug/preview",
1692
+ Component: ProtectedPreviewPage
1693
+ }
1694
+ ];
1281
1695
  const ProtectedEditViewPage = lazy(
1282
- () => import("./EditViewPage-CPj61RMh.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1696
+ () => import("./EditViewPage-COVXj9bh.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1283
1697
  );
1284
1698
  const ProtectedListViewPage = lazy(
1285
- () => import("./ListViewPage-SID6TRb9.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1699
+ () => import("./ListViewPage-B50esy_x.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1286
1700
  );
1287
1701
  const ProtectedListConfiguration = lazy(
1288
- () => import("./ListConfigurationPage-D8wGABj0.mjs").then((mod) => ({
1702
+ () => import("./ListConfigurationPage-DQryo_4i.mjs").then((mod) => ({
1289
1703
  default: mod.ProtectedListConfiguration
1290
1704
  }))
1291
1705
  );
1292
1706
  const ProtectedEditConfigurationPage = lazy(
1293
- () => import("./EditConfigurationPage-JT3E7NZy.mjs").then((mod) => ({
1707
+ () => import("./EditConfigurationPage-DMnf8orh.mjs").then((mod) => ({
1294
1708
  default: mod.ProtectedEditConfigurationPage
1295
1709
  }))
1296
1710
  );
1297
1711
  const ProtectedComponentConfigurationPage = lazy(
1298
- () => import("./ComponentConfigurationPage-DmwmiFQy.mjs").then((mod) => ({
1712
+ () => import("./ComponentConfigurationPage-D4J64ny7.mjs").then((mod) => ({
1299
1713
  default: mod.ProtectedComponentConfigurationPage
1300
1714
  }))
1301
1715
  );
1302
1716
  const NoPermissions = lazy(
1303
- () => import("./NoPermissionsPage-B7syEq5E.mjs").then((mod) => ({ default: mod.NoPermissions }))
1717
+ () => import("./NoPermissionsPage-0-CW106p.mjs").then((mod) => ({ default: mod.NoPermissions }))
1304
1718
  );
1305
1719
  const NoContentType = lazy(
1306
- () => import("./NoContentTypePage-CJ7UXwrQ.mjs").then((mod) => ({ default: mod.NoContentType }))
1720
+ () => import("./NoContentTypePage-CiPP8cLx.mjs").then((mod) => ({ default: mod.NoContentType }))
1307
1721
  );
1308
1722
  const CollectionTypePages = () => {
1309
1723
  const { collectionType } = useParams();
@@ -1315,7 +1729,7 @@ const CollectionTypePages = () => {
1315
1729
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1316
1730
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1317
1731
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1318
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1732
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1319
1733
  const routes = [
1320
1734
  {
1321
1735
  path: LIST_RELATIVE_PATH,
@@ -1349,6 +1763,7 @@ const routes = [
1349
1763
  path: "no-content-types",
1350
1764
  Component: NoContentType
1351
1765
  },
1766
+ ...routes$2,
1352
1767
  ...routes$1
1353
1768
  ];
1354
1769
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1417,12 +1832,14 @@ const DocumentActionButton = (action) => {
1417
1832
  /* @__PURE__ */ jsx(
1418
1833
  Button,
1419
1834
  {
1420
- flex: 1,
1835
+ flex: "auto",
1421
1836
  startIcon: action.icon,
1422
1837
  disabled: action.disabled,
1423
1838
  onClick: handleClick(action),
1424
1839
  justifyContent: "center",
1425
1840
  variant: action.variant || "default",
1841
+ paddingTop: "7px",
1842
+ paddingBottom: "7px",
1426
1843
  children: action.label
1427
1844
  }
1428
1845
  ),
@@ -1430,7 +1847,7 @@ const DocumentActionButton = (action) => {
1430
1847
  DocumentActionConfirmDialog,
1431
1848
  {
1432
1849
  ...action.dialog,
1433
- variant: action.variant,
1850
+ variant: action.dialog?.variant ?? action.variant,
1434
1851
  isOpen: dialogId === action.id,
1435
1852
  onClose: handleClose
1436
1853
  }
@@ -1445,6 +1862,11 @@ const DocumentActionButton = (action) => {
1445
1862
  ) : null
1446
1863
  ] });
1447
1864
  };
1865
+ const MenuItem = styled(Menu.Item)`
1866
+ &:hover {
1867
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
1868
+ }
1869
+ `;
1448
1870
  const DocumentActionsMenu = ({
1449
1871
  actions: actions2,
1450
1872
  children,
@@ -1487,9 +1909,9 @@ const DocumentActionsMenu = ({
1487
1909
  disabled: isDisabled,
1488
1910
  size: "S",
1489
1911
  endIcon: null,
1490
- paddingTop: "7px",
1491
- paddingLeft: "9px",
1492
- paddingRight: "9px",
1912
+ paddingTop: "4px",
1913
+ paddingLeft: "7px",
1914
+ paddingRight: "7px",
1493
1915
  variant,
1494
1916
  children: [
1495
1917
  /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
@@ -1500,36 +1922,35 @@ const DocumentActionsMenu = ({
1500
1922
  ]
1501
1923
  }
1502
1924
  ),
1503
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1925
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1504
1926
  actions2.map((action) => {
1505
1927
  return /* @__PURE__ */ jsx(
1506
- Menu.Item,
1928
+ MenuItem,
1507
1929
  {
1508
1930
  disabled: action.disabled,
1509
1931
  onSelect: handleClick(action),
1510
1932
  display: "block",
1511
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1512
- /* @__PURE__ */ jsxs(Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1513
- /* @__PURE__ */ jsx(Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1514
- action.label
1515
- ] }),
1516
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1517
- Flex,
1518
- {
1519
- alignItems: "center",
1520
- background: "alternative100",
1521
- borderStyle: "solid",
1522
- borderColor: "alternative200",
1523
- borderWidth: "1px",
1524
- height: 5,
1525
- paddingLeft: 2,
1526
- paddingRight: 2,
1527
- hasRadius: true,
1528
- color: "alternative600",
1529
- children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1530
- }
1531
- )
1532
- ] })
1933
+ isVariantDanger: action.variant === "danger",
1934
+ isDisabled: action.disabled,
1935
+ children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxs(
1936
+ Flex,
1937
+ {
1938
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1939
+ gap: 2,
1940
+ tag: "span",
1941
+ children: [
1942
+ /* @__PURE__ */ jsx(
1943
+ Flex,
1944
+ {
1945
+ tag: "span",
1946
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1947
+ children: action.icon
1948
+ }
1949
+ ),
1950
+ action.label
1951
+ ]
1952
+ }
1953
+ ) })
1533
1954
  },
1534
1955
  action.id
1535
1956
  );
@@ -1609,11 +2030,11 @@ const DocumentActionConfirmDialog = ({
1609
2030
  /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1610
2031
  /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1611
2032
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1612
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
2033
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1613
2034
  id: "app.components.Button.cancel",
1614
2035
  defaultMessage: "Cancel"
1615
2036
  }) }) }),
1616
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
2037
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1617
2038
  id: "app.components.Button.confirm",
1618
2039
  defaultMessage: "Confirm"
1619
2040
  }) })
@@ -1640,8 +2061,20 @@ const DocumentActionModal = ({
1640
2061
  typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1641
2062
  ] }) });
1642
2063
  };
1643
- const PublishAction$1 = ({
1644
- activeTab,
2064
+ const transformData = (data) => {
2065
+ if (Array.isArray(data)) {
2066
+ return data.map(transformData);
2067
+ }
2068
+ if (typeof data === "object" && data !== null) {
2069
+ if ("apiData" in data) {
2070
+ return data.apiData;
2071
+ }
2072
+ return mapValues(transformData)(data);
2073
+ }
2074
+ return data;
2075
+ };
2076
+ const PublishAction$1 = ({
2077
+ activeTab,
1645
2078
  documentId,
1646
2079
  model,
1647
2080
  collectionType,
@@ -1652,13 +2085,18 @@ const PublishAction$1 = ({
1652
2085
  const navigate = useNavigate();
1653
2086
  const { toggleNotification } = useNotification();
1654
2087
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2088
+ const isListView = useMatch(LIST_PATH) !== null;
1655
2089
  const isCloning = useMatch(CLONE_PATH) !== null;
2090
+ const { id } = useParams();
1656
2091
  const { formatMessage } = useIntl();
1657
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1658
- "PublishAction",
1659
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1660
- );
2092
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1661
2093
  const { publish } = useDocumentActions();
2094
+ const [
2095
+ countDraftRelations,
2096
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2097
+ ] = useLazyGetDraftRelationCountQuery();
2098
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React.useState(0);
2099
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React.useState(0);
1662
2100
  const [{ query, rawQuery }] = useQueryParams();
1663
2101
  const params = React.useMemo(() => buildValidParams(query), [query]);
1664
2102
  const modified = useForm("PublishAction", ({ modified: modified2 }) => modified2);
@@ -1667,10 +2105,107 @@ const PublishAction$1 = ({
1667
2105
  const validate = useForm("PublishAction", (state) => state.validate);
1668
2106
  const setErrors = useForm("PublishAction", (state) => state.setErrors);
1669
2107
  const formValues = useForm("PublishAction", ({ values }) => values);
2108
+ React.useEffect(() => {
2109
+ if (isErrorDraftRelations) {
2110
+ toggleNotification({
2111
+ type: "danger",
2112
+ message: formatMessage({
2113
+ id: getTranslation("error.records.fetch-draft-relatons"),
2114
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2115
+ })
2116
+ });
2117
+ }
2118
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2119
+ React.useEffect(() => {
2120
+ const localDraftRelations = /* @__PURE__ */ new Set();
2121
+ const extractDraftRelations = (data) => {
2122
+ const relations = data.connect || [];
2123
+ relations.forEach((relation) => {
2124
+ if (relation.status === "draft") {
2125
+ localDraftRelations.add(relation.id);
2126
+ }
2127
+ });
2128
+ };
2129
+ const traverseAndExtract = (data) => {
2130
+ Object.entries(data).forEach(([key, value]) => {
2131
+ if (key === "connect" && Array.isArray(value)) {
2132
+ extractDraftRelations({ connect: value });
2133
+ } else if (typeof value === "object" && value !== null) {
2134
+ traverseAndExtract(value);
2135
+ }
2136
+ });
2137
+ };
2138
+ if (!documentId || modified) {
2139
+ traverseAndExtract(formValues);
2140
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2141
+ }
2142
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2143
+ React.useEffect(() => {
2144
+ if (!document || !document.documentId || isListView) {
2145
+ return;
2146
+ }
2147
+ const fetchDraftRelationsCount = async () => {
2148
+ const { data, error } = await countDraftRelations({
2149
+ collectionType,
2150
+ model,
2151
+ documentId,
2152
+ params
2153
+ });
2154
+ if (error) {
2155
+ throw error;
2156
+ }
2157
+ if (data) {
2158
+ setServerCountOfDraftRelations(data.data);
2159
+ }
2160
+ };
2161
+ fetchDraftRelationsCount();
2162
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1670
2163
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1671
2164
  if (!schema?.options?.draftAndPublish) {
1672
2165
  return null;
1673
2166
  }
2167
+ const performPublish = async () => {
2168
+ setSubmitting(true);
2169
+ try {
2170
+ const { errors } = await validate(true, {
2171
+ status: "published"
2172
+ });
2173
+ if (errors) {
2174
+ toggleNotification({
2175
+ type: "danger",
2176
+ message: formatMessage({
2177
+ id: "content-manager.validation.error",
2178
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2179
+ })
2180
+ });
2181
+ return;
2182
+ }
2183
+ const res = await publish(
2184
+ {
2185
+ collectionType,
2186
+ model,
2187
+ documentId,
2188
+ params
2189
+ },
2190
+ transformData(formValues)
2191
+ );
2192
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2193
+ if (id === "create") {
2194
+ navigate({
2195
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2196
+ search: rawQuery
2197
+ });
2198
+ }
2199
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2200
+ setErrors(formatValidationErrors(res.error));
2201
+ }
2202
+ } finally {
2203
+ setSubmitting(false);
2204
+ }
2205
+ };
2206
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2207
+ const enableDraftRelationsCount = false;
2208
+ const hasDraftRelations = enableDraftRelationsCount;
1674
2209
  return {
1675
2210
  /**
1676
2211
  * Disabled when:
@@ -1680,52 +2215,40 @@ const PublishAction$1 = ({
1680
2215
  * - the document is already published & not modified
1681
2216
  * - the document is being created & not modified
1682
2217
  * - the user doesn't have the permission to publish
1683
- * - the user doesn't have the permission to create a new document
1684
- * - the user doesn't have the permission to update the document
1685
2218
  */
1686
- disabled: isCloning || isSubmitting || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2219
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1687
2220
  label: formatMessage({
1688
2221
  id: "app.utils.publish",
1689
2222
  defaultMessage: "Publish"
1690
2223
  }),
1691
2224
  onClick: async () => {
1692
- setSubmitting(true);
1693
- try {
1694
- const { errors } = await validate();
1695
- if (errors) {
1696
- toggleNotification({
1697
- type: "danger",
1698
- message: formatMessage({
1699
- id: "content-manager.validation.error",
1700
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1701
- })
1702
- });
1703
- return;
1704
- }
1705
- const res = await publish(
1706
- {
1707
- collectionType,
1708
- model,
1709
- documentId,
1710
- params
1711
- },
1712
- formValues
1713
- );
1714
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1715
- navigate({
1716
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1717
- search: rawQuery
1718
- });
1719
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1720
- setErrors(formatValidationErrors(res.error));
2225
+ await performPublish();
2226
+ },
2227
+ dialog: hasDraftRelations ? {
2228
+ type: "dialog",
2229
+ variant: "danger",
2230
+ footer: null,
2231
+ title: formatMessage({
2232
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2233
+ defaultMessage: "Confirmation"
2234
+ }),
2235
+ content: formatMessage(
2236
+ {
2237
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2238
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2239
+ },
2240
+ {
2241
+ count: totalDraftRelations
1721
2242
  }
1722
- } finally {
1723
- setSubmitting(false);
2243
+ ),
2244
+ onConfirm: async () => {
2245
+ await performPublish();
1724
2246
  }
1725
- }
2247
+ } : void 0
1726
2248
  };
1727
2249
  };
1728
2250
  PublishAction$1.type = "publish";
2251
+ PublishAction$1.position = "panel";
1729
2252
  const UpdateAction = ({
1730
2253
  activeTab,
1731
2254
  documentId,
@@ -1738,10 +2261,6 @@ const UpdateAction = ({
1738
2261
  const cloneMatch = useMatch(CLONE_PATH);
1739
2262
  const isCloning = cloneMatch !== null;
1740
2263
  const { formatMessage } = useIntl();
1741
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1742
- canCreate: canCreate2,
1743
- canUpdate: canUpdate2
1744
- }));
1745
2264
  const { create, update, clone } = useDocumentActions();
1746
2265
  const [{ query, rawQuery }] = useQueryParams();
1747
2266
  const params = React.useMemo(() => buildValidParams(query), [query]);
@@ -1758,18 +2277,18 @@ const UpdateAction = ({
1758
2277
  * - the form is submitting
1759
2278
  * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1760
2279
  * - the active tab is the published tab
1761
- * - the user doesn't have the permission to create a new document
1762
- * - the user doesn't have the permission to update the document
1763
2280
  */
1764
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
2281
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1765
2282
  label: formatMessage({
1766
- id: "content-manager.containers.Edit.save",
2283
+ id: "global.save",
1767
2284
  defaultMessage: "Save"
1768
2285
  }),
1769
2286
  onClick: async () => {
1770
2287
  setSubmitting(true);
1771
2288
  try {
1772
- const { errors } = await validate();
2289
+ const { errors } = await validate(true, {
2290
+ status: "draft"
2291
+ });
1773
2292
  if (errors) {
1774
2293
  toggleNotification({
1775
2294
  type: "danger",
@@ -1787,13 +2306,16 @@ const UpdateAction = ({
1787
2306
  documentId: cloneMatch.params.origin,
1788
2307
  params
1789
2308
  },
1790
- document
2309
+ transformData(document)
1791
2310
  );
1792
2311
  if ("data" in res) {
1793
- navigate({
1794
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1795
- search: rawQuery
1796
- });
2312
+ navigate(
2313
+ {
2314
+ pathname: `../${res.data.documentId}`,
2315
+ search: rawQuery
2316
+ },
2317
+ { relative: "path" }
2318
+ );
1797
2319
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1798
2320
  setErrors(formatValidationErrors(res.error));
1799
2321
  }
@@ -1805,7 +2327,7 @@ const UpdateAction = ({
1805
2327
  documentId,
1806
2328
  params
1807
2329
  },
1808
- document
2330
+ transformData(document)
1809
2331
  );
1810
2332
  if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1811
2333
  setErrors(formatValidationErrors(res.error));
@@ -1818,13 +2340,16 @@ const UpdateAction = ({
1818
2340
  model,
1819
2341
  params
1820
2342
  },
1821
- document
2343
+ transformData(document)
1822
2344
  );
1823
2345
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1824
- navigate({
1825
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1826
- search: rawQuery
1827
- });
2346
+ navigate(
2347
+ {
2348
+ pathname: `../${res.data.documentId}`,
2349
+ search: rawQuery
2350
+ },
2351
+ { replace: true, relative: "path" }
2352
+ );
1828
2353
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1829
2354
  setErrors(formatValidationErrors(res.error));
1830
2355
  }
@@ -1836,6 +2361,7 @@ const UpdateAction = ({
1836
2361
  };
1837
2362
  };
1838
2363
  UpdateAction.type = "update";
2364
+ UpdateAction.position = "panel";
1839
2365
  const UNPUBLISH_DRAFT_OPTIONS = {
1840
2366
  KEEP: "keep",
1841
2367
  DISCARD: "discard"
@@ -1868,7 +2394,7 @@ const UnpublishAction$1 = ({
1868
2394
  id: "app.utils.unpublish",
1869
2395
  defaultMessage: "Unpublish"
1870
2396
  }),
1871
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2397
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1872
2398
  onClick: async () => {
1873
2399
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1874
2400
  if (!documentId) {
@@ -1958,6 +2484,7 @@ const UnpublishAction$1 = ({
1958
2484
  };
1959
2485
  };
1960
2486
  UnpublishAction$1.type = "unpublish";
2487
+ UnpublishAction$1.position = "panel";
1961
2488
  const DiscardAction = ({
1962
2489
  activeTab,
1963
2490
  documentId,
@@ -1980,7 +2507,7 @@ const DiscardAction = ({
1980
2507
  id: "content-manager.actions.discard.label",
1981
2508
  defaultMessage: "Discard changes"
1982
2509
  }),
1983
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2510
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1984
2511
  position: ["panel", "table-row"],
1985
2512
  variant: "danger",
1986
2513
  dialog: {
@@ -2008,11 +2535,7 @@ const DiscardAction = ({
2008
2535
  };
2009
2536
  };
2010
2537
  DiscardAction.type = "discard";
2011
- const StyledCrossCircle = styled(CrossCircle)`
2012
- path {
2013
- fill: currentColor;
2014
- }
2015
- `;
2538
+ DiscardAction.position = "panel";
2016
2539
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2017
2540
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2018
2541
  const RelativeTime = React.forwardRef(
@@ -2025,7 +2548,7 @@ const RelativeTime = React.forwardRef(
2025
2548
  });
2026
2549
  const unit = intervals.find((intervalUnit) => {
2027
2550
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2028
- });
2551
+ }) ?? "seconds";
2029
2552
  const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2030
2553
  const customInterval = customIntervals.find(
2031
2554
  (custom) => interval[custom.unit] < custom.threshold
@@ -2059,34 +2582,34 @@ const getDisplayName = ({
2059
2582
  return email ?? "";
2060
2583
  };
2061
2584
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2062
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2063
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2064
- return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2585
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2586
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2587
+ const { formatMessage } = useIntl();
2588
+ return /* @__PURE__ */ jsx(Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2589
+ id: `content-manager.containers.List.${status}`,
2590
+ defaultMessage: capitalise(status)
2591
+ }) }) });
2065
2592
  };
2066
2593
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2067
2594
  const { formatMessage } = useIntl();
2068
2595
  const isCloning = useMatch(CLONE_PATH) !== null;
2596
+ const params = useParams();
2069
2597
  const title = isCreating ? formatMessage({
2070
2598
  id: "content-manager.containers.edit.title.new",
2071
2599
  defaultMessage: "Create an entry"
2072
2600
  }) : documentTitle;
2073
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 8, paddingBottom: 4, gap: 3, children: [
2074
- /* @__PURE__ */ jsx(BackButton, {}),
2075
- /* @__PURE__ */ jsxs(
2076
- Flex,
2601
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2602
+ /* @__PURE__ */ jsx(
2603
+ BackButton,
2077
2604
  {
2078
- width: "100%",
2079
- justifyContent: "space-between",
2080
- paddingTop: 1,
2081
- gap: "80px",
2082
- alignItems: "flex-start",
2083
- children: [
2084
- /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2085
- /* @__PURE__ */ jsx(HeaderToolbar, {})
2086
- ]
2605
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2087
2606
  }
2088
2607
  ),
2089
- status ? /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) : null
2608
+ /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2609
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2610
+ /* @__PURE__ */ jsx(HeaderToolbar, {})
2611
+ ] }),
2612
+ status ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2090
2613
  ] });
2091
2614
  };
2092
2615
  const HeaderToolbar = () => {
@@ -2132,7 +2655,7 @@ const HeaderToolbar = () => {
2132
2655
  meta: isCloning ? void 0 : meta,
2133
2656
  collectionType
2134
2657
  },
2135
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2658
+ descriptions: plugins["content-manager"].apis.getDocumentActions("header"),
2136
2659
  children: (actions2) => {
2137
2660
  const headerActions = actions2.filter((action) => {
2138
2661
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2169,12 +2692,12 @@ const Information = ({ activeTab }) => {
2169
2692
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2170
2693
  label: formatMessage({
2171
2694
  id: "content-manager.containers.edit.information.last-published.label",
2172
- defaultMessage: "Last published"
2695
+ defaultMessage: "Published"
2173
2696
  }),
2174
2697
  value: formatMessage(
2175
2698
  {
2176
2699
  id: "content-manager.containers.edit.information.last-published.value",
2177
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2700
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2178
2701
  },
2179
2702
  {
2180
2703
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2187,12 +2710,12 @@ const Information = ({ activeTab }) => {
2187
2710
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2188
2711
  label: formatMessage({
2189
2712
  id: "content-manager.containers.edit.information.last-draft.label",
2190
- defaultMessage: "Last draft"
2713
+ defaultMessage: "Updated"
2191
2714
  }),
2192
2715
  value: formatMessage(
2193
2716
  {
2194
2717
  id: "content-manager.containers.edit.information.last-draft.value",
2195
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2718
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2196
2719
  },
2197
2720
  {
2198
2721
  time: /* @__PURE__ */ jsx(
@@ -2210,12 +2733,12 @@ const Information = ({ activeTab }) => {
2210
2733
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2211
2734
  label: formatMessage({
2212
2735
  id: "content-manager.containers.edit.information.document.label",
2213
- defaultMessage: "Document"
2736
+ defaultMessage: "Created"
2214
2737
  }),
2215
2738
  value: formatMessage(
2216
2739
  {
2217
2740
  id: "content-manager.containers.edit.information.document.value",
2218
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2741
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2219
2742
  },
2220
2743
  {
2221
2744
  time: /* @__PURE__ */ jsx(
@@ -2253,25 +2776,77 @@ const Information = ({ activeTab }) => {
2253
2776
  );
2254
2777
  };
2255
2778
  const HeaderActions = ({ actions: actions2 }) => {
2256
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2257
- if ("options" in action) {
2779
+ const [dialogId, setDialogId] = React.useState(null);
2780
+ const handleClick = (action) => async (e) => {
2781
+ if (!("options" in action)) {
2782
+ const { onClick = () => false, dialog, id } = action;
2783
+ const muteDialog = await onClick(e);
2784
+ if (dialog && !muteDialog) {
2785
+ e.preventDefault();
2786
+ setDialogId(id);
2787
+ }
2788
+ }
2789
+ };
2790
+ const handleClose = () => {
2791
+ setDialogId(null);
2792
+ };
2793
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2794
+ if (action.options) {
2258
2795
  return /* @__PURE__ */ jsx(
2259
2796
  SingleSelect,
2260
2797
  {
2261
2798
  size: "S",
2262
- disabled: action.disabled,
2263
- "aria-label": action.label,
2264
2799
  onChange: action.onSelect,
2265
- value: action.value,
2800
+ "aria-label": action.label,
2801
+ ...action,
2266
2802
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2267
2803
  },
2268
2804
  action.id
2269
2805
  );
2270
2806
  } else {
2271
- return null;
2807
+ if (action.type === "icon") {
2808
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
2809
+ /* @__PURE__ */ jsx(
2810
+ IconButton,
2811
+ {
2812
+ disabled: action.disabled,
2813
+ label: action.label,
2814
+ size: "S",
2815
+ onClick: handleClick(action),
2816
+ children: action.icon
2817
+ }
2818
+ ),
2819
+ action.dialog ? /* @__PURE__ */ jsx(
2820
+ HeaderActionDialog,
2821
+ {
2822
+ ...action.dialog,
2823
+ isOpen: dialogId === action.id,
2824
+ onClose: handleClose
2825
+ }
2826
+ ) : null
2827
+ ] }, action.id);
2828
+ }
2272
2829
  }
2273
2830
  }) });
2274
2831
  };
2832
+ const HeaderActionDialog = ({
2833
+ onClose,
2834
+ onCancel,
2835
+ title,
2836
+ content: Content,
2837
+ isOpen
2838
+ }) => {
2839
+ const handleClose = async () => {
2840
+ if (onCancel) {
2841
+ await onCancel();
2842
+ }
2843
+ onClose();
2844
+ };
2845
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2846
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2847
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
2848
+ ] }) });
2849
+ };
2275
2850
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2276
2851
  const navigate = useNavigate();
2277
2852
  const { formatMessage } = useIntl();
@@ -2288,6 +2863,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2288
2863
  };
2289
2864
  };
2290
2865
  ConfigureTheViewAction.type = "configure-the-view";
2866
+ ConfigureTheViewAction.position = "header";
2291
2867
  const EditTheModelAction = ({ model }) => {
2292
2868
  const navigate = useNavigate();
2293
2869
  const { formatMessage } = useIntl();
@@ -2304,6 +2880,7 @@ const EditTheModelAction = ({ model }) => {
2304
2880
  };
2305
2881
  };
2306
2882
  EditTheModelAction.type = "edit-the-model";
2883
+ EditTheModelAction.position = "header";
2307
2884
  const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2308
2885
  const navigate = useNavigate();
2309
2886
  const { formatMessage } = useIntl();
@@ -2312,12 +2889,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2312
2889
  const { delete: deleteAction } = useDocumentActions();
2313
2890
  const { toggleNotification } = useNotification();
2314
2891
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2892
+ const isLocalized = document?.locale != null;
2315
2893
  return {
2316
2894
  disabled: !canDelete || !document,
2317
- label: formatMessage({
2318
- id: "content-manager.actions.delete.label",
2319
- defaultMessage: "Delete document"
2320
- }),
2895
+ label: formatMessage(
2896
+ {
2897
+ id: "content-manager.actions.delete.label",
2898
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2899
+ },
2900
+ { isLocalized }
2901
+ ),
2321
2902
  icon: /* @__PURE__ */ jsx(Trash, {}),
2322
2903
  dialog: {
2323
2904
  type: "dialog",
@@ -2373,403 +2954,102 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2373
2954
  };
2374
2955
  };
2375
2956
  DeleteAction$1.type = "delete";
2957
+ DeleteAction$1.position = ["header", "table-row"];
2376
2958
  const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2377
2959
  const Panels = () => {
2378
2960
  const isCloning = useMatch(CLONE_PATH) !== null;
2379
2961
  const [
2380
2962
  {
2381
- query: { status }
2382
- }
2383
- ] = useQueryParams({
2384
- status: "draft"
2385
- });
2386
- const { model, id, document, meta, collectionType } = useDoc();
2387
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2388
- const props = {
2389
- activeTab: status,
2390
- model,
2391
- documentId: id,
2392
- document: isCloning ? void 0 : document,
2393
- meta: isCloning ? void 0 : meta,
2394
- collectionType
2395
- };
2396
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2397
- DescriptionComponentRenderer,
2398
- {
2399
- props,
2400
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2401
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2402
- }
2403
- ) });
2404
- };
2405
- const ActionsPanel = () => {
2406
- const { formatMessage } = useIntl();
2407
- return {
2408
- title: formatMessage({
2409
- id: "content-manager.containers.edit.panels.default.title",
2410
- defaultMessage: "Document"
2411
- }),
2412
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2413
- };
2414
- };
2415
- ActionsPanel.type = "actions";
2416
- const ActionsPanelContent = () => {
2417
- const isCloning = useMatch(CLONE_PATH) !== null;
2418
- const [
2419
- {
2420
- query: { status = "draft" }
2421
- }
2422
- ] = useQueryParams();
2423
- const { model, id, document, meta, collectionType } = useDoc();
2424
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2425
- const props = {
2426
- activeTab: status,
2427
- model,
2428
- documentId: id,
2429
- document: isCloning ? void 0 : document,
2430
- meta: isCloning ? void 0 : meta,
2431
- collectionType
2432
- };
2433
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2434
- /* @__PURE__ */ jsx(
2435
- DescriptionComponentRenderer,
2436
- {
2437
- props,
2438
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2439
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2440
- }
2441
- ),
2442
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2443
- ] });
2444
- };
2445
- const Panel = React.forwardRef(({ children, title }, ref) => {
2446
- return /* @__PURE__ */ jsxs(
2447
- Flex,
2448
- {
2449
- ref,
2450
- tag: "aside",
2451
- "aria-labelledby": "additional-information",
2452
- background: "neutral0",
2453
- borderColor: "neutral150",
2454
- hasRadius: true,
2455
- paddingBottom: 4,
2456
- paddingLeft: 4,
2457
- paddingRight: 4,
2458
- paddingTop: 4,
2459
- shadow: "tableShadow",
2460
- gap: 3,
2461
- direction: "column",
2462
- justifyContent: "stretch",
2463
- alignItems: "flex-start",
2464
- children: [
2465
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2466
- children
2467
- ]
2468
- }
2469
- );
2470
- });
2471
- const HOOKS = {
2472
- /**
2473
- * Hook that allows to mutate the displayed headers of the list view table
2474
- * @constant
2475
- * @type {string}
2476
- */
2477
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2478
- /**
2479
- * Hook that allows to mutate the CM's collection types links pre-set filters
2480
- * @constant
2481
- * @type {string}
2482
- */
2483
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2484
- /**
2485
- * Hook that allows to mutate the CM's edit view layout
2486
- * @constant
2487
- * @type {string}
2488
- */
2489
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2490
- /**
2491
- * Hook that allows to mutate the CM's single types links pre-set filters
2492
- * @constant
2493
- * @type {string}
2494
- */
2495
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2496
- };
2497
- const contentTypesApi = contentManagerApi.injectEndpoints({
2498
- endpoints: (builder) => ({
2499
- getContentTypeConfiguration: builder.query({
2500
- query: (uid) => ({
2501
- url: `/content-manager/content-types/${uid}/configuration`,
2502
- method: "GET"
2503
- }),
2504
- transformResponse: (response) => response.data,
2505
- providesTags: (_result, _error, uid) => [
2506
- { type: "ContentTypesConfiguration", id: uid },
2507
- { type: "ContentTypeSettings", id: "LIST" }
2508
- ]
2509
- }),
2510
- getAllContentTypeSettings: builder.query({
2511
- query: () => "/content-manager/content-types-settings",
2512
- transformResponse: (response) => response.data,
2513
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2514
- }),
2515
- updateContentTypeConfiguration: builder.mutation({
2516
- query: ({ uid, ...body }) => ({
2517
- url: `/content-manager/content-types/${uid}/configuration`,
2518
- method: "PUT",
2519
- data: body
2520
- }),
2521
- transformResponse: (response) => response.data,
2522
- invalidatesTags: (_result, _error, { uid }) => [
2523
- { type: "ContentTypesConfiguration", id: uid },
2524
- { type: "ContentTypeSettings", id: "LIST" },
2525
- // Is this necessary?
2526
- { type: "InitialData" }
2527
- ]
2528
- })
2529
- })
2530
- });
2531
- const {
2532
- useGetContentTypeConfigurationQuery,
2533
- useGetAllContentTypeSettingsQuery,
2534
- useUpdateContentTypeConfigurationMutation
2535
- } = contentTypesApi;
2536
- const checkIfAttributeIsDisplayable = (attribute) => {
2537
- const { type } = attribute;
2538
- if (type === "relation") {
2539
- return !attribute.relation.toLowerCase().includes("morph");
2540
- }
2541
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2542
- };
2543
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2544
- if (!mainFieldName) {
2545
- return void 0;
2546
- }
2547
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2548
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2549
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2550
- );
2551
- return {
2552
- name: mainFieldName,
2553
- type: mainFieldType ?? "string"
2554
- };
2555
- };
2556
- const DEFAULT_SETTINGS = {
2557
- bulkable: false,
2558
- filterable: false,
2559
- searchable: false,
2560
- pagination: false,
2561
- defaultSortBy: "",
2562
- defaultSortOrder: "asc",
2563
- mainField: "id",
2564
- pageSize: 10
2565
- };
2566
- const useDocumentLayout = (model) => {
2567
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2568
- const [{ query }] = useQueryParams();
2569
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2570
- const { toggleNotification } = useNotification();
2571
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2572
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2573
- const {
2574
- data,
2575
- isLoading: isLoadingConfigs,
2576
- error,
2577
- isFetching: isFetchingConfigs
2578
- } = useGetContentTypeConfigurationQuery(model);
2579
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2580
- React.useEffect(() => {
2581
- if (error) {
2582
- toggleNotification({
2583
- type: "danger",
2584
- message: formatAPIError(error)
2585
- });
2586
- }
2587
- }, [error, formatAPIError, toggleNotification]);
2588
- const editLayout = React.useMemo(
2589
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2590
- layout: [],
2591
- components: {},
2592
- metadatas: {},
2593
- options: {},
2594
- settings: DEFAULT_SETTINGS
2595
- },
2596
- [data, isLoading, schemas, schema, components]
2597
- );
2598
- const listLayout = React.useMemo(() => {
2599
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2600
- layout: [],
2601
- metadatas: {},
2602
- options: {},
2603
- settings: DEFAULT_SETTINGS
2604
- };
2605
- }, [data, isLoading, schemas, schema, components]);
2606
- const { layout: edit } = React.useMemo(
2607
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2608
- layout: editLayout,
2609
- query
2610
- }),
2611
- [editLayout, query, runHookWaterfall]
2612
- );
2613
- return {
2614
- error,
2615
- isLoading,
2616
- edit,
2617
- list: listLayout
2618
- };
2619
- };
2620
- const useDocLayout = () => {
2621
- const { model } = useDoc();
2622
- return useDocumentLayout(model);
2623
- };
2624
- const formatEditLayout = (data, {
2625
- schemas,
2626
- schema,
2627
- components
2628
- }) => {
2629
- let currentPanelIndex = 0;
2630
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2631
- data.contentType.layouts.edit,
2632
- schema?.attributes,
2633
- data.contentType.metadatas,
2634
- { configurations: data.components, schemas: components },
2635
- schemas
2636
- ).reduce((panels, row) => {
2637
- if (row.some((field) => field.type === "dynamiczone")) {
2638
- panels.push([row]);
2639
- currentPanelIndex += 2;
2640
- } else {
2641
- if (!panels[currentPanelIndex]) {
2642
- panels.push([]);
2643
- }
2644
- panels[currentPanelIndex].push(row);
2645
- }
2646
- return panels;
2647
- }, []);
2648
- const componentEditAttributes = Object.entries(data.components).reduce(
2649
- (acc, [uid, configuration]) => {
2650
- acc[uid] = {
2651
- layout: convertEditLayoutToFieldLayouts(
2652
- configuration.layouts.edit,
2653
- components[uid].attributes,
2654
- configuration.metadatas
2655
- ),
2656
- settings: {
2657
- ...configuration.settings,
2658
- icon: components[uid].info.icon,
2659
- displayName: components[uid].info.displayName
2660
- }
2661
- };
2662
- return acc;
2663
- },
2664
- {}
2665
- );
2666
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2667
- (acc, [attribute, metadata]) => {
2668
- return {
2669
- ...acc,
2670
- [attribute]: metadata.edit
2671
- };
2672
- },
2673
- {}
2674
- );
2675
- return {
2676
- layout: panelledEditAttributes,
2677
- components: componentEditAttributes,
2678
- metadatas: editMetadatas,
2679
- settings: {
2680
- ...data.contentType.settings,
2681
- displayName: schema?.info.displayName
2682
- },
2683
- options: {
2684
- ...schema?.options,
2685
- ...schema?.pluginOptions,
2686
- ...data.contentType.options
2963
+ query: { status }
2687
2964
  }
2965
+ ] = useQueryParams({
2966
+ status: "draft"
2967
+ });
2968
+ const { model, id, document, meta, collectionType } = useDoc();
2969
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
2970
+ const props = {
2971
+ activeTab: status,
2972
+ model,
2973
+ documentId: id,
2974
+ document: isCloning ? void 0 : document,
2975
+ meta: isCloning ? void 0 : meta,
2976
+ collectionType
2688
2977
  };
2978
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2979
+ DescriptionComponentRenderer,
2980
+ {
2981
+ props,
2982
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2983
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2984
+ }
2985
+ ) });
2689
2986
  };
2690
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2691
- return rows.map(
2692
- (row) => row.map((field) => {
2693
- const attribute = attributes[field.name];
2694
- if (!attribute) {
2695
- return null;
2696
- }
2697
- const { edit: metadata } = metadatas[field.name];
2698
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2699
- return {
2700
- attribute,
2701
- disabled: !metadata.editable,
2702
- hint: metadata.description,
2703
- label: metadata.label ?? "",
2704
- name: field.name,
2705
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2706
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2707
- schemas,
2708
- components: components?.schemas ?? {}
2709
- }),
2710
- placeholder: metadata.placeholder ?? "",
2711
- required: attribute.required ?? false,
2712
- size: field.size,
2713
- unique: "unique" in attribute ? attribute.unique : false,
2714
- visible: metadata.visible ?? true,
2715
- type: attribute.type
2716
- };
2717
- }).filter((field) => field !== null)
2718
- );
2719
- };
2720
- const formatListLayout = (data, {
2721
- schemas,
2722
- schema,
2723
- components
2724
- }) => {
2725
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2726
- (acc, [attribute, metadata]) => {
2727
- return {
2728
- ...acc,
2729
- [attribute]: metadata.list
2730
- };
2731
- },
2732
- {}
2733
- );
2734
- const listAttributes = convertListLayoutToFieldLayouts(
2735
- data.contentType.layouts.list,
2736
- schema?.attributes,
2737
- listMetadatas,
2738
- { configurations: data.components, schemas: components },
2739
- schemas
2740
- );
2987
+ const ActionsPanel = () => {
2988
+ const { formatMessage } = useIntl();
2741
2989
  return {
2742
- layout: listAttributes,
2743
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2744
- metadatas: listMetadatas,
2745
- options: {
2746
- ...schema?.options,
2747
- ...schema?.pluginOptions,
2748
- ...data.contentType.options
2749
- }
2990
+ title: formatMessage({
2991
+ id: "content-manager.containers.edit.panels.default.title",
2992
+ defaultMessage: "Entry"
2993
+ }),
2994
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2750
2995
  };
2751
2996
  };
2752
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2753
- return columns.map((name) => {
2754
- const attribute = attributes[name];
2755
- if (!attribute) {
2756
- return null;
2997
+ ActionsPanel.type = "actions";
2998
+ const ActionsPanelContent = () => {
2999
+ const isCloning = useMatch(CLONE_PATH) !== null;
3000
+ const [
3001
+ {
3002
+ query: { status = "draft" }
2757
3003
  }
2758
- const metadata = metadatas[name];
2759
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2760
- return {
2761
- attribute,
2762
- label: metadata.label ?? "",
2763
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2764
- schemas,
2765
- components: components?.schemas ?? {}
2766
- }),
2767
- name,
2768
- searchable: metadata.searchable ?? true,
2769
- sortable: metadata.sortable ?? true
2770
- };
2771
- }).filter((field) => field !== null);
3004
+ ] = useQueryParams();
3005
+ const { model, id, document, meta, collectionType } = useDoc();
3006
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
3007
+ const props = {
3008
+ activeTab: status,
3009
+ model,
3010
+ documentId: id,
3011
+ document: isCloning ? void 0 : document,
3012
+ meta: isCloning ? void 0 : meta,
3013
+ collectionType
3014
+ };
3015
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
3016
+ /* @__PURE__ */ jsx(
3017
+ DescriptionComponentRenderer,
3018
+ {
3019
+ props,
3020
+ descriptions: plugins["content-manager"].apis.getDocumentActions("panel"),
3021
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
3022
+ }
3023
+ ),
3024
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
3025
+ ] });
2772
3026
  };
3027
+ const Panel = React.forwardRef(({ children, title }, ref) => {
3028
+ return /* @__PURE__ */ jsxs(
3029
+ Flex,
3030
+ {
3031
+ ref,
3032
+ tag: "aside",
3033
+ "aria-labelledby": "additional-information",
3034
+ background: "neutral0",
3035
+ borderColor: "neutral150",
3036
+ hasRadius: true,
3037
+ paddingBottom: 4,
3038
+ paddingLeft: 4,
3039
+ paddingRight: 4,
3040
+ paddingTop: 4,
3041
+ shadow: "tableShadow",
3042
+ gap: 3,
3043
+ direction: "column",
3044
+ justifyContent: "stretch",
3045
+ alignItems: "flex-start",
3046
+ children: [
3047
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3048
+ children
3049
+ ]
3050
+ }
3051
+ );
3052
+ });
2773
3053
  const ConfirmBulkActionDialog = ({
2774
3054
  onToggleDialog,
2775
3055
  isOpen = false,
@@ -2777,7 +3057,7 @@ const ConfirmBulkActionDialog = ({
2777
3057
  endAction
2778
3058
  }) => {
2779
3059
  const { formatMessage } = useIntl();
2780
- return /* @__PURE__ */ jsx(Dialog.Root, { onOpenChange: onToggleDialog, open: isOpen, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
3060
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2781
3061
  /* @__PURE__ */ jsx(Dialog.Header, { children: formatMessage({
2782
3062
  id: "app.components.ConfirmDialog.title",
2783
3063
  defaultMessage: "Confirmation"
@@ -2808,6 +3088,7 @@ const ConfirmDialogPublishAll = ({
2808
3088
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2809
3089
  const { model, schema } = useDoc();
2810
3090
  const [{ query }] = useQueryParams();
3091
+ const enableDraftRelationsCount = false;
2811
3092
  const {
2812
3093
  data: countDraftRelations = 0,
2813
3094
  isLoading,
@@ -2819,7 +3100,7 @@ const ConfirmDialogPublishAll = ({
2819
3100
  locale: query?.plugins?.i18n?.locale
2820
3101
  },
2821
3102
  {
2822
- skip: selectedEntries.length === 0
3103
+ skip: !enableDraftRelationsCount
2823
3104
  }
2824
3105
  );
2825
3106
  React.useEffect(() => {
@@ -2898,7 +3179,14 @@ const formatErrorMessages = (errors, parentKey, formatMessage) => {
2898
3179
  )
2899
3180
  );
2900
3181
  } else {
2901
- messages.push(...formatErrorMessages(value, currentKey, formatMessage));
3182
+ messages.push(
3183
+ ...formatErrorMessages(
3184
+ // @ts-expect-error TODO: check why value is not compatible with FormErrors
3185
+ value,
3186
+ currentKey,
3187
+ formatMessage
3188
+ )
3189
+ );
2902
3190
  }
2903
3191
  } else {
2904
3192
  messages.push(
@@ -2997,7 +3285,7 @@ const SelectedEntriesTableContent = ({
2997
3285
  status: row.status
2998
3286
  }
2999
3287
  ) }),
3000
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3288
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3001
3289
  IconButton,
3002
3290
  {
3003
3291
  tag: Link,
@@ -3006,23 +3294,16 @@ const SelectedEntriesTableContent = ({
3006
3294
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3007
3295
  },
3008
3296
  state: { from: pathname },
3009
- label: formatMessage(
3010
- { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3011
- {
3012
- target: formatMessage(
3013
- {
3014
- id: "content-manager.components.ListViewHelperPluginTable.row-line",
3015
- defaultMessage: "item line {number}"
3016
- },
3017
- { number: index2 + 1 }
3018
- )
3019
- }
3020
- ),
3297
+ label: formatMessage({
3298
+ id: "content-manager.bulk-publish.edit",
3299
+ defaultMessage: "Edit"
3300
+ }),
3021
3301
  target: "_blank",
3022
3302
  marginLeft: "auto",
3023
- children: /* @__PURE__ */ jsx(Pencil, {})
3303
+ variant: "ghost",
3304
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3024
3305
  }
3025
- ) })
3306
+ ) }) })
3026
3307
  ] }, row.id)) })
3027
3308
  ] });
3028
3309
  };
@@ -3059,7 +3340,13 @@ const SelectedEntriesModalContent = ({
3059
3340
  );
3060
3341
  const { rows, validationErrors } = React.useMemo(() => {
3061
3342
  if (data.length > 0 && schema) {
3062
- const validate = createYupSchema(schema.attributes, components);
3343
+ const validate = createYupSchema(
3344
+ schema.attributes,
3345
+ components,
3346
+ // Since this is the "Publish" action, the validation
3347
+ // schema must enforce the rules for published entities
3348
+ { status: "published" }
3349
+ );
3063
3350
  const validationErrors2 = {};
3064
3351
  const rows2 = data.map((entry) => {
3065
3352
  try {
@@ -3184,8 +3471,7 @@ const PublishAction = ({ documents, model }) => {
3184
3471
  const refetchList = () => {
3185
3472
  contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3186
3473
  };
3187
- if (!showPublishButton)
3188
- return null;
3474
+ if (!showPublishButton) return null;
3189
3475
  return {
3190
3476
  actionType: "publish",
3191
3477
  variant: "tertiary",
@@ -3253,8 +3539,7 @@ const DeleteAction = ({ documents, model }) => {
3253
3539
  selectRow([]);
3254
3540
  }
3255
3541
  };
3256
- if (!hasDeletePermission)
3257
- return null;
3542
+ if (!hasDeletePermission) return null;
3258
3543
  return {
3259
3544
  variant: "danger-light",
3260
3545
  label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
@@ -3303,8 +3588,7 @@ const UnpublishAction = ({ documents, model }) => {
3303
3588
  }
3304
3589
  };
3305
3590
  const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3306
- if (!showUnpublishButton)
3307
- return null;
3591
+ if (!showUnpublishButton) return null;
3308
3592
  return {
3309
3593
  variant: "tertiary",
3310
3594
  label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
@@ -3409,7 +3693,7 @@ const TableActions = ({ document }) => {
3409
3693
  DescriptionComponentRenderer,
3410
3694
  {
3411
3695
  props,
3412
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3696
+ descriptions: plugins["content-manager"].apis.getDocumentActions("table-row").filter((action) => action.name !== "PublishAction"),
3413
3697
  children: (actions2) => {
3414
3698
  const tableRowActions = actions2.filter((action) => {
3415
3699
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3468,6 +3752,7 @@ const EditAction = ({ documentId }) => {
3468
3752
  };
3469
3753
  };
3470
3754
  EditAction.type = "edit";
3755
+ EditAction.position = "table-row";
3471
3756
  const StyledPencil = styled(Pencil)`
3472
3757
  path {
3473
3758
  fill: currentColor;
@@ -3520,7 +3805,7 @@ const CloneAction = ({ model, documentId }) => {
3520
3805
  }),
3521
3806
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3522
3807
  footer: ({ onClose }) => {
3523
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3808
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3524
3809
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3525
3810
  id: "cancel",
3526
3811
  defaultMessage: "Cancel"
@@ -3544,6 +3829,7 @@ const CloneAction = ({ model, documentId }) => {
3544
3829
  };
3545
3830
  };
3546
3831
  CloneAction.type = "clone";
3832
+ CloneAction.position = "table-row";
3547
3833
  const StyledDuplicate = styled(Duplicate)`
3548
3834
  path {
3549
3835
  fill: currentColor;
@@ -3561,8 +3847,7 @@ class ContentManagerPlugin {
3561
3847
  documentActions = [
3562
3848
  ...DEFAULT_ACTIONS,
3563
3849
  ...DEFAULT_TABLE_ROW_ACTIONS,
3564
- ...DEFAULT_HEADER_ACTIONS,
3565
- HistoryAction
3850
+ ...DEFAULT_HEADER_ACTIONS
3566
3851
  ];
3567
3852
  editViewSidePanels = [ActionsPanel];
3568
3853
  headerActions = [];
@@ -3631,7 +3916,14 @@ class ContentManagerPlugin {
3631
3916
  addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3632
3917
  addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3633
3918
  getBulkActions: () => this.bulkActions,
3634
- getDocumentActions: () => this.documentActions,
3919
+ getDocumentActions: (position) => {
3920
+ if (position) {
3921
+ return this.documentActions.filter(
3922
+ (action) => action.position == void 0 || [action.position].flat().includes(position)
3923
+ );
3924
+ }
3925
+ return this.documentActions;
3926
+ },
3635
3927
  getEditViewSidePanels: () => this.editViewSidePanels,
3636
3928
  getHeaderActions: () => this.headerActions
3637
3929
  }
@@ -3641,16 +3933,71 @@ class ContentManagerPlugin {
3641
3933
  const getPrintableType = (value) => {
3642
3934
  const nativeType = typeof value;
3643
3935
  if (nativeType === "object") {
3644
- if (value === null)
3645
- return "null";
3646
- if (Array.isArray(value))
3647
- return "array";
3936
+ if (value === null) return "null";
3937
+ if (Array.isArray(value)) return "array";
3648
3938
  if (value instanceof Object && value.constructor.name !== "Object") {
3649
3939
  return value.constructor.name;
3650
3940
  }
3651
3941
  }
3652
3942
  return nativeType;
3653
3943
  };
3944
+ const HistoryAction = ({ model, document }) => {
3945
+ const { formatMessage } = useIntl();
3946
+ const [{ query }] = useQueryParams();
3947
+ const navigate = useNavigate();
3948
+ const { trackUsage } = useTracking();
3949
+ const { pathname } = useLocation();
3950
+ const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
3951
+ if (!window.strapi.features.isEnabled("cms-content-history")) {
3952
+ return null;
3953
+ }
3954
+ const handleOnClick = () => {
3955
+ const destination = { pathname: "history", search: pluginsQueryParams };
3956
+ trackUsage("willNavigate", {
3957
+ from: pathname,
3958
+ to: `${pathname}/${destination.pathname}`
3959
+ });
3960
+ navigate(destination);
3961
+ };
3962
+ return {
3963
+ icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
3964
+ label: formatMessage({
3965
+ id: "content-manager.history.document-action",
3966
+ defaultMessage: "Content History"
3967
+ }),
3968
+ onClick: handleOnClick,
3969
+ disabled: (
3970
+ /**
3971
+ * The user is creating a new document.
3972
+ * It hasn't been saved yet, so there's no history to go to
3973
+ */
3974
+ !document || /**
3975
+ * The document has been created but the current dimension has never been saved.
3976
+ * For example, the user is creating a new locale in an existing document,
3977
+ * so there's no history for the document in that locale
3978
+ */
3979
+ !document.id || /**
3980
+ * History is only available for content types created by the user.
3981
+ * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
3982
+ * which start with `admin::` or `plugin::`
3983
+ */
3984
+ !model.startsWith("api::")
3985
+ ),
3986
+ position: "header"
3987
+ };
3988
+ };
3989
+ HistoryAction.type = "history";
3990
+ HistoryAction.position = "header";
3991
+ const historyAdmin = {
3992
+ bootstrap(app) {
3993
+ const { addDocumentAction } = app.getPlugin("content-manager").apis;
3994
+ addDocumentAction((actions2) => {
3995
+ const indexOfDeleteAction = actions2.findIndex((action) => action.type === "delete");
3996
+ actions2.splice(indexOfDeleteAction, 0, HistoryAction);
3997
+ return actions2;
3998
+ });
3999
+ }
4000
+ };
3654
4001
  const initialState = {
3655
4002
  collectionTypeLinks: [],
3656
4003
  components: [],
@@ -3687,6 +4034,90 @@ const { setInitialData } = actions;
3687
4034
  const reducer = combineReducers({
3688
4035
  app: reducer$1
3689
4036
  });
4037
+ const previewApi = contentManagerApi.injectEndpoints({
4038
+ endpoints: (builder) => ({
4039
+ getPreviewUrl: builder.query({
4040
+ query({ query, params }) {
4041
+ return {
4042
+ url: `/content-manager/preview/url/${params.contentType}`,
4043
+ method: "GET",
4044
+ config: {
4045
+ params: query
4046
+ }
4047
+ };
4048
+ }
4049
+ })
4050
+ })
4051
+ });
4052
+ const { useGetPreviewUrlQuery } = previewApi;
4053
+ const ConditionalTooltip = ({ isShown, label, children }) => {
4054
+ if (isShown) {
4055
+ return /* @__PURE__ */ jsx(Tooltip, { label, children });
4056
+ }
4057
+ return children;
4058
+ };
4059
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4060
+ const { formatMessage } = useIntl();
4061
+ const { trackUsage } = useTracking();
4062
+ const { pathname } = useLocation();
4063
+ const [{ query }] = useQueryParams();
4064
+ const isModified = useForm("PreviewSidePanel", (state) => state.modified);
4065
+ const { data, error } = useGetPreviewUrlQuery({
4066
+ params: {
4067
+ contentType: model
4068
+ },
4069
+ query: {
4070
+ documentId,
4071
+ locale: document?.locale,
4072
+ status: document?.status
4073
+ }
4074
+ });
4075
+ if (!data?.data?.url || error) {
4076
+ return null;
4077
+ }
4078
+ const trackNavigation = () => {
4079
+ const destinationPathname = pathname.replace(/\/$/, "") + "/preview";
4080
+ trackUsage("willNavigate", { from: pathname, to: destinationPathname });
4081
+ };
4082
+ return {
4083
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4084
+ content: /* @__PURE__ */ jsx(Flex, { gap: 2, width: "100%", children: /* @__PURE__ */ jsx(
4085
+ ConditionalTooltip,
4086
+ {
4087
+ label: formatMessage({
4088
+ id: "content-manager.preview.panel.button-disabled-tooltip",
4089
+ defaultMessage: "Please save to open the preview"
4090
+ }),
4091
+ isShown: isModified,
4092
+ children: /* @__PURE__ */ jsx(
4093
+ Button,
4094
+ {
4095
+ variant: "tertiary",
4096
+ tag: Link,
4097
+ to: { pathname: "preview", search: stringify(query, { encode: false }) },
4098
+ onClick: trackNavigation,
4099
+ flex: "auto",
4100
+ disabled: isModified,
4101
+ children: formatMessage({
4102
+ id: "content-manager.preview.panel.button",
4103
+ defaultMessage: "Open preview"
4104
+ })
4105
+ }
4106
+ )
4107
+ }
4108
+ ) })
4109
+ };
4110
+ };
4111
+ const FEATURE_ID = "preview";
4112
+ const previewAdmin = {
4113
+ bootstrap(app) {
4114
+ if (!window.strapi.future.isEnabled(FEATURE_ID)) {
4115
+ return;
4116
+ }
4117
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4118
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4119
+ }
4120
+ };
3690
4121
  const index = {
3691
4122
  register(app) {
3692
4123
  const cm = new ContentManagerPlugin();
@@ -3706,7 +4137,7 @@ const index = {
3706
4137
  app.router.addRoute({
3707
4138
  path: "content-manager/*",
3708
4139
  lazy: async () => {
3709
- const { Layout } = await import("./layout-Bau7ZfLV.mjs");
4140
+ const { Layout } = await import("./layout-CUTOYU8I.mjs");
3710
4141
  return {
3711
4142
  Component: Layout
3712
4143
  };
@@ -3715,10 +4146,18 @@ const index = {
3715
4146
  });
3716
4147
  app.registerPlugin(cm.config);
3717
4148
  },
4149
+ bootstrap(app) {
4150
+ if (typeof historyAdmin.bootstrap === "function") {
4151
+ historyAdmin.bootstrap(app);
4152
+ }
4153
+ if (typeof previewAdmin.bootstrap === "function") {
4154
+ previewAdmin.bootstrap(app);
4155
+ }
4156
+ },
3718
4157
  async registerTrads({ locales }) {
3719
4158
  const importedTrads = await Promise.all(
3720
4159
  locales.map((locale) => {
3721
- 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-Ux26r5pl.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 }) => {
4160
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-Dtk_ot79.mjs"), "./translations/es.json": () => import("./es-D34tqjMw.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr--pg5jUbt.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-BHqhDq4V.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`, 3).then(({ default: data }) => {
3722
4161
  return {
3723
4162
  data: prefixPluginTranslations(data, PLUGIN_ID),
3724
4163
  locale
@@ -3739,13 +4178,16 @@ export {
3739
4178
  BulkActionsRenderer as B,
3740
4179
  COLLECTION_TYPES as C,
3741
4180
  DocumentStatus as D,
3742
- DEFAULT_SETTINGS as E,
3743
- convertEditLayoutToFieldLayouts as F,
3744
- useDocument as G,
4181
+ extractContentTypeComponents as E,
4182
+ DEFAULT_SETTINGS as F,
4183
+ convertEditLayoutToFieldLayouts as G,
3745
4184
  HOOKS as H,
3746
4185
  InjectionZone as I,
3747
- index as J,
3748
- useDocumentActions as K,
4186
+ useDocument as J,
4187
+ useGetPreviewUrlQuery as K,
4188
+ index as L,
4189
+ useContentManagerContext as M,
4190
+ useDocumentActions as N,
3749
4191
  Panels as P,
3750
4192
  RelativeTime as R,
3751
4193
  SINGLE_TYPES as S,
@@ -3763,18 +4205,18 @@ export {
3763
4205
  PERMISSIONS as k,
3764
4206
  DocumentRBAC as l,
3765
4207
  DOCUMENT_META_FIELDS as m,
3766
- useDocLayout as n,
3767
- useGetContentTypeConfigurationQuery as o,
3768
- CREATOR_FIELDS as p,
3769
- getMainField as q,
3770
- getDisplayName as r,
4208
+ CLONE_PATH as n,
4209
+ useDocLayout as o,
4210
+ useGetContentTypeConfigurationQuery as p,
4211
+ CREATOR_FIELDS as q,
4212
+ getMainField as r,
3771
4213
  setInitialData as s,
3772
- checkIfAttributeIsDisplayable as t,
4214
+ getDisplayName as t,
3773
4215
  useContentTypeSchema as u,
3774
- useGetAllDocumentsQuery as v,
3775
- convertListLayoutToFieldLayouts as w,
3776
- capitalise as x,
3777
- useUpdateContentTypeConfigurationMutation as y,
3778
- extractContentTypeComponents as z
4216
+ checkIfAttributeIsDisplayable as v,
4217
+ useGetAllDocumentsQuery as w,
4218
+ convertListLayoutToFieldLayouts as x,
4219
+ capitalise as y,
4220
+ useUpdateContentTypeConfigurationMutation as z
3779
4221
  };
3780
- //# sourceMappingURL=index-DJXJw9V5.mjs.map
4222
+ //# sourceMappingURL=index-ByPZ754U.mjs.map