@strapi/content-manager 0.0.0-experimental.bd712ad3930045f4a5d2144c119e0b7856e97fc4 → 0.0.0-experimental.bec5a58066c034a7ebf5b14df62560e68a456fa3

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 (206) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-C7ImeKGM.mjs → ComponentConfigurationPage-BaJMOQyq.mjs} +4 -4
  2. package/dist/_chunks/{ComponentConfigurationPage-C7ImeKGM.mjs.map → ComponentConfigurationPage-BaJMOQyq.mjs.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-BWQv6yRj.js → ComponentConfigurationPage-N-CTtgQa.js} +4 -4
  4. package/dist/_chunks/{ComponentConfigurationPage-BWQv6yRj.js.map → ComponentConfigurationPage-N-CTtgQa.js.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-CEGwxV-L.js → EditConfigurationPage-BHkjAbxH.js} +4 -4
  6. package/dist/_chunks/{EditConfigurationPage-CEGwxV-L.js.map → EditConfigurationPage-BHkjAbxH.js.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-MItFGzT9.mjs → EditConfigurationPage-CKK-5LfX.mjs} +4 -4
  8. package/dist/_chunks/{EditConfigurationPage-MItFGzT9.mjs.map → EditConfigurationPage-CKK-5LfX.mjs.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-DhmAg0NK.mjs → EditViewPage-B11aeMcf.mjs} +63 -12
  10. package/dist/_chunks/EditViewPage-B11aeMcf.mjs.map +1 -0
  11. package/dist/_chunks/{EditViewPage-CmMi2Xsn.js → EditViewPage-QPUftxUd.js} +62 -11
  12. package/dist/_chunks/EditViewPage-QPUftxUd.js.map +1 -0
  13. package/dist/_chunks/{Field-Cs62u5pl.mjs → Field-Bj_RgtGo.mjs} +215 -124
  14. package/dist/_chunks/Field-Bj_RgtGo.mjs.map +1 -0
  15. package/dist/_chunks/{Field-1DLtcLAI.js → Field-DUK83cfh.js} +216 -125
  16. package/dist/_chunks/Field-DUK83cfh.js.map +1 -0
  17. package/dist/_chunks/{Form-CqFA7F_V.js → Form-DHmBRlHd.js} +36 -17
  18. package/dist/_chunks/Form-DHmBRlHd.js.map +1 -0
  19. package/dist/_chunks/{Form-zYHtzGUX.mjs → Form-DLMSoXV7.mjs} +36 -17
  20. package/dist/_chunks/Form-DLMSoXV7.mjs.map +1 -0
  21. package/dist/_chunks/{History-DalgFQ3D.mjs → History-CfCSNlG9.mjs} +59 -106
  22. package/dist/_chunks/History-CfCSNlG9.mjs.map +1 -0
  23. package/dist/_chunks/{History-BblwXv7-.js → History-Di3zm4HT.js} +57 -104
  24. package/dist/_chunks/History-Di3zm4HT.js.map +1 -0
  25. package/dist/_chunks/{ListConfigurationPage-DWy-vRzs.mjs → ListConfigurationPage-0mtv_iqk.mjs} +18 -7
  26. package/dist/_chunks/ListConfigurationPage-0mtv_iqk.mjs.map +1 -0
  27. package/dist/_chunks/{ListConfigurationPage-Cpy4QqNd.js → ListConfigurationPage-Cq361KIt.js} +17 -6
  28. package/dist/_chunks/ListConfigurationPage-Cq361KIt.js.map +1 -0
  29. package/dist/_chunks/{ListViewPage-BkAwIW9s.mjs → ListViewPage-BxLVROX8.mjs} +106 -74
  30. package/dist/_chunks/ListViewPage-BxLVROX8.mjs.map +1 -0
  31. package/dist/_chunks/{ListViewPage-DFjn1DNW.js → ListViewPage-DFDcG8gM.js} +108 -76
  32. package/dist/_chunks/ListViewPage-DFDcG8gM.js.map +1 -0
  33. package/dist/_chunks/{NoContentTypePage-B9BCNNdL.mjs → NoContentTypePage-BRfDd67_.mjs} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-B9BCNNdL.mjs.map → NoContentTypePage-BRfDd67_.mjs.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-C-3ykoxs.js → NoContentTypePage-BSyvnDZZ.js} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-C-3ykoxs.js.map → NoContentTypePage-BSyvnDZZ.js.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-Bt_HWGat.mjs → NoPermissionsPage-CV9V8KWa.mjs} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-Bt_HWGat.mjs.map → NoPermissionsPage-CV9V8KWa.mjs.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-DKLmDZnZ.js → NoPermissionsPage-DyLphsn_.js} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-DKLmDZnZ.js.map → NoPermissionsPage-DyLphsn_.js.map} +1 -1
  41. package/dist/_chunks/Preview-C_B1nx3g.mjs +272 -0
  42. package/dist/_chunks/Preview-C_B1nx3g.mjs.map +1 -0
  43. package/dist/_chunks/Preview-D_3aO6Ly.js +291 -0
  44. package/dist/_chunks/Preview-D_3aO6Ly.js.map +1 -0
  45. package/dist/_chunks/{Relations-CJmTbZ8T.mjs → Relations-C6pwmDXh.mjs} +73 -37
  46. package/dist/_chunks/Relations-C6pwmDXh.mjs.map +1 -0
  47. package/dist/_chunks/{Relations-CrxfoH2n.js → Relations-Cne2AlrL.js} +72 -36
  48. package/dist/_chunks/Relations-Cne2AlrL.js.map +1 -0
  49. package/dist/_chunks/{en-Ux26r5pl.mjs → en-DhFUjrNW.mjs} +31 -18
  50. package/dist/_chunks/{en-Ux26r5pl.mjs.map → en-DhFUjrNW.mjs.map} +1 -1
  51. package/dist/_chunks/{en-fbKQxLGn.js → en-Ic0kXjxB.js} +31 -18
  52. package/dist/_chunks/{en-fbKQxLGn.js.map → en-Ic0kXjxB.js.map} +1 -1
  53. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  54. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  55. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  56. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  57. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  58. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  59. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  60. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  61. package/dist/_chunks/{index-D1344xdw.mjs → index-BpxR3En4.mjs} +1092 -721
  62. package/dist/_chunks/index-BpxR3En4.mjs.map +1 -0
  63. package/dist/_chunks/{index-Buwn78Rt.js → index-T-aWjbj2.js} +1073 -701
  64. package/dist/_chunks/index-T-aWjbj2.js.map +1 -0
  65. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  66. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  67. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  68. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  69. package/dist/_chunks/{layout-DRuJUpas.js → layout-BEuNwv-F.js} +21 -8
  70. package/dist/_chunks/layout-BEuNwv-F.js.map +1 -0
  71. package/dist/_chunks/{layout-ChVuUpa1.mjs → layout-DhMZ_lDx.mjs} +22 -9
  72. package/dist/_chunks/layout-DhMZ_lDx.mjs.map +1 -0
  73. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  74. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  75. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  76. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  77. package/dist/_chunks/{relations-B-deMCy4.mjs → relations-BdnxoX6f.mjs} +6 -7
  78. package/dist/_chunks/relations-BdnxoX6f.mjs.map +1 -0
  79. package/dist/_chunks/{relations-DuoUwyJr.js → relations-kLcuobLk.js} +6 -7
  80. package/dist/_chunks/relations-kLcuobLk.js.map +1 -0
  81. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  82. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  83. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  84. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  85. package/dist/admin/index.js +2 -1
  86. package/dist/admin/index.js.map +1 -1
  87. package/dist/admin/index.mjs +5 -4
  88. package/dist/admin/src/exports.d.ts +1 -1
  89. package/dist/admin/src/history/index.d.ts +3 -0
  90. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  91. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  92. package/dist/admin/src/index.d.ts +1 -0
  93. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  94. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -0
  95. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +2 -2
  96. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  97. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  98. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  99. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  100. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  101. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  102. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  103. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  104. package/dist/admin/src/preview/constants.d.ts +1 -0
  105. package/dist/admin/src/preview/index.d.ts +4 -0
  106. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  107. package/dist/admin/src/preview/routes.d.ts +3 -0
  108. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  109. package/dist/admin/src/router.d.ts +1 -1
  110. package/dist/admin/src/services/api.d.ts +1 -1
  111. package/dist/admin/src/services/components.d.ts +2 -2
  112. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  113. package/dist/admin/src/services/documents.d.ts +19 -20
  114. package/dist/admin/src/services/init.d.ts +1 -1
  115. package/dist/admin/src/services/relations.d.ts +2 -2
  116. package/dist/admin/src/services/uid.d.ts +3 -3
  117. package/dist/admin/src/utils/validation.d.ts +4 -1
  118. package/dist/server/index.js +591 -261
  119. package/dist/server/index.js.map +1 -1
  120. package/dist/server/index.mjs +592 -262
  121. package/dist/server/index.mjs.map +1 -1
  122. package/dist/server/src/bootstrap.d.ts.map +1 -1
  123. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  124. package/dist/server/src/controllers/index.d.ts.map +1 -1
  125. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  126. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  127. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  128. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  129. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  130. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  131. package/dist/server/src/history/services/history.d.ts.map +1 -1
  132. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  133. package/dist/server/src/history/services/utils.d.ts +4 -4
  134. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  135. package/dist/server/src/index.d.ts +4 -4
  136. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  137. package/dist/server/src/preview/constants.d.ts +2 -0
  138. package/dist/server/src/preview/constants.d.ts.map +1 -0
  139. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  140. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  141. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  142. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  143. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  144. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  145. package/dist/server/src/preview/index.d.ts +4 -0
  146. package/dist/server/src/preview/index.d.ts.map +1 -0
  147. package/dist/server/src/preview/routes/index.d.ts +8 -0
  148. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  149. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  150. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  151. package/dist/server/src/preview/services/index.d.ts +16 -0
  152. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  153. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  154. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  155. package/dist/server/src/preview/services/preview.d.ts +12 -0
  156. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  157. package/dist/server/src/preview/utils.d.ts +19 -0
  158. package/dist/server/src/preview/utils.d.ts.map +1 -0
  159. package/dist/server/src/register.d.ts.map +1 -1
  160. package/dist/server/src/routes/index.d.ts.map +1 -1
  161. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  162. package/dist/server/src/services/document-metadata.d.ts +8 -8
  163. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  164. package/dist/server/src/services/index.d.ts +4 -4
  165. package/dist/server/src/services/index.d.ts.map +1 -1
  166. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  167. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  168. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  169. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  170. package/dist/server/src/utils/index.d.ts +2 -0
  171. package/dist/server/src/utils/index.d.ts.map +1 -1
  172. package/dist/shared/contracts/collection-types.d.ts +3 -1
  173. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  174. package/dist/shared/contracts/index.d.ts +1 -0
  175. package/dist/shared/contracts/index.d.ts.map +1 -1
  176. package/dist/shared/contracts/preview.d.ts +27 -0
  177. package/dist/shared/contracts/preview.d.ts.map +1 -0
  178. package/dist/shared/index.js +4 -0
  179. package/dist/shared/index.js.map +1 -1
  180. package/dist/shared/index.mjs +4 -0
  181. package/dist/shared/index.mjs.map +1 -1
  182. package/package.json +14 -14
  183. package/dist/_chunks/EditViewPage-CmMi2Xsn.js.map +0 -1
  184. package/dist/_chunks/EditViewPage-DhmAg0NK.mjs.map +0 -1
  185. package/dist/_chunks/Field-1DLtcLAI.js.map +0 -1
  186. package/dist/_chunks/Field-Cs62u5pl.mjs.map +0 -1
  187. package/dist/_chunks/Form-CqFA7F_V.js.map +0 -1
  188. package/dist/_chunks/Form-zYHtzGUX.mjs.map +0 -1
  189. package/dist/_chunks/History-BblwXv7-.js.map +0 -1
  190. package/dist/_chunks/History-DalgFQ3D.mjs.map +0 -1
  191. package/dist/_chunks/ListConfigurationPage-Cpy4QqNd.js.map +0 -1
  192. package/dist/_chunks/ListConfigurationPage-DWy-vRzs.mjs.map +0 -1
  193. package/dist/_chunks/ListViewPage-BkAwIW9s.mjs.map +0 -1
  194. package/dist/_chunks/ListViewPage-DFjn1DNW.js.map +0 -1
  195. package/dist/_chunks/Relations-CJmTbZ8T.mjs.map +0 -1
  196. package/dist/_chunks/Relations-CrxfoH2n.js.map +0 -1
  197. package/dist/_chunks/index-Buwn78Rt.js.map +0 -1
  198. package/dist/_chunks/index-D1344xdw.mjs.map +0 -1
  199. package/dist/_chunks/layout-ChVuUpa1.mjs.map +0 -1
  200. package/dist/_chunks/layout-DRuJUpas.js.map +0 -1
  201. package/dist/_chunks/relations-B-deMCy4.mjs.map +0 -1
  202. package/dist/_chunks/relations-DuoUwyJr.js.map +0 -1
  203. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  204. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  205. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  206. package/strapi-server.js +0 -3
