@strapi/content-manager 0.0.0-experimental.3c73a4c6f6073abdf1608121a200c3d4d87b1aa8 → 0.0.0-experimental.3d5794391d4c2d9eddf7bd162c02f4bb836eb7a6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-BZIaEffq.js → ComponentConfigurationPage-Cjr64OS0.js} +4 -4
  3. package/dist/_chunks/{ComponentConfigurationPage-BZIaEffq.js.map → ComponentConfigurationPage-Cjr64OS0.js.map} +1 -1
  4. package/dist/_chunks/{ComponentConfigurationPage-Cxz51Sve.mjs → ComponentConfigurationPage-DKuCF_uX.mjs} +4 -4
  5. package/dist/_chunks/{ComponentConfigurationPage-Cxz51Sve.mjs.map → ComponentConfigurationPage-DKuCF_uX.mjs.map} +1 -1
  6. package/dist/_chunks/{EditConfigurationPage-CM62NN0L.js → EditConfigurationPage-BeikGxvq.js} +4 -4
  7. package/dist/_chunks/{EditConfigurationPage-CM62NN0L.js.map → EditConfigurationPage-BeikGxvq.js.map} +1 -1
  8. package/dist/_chunks/{EditConfigurationPage-CZLbgfIp.mjs → EditConfigurationPage-CyqSP6ru.mjs} +4 -4
  9. package/dist/_chunks/{EditConfigurationPage-CZLbgfIp.mjs.map → EditConfigurationPage-CyqSP6ru.mjs.map} +1 -1
  10. package/dist/_chunks/{EditViewPage-CzuJgWWp.mjs → EditViewPage-PrPHZN_9.mjs} +102 -51
  11. package/dist/_chunks/EditViewPage-PrPHZN_9.mjs.map +1 -0
  12. package/dist/_chunks/{EditViewPage-CU7724gt.js → EditViewPage-pyqEgLgP.js} +100 -49
  13. package/dist/_chunks/EditViewPage-pyqEgLgP.js.map +1 -0
  14. package/dist/_chunks/{Field-QtUSh5mU.mjs → Field-DuAYQka5.mjs} +600 -228
  15. package/dist/_chunks/Field-DuAYQka5.mjs.map +1 -0
  16. package/dist/_chunks/{Field-Dh1yZyqy.js → Field-fKtb7rWK.js} +602 -230
  17. package/dist/_chunks/Field-fKtb7rWK.js.map +1 -0
  18. package/dist/_chunks/{Form-BOR8NOe1.js → Form-BPXw-S-J.js} +52 -34
  19. package/dist/_chunks/Form-BPXw-S-J.js.map +1 -0
  20. package/dist/_chunks/{Form-COLpvlnv.mjs → Form-Dxh71ckp.mjs} +54 -36
  21. package/dist/_chunks/Form-Dxh71ckp.mjs.map +1 -0
  22. package/dist/_chunks/{History-CW2akQ6h.js → History-BGGn9JGY.js} +171 -126
  23. package/dist/_chunks/History-BGGn9JGY.js.map +1 -0
  24. package/dist/_chunks/{History-Bu53Yfw-.mjs → History-CGblSVMc.mjs} +172 -127
  25. package/dist/_chunks/History-CGblSVMc.mjs.map +1 -0
  26. package/dist/_chunks/{ListConfigurationPage-jzdhEk_u.js → ListConfigurationPage-D3Avyi4t.js} +58 -47
  27. package/dist/_chunks/ListConfigurationPage-D3Avyi4t.js.map +1 -0
  28. package/dist/_chunks/{ListConfigurationPage-TqrmwjPN.mjs → ListConfigurationPage-TIM0JveM.mjs} +59 -49
  29. package/dist/_chunks/ListConfigurationPage-TIM0JveM.mjs.map +1 -0
  30. package/dist/_chunks/{ListViewPage-BO_mOXIl.mjs → ListViewPage-C975eW-t.mjs} +128 -105
  31. package/dist/_chunks/ListViewPage-C975eW-t.mjs.map +1 -0
  32. package/dist/_chunks/{ListViewPage-B3bMOrMv.js → ListViewPage-DtAHWFMV.js} +130 -107
  33. package/dist/_chunks/ListViewPage-DtAHWFMV.js.map +1 -0
  34. package/dist/_chunks/{NoContentTypePage-D77xsNHj.js → NoContentTypePage-BFjWZX0i.js} +2 -2
  35. package/dist/_chunks/{NoContentTypePage-D77xsNHj.js.map → NoContentTypePage-BFjWZX0i.js.map} +1 -1
  36. package/dist/_chunks/{NoContentTypePage-DqB0QV0k.mjs → NoContentTypePage-SgNTVGjF.mjs} +2 -2
  37. package/dist/_chunks/{NoContentTypePage-DqB0QV0k.mjs.map → NoContentTypePage-SgNTVGjF.mjs.map} +1 -1
  38. package/dist/_chunks/{NoPermissionsPage-DTXi042N.mjs → NoPermissionsPage-D4XYRoPf.mjs} +2 -2
  39. package/dist/_chunks/{NoPermissionsPage-DTXi042N.mjs.map → NoPermissionsPage-D4XYRoPf.mjs.map} +1 -1
  40. package/dist/_chunks/{NoPermissionsPage-C6qTGogm.js → NoPermissionsPage-j7oulOpl.js} +2 -2
  41. package/dist/_chunks/{NoPermissionsPage-C6qTGogm.js.map → NoPermissionsPage-j7oulOpl.js.map} +1 -1
  42. package/dist/_chunks/Preview-Bx1WfmKJ.js +256 -0
  43. package/dist/_chunks/Preview-Bx1WfmKJ.js.map +1 -0
  44. package/dist/_chunks/Preview-Cy6fuAnd.mjs +237 -0
  45. package/dist/_chunks/Preview-Cy6fuAnd.mjs.map +1 -0
  46. package/dist/_chunks/{Relations-B6fb2POW.js → Relations-CXQqwRXC.js} +72 -36
  47. package/dist/_chunks/Relations-CXQqwRXC.js.map +1 -0
  48. package/dist/_chunks/{Relations-CJ4qdkRo.mjs → Relations-DUrYWw0N.mjs} +73 -37
  49. package/dist/_chunks/Relations-DUrYWw0N.mjs.map +1 -0
  50. package/dist/_chunks/{en-9GwRW_ku.mjs → en-69jRDM9j.mjs} +31 -17
  51. package/dist/_chunks/{en-9GwRW_ku.mjs.map → en-69jRDM9j.mjs.map} +1 -1
  52. package/dist/_chunks/{en-DZXjRiWA.js → en-DAgtrRoa.js} +31 -17
  53. package/dist/_chunks/{en-DZXjRiWA.js.map → en-DAgtrRoa.js.map} +1 -1
  54. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  55. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  56. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  57. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  58. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  59. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  60. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  61. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  62. package/dist/_chunks/{index-Dahjdw4h.mjs → index-Ta--2bRa.mjs} +1107 -883
  63. package/dist/_chunks/index-Ta--2bRa.mjs.map +1 -0
  64. package/dist/_chunks/{index-DcUu-_72.js → index-_j7lH3CO.js} +1099 -874
  65. package/dist/_chunks/index-_j7lH3CO.js.map +1 -0
  66. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  67. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  68. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  69. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  70. package/dist/_chunks/{layout-jcY4dyUG.js → layout-BDwU2I_y.js} +25 -12
  71. package/dist/_chunks/layout-BDwU2I_y.js.map +1 -0
  72. package/dist/_chunks/{layout-omucV6TV.mjs → layout-C8H4oKDo.mjs} +27 -14
  73. package/dist/_chunks/layout-C8H4oKDo.mjs.map +1 -0
  74. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  75. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  76. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  77. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  78. package/dist/_chunks/{relations-DGzD7ORa.js → relations-CPfMNzM6.js} +6 -7
  79. package/dist/_chunks/relations-CPfMNzM6.js.map +1 -0
  80. package/dist/_chunks/{relations-CN0-aw6p.mjs → relations-Ch70q86O.mjs} +6 -7
  81. package/dist/_chunks/relations-Ch70q86O.mjs.map +1 -0
  82. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  83. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  84. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  85. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  86. package/dist/admin/index.js +2 -1
  87. package/dist/admin/index.js.map +1 -1
  88. package/dist/admin/index.mjs +4 -3
  89. package/dist/admin/src/exports.d.ts +1 -1
  90. package/dist/admin/src/history/components/VersionInputRenderer.d.ts +1 -1
  91. package/dist/admin/src/history/index.d.ts +3 -0
  92. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  93. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  94. package/dist/admin/src/index.d.ts +1 -0
  95. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  96. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +8 -3
  97. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +2 -2
  98. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +4 -0
  99. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  100. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  101. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  102. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +6 -58
  103. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  104. package/dist/admin/src/pages/ListView/components/BulkActions/Actions.d.ts +3 -30
  105. package/dist/admin/src/pages/ListView/components/BulkActions/ConfirmBulkActionDialog.d.ts +2 -2
  106. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  107. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  108. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  109. package/dist/admin/src/preview/constants.d.ts +1 -0
  110. package/dist/admin/src/preview/index.d.ts +4 -0
  111. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  112. package/dist/admin/src/preview/routes.d.ts +3 -0
  113. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  114. package/dist/admin/src/router.d.ts +1 -1
  115. package/dist/admin/src/services/api.d.ts +1 -1
  116. package/dist/admin/src/services/components.d.ts +2 -2
  117. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  118. package/dist/admin/src/services/documents.d.ts +19 -17
  119. package/dist/admin/src/services/init.d.ts +1 -1
  120. package/dist/admin/src/services/relations.d.ts +2 -2
  121. package/dist/admin/src/services/uid.d.ts +3 -3
  122. package/dist/admin/src/utils/validation.d.ts +4 -1
  123. package/dist/server/index.js +551 -263
  124. package/dist/server/index.js.map +1 -1
  125. package/dist/server/index.mjs +552 -264
  126. package/dist/server/index.mjs.map +1 -1
  127. package/dist/server/src/bootstrap.d.ts.map +1 -1
  128. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  129. package/dist/server/src/controllers/index.d.ts.map +1 -1
  130. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  131. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  132. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  133. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  134. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  135. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  136. package/dist/server/src/history/services/history.d.ts.map +1 -1
  137. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  138. package/dist/server/src/history/services/utils.d.ts +4 -4
  139. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  140. package/dist/server/src/index.d.ts +4 -4
  141. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  142. package/dist/server/src/preview/constants.d.ts +2 -0
  143. package/dist/server/src/preview/constants.d.ts.map +1 -0
  144. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  145. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  146. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  147. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  148. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  149. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  150. package/dist/server/src/preview/index.d.ts +4 -0
  151. package/dist/server/src/preview/index.d.ts.map +1 -0
  152. package/dist/server/src/preview/routes/index.d.ts +8 -0
  153. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  154. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  155. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  156. package/dist/server/src/preview/services/index.d.ts +15 -0
  157. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  158. package/dist/server/src/preview/services/preview-config.d.ts +30 -0
  159. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  160. package/dist/server/src/preview/services/preview.d.ts +12 -0
  161. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  162. package/dist/server/src/preview/utils.d.ts +18 -0
  163. package/dist/server/src/preview/utils.d.ts.map +1 -0
  164. package/dist/server/src/routes/index.d.ts.map +1 -1
  165. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  166. package/dist/server/src/services/document-metadata.d.ts +8 -8
  167. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  168. package/dist/server/src/services/index.d.ts +4 -4
  169. package/dist/server/src/services/index.d.ts.map +1 -1
  170. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  171. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  172. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  173. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  174. package/dist/server/src/utils/index.d.ts +2 -0
  175. package/dist/server/src/utils/index.d.ts.map +1 -1
  176. package/dist/shared/contracts/collection-types.d.ts +3 -1
  177. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  178. package/dist/shared/contracts/index.d.ts +1 -0
  179. package/dist/shared/contracts/index.d.ts.map +1 -1
  180. package/dist/shared/contracts/preview.d.ts +27 -0
  181. package/dist/shared/contracts/preview.d.ts.map +1 -0
  182. package/dist/shared/index.js +4 -0
  183. package/dist/shared/index.js.map +1 -1
  184. package/dist/shared/index.mjs +4 -0
  185. package/dist/shared/index.mjs.map +1 -1
  186. package/package.json +14 -14
  187. package/dist/_chunks/EditViewPage-CU7724gt.js.map +0 -1
  188. package/dist/_chunks/EditViewPage-CzuJgWWp.mjs.map +0 -1
  189. package/dist/_chunks/Field-Dh1yZyqy.js.map +0 -1
  190. package/dist/_chunks/Field-QtUSh5mU.mjs.map +0 -1
  191. package/dist/_chunks/Form-BOR8NOe1.js.map +0 -1
  192. package/dist/_chunks/Form-COLpvlnv.mjs.map +0 -1
  193. package/dist/_chunks/History-Bu53Yfw-.mjs.map +0 -1
  194. package/dist/_chunks/History-CW2akQ6h.js.map +0 -1
  195. package/dist/_chunks/ListConfigurationPage-TqrmwjPN.mjs.map +0 -1
  196. package/dist/_chunks/ListConfigurationPage-jzdhEk_u.js.map +0 -1
  197. package/dist/_chunks/ListViewPage-B3bMOrMv.js.map +0 -1
  198. package/dist/_chunks/ListViewPage-BO_mOXIl.mjs.map +0 -1
  199. package/dist/_chunks/Relations-B6fb2POW.js.map +0 -1
  200. package/dist/_chunks/Relations-CJ4qdkRo.mjs.map +0 -1
  201. package/dist/_chunks/index-Dahjdw4h.mjs.map +0 -1
  202. package/dist/_chunks/index-DcUu-_72.js.map +0 -1
  203. package/dist/_chunks/layout-jcY4dyUG.js.map +0 -1
  204. package/dist/_chunks/layout-omucV6TV.mjs.map +0 -1
  205. package/dist/_chunks/relations-CN0-aw6p.mjs.map +0 -1
  206. package/dist/_chunks/relations-DGzD7ORa.js.map +0 -1
  207. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  208. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  209. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  210. package/strapi-server.js +0 -3
