@strapi/content-manager 0.0.0-experimental.e14656d3b8681880212c13260b9a2b340c182f2d → 0.0.0-experimental.e350eaa6073e65190102b4b798c32c287053cc02

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 (198) hide show
  1. package/dist/_chunks/CardDragPreview-C0QyJgRA.js.map +1 -1
  2. package/dist/_chunks/CardDragPreview-DOxamsuj.mjs.map +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-D_M8iBw5.js → ComponentConfigurationPage-BTR_hQow.js} +5 -6
  4. package/dist/_chunks/{ComponentConfigurationPage-D_M8iBw5.js.map → ComponentConfigurationPage-BTR_hQow.js.map} +1 -1
  5. package/dist/_chunks/{ComponentConfigurationPage-qemkOlnj.mjs → ComponentConfigurationPage-bLQr82ce.mjs} +4 -4
  6. package/dist/_chunks/{ComponentConfigurationPage-qemkOlnj.mjs.map → ComponentConfigurationPage-bLQr82ce.mjs.map} +1 -1
  7. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js → ComponentIcon-CRbtQEUV.js} +2 -3
  8. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js.map → ComponentIcon-CRbtQEUV.js.map} +1 -1
  9. package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -1
  10. package/dist/_chunks/{EditConfigurationPage-CjUrEewK.mjs → EditConfigurationPage-BhRSnUsL.mjs} +4 -4
  11. package/dist/_chunks/{EditConfigurationPage-CjUrEewK.mjs.map → EditConfigurationPage-BhRSnUsL.mjs.map} +1 -1
  12. package/dist/_chunks/{EditConfigurationPage-BePwPuHy.js → EditConfigurationPage-z39Wv3E6.js} +5 -6
  13. package/dist/_chunks/{EditConfigurationPage-BePwPuHy.js.map → EditConfigurationPage-z39Wv3E6.js.map} +1 -1
  14. package/dist/_chunks/{EditViewPage-De8GyU8P.mjs → EditViewPage-BCjNxNlY.mjs} +50 -10
  15. package/dist/_chunks/EditViewPage-BCjNxNlY.mjs.map +1 -0
  16. package/dist/_chunks/{EditViewPage-B-RJeiJD.js → EditViewPage-wujOq90c.js} +50 -11
  17. package/dist/_chunks/EditViewPage-wujOq90c.js.map +1 -0
  18. package/dist/_chunks/{Field-dq8Tg1M_.js → Field-B5QXnctJ.js} +238 -153
  19. package/dist/_chunks/Field-B5QXnctJ.js.map +1 -0
  20. package/dist/_chunks/{Field-pb2o8uBe.mjs → Field-Byr3mPTl.mjs} +232 -147
  21. package/dist/_chunks/Field-Byr3mPTl.mjs.map +1 -0
  22. package/dist/_chunks/FieldTypeIcon-CMlNO8PE.mjs.map +1 -1
  23. package/dist/_chunks/FieldTypeIcon-Dnwq_IRF.js.map +1 -1
  24. package/dist/_chunks/{Form-DJn0Dxha.mjs → Form-BZgvE8C8.mjs} +17 -9
  25. package/dist/_chunks/Form-BZgvE8C8.mjs.map +1 -0
  26. package/dist/_chunks/{Form-DGIf4jQU.js → Form-D7mexvm3.js} +19 -12
  27. package/dist/_chunks/Form-D7mexvm3.js.map +1 -0
  28. package/dist/_chunks/{History-BowL3JKP.mjs → History-CqNgxkqK.mjs} +43 -100
  29. package/dist/_chunks/History-CqNgxkqK.mjs.map +1 -0
  30. package/dist/_chunks/{History-Dh2NEHnR.js → History-DYl2A8Z_.js} +42 -100
  31. package/dist/_chunks/History-DYl2A8Z_.js.map +1 -0
  32. package/dist/_chunks/{ListConfigurationPage-BxYCWz9e.js → ListConfigurationPage-BXnu_OoY.js} +7 -7
  33. package/dist/_chunks/ListConfigurationPage-BXnu_OoY.js.map +1 -0
  34. package/dist/_chunks/{ListConfigurationPage-BpVOB-hn.mjs → ListConfigurationPage-BbQjzKkQ.mjs} +7 -6
  35. package/dist/_chunks/ListConfigurationPage-BbQjzKkQ.mjs.map +1 -0
  36. package/dist/_chunks/{ListViewPage-4XsciqHZ.js → ListViewPage-BtSi8C1l.js} +103 -77
  37. package/dist/_chunks/ListViewPage-BtSi8C1l.js.map +1 -0
  38. package/dist/_chunks/{ListViewPage-CXFUjZQC.mjs → ListViewPage-D4ofkbjR.mjs} +99 -72
  39. package/dist/_chunks/ListViewPage-D4ofkbjR.mjs.map +1 -0
  40. package/dist/_chunks/{NoContentTypePage-C8OpoHeU.js → NoContentTypePage-CitJeOq4.js} +2 -2
  41. package/dist/_chunks/{NoContentTypePage-C8OpoHeU.js.map → NoContentTypePage-CitJeOq4.js.map} +1 -1
  42. package/dist/_chunks/{NoContentTypePage-DuhOTp3x.mjs → NoContentTypePage-DyUx5mXh.mjs} +2 -2
  43. package/dist/_chunks/{NoContentTypePage-DuhOTp3x.mjs.map → NoContentTypePage-DyUx5mXh.mjs.map} +1 -1
  44. package/dist/_chunks/{NoPermissionsPage-DVz3mzDz.mjs → NoPermissionsPage-DhIiyWkk.mjs} +2 -2
  45. package/dist/_chunks/{NoPermissionsPage-DVz3mzDz.mjs.map → NoPermissionsPage-DhIiyWkk.mjs.map} +1 -1
  46. package/dist/_chunks/{NoPermissionsPage-y_r7DVA2.js → NoPermissionsPage-DzgWz0M-.js} +2 -2
  47. package/dist/_chunks/{NoPermissionsPage-y_r7DVA2.js.map → NoPermissionsPage-DzgWz0M-.js.map} +1 -1
  48. package/dist/_chunks/Preview-BaYGJ0nb.mjs +293 -0
  49. package/dist/_chunks/Preview-BaYGJ0nb.mjs.map +1 -0
  50. package/dist/_chunks/Preview-DfNx8Ke-.js +311 -0
  51. package/dist/_chunks/Preview-DfNx8Ke-.js.map +1 -0
  52. package/dist/_chunks/{Relations-CVNLrn1Y.mjs → Relations-DM2yUTST.mjs} +75 -41
  53. package/dist/_chunks/Relations-DM2yUTST.mjs.map +1 -0
  54. package/dist/_chunks/{Relations-DPFCAa7b.js → Relations-DuKCaXrv.js} +75 -42
  55. package/dist/_chunks/Relations-DuKCaXrv.js.map +1 -0
  56. package/dist/_chunks/{en-uOUIxfcQ.js → en-BK8Xyl5I.js} +28 -15
  57. package/dist/_chunks/{en-uOUIxfcQ.js.map → en-BK8Xyl5I.js.map} +1 -1
  58. package/dist/_chunks/{en-BrCTWlZv.mjs → en-Dtk_ot79.mjs} +28 -15
  59. package/dist/_chunks/{en-BrCTWlZv.mjs.map → en-Dtk_ot79.mjs.map} +1 -1
  60. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  61. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  62. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  63. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  64. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  65. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  66. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  67. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  68. package/dist/_chunks/hooks-BAaaKPS_.js.map +1 -1
  69. package/dist/_chunks/{index-C3fJE-1-.js → index-BUWEmX8m.js} +1273 -996
  70. package/dist/_chunks/index-BUWEmX8m.js.map +1 -0
  71. package/dist/_chunks/{index-DiMrfcfy.mjs → index-DVAIIsOs.mjs} +1276 -999
  72. package/dist/_chunks/index-DVAIIsOs.mjs.map +1 -0
  73. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  74. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  75. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  76. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  77. package/dist/_chunks/{layout-ls3gxfpH.mjs → layout-Bxsv5mP7.mjs} +6 -5
  78. package/dist/_chunks/{layout-ls3gxfpH.mjs.map → layout-Bxsv5mP7.mjs.map} +1 -1
  79. package/dist/_chunks/{layout-C788OmNr.js → layout-C3fN7Ejz.js} +7 -7
  80. package/dist/_chunks/{layout-C788OmNr.js.map → layout-C3fN7Ejz.js.map} +1 -1
  81. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  82. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  83. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  84. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  85. package/dist/_chunks/{relations-DYeotliT.js → relations-BPZKAoEY.js} +6 -7
  86. package/dist/_chunks/relations-BPZKAoEY.js.map +1 -0
  87. package/dist/_chunks/{relations-CLcOmGO0.mjs → relations-o3pPhzY4.mjs} +6 -7
  88. package/dist/_chunks/relations-o3pPhzY4.mjs.map +1 -0
  89. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  90. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  91. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  92. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  93. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js → useDragAndDrop-BMtgCYzL.js} +5 -9
  94. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js.map → useDragAndDrop-BMtgCYzL.js.map} +1 -1
  95. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs → useDragAndDrop-DJ6jqvZN.mjs} +4 -7
  96. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs.map → useDragAndDrop-DJ6jqvZN.mjs.map} +1 -1
  97. package/dist/admin/index.js +2 -1
  98. package/dist/admin/index.js.map +1 -1
  99. package/dist/admin/index.mjs +4 -3
  100. package/dist/admin/src/content-manager.d.ts +3 -2
  101. package/dist/admin/src/exports.d.ts +1 -1
  102. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  103. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  104. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -1
  105. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +2 -2
  106. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Code.d.ts +7 -0
  107. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/utils/prismLanguages.d.ts +49 -0
  108. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +1 -0
  109. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +0 -32
  110. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  111. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  112. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  113. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  114. package/dist/admin/src/preview/index.d.ts +4 -0
  115. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  116. package/dist/admin/src/preview/routes.d.ts +3 -0
  117. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  118. package/dist/admin/src/router.d.ts +1 -1
  119. package/dist/admin/src/services/documents.d.ts +3 -4
  120. package/dist/server/index.js +453 -201
  121. package/dist/server/index.js.map +1 -1
  122. package/dist/server/index.mjs +453 -200
  123. package/dist/server/index.mjs.map +1 -1
  124. package/dist/server/src/bootstrap.d.ts.map +1 -1
  125. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  126. package/dist/server/src/controllers/index.d.ts.map +1 -1
  127. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  128. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  129. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  130. package/dist/server/src/history/services/history.d.ts.map +1 -1
  131. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  132. package/dist/server/src/history/services/utils.d.ts +3 -3
  133. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  134. package/dist/server/src/index.d.ts +4 -4
  135. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  136. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  137. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  138. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  139. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  140. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  141. package/dist/server/src/preview/index.d.ts +4 -0
  142. package/dist/server/src/preview/index.d.ts.map +1 -0
  143. package/dist/server/src/preview/routes/index.d.ts +8 -0
  144. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  145. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  146. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  147. package/dist/server/src/preview/services/index.d.ts +16 -0
  148. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  149. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  150. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  151. package/dist/server/src/preview/services/preview.d.ts +12 -0
  152. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  153. package/dist/server/src/preview/utils.d.ts +19 -0
  154. package/dist/server/src/preview/utils.d.ts.map +1 -0
  155. package/dist/server/src/register.d.ts.map +1 -1
  156. package/dist/server/src/routes/index.d.ts.map +1 -1
  157. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  158. package/dist/server/src/services/document-metadata.d.ts +8 -8
  159. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  160. package/dist/server/src/services/index.d.ts +4 -4
  161. package/dist/server/src/services/index.d.ts.map +1 -1
  162. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  163. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  164. package/dist/server/src/utils/index.d.ts +2 -0
  165. package/dist/server/src/utils/index.d.ts.map +1 -1
  166. package/dist/shared/contracts/collection-types.d.ts +3 -1
  167. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  168. package/dist/shared/contracts/index.d.ts +1 -0
  169. package/dist/shared/contracts/index.d.ts.map +1 -1
  170. package/dist/shared/contracts/preview.d.ts +27 -0
  171. package/dist/shared/contracts/preview.d.ts.map +1 -0
  172. package/dist/shared/index.js +4 -0
  173. package/dist/shared/index.js.map +1 -1
  174. package/dist/shared/index.mjs +4 -0
  175. package/dist/shared/index.mjs.map +1 -1
  176. package/package.json +17 -15
  177. package/dist/_chunks/EditViewPage-B-RJeiJD.js.map +0 -1
  178. package/dist/_chunks/EditViewPage-De8GyU8P.mjs.map +0 -1
  179. package/dist/_chunks/Field-dq8Tg1M_.js.map +0 -1
  180. package/dist/_chunks/Field-pb2o8uBe.mjs.map +0 -1
  181. package/dist/_chunks/Form-DGIf4jQU.js.map +0 -1
  182. package/dist/_chunks/Form-DJn0Dxha.mjs.map +0 -1
  183. package/dist/_chunks/History-BowL3JKP.mjs.map +0 -1
  184. package/dist/_chunks/History-Dh2NEHnR.js.map +0 -1
  185. package/dist/_chunks/ListConfigurationPage-BpVOB-hn.mjs.map +0 -1
  186. package/dist/_chunks/ListConfigurationPage-BxYCWz9e.js.map +0 -1
  187. package/dist/_chunks/ListViewPage-4XsciqHZ.js.map +0 -1
  188. package/dist/_chunks/ListViewPage-CXFUjZQC.mjs.map +0 -1
  189. package/dist/_chunks/Relations-CVNLrn1Y.mjs.map +0 -1
  190. package/dist/_chunks/Relations-DPFCAa7b.js.map +0 -1
  191. package/dist/_chunks/index-C3fJE-1-.js.map +0 -1
  192. package/dist/_chunks/index-DiMrfcfy.mjs.map +0 -1
  193. package/dist/_chunks/relations-CLcOmGO0.mjs.map +0 -1
  194. package/dist/_chunks/relations-DYeotliT.js.map +0 -1
  195. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  196. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  197. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  198. package/strapi-server.js +0 -3