@@ -1,15 +1,16 @@
1
- import { ClockCounterClockwise, CrossCircle, More, WarningCircle, ListPlus, Pencil, Trash, Check, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, Feather } from "@strapi/icons";
1
+ import { More, Cross, WarningCircle, ListPlus, Pencil, Trash, Check, CrossCircle, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
2
2
  import { jsx, Fragment, jsxs } from "react/jsx-runtime";
3
- import { useStrapiApp, useQueryParams, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useTracking, useForm, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
4
- import { stringify } from "qs";
5
- import { useIntl } from "react-intl";
6
- import { useNavigate, useParams, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
3
+ import { useStrapiApp, createContext, useQueryParams, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useForm, useTracking, useGuidedTour, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
7
4
  import * as React from "react";
8
5
  import { lazy } from "react";
9
- import { Button, Menu, VisuallyHidden, Flex, Box, Typography, Dialog, Modal, Radio, Status, SingleSelect, SingleSelectOption, Loader, IconButton, Tooltip, LinkButton } from "@strapi/design-system";
6
+ import { Menu, Button, VisuallyHidden, Flex, Dialog, Modal, Typography, Radio, Status, Box, SingleSelect, SingleSelectOption, IconButton, Loader, Tooltip, LinkButton } from "@strapi/design-system";
7
+ import mapValues from "lodash/fp/mapValues";
8
+ import { useIntl } from "react-intl";
9
+ import { useParams, useNavigate, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
10
10
  import { styled } from "styled-components";
11
11
  import * as yup from "yup";
12
12
  import { ValidationError } from "yup";
13
+ import { stringify } from "qs";
13
14
  import pipe from "lodash/fp/pipe";
14
15
  import { intervalToDuration, isPast } from "date-fns";
15
16
  import { createSlice, combineReducers } from "@reduxjs/toolkit";
@@ -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,7 +167,8 @@ 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({
@@ -208,7 +182,12 @@ const documentApi = contentManagerApi.injectEndpoints({
208
182
  params: query
209
183
  }
210
184
  }),
211
- 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
+ }
212
191
  }),
213
192
  cloneDocument: builder.mutation({
214
193
  query: ({ model, sourceId, data, params }) => ({
@@ -219,7 +198,10 @@ const documentApi = contentManagerApi.injectEndpoints({
219
198
  params
220
199
  }
221
200
  }),
222
- 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
+ ]
223
205
  }),
224
206
  /**
225
207
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -236,7 +218,8 @@ const documentApi = contentManagerApi.injectEndpoints({
236
218
  }),
237
219
  invalidatesTags: (result, _error, { model }) => [
238
220
  { type: "Document", id: `${model}_LIST` },
239
- "Relations"
221
+ "Relations",
222
+ { type: "UidAvailability", id: model }
240
223
  ]
241
224
  }),
242
225
  deleteDocument: builder.mutation({
@@ -277,7 +260,8 @@ const documentApi = contentManagerApi.injectEndpoints({
277
260
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
278
261
  },
279
262
  { type: "Document", id: `${model}_LIST` },
280
- "Relations"
263
+ "Relations",
264
+ { type: "UidAvailability", id: model }
281
265
  ];
282
266
  }
283
267
  }),
@@ -290,11 +274,12 @@ const documentApi = contentManagerApi.injectEndpoints({
290
274
  url: `/content-manager/collection-types/${model}`,
291
275
  method: "GET",
292
276
  config: {
293
- params
277
+ params: stringify(params, { encode: true })
294
278
  }
295
279
  }),
296
280
  providesTags: (result, _error, arg) => {
297
281
  return [
282
+ { type: "Document", id: `ALL_LIST` },
298
283
  { type: "Document", id: `${arg.model}_LIST` },
299
284
  ...result?.results.map(({ documentId }) => ({
300
285
  type: "Document",
@@ -333,6 +318,11 @@ const documentApi = contentManagerApi.injectEndpoints({
333
318
  {
334
319
  type: "Document",
335
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`
336
326
  }
337
327
  ];
338
328
  }
@@ -396,8 +386,21 @@ const documentApi = contentManagerApi.injectEndpoints({
396
386
  type: "Document",
397
387
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
398
388
  },
399
- "Relations"
389
+ "Relations",
390
+ { type: "UidAvailability", id: model }
400
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
+ }
401
404
  }
402
405
  }),
403
406
  unpublishDocument: builder.mutation({
@@ -459,28 +462,44 @@ const buildValidParams = (query) => {
459
462
  {}
460
463
  )
461
464
  };
462
- if ("_q" in validQueryParams) {
463
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
464
- }
465
465
  return validQueryParams;
466
466
  };
467
467
  const isBaseQueryError = (error) => {
468
468
  return error.name !== void 0;
469
469
  };
470
- const createYupSchema = (attributes = {}, components = {}) => {
470
+ const arrayValidator = (attribute, options) => ({
471
+ message: translatedErrors.required,
472
+ test(value) {
473
+ if (options.status === "draft") {
474
+ return true;
475
+ }
476
+ if (!attribute.required) {
477
+ return true;
478
+ }
479
+ if (!value) {
480
+ return false;
481
+ }
482
+ if (Array.isArray(value) && value.length === 0) {
483
+ return false;
484
+ }
485
+ return true;
486
+ }
487
+ });
488
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
471
489
  const createModelSchema = (attributes2) => yup.object().shape(
472
490
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
473
491
  if (DOCUMENT_META_FIELDS.includes(name)) {
474
492
  return acc;
475
493
  }
476
494
  const validations = [
495
+ addNullableValidation,
477
496
  addRequiredValidation,
478
497
  addMinLengthValidation,
479
498
  addMaxLengthValidation,
480
499
  addMinValidation,
481
500
  addMaxValidation,
482
501
  addRegexValidation
483
- ].map((fn) => fn(attribute));
502
+ ].map((fn) => fn(attribute, options));
484
503
  const transformSchema = pipe(...validations);
485
504
  switch (attribute.type) {
486
505
  case "component": {
@@ -490,12 +509,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
490
509
  ...acc,
491
510
  [name]: transformSchema(
492
511
  yup.array().of(createModelSchema(attributes3).nullable(false))
493
- )
512
+ ).test(arrayValidator(attribute, options))
494
513
  };
495
514
  } else {
496
515
  return {
497
516
  ...acc,
498
- [name]: transformSchema(createModelSchema(attributes3))
517
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
499
518
  };
500
519
  }
501
520
  }
@@ -517,7 +536,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
517
536
  }
518
537
  )
519
538
  )
520
- )
539
+ ).test(arrayValidator(attribute, options))
521
540
  };
522
541
  case "relation":
523
542
  return {
@@ -529,7 +548,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
529
548
  } else if (Array.isArray(value)) {
530
549
  return yup.array().of(
531
550
  yup.object().shape({
532
- id: yup.string().required()
551
+ id: yup.number().required()
533
552
  })
534
553
  );
535
554
  } else if (typeof value === "object") {
@@ -581,6 +600,14 @@ const createAttributeSchema = (attribute) => {
581
600
  if (!value || typeof value === "string" && value.length === 0) {
582
601
  return true;
583
602
  }
603
+ if (typeof value === "object") {
604
+ try {
605
+ JSON.stringify(value);
606
+ return true;
607
+ } catch (err) {
608
+ return false;
609
+ }
610
+ }
584
611
  try {
585
612
  JSON.parse(value);
586
613
  return true;
@@ -599,13 +626,7 @@ const createAttributeSchema = (attribute) => {
599
626
  return yup.mixed();
600
627
  }
601
628
  };
602
- const addRequiredValidation = (attribute) => (schema) => {
603
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
604
- return schema.min(1, translatedErrors.required);
605
- }
606
- if (attribute.required && attribute.type !== "relation") {
607
- return schema.required(translatedErrors.required);
608
- }
629
+ const nullableSchema = (schema) => {
609
630
  return schema?.nullable ? schema.nullable() : (
610
631
  // In some cases '.nullable' will not be available on the schema.
611
632
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -613,7 +634,22 @@ const addRequiredValidation = (attribute) => (schema) => {
613
634
  schema
614
635
  );
615
636
  };
616
- const addMinLengthValidation = (attribute) => (schema) => {
637
+ const addNullableValidation = () => (schema) => {
638
+ return nullableSchema(schema);
639
+ };
640
+ const addRequiredValidation = (attribute, options) => (schema) => {
641
+ if (options.status === "draft" || !attribute.required) {
642
+ return schema;
643
+ }
644
+ if (attribute.required && "required" in schema) {
645
+ return schema.required(translatedErrors.required);
646
+ }
647
+ return schema;
648
+ };
649
+ const addMinLengthValidation = (attribute, options) => (schema) => {
650
+ if (options.status === "draft") {
651
+ return schema;
652
+ }
617
653
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
618
654
  return schema.min(attribute.minLength, {
619
655
  ...translatedErrors.minLength,
@@ -635,32 +671,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
635
671
  }
636
672
  return schema;
637
673
  };
638
- const addMinValidation = (attribute) => (schema) => {
639
- if ("min" in attribute) {
674
+ const addMinValidation = (attribute, options) => (schema) => {
675
+ if (options.status === "draft") {
676
+ return schema;
677
+ }
678
+ if ("min" in attribute && "min" in schema) {
640
679
  const min = toInteger(attribute.min);
641
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
642
- if (!attribute.required && "test" in schema && min) {
643
- return schema.test(
644
- "custom-min",
645
- {
646
- ...translatedErrors.min,
647
- values: {
648
- min: attribute.min
649
- }
650
- },
651
- (value) => {
652
- if (!value) {
653
- return true;
654
- }
655
- if (Array.isArray(value) && value.length === 0) {
656
- return true;
657
- }
658
- return value.length >= min;
659
- }
660
- );
661
- }
662
- }
663
- if ("min" in schema && min) {
680
+ if (min) {
664
681
  return schema.min(min, {
665
682
  ...translatedErrors.min,
666
683
  values: {
@@ -778,19 +795,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
778
795
  }, {});
779
796
  return componentsByKey;
780
797
  };
781
- const useDocument = (args, opts) => {
798
+ const HOOKS = {
799
+ /**
800
+ * Hook that allows to mutate the displayed headers of the list view table
801
+ * @constant
802
+ * @type {string}
803
+ */
804
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
805
+ /**
806
+ * Hook that allows to mutate the CM's collection types links pre-set filters
807
+ * @constant
808
+ * @type {string}
809
+ */
810
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
811
+ /**
812
+ * Hook that allows to mutate the CM's edit view layout
813
+ * @constant
814
+ * @type {string}
815
+ */
816
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
817
+ /**
818
+ * Hook that allows to mutate the CM's single types links pre-set filters
819
+ * @constant
820
+ * @type {string}
821
+ */
822
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
823
+ };
824
+ const contentTypesApi = contentManagerApi.injectEndpoints({
825
+ endpoints: (builder) => ({
826
+ getContentTypeConfiguration: builder.query({
827
+ query: (uid) => ({
828
+ url: `/content-manager/content-types/${uid}/configuration`,
829
+ method: "GET"
830
+ }),
831
+ transformResponse: (response) => response.data,
832
+ providesTags: (_result, _error, uid) => [
833
+ { type: "ContentTypesConfiguration", id: uid },
834
+ { type: "ContentTypeSettings", id: "LIST" }
835
+ ]
836
+ }),
837
+ getAllContentTypeSettings: builder.query({
838
+ query: () => "/content-manager/content-types-settings",
839
+ transformResponse: (response) => response.data,
840
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
841
+ }),
842
+ updateContentTypeConfiguration: builder.mutation({
843
+ query: ({ uid, ...body }) => ({
844
+ url: `/content-manager/content-types/${uid}/configuration`,
845
+ method: "PUT",
846
+ data: body
847
+ }),
848
+ transformResponse: (response) => response.data,
849
+ invalidatesTags: (_result, _error, { uid }) => [
850
+ { type: "ContentTypesConfiguration", id: uid },
851
+ { type: "ContentTypeSettings", id: "LIST" },
852
+ // Is this necessary?
853
+ { type: "InitialData" }
854
+ ]
855
+ })
856
+ })
857
+ });
858
+ const {
859
+ useGetContentTypeConfigurationQuery,
860
+ useGetAllContentTypeSettingsQuery,
861
+ useUpdateContentTypeConfigurationMutation
862
+ } = contentTypesApi;
863
+ const checkIfAttributeIsDisplayable = (attribute) => {
864
+ const { type } = attribute;
865
+ if (type === "relation") {
866
+ return !attribute.relation.toLowerCase().includes("morph");
867
+ }
868
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
869
+ };
870
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
871
+ if (!mainFieldName) {
872
+ return void 0;
873
+ }
874
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
875
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
876
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
877
+ );
878
+ return {
879
+ name: mainFieldName,
880
+ type: mainFieldType ?? "string"
881
+ };
882
+ };
883
+ const DEFAULT_SETTINGS = {
884
+ bulkable: false,
885
+ filterable: false,
886
+ searchable: false,
887
+ pagination: false,
888
+ defaultSortBy: "",
889
+ defaultSortOrder: "asc",
890
+ mainField: "id",
891
+ pageSize: 10
892
+ };
893
+ const useDocumentLayout = (model) => {
894
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
895
+ const [{ query }] = useQueryParams();
896
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
782
897
  const { toggleNotification } = useNotification();
783
898
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
899
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
784
900
  const {
785
- currentData: data,
786
- isLoading: isLoadingDocument,
787
- isFetching: isFetchingDocument,
788
- error
789
- } = useGetDocumentQuery(args, {
790
- ...opts,
791
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
792
- });
793
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
901
+ data,
902
+ isLoading: isLoadingConfigs,
903
+ error,
904
+ isFetching: isFetchingConfigs
905
+ } = useGetContentTypeConfigurationQuery(model);
906
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
794
907
  React.useEffect(() => {
795
908
  if (error) {
796
909
  toggleNotification({
@@ -798,39 +911,255 @@ const useDocument = (args, opts) => {
798
911
  message: formatAPIError(error)
799
912
  });
800
913
  }
801
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
802
- const validationSchema = React.useMemo(() => {
803
- if (!schema) {
804
- return null;
805
- }
806
- return createYupSchema(schema.attributes, components);
807
- }, [schema, components]);
808
- const validate = React.useCallback(
809
- (document) => {
810
- if (!validationSchema) {
811
- throw new Error(
812
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
813
- );
814
- }
815
- try {
816
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
817
- return null;
818
- } catch (error2) {
819
- if (error2 instanceof ValidationError) {
820
- return getYupValidationErrors(error2);
821
- }
822
- throw error2;
823
- }
914
+ }, [error, formatAPIError, toggleNotification]);
915
+ const editLayout = React.useMemo(
916
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
917
+ layout: [],
918
+ components: {},
919
+ metadatas: {},
920
+ options: {},
921
+ settings: DEFAULT_SETTINGS
824
922
  },
825
- [validationSchema]
923
+ [data, isLoading, schemas, schema, components]
924
+ );
925
+ const listLayout = React.useMemo(() => {
926
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
927
+ layout: [],
928
+ metadatas: {},
929
+ options: {},
930
+ settings: DEFAULT_SETTINGS
931
+ };
932
+ }, [data, isLoading, schemas, schema, components]);
933
+ const { layout: edit } = React.useMemo(
934
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
935
+ layout: editLayout,
936
+ query
937
+ }),
938
+ [editLayout, query, runHookWaterfall]
826
939
  );