@@ -1,17 +1,18 @@
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 { Menu, VisuallyHidden, Flex, Typography, Dialog, DialogBody, DialogFooter, Button, ModalLayout, ModalHeader, ModalBody, Box, Radio, Status, SingleSelect, SingleSelectOption, ModalFooter, Loader, IconButton, Tooltip, LinkButton } from "@strapi/design-system";
10
- import { styled } from "styled-components";
6
+ import { Button, Menu, 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";
11
10
  import * as yup from "yup";
12
11
  import { ValidationError } from "yup";
13
12
  import pipe from "lodash/fp/pipe";
14
13
  import { intervalToDuration, isPast } from "date-fns";
14
+ import { styled } from "styled-components";
15
+ import { stringify } from "qs";
15
16
  import { createSlice, combineReducers } from "@reduxjs/toolkit";
16
17
  const __variableDynamicImportRuntimeHelper = (glob, path) => {
17
18
  const v = glob[path];
@@ -49,42 +50,6 @@ const useInjectionZone = (area) => {
49
50
  const [page, position] = area.split(".");
50
51
  return contentManagerPlugin.getInjectedComponents(page, position);
51
52
  };
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
53
  const ID = "id";
89
54
  const CREATED_BY_ATTRIBUTE_NAME = "createdBy";
90
55
  const UPDATED_BY_ATTRIBUTE_NAME = "updatedBy";
@@ -136,6 +101,7 @@ const DocumentRBAC = ({ children, permissions }) => {
136
101
  if (!slug) {
137
102
  throw new Error("Cannot find the slug param in the URL");
138
103
  }
104
+ const [{ rawQuery }] = useQueryParams();
139
105
  const userPermissions = useAuth("DocumentRBAC", (state) => state.permissions);
140
106
  const contentTypePermissions = React.useMemo(() => {
141
107
  const contentTypePermissions2 = userPermissions.filter(
@@ -146,7 +112,14 @@ const DocumentRBAC = ({ children, permissions }) => {
146
112
  return { ...acc, [action]: [permission] };
147
113
  }, {});
148
114
  }, [slug, userPermissions]);
149
- const { isLoading, allowedActions } = useRBAC(contentTypePermissions, permissions ?? void 0);
115
+ const { isLoading, allowedActions } = useRBAC(
116
+ contentTypePermissions,
117
+ permissions ?? void 0,
118
+ // TODO: useRBAC context should be typed and built differently
119
+ // We are passing raw query as context to the hook so that it can
120
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
121
+ rawQuery
122
+ );
150
123
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
151
124
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
152
125
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -194,10 +167,12 @@ const contentManagerApi = adminApi.enhanceEndpoints({
194
167
  "Document",
195
168
  "InitialData",
196
169
  "HistoryVersion",
197
- "Relations"
170
+ "Relations",
171
+ "UidAvailability"
198
172
  ]
199
173
  });
200
174
  const documentApi = contentManagerApi.injectEndpoints({
175
+ overrideExisting: true,
201
176
  endpoints: (builder) => ({
202
177
  autoCloneDocument: builder.mutation({
203
178
  query: ({ model, sourceId, query }) => ({
@@ -207,7 +182,12 @@ const documentApi = contentManagerApi.injectEndpoints({
207
182
  params: query
208
183
  }
209
184
  }),
210
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
185
+ invalidatesTags: (_result, error, { model }) => {
186
+ if (error) {
187
+ return [];
188
+ }
189
+ return [{ type: "Document", id: `${model}_LIST` }];
190
+ }
211
191
  }),
212
192
  cloneDocument: builder.mutation({
213
193
  query: ({ model, sourceId, data, params }) => ({
@@ -218,7 +198,10 @@ const documentApi = contentManagerApi.injectEndpoints({
218
198
  params
219
199
  }
220
200
  }),
221
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
201
+ invalidatesTags: (_result, _error, { model }) => [
202
+ { type: "Document", id: `${model}_LIST` },
203
+ { type: "UidAvailability", id: model }
204
+ ]
222
205
  }),
223
206
  /**
224
207
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -235,7 +218,8 @@ const documentApi = contentManagerApi.injectEndpoints({
235
218
  }),
236
219
  invalidatesTags: (result, _error, { model }) => [
237
220
  { type: "Document", id: `${model}_LIST` },
238
- "Relations"
221
+ "Relations",
222
+ { type: "UidAvailability", id: model }
239
223
  ]
240
224
  }),
241
225
  deleteDocument: builder.mutation({
@@ -276,7 +260,8 @@ const documentApi = contentManagerApi.injectEndpoints({
276
260
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
277
261
  },
278
262
  { type: "Document", id: `${model}_LIST` },
279
- "Relations"
263
+ "Relations",
264
+ { type: "UidAvailability", id: model }
280
265
  ];
281
266
  }
282
267
  }),
@@ -294,6 +279,7 @@ const documentApi = contentManagerApi.injectEndpoints({
294
279
  }),
295
280
  providesTags: (result, _error, arg) => {
296
281
  return [
282
+ { type: "Document", id: `ALL_LIST` },
297
283
  { type: "Document", id: `${arg.model}_LIST` },
298
284
  ...result?.results.map(({ documentId }) => ({
299
285
  type: "Document",
@@ -332,6 +318,11 @@ const documentApi = contentManagerApi.injectEndpoints({
332
318
  {
333
319
  type: "Document",
334
320
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
321
+ },
322
+ // Make it easy to invalidate all individual documents queries for a model
323
+ {
324
+ type: "Document",
325
+ id: `${model}_ALL_ITEMS`
335
326
  }
336
327
  ];
337
328
  }
@@ -395,8 +386,21 @@ const documentApi = contentManagerApi.injectEndpoints({
395
386
  type: "Document",
396
387
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
397
388
  },
398
- "Relations"
389
+ "Relations",
390
+ { type: "UidAvailability", id: model }
399
391
  ];
392
+ },
393
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
394
+ const patchResult = dispatch(
395
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
396
+ Object.assign(draft.data, data);
397
+ })
398
+ );
399
+ try {
400
+ await queryFulfilled;
401
+ } catch {
402
+ patchResult.undo();
403
+ }
400
404
  }
401
405
  }),
402
406
  unpublishDocument: builder.mutation({
@@ -466,20 +470,39 @@ const buildValidParams = (query) => {
466
470
  const isBaseQueryError = (error) => {
467
471
  return error.name !== void 0;
468
472
  };
469
- const createYupSchema = (attributes = {}, components = {}) => {
473
+ const arrayValidator = (attribute, options) => ({
474
+ message: translatedErrors.required,
475
+ test(value) {
476
+ if (options.status === "draft") {
477
+ return true;
478
+ }
479
+ if (!attribute.required) {
480
+ return true;
481
+ }
482
+ if (!value) {
483
+ return false;
484
+ }
485
+ if (Array.isArray(value) && value.length === 0) {
486
+ return false;
487
+ }
488
+ return true;
489
+ }
490
+ });
491
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
470
492
  const createModelSchema = (attributes2) => yup.object().shape(
471
493
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
472
494
  if (DOCUMENT_META_FIELDS.includes(name)) {
473
495
  return acc;
474
496
  }
475
497
  const validations = [
498
+ addNullableValidation,
476
499
  addRequiredValidation,
477
500
  addMinLengthValidation,
478
501
  addMaxLengthValidation,
479
502
  addMinValidation,
480
503
  addMaxValidation,
481
504
  addRegexValidation
482
- ].map((fn) => fn(attribute));
505
+ ].map((fn) => fn(attribute, options));
483
506
  const transformSchema = pipe(...validations);
484
507
  switch (attribute.type) {
485
508
  case "component": {
@@ -489,12 +512,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
489
512
  ...acc,
490
513
  [name]: transformSchema(
491
514
  yup.array().of(createModelSchema(attributes3).nullable(false))
492
- )
515
+ ).test(arrayValidator(attribute, options))
493
516
  };
494
517
  } else {
495
518
  return {
496
519
  ...acc,
497
- [name]: transformSchema(createModelSchema(attributes3))
520
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
498
521
  };
499
522
  }
500
523
  }
@@ -516,7 +539,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
516
539
  }
517
540
  )
518
541
  )
519
- )
542
+ ).test(arrayValidator(attribute, options))
520
543
  };
521
544
  case "relation":
522
545
  return {
@@ -528,7 +551,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
528
551
  } else if (Array.isArray(value)) {
529
552
  return yup.array().of(
530
553
  yup.object().shape({
531
- id: yup.string().required()
554
+ id: yup.number().required()
532
555
  })
533
556
  );
534
557
  } else if (typeof value === "object") {
@@ -580,6 +603,14 @@ const createAttributeSchema = (attribute) => {
580
603
  if (!value || typeof value === "string" && value.length === 0) {
581
604
  return true;
582
605
  }
606
+ if (typeof value === "object") {
607
+ try {
608
+ JSON.stringify(value);
609
+ return true;
610
+ } catch (err) {
611
+ return false;
612
+ }
613
+ }
583
614
  try {
584
615
  JSON.parse(value);
585
616
  return true;
@@ -598,13 +629,7 @@ const createAttributeSchema = (attribute) => {
598
629
  return yup.mixed();
599
630
  }
600
631
  };
601
- const addRequiredValidation = (attribute) => (schema) => {
602
- if (attribute.required) {
603
- return schema.required({
604
- id: translatedErrors.required.id,
605
- defaultMessage: "This field is required."
606
- });
607
- }
632
+ const nullableSchema = (schema) => {
608
633
  return schema?.nullable ? schema.nullable() : (
609
634
  // In some cases '.nullable' will not be available on the schema.
610
635
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -612,7 +637,22 @@ const addRequiredValidation = (attribute) => (schema) => {
612
637
  schema
613
638
  );
614
639
  };
615
- const addMinLengthValidation = (attribute) => (schema) => {
640
+ const addNullableValidation = () => (schema) => {
641
+ return nullableSchema(schema);
642
+ };
643
+ const addRequiredValidation = (attribute, options) => (schema) => {
644
+ if (options.status === "draft" || !attribute.required) {
645
+ return schema;
646
+ }
647
+ if (attribute.required && "required" in schema) {
648
+ return schema.required(translatedErrors.required);
649
+ }
650
+ return schema;
651
+ };
652
+ const addMinLengthValidation = (attribute, options) => (schema) => {
653
+ if (options.status === "draft") {
654
+ return schema;
655
+ }
616
656
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
617
657
  return schema.min(attribute.minLength, {
618
658
  ...translatedErrors.minLength,
@@ -634,10 +674,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
634
674
  }
635
675
  return schema;
636
676
  };
637
- const addMinValidation = (attribute) => (schema) => {
638
- if ("min" in attribute) {
677
+ const addMinValidation = (attribute, options) => (schema) => {
678
+ if (options.status === "draft") {
679
+ return schema;
680
+ }
681
+ if ("min" in attribute && "min" in schema) {
639
682
  const min = toInteger(attribute.min);
640
- if ("min" in schema && min) {
683
+ if (min) {
641
684
  return schema.min(min, {
642
685
  ...translatedErrors.min,
643
686
  values: {
@@ -755,16 +798,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
755
798
  }, {});
756
799
  return componentsByKey;
757
800
  };
758
- const useDocument = (args, opts) => {
801
+ const HOOKS = {
802
+ /**
803
+ * Hook that allows to mutate the displayed headers of the list view table
804
+ * @constant
805
+ * @type {string}
806
+ */
807
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
808
+ /**
809
+ * Hook that allows to mutate the CM's collection types links pre-set filters
810
+ * @constant
811
+ * @type {string}
812
+ */
813
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
814
+ /**
815
+ * Hook that allows to mutate the CM's edit view layout
816
+ * @constant
817
+ * @type {string}
818
+ */
819
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
820
+ /**
821
+ * Hook that allows to mutate the CM's single types links pre-set filters
822
+ * @constant
823
+ * @type {string}
824
+ */
825
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
826
+ };
827
+ const contentTypesApi = contentManagerApi.injectEndpoints({
828
+ endpoints: (builder) => ({
829
+ getContentTypeConfiguration: builder.query({
830
+ query: (uid) => ({
831
+ url: `/content-manager/content-types/${uid}/configuration`,
832
+ method: "GET"
833
+ }),
834
+ transformResponse: (response) => response.data,
835
+ providesTags: (_result, _error, uid) => [
836
+ { type: "ContentTypesConfiguration", id: uid },
837
+ { type: "ContentTypeSettings", id: "LIST" }
838
+ ]
839
+ }),
840
+ getAllContentTypeSettings: builder.query({
841
+ query: () => "/content-manager/content-types-settings",
842
+ transformResponse: (response) => response.data,
843
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
844
+ }),
845
+ updateContentTypeConfiguration: builder.mutation({
846
+ query: ({ uid, ...body }) => ({
847
+ url: `/content-manager/content-types/${uid}/configuration`,
848
+ method: "PUT",
849
+ data: body
850
+ }),
851
+ transformResponse: (response) => response.data,
852
+ invalidatesTags: (_result, _error, { uid }) => [
853
+ { type: "ContentTypesConfiguration", id: uid },
854
+ { type: "ContentTypeSettings", id: "LIST" },
855
+ // Is this necessary?
856
+ { type: "InitialData" }
857
+ ]
858
+ })
859
+ })
860
+ });
861
+ const {
862
+ useGetContentTypeConfigurationQuery,
863
+ useGetAllContentTypeSettingsQuery,
864
+ useUpdateContentTypeConfigurationMutation
865
+ } = contentTypesApi;
866
+ const checkIfAttributeIsDisplayable = (attribute) => {
867
+ const { type } = attribute;
868
+ if (type === "relation") {
869
+ return !attribute.relation.toLowerCase().includes("morph");
870
+ }
871
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
872
+ };
873
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
874
+ if (!mainFieldName) {
875
+ return void 0;
876
+ }
877
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
878
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
879
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
880
+ );
881
+ return {
882
+ name: mainFieldName,
883
+ type: mainFieldType ?? "string"
884
+ };
885
+ };
886
+ const DEFAULT_SETTINGS = {
887
+ bulkable: false,
888
+ filterable: false,
889
+ searchable: false,
890
+ pagination: false,
891
+ defaultSortBy: "",
892
+ defaultSortOrder: "asc",
893
+ mainField: "id",
894
+ pageSize: 10
895
+ };
896
+ const useDocumentLayout = (model) => {
897
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
898
+ const [{ query }] = useQueryParams();
899
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
759
900
  const { toggleNotification } = useNotification();
760
901
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
902
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
761
903
  const {
762
- currentData: data,
763
- isLoading: isLoadingDocument,
764
- isFetching: isFetchingDocument,
765
- error
766
- } = useGetDocumentQuery(args, opts);
767
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
904
+ data,
905
+ isLoading: isLoadingConfigs,
906
+ error,
907
+ isFetching: isFetchingConfigs
908
+ } = useGetContentTypeConfigurationQuery(model);
909
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
768
910
  React.useEffect(() => {
769
911
  if (error) {
770
912
  toggleNotification({
@@ -772,68 +914,322 @@ const useDocument = (args, opts) => {
772
914
  message: formatAPIError(error)
773
915
  });
774
916
  }
775
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
776
- const validationSchema = React.useMemo(() => {
777
- if (!schema) {
778
- return null;
779
- }
780
- return createYupSchema(schema.attributes, components);
781
- }, [schema, components]);
782
- const validate = React.useCallback(
783
- (document) => {
784
- if (!validationSchema) {
785
- throw new Error(
786
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
787
- );
788
- }
789
- try {
790
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
791
- return null;
792
- } catch (error2) {
793
- if (error2 instanceof ValidationError) {
794
- return getYupValidationErrors(error2);
795
- }
796
- throw error2;
797
- }
917
+ }, [error, formatAPIError, toggleNotification]);
918
+ const editLayout = React.useMemo(
919
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
920
+ layout: [],
921
+ components: {},
922
+ metadatas: {},
923
+ options: {},
924
+ settings: DEFAULT_SETTINGS
798
925
  },
799
- [validationSchema]
926
+ [data, isLoading, schemas, schema, components]
927
+ );
928
+ const listLayout = React.useMemo(() => {
929
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
930
+ layout: [],
931
+ metadatas: {},
932
+ options: {},
933
+ settings: DEFAULT_SETTINGS
934
+ };
935
+ }, [data, isLoading, schemas, schema, components]);
936
+ const { layout: edit } = React.useMemo(
937
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
938
+ layout: editLayout,
939
+ query
940
+ }),
941
+ [editLayout, query, runHookWaterfall]
800
942
  );
801
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
802
943
  return {
803
- components,
804
- document: data?.data,
805
- meta: data?.meta,
944
+ error,
806
945
  isLoading,
807
- schema,
808
- validate
946
+ edit,
947
+ list: listLayout
809
948
  };
810
949
  };
811
- const useDoc = () => {
812
- const { id, slug, collectionType, origin } = useParams();
813
- const [{ query }] = useQueryParams();
814
- const params = React.useMemo(() => buildValidParams(query), [query]);
815
- if (!collectionType) {
816
- throw new Error("Could not find collectionType in url params");
817
- }
818
- if (!slug) {
819
- throw new Error("Could not find model in url params");
820
- }
950
+ const useDocLayout = () => {
951
+ const { model } = useDoc();
952
+ return useDocumentLayout(model);
953
+ };
954
+ const formatEditLayout = (data, {
955
+ schemas,
956
+ schema,
957
+ components
958
+ }) => {
959
+ let currentPanelIndex = 0;
960
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
961
+ data.contentType.layouts.edit,
962
+ schema?.attributes,
963
+ data.contentType.metadatas,
964
+ { configurations: data.components, schemas: components },
965
+ schemas
966
+ ).reduce((panels, row) => {
967
+ if (row.some((field) => field.type === "dynamiczone")) {
968
+ panels.push([row]);
969
+ currentPanelIndex += 2;
970
+ } else {
971
+ if (!panels[currentPanelIndex]) {
972
+ panels.push([row]);
973
+ } else {
974
+ panels[currentPanelIndex].push(row);
975
+ }
976
+ }
977
+ return panels;
978
+ }, []);
979
+ const componentEditAttributes = Object.entries(data.components).reduce(
980
+ (acc, [uid, configuration]) => {
981
+ acc[uid] = {
982
+ layout: convertEditLayoutToFieldLayouts(
983
+ configuration.layouts.edit,
984
+ components[uid].attributes,
985
+ configuration.metadatas,
986
+ { configurations: data.components, schemas: components }
987
+ ),
988
+ settings: {
989
+ ...configuration.settings,
990
+ icon: components[uid].info.icon,
991
+ displayName: components[uid].info.displayName
992
+ }
993
+ };
994
+ return acc;
995
+ },
996
+ {}
997
+ );
998
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
999
+ (acc, [attribute, metadata]) => {
1000
+ return {
1001
+ ...acc,
1002
+ [attribute]: metadata.edit
1003
+ };
1004
+ },
1005
+ {}
1006
+ );
1007
+ return {
1008
+ layout: panelledEditAttributes,
1009
+ components: componentEditAttributes,
1010
+ metadatas: editMetadatas,
1011
+ settings: {
1012
+ ...data.contentType.settings,
1013
+ displayName: schema?.info.displayName
1014
+ },
1015
+ options: {
1016
+ ...schema?.options,
1017
+ ...schema?.pluginOptions,
1018
+ ...data.contentType.options
1019
+ }
1020
+ };
1021
+ };
1022
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1023
+ return rows.map(
1024
+ (row) => row.map((field) => {
1025
+ const attribute = attributes[field.name];
1026
+ if (!attribute) {
1027
+ return null;
1028
+ }
1029
+ const { edit: metadata } = metadatas[field.name];
1030
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1031
+ return {
1032
+ attribute,
1033
+ disabled: !metadata.editable,
1034
+ hint: metadata.description,
1035
+ label: metadata.label ?? "",
1036
+ name: field.name,
1037
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1038
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1039
+ schemas,
1040
+ components: components?.schemas ?? {}
1041
+ }),
1042
+ placeholder: metadata.placeholder ?? "",
1043
+ required: attribute.required ?? false,
1044
+ size: field.size,
1045
+ unique: "unique" in attribute ? attribute.unique : false,
1046
+ visible: metadata.visible ?? true,
1047
+ type: attribute.type
1048
+ };
1049
+ }).filter((field) => field !== null)
1050
+ );
1051
+ };
1052
+ const formatListLayout = (data, {
1053
+ schemas,
1054
+ schema,
1055
+ components
1056
+ }) => {
1057
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1058
+ (acc, [attribute, metadata]) => {
1059
+ return {
1060
+ ...acc,
1061
+ [attribute]: metadata.list
1062
+ };
1063
+ },
1064
+ {}
1065
+ );
1066
+ const listAttributes = convertListLayoutToFieldLayouts(
1067
+ data.contentType.layouts.list,
1068
+ schema?.attributes,
1069
+ listMetadatas,
1070
+ { configurations: data.components, schemas: components },
1071
+ schemas
1072
+ );
1073
+ return {
1074
+ layout: listAttributes,
1075
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1076
+ metadatas: listMetadatas,
1077
+ options: {
1078
+ ...schema?.options,
1079
+ ...schema?.pluginOptions,
1080
+ ...data.contentType.options
1081
+ }
1082
+ };
1083
+ };
1084
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1085
+ return columns.map((name) => {
1086
+ const attribute = attributes[name];
1087
+ if (!attribute) {
1088
+ return null;
1089
+ }
1090
+ const metadata = metadatas[name];
1091
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1092
+ return {
1093
+ attribute,
1094
+ label: metadata.label ?? "",
1095
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1096
+ schemas,
1097
+ components: components?.schemas ?? {}
1098
+ }),
1099
+ name,
1100
+ searchable: metadata.searchable ?? true,
1101
+ sortable: metadata.sortable ?? true
1102
+ };
1103
+ }).filter((field) => field !== null);
1104
+ };
1105
+ const useDocument = (args, opts) => {
1106
+ const { toggleNotification } = useNotification();
1107
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1108
+ const {
1109
+ currentData: data,
1110
+ isLoading: isLoadingDocument,
1111
+ isFetching: isFetchingDocument,
1112
+ error
1113
+ } = useGetDocumentQuery(args, {
1114
+ ...opts,
1115
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1116
+ });
1117
+ const {
1118
+ components,
1119
+ schema,
1120
+ schemas,
1121
+ isLoading: isLoadingSchema
1122
+ } = useContentTypeSchema(args.model);
1123
+ React.useEffect(() => {
1124
+ if (error) {
1125
+ toggleNotification({
1126
+ type: "danger",
1127
+ message: formatAPIError(error)
1128
+ });
1129
+ }
1130
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1131
+ const validationSchema = React.useMemo(() => {
1132
+ if (!schema) {
1133
+ return null;
1134
+ }
1135
+ return createYupSchema(schema.attributes, components);
1136
+ }, [schema, components]);
1137
+ const validate = React.useCallback(
1138
+ (document) => {
1139
+ if (!validationSchema) {
1140
+ throw new Error(
1141
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1142
+ );
1143
+ }
1144
+ try {
1145
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1146
+ return null;
1147
+ } catch (error2) {
1148
+ if (error2 instanceof ValidationError) {
1149
+ return getYupValidationErrors(error2);
1150
+ }
1151
+ throw error2;
1152
+ }
1153
+ },
1154
+ [validationSchema]
1155
+ );
1156
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1157
+ const hasError = !!error;
1158
+ return {
1159
+ components,
1160
+ document: data?.data,
1161
+ meta: data?.meta,
1162
+ isLoading,
1163
+ hasError,
1164
+ schema,
1165
+ schemas,
1166
+ validate
1167
+ };
1168
+ };
1169
+ const useDoc = () => {
1170
+ const { id, slug, collectionType, origin } = useParams();
1171
+ const [{ query }] = useQueryParams();
1172
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1173
+ if (!collectionType) {
1174
+ throw new Error("Could not find collectionType in url params");
1175
+ }
1176
+ if (!slug) {
1177
+ throw new Error("Could not find model in url params");
1178
+ }
1179
+ const document = useDocument(
1180
+ { documentId: origin || id, model: slug, collectionType, params },
1181
+ {
1182
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1183
+ }
1184
+ );
1185
+ const returnId = origin || id === "create" ? void 0 : id;
821
1186
  return {
822
1187
  collectionType,
823
1188
  model: slug,
824
- id: origin || id === "create" ? void 0 : id,
825
- ...useDocument(
826
- { documentId: origin || id, model: slug, collectionType, params },
827
- {
828
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
829
- }
830
- )
1189
+ id: returnId,
1190
+ ...document
1191
+ };
1192
+ };
1193
+ const useContentManagerContext = () => {
1194
+ const {
1195
+ collectionType,
1196
+ model,
1197
+ id,
1198
+ components,
1199
+ isLoading: isLoadingDoc,
1200
+ schema,
1201
+ schemas
1202
+ } = useDoc();
1203
+ const layout = useDocumentLayout(model);
1204
+ const form = useForm("useContentManagerContext", (state) => state);
1205
+ const isSingleType = collectionType === SINGLE_TYPES;
1206
+ const slug = model;
1207
+ const isCreatingEntry = id === "create";
1208
+ useContentTypeSchema();
1209
+ const isLoading = isLoadingDoc || layout.isLoading;
1210
+ const error = layout.error;
1211
+ return {
1212
+ error,
1213
+ isLoading,
1214
+ // Base metadata
1215
+ model,
1216
+ collectionType,
1217
+ id,
1218
+ slug,
1219
+ isCreatingEntry,
1220
+ isSingleType,
1221
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1222
+ // All schema infos
1223
+ components,
1224
+ contentType: schema,
1225
+ contentTypes: schemas,
1226
+ // Form state
1227
+ form,
1228
+ // layout infos
1229
+ layout
831
1230
  };
832
1231
  };
833
1232
  const prefixPluginTranslations = (trad, pluginId) => {
834
- if (!pluginId) {
835
- throw new TypeError("pluginId can't be empty");
836
- }
837
1233
  return Object.keys(trad).reduce((acc, current) => {
838
1234
  acc[`${pluginId}.${current}`] = trad[current];
839
1235
  return acc;
@@ -849,6 +1245,8 @@ const useDocumentActions = () => {
849
1245
  const { formatMessage } = useIntl();
850
1246
  const { trackUsage } = useTracking();
851
1247
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1248
+ const navigate = useNavigate();
1249
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
852
1250
  const [deleteDocument] = useDeleteDocumentMutation();
853
1251
  const _delete = React.useCallback(
854
1252
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1163,6 +1561,7 @@ const useDocumentActions = () => {
1163
1561
  defaultMessage: "Saved document"
1164
1562
  })
1165
1563
  });
1564
+ setCurrentStep("contentManager.success");
1166
1565
  return res.data;
1167
1566
  } catch (err) {
1168
1567
  toggleNotification({
@@ -1184,7 +1583,6 @@ const useDocumentActions = () => {
1184
1583
  sourceId
1185
1584
  });
1186
1585
  if ("error" in res) {
1187
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1188
1586
  return { error: res.error };
1189
1587
  }
1190
1588
  toggleNotification({
@@ -1203,7 +1601,7 @@ const useDocumentActions = () => {
1203
1601
  throw err;
1204
1602
  }
1205
1603
  },
1206
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1604
+ [autoCloneDocument, formatMessage, toggleNotification]
1207
1605
  );
1208
1606
  const [cloneDocument] = useCloneDocumentMutation();
1209
1607
  const clone = React.useCallback(
@@ -1229,6 +1627,7 @@ const useDocumentActions = () => {
1229
1627
  defaultMessage: "Cloned document"
1230
1628
  })
1231
1629
  });
1630
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1232
1631
  return res.data;
1233
1632
  } catch (err) {
1234
1633
  toggleNotification({
@@ -1239,7 +1638,7 @@ const useDocumentActions = () => {
1239
1638
  throw err;
1240
1639
  }
1241
1640
  },
1242
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1641
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1243
1642
  );
1244
1643
  const [getDoc] = useLazyGetDocumentQuery();
1245
1644
  const getDocument = React.useCallback(
@@ -1264,10 +1663,10 @@ const useDocumentActions = () => {
1264
1663
  update
1265
1664
  };
1266
1665
  };
1267
- const ProtectedHistoryPage = lazy(
1268
- () => import("./History-Bu53Yfw-.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1666
+ const ProtectedHistoryPage = React.lazy(
1667
+ () => import("./History-CGblSVMc.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1269
1668
  );
1270
- const routes$1 = [
1669
+ const routes$2 = [
1271
1670
  {
1272
1671
  path: ":collectionType/:slug/:id/history",
1273
1672
  Component: ProtectedHistoryPage
@@ -1277,32 +1676,45 @@ const routes$1 = [
1277
1676
  Component: ProtectedHistoryPage
1278
1677
  }
1279
1678
  ];
1679
+ const ProtectedPreviewPage = React.lazy(
1680
+ () => import("./Preview-Cy6fuAnd.mjs").then((mod) => ({ default: mod.ProtectedPreviewPage }))
1681
+ );
1682
+ const routes$1 = [
1683
+ {
1684
+ path: ":collectionType/:slug/:id/preview",
1685
+ Component: ProtectedPreviewPage
1686
+ },
1687
+ {
1688
+ path: ":collectionType/:slug/preview",
1689
+ Component: ProtectedPreviewPage
1690
+ }
1691
+ ];
1280
1692
  const ProtectedEditViewPage = lazy(
1281
- () => import("./EditViewPage-CzuJgWWp.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1693
+ () => import("./EditViewPage-PrPHZN_9.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1282
1694
  );
1283
1695
  const ProtectedListViewPage = lazy(
1284
- () => import("./ListViewPage-BO_mOXIl.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1696
+ () => import("./ListViewPage-C975eW-t.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1285
1697
  );
1286
1698
  const ProtectedListConfiguration = lazy(
1287
- () => import("./ListConfigurationPage-TqrmwjPN.mjs").then((mod) => ({
1699
+ () => import("./ListConfigurationPage-TIM0JveM.mjs").then((mod) => ({
1288
1700
  default: mod.ProtectedListConfiguration
1289
1701
  }))
1290
1702
  );
1291
1703
  const ProtectedEditConfigurationPage = lazy(
1292
- () => import("./EditConfigurationPage-CZLbgfIp.mjs").then((mod) => ({
1704
+ () => import("./EditConfigurationPage-CyqSP6ru.mjs").then((mod) => ({
1293
1705
  default: mod.ProtectedEditConfigurationPage
1294
1706
  }))
1295
1707
  );
1296
1708
  const ProtectedComponentConfigurationPage = lazy(
1297
- () => import("./ComponentConfigurationPage-Cxz51Sve.mjs").then((mod) => ({
1709
+ () => import("./ComponentConfigurationPage-DKuCF_uX.mjs").then((mod) => ({
1298
1710
  default: mod.ProtectedComponentConfigurationPage
1299
1711
  }))
1300
1712
  );
1301
1713
  const NoPermissions = lazy(
1302
- () => import("./NoPermissionsPage-DTXi042N.mjs").then((mod) => ({ default: mod.NoPermissions }))
1714
+ () => import("./NoPermissionsPage-D4XYRoPf.mjs").then((mod) => ({ default: mod.NoPermissions }))
1303
1715
  );
1304
1716
  const NoContentType = lazy(
1305
- () => import("./NoContentTypePage-DqB0QV0k.mjs").then((mod) => ({ default: mod.NoContentType }))
1717
+ () => import("./NoContentTypePage-SgNTVGjF.mjs").then((mod) => ({ default: mod.NoContentType }))
1306
1718
  );
1307
1719
  const CollectionTypePages = () => {
1308
1720
  const { collectionType } = useParams();
@@ -1314,7 +1726,7 @@ const CollectionTypePages = () => {
1314
1726
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1315
1727
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1316
1728
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1317
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1729
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1318
1730
  const routes = [
1319
1731
  {
1320
1732
  path: LIST_RELATIVE_PATH,
@@ -1348,6 +1760,7 @@ const routes = [
1348
1760
  path: "no-content-types",
1349
1761
  Component: NoContentType
1350
1762
  },
1763
+ ...routes$2,
1351
1764
  ...routes$1
1352
1765
  ];
1353
1766
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1416,12 +1829,14 @@ const DocumentActionButton = (action) => {
1416
1829
  /* @__PURE__ */ jsx(
1417
1830
  Button,
1418
1831
  {
1419
- flex: 1,
1832
+ flex: "auto",
1420
1833
  startIcon: action.icon,
1421
1834
  disabled: action.disabled,
1422
1835
  onClick: handleClick(action),
1423
1836
  justifyContent: "center",
1424
1837
  variant: action.variant || "default",
1838
+ paddingTop: "7px",
1839
+ paddingBottom: "7px",
1425
1840
  children: action.label
1426
1841
  }
1427
1842
  ),
@@ -1429,7 +1844,7 @@ const DocumentActionButton = (action) => {
1429
1844
  DocumentActionConfirmDialog,
1430
1845
  {
1431
1846
  ...action.dialog,
1432
- variant: action.variant,
1847
+ variant: action.dialog?.variant ?? action.variant,
1433
1848
  isOpen: dialogId === action.id,
1434
1849
  onClose: handleClose
1435
1850
  }
@@ -1486,9 +1901,9 @@ const DocumentActionsMenu = ({
1486
1901
  disabled: isDisabled,
1487
1902
  size: "S",
1488
1903
  endIcon: null,
1489
- paddingTop: "7px",
1490
- paddingLeft: "9px",
1491
- paddingRight: "9px",
1904
+ paddingTop: "4px",
1905
+ paddingLeft: "7px",
1906
+ paddingRight: "7px",
1492
1907
  variant,
1493
1908
  children: [
1494
1909
  /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
@@ -1499,7 +1914,7 @@ const DocumentActionsMenu = ({
1499
1914
  ]
1500
1915
  }
1501
1916
  ),
1502
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1917
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1503
1918
  actions2.map((action) => {
1504
1919
  return /* @__PURE__ */ jsx(
1505
1920
  Menu.Item,
@@ -1507,28 +1922,25 @@ const DocumentActionsMenu = ({
1507
1922
  disabled: action.disabled,
1508
1923
  onSelect: handleClick(action),
1509
1924
  display: "block",
1510
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1511
- /* @__PURE__ */ jsxs(Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1512
- action.icon,
1513
- action.label
1514
- ] }),
1515
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1516
- Flex,
1517
- {
1518
- alignItems: "center",
1519
- background: "alternative100",
1520
- borderStyle: "solid",
1521
- borderColor: "alternative200",
1522
- borderWidth: "1px",
1523
- height: 5,
1524
- paddingLeft: 2,
1525
- paddingRight: 2,
1526
- hasRadius: true,
1527
- color: "alternative600",
1528
- children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1529
- }
1530
- )
1531
- ] })
1925
+ children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxs(
1926
+ Flex,
1927
+ {
1928
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1929
+ gap: 2,
1930
+ tag: "span",
1931
+ children: [
1932
+ /* @__PURE__ */ jsx(
1933
+ Flex,
1934
+ {
1935
+ tag: "span",
1936
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1937
+ children: action.icon
1938
+ }
1939
+ ),
1940
+ action.label
1941
+ ]
1942
+ }
1943
+ ) })
1532
1944
  },
1533
1945
  action.id
1534
1946
  );
@@ -1570,8 +1982,20 @@ const convertActionVariantToColor = (variant = "secondary") => {
1570
1982
  return "primary600";
1571
1983
  }
1572
1984
  };
1573
- const DocumentActionConfirmDialog = ({
1574
- onClose,
1985
+ const convertActionVariantToIconColor = (variant = "secondary") => {
1986
+ switch (variant) {
1987
+ case "danger":
1988
+ return "danger600";
1989
+ case "secondary":
1990
+ return "neutral500";
1991
+ case "success":
1992
+ return "success600";
1993
+ default:
1994
+ return "primary600";
1995
+ }
1996
+ };
1997
+ const DocumentActionConfirmDialog = ({
1998
+ onClose,
1575
1999
  onCancel,
1576
2000
  onConfirm,
1577
2001
  title,
@@ -1592,22 +2016,20 @@ const DocumentActionConfirmDialog = ({
1592
2016
  }
1593
2017
  onClose();
1594
2018
  };
1595
- return /* @__PURE__ */ jsxs(Dialog, { isOpen, title, onClose: handleClose, children: [
1596
- /* @__PURE__ */ jsx(DialogBody, { children: content }),
1597
- /* @__PURE__ */ jsx(
1598
- DialogFooter,
1599
- {
1600
- startAction: /* @__PURE__ */ jsx(Button, { onClick: handleClose, variant: "tertiary", children: formatMessage({
1601
- id: "app.components.Button.cancel",
1602
- defaultMessage: "Cancel"
1603
- }) }),
1604
- endAction: /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
1605
- id: "app.components.Button.confirm",
1606
- defaultMessage: "Confirm"
1607
- }) })
1608
- }
1609
- )
1610
- ] });
2019
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2020
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2021
+ /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
2022
+ /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
2023
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
2024
+ id: "app.components.Button.cancel",
2025
+ defaultMessage: "Cancel"
2026
+ }) }) }),
2027
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
2028
+ id: "app.components.Button.confirm",
2029
+ defaultMessage: "Confirm"
2030
+ }) })
2031
+ ] })
2032
+ ] }) });
1611
2033
  };
1612
2034
  const DocumentActionModal = ({
1613
2035
  isOpen,
@@ -1617,34 +2039,29 @@ const DocumentActionModal = ({
1617
2039
  content: Content,
1618
2040
  onModalClose
1619
2041
  }) => {
1620
- const id = React.useId();
1621
- if (!isOpen) {
1622
- return null;
1623
- }
1624
2042
  const handleClose = () => {
1625
2043
  if (onClose) {
1626
2044
  onClose();
1627
2045
  }
1628
2046
  onModalClose();
1629
2047
  };
1630
- return /* @__PURE__ */ jsxs(ModalLayout, { borderRadius: "4px", overflow: "hidden", onClose: handleClose, labelledBy: id, children: [
1631
- /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", textColor: "neutral800", tag: "h2", id, children: title }) }),
1632
- /* @__PURE__ */ jsx(ModalBody, { children: typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content }),
1633
- /* @__PURE__ */ jsx(
1634
- Box,
1635
- {
1636
- paddingTop: 4,
1637
- paddingBottom: 4,
1638
- paddingLeft: 5,
1639
- paddingRight: 5,
1640
- borderWidth: "1px 0 0 0",
1641
- borderStyle: "solid",
1642
- borderColor: "neutral150",
1643
- background: "neutral100",
1644
- children: typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1645
- }
1646
- )
1647
- ] });
2048
+ return /* @__PURE__ */ jsx(Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
2049
+ /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: title }) }),
2050
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsx(Modal.Body, { children: Content }),
2051
+ typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
2052
+ ] }) });
2053
+ };
2054
+ const transformData = (data) => {
2055
+ if (Array.isArray(data)) {
2056
+ return data.map(transformData);
2057
+ }
2058
+ if (typeof data === "object" && data !== null) {
2059
+ if ("apiData" in data) {
2060
+ return data.apiData;
2061
+ }
2062
+ return mapValues(transformData)(data);
2063
+ }
2064
+ return data;
1648
2065
  };