@@ -1,25 +1,33 @@
1
- import { CrossCircle, More, WarningCircle, ListPlus, Pencil, Trash, Check, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
1
+ import { More, Cross, WarningCircle, ListPlus, Pencil, Trash, Check, CrossCircle, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
2
2
  import { jsx, Fragment, jsxs } from "react/jsx-runtime";
3
- import { useStrapiApp, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useQueryParams, useTracking, useForm, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
3
+ import { useStrapiApp, createContext, useQueryParams, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useForm, useTracking, useGuidedTour, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
4
4
  import * as React from "react";
5
5
  import { lazy } from "react";
6
- import { Menu, Button, VisuallyHidden, Flex, Typography, Dialog, Modal, Radio, Status, Box, 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";
7
8
  import { useIntl } from "react-intl";
8
9
  import { useParams, useNavigate, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
9
10
  import { styled } from "styled-components";
10
11
  import * as yup from "yup";
11
12
  import { ValidationError } from "yup";
13
+ import { stringify } from "qs";
12
14
  import pipe from "lodash/fp/pipe";
13
15
  import { intervalToDuration, isPast } from "date-fns";
14
- import { stringify } from "qs";
15
16
  import { createSlice, combineReducers } from "@reduxjs/toolkit";
16
- const __variableDynamicImportRuntimeHelper = (glob, path) => {
17
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
17
18
  const v = glob[path];
18
19
  if (v) {
19
20
  return typeof v === "function" ? v() : Promise.resolve(v);
20
21
  }
21
22
  return new Promise((_, reject) => {
22
- (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(reject.bind(null, new Error("Unknown variable dynamic import: " + path)));
23
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
24
+ reject.bind(
25
+ null,
26
+ new Error(
27
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
28
+ )
29
+ )
30
+ );
23
31
  });
24
32
  };
25
33
  const PLUGIN_ID = "content-manager";
@@ -100,6 +108,7 @@ const DocumentRBAC = ({ children, permissions }) => {
100
108
  if (!slug) {
101
109
  throw new Error("Cannot find the slug param in the URL");
102
110
  }
111
+ const [{ rawQuery }] = useQueryParams();
103
112
  const userPermissions = useAuth("DocumentRBAC", (state) => state.permissions);
104
113
  const contentTypePermissions = React.useMemo(() => {
105
114
  const contentTypePermissions2 = userPermissions.filter(
@@ -110,7 +119,14 @@ const DocumentRBAC = ({ children, permissions }) => {
110
119
  return { ...acc, [action]: [permission] };
111
120
  }, {});
112
121
  }, [slug, userPermissions]);
113
- const { isLoading, allowedActions } = useRBAC(contentTypePermissions, permissions ?? void 0);
122
+ const { isLoading, allowedActions } = useRBAC(
123
+ contentTypePermissions,
124
+ permissions ?? void 0,
125
+ // TODO: useRBAC context should be typed and built differently
126
+ // We are passing raw query as context to the hook so that it can
127
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
128
+ rawQuery
129
+ );
114
130
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
115
131
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
116
132
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -265,7 +281,7 @@ const documentApi = contentManagerApi.injectEndpoints({
265
281
  url: `/content-manager/collection-types/${model}`,
266
282
  method: "GET",
267
283
  config: {
268
- params
284
+ params: stringify(params, { encode: true })
269
285
  }
270
286
  }),
271
287
  providesTags: (result, _error, arg) => {
@@ -444,8 +460,7 @@ const {
444
460
  useUnpublishManyDocumentsMutation
445
461
  } = documentApi;
446
462
  const buildValidParams = (query) => {
447
- if (!query)
448
- return query;
463
+ if (!query) return query;
449
464
  const { plugins: _, ...validQueryParams } = {
450
465
  ...query,
451
466
  ...Object.values(query?.plugins ?? {}).reduce(
@@ -453,14 +468,29 @@ const buildValidParams = (query) => {
453
468
  {}
454
469
  )
455
470
  };
456
- if ("_q" in validQueryParams) {
457
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
458
- }
459
471
  return validQueryParams;
460
472
  };
461
473
  const isBaseQueryError = (error) => {
462
474
  return error.name !== void 0;
463
475
  };
476
+ const arrayValidator = (attribute, options) => ({
477
+ message: translatedErrors.required,
478
+ test(value) {
479
+ if (options.status === "draft") {
480
+ return true;
481
+ }
482
+ if (!attribute.required) {
483
+ return true;
484
+ }
485
+ if (!value) {
486
+ return false;
487
+ }
488
+ if (Array.isArray(value) && value.length === 0) {
489
+ return false;
490
+ }
491
+ return true;
492
+ }
493
+ });
464
494
  const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
465
495
  const createModelSchema = (attributes2) => yup.object().shape(
466
496
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
@@ -468,6 +498,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
468
498
  return acc;
469
499
  }
470
500
  const validations = [
501
+ addNullableValidation,
471
502
  addRequiredValidation,
472
503
  addMinLengthValidation,
473
504
  addMaxLengthValidation,
@@ -484,12 +515,12 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
484
515
  ...acc,
485
516
  [name]: transformSchema(
486
517
  yup.array().of(createModelSchema(attributes3).nullable(false))
487
- )
518
+ ).test(arrayValidator(attribute, options))
488
519
  };
489
520
  } else {
490
521
  return {
491
522
  ...acc,
492
- [name]: transformSchema(createModelSchema(attributes3))
523
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
493
524
  };
494
525
  }
495
526
  }
@@ -511,7 +542,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
511
542
  }
512
543
  )
513
544
  )
514
- )
545
+ ).test(arrayValidator(attribute, options))
515
546
  };
516
547
  case "relation":
517
548
  return {
@@ -523,7 +554,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
523
554
  } else if (Array.isArray(value)) {
524
555
  return yup.array().of(
525
556
  yup.object().shape({
526
- id: yup.string().required()
557
+ id: yup.number().required()
527
558
  })
528
559
  );
529
560
  } else if (typeof value === "object") {
@@ -609,17 +640,17 @@ const nullableSchema = (schema) => {
609
640
  schema
610
641
  );
611
642
  };