827
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
828
940
  return {
829
- components,
830
- document: data?.data,
831
- meta: data?.meta,
941
+ error,
832
942
  isLoading,
943
+ edit,
944
+ list: listLayout
945
+ };
946
+ };
947
+ const useDocLayout = () => {
948
+ const { model } = useDoc();
949
+ return useDocumentLayout(model);
950
+ };
951
+ const formatEditLayout = (data, {
952
+ schemas,
953
+ schema,
954
+ components
955
+ }) => {
956
+ let currentPanelIndex = 0;
957
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
958
+ data.contentType.layouts.edit,
959
+ schema?.attributes,
960
+ data.contentType.metadatas,
961
+ { configurations: data.components, schemas: components },
962
+ schemas
963
+ ).reduce((panels, row) => {
964
+ if (row.some((field) => field.type === "dynamiczone")) {
965
+ panels.push([row]);
966
+ currentPanelIndex += 2;
967
+ } else {
968
+ if (!panels[currentPanelIndex]) {
969
+ panels.push([row]);
970
+ } else {
971
+ panels[currentPanelIndex].push(row);
972
+ }
973
+ }
974
+ return panels;
975
+ }, []);
976
+ const componentEditAttributes = Object.entries(data.components).reduce(
977
+ (acc, [uid, configuration]) => {
978
+ acc[uid] = {
979
+ layout: convertEditLayoutToFieldLayouts(
980
+ configuration.layouts.edit,
981
+ components[uid].attributes,
982
+ configuration.metadatas,
983
+ { configurations: data.components, schemas: components }
984
+ ),
985
+ settings: {
986
+ ...configuration.settings,
987
+ icon: components[uid].info.icon,
988
+ displayName: components[uid].info.displayName
989
+ }
990
+ };
991
+ return acc;
992
+ },
993
+ {}
994
+ );
995
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
996
+ (acc, [attribute, metadata]) => {
997
+ return {
998
+ ...acc,
999
+ [attribute]: metadata.edit
1000
+ };
1001
+ },
1002
+ {}
1003
+ );
1004
+ return {
1005
+ layout: panelledEditAttributes,
1006
+ components: componentEditAttributes,
1007
+ metadatas: editMetadatas,
1008
+ settings: {
1009
+ ...data.contentType.settings,
1010
+ displayName: schema?.info.displayName
1011
+ },
1012
+ options: {
1013
+ ...schema?.options,
1014
+ ...schema?.pluginOptions,
1015
+ ...data.contentType.options
1016
+ }
1017
+ };
1018
+ };
1019
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1020
+ return rows.map(
1021
+ (row) => row.map((field) => {
1022
+ const attribute = attributes[field.name];
1023
+ if (!attribute) {
1024
+ return null;
1025
+ }
1026
+ const { edit: metadata } = metadatas[field.name];
1027
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1028
+ return {
1029
+ attribute,
1030
+ disabled: !metadata.editable,
1031
+ hint: metadata.description,
1032
+ label: metadata.label ?? "",
1033
+ name: field.name,
1034
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1035
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1036
+ schemas,
1037
+ components: components?.schemas ?? {}
1038
+ }),
1039
+ placeholder: metadata.placeholder ?? "",
1040
+ required: attribute.required ?? false,
1041
+ size: field.size,
1042
+ unique: "unique" in attribute ? attribute.unique : false,
1043
+ visible: metadata.visible ?? true,
1044
+ type: attribute.type
1045
+ };
1046
+ }).filter((field) => field !== null)
1047
+ );
1048
+ };
1049
+ const formatListLayout = (data, {
1050
+ schemas,
1051
+ schema,
1052
+ components
1053
+ }) => {
1054
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1055
+ (acc, [attribute, metadata]) => {
1056
+ return {
1057
+ ...acc,
1058
+ [attribute]: metadata.list
1059
+ };
1060
+ },
1061
+ {}
1062
+ );
1063
+ const listAttributes = convertListLayoutToFieldLayouts(
1064
+ data.contentType.layouts.list,
1065
+ schema?.attributes,
1066
+ listMetadatas,
1067
+ { configurations: data.components, schemas: components },
1068
+ schemas
1069
+ );
1070
+ return {
1071
+ layout: listAttributes,
1072
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1073
+ metadatas: listMetadatas,
1074
+ options: {
1075
+ ...schema?.options,
1076
+ ...schema?.pluginOptions,
1077
+ ...data.contentType.options
1078
+ }
1079
+ };
1080
+ };
1081
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1082
+ return columns.map((name) => {
1083
+ const attribute = attributes[name];
1084
+ if (!attribute) {
1085
+ return null;
1086
+ }
1087
+ const metadata = metadatas[name];
1088
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1089
+ return {
1090
+ attribute,
1091
+ label: metadata.label ?? "",
1092
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1093
+ schemas,
1094
+ components: components?.schemas ?? {}
1095
+ }),
1096
+ name,
1097
+ searchable: metadata.searchable ?? true,
1098
+ sortable: metadata.sortable ?? true
1099
+ };
1100
+ }).filter((field) => field !== null);
1101
+ };
1102
+ const useDocument = (args, opts) => {
1103
+ const { toggleNotification } = useNotification();
1104
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1105
+ const {
1106
+ currentData: data,
1107
+ isLoading: isLoadingDocument,
1108
+ isFetching: isFetchingDocument,
1109
+ error
1110
+ } = useGetDocumentQuery(args, {
1111
+ ...opts,
1112
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1113
+ });
1114
+ const {
1115
+ components,
1116
+ schema,
1117
+ schemas,
1118
+ isLoading: isLoadingSchema
1119
+ } = useContentTypeSchema(args.model);
1120
+ React.useEffect(() => {
1121
+ if (error) {
1122
+ toggleNotification({
1123
+ type: "danger",
1124
+ message: formatAPIError(error)
1125
+ });
1126
+ }
1127
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1128
+ const validationSchema = React.useMemo(() => {
1129
+ if (!schema) {
1130
+ return null;
1131
+ }
1132
+ return createYupSchema(schema.attributes, components);
1133
+ }, [schema, components]);
1134
+ const validate = React.useCallback(
1135
+ (document) => {
1136
+ if (!validationSchema) {
1137
+ throw new Error(
1138
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1139
+ );
1140
+ }
1141
+ try {
1142
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1143
+ return null;
1144
+ } catch (error2) {
1145
+ if (error2 instanceof ValidationError) {
1146
+ return getYupValidationErrors(error2);
1147
+ }
1148
+ throw error2;
1149
+ }
1150
+ },
1151
+ [validationSchema]
1152
+ );
1153
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1154
+ const hasError = !!error;
1155
+ return {
1156
+ components,
1157
+ document: data?.data,
1158
+ meta: data?.meta,
1159
+ isLoading,
1160
+ hasError,
833
1161
  schema,
1162
+ schemas,
834
1163
  validate
835
1164
  };
836
1165
  };
@@ -844,22 +1173,60 @@ const useDoc = () => {
844
1173
  if (!slug) {
845
1174
  throw new Error("Could not find model in url params");
846
1175
  }
1176
+ const document = useDocument(
1177
+ { documentId: origin || id, model: slug, collectionType, params },
1178
+ {
1179
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1180
+ }
1181
+ );
1182
+ const returnId = origin || id === "create" ? void 0 : id;
847
1183
  return {
848
1184
  collectionType,
849
1185
  model: slug,
850
- id: origin || id === "create" ? void 0 : id,
851
- ...useDocument(
852
- { documentId: origin || id, model: slug, collectionType, params },
853
- {
854
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
855
- }
856
- )
1186
+ id: returnId,
1187
+ ...document
1188
+ };
1189
+ };
1190
+ const useContentManagerContext = () => {
1191
+ const {
1192
+ collectionType,
1193
+ model,
1194
+ id,
1195
+ components,
1196
+ isLoading: isLoadingDoc,
1197
+ schema,
1198
+ schemas
1199
+ } = useDoc();
1200
+ const layout = useDocumentLayout(model);
1201
+ const form = useForm("useContentManagerContext", (state) => state);
1202
+ const isSingleType = collectionType === SINGLE_TYPES;
1203
+ const slug = model;
1204
+ const isCreatingEntry = id === "create";
1205
+ useContentTypeSchema();
1206
+ const isLoading = isLoadingDoc || layout.isLoading;
1207
+ const error = layout.error;
1208
+ return {
1209
+ error,
1210
+ isLoading,
1211
+ // Base metadata
1212
+ model,
1213
+ collectionType,
1214
+ id,
1215
+ slug,
1216
+ isCreatingEntry,
1217
+ isSingleType,
1218
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1219
+ // All schema infos
1220
+ components,
1221
+ contentType: schema,
1222
+ contentTypes: schemas,
1223
+ // Form state
1224
+ form,
1225
+ // layout infos
1226
+ layout
857
1227
  };
858
1228
  };
859
1229
  const prefixPluginTranslations = (trad, pluginId) => {
860
- if (!pluginId) {
861
- throw new TypeError("pluginId can't be empty");
862
- }
863
1230
  return Object.keys(trad).reduce((acc, current) => {
864
1231
  acc[`${pluginId}.${current}`] = trad[current];
865
1232
  return acc;
@@ -875,6 +1242,8 @@ const useDocumentActions = () => {
875
1242
  const { formatMessage } = useIntl();
876
1243
  const { trackUsage } = useTracking();
877
1244
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1245
+ const navigate = useNavigate();
1246
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
878
1247
  const [deleteDocument] = useDeleteDocumentMutation();
879
1248
  const _delete = React.useCallback(
880
1249
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1189,6 +1558,7 @@ const useDocumentActions = () => {
1189
1558
  defaultMessage: "Saved document"
1190
1559
  })
1191
1560
  });
1561
+ setCurrentStep("contentManager.success");
1192
1562
  return res.data;
1193
1563
  } catch (err) {
1194
1564
  toggleNotification({
@@ -1210,7 +1580,6 @@ const useDocumentActions = () => {
1210
1580
  sourceId
1211
1581
  });
1212
1582
  if ("error" in res) {
1213
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1214
1583
  return { error: res.error };
1215
1584
  }
1216
1585
  toggleNotification({
@@ -1229,7 +1598,7 @@ const useDocumentActions = () => {
1229
1598
  throw err;
1230
1599
  }
1231
1600
  },
1232
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1601
+ [autoCloneDocument, formatMessage, toggleNotification]
1233
1602
  );
1234
1603
  const [cloneDocument] = useCloneDocumentMutation();
1235
1604
  const clone = React.useCallback(
@@ -1255,6 +1624,7 @@ const useDocumentActions = () => {
1255
1624
  defaultMessage: "Cloned document"
1256
1625
  })
1257
1626
  });
1627
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1258
1628
  return res.data;
1259
1629
  } catch (err) {
1260
1630
  toggleNotification({
@@ -1265,7 +1635,7 @@ const useDocumentActions = () => {
1265
1635
  throw err;
1266
1636
  }
1267
1637
  },
1268
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1638
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1269
1639
  );
1270
1640
  const [getDoc] = useLazyGetDocumentQuery();
1271
1641
  const getDocument = React.useCallback(
@@ -1290,10 +1660,10 @@ const useDocumentActions = () => {
1290
1660
  update
1291
1661
  };
1292
1662
  };
1293
- const ProtectedHistoryPage = lazy(
1294
- () => import("./History-DalgFQ3D.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1663
+ const ProtectedHistoryPage = React.lazy(
1664
+ () => import("./History-CfCSNlG9.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1295
1665
  );
1296
- const routes$1 = [
1666
+ const routes$2 = [
1297
1667
  {
1298
1668
  path: ":collectionType/:slug/:id/history",
1299
1669
  Component: ProtectedHistoryPage
@@ -1303,32 +1673,45 @@ const routes$1 = [
1303
1673
  Component: ProtectedHistoryPage
1304
1674
  }
1305
1675
  ];
1676
+ const ProtectedPreviewPage = React.lazy(
1677
+ () => import("./Preview-C_B1nx3g.mjs").then((mod) => ({ default: mod.ProtectedPreviewPage }))
1678
+ );
1679
+ const routes$1 = [
1680
+ {
1681
+ path: ":collectionType/:slug/:id/preview",
1682
+ Component: ProtectedPreviewPage
1683
+ },
1684
+ {
1685
+ path: ":collectionType/:slug/preview",
1686
+ Component: ProtectedPreviewPage
1687
+ }
1688
+ ];
1306
1689
  const ProtectedEditViewPage = lazy(
1307
- () => import("./EditViewPage-DhmAg0NK.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1690
+ () => import("./EditViewPage-B11aeMcf.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1308
1691
  );
1309
1692
  const ProtectedListViewPage = lazy(
1310
- () => import("./ListViewPage-BkAwIW9s.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1693
+ () => import("./ListViewPage-BxLVROX8.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1311
1694
  );
1312
1695
  const ProtectedListConfiguration = lazy(
1313
- () => import("./ListConfigurationPage-DWy-vRzs.mjs").then((mod) => ({
1696
+ () => import("./ListConfigurationPage-0mtv_iqk.mjs").then((mod) => ({
1314
1697
  default: mod.ProtectedListConfiguration
1315
1698
  }))
1316
1699
  );
1317
1700
  const ProtectedEditConfigurationPage = lazy(
1318
- () => import("./EditConfigurationPage-MItFGzT9.mjs").then((mod) => ({
1701
+ () => import("./EditConfigurationPage-CKK-5LfX.mjs").then((mod) => ({
1319
1702
  default: mod.ProtectedEditConfigurationPage
1320
1703
  }))
1321
1704
  );
1322
1705
  const ProtectedComponentConfigurationPage = lazy(
1323
- () => import("./ComponentConfigurationPage-C7ImeKGM.mjs").then((mod) => ({
1706
+ () => import("./ComponentConfigurationPage-BaJMOQyq.mjs").then((mod) => ({
1324
1707
  default: mod.ProtectedComponentConfigurationPage
1325
1708
  }))
1326
1709
  );
1327
1710
  const NoPermissions = lazy(
1328
- () => import("./NoPermissionsPage-Bt_HWGat.mjs").then((mod) => ({ default: mod.NoPermissions }))
1711
+ () => import("./NoPermissionsPage-CV9V8KWa.mjs").then((mod) => ({ default: mod.NoPermissions }))
1329
1712
  );
1330
1713
  const NoContentType = lazy(
1331
- () => import("./NoContentTypePage-B9BCNNdL.mjs").then((mod) => ({ default: mod.NoContentType }))
1714
+ () => import("./NoContentTypePage-BRfDd67_.mjs").then((mod) => ({ default: mod.NoContentType }))
1332
1715
  );
1333
1716
  const CollectionTypePages = () => {
1334
1717
  const { collectionType } = useParams();
@@ -1340,7 +1723,7 @@ const CollectionTypePages = () => {
1340
1723
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1341
1724
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1342
1725
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1343
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1726
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1344
1727
  const routes = [
1345
1728
  {
1346
1729
  path: LIST_RELATIVE_PATH,
@@ -1374,6 +1757,7 @@ const routes = [
1374
1757
  path: "no-content-types",
1375
1758
  Component: NoContentType
1376
1759
  },
1760
+ ...routes$2,
1377
1761
  ...routes$1
1378
1762
  ];
1379
1763
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1442,12 +1826,14 @@ const DocumentActionButton = (action) => {
1442
1826
  /* @__PURE__ */ jsx(
1443
1827
  Button,
1444
1828
  {
1445
- flex: 1,
1829
+ flex: "auto",
1446
1830
  startIcon: action.icon,
1447
1831
  disabled: action.disabled,
1448
1832
  onClick: handleClick(action),
1449
1833
  justifyContent: "center",
1450
1834
  variant: action.variant || "default",
1835
+ paddingTop: "7px",
1836
+ paddingBottom: "7px",
1451
1837
  children: action.label
1452
1838
  }
1453
1839
  ),
@@ -1455,7 +1841,7 @@ const DocumentActionButton = (action) => {
1455
1841
  DocumentActionConfirmDialog,
1456
1842
  {
1457
1843
  ...action.dialog,
1458
- variant: action.variant,
1844
+ variant: action.dialog?.variant ?? action.variant,
1459
1845
  isOpen: dialogId === action.id,
1460
1846
  onClose: handleClose
1461
1847
  }
@@ -1470,6 +1856,11 @@ const DocumentActionButton = (action) => {
1470
1856
  ) : null
1471
1857
  ] });
1472
1858
  };
1859
+ const MenuItem = styled(Menu.Item)`
1860
+ &:hover {
1861
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
1862
+ }
1863
+ `;
1473
1864
  const DocumentActionsMenu = ({
1474
1865
  actions: actions2,
1475
1866
  children,
@@ -1512,9 +1903,9 @@ const DocumentActionsMenu = ({
1512
1903
  disabled: isDisabled,
1513
1904
  size: "S",
1514
1905
  endIcon: null,
1515
- paddingTop: "7px",
1516
- paddingLeft: "9px",
1517
- paddingRight: "9px",
1906
+ paddingTop: "4px",
1907
+ paddingLeft: "7px",
1908
+ paddingRight: "7px",
1518
1909
  variant,
1519
1910
  children: [
1520
1911
  /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
@@ -1525,36 +1916,35 @@ const DocumentActionsMenu = ({
1525
1916
  ]
1526
1917
  }
1527
1918
  ),
1528
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1919
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1529
1920
  actions2.map((action) => {
1530
1921
  return /* @__PURE__ */ jsx(
1531
- Menu.Item,
1922
+ MenuItem,
1532
1923
  {
1533
1924
  disabled: action.disabled,
1534
1925
  onSelect: handleClick(action),
1535
1926
  display: "block",
1536
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1537
- /* @__PURE__ */ jsxs(Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1538
- /* @__PURE__ */ jsx(Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1539
- action.label
1540
- ] }),
1541
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1542
- Flex,
1543
- {
1544
- alignItems: "center",
1545
- background: "alternative100",
1546
- borderStyle: "solid",
1547
- borderColor: "alternative200",
1548
- borderWidth: "1px",
1549
- height: 5,
1550
- paddingLeft: 2,
1551
- paddingRight: 2,
1552
- hasRadius: true,
1553
- color: "alternative600",
1554
- children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1555
- }
1556
- )
1557
- ] })
1927
+ isVariantDanger: action.variant === "danger",
1928
+ isDisabled: action.disabled,
1929
+ children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxs(
1930
+ Flex,
1931
+ {
1932
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1933
+ gap: 2,
1934
+ tag: "span",
1935
+ children: [
1936
+ /* @__PURE__ */ jsx(
1937
+ Flex,
1938
+ {
1939
+ tag: "span",
1940
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1941
+ children: action.icon
1942
+ }
1943
+ ),
1944
+ action.label
1945
+ ]
1946
+ }
1947
+ ) })
1558
1948
  },
1559
1949
  action.id
1560
1950
  );
@@ -1634,11 +2024,11 @@ const DocumentActionConfirmDialog = ({
1634
2024
  /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1635
2025
  /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1636
2026
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1637
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
2027
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1638
2028
  id: "app.components.Button.cancel",
1639
2029
  defaultMessage: "Cancel"
1640
2030
  }) }) }),
1641
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
2031
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1642
2032
  id: "app.components.Button.confirm",
1643
2033
  defaultMessage: "Confirm"
1644
2034
  }) })
@@ -1661,10 +2051,22 @@ const DocumentActionModal = ({
1661
2051
  };
1662
2052
  return /* @__PURE__ */ jsx(Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
1663
2053
  /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: title }) }),
1664
- /* @__PURE__ */ jsx(Modal.Body, { children: typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content }),
1665
- /* @__PURE__ */ jsx(Modal.Footer, { children: typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer })
2054
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsx(Modal.Body, { children: Content }),
2055
+ typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1666
2056
  ] }) });
1667
2057
  };
2058
+ const transformData = (data) => {
2059
+ if (Array.isArray(data)) {
2060
+ return data.map(transformData);
2061
+ }
2062
+ if (typeof data === "object" && data !== null) {
2063
+ if ("apiData" in data) {
2064
+ return data.apiData;
2065
+ }
2066
+ return mapValues(transformData)(data);
2067
+ }
2068
+ return data;
2069
+ };
1668
2070
  const PublishAction$1 = ({
1669
2071
  activeTab,
1670
2072
  documentId,
@@ -1677,13 +2079,18 @@ const PublishAction$1 = ({
1677
2079
  const navigate = useNavigate();
1678
2080
  const { toggleNotification } = useNotification();
1679
2081
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2082
+ const isListView = useMatch(LIST_PATH) !== null;
1680
2083
  const isCloning = useMatch(CLONE_PATH) !== null;
2084
+ const { id } = useParams();
1681
2085
  const { formatMessage } = useIntl();
1682
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1683
- "PublishAction",
1684
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1685
- );
2086
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1686
2087
  const { publish } = useDocumentActions();
2088
+ const [
2089
+ countDraftRelations,
2090
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2091
+ ] = useLazyGetDraftRelationCountQuery();
2092
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React.useState(0);
2093
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React.useState(0);
1687
2094
  const [{ query, rawQuery }] = useQueryParams();
1688
2095
  const params = React.useMemo(() => buildValidParams(query), [query]);
1689
2096
  const modified = useForm("PublishAction", ({ modified: modified2 }) => modified2);
@@ -1692,10 +2099,107 @@ const PublishAction$1 = ({
1692
2099
  const validate = useForm("PublishAction", (state) => state.validate);
1693
2100
  const setErrors = useForm("PublishAction", (state) => state.setErrors);
1694
2101
  const formValues = useForm("PublishAction", ({ values }) => values);
1695
- const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
2102
+ React.useEffect(() => {
2103
+ if (isErrorDraftRelations) {
2104
+ toggleNotification({
2105
+ type: "danger",
2106
+ message: formatMessage({
2107
+ id: getTranslation("error.records.fetch-draft-relatons"),
2108
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2109
+ })
2110
+ });
2111
+ }
2112
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2113
+ React.useEffect(() => {
2114
+ const localDraftRelations = /* @__PURE__ */ new Set();
2115
+ const extractDraftRelations = (data) => {
2116
+ const relations = data.connect || [];
2117
+ relations.forEach((relation) => {
2118
+ if (relation.status === "draft") {
2119
+ localDraftRelations.add(relation.id);
2120
+ }
2121
+ });
2122
+ };
2123
+ const traverseAndExtract = (data) => {
2124
+ Object.entries(data).forEach(([key, value]) => {
2125
+ if (key === "connect" && Array.isArray(value)) {
2126
+ extractDraftRelations({ connect: value });
2127
+ } else if (typeof value === "object" && value !== null) {
2128
+ traverseAndExtract(value);
2129
+ }
2130
+ });
2131
+ };
2132
+ if (!documentId || modified) {
2133
+ traverseAndExtract(formValues);
2134
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2135
+ }
2136
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2137
+ React.useEffect(() => {
2138
+ if (!document || !document.documentId || isListView) {
2139
+ return;
2140
+ }
2141
+ const fetchDraftRelationsCount = async () => {
2142
+ const { data, error } = await countDraftRelations({
2143
+ collectionType,
2144
+ model,
2145
+ documentId,
2146
+ params
2147
+ });
2148
+ if (error) {
2149
+ throw error;
2150
+ }
2151
+ if (data) {
2152
+ setServerCountOfDraftRelations(data.data);
2153
+ }
2154
+ };
2155
+ fetchDraftRelationsCount();
2156
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
2157
+ const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1696
2158
  if (!schema?.options?.draftAndPublish) {
1697
2159
  return null;
1698
2160
  }
2161
+ const performPublish = async () => {
2162
+ setSubmitting(true);
2163
+ try {
2164
+ const { errors } = await validate(true, {
2165
+ status: "published"
2166
+ });
2167
+ if (errors) {
2168
+ toggleNotification({
2169
+ type: "danger",
2170
+ message: formatMessage({
2171
+ id: "content-manager.validation.error",
2172
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2173
+ })
2174
+ });
2175
+ return;
2176
+ }
2177
+ const res = await publish(
2178
+ {
2179
+ collectionType,
2180
+ model,
2181
+ documentId,
2182
+ params
2183
+ },
2184
+ transformData(formValues)
2185
+ );
2186
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2187
+ if (id === "create") {
2188
+ navigate({
2189
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2190
+ search: rawQuery
2191
+ });
2192
+ }
2193
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2194
+ setErrors(formatValidationErrors(res.error));
2195
+ }
2196
+ } finally {
2197
+ setSubmitting(false);
2198
+ }
2199
+ };
2200
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2201
+ const enableDraftRelationsCount = false;
2202
+ const hasDraftRelations = enableDraftRelationsCount;
1699
2203
  return {
1700
2204
  /**
1701
2205
  * Disabled when:
@@ -1705,49 +2209,36 @@ const PublishAction$1 = ({
1705
2209
  * - the document is already published & not modified
1706
2210
  * - the document is being created & not modified
1707
2211
  * - the user doesn't have the permission to publish
1708
- * - the user doesn't have the permission to create a new document
1709
- * - the user doesn't have the permission to update the document
1710
2212
  */
1711
- disabled: isCloning || isSubmitting || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2213
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1712
2214
  label: formatMessage({
1713
2215
  id: "app.utils.publish",
1714
2216
  defaultMessage: "Publish"
1715
2217
  }),
1716
2218
  onClick: async () => {
1717
- setSubmitting(true);
1718
- try {
1719
- const { errors } = await validate();
1720
- if (errors) {
1721
- toggleNotification({
1722
- type: "danger",
1723
- message: formatMessage({
1724
- id: "content-manager.validation.error",
1725
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1726
- })
1727
- });
1728
- return;
1729
- }
1730
- const res = await publish(
1731
- {
1732
- collectionType,
1733
- model,
1734
- documentId,
1735
- params
1736
- },
1737
- formValues
1738
- );
1739
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1740
- navigate({
1741
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1742
- search: rawQuery
1743
- });
1744
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1745
- setErrors(formatValidationErrors(res.error));
2219
+ await performPublish();
2220
+ },
2221
+ dialog: hasDraftRelations ? {
2222
+ type: "dialog",
2223
+ variant: "danger",
2224
+ footer: null,
2225
+ title: formatMessage({
2226
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2227
+ defaultMessage: "Confirmation"
2228
+ }),
2229
+ content: formatMessage(
2230
+ {
2231
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2232
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2233
+ },
2234
+ {
2235
+ count: totalDraftRelations
1746
2236
  }
1747
- } finally {
1748
- setSubmitting(false);
2237
+ ),
2238
+ onConfirm: async () => {
2239
+ await performPublish();
1749
2240
  }
1750
- }
2241
+ } : void 0
1751
2242
  };
1752
2243
  };
1753
2244
  PublishAction$1.type = "publish";
@@ -1763,10 +2254,6 @@ const UpdateAction = ({
1763
2254
  const cloneMatch = useMatch(CLONE_PATH);
1764
2255
  const isCloning = cloneMatch !== null;
1765
2256
  const { formatMessage } = useIntl();
1766
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1767
- canCreate: canCreate2,
1768
- canUpdate: canUpdate2
1769
- }));
1770
2257
  const { create, update, clone } = useDocumentActions();
1771
2258
  const [{ query, rawQuery }] = useQueryParams();
1772
2259
  const params = React.useMemo(() => buildValidParams(query), [query]);
@@ -1783,18 +2270,18 @@ const UpdateAction = ({
1783
2270
  * - the form is submitting
1784
2271
  * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1785
2272
  * - the active tab is the published tab
1786
- * - the user doesn't have the permission to create a new document
1787
- * - the user doesn't have the permission to update the document
1788
2273
  */
1789
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
2274
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1790
2275
  label: formatMessage({
1791
- id: "content-manager.containers.Edit.save",
2276
+ id: "global.save",
1792
2277
  defaultMessage: "Save"
1793
2278
  }),
1794
2279
  onClick: async () => {
1795
2280
  setSubmitting(true);
1796
2281
  try {
1797
- const { errors } = await validate();
2282
+ const { errors } = await validate(true, {
2283
+ status: "draft"
2284
+ });
1798
2285
  if (errors) {
1799
2286
  toggleNotification({
1800
2287
  type: "danger",
@@ -1812,13 +2299,16 @@ const UpdateAction = ({
1812
2299
  documentId: cloneMatch.params.origin,
1813
2300
  params
1814
2301
  },
1815
- document
2302
+ transformData(document)
1816
2303
  );
1817
2304
  if ("data" in res) {
1818
- navigate({
1819
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1820
- search: rawQuery
1821
- });
2305
+ navigate(
2306
+ {
2307
+ pathname: `../${res.data.documentId}`,
2308
+ search: rawQuery
2309
+ },
2310
+ { relative: "path" }
2311
+ );
1822
2312
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1823
2313
  setErrors(formatValidationErrors(res.error));
1824
2314
  }
@@ -1830,7 +2320,7 @@ const UpdateAction = ({
1830
2320
  documentId,
1831
2321
  params
1832
2322
  },
1833
- document
2323
+ transformData(document)
1834
2324
  );
1835
2325
  if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1836
2326
  setErrors(formatValidationErrors(res.error));
@@ -1843,15 +2333,15 @@ const UpdateAction = ({
1843
2333
  model,
1844
2334
  params
1845
2335
  },
1846
- document
2336
+ transformData(document)
1847
2337
  );
1848
2338
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1849
2339
  navigate(
1850
2340
  {
1851
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2341
+ pathname: `../${res.data.documentId}`,
1852
2342
  search: rawQuery
1853
2343
  },
1854
- { replace: true }
2344
+ { replace: true, relative: "path" }
1855
2345
  );
1856
2346
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1857
2347
  setErrors(formatValidationErrors(res.error));
@@ -1896,7 +2386,7 @@ const UnpublishAction$1 = ({
1896
2386
  id: "app.utils.unpublish",
1897
2387
  defaultMessage: "Unpublish"
1898
2388
  }),
1899
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2389
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1900
2390
  onClick: async () => {
1901
2391
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1902
2392
  if (!documentId) {
@@ -2008,7 +2498,7 @@ const DiscardAction = ({
2008
2498
  id: "content-manager.actions.discard.label",
2009
2499
  defaultMessage: "Discard changes"
2010
2500
  }),
2011
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2501
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2012
2502
  position: ["panel", "table-row"],
2013
2503
  variant: "danger",
2014
2504
  dialog: {
@@ -2036,11 +2526,6 @@ const DiscardAction = ({
2036
2526
  };
2037
2527
  };
2038
2528
  DiscardAction.type = "discard";
2039
- const StyledCrossCircle = styled(CrossCircle)`
2040
- path {
2041
- fill: currentColor;
2042
- }
2043
- `;
2044
2529
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2045
2530
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2046
2531
  const RelativeTime = React.forwardRef(
@@ -2053,7 +2538,7 @@ const RelativeTime = React.forwardRef(
2053
2538
  });
2054
2539
  const unit = intervals.find((intervalUnit) => {
2055
2540
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2056
- });
2541
+ }) ?? "seconds";
2057
2542
  const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2058
2543
  const customInterval = customIntervals.find(
2059
2544
  (custom) => interval[custom.unit] < custom.threshold
@@ -2087,34 +2572,34 @@ const getDisplayName = ({
2087
2572
  return email ?? "";
2088
2573
  };
2089
2574
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2090
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2091
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2092
- return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2575
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2576
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2577
+ const { formatMessage } = useIntl();
2578
+ return /* @__PURE__ */ jsx(Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2579
+ id: `content-manager.containers.List.${status}`,
2580
+ defaultMessage: capitalise(status)
2581
+ }) }) });
2093
2582
  };
2094
2583
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2095
2584
  const { formatMessage } = useIntl();
2096
2585
  const isCloning = useMatch(CLONE_PATH) !== null;
2586
+ const params = useParams();
2097
2587
  const title = isCreating ? formatMessage({
2098
2588
  id: "content-manager.containers.edit.title.new",
2099
2589
  defaultMessage: "Create an entry"
2100
2590
  }) : documentTitle;
2101
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 8, paddingBottom: 4, gap: 3, children: [
2102
- /* @__PURE__ */ jsx(BackButton, {}),
2103
- /* @__PURE__ */ jsxs(
2104
- Flex,
2591
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2592
+ /* @__PURE__ */ jsx(
2593
+ BackButton,
2105
2594
  {
2106
- width: "100%",
2107
- justifyContent: "space-between",
2108
- paddingTop: 1,
2109
- gap: "80px",
2110
- alignItems: "flex-start",
2111
- children: [
2112
- /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2113
- /* @__PURE__ */ jsx(HeaderToolbar, {})
2114
- ]
2595
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2115
2596
  }
2116
2597
  ),
2117
- status ? /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) : null
2598
+ /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2599
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2600
+ /* @__PURE__ */ jsx(HeaderToolbar, {})
2601
+ ] }),
2602
+ status ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2118
2603
  ] });
2119
2604
  };
2120
2605
  const HeaderToolbar = () => {
@@ -2197,12 +2682,12 @@ const Information = ({ activeTab }) => {
2197
2682
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2198
2683
  label: formatMessage({
2199
2684
  id: "content-manager.containers.edit.information.last-published.label",
2200
- defaultMessage: "Last published"
2685
+ defaultMessage: "Published"
2201
2686
  }),
2202
2687
  value: formatMessage(
2203
2688
  {
2204
2689
  id: "content-manager.containers.edit.information.last-published.value",
2205
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2690
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2206
2691
  },
2207
2692
  {
2208
2693
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2215,12 +2700,12 @@ const Information = ({ activeTab }) => {
2215
2700
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2216
2701
  label: formatMessage({
2217
2702
  id: "content-manager.containers.edit.information.last-draft.label",
2218
- defaultMessage: "Last draft"
2703
+ defaultMessage: "Updated"
2219
2704
  }),
2220
2705
  value: formatMessage(
2221
2706
  {
2222
2707
  id: "content-manager.containers.edit.information.last-draft.value",
2223
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2708
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2224
2709
  },
2225
2710
  {
2226
2711
  time: /* @__PURE__ */ jsx(
@@ -2238,12 +2723,12 @@ const Information = ({ activeTab }) => {
2238
2723
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2239
2724
  label: formatMessage({
2240
2725
  id: "content-manager.containers.edit.information.document.label",
2241
- defaultMessage: "Document"
2726
+ defaultMessage: "Created"
2242
2727
  }),
2243
2728
  value: formatMessage(
2244
2729
  {
2245
2730
  id: "content-manager.containers.edit.information.document.value",
2246
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2731
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2247
2732
  },
2248
2733
  {
2249
2734
  time: /* @__PURE__ */ jsx(
@@ -2281,25 +2766,77 @@ const Information = ({ activeTab }) => {
2281
2766
  );
2282
2767
  };
2283
2768
  const HeaderActions = ({ actions: actions2 }) => {
2284
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2285
- if ("options" in action) {
2769
+ const [dialogId, setDialogId] = React.useState(null);
2770
+ const handleClick = (action) => async (e) => {
2771
+ if (!("options" in action)) {
2772
+ const { onClick = () => false, dialog, id } = action;
2773
+ const muteDialog = await onClick(e);
2774
+ if (dialog && !muteDialog) {
2775
+ e.preventDefault();
2776
+ setDialogId(id);
2777
+ }
2778
+ }
2779
+ };
2780
+ const handleClose = () => {
2781
+ setDialogId(null);
2782
+ };
2783
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2784
+ if (action.options) {
2286
2785
  return /* @__PURE__ */ jsx(
2287
2786
  SingleSelect,
2288
2787
  {
2289
2788
  size: "S",
2290
- disabled: action.disabled,
2291
- "aria-label": action.label,
2292
2789
  onChange: action.onSelect,
2293
- value: action.value,
2790
+ "aria-label": action.label,
2791
+ ...action,
2294
2792
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2295
2793
  },
2296
2794
  action.id
2297
2795
  );
2298
2796
  } else {
2299
- return null;
2797
+ if (action.type === "icon") {
2798
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
2799
+ /* @__PURE__ */ jsx(
2800
+ IconButton,
2801
+ {
2802
+ disabled: action.disabled,
2803
+ label: action.label,
2804
+ size: "S",
2805
+ onClick: handleClick(action),
2806
+ children: action.icon
2807
+ }
2808
+ ),
2809
+ action.dialog ? /* @__PURE__ */ jsx(
2810
+ HeaderActionDialog,
2811
+ {
2812
+ ...action.dialog,
2813
+ isOpen: dialogId === action.id,
2814
+ onClose: handleClose
2815
+ }
2816
+ ) : null
2817
+ ] }, action.id);
2818
+ }
2300
2819
  }
2301
2820
  }) });
2302
2821
  };
2822
+ const HeaderActionDialog = ({
2823
+ onClose,
2824
+ onCancel,
2825
+ title,
2826
+ content: Content,
2827
+ isOpen
2828
+ }) => {
2829
+ const handleClose = async () => {
2830
+ if (onCancel) {
2831
+ await onCancel();
2832
+ }
2833
+ onClose();
2834
+ };
2835
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2836
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2837
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
2838
+ ] }) });
2839
+ };
2303
2840
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2304
2841
  const navigate = useNavigate();
2305
2842
  const { formatMessage } = useIntl();
@@ -2340,12 +2877,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2340
2877
  const { delete: deleteAction } = useDocumentActions();
2341
2878
  const { toggleNotification } = useNotification();
2342
2879
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2880
+ const isLocalized = document?.locale != null;
2343
2881
  return {
2344
2882
  disabled: !canDelete || !document,
2345
- label: formatMessage({
2346
- id: "content-manager.actions.delete.label",
2347
- defaultMessage: "Delete document"
2348
- }),
2883
+ label: formatMessage(
2884
+ {
2885
+ id: "content-manager.actions.delete.label",
2886
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2887
+ },
2888
+ { isLocalized }
2889
+ ),
2349
2890
  icon: /* @__PURE__ */ jsx(Trash, {}),
2350
2891
  dialog: {
2351
2892
  type: "dialog",
@@ -2396,408 +2937,106 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2396
2937
  }
2397
2938
  }
2398
2939
  },
2399
- variant: "danger",
2400
- position: ["header", "table-row"]
2401
- };
2402
- };
2403
- DeleteAction$1.type = "delete";
2404
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2405
- const Panels = () => {
2406
- const isCloning = useMatch(CLONE_PATH) !== null;
2407
- const [
2408
- {
2409
- query: { status }
2410
- }
2411
- ] = useQueryParams({
2412
- status: "draft"
2413
- });
2414
- const { model, id, document, meta, collectionType } = useDoc();
2415
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2416
- const props = {
2417
- activeTab: status,
2418
- model,
2419
- documentId: id,
2420
- document: isCloning ? void 0 : document,
2421
- meta: isCloning ? void 0 : meta,
2422
- collectionType
2423
- };
2424
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2425
- DescriptionComponentRenderer,
2426
- {
2427
- props,
2428
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2429
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2430
- }
2431
- ) });
2432
- };
2433
- const ActionsPanel = () => {
2434
- const { formatMessage } = useIntl();
2435
- return {
2436
- title: formatMessage({
2437
- id: "content-manager.containers.edit.panels.default.title",
2438
- defaultMessage: "Document"
2439
- }),
2440
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2441
- };
2442
- };
2443
- ActionsPanel.type = "actions";
2444
- const ActionsPanelContent = () => {
2445
- const isCloning = useMatch(CLONE_PATH) !== null;
2446
- const [
2447
- {
2448
- query: { status = "draft" }
2449
- }
2450
- ] = useQueryParams();
2451
- const { model, id, document, meta, collectionType } = useDoc();
2452
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2453
- const props = {
2454
- activeTab: status,
2455
- model,
2456
- documentId: id,
2457
- document: isCloning ? void 0 : document,
2458
- meta: isCloning ? void 0 : meta,
2459
- collectionType
2460
- };
2461
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2462
- /* @__PURE__ */ jsx(
2463
- DescriptionComponentRenderer,
2464
- {
2465
- props,
2466
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2467
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2468
- }
2469
- ),
2470
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2471
- ] });
2472
- };
2473
- const Panel = React.forwardRef(({ children, title }, ref) => {
2474
- return /* @__PURE__ */ jsxs(
2475
- Flex,
2476
- {
2477
- ref,
2478
- tag: "aside",
2479
- "aria-labelledby": "additional-information",
2480
- background: "neutral0",
2481
- borderColor: "neutral150",
2482
- hasRadius: true,
2483
- paddingBottom: 4,
2484
- paddingLeft: 4,
2485
- paddingRight: 4,
2486
- paddingTop: 4,
2487
- shadow: "tableShadow",
2488
- gap: 3,
2489
- direction: "column",
2490
- justifyContent: "stretch",
2491
- alignItems: "flex-start",
2492
- children: [
2493
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2494
- children
2495
- ]
2496
- }
2497
- );
2498
- });
2499
- const HOOKS = {
2500
- /**
2501
- * Hook that allows to mutate the displayed headers of the list view table
2502
- * @constant
2503
- * @type {string}
2504
- */
2505
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2506
- /**
2507
- * Hook that allows to mutate the CM's collection types links pre-set filters
2508
- * @constant
2509
- * @type {string}
2510
- */
2511
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2512
- /**
2513
- * Hook that allows to mutate the CM's edit view layout
2514
- * @constant
2515
- * @type {string}
2516
- */
2517
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2518
- /**
2519
- * Hook that allows to mutate the CM's single types links pre-set filters
2520
- * @constant
2521
- * @type {string}
2522
- */
2523
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2524
- };
2525
- const contentTypesApi = contentManagerApi.injectEndpoints({
2526
- endpoints: (builder) => ({
2527
- getContentTypeConfiguration: builder.query({
2528
- query: (uid) => ({
2529
- url: `/content-manager/content-types/${uid}/configuration`,
2530
- method: "GET"
2531
- }),
2532
- transformResponse: (response) => response.data,
2533
- providesTags: (_result, _error, uid) => [
2534
- { type: "ContentTypesConfiguration", id: uid },
2535
- { type: "ContentTypeSettings", id: "LIST" }
2536
- ]
2537
- }),
2538
- getAllContentTypeSettings: builder.query({
2539
- query: () => "/content-manager/content-types-settings",
2540
- transformResponse: (response) => response.data,
2541
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2542
- }),
2543
- updateContentTypeConfiguration: builder.mutation({
2544
- query: ({ uid, ...body }) => ({
2545
- url: `/content-manager/content-types/${uid}/configuration`,
2546
- method: "PUT",
2547
- data: body
2548
- }),
2549
- transformResponse: (response) => response.data,
2550
- invalidatesTags: (_result, _error, { uid }) => [
2551
- { type: "ContentTypesConfiguration", id: uid },
2552
- { type: "ContentTypeSettings", id: "LIST" },
2553
- // Is this necessary?
2554
- { type: "InitialData" }
2555
- ]
2556
- })
2557
- })
2558
- });
2559
- const {
2560
- useGetContentTypeConfigurationQuery,
2561
- useGetAllContentTypeSettingsQuery,
2562
- useUpdateContentTypeConfigurationMutation
2563
- } = contentTypesApi;
2564
- const checkIfAttributeIsDisplayable = (attribute) => {
2565
- const { type } = attribute;
2566
- if (type === "relation") {
2567
- return !attribute.relation.toLowerCase().includes("morph");
2568
- }
2569
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2570
- };
2571
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2572
- if (!mainFieldName) {
2573
- return void 0;
2574
- }
2575
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2576
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2577
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2578
- );
2579
- return {
2580
- name: mainFieldName,
2581
- type: mainFieldType ?? "string"
2582
- };
2583
- };
2584
- const DEFAULT_SETTINGS = {
2585
- bulkable: false,
2586
- filterable: false,
2587
- searchable: false,
2588
- pagination: false,
2589
- defaultSortBy: "",
2590
- defaultSortOrder: "asc",
2591
- mainField: "id",
2592
- pageSize: 10
2593
- };
2594
- const useDocumentLayout = (model) => {
2595
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2596
- const [{ query }] = useQueryParams();
2597
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2598
- const { toggleNotification } = useNotification();
2599
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2600
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2601
- const {
2602
- data,
2603
- isLoading: isLoadingConfigs,
2604
- error,
2605
- isFetching: isFetchingConfigs
2606
- } = useGetContentTypeConfigurationQuery(model);
2607
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2608
- React.useEffect(() => {
2609
- if (error) {
2610
- toggleNotification({
2611
- type: "danger",
2612
- message: formatAPIError(error)
2613
- });
2614
- }
2615
- }, [error, formatAPIError, toggleNotification]);
2616
- const editLayout = React.useMemo(
2617
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2618
- layout: [],
2619
- components: {},
2620
- metadatas: {},
2621
- options: {},
2622
- settings: DEFAULT_SETTINGS
2623
- },
2624
- [data, isLoading, schemas, schema, components]
2625
- );
2626
- const listLayout = React.useMemo(() => {
2627
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2628
- layout: [],
2629
- metadatas: {},
2630
- options: {},
2631
- settings: DEFAULT_SETTINGS
2632
- };
2633
- }, [data, isLoading, schemas, schema, components]);
2634
- const { layout: edit } = React.useMemo(
2635
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2636
- layout: editLayout,
2637
- query
2638
- }),
2639
- [editLayout, query, runHookWaterfall]
2640
- );
2641
- return {
2642
- error,
2643
- isLoading,
2644
- edit,
2645
- list: listLayout
2646
- };
2647
- };
2648
- const useDocLayout = () => {
2649
- const { model } = useDoc();
2650
- return useDocumentLayout(model);
2651
- };
2652
- const formatEditLayout = (data, {
2653
- schemas,
2654
- schema,
2655
- components
2656
- }) => {
2657
- let currentPanelIndex = 0;
2658
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2659
- data.contentType.layouts.edit,
2660
- schema?.attributes,
2661
- data.contentType.metadatas,
2662
- { configurations: data.components, schemas: components },
2663
- schemas
2664
- ).reduce((panels, row) => {
2665
- if (row.some((field) => field.type === "dynamiczone")) {
2666
- panels.push([row]);
2667
- currentPanelIndex += 2;
2668
- } else {
2669
- if (!panels[currentPanelIndex]) {
2670
- panels.push([]);
2671
- }
2672
- panels[currentPanelIndex].push(row);
2673
- }
2674
- return panels;
2675
- }, []);
2676
- const componentEditAttributes = Object.entries(data.components).reduce(
2677
- (acc, [uid, configuration]) => {
2678
- acc[uid] = {
2679
- layout: convertEditLayoutToFieldLayouts(
2680
- configuration.layouts.edit,
2681
- components[uid].attributes,
2682
- configuration.metadatas
2683
- ),
2684
- settings: {
2685
- ...configuration.settings,
2686
- icon: components[uid].info.icon,
2687
- displayName: components[uid].info.displayName
2688
- }
2689
- };
2690
- return acc;
2691
- },
2692
- {}
2693
- );
2694
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2695
- (acc, [attribute, metadata]) => {
2696
- return {
2697
- ...acc,
2698
- [attribute]: metadata.edit
2699
- };
2700
- },
2701
- {}
2702
- );
2703
- return {
2704
- layout: panelledEditAttributes,
2705
- components: componentEditAttributes,
2706
- metadatas: editMetadatas,
2707
- settings: {
2708
- ...data.contentType.settings,
2709
- displayName: schema?.info.displayName
2710
- },
2711
- options: {
2712
- ...schema?.options,
2713
- ...schema?.pluginOptions,
2714
- ...data.contentType.options
2715
- }
2940
+ variant: "danger",
2941
+ position: ["header", "table-row"]
2716
2942
  };