1649
2066
  const PublishAction$1 = ({
1650
2067
  activeTab,
@@ -1658,13 +2075,17 @@ const PublishAction$1 = ({
1658
2075
  const navigate = useNavigate();
1659
2076
  const { toggleNotification } = useNotification();
1660
2077
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2078
+ const isListView = useMatch(LIST_PATH) !== null;
1661
2079
  const isCloning = useMatch(CLONE_PATH) !== null;
1662
2080
  const { formatMessage } = useIntl();
1663
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1664
- "PublishAction",
1665
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1666
- );
2081
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1667
2082
  const { publish } = useDocumentActions();
2083
+ const [
2084
+ countDraftRelations,
2085
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2086
+ ] = useLazyGetDraftRelationCountQuery();
2087
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React.useState(0);
2088
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React.useState(0);
1668
2089
  const [{ query, rawQuery }] = useQueryParams();
1669
2090
  const params = React.useMemo(() => buildValidParams(query), [query]);
1670
2091
  const modified = useForm("PublishAction", ({ modified: modified2 }) => modified2);
@@ -1673,10 +2094,105 @@ const PublishAction$1 = ({
1673
2094
  const validate = useForm("PublishAction", (state) => state.validate);
1674
2095
  const setErrors = useForm("PublishAction", (state) => state.setErrors);
1675
2096
  const formValues = useForm("PublishAction", ({ values }) => values);
2097
+ React.useEffect(() => {
2098
+ if (isErrorDraftRelations) {
2099
+ toggleNotification({
2100
+ type: "danger",
2101
+ message: formatMessage({
2102
+ id: getTranslation("error.records.fetch-draft-relatons"),
2103
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2104
+ })
2105
+ });
2106
+ }
2107
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2108
+ React.useEffect(() => {
2109
+ const localDraftRelations = /* @__PURE__ */ new Set();
2110
+ const extractDraftRelations = (data) => {
2111
+ const relations = data.connect || [];
2112
+ relations.forEach((relation) => {
2113
+ if (relation.status === "draft") {
2114
+ localDraftRelations.add(relation.id);
2115
+ }
2116
+ });
2117
+ };
2118
+ const traverseAndExtract = (data) => {
2119
+ Object.entries(data).forEach(([key, value]) => {
2120
+ if (key === "connect" && Array.isArray(value)) {
2121
+ extractDraftRelations({ connect: value });
2122
+ } else if (typeof value === "object" && value !== null) {
2123
+ traverseAndExtract(value);
2124
+ }
2125
+ });
2126
+ };
2127
+ if (!documentId || modified) {
2128
+ traverseAndExtract(formValues);
2129
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2130
+ }
2131
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2132
+ React.useEffect(() => {
2133
+ if (!document || !document.documentId || isListView) {
2134
+ return;
2135
+ }
2136
+ const fetchDraftRelationsCount = async () => {
2137
+ const { data, error } = await countDraftRelations({
2138
+ collectionType,
2139
+ model,
2140
+ documentId,
2141
+ params
2142
+ });
2143
+ if (error) {
2144
+ throw error;
2145
+ }
2146
+ if (data) {
2147
+ setServerCountOfDraftRelations(data.data);
2148
+ }
2149
+ };
2150
+ fetchDraftRelationsCount();
2151
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1676
2152
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1677
2153
  if (!schema?.options?.draftAndPublish) {
1678
2154
  return null;
1679
2155
  }
2156
+ const performPublish = async () => {
2157
+ setSubmitting(true);
2158
+ try {
2159
+ const { errors } = await validate(true, {
2160
+ status: "published"
2161
+ });
2162
+ if (errors) {
2163
+ toggleNotification({
2164
+ type: "danger",
2165
+ message: formatMessage({
2166
+ id: "content-manager.validation.error",
2167
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2168
+ })
2169
+ });
2170
+ return;
2171
+ }
2172
+ const res = await publish(
2173
+ {
2174
+ collectionType,
2175
+ model,
2176
+ documentId,
2177
+ params
2178
+ },
2179
+ transformData(formValues)
2180
+ );
2181
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2182
+ navigate({
2183
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2184
+ search: rawQuery
2185
+ });
2186
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2187
+ setErrors(formatValidationErrors(res.error));
2188
+ }
2189
+ } finally {
2190
+ setSubmitting(false);
2191
+ }
2192
+ };
2193
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2194
+ const enableDraftRelationsCount = false;
2195
+ const hasDraftRelations = enableDraftRelationsCount;
1680
2196
  return {
1681
2197
  /**
1682
2198
  * Disabled when:
@@ -1686,49 +2202,36 @@ const PublishAction$1 = ({
1686
2202
  * - the document is already published & not modified
1687
2203
  * - the document is being created & not modified
1688
2204
  * - the user doesn't have the permission to publish
1689
- * - the user doesn't have the permission to create a new document
1690
- * - the user doesn't have the permission to update the document
1691
2205
  */
1692
- disabled: isCloning || isSubmitting || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2206
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1693
2207
  label: formatMessage({
1694
2208
  id: "app.utils.publish",
1695
2209
  defaultMessage: "Publish"
1696
2210
  }),
1697
2211
  onClick: async () => {
1698
- setSubmitting(true);
1699
- try {
1700
- const { errors } = await validate();
1701
- if (errors) {
1702
- toggleNotification({
1703
- type: "danger",
1704
- message: formatMessage({
1705
- id: "content-manager.validation.error",
1706
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1707
- })
1708
- });
1709
- return;
1710
- }
1711
- const res = await publish(
1712
- {
1713
- collectionType,
1714
- model,
1715
- documentId,
1716
- params
1717
- },
1718
- formValues
1719
- );
1720
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1721
- navigate({
1722
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1723
- search: rawQuery
1724
- });
1725
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1726
- setErrors(formatValidationErrors(res.error));
2212
+ await performPublish();
2213
+ },
2214
+ dialog: hasDraftRelations ? {
2215
+ type: "dialog",
2216
+ variant: "danger",
2217
+ footer: null,
2218
+ title: formatMessage({
2219
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2220
+ defaultMessage: "Confirmation"
2221
+ }),
2222
+ content: formatMessage(
2223
+ {
2224
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2225
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2226
+ },
2227
+ {
2228
+ count: totalDraftRelations
1727
2229
  }
1728
- } finally {
1729
- setSubmitting(false);
2230
+ ),
2231
+ onConfirm: async () => {
2232
+ await performPublish();
1730
2233
  }
1731
- }
2234
+ } : void 0
1732
2235
  };
1733
2236
  };
1734
2237
  PublishAction$1.type = "publish";
@@ -1744,10 +2247,6 @@ const UpdateAction = ({
1744
2247
  const cloneMatch = useMatch(CLONE_PATH);
1745
2248
  const isCloning = cloneMatch !== null;
1746
2249
  const { formatMessage } = useIntl();
1747
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1748
- canCreate: canCreate2,
1749
- canUpdate: canUpdate2
1750
- }));
1751
2250
  const { create, update, clone } = useDocumentActions();
1752
2251
  const [{ query, rawQuery }] = useQueryParams();
1753
2252
  const params = React.useMemo(() => buildValidParams(query), [query]);
@@ -1764,10 +2263,8 @@ const UpdateAction = ({
1764
2263
  * - the form is submitting
1765
2264
  * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1766
2265
  * - the active tab is the published tab
1767
- * - the user doesn't have the permission to create a new document
1768
- * - the user doesn't have the permission to update the document
1769
2266
  */
1770
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
2267
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1771
2268
  label: formatMessage({
1772
2269
  id: "content-manager.containers.Edit.save",
1773
2270
  defaultMessage: "Save"
@@ -1775,7 +2272,9 @@ const UpdateAction = ({
1775
2272
  onClick: async () => {
1776
2273
  setSubmitting(true);
1777
2274
  try {
1778
- const { errors } = await validate();
2275
+ const { errors } = await validate(true, {
2276
+ status: "draft"
2277
+ });
1779
2278
  if (errors) {
1780
2279
  toggleNotification({
1781
2280
  type: "danger",
@@ -1793,13 +2292,16 @@ const UpdateAction = ({
1793
2292
  documentId: cloneMatch.params.origin,
1794
2293
  params
1795
2294
  },
1796
- document
2295
+ transformData(document)
1797
2296
  );
1798
2297
  if ("data" in res) {
1799
- navigate({
1800
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1801
- search: rawQuery
1802
- });
2298
+ navigate(
2299
+ {
2300
+ pathname: `../${res.data.documentId}`,
2301
+ search: rawQuery
2302
+ },
2303
+ { relative: "path" }
2304
+ );
1803
2305
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1804
2306
  setErrors(formatValidationErrors(res.error));
1805
2307
  }
@@ -1811,7 +2313,7 @@ const UpdateAction = ({
1811
2313
  documentId,
1812
2314
  params
1813
2315
  },
1814
- document
2316
+ transformData(document)
1815
2317
  );
1816
2318
  if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1817
2319
  setErrors(formatValidationErrors(res.error));
@@ -1824,13 +2326,16 @@ const UpdateAction = ({
1824
2326
  model,
1825
2327
  params
1826
2328
  },
1827
- document
2329
+ transformData(document)
1828
2330
  );
1829
2331
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1830
- navigate({
1831
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1832
- search: rawQuery
1833
- });
2332
+ navigate(
2333
+ {
2334
+ pathname: `../${res.data.documentId}`,
2335
+ search: rawQuery
2336
+ },
2337
+ { replace: true, relative: "path" }
2338
+ );
1834
2339
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1835
2340
  setErrors(formatValidationErrors(res.error));
1836
2341
  }
@@ -1862,10 +2367,8 @@ const UnpublishAction$1 = ({
1862
2367
  const { toggleNotification } = useNotification();
1863
2368
  const [shouldKeepDraft, setShouldKeepDraft] = React.useState(true);
1864
2369
  const isDocumentModified = document?.status === "modified";
1865
- const handleChange = (e) => {
1866
- if ("value" in e.target) {
1867
- setShouldKeepDraft(e.target.value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
1868
- }
2370
+ const handleChange = (value) => {
2371
+ setShouldKeepDraft(value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
1869
2372
  };
1870
2373
  if (!schema?.options?.draftAndPublish) {
1871
2374
  return null;
@@ -1876,7 +2379,7 @@ const UnpublishAction$1 = ({
1876
2379
  id: "app.utils.unpublish",
1877
2380
  defaultMessage: "Unpublish"
1878
2381
  }),
1879
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2382
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1880
2383
  onClick: async () => {
1881
2384
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1882
2385
  if (!documentId) {
@@ -1915,40 +2418,24 @@ const UnpublishAction$1 = ({
1915
2418
  }) })
1916
2419
  ] }),
1917
2420
  /* @__PURE__ */ jsxs(
1918
- Flex,
2421
+ Radio.Group,
1919
2422
  {
1920
- onChange: handleChange,
1921
- direction: "column",
1922
- alignItems: "flex-start",
1923
- tag: "fieldset",
1924
- borderWidth: 0,
1925
- gap: 3,
2423
+ defaultValue: UNPUBLISH_DRAFT_OPTIONS.KEEP,
2424
+ name: "discard-options",
2425
+ "aria-label": formatMessage({
2426
+ id: "content-manager.actions.unpublish.dialog.radio-label",
2427
+ defaultMessage: "Choose an option to unpublish the document."
2428
+ }),
2429
+ onValueChange: handleChange,
1926
2430
  children: [
1927
- /* @__PURE__ */ jsx(VisuallyHidden, { tag: "legend" }),
1928
- /* @__PURE__ */ jsx(
1929
- Radio,
1930
- {
1931
- checked: shouldKeepDraft,
1932
- value: UNPUBLISH_DRAFT_OPTIONS.KEEP,
1933
- name: "discard-options",
1934
- children: formatMessage({
1935
- id: "content-manager.actions.unpublish.dialog.option.keep-draft",
1936
- defaultMessage: "Keep draft"
1937
- })
1938
- }
1939
- ),
1940
- /* @__PURE__ */ jsx(
1941
- Radio,
1942
- {
1943
- checked: !shouldKeepDraft,
1944
- value: UNPUBLISH_DRAFT_OPTIONS.DISCARD,
1945
- name: "discard-options",
1946
- children: formatMessage({
1947
- id: "content-manager.actions.unpublish.dialog.option.replace-draft",
1948
- defaultMessage: "Replace draft"
1949
- })
1950
- }
1951
- )
2431
+ /* @__PURE__ */ jsx(Radio.Item, { checked: shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.KEEP, children: formatMessage({
2432
+ id: "content-manager.actions.unpublish.dialog.option.keep-draft",
2433
+ defaultMessage: "Keep draft"
2434
+ }) }),
2435
+ /* @__PURE__ */ jsx(Radio.Item, { checked: !shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.DISCARD, children: formatMessage({
2436
+ id: "content-manager.actions.unpublish.dialog.option.replace-draft",
2437
+ defaultMessage: "Replace draft"
2438
+ }) })
1952
2439
  ]
1953
2440
  }
1954
2441
  )
@@ -2004,7 +2491,7 @@ const DiscardAction = ({
2004
2491
  id: "content-manager.actions.discard.label",
2005
2492
  defaultMessage: "Discard changes"
2006
2493
  }),
2007
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2494
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2008
2495
  position: ["panel", "table-row"],
2009
2496
  variant: "danger",
2010
2497
  dialog: {
@@ -2032,11 +2519,6 @@ const DiscardAction = ({
2032
2519
  };
2033
2520
  };
2034
2521
  DiscardAction.type = "discard";
2035
- const StyledCrossCircle = styled(CrossCircle)`
2036
- path {
2037
- fill: currentColor;
2038
- }
2039
- `;
2040
2522
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2041
2523
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2042
2524
  const RelativeTime = React.forwardRef(
@@ -2049,7 +2531,7 @@ const RelativeTime = React.forwardRef(
2049
2531
  });
2050
2532
  const unit = intervals.find((intervalUnit) => {
2051
2533
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2052
- });
2534
+ }) ?? "seconds";
2053
2535
  const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2054
2536
  const customInterval = customIntervals.find(
2055
2537
  (custom) => interval[custom.unit] < custom.threshold
@@ -2083,9 +2565,13 @@ const getDisplayName = ({
2083
2565
  return email ?? "";
2084
2566
  };
2085
2567
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2086
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2087
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2088
- return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2568
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2569
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2570
+ const { formatMessage } = useIntl();
2571
+ return /* @__PURE__ */ jsx(Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2572
+ id: `content-manager.containers.List.${status}`,
2573
+ defaultMessage: capitalise(status)
2574
+ }) }) });
2089
2575
  };
2090
2576
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2091
2577
  const { formatMessage } = useIntl();
@@ -2094,23 +2580,13 @@ const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2094
2580
  id: "content-manager.containers.edit.title.new",
2095
2581
  defaultMessage: "Create an entry"
2096
2582
  }) : documentTitle;
2097
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 8, paddingBottom: 4, gap: 3, children: [
2583
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2098
2584
  /* @__PURE__ */ jsx(BackButton, {}),
2099
- /* @__PURE__ */ jsxs(
2100
- Flex,
2101
- {
2102
- width: "100%",
2103
- justifyContent: "space-between",
2104
- paddingTop: 1,
2105
- gap: "80px",
2106
- alignItems: "flex-start",
2107
- children: [
2108
- /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2109
- /* @__PURE__ */ jsx(HeaderToolbar, {})
2110
- ]
2111
- }
2112
- ),
2113
- status ? /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) : null
2585
+ /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2586
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2587
+ /* @__PURE__ */ jsx(HeaderToolbar, {})
2588
+ ] }),
2589
+ status ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2114
2590
  ] });
2115
2591
  };
2116
2592
  const HeaderToolbar = () => {
@@ -2193,12 +2669,12 @@ const Information = ({ activeTab }) => {
2193
2669
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2194
2670
  label: formatMessage({
2195
2671
  id: "content-manager.containers.edit.information.last-published.label",
2196
- defaultMessage: "Last published"
2672
+ defaultMessage: "Published"
2197
2673
  }),
2198
2674
  value: formatMessage(
2199
2675
  {
2200
2676
  id: "content-manager.containers.edit.information.last-published.value",
2201
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2677
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2202
2678
  },
2203
2679
  {
2204
2680
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2211,12 +2687,12 @@ const Information = ({ activeTab }) => {
2211
2687
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2212
2688
  label: formatMessage({
2213
2689
  id: "content-manager.containers.edit.information.last-draft.label",
2214
- defaultMessage: "Last draft"
2690
+ defaultMessage: "Updated"
2215
2691
  }),
2216
2692
  value: formatMessage(
2217
2693
  {
2218
2694
  id: "content-manager.containers.edit.information.last-draft.value",
2219
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2695
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2220
2696
  },
2221
2697
  {
2222
2698
  time: /* @__PURE__ */ jsx(
@@ -2234,12 +2710,12 @@ const Information = ({ activeTab }) => {
2234
2710
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2235
2711
  label: formatMessage({
2236
2712
  id: "content-manager.containers.edit.information.document.label",
2237
- defaultMessage: "Document"
2713
+ defaultMessage: "Created"
2238
2714
  }),
2239
2715
  value: formatMessage(
2240
2716
  {
2241
2717
  id: "content-manager.containers.edit.information.document.value",
2242
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2718
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2243
2719
  },
2244
2720
  {
2245
2721
  time: /* @__PURE__ */ jsx(
@@ -2277,25 +2753,77 @@ const Information = ({ activeTab }) => {
2277
2753
  );
2278
2754
  };
2279
2755
  const HeaderActions = ({ actions: actions2 }) => {
2280
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2281
- if ("options" in action) {
2756
+ const [dialogId, setDialogId] = React.useState(null);
2757
+ const handleClick = (action) => async (e) => {
2758
+ if (!("options" in action)) {
2759
+ const { onClick = () => false, dialog, id } = action;
2760
+ const muteDialog = await onClick(e);
2761
+ if (dialog && !muteDialog) {
2762
+ e.preventDefault();
2763
+ setDialogId(id);
2764
+ }
2765
+ }
2766
+ };
2767
+ const handleClose = () => {
2768
+ setDialogId(null);
2769
+ };
2770
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2771
+ if (action.options) {
2282
2772
  return /* @__PURE__ */ jsx(
2283
2773
  SingleSelect,
2284
2774
  {
2285
2775
  size: "S",
2286
- disabled: action.disabled,
2287
- "aria-label": action.label,
2288
2776
  onChange: action.onSelect,
2289
- value: action.value,
2777
+ "aria-label": action.label,
2778
+ ...action,
2290
2779
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2291
2780
  },
2292
2781
  action.id
2293
2782
  );
2294
2783
  } else {
2295
- return null;
2784
+ if (action.type === "icon") {
2785
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
2786
+ /* @__PURE__ */ jsx(
2787
+ IconButton,
2788
+ {
2789
+ disabled: action.disabled,
2790
+ label: action.label,
2791
+ size: "S",
2792
+ onClick: handleClick(action),
2793
+ children: action.icon
2794
+ }
2795
+ ),
2796
+ action.dialog ? /* @__PURE__ */ jsx(
2797
+ HeaderActionDialog,
2798
+ {
2799
+ ...action.dialog,
2800
+ isOpen: dialogId === action.id,
2801
+ onClose: handleClose
2802
+ }
2803
+ ) : null
2804
+ ] }, action.id);
2805
+ }
2296
2806
  }
2297
2807
  }) });
2298
2808
  };
2809
+ const HeaderActionDialog = ({
2810
+ onClose,
2811
+ onCancel,
2812
+ title,
2813
+ content: Content,
2814
+ isOpen
2815
+ }) => {
2816
+ const handleClose = async () => {
2817
+ if (onCancel) {
2818
+ await onCancel();
2819
+ }
2820
+ onClose();
2821
+ };
2822
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2823
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2824
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
2825
+ ] }) });
2826
+ };
2299
2827
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2300
2828
  const navigate = useNavigate();
2301
2829
  const { formatMessage } = useIntl();
@@ -2336,12 +2864,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2336
2864
  const { delete: deleteAction } = useDocumentActions();
2337
2865
  const { toggleNotification } = useNotification();
2338
2866
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2867
+ const isLocalized = document?.locale != null;
2339
2868
  return {
2340
2869
  disabled: !canDelete || !document,
2341
- label: formatMessage({
2342
- id: "content-manager.actions.delete.label",
2343
- defaultMessage: "Delete document"
2344
- }),
2870
+ label: formatMessage(
2871
+ {
2872
+ id: "content-manager.actions.delete.label",
2873
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2874
+ },
2875
+ { isLocalized }
2876
+ ),
2345
2877
  icon: /* @__PURE__ */ jsx(Trash, {}),
2346
2878
  dialog: {
2347
2879
  type: "dialog",
@@ -2431,7 +2963,7 @@ const ActionsPanel = () => {
2431
2963
  return {
2432
2964
  title: formatMessage({
2433
2965
  id: "content-manager.containers.edit.panels.default.title",
2434
- defaultMessage: "Document"
2966
+ defaultMessage: "Entry"
2435
2967
  }),
2436
2968
  content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2437
2969
  };
@@ -2466,334 +2998,32 @@ const ActionsPanelContent = () => {
2466
2998
  /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2467
2999
  ] });
2468
3000
  };
2469
- const Panel = React.forwardRef(({ children, title }, ref) => {
2470
- return /* @__PURE__ */ jsxs(
2471
- Flex,
2472
- {
2473
- ref,
2474
- tag: "aside",
2475
- "aria-labelledby": "additional-information",
2476
- background: "neutral0",
2477
- borderColor: "neutral150",
2478
- hasRadius: true,
2479
- paddingBottom: 4,
2480
- paddingLeft: 4,
2481
- paddingRight: 4,
2482
- paddingTop: 4,
2483
- shadow: "tableShadow",
2484
- gap: 3,
2485
- direction: "column",
2486
- justifyContent: "stretch",
2487
- alignItems: "flex-start",
2488
- children: [
2489
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2490
- children
2491
- ]
2492
- }
2493
- );
2494
- });
2495
- const HOOKS = {
2496
- /**
2497
- * Hook that allows to mutate the displayed headers of the list view table
2498
- * @constant
2499
- * @type {string}
2500
- */
2501
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2502
- /**
2503
- * Hook that allows to mutate the CM's collection types links pre-set filters
2504
- * @constant
2505
- * @type {string}
2506
- */
2507
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2508
- /**
2509
- * Hook that allows to mutate the CM's edit view layout
2510
- * @constant
2511
- * @type {string}
2512
- */
2513
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2514
- /**
2515
- * Hook that allows to mutate the CM's single types links pre-set filters
2516
- * @constant
2517
- * @type {string}
2518
- */
2519
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2520
- };
2521
- const contentTypesApi = contentManagerApi.injectEndpoints({
2522
- endpoints: (builder) => ({
2523
- getContentTypeConfiguration: builder.query({
2524
- query: (uid) => ({
2525
- url: `/content-manager/content-types/${uid}/configuration`,
2526
- method: "GET"
2527
- }),
2528
- transformResponse: (response) => response.data,
2529
- providesTags: (_result, _error, uid) => [
2530
- { type: "ContentTypesConfiguration", id: uid },
2531
- { type: "ContentTypeSettings", id: "LIST" }
2532
- ]
2533
- }),
2534
- getAllContentTypeSettings: builder.query({
2535
- query: () => "/content-manager/content-types-settings",
2536
- transformResponse: (response) => response.data,
2537
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2538
- }),
2539
- updateContentTypeConfiguration: builder.mutation({
2540
- query: ({ uid, ...body }) => ({
2541
- url: `/content-manager/content-types/${uid}/configuration`,
2542
- method: "PUT",
2543
- data: body
2544
- }),
2545
- transformResponse: (response) => response.data,
2546
- invalidatesTags: (_result, _error, { uid }) => [
2547
- { type: "ContentTypesConfiguration", id: uid },
2548
- { type: "ContentTypeSettings", id: "LIST" },
2549
- // Is this necessary?
2550
- { type: "InitialData" }
2551
- ]
2552
- })
2553
- })
2554
- });
2555
- const {
2556
- useGetContentTypeConfigurationQuery,
2557
- useGetAllContentTypeSettingsQuery,
2558
- useUpdateContentTypeConfigurationMutation
2559
- } = contentTypesApi;
2560
- const checkIfAttributeIsDisplayable = (attribute) => {
2561
- const { type } = attribute;
2562
- if (type === "relation") {
2563
- return !attribute.relation.toLowerCase().includes("morph");
2564
- }
2565
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2566
- };
2567
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2568
- if (!mainFieldName) {
2569
- return void 0;
2570
- }
2571
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2572
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2573
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2574
- );
2575
- return {
2576
- name: mainFieldName,
2577
- type: mainFieldType ?? "string"
2578
- };
2579
- };
2580
- const DEFAULT_SETTINGS = {
2581
- bulkable: false,
2582
- filterable: false,
2583
- searchable: false,
2584
- pagination: false,
2585
- defaultSortBy: "",
2586
- defaultSortOrder: "asc",
2587
- mainField: "id",
2588
- pageSize: 10
2589
- };
2590
- const useDocumentLayout = (model) => {
2591
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2592
- const [{ query }] = useQueryParams();
2593
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2594
- const { toggleNotification } = useNotification();
2595
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2596
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2597
- const {
2598
- data,
2599
- isLoading: isLoadingConfigs,
2600
- error,
2601
- isFetching: isFetchingConfigs
2602
- } = useGetContentTypeConfigurationQuery(model);
2603
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2604
- React.useEffect(() => {
2605
- if (error) {
2606
- toggleNotification({
2607
- type: "danger",
2608
- message: formatAPIError(error)
2609
- });
2610
- }
2611
- }, [error, formatAPIError, toggleNotification]);
2612
- const editLayout = React.useMemo(
2613
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2614
- layout: [],
2615
- components: {},
2616
- metadatas: {},
2617
- options: {},
2618
- settings: DEFAULT_SETTINGS
2619
- },
2620
- [data, isLoading, schemas, schema, components]
2621
- );
2622
- const listLayout = React.useMemo(() => {
2623
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2624
- layout: [],
2625
- metadatas: {},
2626
- options: {},
2627
- settings: DEFAULT_SETTINGS
2628
- };
2629
- }, [data, isLoading, schemas, schema, components]);
2630
- const { layout: edit } = React.useMemo(
2631
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2632
- layout: editLayout,
2633
- query
2634
- }),
2635
- [editLayout, query, runHookWaterfall]
2636
- );
2637
- return {
2638
- error,
2639
- isLoading,
2640
- edit,
2641
- list: listLayout
2642
- };
2643
- };
2644
- const useDocLayout = () => {
2645
- const { model } = useDoc();
2646
- return useDocumentLayout(model);
2647
- };
2648
- const formatEditLayout = (data, {
2649
- schemas,
2650
- schema,
2651
- components
2652
- }) => {
2653
- let currentPanelIndex = 0;
2654
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2655
- data.contentType.layouts.edit,
2656
- schema?.attributes,
2657
- data.contentType.metadatas,
2658
- { configurations: data.components, schemas: components },
2659
- schemas
2660
- ).reduce((panels, row) => {
2661
- if (row.some((field) => field.type === "dynamiczone")) {
2662
- panels.push([row]);
2663
- currentPanelIndex += 2;
2664
- } else {
2665
- if (!panels[currentPanelIndex]) {
2666
- panels.push([]);
2667
- }
2668
- panels[currentPanelIndex].push(row);
2669
- }
2670
- return panels;
2671
- }, []);
2672
- const componentEditAttributes = Object.entries(data.components).reduce(
2673
- (acc, [uid, configuration]) => {
2674
- acc[uid] = {
2675
- layout: convertEditLayoutToFieldLayouts(
2676
- configuration.layouts.edit,
2677
- components[uid].attributes,
2678
- configuration.metadatas
2679
- ),
2680
- settings: {
2681
- ...configuration.settings,
2682
- icon: components[uid].info.icon,
2683
- displayName: components[uid].info.displayName
2684
- }
2685
- };
2686
- return acc;
2687
- },
2688
- {}
2689
- );
2690
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2691
- (acc, [attribute, metadata]) => {
2692
- return {
2693
- ...acc,
2694
- [attribute]: metadata.edit
2695
- };
2696
- },
2697
- {}
2698
- );
2699
- return {
2700
- layout: panelledEditAttributes,
2701
- components: componentEditAttributes,
2702
- metadatas: editMetadatas,
2703
- settings: {
2704
- ...data.contentType.settings,
2705
- displayName: schema?.info.displayName
2706
- },
2707
- options: {
2708
- ...schema?.options,
2709
- ...schema?.pluginOptions,
2710
- ...data.contentType.options
2711
- }
2712
- };
2713
- };
2714
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2715
- return rows.map(
2716
- (row) => row.map((field) => {
2717
- const attribute = attributes[field.name];
2718
- if (!attribute) {
2719
- return null;
2720
- }
2721
- const { edit: metadata } = metadatas[field.name];
2722
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2723
- return {
2724
- attribute,
2725
- disabled: !metadata.editable,
2726
- hint: metadata.description,
2727
- label: metadata.label ?? "",
2728
- name: field.name,
2729
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2730
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2731
- schemas,
2732
- components: components?.schemas ?? {}
2733
- }),
2734
- placeholder: metadata.placeholder ?? "",
2735
- required: attribute.required ?? false,
2736
- size: field.size,
2737
- unique: "unique" in attribute ? attribute.unique : false,
2738
- visible: metadata.visible ?? true,
2739
- type: attribute.type
2740
- };
2741
- }).filter((field) => field !== null)
2742
- );
2743
- };
2744
- const formatListLayout = (data, {
2745
- schemas,
2746
- schema,
2747
- components
2748
- }) => {
2749
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2750
- (acc, [attribute, metadata]) => {
2751
- return {
2752
- ...acc,
2753
- [attribute]: metadata.list
2754
- };
2755
- },
2756
- {}
2757
- );
2758
- const listAttributes = convertListLayoutToFieldLayouts(
2759
- data.contentType.layouts.list,
2760
- schema?.attributes,
2761
- listMetadatas,
2762
- { configurations: data.components, schemas: components },
2763
- schemas
2764
- );
2765
- return {
2766
- layout: listAttributes,
2767
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2768
- metadatas: listMetadatas,
2769
- options: {
2770
- ...schema?.options,
2771
- ...schema?.pluginOptions,
2772
- ...data.contentType.options
2773
- }
2774
- };
2775
- };
2776
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2777
- return columns.map((name) => {
2778
- const attribute = attributes[name];
2779
- if (!attribute) {
2780
- return null;
3001
+ const Panel = React.forwardRef(({ children, title }, ref) => {
3002
+ return /* @__PURE__ */ jsxs(
3003
+ Flex,
3004
+ {
3005
+ ref,
3006
+ tag: "aside",
3007
+ "aria-labelledby": "additional-information",
3008
+ background: "neutral0",
3009
+ borderColor: "neutral150",
3010
+ hasRadius: true,
3011
+ paddingBottom: 4,
3012
+ paddingLeft: 4,
3013
+ paddingRight: 4,
3014
+ paddingTop: 4,
3015
+ shadow: "tableShadow",
3016
+ gap: 3,
3017
+ direction: "column",
3018
+ justifyContent: "stretch",
3019
+ alignItems: "flex-start",
3020
+ children: [
3021
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3022
+ children
3023
+ ]
2781
3024
  }
2782
- const metadata = metadatas[name];
2783
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2784
- return {
2785
- attribute,
2786
- label: metadata.label ?? "",
2787
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2788
- schemas,
2789
- components: components?.schemas ?? {}
2790
- }),
2791
- name,
2792
- searchable: metadata.searchable ?? true,
2793
- sortable: metadata.sortable ?? true
2794
- };
2795
- }).filter((field) => field !== null);
2796
- };
3025
+ );
3026
+ });
2797
3027
  const ConfirmBulkActionDialog = ({
2798
3028
  onToggleDialog,
2799
3029
  isOpen = false,
@@ -2801,30 +3031,23 @@ const ConfirmBulkActionDialog = ({
2801
3031
  endAction
2802
3032
  }) => {
2803
3033
  const { formatMessage } = useIntl();
2804
- return /* @__PURE__ */ jsxs(
2805
- Dialog,
2806
- {
2807
- onClose: onToggleDialog,
2808
- title: formatMessage({
2809
- id: "app.components.ConfirmDialog.title",
2810
- defaultMessage: "Confirmation"
2811
- }),
2812
- isOpen,
2813
- children: [
2814
- /* @__PURE__ */ jsx(DialogBody, { icon: /* @__PURE__ */ jsx(WarningCircle, {}), children: /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: dialogBody }) }),
2815
- /* @__PURE__ */ jsx(
2816
- DialogFooter,
2817
- {
2818
- startAction: /* @__PURE__ */ jsx(Button, { onClick: onToggleDialog, variant: "tertiary", children: formatMessage({
2819
- id: "app.components.Button.cancel",
2820
- defaultMessage: "Cancel"
2821
- }) }),
2822
- endAction
2823
- }
2824
- )
2825
- ]
2826
- }
2827
- );
3034
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
3035
+ /* @__PURE__ */ jsx(Dialog.Header, { children: formatMessage({
3036
+ id: "app.components.ConfirmDialog.title",
3037
+ defaultMessage: "Confirmation"
3038
+ }) }),
3039
+ /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3040
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
3041
+ dialogBody
3042
+ ] }) }),
3043
+ /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
3044
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { fullWidth: true, onClick: onToggleDialog, variant: "tertiary", children: formatMessage({
3045
+ id: "app.components.Button.cancel",
3046
+ defaultMessage: "Cancel"
3047
+ }) }) }),
3048
+ endAction
3049
+ ] })
3050
+ ] }) });
2828
3051
  };