643
+ const addNullableValidation = () => (schema) => {
644
+ return nullableSchema(schema);
645
+ };
612
646
  const addRequiredValidation = (attribute, options) => (schema) => {
613
- if (options.status === "draft") {
614
- return nullableSchema(schema);
615
- }
616
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
617
- return schema.min(1, translatedErrors.required);
647
+ if (options.status === "draft" || !attribute.required) {
648
+ return schema;
618
649
  }
619
- if (attribute.required && attribute.type !== "relation") {
650
+ if (attribute.required && "required" in schema) {
620
651
  return schema.required(translatedErrors.required);
621
652
  }
622
- return nullableSchema(schema);
653
+ return schema;
623
654
  };
624
655
  const addMinLengthValidation = (attribute, options) => (schema) => {
625
656
  if (options.status === "draft") {
@@ -647,31 +678,12 @@ const addMaxLengthValidation = (attribute) => (schema) => {
647
678
  return schema;
648
679
  };
649
680
  const addMinValidation = (attribute, options) => (schema) => {
650
- if ("min" in attribute) {
681
+ if (options.status === "draft") {
682
+ return schema;
683
+ }
684
+ if ("min" in attribute && "min" in schema) {
651
685
  const min = toInteger(attribute.min);
652
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
653
- if (options.status !== "draft" && !attribute.required && "test" in schema && min) {
654
- return schema.test(
655
- "custom-min",
656
- {
657
- ...translatedErrors.min,
658
- values: {
659
- min: attribute.min
660
- }
661
- },
662
- (value) => {
663
- if (!value) {
664
- return true;
665
- }
666
- if (Array.isArray(value) && value.length === 0) {
667
- return true;
668
- }
669
- return value.length >= min;
670
- }
671
- );
672
- }
673
- }
674
- if ("min" in schema && min) {
686
+ if (min) {
675
687
  return schema.min(min, {
676
688
  ...translatedErrors.min,
677
689
  values: {
@@ -789,19 +801,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
789
801
  }, {});
790
802
  return componentsByKey;
791
803
  };
792
- const useDocument = (args, opts) => {
804
+ const HOOKS = {
805
+ /**
806
+ * Hook that allows to mutate the displayed headers of the list view table
807
+ * @constant
808
+ * @type {string}
809
+ */
810
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
811
+ /**
812
+ * Hook that allows to mutate the CM's collection types links pre-set filters
813
+ * @constant
814
+ * @type {string}
815
+ */
816
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
817
+ /**
818
+ * Hook that allows to mutate the CM's edit view layout
819
+ * @constant
820
+ * @type {string}
821
+ */
822
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
823
+ /**
824
+ * Hook that allows to mutate the CM's single types links pre-set filters
825
+ * @constant
826
+ * @type {string}
827
+ */
828
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
829
+ };
830
+ const contentTypesApi = contentManagerApi.injectEndpoints({
831
+ endpoints: (builder) => ({
832
+ getContentTypeConfiguration: builder.query({
833
+ query: (uid) => ({
834
+ url: `/content-manager/content-types/${uid}/configuration`,
835
+ method: "GET"
836
+ }),
837
+ transformResponse: (response) => response.data,
838
+ providesTags: (_result, _error, uid) => [
839
+ { type: "ContentTypesConfiguration", id: uid },
840
+ { type: "ContentTypeSettings", id: "LIST" }
841
+ ]
842
+ }),
843
+ getAllContentTypeSettings: builder.query({
844
+ query: () => "/content-manager/content-types-settings",
845
+ transformResponse: (response) => response.data,
846
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
847
+ }),
848
+ updateContentTypeConfiguration: builder.mutation({
849
+ query: ({ uid, ...body }) => ({
850
+ url: `/content-manager/content-types/${uid}/configuration`,
851
+ method: "PUT",
852
+ data: body
853
+ }),
854
+ transformResponse: (response) => response.data,
855
+ invalidatesTags: (_result, _error, { uid }) => [
856
+ { type: "ContentTypesConfiguration", id: uid },
857
+ { type: "ContentTypeSettings", id: "LIST" },
858
+ // Is this necessary?
859
+ { type: "InitialData" }
860
+ ]
861
+ })
862
+ })
863
+ });
864
+ const {
865
+ useGetContentTypeConfigurationQuery,
866
+ useGetAllContentTypeSettingsQuery,
867
+ useUpdateContentTypeConfigurationMutation
868
+ } = contentTypesApi;
869
+ const checkIfAttributeIsDisplayable = (attribute) => {
870
+ const { type } = attribute;
871
+ if (type === "relation") {
872
+ return !attribute.relation.toLowerCase().includes("morph");
873
+ }
874
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
875
+ };
876
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
877
+ if (!mainFieldName) {
878
+ return void 0;
879
+ }
880
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
881
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
882
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
883
+ );
884
+ return {
885
+ name: mainFieldName,
886
+ type: mainFieldType ?? "string"
887
+ };
888
+ };
889
+ const DEFAULT_SETTINGS = {
890
+ bulkable: false,
891
+ filterable: false,
892
+ searchable: false,
893
+ pagination: false,
894
+ defaultSortBy: "",
895
+ defaultSortOrder: "asc",
896
+ mainField: "id",
897
+ pageSize: 10
898
+ };
899
+ const useDocumentLayout = (model) => {
900
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
901
+ const [{ query }] = useQueryParams();
902
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
793
903
  const { toggleNotification } = useNotification();
794
904
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
905
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
795
906
  const {
796
- currentData: data,
797
- isLoading: isLoadingDocument,
798
- isFetching: isFetchingDocument,
799
- error
800
- } = useGetDocumentQuery(args, {
801
- ...opts,
802
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
803
- });
804
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
907
+ data,
908
+ isLoading: isLoadingConfigs,
909
+ error,
910
+ isFetching: isFetchingConfigs
911
+ } = useGetContentTypeConfigurationQuery(model);
912
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
805
913
  React.useEffect(() => {
806
914
  if (error) {
807
915
  toggleNotification({
@@ -809,398 +917,654 @@ const useDocument = (args, opts) => {
809
917
  message: formatAPIError(error)
810
918
  });
811
919
  }
812
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
813
- const validationSchema = React.useMemo(() => {
814
- if (!schema) {
815
- return null;
816
- }
817
- return createYupSchema(schema.attributes, components);
818
- }, [schema, components]);
819
- const validate = React.useCallback(
820
- (document) => {
821
- if (!validationSchema) {
822
- throw new Error(
823
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
824
- );
825
- }
826
- try {
827
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
828
- return null;
829
- } catch (error2) {
830
- if (error2 instanceof ValidationError) {
831
- return getYupValidationErrors(error2);
832
- }
833
- throw error2;
834
- }
920
+ }, [error, formatAPIError, toggleNotification]);
921
+ const editLayout = React.useMemo(
922
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
923
+ layout: [],
924
+ components: {},
925
+ metadatas: {},
926
+ options: {},
927
+ settings: DEFAULT_SETTINGS
835
928
  },
836
- [validationSchema]
929
+ [data, isLoading, schemas, schema, components]
930
+ );
931
+ const listLayout = React.useMemo(() => {
932
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
933
+ layout: [],
934
+ metadatas: {},
935
+ options: {},
936
+ settings: DEFAULT_SETTINGS
937
+ };
938
+ }, [data, isLoading, schemas, schema, components]);
939
+ const { layout: edit } = React.useMemo(
940
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
941
+ layout: editLayout,
942
+ query
943
+ }),
944
+ [editLayout, query, runHookWaterfall]
837
945
  );
838
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
839
946
  return {
840
- components,
841
- document: data?.data,
842
- meta: data?.meta,
947
+ error,
843
948
  isLoading,
844
- schema,
845
- validate
846
- };
847
- };
848
- const useDoc = () => {
849
- const { id, slug, collectionType, origin } = useParams();
850
- const [{ query }] = useQueryParams();
851
- const params = React.useMemo(() => buildValidParams(query), [query]);
852
- if (!collectionType) {
853
- throw new Error("Could not find collectionType in url params");
854
- }
855
- if (!slug) {
856
- throw new Error("Could not find model in url params");
857
- }
858
- return {
859
- collectionType,
860
- model: slug,
861
- id: origin || id === "create" ? void 0 : id,
862
- ...useDocument(
863
- { documentId: origin || id, model: slug, collectionType, params },
864
- {
865
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
866
- }
867
- )
949
+ edit,
950
+ list: listLayout
868
951
  };
869
952
  };
870
- const prefixPluginTranslations = (trad, pluginId) => {
871
- if (!pluginId) {
872
- throw new TypeError("pluginId can't be empty");
873
- }
874
- return Object.keys(trad).reduce((acc, current) => {
875
- acc[`${pluginId}.${current}`] = trad[current];
876
- return acc;
877
- }, {});
878
- };
879
- const getTranslation = (id) => `content-manager.${id}`;
880
- const DEFAULT_UNEXPECTED_ERROR_MSG = {
881
- id: "notification.error",
882
- defaultMessage: "An error occurred, please try again"
953
+ const useDocLayout = () => {
954
+ const { model } = useDoc();
955
+ return useDocumentLayout(model);
883
956
  };
884
- const useDocumentActions = () => {
885
- const { toggleNotification } = useNotification();
886
- const { formatMessage } = useIntl();
887
- const { trackUsage } = useTracking();
888
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
889
- const navigate = useNavigate();
890
- const [deleteDocument] = useDeleteDocumentMutation();
891
- const _delete = React.useCallback(
892
- async ({ collectionType, model, documentId, params }, trackerProperty) => {
893
- try {
894
- trackUsage("willDeleteEntry", trackerProperty);
895
- const res = await deleteDocument({
896
- collectionType,
897
- model,
898
- documentId,
899
- params
900
- });
901
- if ("error" in res) {
902
- toggleNotification({
903
- type: "danger",
904
- message: formatAPIError(res.error)
905
- });
906
- return { error: res.error };
907
- }
908
- toggleNotification({
909
- type: "success",
910
- message: formatMessage({
911
- id: getTranslation("success.record.delete"),
912
- defaultMessage: "Deleted document"
913
- })
914
- });
915
- trackUsage("didDeleteEntry", trackerProperty);
916
- return res.data;
917
- } catch (err) {
918
- toggleNotification({
919
- type: "danger",
920
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
921
- });
922
- trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
923
- throw err;
957
+ const formatEditLayout = (data, {
958
+ schemas,
959
+ schema,
960
+ components
961
+ }) => {
962
+ let currentPanelIndex = 0;
963
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
964
+ data.contentType.layouts.edit,
965
+ schema?.attributes,
966
+ data.contentType.metadatas,
967
+ { configurations: data.components, schemas: components },
968
+ schemas
969
+ ).reduce((panels, row) => {
970
+ if (row.some((field) => field.type === "dynamiczone")) {
971
+ panels.push([row]);
972
+ currentPanelIndex += 2;
973
+ } else {
974
+ if (!panels[currentPanelIndex]) {
975
+ panels.push([row]);
976
+ } else {
977
+ panels[currentPanelIndex].push(row);
924
978
  }
925
- },
926
- [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
927
- );
928
- const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
929
- const deleteMany = React.useCallback(
930
- async ({ model, documentIds, params }) => {
931
- try {
932
- trackUsage("willBulkDeleteEntries");
933
- const res = await deleteManyDocuments({
934
- model,
935
- documentIds,
936
- params
937
- });
938
- if ("error" in res) {
939
- toggleNotification({
940
- type: "danger",
941
- message: formatAPIError(res.error)
942
- });
943
- return { error: res.error };
979
+ }
980
+ return panels;
981
+ }, []);
982
+ const componentEditAttributes = Object.entries(data.components).reduce(
983
+ (acc, [uid, configuration]) => {
984
+ acc[uid] = {
985
+ layout: convertEditLayoutToFieldLayouts(
986
+ configuration.layouts.edit,
987
+ components[uid].attributes,
988
+ configuration.metadatas,
989
+ { configurations: data.components, schemas: components }
990
+ ),
991
+ settings: {
992
+ ...configuration.settings,
993
+ icon: components[uid].info.icon,
994
+ displayName: components[uid].info.displayName
944
995
  }
945
- toggleNotification({
946
- type: "success",
947
- title: formatMessage({
948
- id: getTranslation("success.records.delete"),
949
- defaultMessage: "Successfully deleted."
950
- }),
951
- message: ""
952
- });
953
- trackUsage("didBulkDeleteEntries");
954
- return res.data;
955
- } catch (err) {
956
- toggleNotification({
957
- type: "danger",
958
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
959
- });
960
- trackUsage("didNotBulkDeleteEntries");
961
- throw err;
962
- }
996
+ };
997
+ return acc;
963
998
  },
964
- [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
999
+ {}
965
1000
  );
966
- const [discardDocument] = useDiscardDocumentMutation();
967
- const discard = React.useCallback(
968
- async ({ collectionType, model, documentId, params }) => {
969
- try {
970
- const res = await discardDocument({
971
- collectionType,
972
- model,
973
- documentId,
974
- params
975
- });
976
- if ("error" in res) {
977
- toggleNotification({
978
- type: "danger",
979
- message: formatAPIError(res.error)
980
- });
981
- return { error: res.error };
982
- }
983
- toggleNotification({
984
- type: "success",
985
- message: formatMessage({
986
- id: "content-manager.success.record.discard",
987
- defaultMessage: "Changes discarded"
988
- })
989
- });
990
- return res.data;
991
- } catch (err) {
992
- toggleNotification({
993
- type: "danger",
994
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
995
- });
996
- throw err;
997
- }
1001
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1002
+ (acc, [attribute, metadata]) => {
1003
+ return {
1004
+ ...acc,
1005
+ [attribute]: metadata.edit
1006
+ };
998
1007
  },
999
- [discardDocument, formatAPIError, formatMessage, toggleNotification]
1008
+ {}
1000
1009
  );
1001
- const [publishDocument] = usePublishDocumentMutation();
1002
- const publish = React.useCallback(
1003
- async ({ collectionType, model, documentId, params }, data) => {
1004
- try {
1005
- trackUsage("willPublishEntry");
1006
- const res = await publishDocument({
1007
- collectionType,
1008
- model,
1009
- documentId,
1010
- data,
1011
- params
1012
- });
1013
- if ("error" in res) {
1014
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1015
- return { error: res.error };
1016
- }
1017
- trackUsage("didPublishEntry");
1018
- toggleNotification({
1019
- type: "success",
1020
- message: formatMessage({
1021
- id: getTranslation("success.record.publish"),
1022
- defaultMessage: "Published document"
1023
- })
1024
- });
1025
- return res.data;
1026
- } catch (err) {
1027
- toggleNotification({
1028
- type: "danger",
1029
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1030
- });
1031
- throw err;
1032
- }
1010
+ return {
1011
+ layout: panelledEditAttributes,
1012
+ components: componentEditAttributes,
1013
+ metadatas: editMetadatas,
1014
+ settings: {
1015
+ ...data.contentType.settings,
1016
+ displayName: schema?.info.displayName
1033
1017
  },
1034
- [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1035
- );
1036
- const [publishManyDocuments] = usePublishManyDocumentsMutation();
1037
- const publishMany = React.useCallback(
1038
- async ({ model, documentIds, params }) => {
1039
- try {
1040
- const res = await publishManyDocuments({
1041
- model,
1042
- documentIds,
1043
- params
1044
- });
1045
- if ("error" in res) {
1046
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1047
- return { error: res.error };
1048
- }
1049
- toggleNotification({
1050
- type: "success",
1051
- message: formatMessage({
1052
- id: getTranslation("success.record.publish"),
1053
- defaultMessage: "Published document"
1054
- })
1055
- });
1056
- return res.data;
1057
- } catch (err) {
1058
- toggleNotification({
1059
- type: "danger",
1060
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1061
- });
1062
- throw err;
1018
+ options: {
1019
+ ...schema?.options,
1020
+ ...schema?.pluginOptions,
1021
+ ...data.contentType.options
1022
+ }
1023
+ };
1024
+ };
1025
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1026
+ return rows.map(
1027
+ (row) => row.map((field) => {
1028
+ const attribute = attributes[field.name];
1029
+ if (!attribute) {
1030
+ return null;
1063
1031
  }
1064
- },
1065
- [
1066
- // trackUsage,
1067
- publishManyDocuments,
1068
- toggleNotification,
1069
- formatMessage,
1070
- formatAPIError
1071
- ]
1032
+ const { edit: metadata } = metadatas[field.name];
1033
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1034
+ return {
1035
+ attribute,
1036
+ disabled: !metadata.editable,
1037
+ hint: metadata.description,
1038
+ label: metadata.label ?? "",
1039
+ name: field.name,
1040
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1041
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1042
+ schemas,
1043
+ components: components?.schemas ?? {}
1044
+ }),
1045
+ placeholder: metadata.placeholder ?? "",
1046
+ required: attribute.required ?? false,
1047
+ size: field.size,
1048
+ unique: "unique" in attribute ? attribute.unique : false,
1049
+ visible: metadata.visible ?? true,
1050
+ type: attribute.type
1051
+ };
1052
+ }).filter((field) => field !== null)
1072
1053
  );
1073
- const [updateDocument] = useUpdateDocumentMutation();
1074
- const update = React.useCallback(
1075
- async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1076
- try {
1077
- trackUsage("willEditEntry", trackerProperty);
1078
- const res = await updateDocument({
1079
- collectionType,
1080
- model,
1081
- documentId,
1082
- data,
1083
- params
1084
- });
1085
- if ("error" in res) {
1086
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1087
- trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1088
- return { error: res.error };
1054
+ };
1055
+ const formatListLayout = (data, {
1056
+ schemas,
1057
+ schema,
1058
+ components
1059
+ }) => {
1060
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1061
+ (acc, [attribute, metadata]) => {
1062
+ return {
1063
+ ...acc,
1064
+ [attribute]: metadata.list
1065
+ };
1066
+ },
1067
+ {}
1068
+ );
1069
+ const listAttributes = convertListLayoutToFieldLayouts(
1070
+ data.contentType.layouts.list,
1071
+ schema?.attributes,
1072
+ listMetadatas,
1073
+ { configurations: data.components, schemas: components },
1074
+ schemas
1075
+ );
1076
+ return {
1077
+ layout: listAttributes,
1078
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1079
+ metadatas: listMetadatas,
1080
+ options: {
1081
+ ...schema?.options,
1082
+ ...schema?.pluginOptions,
1083
+ ...data.contentType.options
1084
+ }
1085
+ };
1086
+ };
1087
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1088
+ return columns.map((name) => {
1089
+ const attribute = attributes[name];
1090
+ if (!attribute) {
1091
+ return null;
1092
+ }
1093
+ const metadata = metadatas[name];
1094
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1095
+ return {
1096
+ attribute,
1097
+ label: metadata.label ?? "",
1098
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1099
+ schemas,
1100
+ components: components?.schemas ?? {}
1101
+ }),
1102
+ name,
1103
+ searchable: metadata.searchable ?? true,
1104
+ sortable: metadata.sortable ?? true
1105
+ };
1106
+ }).filter((field) => field !== null);
1107
+ };
1108
+ const useDocument = (args, opts) => {
1109
+ const { toggleNotification } = useNotification();
1110
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1111
+ const {
1112
+ currentData: data,
1113
+ isLoading: isLoadingDocument,
1114
+ isFetching: isFetchingDocument,
1115
+ error
1116
+ } = useGetDocumentQuery(args, {
1117
+ ...opts,
1118
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1119
+ });
1120
+ const {
1121
+ components,
1122
+ schema,
1123
+ schemas,
1124
+ isLoading: isLoadingSchema
1125
+ } = useContentTypeSchema(args.model);
1126
+ React.useEffect(() => {
1127
+ if (error) {
1128
+ toggleNotification({
1129
+ type: "danger",
1130
+ message: formatAPIError(error)
1131
+ });
1132
+ }
1133
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1134
+ const validationSchema = React.useMemo(() => {
1135
+ if (!schema) {
1136
+ return null;
1137
+ }
1138
+ return createYupSchema(schema.attributes, components);
1139
+ }, [schema, components]);
1140
+ const validate = React.useCallback(
1141
+ (document) => {
1142
+ if (!validationSchema) {
1143
+ throw new Error(
1144
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1145
+ );
1146
+ }
1147
+ try {
1148
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1149
+ return null;
1150
+ } catch (error2) {
1151
+ if (error2 instanceof ValidationError) {
1152
+ return getYupValidationErrors(error2);
1089
1153
  }
1090
- trackUsage("didEditEntry", trackerProperty);
1091
- toggleNotification({
1092
- type: "success",
1093
- message: formatMessage({
1094
- id: getTranslation("success.record.save"),
1095
- defaultMessage: "Saved document"
1096
- })
1097
- });
1098
- return res.data;
1099
- } catch (err) {
1100
- trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1101
- toggleNotification({
1102
- type: "danger",
1103
- message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1104
- });
1105
- throw err;
1154
+ throw error2;
1106
1155
  }
1107
1156
  },
1108
- [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1157
+ [validationSchema]
1109
1158
  );
1110
- const [unpublishDocument] = useUnpublishDocumentMutation();
1111
- const unpublish = React.useCallback(
1112
- async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1159
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1160
+ const hasError = !!error;
1161
+ return {
1162
+ components,
1163
+ document: data?.data,
1164
+ meta: data?.meta,
1165
+ isLoading,
1166
+ hasError,
1167
+ schema,
1168
+ schemas,
1169
+ validate
1170
+ };
1171
+ };
1172
+ const useDoc = () => {
1173
+ const { id, slug, collectionType, origin } = useParams();
1174
+ const [{ query }] = useQueryParams();
1175
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1176
+ if (!collectionType) {
1177
+ throw new Error("Could not find collectionType in url params");
1178
+ }
1179
+ if (!slug) {
1180
+ throw new Error("Could not find model in url params");
1181
+ }
1182
+ const document = useDocument(
1183
+ { documentId: origin || id, model: slug, collectionType, params },
1184
+ {
1185
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1186
+ }
1187
+ );
1188
+ const returnId = origin || id === "create" ? void 0 : id;
1189
+ return {
1190
+ collectionType,
1191
+ model: slug,
1192
+ id: returnId,
1193
+ ...document
1194
+ };
1195
+ };
1196
+ const useContentManagerContext = () => {
1197
+ const {
1198
+ collectionType,
1199
+ model,
1200
+ id,
1201
+ components,
1202
+ isLoading: isLoadingDoc,
1203
+ schema,
1204
+ schemas
1205
+ } = useDoc();
1206
+ const layout = useDocumentLayout(model);
1207
+ const form = useForm("useContentManagerContext", (state) => state);
1208
+ const isSingleType = collectionType === SINGLE_TYPES;
1209
+ const slug = model;
1210
+ const isCreatingEntry = id === "create";
1211
+ useContentTypeSchema();
1212
+ const isLoading = isLoadingDoc || layout.isLoading;
1213
+ const error = layout.error;
1214
+ return {
1215
+ error,
1216
+ isLoading,
1217
+ // Base metadata
1218
+ model,
1219
+ collectionType,
1220
+ id,
1221
+ slug,
1222
+ isCreatingEntry,
1223
+ isSingleType,
1224
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1225
+ // All schema infos
1226
+ components,
1227
+ contentType: schema,
1228
+ contentTypes: schemas,
1229
+ // Form state
1230
+ form,
1231
+ // layout infos
1232
+ layout
1233
+ };
1234
+ };
1235
+ const prefixPluginTranslations = (trad, pluginId) => {
1236
+ return Object.keys(trad).reduce((acc, current) => {
1237
+ acc[`${pluginId}.${current}`] = trad[current];
1238
+ return acc;
1239
+ }, {});
1240
+ };
1241
+ const getTranslation = (id) => `content-manager.${id}`;
1242
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1243
+ id: "notification.error",
1244
+ defaultMessage: "An error occurred, please try again"
1245
+ };
1246
+ const useDocumentActions = () => {
1247
+ const { toggleNotification } = useNotification();
1248
+ const { formatMessage } = useIntl();
1249
+ const { trackUsage } = useTracking();
1250
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1251
+ const navigate = useNavigate();
1252
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
1253
+ const [deleteDocument] = useDeleteDocumentMutation();
1254
+ const _delete = React.useCallback(
1255
+ async ({ collectionType, model, documentId, params }, trackerProperty) => {
1113
1256
  try {
1114
- trackUsage("willUnpublishEntry");
1115
- const res = await unpublishDocument({
1257
+ trackUsage("willDeleteEntry", trackerProperty);
1258
+ const res = await deleteDocument({
1116
1259
  collectionType,
1117
1260
  model,
1118
1261
  documentId,
1119
- params,
1120
- data: {
1121
- discardDraft
1122
- }
1262
+ params
1123
1263
  });
1124
1264
  if ("error" in res) {
1125
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1265
+ toggleNotification({
1266
+ type: "danger",
1267
+ message: formatAPIError(res.error)
1268
+ });
1126
1269
  return { error: res.error };
1127
1270
  }
1128
- trackUsage("didUnpublishEntry");
1129
1271
  toggleNotification({
1130
1272
  type: "success",
1131
1273
  message: formatMessage({
1132
- id: getTranslation("success.record.unpublish"),
1133
- defaultMessage: "Unpublished document"
1274
+ id: getTranslation("success.record.delete"),
1275
+ defaultMessage: "Deleted document"
1134
1276
  })
1135
1277
  });
1278
+ trackUsage("didDeleteEntry", trackerProperty);
1136
1279
  return res.data;
1137
1280
  } catch (err) {
1138
1281
  toggleNotification({
1139
1282
  type: "danger",
1140
1283
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1141
1284
  });
1285
+ trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
1142
1286
  throw err;
1143
1287
  }
1144
1288
  },
1145
- [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1289
+ [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
1146
1290
  );
1147
- const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1148
- const unpublishMany = React.useCallback(
1291
+ const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
1292
+ const deleteMany = React.useCallback(
1149
1293
  async ({ model, documentIds, params }) => {
1150
1294
  try {
1151
- trackUsage("willBulkUnpublishEntries");
1152
- const res = await unpublishManyDocuments({
1295
+ trackUsage("willBulkDeleteEntries");
1296
+ const res = await deleteManyDocuments({
1153
1297
  model,
1154
1298
  documentIds,
1155
1299
  params
1156
1300
  });
1157
1301
  if ("error" in res) {
1158
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1302
+ toggleNotification({
1303
+ type: "danger",
1304
+ message: formatAPIError(res.error)
1305
+ });
1159
1306
  return { error: res.error };
1160
1307
  }
1161
- trackUsage("didBulkUnpublishEntries");
1162
1308
  toggleNotification({
1163
1309
  type: "success",
1164
1310
  title: formatMessage({
1165
- id: getTranslation("success.records.unpublish"),
1166
- defaultMessage: "Successfully unpublished."
1311
+ id: getTranslation("success.records.delete"),
1312
+ defaultMessage: "Successfully deleted."
1167
1313
  }),
1168
1314
  message: ""
1169
1315
  });
1316
+ trackUsage("didBulkDeleteEntries");
1170
1317
  return res.data;
1171
1318
  } catch (err) {
1172
1319
  toggleNotification({
1173
1320
  type: "danger",
1174
1321
  message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1175
1322
  });
1176
- trackUsage("didNotBulkUnpublishEntries");
1323
+ trackUsage("didNotBulkDeleteEntries");
1177
1324
  throw err;
1178
1325
  }
1179
1326
  },
1180
- [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1327
+ [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
1181
1328
  );
1182
- const [createDocument] = useCreateDocumentMutation();
1183
- const create = React.useCallback(
1184
- async ({ model, params }, data, trackerProperty) => {
1329
+ const [discardDocument] = useDiscardDocumentMutation();
1330
+ const discard = React.useCallback(
1331
+ async ({ collectionType, model, documentId, params }) => {
1185
1332
  try {
1186
- const res = await createDocument({
1333
+ const res = await discardDocument({
1334
+ collectionType,
1187
1335
  model,
1188
- data,
1336
+ documentId,
1189
1337
  params
1190
1338
  });
1191
1339
  if ("error" in res) {
1192
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1193
- trackUsage("didNotCreateEntry", { error: res.error, ...trackerProperty });
1340
+ toggleNotification({
1341
+ type: "danger",
1342
+ message: formatAPIError(res.error)
1343
+ });
1194
1344
  return { error: res.error };
1195
1345
  }
1196
- trackUsage("didCreateEntry", trackerProperty);
1197
1346
  toggleNotification({
1198
1347
  type: "success",
1199
1348
  message: formatMessage({
1200
- id: getTranslation("success.record.save"),
1201
- defaultMessage: "Saved document"
1202
- })
1349
+ id: "content-manager.success.record.discard",
1350
+ defaultMessage: "Changes discarded"
1351
+ })
1352
+ });
1353
+ return res.data;
1354
+ } catch (err) {
1355
+ toggleNotification({
1356
+ type: "danger",
1357
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1358
+ });
1359
+ throw err;
1360
+ }
1361
+ },
1362
+ [discardDocument, formatAPIError, formatMessage, toggleNotification]
1363
+ );
1364
+ const [publishDocument] = usePublishDocumentMutation();
1365
+ const publish = React.useCallback(
1366
+ async ({ collectionType, model, documentId, params }, data) => {
1367
+ try {
1368
+ trackUsage("willPublishEntry");
1369
+ const res = await publishDocument({
1370
+ collectionType,
1371
+ model,
1372
+ documentId,
1373
+ data,
1374
+ params
1375
+ });
1376
+ if ("error" in res) {
1377
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1378
+ return { error: res.error };
1379
+ }
1380
+ trackUsage("didPublishEntry");
1381
+ toggleNotification({
1382
+ type: "success",
1383
+ message: formatMessage({
1384
+ id: getTranslation("success.record.publish"),
1385
+ defaultMessage: "Published document"
1386
+ })
1387
+ });
1388
+ return res.data;
1389
+ } catch (err) {
1390
+ toggleNotification({
1391
+ type: "danger",
1392
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1393
+ });
1394
+ throw err;
1395
+ }
1396
+ },
1397
+ [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1398
+ );
1399
+ const [publishManyDocuments] = usePublishManyDocumentsMutation();
1400
+ const publishMany = React.useCallback(
1401
+ async ({ model, documentIds, params }) => {
1402
+ try {
1403
+ const res = await publishManyDocuments({
1404
+ model,
1405
+ documentIds,
1406
+ params
1407
+ });
1408
+ if ("error" in res) {
1409
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1410
+ return { error: res.error };
1411
+ }
1412
+ toggleNotification({
1413
+ type: "success",
1414
+ message: formatMessage({
1415
+ id: getTranslation("success.record.publish"),
1416
+ defaultMessage: "Published document"
1417
+ })
1418
+ });
1419
+ return res.data;
1420
+ } catch (err) {
1421
+ toggleNotification({
1422
+ type: "danger",
1423
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1424
+ });
1425
+ throw err;
1426
+ }
1427
+ },
1428
+ [
1429
+ // trackUsage,
1430
+ publishManyDocuments,
1431
+ toggleNotification,
1432
+ formatMessage,
1433
+ formatAPIError
1434
+ ]
1435
+ );
1436
+ const [updateDocument] = useUpdateDocumentMutation();
1437
+ const update = React.useCallback(
1438
+ async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
1439
+ try {
1440
+ trackUsage("willEditEntry", trackerProperty);
1441
+ const res = await updateDocument({
1442
+ collectionType,
1443
+ model,
1444
+ documentId,
1445
+ data,
1446
+ params
1447
+ });
1448
+ if ("error" in res) {
1449
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1450
+ trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
1451
+ return { error: res.error };
1452
+ }
1453
+ trackUsage("didEditEntry", trackerProperty);
1454
+ toggleNotification({
1455
+ type: "success",
1456
+ message: formatMessage({
1457
+ id: getTranslation("success.record.save"),
1458
+ defaultMessage: "Saved document"
1459
+ })
1460
+ });
1461
+ return res.data;
1462
+ } catch (err) {
1463
+ trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
1464
+ toggleNotification({
1465
+ type: "danger",
1466
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1467
+ });
1468
+ throw err;
1469
+ }
1470
+ },
1471
+ [trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
1472
+ );
1473
+ const [unpublishDocument] = useUnpublishDocumentMutation();
1474
+ const unpublish = React.useCallback(
1475
+ async ({ collectionType, model, documentId, params }, discardDraft = false) => {
1476
+ try {
1477
+ trackUsage("willUnpublishEntry");
1478
+ const res = await unpublishDocument({
1479
+ collectionType,
1480
+ model,
1481
+ documentId,
1482
+ params,
1483
+ data: {
1484
+ discardDraft
1485
+ }
1486
+ });
1487
+ if ("error" in res) {
1488
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1489
+ return { error: res.error };
1490
+ }
1491
+ trackUsage("didUnpublishEntry");
1492
+ toggleNotification({
1493
+ type: "success",
1494
+ message: formatMessage({
1495
+ id: getTranslation("success.record.unpublish"),
1496
+ defaultMessage: "Unpublished document"
1497
+ })
1498
+ });
1499
+ return res.data;
1500
+ } catch (err) {
1501
+ toggleNotification({
1502
+ type: "danger",
1503
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1504
+ });
1505
+ throw err;
1506
+ }
1507
+ },
1508
+ [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1509
+ );
1510
+ const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1511
+ const unpublishMany = React.useCallback(
1512
+ async ({ model, documentIds, params }) => {
1513
+ try {
1514
+ trackUsage("willBulkUnpublishEntries");
1515
+ const res = await unpublishManyDocuments({
1516
+ model,
1517
+ documentIds,
1518
+ params
1519
+ });
1520
+ if ("error" in res) {
1521
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1522
+ return { error: res.error };
1523
+ }
1524
+ trackUsage("didBulkUnpublishEntries");
1525
+ toggleNotification({
1526
+ type: "success",
1527
+ title: formatMessage({
1528
+ id: getTranslation("success.records.unpublish"),
1529
+ defaultMessage: "Successfully unpublished."
1530
+ }),
1531
+ message: ""
1532
+ });
1533
+ return res.data;
1534
+ } catch (err) {
1535
+ toggleNotification({
1536
+ type: "danger",
1537
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1538
+ });
1539
+ trackUsage("didNotBulkUnpublishEntries");
1540
+ throw err;
1541
+ }
1542
+ },
1543
+ [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1544
+ );
1545
+ const [createDocument] = useCreateDocumentMutation();
1546
+ const create = React.useCallback(
1547
+ async ({ model, params }, data, trackerProperty) => {
1548
+ try {
1549
+ const res = await createDocument({
1550
+ model,
1551
+ data,
1552
+ params
1553
+ });
1554
+ if ("error" in res) {
1555
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1556
+ trackUsage("didNotCreateEntry", { error: res.error, ...trackerProperty });
1557
+ return { error: res.error };
1558
+ }
1559
+ trackUsage("didCreateEntry", trackerProperty);
1560
+ toggleNotification({
1561
+ type: "success",
1562
+ message: formatMessage({
1563
+ id: getTranslation("success.record.save"),
1564
+ defaultMessage: "Saved document"
1565
+ })
1203
1566
  });
1567
+ setCurrentStep("contentManager.success");
1204
1568
  return res.data;
1205
1569
  } catch (err) {
1206
1570
  toggleNotification({
@@ -1302,10 +1666,10 @@ const useDocumentActions = () => {
1302
1666
  update
1303
1667
  };
1304
1668
  };
1305
- const ProtectedHistoryPage = lazy(
1306
- () => import("./History-BowL3JKP.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1669
+ const ProtectedHistoryPage = React.lazy(
1670
+ () => import("./History-CqNgxkqK.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1307
1671
  );
1308
- const routes$1 = [
1672
+ const routes$2 = [
1309
1673
  {
1310
1674
  path: ":collectionType/:slug/:id/history",
1311
1675
  Component: ProtectedHistoryPage
@@ -1315,32 +1679,45 @@ const routes$1 = [
1315
1679
  Component: ProtectedHistoryPage
1316
1680
  }
1317
1681
  ];
1682
+ const ProtectedPreviewPage = React.lazy(
1683
+ () => import("./Preview-BaYGJ0nb.mjs").then((mod) => ({ default: mod.ProtectedPreviewPage }))
1684
+ );
1685
+ const routes$1 = [
1686
+ {
1687
+ path: ":collectionType/:slug/:id/preview",
1688
+ Component: ProtectedPreviewPage
1689
+ },
1690
+ {
1691
+ path: ":collectionType/:slug/preview",
1692
+ Component: ProtectedPreviewPage
1693
+ }
1694
+ ];
1318
1695
  const ProtectedEditViewPage = lazy(
1319
- () => import("./EditViewPage-De8GyU8P.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1696
+ () => import("./EditViewPage-BCjNxNlY.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1320
1697
  );
1321
1698
  const ProtectedListViewPage = lazy(
1322
- () => import("./ListViewPage-CXFUjZQC.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1699
+ () => import("./ListViewPage-D4ofkbjR.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1323
1700
  );
1324
1701
  const ProtectedListConfiguration = lazy(
1325
- () => import("./ListConfigurationPage-BpVOB-hn.mjs").then((mod) => ({
1702
+ () => import("./ListConfigurationPage-BbQjzKkQ.mjs").then((mod) => ({
1326
1703
  default: mod.ProtectedListConfiguration
1327
1704
  }))
1328
1705
  );
1329
1706
  const ProtectedEditConfigurationPage = lazy(
1330
- () => import("./EditConfigurationPage-CjUrEewK.mjs").then((mod) => ({
1707
+ () => import("./EditConfigurationPage-BhRSnUsL.mjs").then((mod) => ({
1331
1708
  default: mod.ProtectedEditConfigurationPage
1332
1709
  }))
1333
1710
  );
1334
1711
  const ProtectedComponentConfigurationPage = lazy(
1335
- () => import("./ComponentConfigurationPage-qemkOlnj.mjs").then((mod) => ({
1712
+ () => import("./ComponentConfigurationPage-bLQr82ce.mjs").then((mod) => ({
1336
1713
  default: mod.ProtectedComponentConfigurationPage
1337
1714
  }))
1338
1715
  );
1339
1716
  const NoPermissions = lazy(
1340
- () => import("./NoPermissionsPage-DVz3mzDz.mjs").then((mod) => ({ default: mod.NoPermissions }))
1717
+ () => import("./NoPermissionsPage-DhIiyWkk.mjs").then((mod) => ({ default: mod.NoPermissions }))
1341
1718
  );
1342
1719
  const NoContentType = lazy(
1343
- () => import("./NoContentTypePage-DuhOTp3x.mjs").then((mod) => ({ default: mod.NoContentType }))
1720
+ () => import("./NoContentTypePage-DyUx5mXh.mjs").then((mod) => ({ default: mod.NoContentType }))
1344
1721
  );
1345
1722
  const CollectionTypePages = () => {
1346
1723
  const { collectionType } = useParams();
@@ -1352,7 +1729,7 @@ const CollectionTypePages = () => {
1352
1729
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1353
1730
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1354
1731
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1355
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1732
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1356
1733
  const routes = [
1357
1734
  {
1358
1735
  path: LIST_RELATIVE_PATH,
@@ -1386,6 +1763,7 @@ const routes = [
1386
1763
  path: "no-content-types",
1387
1764
  Component: NoContentType
1388
1765
  },
1766
+ ...routes$2,
1389
1767
  ...routes$1
1390
1768
  ];
1391
1769
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1484,6 +1862,11 @@ const DocumentActionButton = (action) => {
1484
1862
  ) : null
1485
1863
  ] });
1486
1864
  };
1865
+ const MenuItem = styled(Menu.Item)`
1866
+ &:hover {
1867
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
1868
+ }
1869
+ `;
1487
1870
  const DocumentActionsMenu = ({
1488
1871
  actions: actions2,
1489
1872
  children,
@@ -1521,7 +1904,7 @@ const DocumentActionsMenu = ({
1521
1904
  };
1522
1905
  return /* @__PURE__ */ jsxs(Menu.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
1523
1906
  /* @__PURE__ */ jsxs(
1524
- StyledMoreButton,
1907
+ Menu.Trigger,
1525
1908
  {
1526
1909
  disabled: isDisabled,
1527
1910
  size: "S",
@@ -1539,51 +1922,35 @@ const DocumentActionsMenu = ({
1539
1922
  ]
1540
1923
  }
1541
1924
  ),
1542
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1925
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1543
1926
  actions2.map((action) => {
1544
1927
  return /* @__PURE__ */ jsx(
1545
- Menu.Item,
1928
+ MenuItem,
1546
1929
  {
1547
1930
  disabled: action.disabled,
1548
1931
  onSelect: handleClick(action),
1549
1932
  display: "block",
1550
- children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1551
- /* @__PURE__ */ jsxs(
1552
- Flex,
1553
- {
1554
- color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1555
- gap: 2,
1556
- tag: "span",
1557
- children: [
1558
- /* @__PURE__ */ jsx(
1559
- Flex,
1560
- {
1561
- tag: "span",
1562
- color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1563
- children: action.icon
1564
- }
1565
- ),
1566
- action.label
1567
- ]
1568
- }
1569
- ),
1570
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1571
- Flex,
1572
- {
1573
- alignItems: "center",
1574
- background: "alternative100",
1575
- borderStyle: "solid",
1576
- borderColor: "alternative200",
1577
- borderWidth: "1px",
1578
- height: 5,
1579
- paddingLeft: 2,
1580
- paddingRight: 2,
1581
- hasRadius: true,
1582
- color: "alternative600",
1583
- children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
1584
- }
1585
- )
1586
- ] })
1933
+ isVariantDanger: action.variant === "danger",
1934
+ isDisabled: action.disabled,
1935
+ children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxs(
1936
+ Flex,
1937
+ {
1938
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1939
+ gap: 2,
1940
+ tag: "span",
1941
+ children: [
1942
+ /* @__PURE__ */ jsx(
1943
+ Flex,
1944
+ {
1945
+ tag: "span",
1946
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1947
+ children: action.icon
1948
+ }
1949
+ ),
1950
+ action.label
1951
+ ]
1952
+ }
1953
+ ) })
1587
1954
  },
1588
1955
  action.id
1589
1956
  );
@@ -1637,11 +2004,6 @@ const convertActionVariantToIconColor = (variant = "secondary") => {
1637
2004
  return "primary600";
1638
2005
  }
1639
2006
  };
1640
- const StyledMoreButton = styled(Menu.Trigger)`
1641
- & > span {
1642
- display: flex;
1643
- }
1644
- `;
1645
2007
  const DocumentActionConfirmDialog = ({
1646
2008
  onClose,
1647
2009
  onCancel,
@@ -1668,11 +2030,11 @@ const DocumentActionConfirmDialog = ({
1668
2030
  /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1669
2031
  /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1670
2032
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1671
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
2033
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1672
2034
  id: "app.components.Button.cancel",
1673
2035
  defaultMessage: "Cancel"
1674
2036
  }) }) }),
1675
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
2037
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1676
2038
  id: "app.components.Button.confirm",
1677
2039
  defaultMessage: "Confirm"
1678
2040
  }) })
@@ -1699,6 +2061,18 @@ const DocumentActionModal = ({
1699
2061
  typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1700
2062
  ] }) });
1701
2063
  };
2064
+ const transformData = (data) => {
2065
+ if (Array.isArray(data)) {
2066
+ return data.map(transformData);
2067
+ }
2068
+ if (typeof data === "object" && data !== null) {
2069
+ if ("apiData" in data) {
2070
+ return data.apiData;
2071
+ }
2072
+ return mapValues(transformData)(data);
2073
+ }
2074
+ return data;
2075
+ };
1702
2076
  const PublishAction$1 = ({
1703
2077
  activeTab,
1704
2078
  documentId,
@@ -1713,6 +2087,7 @@ const PublishAction$1 = ({
1713
2087
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
1714
2088
  const isListView = useMatch(LIST_PATH) !== null;
1715
2089
  const isCloning = useMatch(CLONE_PATH) !== null;
2090
+ const { id } = useParams();
1716
2091
  const { formatMessage } = useIntl();
1717
2092
  const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1718
2093
  const { publish } = useDocumentActions();
@@ -1766,24 +2141,25 @@ const PublishAction$1 = ({
1766
2141
  }
1767
2142
  }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1768
2143
  React.useEffect(() => {
1769
- if (documentId && !isListView) {
1770
- const fetchDraftRelationsCount = async () => {
1771
- const { data, error } = await countDraftRelations({
1772
- collectionType,
1773
- model,
1774
- documentId,
1775
- params
1776
- });
1777
- if (error) {
1778
- throw error;
1779
- }
1780
- if (data) {
1781
- setServerCountOfDraftRelations(data.data);
1782
- }
1783
- };
1784
- fetchDraftRelationsCount();
2144
+ if (!document || !document.documentId || isListView) {
2145
+ return;
1785
2146
  }
1786
- }, [isListView, documentId, countDraftRelations, collectionType, model, params]);
2147
+ const fetchDraftRelationsCount = async () => {
2148
+ const { data, error } = await countDraftRelations({
2149
+ collectionType,
2150
+ model,
2151
+ documentId,
2152
+ params
2153
+ });
2154
+ if (error) {
2155
+ throw error;
2156
+ }
2157
+ if (data) {
2158
+ setServerCountOfDraftRelations(data.data);
2159
+ }
2160
+ };
2161
+ fetchDraftRelationsCount();
2162
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1787
2163
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1788
2164
  if (!schema?.options?.draftAndPublish) {
1789
2165
  return null;
@@ -1791,7 +2167,9 @@ const PublishAction$1 = ({
1791
2167
  const performPublish = async () => {
1792
2168
  setSubmitting(true);
1793
2169
  try {
1794
- const { errors } = await validate();
2170
+ const { errors } = await validate(true, {
2171
+ status: "published"
2172
+ });
1795
2173
  if (errors) {
1796
2174
  toggleNotification({
1797
2175
  type: "danger",
@@ -1809,13 +2187,15 @@ const PublishAction$1 = ({
1809
2187
  documentId,
1810
2188
  params
1811
2189
  },
1812
- formValues
2190
+ transformData(formValues)
1813
2191
  );
1814
2192
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1815
- navigate({
1816
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1817
- search: rawQuery
1818
- });
2193
+ if (id === "create") {
2194
+ navigate({
2195
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2196
+ search: rawQuery
2197
+ });
2198
+ }
1819
2199
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1820
2200
  setErrors(formatValidationErrors(res.error));
1821
2201
  }
@@ -1824,7 +2204,8 @@ const PublishAction$1 = ({
1824
2204
  }
1825
2205
  };
1826
2206
  const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1827
- const hasDraftRelations = totalDraftRelations > 0;
2207
+ const enableDraftRelationsCount = false;
2208
+ const hasDraftRelations = enableDraftRelationsCount;
1828
2209
  return {
1829
2210
  /**
1830
2211
  * Disabled when:
@@ -1841,9 +2222,6 @@ const PublishAction$1 = ({
1841
2222
  defaultMessage: "Publish"
1842
2223
  }),
1843
2224
  onClick: async () => {
1844
- if (hasDraftRelations) {
1845
- return;
1846
- }
1847
2225
  await performPublish();
1848
2226
  },
1849
2227
  dialog: hasDraftRelations ? {
@@ -1870,6 +2248,7 @@ const PublishAction$1 = ({
1870
2248
  };
1871
2249
  };
1872
2250
  PublishAction$1.type = "publish";
2251
+ PublishAction$1.position = "panel";
1873
2252
  const UpdateAction = ({
1874
2253
  activeTab,
1875
2254
  documentId,
@@ -1892,6 +2271,117 @@ const UpdateAction = ({
1892
2271
  const validate = useForm("UpdateAction", (state) => state.validate);
1893
2272
  const setErrors = useForm("UpdateAction", (state) => state.setErrors);
1894
2273
  const resetForm = useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
2274
+ const handleUpdate = React.useCallback(async () => {
2275
+ setSubmitting(true);
2276
+ try {
2277
+ if (!modified) {
2278
+ return;
2279
+ }
2280
+ const { errors } = await validate(true, {
2281
+ status: "draft"
2282
+ });
2283
+ if (errors) {
2284
+ toggleNotification({
2285
+ type: "danger",
2286
+ message: formatMessage({
2287
+ id: "content-manager.validation.error",
2288
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2289
+ })
2290
+ });
2291
+ return;
2292
+ }
2293
+ if (isCloning) {
2294
+ const res = await clone(
2295
+ {
2296
+ model,
2297
+ documentId: cloneMatch.params.origin,
2298
+ params
2299
+ },
2300
+ transformData(document)
2301
+ );
2302
+ if ("data" in res) {
2303
+ navigate(
2304
+ {
2305
+ pathname: `../${res.data.documentId}`,
2306
+ search: rawQuery
2307
+ },
2308
+ { relative: "path" }
2309
+ );
2310
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2311
+ setErrors(formatValidationErrors(res.error));
2312
+ }
2313
+ } else if (documentId || collectionType === SINGLE_TYPES) {
2314
+ const res = await update(
2315
+ {
2316
+ collectionType,
2317
+ model,
2318
+ documentId,
2319
+ params
2320
+ },
2321
+ transformData(document)
2322
+ );
2323
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2324
+ setErrors(formatValidationErrors(res.error));
2325
+ } else {
2326
+ resetForm();
2327
+ }
2328
+ } else {
2329
+ const res = await create(
2330
+ {
2331
+ model,
2332
+ params
2333
+ },
2334
+ transformData(document)
2335
+ );
2336
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2337
+ navigate(
2338
+ {
2339
+ pathname: `../${res.data.documentId}`,
2340
+ search: rawQuery
2341
+ },
2342
+ { replace: true, relative: "path" }
2343
+ );
2344
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2345
+ setErrors(formatValidationErrors(res.error));
2346
+ }
2347
+ }
2348
+ } finally {
2349
+ setSubmitting(false);
2350
+ }
2351
+ }, [
2352
+ clone,
2353
+ cloneMatch?.params.origin,
2354
+ collectionType,
2355
+ create,
2356
+ document,
2357
+ documentId,
2358
+ formatMessage,
2359
+ formatValidationErrors,
2360
+ isCloning,
2361
+ model,
2362
+ modified,
2363
+ navigate,
2364
+ params,
2365
+ rawQuery,
2366
+ resetForm,
2367
+ setErrors,
2368
+ setSubmitting,
2369
+ toggleNotification,
2370
+ update,
2371
+ validate
2372
+ ]);
2373
+ React.useEffect(() => {
2374
+ const handleKeyDown = (e) => {
2375
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
2376
+ e.preventDefault();
2377
+ handleUpdate();
2378
+ }
2379
+ };
2380
+ window.addEventListener("keydown", handleKeyDown);
2381
+ return () => {
2382
+ window.removeEventListener("keydown", handleKeyDown);
2383
+ };
2384
+ }, [handleUpdate]);
1895
2385
  return {
1896
2386
  /**
1897
2387
  * Disabled when:
@@ -1901,87 +2391,14 @@ const UpdateAction = ({
1901
2391
  */
1902
2392
  disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1903
2393
  label: formatMessage({
1904
- id: "content-manager.containers.Edit.save",
2394
+ id: "global.save",
1905
2395
  defaultMessage: "Save"
1906
2396
  }),
1907
- onClick: async () => {
1908
- setSubmitting(true);
1909
- try {
1910
- if (activeTab !== "draft") {
1911
- const { errors } = await validate();
1912
- if (errors) {
1913
- toggleNotification({
1914
- type: "danger",
1915
- message: formatMessage({
1916
- id: "content-manager.validation.error",
1917
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1918
- })
1919
- });
1920
- return;
1921
- }
1922
- }
1923
- if (isCloning) {
1924
- const res = await clone(
1925
- {
1926
- model,
1927
- documentId: cloneMatch.params.origin,
1928
- params
1929
- },
1930
- document
1931
- );
1932
- if ("data" in res) {
1933
- navigate(
1934
- {
1935
- pathname: `../${res.data.documentId}`,
1936
- search: rawQuery
1937
- },
1938
- { relative: "path" }
1939
- );
1940
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1941
- setErrors(formatValidationErrors(res.error));
1942
- }
1943
- } else if (documentId || collectionType === SINGLE_TYPES) {
1944
- const res = await update(
1945
- {
1946
- collectionType,
1947
- model,
1948
- documentId,
1949
- params
1950
- },
1951
- document
1952
- );
1953
- if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1954
- setErrors(formatValidationErrors(res.error));
1955
- } else {
1956
- resetForm();
1957
- }
1958
- } else {
1959
- const res = await create(
1960
- {
1961
- model,
1962
- params
1963
- },
1964
- document
1965
- );
1966
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1967
- navigate(
1968
- {
1969
- pathname: `../${res.data.documentId}`,
1970
- search: rawQuery
1971
- },
1972
- { replace: true, relative: "path" }
1973
- );
1974
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1975
- setErrors(formatValidationErrors(res.error));
1976
- }
1977
- }
1978
- } finally {
1979
- setSubmitting(false);
1980
- }
1981
- }
2397
+ onClick: handleUpdate
1982
2398
  };
1983
2399
  };
1984
2400
  UpdateAction.type = "update";
2401
+ UpdateAction.position = "panel";
1985
2402
  const UNPUBLISH_DRAFT_OPTIONS = {
1986
2403
  KEEP: "keep",
1987
2404
  DISCARD: "discard"
@@ -2014,7 +2431,7 @@ const UnpublishAction$1 = ({
2014
2431
  id: "app.utils.unpublish",
2015
2432
  defaultMessage: "Unpublish"
2016
2433
  }),
2017
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2434
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2018
2435
  onClick: async () => {
2019
2436
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
2020
2437
  if (!documentId) {
@@ -2104,6 +2521,7 @@ const UnpublishAction$1 = ({
2104
2521
  };
2105
2522
  };
2106
2523
  UnpublishAction$1.type = "unpublish";
2524
+ UnpublishAction$1.position = "panel";
2107
2525
  const DiscardAction = ({
2108
2526
  activeTab,
2109
2527
  documentId,
@@ -2126,7 +2544,7 @@ const DiscardAction = ({
2126
2544
  id: "content-manager.actions.discard.label",
2127
2545
  defaultMessage: "Discard changes"
2128
2546
  }),
2129
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2547
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2130
2548
  position: ["panel", "table-row"],
2131
2549
  variant: "danger",
2132
2550
  dialog: {
@@ -2154,11 +2572,7 @@ const DiscardAction = ({
2154
2572
  };
2155
2573
  };
2156
2574
  DiscardAction.type = "discard";
2157
- const StyledCrossCircle = styled(CrossCircle)`
2158
- path {
2159
- fill: currentColor;
2160
- }
2161
- `;
2575
+ DiscardAction.position = "panel";
2162
2576
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2163
2577
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2164
2578
  const RelativeTime = React.forwardRef(
@@ -2171,7 +2585,7 @@ const RelativeTime = React.forwardRef(
2171
2585
  });
2172
2586
  const unit = intervals.find((intervalUnit) => {
2173
2587
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2174
- });
2588
+ }) ?? "seconds";
2175
2589
  const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2176
2590
  const customInterval = customIntervals.find(
2177
2591
  (custom) => interval[custom.unit] < custom.threshold
@@ -2205,19 +2619,29 @@ const getDisplayName = ({
2205
2619
  return email ?? "";
2206
2620
  };
2207
2621
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2208
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2209
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2210
- return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2622
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2623
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2624
+ const { formatMessage } = useIntl();
2625
+ return /* @__PURE__ */ jsx(Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2626
+ id: `content-manager.containers.List.${status}`,
2627
+ defaultMessage: capitalise(status)
2628
+ }) }) });
2211
2629
  };
2212
2630
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2213
2631
  const { formatMessage } = useIntl();
2214
2632
  const isCloning = useMatch(CLONE_PATH) !== null;
2633
+ const params = useParams();
2215
2634
  const title = isCreating ? formatMessage({
2216
2635
  id: "content-manager.containers.edit.title.new",
2217
2636
  defaultMessage: "Create an entry"
2218
2637
  }) : documentTitle;
2219
2638
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2220
- /* @__PURE__ */ jsx(BackButton, {}),
2639
+ /* @__PURE__ */ jsx(
2640
+ BackButton,
2641
+ {
2642
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2643
+ }
2644
+ ),
2221
2645
  /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2222
2646
  /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2223
2647
  /* @__PURE__ */ jsx(HeaderToolbar, {})
@@ -2268,7 +2692,7 @@ const HeaderToolbar = () => {
2268
2692
  meta: isCloning ? void 0 : meta,
2269
2693
  collectionType
2270
2694
  },
2271
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2695
+ descriptions: plugins["content-manager"].apis.getDocumentActions("header"),
2272
2696
  children: (actions2) => {
2273
2697
  const headerActions = actions2.filter((action) => {
2274
2698
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2305,12 +2729,12 @@ const Information = ({ activeTab }) => {
2305
2729
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2306
2730
  label: formatMessage({
2307
2731
  id: "content-manager.containers.edit.information.last-published.label",
2308
- defaultMessage: "Last published"
2732
+ defaultMessage: "Published"
2309
2733
  }),
2310
2734
  value: formatMessage(
2311
2735
  {
2312
2736
  id: "content-manager.containers.edit.information.last-published.value",
2313
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2737
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2314
2738
  },
2315
2739
  {
2316
2740
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2323,12 +2747,12 @@ const Information = ({ activeTab }) => {
2323
2747
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2324
2748
  label: formatMessage({
2325
2749
  id: "content-manager.containers.edit.information.last-draft.label",
2326
- defaultMessage: "Last draft"
2750
+ defaultMessage: "Updated"
2327
2751
  }),
2328
2752
  value: formatMessage(
2329
2753
  {
2330
2754
  id: "content-manager.containers.edit.information.last-draft.value",
2331
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2755
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2332
2756
  },
2333
2757
  {
2334
2758
  time: /* @__PURE__ */ jsx(
@@ -2346,12 +2770,12 @@ const Information = ({ activeTab }) => {
2346
2770
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2347
2771
  label: formatMessage({
2348
2772
  id: "content-manager.containers.edit.information.document.label",
2349
- defaultMessage: "Document"
2773
+ defaultMessage: "Created"
2350
2774
  }),
2351
2775
  value: formatMessage(
2352
2776
  {
2353
2777
  id: "content-manager.containers.edit.information.document.value",
2354
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2778
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2355
2779
  },
2356
2780
  {
2357
2781
  time: /* @__PURE__ */ jsx(
@@ -2389,25 +2813,77 @@ const Information = ({ activeTab }) => {
2389
2813
  );
2390
2814
  };
2391
2815
  const HeaderActions = ({ actions: actions2 }) => {
2392
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2393
- if ("options" in action) {
2816
+ const [dialogId, setDialogId] = React.useState(null);
2817
+ const handleClick = (action) => async (e) => {
2818
+ if (!("options" in action)) {
2819
+ const { onClick = () => false, dialog, id } = action;
2820
+ const muteDialog = await onClick(e);
2821
+ if (dialog && !muteDialog) {
2822
+ e.preventDefault();
2823
+ setDialogId(id);
2824
+ }
2825
+ }
2826
+ };
2827
+ const handleClose = () => {
2828
+ setDialogId(null);
2829
+ };
2830
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2831
+ if (action.options) {
2394
2832
  return /* @__PURE__ */ jsx(
2395
2833
  SingleSelect,
2396
2834
  {
2397
2835
  size: "S",
2398
- disabled: action.disabled,
2399
- "aria-label": action.label,
2400
2836
  onChange: action.onSelect,
2401
- value: action.value,
2837
+ "aria-label": action.label,
2838
+ ...action,
2402
2839
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2403
2840
  },
2404
2841
  action.id
2405
2842
  );
2406
2843
  } else {
2407
- return null;
2844
+ if (action.type === "icon") {
2845
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
2846
+ /* @__PURE__ */ jsx(
2847
+ IconButton,
2848
+ {
2849
+ disabled: action.disabled,
2850
+ label: action.label,
2851
+ size: "S",
2852
+ onClick: handleClick(action),
2853
+ children: action.icon
2854
+ }
2855
+ ),
2856
+ action.dialog ? /* @__PURE__ */ jsx(
2857
+ HeaderActionDialog,
2858
+ {
2859
+ ...action.dialog,
2860
+ isOpen: dialogId === action.id,
2861
+ onClose: handleClose
2862
+ }
2863
+ ) : null
2864
+ ] }, action.id);
2865
+ }
2408
2866
  }
2409
2867
  }) });
2410
2868
  };
2869
+ const HeaderActionDialog = ({
2870
+ onClose,
2871
+ onCancel,
2872
+ title,
2873
+ content: Content,
2874
+ isOpen
2875
+ }) => {
2876
+ const handleClose = async () => {
2877
+ if (onCancel) {
2878
+ await onCancel();
2879
+ }
2880
+ onClose();
2881
+ };
2882
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2883
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2884
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
2885
+ ] }) });
2886
+ };
2411
2887
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2412
2888
  const navigate = useNavigate();
2413
2889
  const { formatMessage } = useIntl();
@@ -2424,6 +2900,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2424
2900
  };
2425
2901
  };
2426
2902
  ConfigureTheViewAction.type = "configure-the-view";
2903
+ ConfigureTheViewAction.position = "header";
2427
2904
  const EditTheModelAction = ({ model }) => {
2428
2905
  const navigate = useNavigate();
2429
2906
  const { formatMessage } = useIntl();
@@ -2440,6 +2917,7 @@ const EditTheModelAction = ({ model }) => {
2440
2917
  };
2441
2918
  };
2442
2919
  EditTheModelAction.type = "edit-the-model";
2920
+ EditTheModelAction.position = "header";
2443
2921
  const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2444
2922
  const navigate = useNavigate();
2445
2923
  const { formatMessage } = useIntl();
@@ -2448,12 +2926,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2448
2926
  const { delete: deleteAction } = useDocumentActions();
2449
2927
  const { toggleNotification } = useNotification();
2450
2928
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2929
+ const isLocalized = document?.locale != null;
2451
2930
  return {
2452
2931
  disabled: !canDelete || !document,
2453
- label: formatMessage({
2454
- id: "content-manager.actions.delete.label",
2455
- defaultMessage: "Delete document"
2456
- }),
2932
+ label: formatMessage(
2933
+ {
2934
+ id: "content-manager.actions.delete.label",
2935
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2936
+ },
2937
+ { isLocalized }
2938
+ ),
2457
2939
  icon: /* @__PURE__ */ jsx(Trash, {}),
2458
2940
  dialog: {
2459
2941
  type: "dialog",
@@ -2492,420 +2974,119 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2492
2974
  collectionType,
2493
2975
  params: {
2494
2976
  locale: "*"
2495
- }
2496
- });
2497
- if (!("error" in res)) {
2498
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2499
- }
2500
- } finally {
2501
- if (!listViewPathMatch) {
2502
- setSubmitting(false);
2503
- }
2504
- }
2505
- }
2506
- },
2507
- variant: "danger",
2508
- position: ["header", "table-row"]
2509
- };
2510
- };
2511
- DeleteAction$1.type = "delete";
2512
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2513
- const Panels = () => {
2514
- const isCloning = useMatch(CLONE_PATH) !== null;
2515
- const [
2516
- {
2517
- query: { status }
2518
- }
2519
- ] = useQueryParams({
2520
- status: "draft"
2521
- });
2522
- const { model, id, document, meta, collectionType } = useDoc();
2523
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2524
- const props = {
2525
- activeTab: status,
2526
- model,
2527
- documentId: id,
2528
- document: isCloning ? void 0 : document,
2529
- meta: isCloning ? void 0 : meta,
2530
- collectionType
2531
- };
2532
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2533
- DescriptionComponentRenderer,
2534
- {
2535
- props,
2536
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2537
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2538
- }
2539
- ) });
2540
- };
2541
- const ActionsPanel = () => {
2542
- const { formatMessage } = useIntl();
2543
- return {
2544
- title: formatMessage({
2545
- id: "content-manager.containers.edit.panels.default.title",
2546
- defaultMessage: "Document"
2547
- }),
2548
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2549
- };
2550
- };
2551
- ActionsPanel.type = "actions";
2552
- const ActionsPanelContent = () => {
2553
- const isCloning = useMatch(CLONE_PATH) !== null;
2554
- const [
2555
- {
2556
- query: { status = "draft" }
2557
- }
2558
- ] = useQueryParams();
2559
- const { model, id, document, meta, collectionType } = useDoc();
2560
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2561
- const props = {
2562
- activeTab: status,
2563
- model,
2564
- documentId: id,
2565
- document: isCloning ? void 0 : document,
2566
- meta: isCloning ? void 0 : meta,
2567
- collectionType
2568
- };
2569
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2570
- /* @__PURE__ */ jsx(
2571
- DescriptionComponentRenderer,
2572
- {
2573
- props,
2574
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2575
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2576
- }
2577
- ),
2578
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2579
- ] });
2580
- };
2581
- const Panel = React.forwardRef(({ children, title }, ref) => {
2582
- return /* @__PURE__ */ jsxs(
2583
- Flex,
2584
- {
2585
- ref,
2586
- tag: "aside",
2587
- "aria-labelledby": "additional-information",
2588
- background: "neutral0",
2589
- borderColor: "neutral150",
2590
- hasRadius: true,
2591
- paddingBottom: 4,
2592
- paddingLeft: 4,
2593
- paddingRight: 4,
2594
- paddingTop: 4,
2595
- shadow: "tableShadow",
2596
- gap: 3,
2597
- direction: "column",
2598
- justifyContent: "stretch",
2599
- alignItems: "flex-start",
2600
- children: [
2601
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2602
- children
2603
- ]
2604
- }
2605
- );
2606
- });
2607
- const HOOKS = {
2608
- /**
2609
- * Hook that allows to mutate the displayed headers of the list view table
2610
- * @constant
2611
- * @type {string}
2612
- */
2613
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2614
- /**
2615
- * Hook that allows to mutate the CM's collection types links pre-set filters
2616
- * @constant
2617
- * @type {string}
2618
- */
2619
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2620
- /**
2621
- * Hook that allows to mutate the CM's edit view layout
2622
- * @constant
2623
- * @type {string}
2624
- */
2625
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2626
- /**
2627
- * Hook that allows to mutate the CM's single types links pre-set filters
2628
- * @constant
2629
- * @type {string}
2630
- */
2631
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2632
- };
2633
- const contentTypesApi = contentManagerApi.injectEndpoints({
2634
- endpoints: (builder) => ({
2635
- getContentTypeConfiguration: builder.query({
2636
- query: (uid) => ({
2637
- url: `/content-manager/content-types/${uid}/configuration`,
2638
- method: "GET"
2639
- }),
2640
- transformResponse: (response) => response.data,
2641
- providesTags: (_result, _error, uid) => [
2642
- { type: "ContentTypesConfiguration", id: uid },
2643
- { type: "ContentTypeSettings", id: "LIST" }
2644
- ]
2645
- }),
2646
- getAllContentTypeSettings: builder.query({
2647
- query: () => "/content-manager/content-types-settings",
2648
- transformResponse: (response) => response.data,
2649
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2650
- }),
2651
- updateContentTypeConfiguration: builder.mutation({
2652
- query: ({ uid, ...body }) => ({
2653
- url: `/content-manager/content-types/${uid}/configuration`,
2654
- method: "PUT",
2655
- data: body
2656
- }),
2657
- transformResponse: (response) => response.data,
2658
- invalidatesTags: (_result, _error, { uid }) => [
2659
- { type: "ContentTypesConfiguration", id: uid },
2660
- { type: "ContentTypeSettings", id: "LIST" },
2661
- // Is this necessary?
2662
- { type: "InitialData" }
2663
- ]
2664
- })
2665
- })
2666
- });
2667
- const {
2668
- useGetContentTypeConfigurationQuery,
2669
- useGetAllContentTypeSettingsQuery,
2670
- useUpdateContentTypeConfigurationMutation
2671
- } = contentTypesApi;
2672
- const checkIfAttributeIsDisplayable = (attribute) => {
2673
- const { type } = attribute;
2674
- if (type === "relation") {
2675
- return !attribute.relation.toLowerCase().includes("morph");
2676
- }
2677
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2678
- };
2679
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2680
- if (!mainFieldName) {
2681
- return void 0;
2682
- }
2683
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2684
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2685
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2686
- );
2687
- return {
2688
- name: mainFieldName,
2689
- type: mainFieldType ?? "string"
2690
- };
2691
- };
2692
- const DEFAULT_SETTINGS = {
2693
- bulkable: false,
2694
- filterable: false,
2695
- searchable: false,
2696
- pagination: false,
2697
- defaultSortBy: "",
2698
- defaultSortOrder: "asc",
2699
- mainField: "id",
2700
- pageSize: 10
2701
- };
2702
- const useDocumentLayout = (model) => {
2703
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2704
- const [{ query }] = useQueryParams();
2705
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2706
- const { toggleNotification } = useNotification();
2707
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2708
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2709
- const {
2710
- data,
2711
- isLoading: isLoadingConfigs,
2712
- error,
2713
- isFetching: isFetchingConfigs
2714
- } = useGetContentTypeConfigurationQuery(model);
2715
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2716
- React.useEffect(() => {
2717
- if (error) {
2718
- toggleNotification({
2719
- type: "danger",
2720
- message: formatAPIError(error)
2721
- });
2722
- }
2723
- }, [error, formatAPIError, toggleNotification]);
2724
- const editLayout = React.useMemo(
2725
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2726
- layout: [],
2727
- components: {},
2728
- metadatas: {},
2729
- options: {},
2730
- settings: DEFAULT_SETTINGS
2731
- },
2732
- [data, isLoading, schemas, schema, components]
2733
- );
2734
- const listLayout = React.useMemo(() => {
2735
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2736
- layout: [],
2737
- metadatas: {},
2738
- options: {},
2739
- settings: DEFAULT_SETTINGS
2740
- };
2741
- }, [data, isLoading, schemas, schema, components]);
2742
- const { layout: edit } = React.useMemo(
2743
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2744
- layout: editLayout,
2745
- query
2746
- }),
2747
- [editLayout, query, runHookWaterfall]
2748
- );
2749
- return {
2750
- error,
2751
- isLoading,
2752
- edit,
2753
- list: listLayout
2754
- };
2755
- };
2756
- const useDocLayout = () => {
2757
- const { model } = useDoc();
2758
- return useDocumentLayout(model);
2759
- };
2760
- const formatEditLayout = (data, {
2761
- schemas,
2762
- schema,
2763
- components
2764
- }) => {
2765
- let currentPanelIndex = 0;
2766
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2767
- data.contentType.layouts.edit,
2768
- schema?.attributes,
2769
- data.contentType.metadatas,
2770
- { configurations: data.components, schemas: components },
2771
- schemas
2772
- ).reduce((panels, row) => {
2773
- if (row.some((field) => field.type === "dynamiczone")) {
2774
- panels.push([row]);
2775
- currentPanelIndex += 2;
2776
- } else {
2777
- if (!panels[currentPanelIndex]) {
2778
- panels.push([]);
2779
- }
2780
- panels[currentPanelIndex].push(row);
2781
- }
2782
- return panels;
2783
- }, []);
2784
- const componentEditAttributes = Object.entries(data.components).reduce(
2785
- (acc, [uid, configuration]) => {
2786
- acc[uid] = {
2787
- layout: convertEditLayoutToFieldLayouts(
2788
- configuration.layouts.edit,
2789
- components[uid].attributes,
2790
- configuration.metadatas
2791
- ),
2792
- settings: {
2793
- ...configuration.settings,
2794
- icon: components[uid].info.icon,
2795
- displayName: components[uid].info.displayName
2977
+ }
2978
+ });
2979
+ if (!("error" in res)) {
2980
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2981
+ }
2982
+ } finally {
2983
+ if (!listViewPathMatch) {
2984
+ setSubmitting(false);
2985
+ }
2796
2986
  }
2797
- };
2798
- return acc;
2799
- },
2800
- {}
2801
- );
2802
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2803
- (acc, [attribute, metadata]) => {
2804
- return {
2805
- ...acc,
2806
- [attribute]: metadata.edit
2807
- };
2808
- },
2809
- {}
2810
- );
2811
- return {
2812
- layout: panelledEditAttributes,
2813
- components: componentEditAttributes,
2814
- metadatas: editMetadatas,
2815
- settings: {
2816
- ...data.contentType.settings,
2817
- displayName: schema?.info.displayName
2987
+ }
2818
2988
  },
2819
- options: {
2820
- ...schema?.options,
2821
- ...schema?.pluginOptions,
2822
- ...data.contentType.options
2823
- }
2989
+ variant: "danger",
2990
+ position: ["header", "table-row"]
2824
2991
  };
2825
2992
  };
2826
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2827
- return rows.map(
2828
- (row) => row.map((field) => {
2829
- const attribute = attributes[field.name];
2830
- if (!attribute) {
2831
- return null;
2832
- }
2833
- const { edit: metadata } = metadatas[field.name];
2834
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2835
- return {
2836
- attribute,
2837
- disabled: !metadata.editable,
2838
- hint: metadata.description,
2839
- label: metadata.label ?? "",
2840
- name: field.name,
2841
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2842
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2843
- schemas,
2844
- components: components?.schemas ?? {}
2845
- }),
2846
- placeholder: metadata.placeholder ?? "",
2847
- required: attribute.required ?? false,
2848
- size: field.size,
2849
- unique: "unique" in attribute ? attribute.unique : false,
2850
- visible: metadata.visible ?? true,
2851
- type: attribute.type
2852
- };
2853
- }).filter((field) => field !== null)
2854
- );
2993
+ DeleteAction$1.type = "delete";
2994
+ DeleteAction$1.position = ["header", "table-row"];
2995
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2996
+ const Panels = () => {
2997
+ const isCloning = useMatch(CLONE_PATH) !== null;
2998
+ const [
2999
+ {
3000
+ query: { status }
3001
+ }
3002
+ ] = useQueryParams({
3003
+ status: "draft"
3004
+ });
3005
+ const { model, id, document, meta, collectionType } = useDoc();
3006
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
3007
+ const props = {
3008
+ activeTab: status,
3009
+ model,
3010
+ documentId: id,
3011
+ document: isCloning ? void 0 : document,
3012
+ meta: isCloning ? void 0 : meta,
3013
+ collectionType
3014
+ };
3015
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
3016
+ DescriptionComponentRenderer,
3017
+ {
3018
+ props,
3019
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
3020
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
3021
+ }
3022
+ ) });
2855
3023
  };
2856
- const formatListLayout = (data, {
2857
- schemas,
2858
- schema,
2859
- components
2860
- }) => {
2861
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2862
- (acc, [attribute, metadata]) => {
2863
- return {
2864
- ...acc,
2865
- [attribute]: metadata.list
2866
- };
2867
- },
2868
- {}
2869
- );
2870
- const listAttributes = convertListLayoutToFieldLayouts(
2871
- data.contentType.layouts.list,
2872
- schema?.attributes,
2873
- listMetadatas,
2874
- { configurations: data.components, schemas: components },
2875
- schemas
2876
- );
3024
+ const ActionsPanel = () => {
3025
+ const { formatMessage } = useIntl();
2877
3026
  return {
2878
- layout: listAttributes,
2879
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2880
- metadatas: listMetadatas,
2881
- options: {
2882
- ...schema?.options,
2883
- ...schema?.pluginOptions,
2884
- ...data.contentType.options
2885
- }
3027
+ title: formatMessage({
3028
+ id: "content-manager.containers.edit.panels.default.title",
3029
+ defaultMessage: "Entry"
3030
+ }),
3031
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2886
3032
  };
2887
3033
  };
2888
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2889
- return columns.map((name) => {
2890
- const attribute = attributes[name];
2891
- if (!attribute) {
2892
- return null;
3034
+ ActionsPanel.type = "actions";
3035
+ const ActionsPanelContent = () => {
3036
+ const isCloning = useMatch(CLONE_PATH) !== null;
3037
+ const [
3038
+ {
3039
+ query: { status = "draft" }
2893
3040
  }
2894
- const metadata = metadatas[name];
2895
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2896
- return {
2897
- attribute,
2898
- label: metadata.label ?? "",
2899
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2900
- schemas,
2901
- components: components?.schemas ?? {}
2902
- }),
2903
- name,
2904
- searchable: metadata.searchable ?? true,
2905
- sortable: metadata.sortable ?? true
2906
- };
2907
- }).filter((field) => field !== null);
3041
+ ] = useQueryParams();
3042
+ const { model, id, document, meta, collectionType } = useDoc();
3043
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
3044
+ const props = {
3045
+ activeTab: status,
3046
+ model,
3047
+ documentId: id,
3048
+ document: isCloning ? void 0 : document,
3049
+ meta: isCloning ? void 0 : meta,
3050
+ collectionType
3051
+ };
3052
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
3053
+ /* @__PURE__ */ jsx(
3054
+ DescriptionComponentRenderer,
3055
+ {
3056
+ props,
3057
+ descriptions: plugins["content-manager"].apis.getDocumentActions("panel"),
3058
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
3059
+ }
3060
+ ),
3061
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
3062
+ ] });
2908
3063
  };
3064
+ const Panel = React.forwardRef(({ children, title }, ref) => {
3065
+ return /* @__PURE__ */ jsxs(
3066
+ Flex,
3067
+ {
3068
+ ref,
3069
+ tag: "aside",
3070
+ "aria-labelledby": "additional-information",
3071
+ background: "neutral0",
3072
+ borderColor: "neutral150",
3073
+ hasRadius: true,
3074
+ paddingBottom: 4,
3075
+ paddingLeft: 4,
3076
+ paddingRight: 4,
3077
+ paddingTop: 4,
3078
+ shadow: "tableShadow",
3079
+ gap: 3,
3080
+ direction: "column",
3081
+ justifyContent: "stretch",
3082
+ alignItems: "flex-start",
3083
+ children: [
3084
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3085
+ children
3086
+ ]
3087
+ }
3088
+ );
3089
+ });
2909
3090
  const ConfirmBulkActionDialog = ({
2910
3091
  onToggleDialog,
2911
3092
  isOpen = false,
@@ -2944,6 +3125,7 @@ const ConfirmDialogPublishAll = ({
2944
3125
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2945
3126
  const { model, schema } = useDoc();
2946
3127
  const [{ query }] = useQueryParams();
3128
+ const enableDraftRelationsCount = false;
2947
3129
  const {
2948
3130
  data: countDraftRelations = 0,
2949
3131
  isLoading,
@@ -2955,7 +3137,7 @@ const ConfirmDialogPublishAll = ({
2955
3137
  locale: query?.plugins?.i18n?.locale
2956
3138
  },
2957
3139
  {
2958
- skip: selectedEntries.length === 0
3140
+ skip: !enableDraftRelationsCount
2959
3141
  }
2960
3142
  );
2961
3143
  React.useEffect(() => {
@@ -3140,7 +3322,7 @@ const SelectedEntriesTableContent = ({
3140
3322
  status: row.status
3141
3323
  }
3142
3324
  ) }),
3143
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3325
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3144
3326
  IconButton,
3145
3327
  {
3146
3328
  tag: Link,
@@ -3149,23 +3331,16 @@ const SelectedEntriesTableContent = ({
3149
3331
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3150
3332
  },
3151
3333
  state: { from: pathname },
3152
- label: formatMessage(
3153
- { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3154
- {
3155
- target: formatMessage(
3156
- {
3157
- id: "content-manager.components.ListViewHelperPluginTable.row-line",
3158
- defaultMessage: "item line {number}"
3159
- },
3160
- { number: index2 + 1 }
3161
- )
3162
- }
3163
- ),
3334
+ label: formatMessage({
3335
+ id: "content-manager.bulk-publish.edit",
3336
+ defaultMessage: "Edit"
3337
+ }),
3164
3338
  target: "_blank",
3165
3339
  marginLeft: "auto",
3166
- children: /* @__PURE__ */ jsx(Pencil, {})
3340
+ variant: "ghost",
3341
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3167
3342
  }
3168
- ) })
3343
+ ) }) })
3169
3344
  ] }, row.id)) })
3170
3345
  ] });
3171
3346
  };
@@ -3333,8 +3508,7 @@ const PublishAction = ({ documents, model }) => {
3333
3508
  const refetchList = () => {
3334
3509
  contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3335
3510
  };
3336
- if (!showPublishButton)
3337
- return null;
3511
+ if (!showPublishButton) return null;
3338
3512
  return {
3339
3513
  actionType: "publish",
3340
3514
  variant: "tertiary",
@@ -3402,8 +3576,7 @@ const DeleteAction = ({ documents, model }) => {
3402
3576
  selectRow([]);
3403
3577
  }
3404
3578
  };
3405
- if (!hasDeletePermission)
3406
- return null;
3579
+ if (!hasDeletePermission) return null;
3407
3580
  return {
3408
3581
  variant: "danger-light",
3409
3582
  label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
@@ -3452,8 +3625,7 @@ const UnpublishAction = ({ documents, model }) => {
3452
3625
  }
3453
3626
  };
3454
3627
  const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3455
- if (!showUnpublishButton)
3456
- return null;
3628
+ if (!showUnpublishButton) return null;
3457
3629
  return {
3458
3630
  variant: "tertiary",
3459
3631
  label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
@@ -3558,7 +3730,7 @@ const TableActions = ({ document }) => {
3558
3730
  DescriptionComponentRenderer,
3559
3731
  {
3560
3732
  props,
3561
- descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3733
+ descriptions: plugins["content-manager"].apis.getDocumentActions("table-row").filter((action) => action.name !== "PublishAction"),
3562
3734
  children: (actions2) => {
3563
3735
  const tableRowActions = actions2.filter((action) => {
3564
3736
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3617,6 +3789,7 @@ const EditAction = ({ documentId }) => {
3617
3789
  };
3618
3790
  };
3619
3791
  EditAction.type = "edit";
3792
+ EditAction.position = "table-row";
3620
3793
  const StyledPencil = styled(Pencil)`
3621
3794
  path {
3622
3795
  fill: currentColor;
@@ -3693,6 +3866,7 @@ const CloneAction = ({ model, documentId }) => {
3693
3866
  };
3694
3867
  };
3695
3868
  CloneAction.type = "clone";
3869
+ CloneAction.position = "table-row";
3696
3870
  const StyledDuplicate = styled(Duplicate)`
3697
3871
  path {
3698
3872
  fill: currentColor;
@@ -3779,7 +3953,14 @@ class ContentManagerPlugin {
3779
3953
  addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3780
3954
  addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3781
3955
  getBulkActions: () => this.bulkActions,
3782
- getDocumentActions: () => this.documentActions,
3956
+ getDocumentActions: (position) => {
3957
+ if (position) {
3958
+ return this.documentActions.filter(
3959
+ (action) => action.position == void 0 || [action.position].flat().includes(position)
3960
+ );
3961
+ }
3962
+ return this.documentActions;
3963
+ },
3783
3964
  getEditViewSidePanels: () => this.editViewSidePanels,
3784
3965
  getHeaderActions: () => this.headerActions
3785
3966
  }
@@ -3789,10 +3970,8 @@ class ContentManagerPlugin {
3789
3970
  const getPrintableType = (value) => {
3790
3971
  const nativeType = typeof value;
3791
3972
  if (nativeType === "object") {
3792
- if (value === null)
3793
- return "null";
3794
- if (Array.isArray(value))
3795
- return "array";
3973
+ if (value === null) return "null";
3974
+ if (Array.isArray(value)) return "array";
3796
3975
  if (value instanceof Object && value.constructor.name !== "Object") {
3797
3976
  return value.constructor.name;
3798
3977
  }
@@ -3803,17 +3982,27 @@ const HistoryAction = ({ model, document }) => {
3803
3982
  const { formatMessage } = useIntl();
3804
3983
  const [{ query }] = useQueryParams();
3805
3984
  const navigate = useNavigate();
3985
+ const { trackUsage } = useTracking();
3986
+ const { pathname } = useLocation();
3806
3987
  const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
3807
3988
  if (!window.strapi.features.isEnabled("cms-content-history")) {
3808
3989
  return null;
3809
3990
  }
3991
+ const handleOnClick = () => {
3992
+ const destination = { pathname: "history", search: pluginsQueryParams };
3993
+ trackUsage("willNavigate", {
3994
+ from: pathname,
3995
+ to: `${pathname}/${destination.pathname}`
3996
+ });
3997
+ navigate(destination);
3998
+ };
3810
3999
  return {
3811
4000
  icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
3812
4001
  label: formatMessage({
3813
4002
  id: "content-manager.history.document-action",
3814
4003
  defaultMessage: "Content History"
3815
4004
  }),
3816
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
4005
+ onClick: handleOnClick,
3817
4006
  disabled: (
3818
4007
  /**
3819
4008
  * The user is creating a new document.
@@ -3835,6 +4024,7 @@ const HistoryAction = ({ model, document }) => {
3835
4024
  };
3836
4025
  };
3837
4026
  HistoryAction.type = "history";
4027
+ HistoryAction.position = "header";
3838
4028
  const historyAdmin = {
3839
4029
  bootstrap(app) {
3840
4030
  const { addDocumentAction } = app.getPlugin("content-manager").apis;
@@ -3881,6 +4071,88 @@ const { setInitialData } = actions;
3881
4071
  const reducer = combineReducers({
3882
4072
  app: reducer$1
3883
4073
  });
4074
+ const previewApi = contentManagerApi.injectEndpoints({
4075
+ endpoints: (builder) => ({
4076
+ getPreviewUrl: builder.query({
4077
+ query({ query, params }) {
4078
+ return {
4079
+ url: `/content-manager/preview/url/${params.contentType}`,
4080
+ method: "GET",
4081
+ config: {
4082
+ params: query
4083
+ }
4084
+ };
4085
+ }
4086
+ })
4087
+ })
4088
+ });
4089
+ const { useGetPreviewUrlQuery } = previewApi;
4090
+ const ConditionalTooltip = ({ isShown, label, children }) => {
4091
+ if (isShown) {
4092
+ return /* @__PURE__ */ jsx(Tooltip, { label, children });
4093
+ }
4094
+ return children;
4095
+ };
4096
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4097
+ const { formatMessage } = useIntl();
4098
+ const { trackUsage } = useTracking();
4099
+ const { pathname } = useLocation();
4100
+ const [{ query }] = useQueryParams();
4101
+ const isModified = useForm("PreviewSidePanel", (state) => state.modified);
4102
+ const { data, error } = useGetPreviewUrlQuery({
4103
+ params: {
4104
+ contentType: model
4105
+ },
4106
+ query: {
4107
+ documentId,
4108
+ locale: document?.locale,
4109
+ status: document?.status
4110
+ }
4111
+ });
4112
+ if (!data?.data?.url || error) {
4113
+ return null;
4114
+ }
4115
+ const trackNavigation = () => {
4116
+ const destinationPathname = pathname.replace(/\/$/, "") + "/preview";
4117
+ trackUsage("willNavigate", { from: pathname, to: destinationPathname });
4118
+ };
4119
+ return {
4120
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4121
+ content: /* @__PURE__ */ jsx(
4122
+ ConditionalTooltip,
4123
+ {
4124
+ label: formatMessage({
4125
+ id: "content-manager.preview.panel.button-disabled-tooltip",
4126
+ defaultMessage: "Please save to open the preview"
4127
+ }),
4128
+ isShown: isModified,
4129
+ children: /* @__PURE__ */ jsx(Box, { cursor: "not-allowed", width: "100%", children: /* @__PURE__ */ jsx(
4130
+ Button,
4131
+ {
4132
+ variant: "tertiary",
4133
+ tag: Link,
4134
+ to: { pathname: "preview", search: stringify(query, { encode: false }) },
4135
+ onClick: trackNavigation,
4136
+ width: "100%",
4137
+ disabled: isModified,
4138
+ pointerEvents: isModified ? "none" : void 0,
4139
+ tabIndex: isModified ? -1 : void 0,
4140
+ children: formatMessage({
4141
+ id: "content-manager.preview.panel.button",
4142
+ defaultMessage: "Open preview"
4143
+ })
4144
+ }
4145
+ ) })
4146
+ }
4147
+ )
4148
+ };
4149
+ };
4150
+ const previewAdmin = {
4151
+ bootstrap(app) {
4152
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4153
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4154
+ }
4155
+ };
3884
4156
  const index = {
3885
4157
  register(app) {
3886
4158
  const cm = new ContentManagerPlugin();
@@ -3900,7 +4172,7 @@ const index = {
3900
4172
  app.router.addRoute({
3901
4173
  path: "content-manager/*",
3902
4174
  lazy: async () => {
3903
- const { Layout } = await import("./layout-ls3gxfpH.mjs");
4175
+ const { Layout } = await import("./layout-Bxsv5mP7.mjs");
3904
4176
  return {
3905
4177
  Component: Layout
3906
4178
  };
@@ -3913,11 +4185,14 @@ const index = {
3913
4185
  if (typeof historyAdmin.bootstrap === "function") {
3914
4186
  historyAdmin.bootstrap(app);
3915
4187
  }
4188
+ if (typeof previewAdmin.bootstrap === "function") {
4189
+ previewAdmin.bootstrap(app);
4190
+ }
3916
4191
  },
3917
4192
  async registerTrads({ locales }) {
3918
4193
  const importedTrads = await Promise.all(
3919
4194
  locales.map((locale) => {
3920
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-BrCTWlZv.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
4195
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-Dtk_ot79.mjs"), "./translations/es.json": () => import("./es-D34tqjMw.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr--pg5jUbt.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-BHqhDq4V.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`, 3).then(({ default: data }) => {
3921
4196
  return {
3922
4197
  data: prefixPluginTranslations(data, PLUGIN_ID),
3923
4198
  locale
@@ -3944,8 +4219,10 @@ export {
3944
4219
  HOOKS as H,
3945
4220
  InjectionZone as I,
3946
4221
  useDocument as J,
3947
- index as K,
3948
- useDocumentActions as L,
4222
+ useGetPreviewUrlQuery as K,
4223
+ index as L,
4224
+ useContentManagerContext as M,
4225
+ useDocumentActions as N,
3949
4226
  Panels as P,
3950
4227
  RelativeTime as R,
3951
4228
  SINGLE_TYPES as S,
@@ -3977,4 +4254,4 @@ export {
3977
4254
  capitalise as y,
3978
4255
  useUpdateContentTypeConfigurationMutation as z
3979
4256
  };
3980
- //# sourceMappingURL=index-DiMrfcfy.mjs.map
4257
+ //# sourceMappingURL=index-DVAIIsOs.mjs.map