2717
2943
  };
2718
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2719
- return rows.map(
2720
- (row) => row.map((field) => {
2721
- const attribute = attributes[field.name];
2722
- if (!attribute) {
2723
- return null;
2724
- }
2725
- const { edit: metadata } = metadatas[field.name];
2726
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2727
- return {
2728
- attribute,
2729
- disabled: !metadata.editable,
2730
- hint: metadata.description,
2731
- label: metadata.label ?? "",
2732
- name: field.name,
2733
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2734
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2735
- schemas,
2736
- components: components?.schemas ?? {}
2737
- }),
2738
- placeholder: metadata.placeholder ?? "",
2739
- required: attribute.required ?? false,
2740
- size: field.size,
2741
- unique: "unique" in attribute ? attribute.unique : false,
2742
- visible: metadata.visible ?? true,
2743
- type: attribute.type
2744
- };
2745
- }).filter((field) => field !== null)
2746
- );
2944
+ DeleteAction$1.type = "delete";
2945
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2946
+ const Panels = () => {
2947
+ const isCloning = useMatch(CLONE_PATH) !== null;
2948
+ const [
2949
+ {
2950
+ query: { status }
2951
+ }
2952
+ ] = useQueryParams({
2953
+ status: "draft"
2954
+ });
2955
+ const { model, id, document, meta, collectionType } = useDoc();
2956
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
2957
+ const props = {
2958
+ activeTab: status,
2959
+ model,
2960
+ documentId: id,
2961
+ document: isCloning ? void 0 : document,
2962
+ meta: isCloning ? void 0 : meta,
2963
+ collectionType
2964
+ };
2965
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2966
+ DescriptionComponentRenderer,
2967
+ {
2968
+ props,
2969
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2970
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2971
+ }
2972
+ ) });
2747
2973
  };