2829
3052
  const BoldChunk$1 = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
2830
3053
  const ConfirmDialogPublishAll = ({
@@ -2839,6 +3062,7 @@ const ConfirmDialogPublishAll = ({
2839
3062
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2840
3063
  const { model, schema } = useDoc();
2841
3064
  const [{ query }] = useQueryParams();
3065
+ const enableDraftRelationsCount = false;
2842
3066
  const {
2843
3067
  data: countDraftRelations = 0,
2844
3068
  isLoading,
@@ -2850,7 +3074,7 @@ const ConfirmDialogPublishAll = ({
2850
3074
  locale: query?.plugins?.i18n?.locale
2851
3075
  },
2852
3076
  {
2853
- skip: selectedEntries.length === 0
3077
+ skip: !enableDraftRelationsCount
2854
3078
  }
2855
3079
  );
2856
3080
  React.useEffect(() => {
@@ -2929,7 +3153,14 @@ const formatErrorMessages = (errors, parentKey, formatMessage) => {
2929
3153
  )
2930
3154
  );
2931
3155
  } else {
2932
- messages.push(...formatErrorMessages(value, currentKey, formatMessage));
3156
+ messages.push(
3157
+ ...formatErrorMessages(
3158
+ // @ts-expect-error TODO: check why value is not compatible with FormErrors
3159
+ value,
3160
+ currentKey,
3161
+ formatMessage
3162
+ )
3163
+ );
2933
3164
  }
2934
3165
  } else {
2935
3166
  messages.push(
@@ -3028,7 +3259,7 @@ const SelectedEntriesTableContent = ({
3028
3259
  status: row.status
3029
3260
  }
3030
3261
  ) }),
3031
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3262
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3032
3263
  IconButton,
3033
3264
  {
3034
3265
  tag: Link,
@@ -3037,23 +3268,16 @@ const SelectedEntriesTableContent = ({
3037
3268
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3038
3269
  },
3039
3270
  state: { from: pathname },
3040
- label: formatMessage(
3041
- { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3042
- {
3043
- target: formatMessage(
3044
- {
3045
- id: "content-manager.components.ListViewHelperPluginTable.row-line",
3046
- defaultMessage: "item line {number}"
3047
- },
3048
- { number: index2 + 1 }
3049
- )
3050
- }
3051
- ),
3271
+ label: formatMessage({
3272
+ id: "content-manager.bulk-publish.edit",
3273
+ defaultMessage: "Edit"
3274
+ }),
3052
3275
  target: "_blank",
3053
3276
  marginLeft: "auto",
3054
- children: /* @__PURE__ */ jsx(Pencil, {})
3277
+ variant: "ghost",
3278
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3055
3279
  }
3056
- ) })
3280
+ ) }) })
3057
3281
  ] }, row.id)) })
3058
3282
  ] });
3059
3283
  };
@@ -3090,7 +3314,13 @@ const SelectedEntriesModalContent = ({
3090
3314
  );
3091
3315
  const { rows, validationErrors } = React.useMemo(() => {
3092
3316
  if (data.length > 0 && schema) {
3093
- const validate = createYupSchema(schema.attributes, components);
3317
+ const validate = createYupSchema(
3318
+ schema.attributes,
3319
+ components,
3320
+ // Since this is the "Publish" action, the validation
3321
+ // schema must enforce the rules for published entities
3322
+ { status: "published" }
3323
+ );
3094
3324
  const validationErrors2 = {};
3095
3325
  const rows2 = data.map((entry) => {
3096
3326
  try {
@@ -3166,7 +3396,7 @@ const SelectedEntriesModalContent = ({
3166
3396
  );
3167
3397
  };
3168
3398
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3169
- /* @__PURE__ */ jsxs(ModalBody, { children: [
3399
+ /* @__PURE__ */ jsxs(Modal.Body, { children: [
3170
3400
  /* @__PURE__ */ jsx(Typography, { children: getFormattedCountMessage() }),
3171
3401
  /* @__PURE__ */ jsx(Box, { marginTop: 5, children: /* @__PURE__ */ jsx(
3172
3402
  SelectedEntriesTableContent,
@@ -3178,27 +3408,24 @@ const SelectedEntriesModalContent = ({
3178
3408
  }
3179
3409
  ) })
3180
3410
  ] }),
3181
- /* @__PURE__ */ jsx(
3182
- ModalFooter,
3183
- {
3184
- startActions: /* @__PURE__ */ jsx(Button, { onClick: toggleModal, variant: "tertiary", children: formatMessage({
3185
- id: "app.components.Button.cancel",
3186
- defaultMessage: "Cancel"
3187
- }) }),
3188
- endActions: /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3189
- /* @__PURE__ */ jsx(Button, { onClick: refetch, variant: "tertiary", loading: isFetching, children: formatMessage({ id: "app.utils.refresh", defaultMessage: "Refresh" }) }),
3190
- /* @__PURE__ */ jsx(
3191
- Button,
3192
- {
3193
- onClick: toggleDialog,
3194
- disabled: selectedEntries.length === 0 || selectedEntries.length === selectedEntriesWithErrorsCount || selectedEntriesPublished === selectedEntries.length || isLoading,
3195
- loading: isSubmittingForm,
3196
- children: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" })
3197
- }
3198
- )
3199
- ] })
3200
- }
3201
- ),
3411
+ /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3412
+ /* @__PURE__ */ jsx(Button, { onClick: toggleModal, variant: "tertiary", children: formatMessage({
3413
+ id: "app.components.Button.cancel",
3414
+ defaultMessage: "Cancel"
3415
+ }) }),
3416
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3417
+ /* @__PURE__ */ jsx(Button, { onClick: refetch, variant: "tertiary", loading: isFetching, children: formatMessage({ id: "app.utils.refresh", defaultMessage: "Refresh" }) }),
3418
+ /* @__PURE__ */ jsx(
3419
+ Button,
3420
+ {
3421
+ onClick: toggleDialog,
3422
+ disabled: selectedEntries.length === 0 || selectedEntries.length === selectedEntriesWithErrorsCount || selectedEntriesPublished === selectedEntries.length || isLoading,
3423
+ loading: isSubmittingForm,
3424
+ children: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" })
3425
+ }
3426
+ )
3427
+ ] })
3428
+ ] }),
3202
3429
  /* @__PURE__ */ jsx(
3203
3430
  ConfirmDialogPublishAll,
3204
3431
  {
@@ -3263,143 +3490,10 @@ const BulkActionsRenderer = () => {
3263
3490
  documents: selectedRows
3264
3491
  },
3265
3492
  descriptions: plugins["content-manager"].apis.getBulkActions(),
3266
- children: (actions2) => actions2.map((action) => /* @__PURE__ */ jsx(BulkActionAction, { ...action }, action.id))
3493
+ children: (actions2) => actions2.map((action) => /* @__PURE__ */ jsx(DocumentActionButton, { ...action }, action.id))
3267
3494
  }
3268
3495
  ) });