2748
- const formatListLayout = (data, {
2749
- schemas,
2750
- schema,
2751
- components
2752
- }) => {
2753
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2754
- (acc, [attribute, metadata]) => {
2755
- return {
2756
- ...acc,
2757
- [attribute]: metadata.list
2758
- };
2759
- },
2760
- {}
2761
- );
2762
- const listAttributes = convertListLayoutToFieldLayouts(
2763
- data.contentType.layouts.list,
2764
- schema?.attributes,
2765
- listMetadatas,
2766
- { configurations: data.components, schemas: components },
2767
- schemas
2768
- );
2974
+ const ActionsPanel = () => {
2975
+ const { formatMessage } = useIntl();
2769
2976
  return {
2770
- layout: listAttributes,
2771
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2772
- metadatas: listMetadatas,
2773
- options: {
2774
- ...schema?.options,
2775
- ...schema?.pluginOptions,
2776
- ...data.contentType.options
2777
- }
2977
+ title: formatMessage({
2978
+ id: "content-manager.containers.edit.panels.default.title",
2979
+ defaultMessage: "Entry"
2980
+ }),
2981
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2778
2982
  };
2779
2983
  };
2780
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2781
- return columns.map((name) => {
2782
- const attribute = attributes[name];
2783
- if (!attribute) {
2784
- return null;
2984
+ ActionsPanel.type = "actions";
2985
+ const ActionsPanelContent = () => {
2986
+ const isCloning = useMatch(CLONE_PATH) !== null;
2987
+ const [
2988
+ {
2989
+ query: { status = "draft" }
2785
2990
  }
2786
- const metadata = metadatas[name];
2787
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2788
- return {
2789
- attribute,
2790
- label: metadata.label ?? "",
2791
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2792
- schemas,
2793
- components: components?.schemas ?? {}
2794
- }),
2795
- name,
2796
- searchable: metadata.searchable ?? true,
2797
- sortable: metadata.sortable ?? true
2798
- };
2799
- }).filter((field) => field !== null);
2991
+ ] = useQueryParams();
2992
+ const { model, id, document, meta, collectionType } = useDoc();
2993
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2994
+ const props = {
2995
+ activeTab: status,
2996
+ model,
2997
+ documentId: id,
2998
+ document: isCloning ? void 0 : document,
2999
+ meta: isCloning ? void 0 : meta,
3000
+ collectionType
3001
+ };
3002
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
3003
+ /* @__PURE__ */ jsx(
3004
+ DescriptionComponentRenderer,
3005
+ {
3006
+ props,
3007
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
3008
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
3009
+ }
3010
+ ),
3011
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
3012
+ ] });
2800
3013
  };
3014
+ const Panel = React.forwardRef(({ children, title }, ref) => {
3015
+ return /* @__PURE__ */ jsxs(
3016
+ Flex,
3017
+ {
3018
+ ref,
3019
+ tag: "aside",
3020
+ "aria-labelledby": "additional-information",
3021
+ background: "neutral0",
3022
+ borderColor: "neutral150",
3023
+ hasRadius: true,
3024
+ paddingBottom: 4,
3025
+ paddingLeft: 4,
3026
+ paddingRight: 4,
3027
+ paddingTop: 4,
3028
+ shadow: "tableShadow",
3029
+ gap: 3,
3030
+ direction: "column",
3031
+ justifyContent: "stretch",
3032
+ alignItems: "flex-start",
3033
+ children: [
3034
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3035
+ children
3036
+ ]
3037
+ }
3038
+ );
3039
+ });
2801
3040
  const ConfirmBulkActionDialog = ({
2802
3041
  onToggleDialog,
2803
3042
  isOpen = false,
@@ -2805,7 +3044,7 @@ const ConfirmBulkActionDialog = ({
2805
3044
  endAction
2806
3045
  }) => {
2807
3046
  const { formatMessage } = useIntl();
2808
- return /* @__PURE__ */ jsx(Dialog.Root, { onOpenChange: onToggleDialog, open: isOpen, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
3047
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2809
3048
  /* @__PURE__ */ jsx(Dialog.Header, { children: formatMessage({
2810
3049
  id: "app.components.ConfirmDialog.title",
2811
3050
  defaultMessage: "Confirmation"
@@ -2836,6 +3075,7 @@ const ConfirmDialogPublishAll = ({
2836
3075
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2837
3076
  const { model, schema } = useDoc();
2838
3077
  const [{ query }] = useQueryParams();
3078
+ const enableDraftRelationsCount = false;
2839
3079
  const {
2840
3080
  data: countDraftRelations = 0,
2841
3081
  isLoading,
@@ -2847,7 +3087,7 @@ const ConfirmDialogPublishAll = ({
2847
3087
  locale: query?.plugins?.i18n?.locale
2848
3088
  },
2849
3089
  {
2850
- skip: selectedEntries.length === 0
3090
+ skip: !enableDraftRelationsCount
2851
3091
  }
2852
3092
  );
2853
3093
  React.useEffect(() => {
@@ -3032,7 +3272,7 @@ const SelectedEntriesTableContent = ({
3032
3272
  status: row.status
3033
3273
  }
3034
3274
  ) }),
3035
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3275
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3036
3276
  IconButton,
3037
3277
  {
3038
3278
  tag: Link,
@@ -3041,23 +3281,16 @@ const SelectedEntriesTableContent = ({
3041
3281
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3042
3282
  },
3043
3283
  state: { from: pathname },
3044
- label: formatMessage(
3045
- { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3046
- {
3047
- target: formatMessage(
3048
- {
3049
- id: "content-manager.components.ListViewHelperPluginTable.row-line",
3050
- defaultMessage: "item line {number}"
3051
- },
3052
- { number: index2 + 1 }
3053
- )
3054
- }
3055
- ),
3284
+ label: formatMessage({
3285
+ id: "content-manager.bulk-publish.edit",
3286
+ defaultMessage: "Edit"
3287
+ }),
3056
3288
  target: "_blank",
3057
3289
  marginLeft: "auto",
3058
- children: /* @__PURE__ */ jsx(Pencil, {})
3290
+ variant: "ghost",
3291
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3059
3292
  }
3060
- ) })
3293
+ ) }) })
3061
3294
  ] }, row.id)) })