3269
3496
  };
3270
- const BulkActionAction = (action) => {
3271
- const [dialogId, setDialogId] = React.useState(null);
3272
- const { toggleNotification } = useNotification();
3273
- const handleClick = (action2) => (e) => {
3274
- const { onClick, dialog, id } = action2;
3275
- if (onClick) {
3276
- onClick(e);
3277
- }
3278
- if (dialog) {
3279
- switch (dialog.type) {
3280
- case "notification":
3281
- toggleNotification({
3282
- title: dialog.title,
3283
- message: dialog.content,
3284
- type: dialog.status,
3285
- timeout: dialog.timeout,
3286
- onClose: dialog.onClose
3287
- });
3288
- break;
3289
- case "dialog":
3290
- case "modal": {
3291
- e.preventDefault();
3292
- setDialogId(id);
3293
- }
3294
- }
3295
- }
3296
- };
3297
- const handleClose = () => {
3298
- setDialogId(null);
3299
- if (action.dialog?.type === "modal" && action.dialog?.onClose) {
3300
- action.dialog.onClose();
3301
- }
3302
- };
3303
- return /* @__PURE__ */ jsxs(Fragment, { children: [
3304
- /* @__PURE__ */ jsx(
3305
- Button,
3306
- {
3307
- disabled: action.disabled,
3308
- startIcon: action.icon,
3309
- variant: action.variant,
3310
- onClick: handleClick(action),
3311
- children: action.label
3312
- }
3313
- ),
3314
- action.dialog?.type === "dialog" ? /* @__PURE__ */ jsx(
3315
- BulkActionConfirmDialog,
3316
- {
3317
- ...action.dialog,
3318
- variant: action.variant,
3319
- isOpen: dialogId === action.id,
3320
- onClose: handleClose
3321
- }
3322
- ) : null,
3323
- action.dialog?.type === "modal" ? /* @__PURE__ */ jsx(
3324
- BulkActionModal,
3325
- {
3326
- ...action.dialog,
3327
- onModalClose: handleClose,
3328
- isOpen: dialogId === action.id
3329
- }
3330
- ) : null
3331
- ] });
3332
- };
3333
- const BulkActionConfirmDialog = ({
3334
- onClose,
3335
- onCancel,
3336
- onConfirm,
3337
- title,
3338
- content,
3339
- confirmButton,
3340
- isOpen,
3341
- variant = "secondary"
3342
- }) => {
3343
- const { formatMessage } = useIntl();
3344
- const handleClose = async () => {
3345
- if (onCancel) {
3346
- await onCancel();
3347
- }
3348
- onClose();
3349
- };
3350
- const handleConfirm = async () => {
3351
- if (onConfirm) {
3352
- await onConfirm();
3353
- }
3354
- onClose();
3355
- };
3356
- return /* @__PURE__ */ jsxs(Dialog, { isOpen, title, onClose: handleClose, children: [
3357
- /* @__PURE__ */ jsx(DialogBody, { icon: /* @__PURE__ */ jsx(WarningCircle, {}), children: content }),
3358
- /* @__PURE__ */ jsx(
3359
- DialogFooter,
3360
- {
3361
- startAction: /* @__PURE__ */ jsx(Button, { onClick: handleClose, variant: "tertiary", children: formatMessage({
3362
- id: "app.components.Button.cancel",
3363
- defaultMessage: "Cancel"
3364
- }) }),
3365
- endAction: /* @__PURE__ */ jsx(
3366
- Button,
3367
- {
3368
- onClick: handleConfirm,
3369
- variant: variant === "danger-light" ? variant : "secondary",
3370
- startIcon: variant === "danger-light" ? /* @__PURE__ */ jsx(Trash, {}) : /* @__PURE__ */ jsx(Check, {}),
3371
- children: confirmButton ? confirmButton : formatMessage({
3372
- id: "app.components.Button.confirm",
3373
- defaultMessage: "Confirm"
3374
- })
3375
- }
3376
- )
3377
- }
3378
- )
3379
- ] });
3380
- };
3381
- const BulkActionModal = ({
3382
- isOpen,
3383
- title,
3384
- onClose,
3385
- content: Content,
3386
- onModalClose
3387
- }) => {
3388
- const id = React.useId();
3389
- if (!isOpen) {
3390
- return null;
3391
- }
3392
- const handleClose = () => {
3393
- if (onClose) {
3394
- onClose();
3395
- }
3396
- onModalClose();
3397
- };
3398
- return /* @__PURE__ */ jsxs(ModalLayout, { borderRadius: "4px", overflow: "hidden", onClose: handleClose, labelledBy: id, children: [
3399
- /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", textColor: "neutral800", tag: "h2", id, children: title }) }),
3400
- /* @__PURE__ */ jsx(Content, { onClose: handleClose })
3401
- ] });
3402
- };
3403
3497
  const DeleteAction = ({ documents, model }) => {
3404
3498
  const { formatMessage } = useIntl();
3405
3499
  const { schema: contentType } = useDoc();
@@ -3432,6 +3526,7 @@ const DeleteAction = ({ documents, model }) => {
3432
3526
  defaultMessage: "Confirmation"
3433
3527
  }),