3062
3295
  ] });
3063
3296
  };
@@ -3094,7 +3327,13 @@ const SelectedEntriesModalContent = ({
3094
3327
  );
3095
3328
  const { rows, validationErrors } = React.useMemo(() => {
3096
3329
  if (data.length > 0 && schema) {
3097
- const validate = createYupSchema(schema.attributes, components);
3330
+ const validate = createYupSchema(
3331
+ schema.attributes,
3332
+ components,
3333
+ // Since this is the "Publish" action, the validation
3334
+ // schema must enforce the rules for published entities
3335
+ { status: "published" }
3336
+ );
3098
3337
  const validationErrors2 = {};
3099
3338
  const rows2 = data.map((entry) => {
3100
3339
  try {
@@ -3444,7 +3683,7 @@ const TableActions = ({ document }) => {
3444
3683
  DescriptionComponentRenderer,
3445
3684
  {
3446
3685
  props,
3447
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3686
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3448
3687
  children: (actions2) => {
3449
3688
  const tableRowActions = actions2.filter((action) => {
3450
3689
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3555,7 +3794,7 @@ const CloneAction = ({ model, documentId }) => {
3555
3794
  }),
3556
3795
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3557
3796
  footer: ({ onClose }) => {
3558
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3797
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3559
3798
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3560
3799
  id: "cancel",
3561
3800
  defaultMessage: "Cancel"
@@ -3596,8 +3835,7 @@ class ContentManagerPlugin {
3596
3835
  documentActions = [
3597
3836
  ...DEFAULT_ACTIONS,
3598
3837
  ...DEFAULT_TABLE_ROW_ACTIONS,
3599
- ...DEFAULT_HEADER_ACTIONS,
3600
- HistoryAction
3838
+ ...DEFAULT_HEADER_ACTIONS
3601
3839
  ];
3602
3840
  editViewSidePanels = [ActionsPanel];
3603
3841
  headerActions = [];
@@ -3686,6 +3924,62 @@ const getPrintableType = (value) => {
3686
3924
  }
3687
3925
  return nativeType;
3688
3926
  };
3927
+ const HistoryAction = ({ model, document }) => {
3928
+ const { formatMessage } = useIntl();
3929
+ const [{ query }] = useQueryParams();
3930
+ const navigate = useNavigate();
3931
+ const { trackUsage } = useTracking();
3932
+ const { pathname } = useLocation();
3933
+ const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
3934
+ if (!window.strapi.features.isEnabled("cms-content-history")) {
3935
+ return null;
3936
+ }
3937
+ const handleOnClick = () => {
3938
+ const destination = { pathname: "history", search: pluginsQueryParams };
3939
+ trackUsage("willNavigate", {
3940
+ from: pathname,
3941
+ to: `${pathname}/${destination.pathname}`
3942
+ });
3943
+ navigate(destination);
3944
+ };
3945
+ return {
3946
+ icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
3947
+ label: formatMessage({
3948
+ id: "content-manager.history.document-action",
3949
+ defaultMessage: "Content History"
3950
+ }),
3951
+ onClick: handleOnClick,
3952
+ disabled: (
3953
+ /**
3954
+ * The user is creating a new document.
3955
+ * It hasn't been saved yet, so there's no history to go to
3956
+ */
3957
+ !document || /**
3958
+ * The document has been created but the current dimension has never been saved.
3959
+ * For example, the user is creating a new locale in an existing document,
3960
+ * so there's no history for the document in that locale
3961
+ */
3962
+ !document.id || /**
3963
+ * History is only available for content types created by the user.
3964
+ * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
3965
+ * which start with `admin::` or `plugin::`
3966
+ */
3967
+ !model.startsWith("api::")
3968
+ ),
3969
+ position: "header"
3970
+ };
3971
+ };
3972
+ HistoryAction.type = "history";
3973
+ const historyAdmin = {
3974
+ bootstrap(app) {
3975
+ const { addDocumentAction } = app.getPlugin("content-manager").apis;
3976
+ addDocumentAction((actions2) => {
3977
+ const indexOfDeleteAction = actions2.findIndex((action) => action.type === "delete");
3978
+ actions2.splice(indexOfDeleteAction, 0, HistoryAction);
3979
+ return actions2;
3980
+ });
3981
+ }
3982
+ };
3689
3983
  const initialState = {
3690
3984
  collectionTypeLinks: [],
3691
3985
  components: [],
@@ -3722,6 +4016,72 @@ const { setInitialData } = actions;
3722
4016
  const reducer = combineReducers({
3723
4017
  app: reducer$1
3724
4018
  });
4019
+ const previewApi = contentManagerApi.injectEndpoints({
4020
+ endpoints: (builder) => ({
4021
+ getPreviewUrl: builder.query({
4022
+ query({ query, params }) {
4023
+ return {
4024
+ url: `/content-manager/preview/url/${params.contentType}`,
4025
+ method: "GET",
4026
+ config: {
4027
+ params: query
4028
+ }
4029
+ };
4030
+ }
4031
+ })
4032
+ })
4033
+ });
4034
+ const { useGetPreviewUrlQuery } = previewApi;
4035
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4036
+ const { formatMessage } = useIntl();
4037
+ const { trackUsage } = useTracking();
4038
+ const { pathname } = useLocation();
4039
+ const [{ query }] = useQueryParams();
4040
+ const { data, error } = useGetPreviewUrlQuery({
4041
+ params: {
4042
+ contentType: model
4043
+ },
4044
+ query: {
4045
+ documentId,
4046
+ locale: document?.locale,
4047
+ status: document?.status
4048
+ }
4049
+ });
4050
+ if (!data?.data?.url || error) {
4051
+ return null;
4052
+ }
4053
+ const trackNavigation = () => {
4054
+ const destinationPathname = pathname.replace(/\/$/, "") + "/preview";
4055
+ trackUsage("willNavigate", { from: pathname, to: destinationPathname });
4056
+ };
4057
+ return {
4058
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4059
+ content: /* @__PURE__ */ jsx(Flex, { gap: 2, width: "100%", children: /* @__PURE__ */ jsx(
4060
+ Button,
4061
+ {
4062
+ variant: "tertiary",
4063
+ tag: Link,
4064
+ to: { pathname: "preview", search: stringify(query, { encode: false }) },
4065
+ onClick: trackNavigation,
4066
+ flex: "auto",
4067
+ children: formatMessage({
4068
+ id: "content-manager.preview.panel.button",
4069
+ defaultMessage: "Open preview"
4070
+ })
4071
+ }
4072
+ ) })
4073
+ };
4074
+ };
4075
+ const FEATURE_ID = "preview";
4076
+ const previewAdmin = {
4077
+ bootstrap(app) {
4078
+ if (!window.strapi.future.isEnabled(FEATURE_ID)) {
4079
+ return;
4080
+ }
4081
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4082
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4083
+ }
4084
+ };
3725
4085
  const index = {
3726
4086
  register(app) {
3727
4087
  const cm = new ContentManagerPlugin();
@@ -3741,7 +4101,7 @@ const index = {
3741
4101
  app.router.addRoute({
3742
4102
  path: "content-manager/*",
3743
4103
  lazy: async () => {
3744
- const { Layout } = await import("./layout-ChVuUpa1.mjs");
4104
+ const { Layout } = await import("./layout-DhMZ_lDx.mjs");
3745
4105
  return {
3746
4106
  Component: Layout
3747
4107
  };
@@ -3750,10 +4110,18 @@ const index = {
3750
4110
  });
3751
4111
  app.registerPlugin(cm.config);
3752
4112
  },
4113
+ bootstrap(app) {
4114
+ if (typeof historyAdmin.bootstrap === "function") {
4115
+ historyAdmin.bootstrap(app);
4116
+ }
4117
+ if (typeof previewAdmin.bootstrap === "function") {
4118
+ previewAdmin.bootstrap(app);
4119
+ }
4120
+ },
3753
4121
  async registerTrads({ locales }) {
3754
4122
  const importedTrads = await Promise.all(
3755
4123
  locales.map((locale) => {
3756
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-Ux26r5pl.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
4124
+ 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-DhFUjrNW.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 }) => {
3757
4125
  return {
3758
4126
  data: prefixPluginTranslations(data, PLUGIN_ID),
3759
4127
  locale
@@ -3774,13 +4142,16 @@ export {
3774
4142
  BulkActionsRenderer as B,
3775
4143
  COLLECTION_TYPES as C,
3776
4144
  DocumentStatus as D,
3777
- DEFAULT_SETTINGS as E,
3778
- convertEditLayoutToFieldLayouts as F,
3779
- useDocument as G,
4145
+ extractContentTypeComponents as E,
4146
+ DEFAULT_SETTINGS as F,
4147
+ convertEditLayoutToFieldLayouts as G,
3780
4148
  HOOKS as H,
3781
4149
  InjectionZone as I,
3782
- index as J,
3783
- useDocumentActions as K,
4150
+ useDocument as J,
4151
+ useGetPreviewUrlQuery as K,
4152
+ index as L,
4153
+ useContentManagerContext as M,
4154
+ useDocumentActions as N,
3784
4155
  Panels as P,
3785
4156
  RelativeTime as R,
3786
4157
  SINGLE_TYPES as S,
@@ -3798,18 +4169,18 @@ export {
3798
4169
  PERMISSIONS as k,
3799
4170
  DocumentRBAC as l,
3800
4171
  DOCUMENT_META_FIELDS as m,
3801
- useDocLayout as n,
3802
- useGetContentTypeConfigurationQuery as o,
3803
- CREATOR_FIELDS as p,
3804
- getMainField as q,
3805
- getDisplayName as r,
4172
+ CLONE_PATH as n,
4173
+ useDocLayout as o,
4174
+ useGetContentTypeConfigurationQuery as p,
4175
+ CREATOR_FIELDS as q,
4176
+ getMainField as r,
3806
4177
  setInitialData as s,
3807
- checkIfAttributeIsDisplayable as t,
4178
+ getDisplayName as t,
3808
4179
  useContentTypeSchema as u,
3809
- useGetAllDocumentsQuery as v,
3810
- convertListLayoutToFieldLayouts as w,
3811
- capitalise as x,
3812
- useUpdateContentTypeConfigurationMutation as y,
3813
- extractContentTypeComponents as z
4180
+ checkIfAttributeIsDisplayable as v,
4181
+ useGetAllDocumentsQuery as w,
4182
+ convertListLayoutToFieldLayouts as x,
4183
+ capitalise as y,
4184
+ useUpdateContentTypeConfigurationMutation as z
3814
4185
  };
3815
- //# sourceMappingURL=index-D1344xdw.mjs.map
4186
+ //# sourceMappingURL=index-BpxR3En4.mjs.map