3434
3528
  content: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3529
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
3435
3530
  /* @__PURE__ */ jsx(Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
3436
3531
  id: "popUpWarning.bodyMessage.contentType.delete.all",
3437
3532
  defaultMessage: "Are you sure you want to delete these entries?"
@@ -3481,6 +3576,7 @@ const UnpublishAction = ({ documents, model }) => {
3481
3576
  defaultMessage: "Confirmation"
3482
3577
  }),
3483
3578
  content: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3579
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
3484
3580
  /* @__PURE__ */ jsx(Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
3485
3581
  id: "popUpWarning.bodyMessage.contentType.unpublish.all",
3486
3582
  defaultMessage: "Are you sure you want to unpublish these entries?"
@@ -3574,7 +3670,7 @@ const TableActions = ({ document }) => {
3574
3670
  DescriptionComponentRenderer,
3575
3671
  {
3576
3672
  props,
3577
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3673
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3578
3674
  children: (actions2) => {
3579
3675
  const tableRowActions = actions2.filter((action) => {
3580
3676
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3685,7 +3781,7 @@ const CloneAction = ({ model, documentId }) => {
3685
3781
  }),
3686
3782
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3687
3783
  footer: ({ onClose }) => {
3688
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3784
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3689
3785
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3690
3786
  id: "cancel",
3691
3787
  defaultMessage: "Cancel"
@@ -3726,8 +3822,7 @@ class ContentManagerPlugin {
3726
3822
  documentActions = [
3727
3823
  ...DEFAULT_ACTIONS,
3728
3824
  ...DEFAULT_TABLE_ROW_ACTIONS,
3729
- ...DEFAULT_HEADER_ACTIONS,
3730
- HistoryAction
3825
+ ...DEFAULT_HEADER_ACTIONS
3731
3826
  ];
3732
3827
  editViewSidePanels = [ActionsPanel];
3733
3828
  headerActions = [];
@@ -3816,6 +3911,52 @@ const getPrintableType = (value) => {
3816
3911
  }
3817
3912
  return nativeType;
3818
3913
  };
3914
+ const HistoryAction = ({ model, document }) => {
3915
+ const { formatMessage } = useIntl();
3916
+ const [{ query }] = useQueryParams();
3917
+ const navigate = useNavigate();
3918
+ const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
3919
+ if (!window.strapi.features.isEnabled("cms-content-history")) {
3920
+ return null;
3921
+ }
3922
+ return {
3923
+ icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
3924
+ label: formatMessage({
3925
+ id: "content-manager.history.document-action",
3926
+ defaultMessage: "Content History"
3927
+ }),
3928
+ onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
3929
+ disabled: (
3930
+ /**
3931
+ * The user is creating a new document.
3932
+ * It hasn't been saved yet, so there's no history to go to
3933
+ */
3934
+ !document || /**
3935
+ * The document has been created but the current dimension has never been saved.
3936
+ * For example, the user is creating a new locale in an existing document,
3937
+ * so there's no history for the document in that locale
3938
+ */
3939
+ !document.id || /**
3940
+ * History is only available for content types created by the user.
3941
+ * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
3942
+ * which start with `admin::` or `plugin::`
3943
+ */
3944
+ !model.startsWith("api::")
3945
+ ),
3946
+ position: "header"
3947
+ };
3948
+ };
3949
+ HistoryAction.type = "history";
3950
+ const historyAdmin = {
3951
+ bootstrap(app) {
3952
+ const { addDocumentAction } = app.getPlugin("content-manager").apis;
3953
+ addDocumentAction((actions2) => {
3954
+ const indexOfDeleteAction = actions2.findIndex((action) => action.type === "delete");
3955
+ actions2.splice(indexOfDeleteAction, 0, HistoryAction);
3956
+ return actions2;
3957
+ });
3958
+ }
3959
+ };
3819
3960
  const initialState = {
3820
3961
  collectionTypeLinks: [],
3821
3962
  components: [],
@@ -3852,6 +3993,70 @@ const { setInitialData } = actions;
3852
3993
  const reducer = combineReducers({
3853
3994
  app: reducer$1
3854
3995
  });
3996
+ const previewApi = contentManagerApi.injectEndpoints({
3997
+ endpoints: (builder) => ({
3998
+ getPreviewUrl: builder.query({
3999
+ query({ query, params }) {
4000
+ return {
4001
+ url: `/content-manager/preview/url/${params.contentType}`,
4002
+ method: "GET",
4003
+ config: {
4004
+ params: query
4005
+ }
4006
+ };
4007
+ }
4008
+ })
4009
+ })
4010
+ });
4011
+ const { useGetPreviewUrlQuery } = previewApi;
4012
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4013
+ const { formatMessage } = useIntl();
4014
+ const { trackUsage } = useTracking();
4015
+ const [{ query }] = useQueryParams();
4016
+ const { data, error } = useGetPreviewUrlQuery({
4017
+ params: {
4018
+ contentType: model
4019
+ },
4020
+ query: {
4021
+ documentId,
4022
+ locale: document?.locale,
4023
+ status: document?.status
4024
+ }
4025
+ });
4026
+ if (!data?.data?.url || error) {
4027
+ return null;
4028
+ }
4029
+ const handleClick = () => {
4030
+ trackUsage("willOpenPreview");
4031
+ };
4032
+ return {
4033
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4034
+ content: /* @__PURE__ */ jsx(Flex, { gap: 2, width: "100%", children: /* @__PURE__ */ jsx(
4035
+ Button,
4036
+ {
4037
+ variant: "tertiary",
4038
+ tag: Link,
4039
+ to: { pathname: "preview", search: stringify(query, { encode: false }) },
4040
+ onClick: handleClick,
4041
+ flex: "auto",
4042
+ children: formatMessage({
4043
+ id: "content-manager.preview.panel.button",
4044
+ defaultMessage: "Open preview"
4045
+ })
4046
+ }
4047
+ ) })
4048
+ };
4049
+ };
4050
+ const FEATURE_ID = "preview";
4051
+ const previewAdmin = {
4052
+ bootstrap(app) {
4053
+ if (!window.strapi.future.isEnabled(FEATURE_ID)) {
4054
+ return;
4055
+ }
4056
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4057
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4058
+ }
4059
+ };
3855
4060
  const index = {
3856
4061
  register(app) {
3857
4062
  const cm = new ContentManagerPlugin();
@@ -3866,15 +4071,32 @@ const index = {
3866
4071
  defaultMessage: "Content Manager"
3867
4072
  },
3868
4073
  permissions: [],
3869
- Component: () => import("./layout-omucV6TV.mjs").then((mod) => ({ default: mod.Layout })),
3870
4074
  position: 1
3871
4075
  });
4076
+ app.router.addRoute({
4077
+ path: "content-manager/*",
4078
+ lazy: async () => {
4079
+ const { Layout } = await import("./layout-C8H4oKDo.mjs");
4080
+ return {
4081
+ Component: Layout
4082
+ };
4083
+ },
4084
+ children: routes
4085
+ });
3872
4086
  app.registerPlugin(cm.config);
3873
4087
  },
4088
+ bootstrap(app) {
4089
+ if (typeof historyAdmin.bootstrap === "function") {
4090
+ historyAdmin.bootstrap(app);
4091
+ }
4092
+ if (typeof previewAdmin.bootstrap === "function") {
4093
+ previewAdmin.bootstrap(app);
4094
+ }
4095
+ },
3874
4096
  async registerTrads({ locales }) {
3875
4097
  const importedTrads = await Promise.all(
3876
4098
  locales.map((locale) => {
3877
- 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-9GwRW_ku.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 }) => {
4099
+ 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-69jRDM9j.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`).then(({ default: data }) => {
3878
4100
  return {
3879
4101
  data: prefixPluginTranslations(data, PLUGIN_ID),
3880
4102
  locale
@@ -3901,8 +4123,10 @@ export {
3901
4123
  HOOKS as H,
3902
4124
  InjectionZone as I,
3903
4125
  useDocument as J,
3904
- index as K,
3905
- useDocumentActions as L,
4126
+ useGetPreviewUrlQuery as K,
4127
+ index as L,
4128
+ useContentManagerContext as M,
4129
+ useDocumentActions as N,
3906
4130
  Panels as P,
3907
4131
  RelativeTime as R,
3908
4132
  SINGLE_TYPES as S,
@@ -3920,11 +4144,11 @@ export {
3920
4144
  PERMISSIONS as k,
3921
4145
  DocumentRBAC as l,
3922
4146
  DOCUMENT_META_FIELDS as m,
3923
- useDocLayout as n,
3924
- useGetContentTypeConfigurationQuery as o,
3925
- CREATOR_FIELDS as p,
3926
- getMainField as q,
3927
- routes as r,
4147
+ CLONE_PATH as n,
4148
+ useDocLayout as o,
4149
+ useGetContentTypeConfigurationQuery as p,
4150
+ CREATOR_FIELDS as q,
4151
+ getMainField as r,
3928
4152
  setInitialData as s,
3929
4153
  getDisplayName as t,
3930
4154
  useContentTypeSchema as u,
@@ -3934,4 +4158,4 @@ export {
3934
4158
  capitalise as y,
3935
4159
  useUpdateContentTypeConfigurationMutation as z
3936
4160
  };
3937
- //# sourceMappingURL=index-Dahjdw4h.mjs.map
4161
+ //# sourceMappingURL=index-Ta--2bRa.mjs.map