@strapi/content-manager 0.0.0-experimental.afa3b513b8f95459043f33fb94f4bac03af1474f → 0.0.0-experimental.b0240412a007bea5d73c29b17c8ce04802fca919

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 (202) 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-G4EIirP8.js → ComponentConfigurationPage-BLWQy8ru.js} +5 -6
  4. package/dist/_chunks/{ComponentConfigurationPage-G4EIirP8.js.map → ComponentConfigurationPage-BLWQy8ru.js.map} +1 -1
  5. package/dist/_chunks/{ComponentConfigurationPage-CnL10QYC.mjs → ComponentConfigurationPage-CtIa3aa2.mjs} +4 -4
  6. package/dist/_chunks/{ComponentConfigurationPage-CnL10QYC.mjs.map → ComponentConfigurationPage-CtIa3aa2.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-I2kKh9dx.mjs → EditConfigurationPage-DsPR2DVk.mjs} +4 -4
  11. package/dist/_chunks/{EditConfigurationPage-I2kKh9dx.mjs.map → EditConfigurationPage-DsPR2DVk.mjs.map} +1 -1
  12. package/dist/_chunks/{EditConfigurationPage-B2AA1kVF.js → EditConfigurationPage-RQkymxCy.js} +5 -6
  13. package/dist/_chunks/{EditConfigurationPage-B2AA1kVF.js.map → EditConfigurationPage-RQkymxCy.js.map} +1 -1
  14. package/dist/_chunks/{EditViewPage-CHgoNwlc.js → EditViewPage-B-kExt8C.js} +50 -11
  15. package/dist/_chunks/EditViewPage-B-kExt8C.js.map +1 -0
  16. package/dist/_chunks/{EditViewPage-zFjJK0s8.mjs → EditViewPage-BPyVuPfM.mjs} +50 -10
  17. package/dist/_chunks/EditViewPage-BPyVuPfM.mjs.map +1 -0
  18. package/dist/_chunks/{Field-9DePZh-0.js → Field-DPIsQRre.js} +243 -144
  19. package/dist/_chunks/Field-DPIsQRre.js.map +1 -0
  20. package/dist/_chunks/{Field-DPAzUS1M.mjs → Field-Dltnt1km.mjs} +241 -142
  21. package/dist/_chunks/Field-Dltnt1km.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-DPm-KZ1A.js → Form-BFi4MXMT.js} +6 -7
  25. package/dist/_chunks/Form-BFi4MXMT.js.map +1 -0
  26. package/dist/_chunks/{Form-CEkENbkF.mjs → Form-C1IcWm1u.mjs} +4 -4
  27. package/dist/_chunks/Form-C1IcWm1u.mjs.map +1 -0
  28. package/dist/_chunks/{History-utls71em.mjs → History-04ChQ4pl.mjs} +71 -104
  29. package/dist/_chunks/History-04ChQ4pl.mjs.map +1 -0
  30. package/dist/_chunks/{History-DXSbTWez.js → History-wjcK4L0C.js} +70 -104
  31. package/dist/_chunks/History-wjcK4L0C.js.map +1 -0
  32. package/dist/_chunks/{ListConfigurationPage-CuMXWWqb.mjs → ListConfigurationPage-BYqPYLSU.mjs} +7 -6
  33. package/dist/_chunks/ListConfigurationPage-BYqPYLSU.mjs.map +1 -0
  34. package/dist/_chunks/{ListConfigurationPage-D5C7ACZ_.js → ListConfigurationPage-CRbxIC3J.js} +7 -7
  35. package/dist/_chunks/ListConfigurationPage-CRbxIC3J.js.map +1 -0
  36. package/dist/_chunks/{ListViewPage-DfuwH1tt.js → ListViewPage-D5NY9183.js} +64 -42
  37. package/dist/_chunks/ListViewPage-D5NY9183.js.map +1 -0
  38. package/dist/_chunks/{ListViewPage-CdKd-PS_.mjs → ListViewPage-FU2LBuhl.mjs} +63 -40
  39. package/dist/_chunks/ListViewPage-FU2LBuhl.mjs.map +1 -0
  40. package/dist/_chunks/{NoContentTypePage-BIxlkWWi.js → NoContentTypePage-BgQVE_Qb.js} +2 -2
  41. package/dist/_chunks/{NoContentTypePage-BIxlkWWi.js.map → NoContentTypePage-BgQVE_Qb.js.map} +1 -1
  42. package/dist/_chunks/{NoContentTypePage-DkToTT7u.mjs → NoContentTypePage-DCKUkwb8.mjs} +2 -2
  43. package/dist/_chunks/{NoContentTypePage-DkToTT7u.mjs.map → NoContentTypePage-DCKUkwb8.mjs.map} +1 -1
  44. package/dist/_chunks/{NoPermissionsPage-Bu4GWYb-.js → NoPermissionsPage-C5jwn70o.js} +2 -2
  45. package/dist/_chunks/{NoPermissionsPage-Bu4GWYb-.js.map → NoPermissionsPage-C5jwn70o.js.map} +1 -1
  46. package/dist/_chunks/{NoPermissionsPage-DlWi4BAH.mjs → NoPermissionsPage-jqve7C8l.mjs} +2 -2
  47. package/dist/_chunks/{NoPermissionsPage-DlWi4BAH.mjs.map → NoPermissionsPage-jqve7C8l.mjs.map} +1 -1
  48. package/dist/_chunks/Preview-BMYN548c.mjs +294 -0
  49. package/dist/_chunks/Preview-BMYN548c.mjs.map +1 -0
  50. package/dist/_chunks/Preview-DaOihysv.js +312 -0
  51. package/dist/_chunks/Preview-DaOihysv.js.map +1 -0
  52. package/dist/_chunks/{Relations-CFjTESWQ.js → Relations-CTGM7Hv5.js} +75 -42
  53. package/dist/_chunks/Relations-CTGM7Hv5.js.map +1 -0
  54. package/dist/_chunks/{Relations-QP5yn9_z.mjs → Relations-gscPkxjF.mjs} +75 -41
  55. package/dist/_chunks/Relations-gscPkxjF.mjs.map +1 -0
  56. package/dist/_chunks/{en-BVzUkPxZ.js → en-BzQmavmK.js} +28 -11
  57. package/dist/_chunks/{en-BVzUkPxZ.js.map → en-BzQmavmK.js.map} +1 -1
  58. package/dist/_chunks/{en-CPTj6CjC.mjs → en-CSxLmrh1.mjs} +28 -11
  59. package/dist/_chunks/{en-CPTj6CjC.mjs.map → en-CSxLmrh1.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-DXiHxy70.js → index-Ca7YWlAA.js} +981 -722
  70. package/dist/_chunks/index-Ca7YWlAA.js.map +1 -0
  71. package/dist/_chunks/{index-BHfS6_D5.mjs → index-DqasUQ6Q.mjs} +983 -724
  72. package/dist/_chunks/index-DqasUQ6Q.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-bE-WUnQ0.js → layout-BW80JSCd.js} +5 -6
  78. package/dist/_chunks/{layout-bE-WUnQ0.js.map → layout-BW80JSCd.js.map} +1 -1
  79. package/dist/_chunks/{layout-DX_52HSH.mjs → layout-W3clJSCy.mjs} +4 -4
  80. package/dist/_chunks/{layout-DX_52HSH.mjs.map → layout-W3clJSCy.mjs.map} +1 -1
  81. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  82. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  83. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  84. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  85. package/dist/_chunks/{relations-SCVAL_aJ.mjs → relations-BlDkoeWh.mjs} +6 -7
  86. package/dist/_chunks/relations-BlDkoeWh.mjs.map +1 -0
  87. package/dist/_chunks/{relations-D706vblp.js → relations-C9Usz9k5.js} +6 -7
  88. package/dist/_chunks/relations-C9Usz9k5.js.map +1 -0
  89. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -1
  90. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -1
  91. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js → useDragAndDrop-BMtgCYzL.js} +5 -9
  92. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js.map → useDragAndDrop-BMtgCYzL.js.map} +1 -1
  93. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs → useDragAndDrop-DJ6jqvZN.mjs} +4 -7
  94. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs.map → useDragAndDrop-DJ6jqvZN.mjs.map} +1 -1
  95. package/dist/admin/index.js +3 -1
  96. package/dist/admin/index.js.map +1 -1
  97. package/dist/admin/index.mjs +5 -3
  98. package/dist/admin/src/content-manager.d.ts +3 -2
  99. package/dist/admin/src/exports.d.ts +2 -1
  100. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  101. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  102. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  103. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -1
  104. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +3 -3
  105. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Code.d.ts +7 -0
  106. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/utils/prismLanguages.d.ts +49 -0
  107. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +1 -0
  108. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.d.ts +4 -1
  109. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +4 -1
  110. package/dist/admin/src/pages/EditView/components/Header.d.ts +1 -0
  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/api.d.ts +1 -1
  120. package/dist/admin/src/services/components.d.ts +2 -2
  121. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  122. package/dist/admin/src/services/documents.d.ts +16 -19
  123. package/dist/admin/src/services/init.d.ts +1 -1
  124. package/dist/admin/src/services/relations.d.ts +2 -2
  125. package/dist/admin/src/services/uid.d.ts +3 -3
  126. package/dist/server/index.js +587 -331
  127. package/dist/server/index.js.map +1 -1
  128. package/dist/server/index.mjs +588 -331
  129. package/dist/server/index.mjs.map +1 -1
  130. package/dist/server/src/bootstrap.d.ts.map +1 -1
  131. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  132. package/dist/server/src/controllers/index.d.ts.map +1 -1
  133. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  134. package/dist/server/src/controllers/utils/metadata.d.ts +16 -1
  135. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  136. package/dist/server/src/history/controllers/history-version.d.ts +1 -1
  137. package/dist/server/src/history/controllers/history-version.d.ts.map +1 -1
  138. package/dist/server/src/history/services/history.d.ts +3 -3
  139. package/dist/server/src/history/services/history.d.ts.map +1 -1
  140. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  141. package/dist/server/src/history/services/utils.d.ts +6 -11
  142. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  143. package/dist/server/src/index.d.ts +7 -6
  144. package/dist/server/src/index.d.ts.map +1 -1
  145. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  146. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  147. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  148. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  149. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  150. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  151. package/dist/server/src/preview/index.d.ts +4 -0
  152. package/dist/server/src/preview/index.d.ts.map +1 -0
  153. package/dist/server/src/preview/routes/index.d.ts +8 -0
  154. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  155. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  156. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  157. package/dist/server/src/preview/services/index.d.ts +16 -0
  158. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  159. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  160. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  161. package/dist/server/src/preview/services/preview.d.ts +12 -0
  162. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  163. package/dist/server/src/preview/utils.d.ts +19 -0
  164. package/dist/server/src/preview/utils.d.ts.map +1 -0
  165. package/dist/server/src/register.d.ts.map +1 -1
  166. package/dist/server/src/routes/index.d.ts.map +1 -1
  167. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  168. package/dist/server/src/services/document-metadata.d.ts +12 -10
  169. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  170. package/dist/server/src/services/index.d.ts +7 -6
  171. package/dist/server/src/services/index.d.ts.map +1 -1
  172. package/dist/server/src/services/utils/populate.d.ts +2 -2
  173. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  174. package/dist/server/src/utils/index.d.ts +2 -0
  175. package/dist/server/src/utils/index.d.ts.map +1 -1
  176. package/dist/shared/contracts/index.d.ts +1 -0
  177. package/dist/shared/contracts/index.d.ts.map +1 -1
  178. package/dist/shared/contracts/preview.d.ts +27 -0
  179. package/dist/shared/contracts/preview.d.ts.map +1 -0
  180. package/dist/shared/index.js +4 -0
  181. package/dist/shared/index.js.map +1 -1
  182. package/dist/shared/index.mjs +4 -0
  183. package/dist/shared/index.mjs.map +1 -1
  184. package/package.json +14 -13
  185. package/dist/_chunks/EditViewPage-CHgoNwlc.js.map +0 -1
  186. package/dist/_chunks/EditViewPage-zFjJK0s8.mjs.map +0 -1
  187. package/dist/_chunks/Field-9DePZh-0.js.map +0 -1
  188. package/dist/_chunks/Field-DPAzUS1M.mjs.map +0 -1
  189. package/dist/_chunks/Form-CEkENbkF.mjs.map +0 -1
  190. package/dist/_chunks/Form-DPm-KZ1A.js.map +0 -1
  191. package/dist/_chunks/History-DXSbTWez.js.map +0 -1
  192. package/dist/_chunks/History-utls71em.mjs.map +0 -1
  193. package/dist/_chunks/ListConfigurationPage-CuMXWWqb.mjs.map +0 -1
  194. package/dist/_chunks/ListConfigurationPage-D5C7ACZ_.js.map +0 -1
  195. package/dist/_chunks/ListViewPage-CdKd-PS_.mjs.map +0 -1
  196. package/dist/_chunks/ListViewPage-DfuwH1tt.js.map +0 -1
  197. package/dist/_chunks/Relations-CFjTESWQ.js.map +0 -1
  198. package/dist/_chunks/Relations-QP5yn9_z.mjs.map +0 -1
  199. package/dist/_chunks/index-BHfS6_D5.mjs.map +0 -1
  200. package/dist/_chunks/index-DXiHxy70.js.map +0 -1
  201. package/dist/_chunks/relations-D706vblp.js.map +0 -1
  202. package/dist/_chunks/relations-SCVAL_aJ.mjs.map +0 -1
@@ -1,25 +1,33 @@
1
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 { Button, Menu, VisuallyHidden, Flex, Typography, Dialog, Modal, Radio, Status, Box, SingleSelect, SingleSelectOption, IconButton, Loader, Tooltip, LinkButton } from "@strapi/design-system";
6
+ import { Menu, Button, VisuallyHidden, Flex, Dialog, Modal, Typography, Radio, Status, Box, SingleSelect, SingleSelectOption, IconButton, 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";
10
+ import { styled } from "styled-components";
9
11
  import * as yup from "yup";
10
12
  import { ValidationError } from "yup";
13
+ import { stringify } from "qs";
11
14
  import pipe from "lodash/fp/pipe";
12
15
  import { intervalToDuration, isPast } from "date-fns";
13
- import { styled } from "styled-components";
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) : [];
@@ -159,7 +175,8 @@ const contentManagerApi = adminApi.enhanceEndpoints({
159
175
  "InitialData",
160
176
  "HistoryVersion",
161
177
  "Relations",
162
- "UidAvailability"
178
+ "UidAvailability",
179
+ "RecentDocumentList"
163
180
  ]
164
181
  });
165
182
  const documentApi = contentManagerApi.injectEndpoints({
@@ -177,7 +194,7 @@ const documentApi = contentManagerApi.injectEndpoints({
177
194
  if (error) {
178
195
  return [];
179
196
  }
180
- return [{ type: "Document", id: `${model}_LIST` }];
197
+ return [{ type: "Document", id: `${model}_LIST` }, "RecentDocumentList"];
181
198
  }
182
199
  }),
183
200
  cloneDocument: builder.mutation({
@@ -191,7 +208,8 @@ const documentApi = contentManagerApi.injectEndpoints({
191
208
  }),
192
209
  invalidatesTags: (_result, _error, { model }) => [
193
210
  { type: "Document", id: `${model}_LIST` },
194
- { type: "UidAvailability", id: model }
211
+ { type: "UidAvailability", id: model },
212
+ "RecentDocumentList"
195
213
  ]
196
214
  }),
197
215
  /**
@@ -210,8 +228,21 @@ const documentApi = contentManagerApi.injectEndpoints({
210
228
  invalidatesTags: (result, _error, { model }) => [
211
229
  { type: "Document", id: `${model}_LIST` },
212
230
  "Relations",
213
- { type: "UidAvailability", id: model }
214
- ]
231
+ { type: "UidAvailability", id: model },
232
+ "RecentDocumentList"
233
+ ],
234
+ transformResponse: (response, meta, arg) => {
235
+ if (!("data" in response) && arg.model === "plugin::users-permissions.user") {
236
+ return {
237
+ data: response,
238
+ meta: {
239
+ availableStatus: [],
240
+ availableLocales: []
241
+ }
242
+ };
243
+ }
244
+ return response;
245
+ }
215
246
  }),
216
247
  deleteDocument: builder.mutation({
217
248
  query: ({ collectionType, model, documentId, params }) => ({
@@ -222,7 +253,8 @@ const documentApi = contentManagerApi.injectEndpoints({
222
253
  }
223
254
  }),
224
255
  invalidatesTags: (_result, _error, { collectionType, model }) => [
225
- { type: "Document", id: collectionType !== SINGLE_TYPES ? `${model}_LIST` : model }
256
+ { type: "Document", id: collectionType !== SINGLE_TYPES ? `${model}_LIST` : model },
257
+ "RecentDocumentList"
226
258
  ]
227
259
  }),
228
260
  deleteManyDocuments: builder.mutation({
@@ -234,7 +266,10 @@ const documentApi = contentManagerApi.injectEndpoints({
234
266
  params
235
267
  }
236
268
  }),
237
- invalidatesTags: (_res, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
269
+ invalidatesTags: (_res, _error, { model }) => [
270
+ { type: "Document", id: `${model}_LIST` },
271
+ "RecentDocumentList"
272
+ ]
238
273
  }),
239
274
  discardDocument: builder.mutation({
240
275
  query: ({ collectionType, model, documentId, params }) => ({
@@ -252,7 +287,8 @@ const documentApi = contentManagerApi.injectEndpoints({
252
287
  },
253
288
  { type: "Document", id: `${model}_LIST` },
254
289
  "Relations",
255
- { type: "UidAvailability", id: model }
290
+ { type: "UidAvailability", id: model },
291
+ "RecentDocumentList"
256
292
  ];
257
293
  }
258
294
  }),
@@ -265,7 +301,7 @@ const documentApi = contentManagerApi.injectEndpoints({
265
301
  url: `/content-manager/collection-types/${model}`,
266
302
  method: "GET",
267
303
  config: {
268
- params
304
+ params: stringify(params, { encode: true })
269
305
  }
270
306
  }),
271
307
  providesTags: (result, _error, arg) => {
@@ -347,7 +383,8 @@ const documentApi = contentManagerApi.injectEndpoints({
347
383
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
348
384
  },
349
385
  { type: "Document", id: `${model}_LIST` },
350
- "Relations"
386
+ "Relations",
387
+ "RecentDocumentList"
351
388
  ];
352
389
  }
353
390
  }),
@@ -378,7 +415,9 @@ const documentApi = contentManagerApi.injectEndpoints({
378
415
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
379
416
  },
380
417
  "Relations",
381
- { type: "UidAvailability", id: model }
418
+ { type: "UidAvailability", id: model },
419
+ "RecentDocumentList",
420
+ "RecentDocumentList"
382
421
  ];
383
422
  },
384
423
  async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
@@ -408,7 +447,8 @@ const documentApi = contentManagerApi.injectEndpoints({
408
447
  {
409
448
  type: "Document",
410
449
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
411
- }
450
+ },
451
+ "RecentDocumentList"
412
452
  ];
413
453
  }
414
454
  }),
@@ -421,7 +461,10 @@ const documentApi = contentManagerApi.injectEndpoints({
421
461
  params
422
462
  }
423
463
  }),
424
- invalidatesTags: (_res, _error, { model, documentIds }) => documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` }))
464
+ invalidatesTags: (_res, _error, { model, documentIds }) => [
465
+ ...documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` })),
466
+ "RecentDocumentList"
467
+ ]
425
468
  })
426
469
  })
427
470
  });
@@ -444,8 +487,7 @@ const {
444
487
  useUnpublishManyDocumentsMutation
445
488
  } = documentApi;
446
489
  const buildValidParams = (query) => {
447
- if (!query)
448
- return query;
490
+ if (!query) return query;
449
491
  const { plugins: _, ...validQueryParams } = {
450
492
  ...query,
451
493
  ...Object.values(query?.plugins ?? {}).reduce(
@@ -453,14 +495,29 @@ const buildValidParams = (query) => {
453
495
  {}
454
496
  )
455
497
  };
456
- if ("_q" in validQueryParams) {
457
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
458
- }
459
498
  return validQueryParams;
460
499
  };
461
500
  const isBaseQueryError = (error) => {
462
501
  return error.name !== void 0;
463
502
  };
503
+ const arrayValidator = (attribute, options) => ({
504
+ message: translatedErrors.required,
505
+ test(value) {
506
+ if (options.status === "draft") {
507
+ return true;
508
+ }
509
+ if (!attribute.required) {
510
+ return true;
511
+ }
512
+ if (!value) {
513
+ return false;
514
+ }
515
+ if (Array.isArray(value) && value.length === 0) {
516
+ return false;
517
+ }
518
+ return true;
519
+ }
520
+ });
464
521
  const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
465
522
  const createModelSchema = (attributes2) => yup.object().shape(
466
523
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
@@ -468,6 +525,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
468
525
  return acc;
469
526
  }
470
527
  const validations = [
528
+ addNullableValidation,
471
529
  addRequiredValidation,
472
530
  addMinLengthValidation,
473
531
  addMaxLengthValidation,
@@ -484,12 +542,12 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
484
542
  ...acc,
485
543
  [name]: transformSchema(
486
544
  yup.array().of(createModelSchema(attributes3).nullable(false))
487
- )
545
+ ).test(arrayValidator(attribute, options))
488
546
  };
489
547
  } else {
490
548
  return {
491
549
  ...acc,
492
- [name]: transformSchema(createModelSchema(attributes3))
550
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
493
551
  };
494
552
  }
495
553
  }
@@ -511,7 +569,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
511
569
  }
512
570
  )
513
571
  )
514
- )
572
+ ).test(arrayValidator(attribute, options))
515
573
  };
516
574
  case "relation":
517
575
  return {
@@ -523,7 +581,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
523
581
  } else if (Array.isArray(value)) {
524
582
  return yup.array().of(
525
583
  yup.object().shape({
526
- id: yup.string().required()
584
+ id: yup.number().required()
527
585
  })
528
586
  );
529
587
  } else if (typeof value === "object") {
@@ -609,17 +667,17 @@ const nullableSchema = (schema) => {
609
667
  schema
610
668
  );
611
669
  };
670
+ const addNullableValidation = () => (schema) => {
671
+ return nullableSchema(schema);
672
+ };
612
673
  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);
674
+ if (options.status === "draft" || !attribute.required) {
675
+ return schema;
618
676
  }
619
- if (attribute.required && attribute.type !== "relation") {
677
+ if (attribute.required && "required" in schema) {
620
678
  return schema.required(translatedErrors.required);
621
679
  }
622
- return nullableSchema(schema);
680
+ return schema;
623
681
  };
624
682
  const addMinLengthValidation = (attribute, options) => (schema) => {
625
683
  if (options.status === "draft") {
@@ -647,31 +705,12 @@ const addMaxLengthValidation = (attribute) => (schema) => {
647
705
  return schema;
648
706
  };
649
707
  const addMinValidation = (attribute, options) => (schema) => {
650
- if ("min" in attribute) {
708
+ if (options.status === "draft") {
709
+ return schema;
710
+ }
711
+ if ("min" in attribute && "min" in schema) {
651
712
  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) {
713
+ if (min) {
675
714
  return schema.min(min, {
676
715
  ...translatedErrors.min,
677
716
  values: {
@@ -789,19 +828,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
789
828
  }, {});
790
829
  return componentsByKey;
791
830
  };
792
- const useDocument = (args, opts) => {
831
+ const HOOKS = {
832
+ /**
833
+ * Hook that allows to mutate the displayed headers of the list view table
834
+ * @constant
835
+ * @type {string}
836
+ */
837
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
838
+ /**
839
+ * Hook that allows to mutate the CM's collection types links pre-set filters
840
+ * @constant
841
+ * @type {string}
842
+ */
843
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
844
+ /**
845
+ * Hook that allows to mutate the CM's edit view layout
846
+ * @constant
847
+ * @type {string}
848
+ */
849
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
850
+ /**
851
+ * Hook that allows to mutate the CM's single types links pre-set filters
852
+ * @constant
853
+ * @type {string}
854
+ */
855
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
856
+ };
857
+ const contentTypesApi = contentManagerApi.injectEndpoints({
858
+ endpoints: (builder) => ({
859
+ getContentTypeConfiguration: builder.query({
860
+ query: (uid) => ({
861
+ url: `/content-manager/content-types/${uid}/configuration`,
862
+ method: "GET"
863
+ }),
864
+ transformResponse: (response) => response.data,
865
+ providesTags: (_result, _error, uid) => [
866
+ { type: "ContentTypesConfiguration", id: uid },
867
+ { type: "ContentTypeSettings", id: "LIST" }
868
+ ]
869
+ }),
870
+ getAllContentTypeSettings: builder.query({
871
+ query: () => "/content-manager/content-types-settings",
872
+ transformResponse: (response) => response.data,
873
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
874
+ }),
875
+ updateContentTypeConfiguration: builder.mutation({
876
+ query: ({ uid, ...body }) => ({
877
+ url: `/content-manager/content-types/${uid}/configuration`,
878
+ method: "PUT",
879
+ data: body
880
+ }),
881
+ transformResponse: (response) => response.data,
882
+ invalidatesTags: (_result, _error, { uid }) => [
883
+ { type: "ContentTypesConfiguration", id: uid },
884
+ { type: "ContentTypeSettings", id: "LIST" },
885
+ // Is this necessary?
886
+ { type: "InitialData" }
887
+ ]
888
+ })
889
+ })
890
+ });
891
+ const {
892
+ useGetContentTypeConfigurationQuery,
893
+ useGetAllContentTypeSettingsQuery,
894
+ useUpdateContentTypeConfigurationMutation
895
+ } = contentTypesApi;
896
+ const checkIfAttributeIsDisplayable = (attribute) => {
897
+ const { type } = attribute;
898
+ if (type === "relation") {
899
+ return !attribute.relation.toLowerCase().includes("morph");
900
+ }
901
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
902
+ };
903
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
904
+ if (!mainFieldName) {
905
+ return void 0;
906
+ }
907
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
908
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
909
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
910
+ );
911
+ return {
912
+ name: mainFieldName,
913
+ type: mainFieldType ?? "string"
914
+ };
915
+ };
916
+ const DEFAULT_SETTINGS = {
917
+ bulkable: false,
918
+ filterable: false,
919
+ searchable: false,
920
+ pagination: false,
921
+ defaultSortBy: "",
922
+ defaultSortOrder: "asc",
923
+ mainField: "id",
924
+ pageSize: 10
925
+ };
926
+ const useDocumentLayout = (model) => {
927
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
928
+ const [{ query }] = useQueryParams();
929
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
793
930
  const { toggleNotification } = useNotification();
794
931
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
932
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
795
933
  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);
934
+ data,
935
+ isLoading: isLoadingConfigs,
936
+ error,
937
+ isFetching: isFetchingConfigs
938
+ } = useGetContentTypeConfigurationQuery(model);
939
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
805
940
  React.useEffect(() => {
806
941
  if (error) {
807
942
  toggleNotification({
@@ -809,77 +944,331 @@ const useDocument = (args, opts) => {
809
944
  message: formatAPIError(error)
810
945
  });
811
946
  }
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
- }
947
+ }, [error, formatAPIError, toggleNotification]);
948
+ const editLayout = React.useMemo(
949
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
950
+ layout: [],
951
+ components: {},
952
+ metadatas: {},
953
+ options: {},
954
+ settings: DEFAULT_SETTINGS
835
955
  },
836
- [validationSchema]
956
+ [data, isLoading, schemas, schema, components]
957
+ );
958
+ const listLayout = React.useMemo(() => {
959
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
960
+ layout: [],
961
+ metadatas: {},
962
+ options: {},
963
+ settings: DEFAULT_SETTINGS
964
+ };
965
+ }, [data, isLoading, schemas, schema, components]);
966
+ const { layout: edit } = React.useMemo(
967
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
968
+ layout: editLayout,
969
+ query
970
+ }),
971
+ [editLayout, query, runHookWaterfall]
837
972
  );
838
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
839
973
  return {
840
- components,
841
- document: data?.data,
842
- meta: data?.meta,
974
+ error,
843
975
  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
- )
976
+ edit,
977
+ list: listLayout
868
978
  };
869
979
  };
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"
980
+ const useDocLayout = () => {
981
+ const { model } = useDoc();
982
+ return useDocumentLayout(model);
983
+ };
984
+ const formatEditLayout = (data, {
985
+ schemas,
986
+ schema,
987
+ components
988
+ }) => {
989
+ let currentPanelIndex = 0;
990
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
991
+ data.contentType.layouts.edit,
992
+ schema?.attributes,
993
+ data.contentType.metadatas,
994
+ { configurations: data.components, schemas: components },
995
+ schemas
996
+ ).reduce((panels, row) => {
997
+ if (row.some((field) => field.type === "dynamiczone")) {
998
+ panels.push([row]);
999
+ currentPanelIndex += 2;
1000
+ } else {
1001
+ if (!panels[currentPanelIndex]) {
1002
+ panels.push([row]);
1003
+ } else {
1004
+ panels[currentPanelIndex].push(row);
1005
+ }
1006
+ }
1007
+ return panels;
1008
+ }, []);
1009
+ const componentEditAttributes = Object.entries(data.components).reduce(
1010
+ (acc, [uid, configuration]) => {
1011
+ acc[uid] = {
1012
+ layout: convertEditLayoutToFieldLayouts(
1013
+ configuration.layouts.edit,
1014
+ components[uid].attributes,
1015
+ configuration.metadatas,
1016
+ { configurations: data.components, schemas: components }
1017
+ ),
1018
+ settings: {
1019
+ ...configuration.settings,
1020
+ icon: components[uid].info.icon,
1021
+ displayName: components[uid].info.displayName
1022
+ }
1023
+ };
1024
+ return acc;
1025
+ },
1026
+ {}
1027
+ );
1028
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1029
+ (acc, [attribute, metadata]) => {
1030
+ return {
1031
+ ...acc,
1032
+ [attribute]: metadata.edit
1033
+ };
1034
+ },
1035
+ {}
1036
+ );
1037
+ return {
1038
+ layout: panelledEditAttributes,
1039
+ components: componentEditAttributes,
1040
+ metadatas: editMetadatas,
1041
+ settings: {
1042
+ ...data.contentType.settings,
1043
+ displayName: schema?.info.displayName
1044
+ },
1045
+ options: {
1046
+ ...schema?.options,
1047
+ ...schema?.pluginOptions,
1048
+ ...data.contentType.options
1049
+ }
1050
+ };
1051
+ };
1052
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1053
+ return rows.map(
1054
+ (row) => row.map((field) => {
1055
+ const attribute = attributes[field.name];
1056
+ if (!attribute) {
1057
+ return null;
1058
+ }
1059
+ const { edit: metadata } = metadatas[field.name];
1060
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1061
+ return {
1062
+ attribute,
1063
+ disabled: !metadata.editable,
1064
+ hint: metadata.description,
1065
+ label: metadata.label ?? "",
1066
+ name: field.name,
1067
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1068
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1069
+ schemas,
1070
+ components: components?.schemas ?? {}
1071
+ }),
1072
+ placeholder: metadata.placeholder ?? "",
1073
+ required: attribute.required ?? false,
1074
+ size: field.size,
1075
+ unique: "unique" in attribute ? attribute.unique : false,
1076
+ visible: metadata.visible ?? true,
1077
+ type: attribute.type
1078
+ };
1079
+ }).filter((field) => field !== null)
1080
+ );
1081
+ };
1082
+ const formatListLayout = (data, {
1083
+ schemas,
1084
+ schema,
1085
+ components
1086
+ }) => {
1087
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1088
+ (acc, [attribute, metadata]) => {
1089
+ return {
1090
+ ...acc,
1091
+ [attribute]: metadata.list
1092
+ };
1093
+ },
1094
+ {}
1095
+ );
1096
+ const listAttributes = convertListLayoutToFieldLayouts(
1097
+ data.contentType.layouts.list,
1098
+ schema?.attributes,
1099
+ listMetadatas,
1100
+ { configurations: data.components, schemas: components },
1101
+ schemas
1102
+ );
1103
+ return {
1104
+ layout: listAttributes,
1105
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1106
+ metadatas: listMetadatas,
1107
+ options: {
1108
+ ...schema?.options,
1109
+ ...schema?.pluginOptions,
1110
+ ...data.contentType.options
1111
+ }
1112
+ };
1113
+ };
1114
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1115
+ return columns.map((name) => {
1116
+ const attribute = attributes[name];
1117
+ if (!attribute) {
1118
+ return null;
1119
+ }
1120
+ const metadata = metadatas[name];
1121
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1122
+ return {
1123
+ attribute,
1124
+ label: metadata.label ?? "",
1125
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1126
+ schemas,
1127
+ components: components?.schemas ?? {}
1128
+ }),
1129
+ name,
1130
+ searchable: metadata.searchable ?? true,
1131
+ sortable: metadata.sortable ?? true
1132
+ };
1133
+ }).filter((field) => field !== null);
1134
+ };
1135
+ const useDocument = (args, opts) => {
1136
+ const { toggleNotification } = useNotification();
1137
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1138
+ const {
1139
+ currentData: data,
1140
+ isLoading: isLoadingDocument,
1141
+ isFetching: isFetchingDocument,
1142
+ error
1143
+ } = useGetDocumentQuery(args, {
1144
+ ...opts,
1145
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1146
+ });
1147
+ const {
1148
+ components,
1149
+ schema,
1150
+ schemas,
1151
+ isLoading: isLoadingSchema
1152
+ } = useContentTypeSchema(args.model);
1153
+ React.useEffect(() => {
1154
+ if (error) {
1155
+ toggleNotification({
1156
+ type: "danger",
1157
+ message: formatAPIError(error)
1158
+ });
1159
+ }
1160
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1161
+ const validationSchema = React.useMemo(() => {
1162
+ if (!schema) {
1163
+ return null;
1164
+ }
1165
+ return createYupSchema(schema.attributes, components);
1166
+ }, [schema, components]);
1167
+ const validate = React.useCallback(
1168
+ (document) => {
1169
+ if (!validationSchema) {
1170
+ throw new Error(
1171
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1172
+ );
1173
+ }
1174
+ try {
1175
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1176
+ return null;
1177
+ } catch (error2) {
1178
+ if (error2 instanceof ValidationError) {
1179
+ return getYupValidationErrors(error2);
1180
+ }
1181
+ throw error2;
1182
+ }
1183
+ },
1184
+ [validationSchema]
1185
+ );
1186
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1187
+ const hasError = !!error;
1188
+ return {
1189
+ components,
1190
+ document: data?.data,
1191
+ meta: data?.meta,
1192
+ isLoading,
1193
+ hasError,
1194
+ schema,
1195
+ schemas,
1196
+ validate
1197
+ };
1198
+ };
1199
+ const useDoc = () => {
1200
+ const { id, slug, collectionType, origin } = useParams();
1201
+ const [{ query }] = useQueryParams();
1202
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1203
+ if (!collectionType) {
1204
+ throw new Error("Could not find collectionType in url params");
1205
+ }
1206
+ if (!slug) {
1207
+ throw new Error("Could not find model in url params");
1208
+ }
1209
+ const document = useDocument(
1210
+ { documentId: origin || id, model: slug, collectionType, params },
1211
+ {
1212
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1213
+ }
1214
+ );
1215
+ const returnId = origin || id === "create" ? void 0 : id;
1216
+ return {
1217
+ collectionType,
1218
+ model: slug,
1219
+ id: returnId,
1220
+ ...document
1221
+ };
1222
+ };
1223
+ const useContentManagerContext = () => {
1224
+ const {
1225
+ collectionType,
1226
+ model,
1227
+ id,
1228
+ components,
1229
+ isLoading: isLoadingDoc,
1230
+ schema,
1231
+ schemas
1232
+ } = useDoc();
1233
+ const layout = useDocumentLayout(model);
1234
+ const form = useForm("useContentManagerContext", (state) => state);
1235
+ const isSingleType = collectionType === SINGLE_TYPES;
1236
+ const slug = model;
1237
+ const isCreatingEntry = id === "create";
1238
+ useContentTypeSchema();
1239
+ const isLoading = isLoadingDoc || layout.isLoading;
1240
+ const error = layout.error;
1241
+ return {
1242
+ error,
1243
+ isLoading,
1244
+ // Base metadata
1245
+ model,
1246
+ collectionType,
1247
+ id,
1248
+ slug,
1249
+ isCreatingEntry,
1250
+ isSingleType,
1251
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1252
+ // All schema infos
1253
+ components,
1254
+ contentType: schema,
1255
+ contentTypes: schemas,
1256
+ // Form state
1257
+ form,
1258
+ // layout infos
1259
+ layout
1260
+ };
1261
+ };
1262
+ const prefixPluginTranslations = (trad, pluginId) => {
1263
+ return Object.keys(trad).reduce((acc, current) => {
1264
+ acc[`${pluginId}.${current}`] = trad[current];
1265
+ return acc;
1266
+ }, {});
1267
+ };
1268
+ const getTranslation = (id) => `content-manager.${id}`;
1269
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1270
+ id: "notification.error",
1271
+ defaultMessage: "An error occurred, please try again"
883
1272
  };
884
1273
  const useDocumentActions = () => {
885
1274
  const { toggleNotification } = useNotification();
@@ -887,6 +1276,7 @@ const useDocumentActions = () => {
887
1276
  const { trackUsage } = useTracking();
888
1277
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
889
1278
  const navigate = useNavigate();
1279
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
890
1280
  const [deleteDocument] = useDeleteDocumentMutation();
891
1281
  const _delete = React.useCallback(
892
1282
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1201,6 +1591,7 @@ const useDocumentActions = () => {
1201
1591
  defaultMessage: "Saved document"
1202
1592
  })
1203
1593
  });
1594
+ setCurrentStep("contentManager.success");
1204
1595
  return res.data;
1205
1596
  } catch (err) {
1206
1597
  toggleNotification({
@@ -1302,10 +1693,10 @@ const useDocumentActions = () => {
1302
1693
  update
1303
1694
  };
1304
1695
  };
1305
- const ProtectedHistoryPage = lazy(
1306
- () => import("./History-utls71em.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1696
+ const ProtectedHistoryPage = React.lazy(
1697
+ () => import("./History-04ChQ4pl.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1307
1698
  );
1308
- const routes$1 = [
1699
+ const routes$2 = [
1309
1700
  {
1310
1701
  path: ":collectionType/:slug/:id/history",
1311
1702
  Component: ProtectedHistoryPage
@@ -1315,32 +1706,45 @@ const routes$1 = [
1315
1706
  Component: ProtectedHistoryPage
1316
1707
  }
1317
1708
  ];
1318
- const ProtectedEditViewPage = lazy(
1319
- () => import("./EditViewPage-zFjJK0s8.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1709
+ const ProtectedPreviewPage = React.lazy(
1710
+ () => import("./Preview-BMYN548c.mjs").then((mod) => ({ default: mod.ProtectedPreviewPage }))
1320
1711
  );
1321
- const ProtectedListViewPage = lazy(
1322
- () => import("./ListViewPage-CdKd-PS_.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1323
- );
1324
- const ProtectedListConfiguration = lazy(
1325
- () => import("./ListConfigurationPage-CuMXWWqb.mjs").then((mod) => ({
1326
- default: mod.ProtectedListConfiguration
1712
+ const routes$1 = [
1713
+ {
1714
+ path: ":collectionType/:slug/:id/preview",
1715
+ Component: ProtectedPreviewPage
1716
+ },
1717
+ {
1718
+ path: ":collectionType/:slug/preview",
1719
+ Component: ProtectedPreviewPage
1720
+ }
1721
+ ];
1722
+ const ProtectedEditViewPage = lazy(
1723
+ () => import("./EditViewPage-BPyVuPfM.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1724
+ );
1725
+ const ProtectedListViewPage = lazy(
1726
+ () => import("./ListViewPage-FU2LBuhl.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1727
+ );
1728
+ const ProtectedListConfiguration = lazy(
1729
+ () => import("./ListConfigurationPage-BYqPYLSU.mjs").then((mod) => ({
1730
+ default: mod.ProtectedListConfiguration
1327
1731
  }))
1328
1732
  );
1329
1733
  const ProtectedEditConfigurationPage = lazy(
1330
- () => import("./EditConfigurationPage-I2kKh9dx.mjs").then((mod) => ({
1734
+ () => import("./EditConfigurationPage-DsPR2DVk.mjs").then((mod) => ({
1331
1735
  default: mod.ProtectedEditConfigurationPage
1332
1736
  }))
1333
1737
  );
1334
1738
  const ProtectedComponentConfigurationPage = lazy(
1335
- () => import("./ComponentConfigurationPage-CnL10QYC.mjs").then((mod) => ({
1739
+ () => import("./ComponentConfigurationPage-CtIa3aa2.mjs").then((mod) => ({
1336
1740
  default: mod.ProtectedComponentConfigurationPage
1337
1741
  }))
1338
1742
  );
1339
1743
  const NoPermissions = lazy(
1340
- () => import("./NoPermissionsPage-DlWi4BAH.mjs").then((mod) => ({ default: mod.NoPermissions }))
1744
+ () => import("./NoPermissionsPage-jqve7C8l.mjs").then((mod) => ({ default: mod.NoPermissions }))
1341
1745
  );
1342
1746
  const NoContentType = lazy(
1343
- () => import("./NoContentTypePage-DkToTT7u.mjs").then((mod) => ({ default: mod.NoContentType }))
1747
+ () => import("./NoContentTypePage-DCKUkwb8.mjs").then((mod) => ({ default: mod.NoContentType }))
1344
1748
  );
1345
1749
  const CollectionTypePages = () => {
1346
1750
  const { collectionType } = useParams();
@@ -1352,7 +1756,7 @@ const CollectionTypePages = () => {
1352
1756
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1353
1757
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1354
1758
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1355
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1759
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1356
1760
  const routes = [
1357
1761
  {
1358
1762
  path: LIST_RELATIVE_PATH,
@@ -1386,6 +1790,7 @@ const routes = [
1386
1790
  path: "no-content-types",
1387
1791
  Component: NoContentType
1388
1792
  },
1793
+ ...routes$2,
1389
1794
  ...routes$1
1390
1795
  ];
1391
1796
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1484,6 +1889,11 @@ const DocumentActionButton = (action) => {
1484
1889
  ) : null
1485
1890
  ] });
1486
1891
  };
1892
+ const MenuItem = styled(Menu.Item)`
1893
+ &:hover {
1894
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
1895
+ }
1896
+ `;
1487
1897
  const DocumentActionsMenu = ({
1488
1898
  actions: actions2,
1489
1899
  children,
@@ -1542,48 +1952,32 @@ const DocumentActionsMenu = ({
1542
1952
  /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1543
1953
  actions2.map((action) => {
1544
1954
  return /* @__PURE__ */ jsx(
1545
- Menu.Item,
1955
+ MenuItem,
1546
1956
  {
1547
1957
  disabled: action.disabled,
1548
1958
  onSelect: handleClick(action),
1549
1959
  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
- ] })
1960
+ isVariantDanger: action.variant === "danger",
1961
+ isDisabled: action.disabled,
1962
+ children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxs(
1963
+ Flex,
1964
+ {
1965
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1966
+ gap: 2,
1967
+ tag: "span",
1968
+ children: [
1969
+ /* @__PURE__ */ jsx(
1970
+ Flex,
1971
+ {
1972
+ tag: "span",
1973
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1974
+ children: action.icon
1975
+ }
1976
+ ),
1977
+ action.label
1978
+ ]
1979
+ }
1980
+ ) })
1587
1981
  },
1588
1982
  action.id
1589
1983
  );
@@ -1694,6 +2088,18 @@ const DocumentActionModal = ({
1694
2088
  typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1695
2089
  ] }) });
1696
2090
  };
2091
+ const transformData = (data) => {
2092
+ if (Array.isArray(data)) {
2093
+ return data.map(transformData);
2094
+ }
2095
+ if (typeof data === "object" && data !== null) {
2096
+ if ("apiData" in data) {
2097
+ return data.apiData;
2098
+ }
2099
+ return mapValues(transformData)(data);
2100
+ }
2101
+ return data;
2102
+ };
1697
2103
  const PublishAction$1 = ({
1698
2104
  activeTab,
1699
2105
  documentId,
@@ -1708,6 +2114,7 @@ const PublishAction$1 = ({
1708
2114
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
1709
2115
  const isListView = useMatch(LIST_PATH) !== null;
1710
2116
  const isCloning = useMatch(CLONE_PATH) !== null;
2117
+ const { id } = useParams();
1711
2118
  const { formatMessage } = useIntl();
1712
2119
  const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1713
2120
  const { publish } = useDocumentActions();
@@ -1787,7 +2194,9 @@ const PublishAction$1 = ({
1787
2194
  const performPublish = async () => {
1788
2195
  setSubmitting(true);
1789
2196
  try {
1790
- const { errors } = await validate();
2197
+ const { errors } = await validate(true, {
2198
+ status: "published"
2199
+ });
1791
2200
  if (errors) {
1792
2201
  toggleNotification({
1793
2202
  type: "danger",
@@ -1805,13 +2214,15 @@ const PublishAction$1 = ({
1805
2214
  documentId,
1806
2215
  params
1807
2216
  },
1808
- formValues
2217
+ transformData(formValues)
1809
2218
  );
1810
2219
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1811
- navigate({
1812
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1813
- search: rawQuery
1814
- });
2220
+ if (id === "create") {
2221
+ navigate({
2222
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2223
+ search: rawQuery
2224
+ });
2225
+ }
1815
2226
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1816
2227
  setErrors(formatValidationErrors(res.error));
1817
2228
  }
@@ -1864,6 +2275,7 @@ const PublishAction$1 = ({
1864
2275
  };
1865
2276
  };
1866
2277
  PublishAction$1.type = "publish";
2278
+ PublishAction$1.position = "panel";
1867
2279
  const UpdateAction = ({
1868
2280
  activeTab,
1869
2281
  documentId,
@@ -1886,6 +2298,117 @@ const UpdateAction = ({
1886
2298
  const validate = useForm("UpdateAction", (state) => state.validate);
1887
2299
  const setErrors = useForm("UpdateAction", (state) => state.setErrors);
1888
2300
  const resetForm = useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
2301
+ const handleUpdate = React.useCallback(async () => {
2302
+ setSubmitting(true);
2303
+ try {
2304
+ if (!modified) {
2305
+ return;
2306
+ }
2307
+ const { errors } = await validate(true, {
2308
+ status: "draft"
2309
+ });
2310
+ if (errors) {
2311
+ toggleNotification({
2312
+ type: "danger",
2313
+ message: formatMessage({
2314
+ id: "content-manager.validation.error",
2315
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2316
+ })
2317
+ });
2318
+ return;
2319
+ }
2320
+ if (isCloning) {
2321
+ const res = await clone(
2322
+ {
2323
+ model,
2324
+ documentId: cloneMatch.params.origin,
2325
+ params
2326
+ },
2327
+ transformData(document)
2328
+ );
2329
+ if ("data" in res) {
2330
+ navigate(
2331
+ {
2332
+ pathname: `../${res.data.documentId}`,
2333
+ search: rawQuery
2334
+ },
2335
+ { relative: "path" }
2336
+ );
2337
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2338
+ setErrors(formatValidationErrors(res.error));
2339
+ }
2340
+ } else if (documentId || collectionType === SINGLE_TYPES) {
2341
+ const res = await update(
2342
+ {
2343
+ collectionType,
2344
+ model,
2345
+ documentId,
2346
+ params
2347
+ },
2348
+ transformData(document)
2349
+ );
2350
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2351
+ setErrors(formatValidationErrors(res.error));
2352
+ } else {
2353
+ resetForm();
2354
+ }
2355
+ } else {
2356
+ const res = await create(
2357
+ {
2358
+ model,
2359
+ params
2360
+ },
2361
+ transformData(document)
2362
+ );
2363
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2364
+ navigate(
2365
+ {
2366
+ pathname: `../${res.data.documentId}`,
2367
+ search: rawQuery
2368
+ },
2369
+ { replace: true, relative: "path" }
2370
+ );
2371
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2372
+ setErrors(formatValidationErrors(res.error));
2373
+ }
2374
+ }
2375
+ } finally {
2376
+ setSubmitting(false);
2377
+ }
2378
+ }, [
2379
+ clone,
2380
+ cloneMatch?.params.origin,
2381
+ collectionType,
2382
+ create,
2383
+ document,
2384
+ documentId,
2385
+ formatMessage,
2386
+ formatValidationErrors,
2387
+ isCloning,
2388
+ model,
2389
+ modified,
2390
+ navigate,
2391
+ params,
2392
+ rawQuery,
2393
+ resetForm,
2394
+ setErrors,
2395
+ setSubmitting,
2396
+ toggleNotification,
2397
+ update,
2398
+ validate
2399
+ ]);
2400
+ React.useEffect(() => {
2401
+ const handleKeyDown = (e) => {
2402
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
2403
+ e.preventDefault();
2404
+ handleUpdate();
2405
+ }
2406
+ };
2407
+ window.addEventListener("keydown", handleKeyDown);
2408
+ return () => {
2409
+ window.removeEventListener("keydown", handleKeyDown);
2410
+ };
2411
+ }, [handleUpdate]);
1889
2412
  return {
1890
2413
  /**
1891
2414
  * Disabled when:
@@ -1895,87 +2418,14 @@ const UpdateAction = ({
1895
2418
  */
1896
2419
  disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1897
2420
  label: formatMessage({
1898
- id: "content-manager.containers.Edit.save",
2421
+ id: "global.save",
1899
2422
  defaultMessage: "Save"
1900
2423
  }),
1901
- onClick: async () => {
1902
- setSubmitting(true);
1903
- try {
1904
- if (activeTab !== "draft") {
1905
- const { errors } = await validate();
1906
- if (errors) {
1907
- toggleNotification({
1908
- type: "danger",
1909
- message: formatMessage({
1910
- id: "content-manager.validation.error",
1911
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1912
- })
1913
- });
1914
- return;
1915
- }
1916
- }
1917
- if (isCloning) {
1918
- const res = await clone(
1919
- {
1920
- model,
1921
- documentId: cloneMatch.params.origin,
1922
- params
1923
- },
1924
- document
1925
- );
1926
- if ("data" in res) {
1927
- navigate(
1928
- {
1929
- pathname: `../${res.data.documentId}`,
1930
- search: rawQuery
1931
- },
1932
- { relative: "path" }
1933
- );
1934
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1935
- setErrors(formatValidationErrors(res.error));
1936
- }
1937
- } else if (documentId || collectionType === SINGLE_TYPES) {
1938
- const res = await update(
1939
- {
1940
- collectionType,
1941
- model,
1942
- documentId,
1943
- params
1944
- },
1945
- document
1946
- );
1947
- if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1948
- setErrors(formatValidationErrors(res.error));
1949
- } else {
1950
- resetForm();
1951
- }
1952
- } else {
1953
- const res = await create(
1954
- {
1955
- model,
1956
- params
1957
- },
1958
- document
1959
- );
1960
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1961
- navigate(
1962
- {
1963
- pathname: `../${res.data.documentId}`,
1964
- search: rawQuery
1965
- },
1966
- { replace: true, relative: "path" }
1967
- );
1968
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1969
- setErrors(formatValidationErrors(res.error));
1970
- }
1971
- }
1972
- } finally {
1973
- setSubmitting(false);
1974
- }
1975
- }
2424
+ onClick: handleUpdate
1976
2425
  };
1977
2426
  };
1978
2427
  UpdateAction.type = "update";
2428
+ UpdateAction.position = "panel";
1979
2429
  const UNPUBLISH_DRAFT_OPTIONS = {
1980
2430
  KEEP: "keep",
1981
2431
  DISCARD: "discard"
@@ -2098,6 +2548,7 @@ const UnpublishAction$1 = ({
2098
2548
  };
2099
2549
  };
2100
2550
  UnpublishAction$1.type = "unpublish";
2551
+ UnpublishAction$1.position = "panel";
2101
2552
  const DiscardAction = ({
2102
2553
  activeTab,
2103
2554
  documentId,
@@ -2148,6 +2599,7 @@ const DiscardAction = ({
2148
2599
  };
2149
2600
  };
2150
2601
  DiscardAction.type = "discard";
2602
+ DiscardAction.position = "panel";
2151
2603
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2152
2604
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2153
2605
  const RelativeTime = React.forwardRef(
@@ -2160,7 +2612,7 @@ const RelativeTime = React.forwardRef(
2160
2612
  });
2161
2613
  const unit = intervals.find((intervalUnit) => {
2162
2614
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2163
- });
2615
+ }) ?? "seconds";
2164
2616
  const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2165
2617
  const customInterval = customIntervals.find(
2166
2618
  (custom) => interval[custom.unit] < custom.threshold
@@ -2194,19 +2646,29 @@ const getDisplayName = ({
2194
2646
  return email ?? "";
2195
2647
  };
2196
2648
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2197
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2649
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2198
2650
  const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2199
- return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2651
+ const { formatMessage } = useIntl();
2652
+ return /* @__PURE__ */ jsx(Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2653
+ id: `content-manager.containers.List.${status}`,
2654
+ defaultMessage: capitalise(status)
2655
+ }) }) });
2200
2656
  };
2201
2657
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2202
2658
  const { formatMessage } = useIntl();
2203
2659
  const isCloning = useMatch(CLONE_PATH) !== null;
2660
+ const params = useParams();
2204
2661
  const title = isCreating ? formatMessage({
2205
2662
  id: "content-manager.containers.edit.title.new",
2206
2663
  defaultMessage: "Create an entry"
2207
2664
  }) : documentTitle;
2208
2665
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2209
- /* @__PURE__ */ jsx(BackButton, {}),
2666
+ /* @__PURE__ */ jsx(
2667
+ BackButton,
2668
+ {
2669
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2670
+ }
2671
+ ),
2210
2672
  /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2211
2673
  /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2212
2674
  /* @__PURE__ */ jsx(HeaderToolbar, {})
@@ -2257,7 +2719,7 @@ const HeaderToolbar = () => {
2257
2719
  meta: isCloning ? void 0 : meta,
2258
2720
  collectionType
2259
2721
  },
2260
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2722
+ descriptions: plugins["content-manager"].apis.getDocumentActions("header"),
2261
2723
  children: (actions2) => {
2262
2724
  const headerActions = actions2.filter((action) => {
2263
2725
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2294,12 +2756,12 @@ const Information = ({ activeTab }) => {
2294
2756
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2295
2757
  label: formatMessage({
2296
2758
  id: "content-manager.containers.edit.information.last-published.label",
2297
- defaultMessage: "Last published"
2759
+ defaultMessage: "Published"
2298
2760
  }),
2299
2761
  value: formatMessage(
2300
2762
  {
2301
2763
  id: "content-manager.containers.edit.information.last-published.value",
2302
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2764
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2303
2765
  },
2304
2766
  {
2305
2767
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2312,12 +2774,12 @@ const Information = ({ activeTab }) => {
2312
2774
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2313
2775
  label: formatMessage({
2314
2776
  id: "content-manager.containers.edit.information.last-draft.label",
2315
- defaultMessage: "Last draft"
2777
+ defaultMessage: "Updated"
2316
2778
  }),
2317
2779
  value: formatMessage(
2318
2780
  {
2319
2781
  id: "content-manager.containers.edit.information.last-draft.value",
2320
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2782
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2321
2783
  },
2322
2784
  {
2323
2785
  time: /* @__PURE__ */ jsx(
@@ -2335,12 +2797,12 @@ const Information = ({ activeTab }) => {
2335
2797
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2336
2798
  label: formatMessage({
2337
2799
  id: "content-manager.containers.edit.information.document.label",
2338
- defaultMessage: "Document"
2800
+ defaultMessage: "Created"
2339
2801
  }),
2340
2802
  value: formatMessage(
2341
2803
  {
2342
2804
  id: "content-manager.containers.edit.information.document.value",
2343
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2805
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2344
2806
  },
2345
2807
  {
2346
2808
  time: /* @__PURE__ */ jsx(
@@ -2398,10 +2860,9 @@ const HeaderActions = ({ actions: actions2 }) => {
2398
2860
  SingleSelect,
2399
2861
  {
2400
2862
  size: "S",
2401
- disabled: action.disabled,
2402
- "aria-label": action.label,
2403
2863
  onChange: action.onSelect,
2404
- value: action.value,
2864
+ "aria-label": action.label,
2865
+ ...action,
2405
2866
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2406
2867
  },
2407
2868
  action.id
@@ -2466,6 +2927,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2466
2927
  };
2467
2928
  };
2468
2929
  ConfigureTheViewAction.type = "configure-the-view";
2930
+ ConfigureTheViewAction.position = "header";
2469
2931
  const EditTheModelAction = ({ model }) => {
2470
2932
  const navigate = useNavigate();
2471
2933
  const { formatMessage } = useIntl();
@@ -2482,6 +2944,7 @@ const EditTheModelAction = ({ model }) => {
2482
2944
  };
2483
2945
  };
2484
2946
  EditTheModelAction.type = "edit-the-model";
2947
+ EditTheModelAction.position = "header";
2485
2948
  const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2486
2949
  const navigate = useNavigate();
2487
2950
  const { formatMessage } = useIntl();
@@ -2490,12 +2953,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2490
2953
  const { delete: deleteAction } = useDocumentActions();
2491
2954
  const { toggleNotification } = useNotification();
2492
2955
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2956
+ const isLocalized = document?.locale != null;
2493
2957
  return {
2494
2958
  disabled: !canDelete || !document,
2495
- label: formatMessage({
2496
- id: "content-manager.actions.delete.label",
2497
- defaultMessage: "Delete document"
2498
- }),
2959
+ label: formatMessage(
2960
+ {
2961
+ id: "content-manager.actions.delete.label",
2962
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2963
+ },
2964
+ { isLocalized }
2965
+ ),
2499
2966
  icon: /* @__PURE__ */ jsx(Trash, {}),
2500
2967
  dialog: {
2501
2968
  type: "dialog",
@@ -2534,421 +3001,119 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2534
3001
  collectionType,
2535
3002
  params: {
2536
3003
  locale: "*"
2537
- }
2538
- });
2539
- if (!("error" in res)) {
2540
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2541
- }
2542
- } finally {
2543
- if (!listViewPathMatch) {
2544
- setSubmitting(false);
2545
- }
2546
- }
2547
- }
2548
- },
2549
- variant: "danger",
2550
- position: ["header", "table-row"]
2551
- };
2552
- };
2553
- DeleteAction$1.type = "delete";
2554
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2555
- const Panels = () => {
2556
- const isCloning = useMatch(CLONE_PATH) !== null;
2557
- const [
2558
- {
2559
- query: { status }
2560
- }
2561
- ] = useQueryParams({
2562
- status: "draft"
2563
- });
2564
- const { model, id, document, meta, collectionType } = useDoc();
2565
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2566
- const props = {
2567
- activeTab: status,
2568
- model,
2569
- documentId: id,
2570
- document: isCloning ? void 0 : document,
2571
- meta: isCloning ? void 0 : meta,
2572
- collectionType
2573
- };
2574
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2575
- DescriptionComponentRenderer,
2576
- {
2577
- props,
2578
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2579
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2580
- }
2581
- ) });
2582
- };
2583
- const ActionsPanel = () => {
2584
- const { formatMessage } = useIntl();
2585
- return {
2586
- title: formatMessage({
2587
- id: "content-manager.containers.edit.panels.default.title",
2588
- defaultMessage: "Entry"
2589
- }),
2590
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2591
- };
2592
- };
2593
- ActionsPanel.type = "actions";
2594
- const ActionsPanelContent = () => {
2595
- const isCloning = useMatch(CLONE_PATH) !== null;
2596
- const [
2597
- {
2598
- query: { status = "draft" }
2599
- }
2600
- ] = useQueryParams();
2601
- const { model, id, document, meta, collectionType } = useDoc();
2602
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2603
- const props = {
2604
- activeTab: status,
2605
- model,
2606
- documentId: id,
2607
- document: isCloning ? void 0 : document,
2608
- meta: isCloning ? void 0 : meta,
2609
- collectionType
2610
- };
2611
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2612
- /* @__PURE__ */ jsx(
2613
- DescriptionComponentRenderer,
2614
- {
2615
- props,
2616
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2617
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2618
- }
2619
- ),
2620
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2621
- ] });
2622
- };
2623
- const Panel = React.forwardRef(({ children, title }, ref) => {
2624
- return /* @__PURE__ */ jsxs(
2625
- Flex,
2626
- {
2627
- ref,
2628
- tag: "aside",
2629
- "aria-labelledby": "additional-information",
2630
- background: "neutral0",
2631
- borderColor: "neutral150",
2632
- hasRadius: true,
2633
- paddingBottom: 4,
2634
- paddingLeft: 4,
2635
- paddingRight: 4,
2636
- paddingTop: 4,
2637
- shadow: "tableShadow",
2638
- gap: 3,
2639
- direction: "column",
2640
- justifyContent: "stretch",
2641
- alignItems: "flex-start",
2642
- children: [
2643
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2644
- children
2645
- ]
2646
- }
2647
- );
2648
- });
2649
- const HOOKS = {
2650
- /**
2651
- * Hook that allows to mutate the displayed headers of the list view table
2652
- * @constant
2653
- * @type {string}
2654
- */
2655
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2656
- /**
2657
- * Hook that allows to mutate the CM's collection types links pre-set filters
2658
- * @constant
2659
- * @type {string}
2660
- */
2661
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2662
- /**
2663
- * Hook that allows to mutate the CM's edit view layout
2664
- * @constant
2665
- * @type {string}
2666
- */
2667
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2668
- /**
2669
- * Hook that allows to mutate the CM's single types links pre-set filters
2670
- * @constant
2671
- * @type {string}
2672
- */
2673
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2674
- };
2675
- const contentTypesApi = contentManagerApi.injectEndpoints({
2676
- endpoints: (builder) => ({
2677
- getContentTypeConfiguration: builder.query({
2678
- query: (uid) => ({
2679
- url: `/content-manager/content-types/${uid}/configuration`,
2680
- method: "GET"
2681
- }),
2682
- transformResponse: (response) => response.data,
2683
- providesTags: (_result, _error, uid) => [
2684
- { type: "ContentTypesConfiguration", id: uid },
2685
- { type: "ContentTypeSettings", id: "LIST" }
2686
- ]
2687
- }),
2688
- getAllContentTypeSettings: builder.query({
2689
- query: () => "/content-manager/content-types-settings",
2690
- transformResponse: (response) => response.data,
2691
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2692
- }),
2693
- updateContentTypeConfiguration: builder.mutation({
2694
- query: ({ uid, ...body }) => ({
2695
- url: `/content-manager/content-types/${uid}/configuration`,
2696
- method: "PUT",
2697
- data: body
2698
- }),
2699
- transformResponse: (response) => response.data,
2700
- invalidatesTags: (_result, _error, { uid }) => [
2701
- { type: "ContentTypesConfiguration", id: uid },
2702
- { type: "ContentTypeSettings", id: "LIST" },
2703
- // Is this necessary?
2704
- { type: "InitialData" }
2705
- ]
2706
- })
2707
- })
2708
- });
2709
- const {
2710
- useGetContentTypeConfigurationQuery,
2711
- useGetAllContentTypeSettingsQuery,
2712
- useUpdateContentTypeConfigurationMutation
2713
- } = contentTypesApi;
2714
- const checkIfAttributeIsDisplayable = (attribute) => {
2715
- const { type } = attribute;
2716
- if (type === "relation") {
2717
- return !attribute.relation.toLowerCase().includes("morph");
2718
- }
2719
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2720
- };
2721
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2722
- if (!mainFieldName) {
2723
- return void 0;
2724
- }
2725
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2726
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2727
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2728
- );
2729
- return {
2730
- name: mainFieldName,
2731
- type: mainFieldType ?? "string"
2732
- };
2733
- };
2734
- const DEFAULT_SETTINGS = {
2735
- bulkable: false,
2736
- filterable: false,
2737
- searchable: false,
2738
- pagination: false,
2739
- defaultSortBy: "",
2740
- defaultSortOrder: "asc",
2741
- mainField: "id",
2742
- pageSize: 10
2743
- };
2744
- const useDocumentLayout = (model) => {
2745
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2746
- const [{ query }] = useQueryParams();
2747
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2748
- const { toggleNotification } = useNotification();
2749
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2750
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2751
- const {
2752
- data,
2753
- isLoading: isLoadingConfigs,
2754
- error,
2755
- isFetching: isFetchingConfigs
2756
- } = useGetContentTypeConfigurationQuery(model);
2757
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2758
- React.useEffect(() => {
2759
- if (error) {
2760
- toggleNotification({
2761
- type: "danger",
2762
- message: formatAPIError(error)
2763
- });
2764
- }
2765
- }, [error, formatAPIError, toggleNotification]);
2766
- const editLayout = React.useMemo(
2767
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2768
- layout: [],
2769
- components: {},
2770
- metadatas: {},
2771
- options: {},
2772
- settings: DEFAULT_SETTINGS
2773
- },
2774
- [data, isLoading, schemas, schema, components]
2775
- );
2776
- const listLayout = React.useMemo(() => {
2777
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2778
- layout: [],
2779
- metadatas: {},
2780
- options: {},
2781
- settings: DEFAULT_SETTINGS
2782
- };
2783
- }, [data, isLoading, schemas, schema, components]);
2784
- const { layout: edit } = React.useMemo(
2785
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2786
- layout: editLayout,
2787
- query
2788
- }),
2789
- [editLayout, query, runHookWaterfall]
2790
- );
2791
- return {
2792
- error,
2793
- isLoading,
2794
- edit,
2795
- list: listLayout
2796
- };
2797
- };
2798
- const useDocLayout = () => {
2799
- const { model } = useDoc();
2800
- return useDocumentLayout(model);
2801
- };
2802
- const formatEditLayout = (data, {
2803
- schemas,
2804
- schema,
2805
- components
2806
- }) => {
2807
- let currentPanelIndex = 0;
2808
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2809
- data.contentType.layouts.edit,
2810
- schema?.attributes,
2811
- data.contentType.metadatas,
2812
- { configurations: data.components, schemas: components },
2813
- schemas
2814
- ).reduce((panels, row) => {
2815
- if (row.some((field) => field.type === "dynamiczone")) {
2816
- panels.push([row]);
2817
- currentPanelIndex += 2;
2818
- } else {
2819
- if (!panels[currentPanelIndex]) {
2820
- panels.push([]);
2821
- }
2822
- panels[currentPanelIndex].push(row);
2823
- }
2824
- return panels;
2825
- }, []);
2826
- const componentEditAttributes = Object.entries(data.components).reduce(
2827
- (acc, [uid, configuration]) => {
2828
- acc[uid] = {
2829
- layout: convertEditLayoutToFieldLayouts(
2830
- configuration.layouts.edit,
2831
- components[uid].attributes,
2832
- configuration.metadatas,
2833
- { configurations: data.components, schemas: components }
2834
- ),
2835
- settings: {
2836
- ...configuration.settings,
2837
- icon: components[uid].info.icon,
2838
- displayName: components[uid].info.displayName
3004
+ }
3005
+ });
3006
+ if (!("error" in res)) {
3007
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
3008
+ }
3009
+ } finally {
3010
+ if (!listViewPathMatch) {
3011
+ setSubmitting(false);
3012
+ }
2839
3013
  }
2840
- };
2841
- return acc;
2842
- },
2843
- {}
2844
- );
2845
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2846
- (acc, [attribute, metadata]) => {
2847
- return {
2848
- ...acc,
2849
- [attribute]: metadata.edit
2850
- };
2851
- },
2852
- {}
2853
- );
2854
- return {
2855
- layout: panelledEditAttributes,
2856
- components: componentEditAttributes,
2857
- metadatas: editMetadatas,
2858
- settings: {
2859
- ...data.contentType.settings,
2860
- displayName: schema?.info.displayName
3014
+ }
2861
3015
  },
2862
- options: {
2863
- ...schema?.options,
2864
- ...schema?.pluginOptions,
2865
- ...data.contentType.options
2866
- }
3016
+ variant: "danger",
3017
+ position: ["header", "table-row"]
2867
3018
  };
2868
3019
  };
2869
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2870
- return rows.map(
2871
- (row) => row.map((field) => {
2872
- const attribute = attributes[field.name];
2873
- if (!attribute) {
2874
- return null;
2875
- }
2876
- const { edit: metadata } = metadatas[field.name];
2877
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2878
- return {
2879
- attribute,
2880
- disabled: !metadata.editable,
2881
- hint: metadata.description,
2882
- label: metadata.label ?? "",
2883
- name: field.name,
2884
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2885
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2886
- schemas,
2887
- components: components?.schemas ?? {}
2888
- }),
2889
- placeholder: metadata.placeholder ?? "",
2890
- required: attribute.required ?? false,
2891
- size: field.size,
2892
- unique: "unique" in attribute ? attribute.unique : false,
2893
- visible: metadata.visible ?? true,
2894
- type: attribute.type
2895
- };
2896
- }).filter((field) => field !== null)
2897
- );
3020
+ DeleteAction$1.type = "delete";
3021
+ DeleteAction$1.position = ["header", "table-row"];
3022
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
3023
+ const Panels = () => {
3024
+ const isCloning = useMatch(CLONE_PATH) !== null;
3025
+ const [
3026
+ {
3027
+ query: { status }
3028
+ }
3029
+ ] = useQueryParams({
3030
+ status: "draft"
3031
+ });
3032
+ const { model, id, document, meta, collectionType } = useDoc();
3033
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
3034
+ const props = {
3035
+ activeTab: status,
3036
+ model,
3037
+ documentId: id,
3038
+ document: isCloning ? void 0 : document,
3039
+ meta: isCloning ? void 0 : meta,
3040
+ collectionType
3041
+ };
3042
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
3043
+ DescriptionComponentRenderer,
3044
+ {
3045
+ props,
3046
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
3047
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
3048
+ }
3049
+ ) });
2898
3050
  };
2899
- const formatListLayout = (data, {
2900
- schemas,
2901
- schema,
2902
- components
2903
- }) => {
2904
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2905
- (acc, [attribute, metadata]) => {
2906
- return {
2907
- ...acc,
2908
- [attribute]: metadata.list
2909
- };
2910
- },
2911
- {}
2912
- );
2913
- const listAttributes = convertListLayoutToFieldLayouts(
2914
- data.contentType.layouts.list,
2915
- schema?.attributes,
2916
- listMetadatas,
2917
- { configurations: data.components, schemas: components },
2918
- schemas
2919
- );
3051
+ const ActionsPanel = () => {
3052
+ const { formatMessage } = useIntl();
2920
3053
  return {
2921
- layout: listAttributes,
2922
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2923
- metadatas: listMetadatas,
2924
- options: {
2925
- ...schema?.options,
2926
- ...schema?.pluginOptions,
2927
- ...data.contentType.options
2928
- }
3054
+ title: formatMessage({
3055
+ id: "content-manager.containers.edit.panels.default.title",
3056
+ defaultMessage: "Entry"
3057
+ }),
3058
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2929
3059
  };
2930
3060
  };
2931
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2932
- return columns.map((name) => {
2933
- const attribute = attributes[name];
2934
- if (!attribute) {
2935
- return null;
3061
+ ActionsPanel.type = "actions";
3062
+ const ActionsPanelContent = () => {
3063
+ const isCloning = useMatch(CLONE_PATH) !== null;
3064
+ const [
3065
+ {
3066
+ query: { status = "draft" }
2936
3067
  }
2937
- const metadata = metadatas[name];
2938
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2939
- return {
2940
- attribute,
2941
- label: metadata.label ?? "",
2942
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2943
- schemas,
2944
- components: components?.schemas ?? {}
2945
- }),
2946
- name,
2947
- searchable: metadata.searchable ?? true,
2948
- sortable: metadata.sortable ?? true
2949
- };
2950
- }).filter((field) => field !== null);
3068
+ ] = useQueryParams();
3069
+ const { model, id, document, meta, collectionType } = useDoc();
3070
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
3071
+ const props = {
3072
+ activeTab: status,
3073
+ model,
3074
+ documentId: id,
3075
+ document: isCloning ? void 0 : document,
3076
+ meta: isCloning ? void 0 : meta,
3077
+ collectionType
3078
+ };
3079
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
3080
+ /* @__PURE__ */ jsx(
3081
+ DescriptionComponentRenderer,
3082
+ {
3083
+ props,
3084
+ descriptions: plugins["content-manager"].apis.getDocumentActions("panel"),
3085
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
3086
+ }
3087
+ ),
3088
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
3089
+ ] });
2951
3090
  };
3091
+ const Panel = React.forwardRef(({ children, title }, ref) => {
3092
+ return /* @__PURE__ */ jsxs(
3093
+ Flex,
3094
+ {
3095
+ ref,
3096
+ tag: "aside",
3097
+ "aria-labelledby": "additional-information",
3098
+ background: "neutral0",
3099
+ borderColor: "neutral150",
3100
+ hasRadius: true,
3101
+ paddingBottom: 4,
3102
+ paddingLeft: 4,
3103
+ paddingRight: 4,
3104
+ paddingTop: 4,
3105
+ shadow: "tableShadow",
3106
+ gap: 3,
3107
+ direction: "column",
3108
+ justifyContent: "stretch",
3109
+ alignItems: "flex-start",
3110
+ children: [
3111
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3112
+ children
3113
+ ]
3114
+ }
3115
+ );
3116
+ });
2952
3117
  const ConfirmBulkActionDialog = ({
2953
3118
  onToggleDialog,
2954
3119
  isOpen = false,
@@ -3193,18 +3358,10 @@ const SelectedEntriesTableContent = ({
3193
3358
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3194
3359
  },
3195
3360
  state: { from: pathname },
3196
- label: formatMessage(
3197
- { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3198
- {
3199
- target: formatMessage(
3200
- {
3201
- id: "content-manager.components.ListViewHelperPluginTable.row-line",
3202
- defaultMessage: "item line {number}"
3203
- },
3204
- { number: index2 + 1 }
3205
- )
3206
- }
3207
- ),
3361
+ label: formatMessage({
3362
+ id: "content-manager.bulk-publish.edit",
3363
+ defaultMessage: "Edit"
3364
+ }),
3208
3365
  target: "_blank",
3209
3366
  marginLeft: "auto",
3210
3367
  variant: "ghost",
@@ -3378,8 +3535,7 @@ const PublishAction = ({ documents, model }) => {
3378
3535
  const refetchList = () => {
3379
3536
  contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3380
3537
  };
3381
- if (!showPublishButton)
3382
- return null;
3538
+ if (!showPublishButton) return null;
3383
3539
  return {
3384
3540
  actionType: "publish",
3385
3541
  variant: "tertiary",
@@ -3447,8 +3603,7 @@ const DeleteAction = ({ documents, model }) => {
3447
3603
  selectRow([]);
3448
3604
  }
3449
3605
  };
3450
- if (!hasDeletePermission)
3451
- return null;
3606
+ if (!hasDeletePermission) return null;
3452
3607
  return {
3453
3608
  variant: "danger-light",
3454
3609
  label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
@@ -3497,8 +3652,7 @@ const UnpublishAction = ({ documents, model }) => {
3497
3652
  }
3498
3653
  };
3499
3654
  const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3500
- if (!showUnpublishButton)
3501
- return null;
3655
+ if (!showUnpublishButton) return null;
3502
3656
  return {
3503
3657
  variant: "tertiary",
3504
3658
  label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
@@ -3603,7 +3757,7 @@ const TableActions = ({ document }) => {
3603
3757
  DescriptionComponentRenderer,
3604
3758
  {
3605
3759
  props,
3606
- descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3760
+ descriptions: plugins["content-manager"].apis.getDocumentActions("table-row").filter((action) => action.name !== "PublishAction"),
3607
3761
  children: (actions2) => {
3608
3762
  const tableRowActions = actions2.filter((action) => {
3609
3763
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3662,6 +3816,7 @@ const EditAction = ({ documentId }) => {
3662
3816
  };
3663
3817
  };
3664
3818
  EditAction.type = "edit";
3819
+ EditAction.position = "table-row";
3665
3820
  const StyledPencil = styled(Pencil)`
3666
3821
  path {
3667
3822
  fill: currentColor;
@@ -3738,6 +3893,7 @@ const CloneAction = ({ model, documentId }) => {
3738
3893
  };
3739
3894
  };
3740
3895
  CloneAction.type = "clone";
3896
+ CloneAction.position = "table-row";
3741
3897
  const StyledDuplicate = styled(Duplicate)`
3742
3898
  path {
3743
3899
  fill: currentColor;
@@ -3824,7 +3980,14 @@ class ContentManagerPlugin {
3824
3980
  addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3825
3981
  addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3826
3982
  getBulkActions: () => this.bulkActions,
3827
- getDocumentActions: () => this.documentActions,
3983
+ getDocumentActions: (position) => {
3984
+ if (position) {
3985
+ return this.documentActions.filter(
3986
+ (action) => action.position == void 0 || [action.position].flat().includes(position)
3987
+ );
3988
+ }
3989
+ return this.documentActions;
3990
+ },
3828
3991
  getEditViewSidePanels: () => this.editViewSidePanels,
3829
3992
  getHeaderActions: () => this.headerActions
3830
3993
  }
@@ -3834,10 +3997,8 @@ class ContentManagerPlugin {
3834
3997
  const getPrintableType = (value) => {
3835
3998
  const nativeType = typeof value;
3836
3999
  if (nativeType === "object") {
3837
- if (value === null)
3838
- return "null";
3839
- if (Array.isArray(value))
3840
- return "array";
4000
+ if (value === null) return "null";
4001
+ if (Array.isArray(value)) return "array";
3841
4002
  if (value instanceof Object && value.constructor.name !== "Object") {
3842
4003
  return value.constructor.name;
3843
4004
  }
@@ -3848,17 +4009,27 @@ const HistoryAction = ({ model, document }) => {
3848
4009
  const { formatMessage } = useIntl();
3849
4010
  const [{ query }] = useQueryParams();
3850
4011
  const navigate = useNavigate();
4012
+ const { trackUsage } = useTracking();
4013
+ const { pathname } = useLocation();
3851
4014
  const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
3852
4015
  if (!window.strapi.features.isEnabled("cms-content-history")) {
3853
4016
  return null;
3854
4017
  }
4018
+ const handleOnClick = () => {
4019
+ const destination = { pathname: "history", search: pluginsQueryParams };
4020
+ trackUsage("willNavigate", {
4021
+ from: pathname,
4022
+ to: `${pathname}/${destination.pathname}`
4023
+ });
4024
+ navigate(destination);
4025
+ };
3855
4026
  return {
3856
4027
  icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
3857
4028
  label: formatMessage({
3858
4029
  id: "content-manager.history.document-action",
3859
4030
  defaultMessage: "Content History"
3860
4031
  }),
3861
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
4032
+ onClick: handleOnClick,
3862
4033
  disabled: (
3863
4034
  /**
3864
4035
  * The user is creating a new document.
@@ -3880,6 +4051,7 @@ const HistoryAction = ({ model, document }) => {
3880
4051
  };
3881
4052
  };
3882
4053
  HistoryAction.type = "history";
4054
+ HistoryAction.position = "header";
3883
4055
  const historyAdmin = {
3884
4056
  bootstrap(app) {
3885
4057
  const { addDocumentAction } = app.getPlugin("content-manager").apis;
@@ -3926,6 +4098,88 @@ const { setInitialData } = actions;
3926
4098
  const reducer = combineReducers({
3927
4099
  app: reducer$1
3928
4100
  });
4101
+ const previewApi = contentManagerApi.injectEndpoints({
4102
+ endpoints: (builder) => ({
4103
+ getPreviewUrl: builder.query({
4104
+ query({ query, params }) {
4105
+ return {
4106
+ url: `/content-manager/preview/url/${params.contentType}`,
4107
+ method: "GET",
4108
+ config: {
4109
+ params: query
4110
+ }
4111
+ };
4112
+ }
4113
+ })
4114
+ })
4115
+ });
4116
+ const { useGetPreviewUrlQuery } = previewApi;
4117
+ const ConditionalTooltip = ({ isShown, label, children }) => {
4118
+ if (isShown) {
4119
+ return /* @__PURE__ */ jsx(Tooltip, { label, children });
4120
+ }
4121
+ return children;
4122
+ };
4123
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4124
+ const { formatMessage } = useIntl();
4125
+ const { trackUsage } = useTracking();
4126
+ const { pathname } = useLocation();
4127
+ const [{ query }] = useQueryParams();
4128
+ const isModified = useForm("PreviewSidePanel", (state) => state.modified);
4129
+ const { data, error } = useGetPreviewUrlQuery({
4130
+ params: {
4131
+ contentType: model
4132
+ },
4133
+ query: {
4134
+ documentId,
4135
+ locale: document?.locale,
4136
+ status: document?.status
4137
+ }
4138
+ });
4139
+ if (!data?.data?.url || error) {
4140
+ return null;
4141
+ }
4142
+ const trackNavigation = () => {
4143
+ const destinationPathname = pathname.replace(/\/$/, "") + "/preview";
4144
+ trackUsage("willNavigate", { from: pathname, to: destinationPathname });
4145
+ };
4146
+ return {
4147
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4148
+ content: /* @__PURE__ */ jsx(
4149
+ ConditionalTooltip,
4150
+ {
4151
+ label: formatMessage({
4152
+ id: "content-manager.preview.panel.button-disabled-tooltip",
4153
+ defaultMessage: "Please save to open the preview"
4154
+ }),
4155
+ isShown: isModified,
4156
+ children: /* @__PURE__ */ jsx(Box, { cursor: "not-allowed", width: "100%", children: /* @__PURE__ */ jsx(
4157
+ Button,
4158
+ {
4159
+ variant: "tertiary",
4160
+ tag: Link,
4161
+ to: { pathname: "preview", search: stringify(query, { encode: false }) },
4162
+ onClick: trackNavigation,
4163
+ width: "100%",
4164
+ disabled: isModified,
4165
+ pointerEvents: isModified ? "none" : void 0,
4166
+ tabIndex: isModified ? -1 : void 0,
4167
+ children: formatMessage({
4168
+ id: "content-manager.preview.panel.button",
4169
+ defaultMessage: "Open preview"
4170
+ })
4171
+ }
4172
+ ) })
4173
+ }
4174
+ )
4175
+ };
4176
+ };
4177
+ const previewAdmin = {
4178
+ bootstrap(app) {
4179
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4180
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4181
+ }
4182
+ };
3929
4183
  const index = {
3930
4184
  register(app) {
3931
4185
  const cm = new ContentManagerPlugin();
@@ -3945,7 +4199,7 @@ const index = {
3945
4199
  app.router.addRoute({
3946
4200
  path: "content-manager/*",
3947
4201
  lazy: async () => {
3948
- const { Layout } = await import("./layout-DX_52HSH.mjs");
4202
+ const { Layout } = await import("./layout-W3clJSCy.mjs");
3949
4203
  return {
3950
4204
  Component: Layout
3951
4205
  };
@@ -3958,11 +4212,14 @@ const index = {
3958
4212
  if (typeof historyAdmin.bootstrap === "function") {
3959
4213
  historyAdmin.bootstrap(app);
3960
4214
  }
4215
+ if (typeof previewAdmin.bootstrap === "function") {
4216
+ previewAdmin.bootstrap(app);
4217
+ }
3961
4218
  },
3962
4219
  async registerTrads({ locales }) {
3963
4220
  const importedTrads = await Promise.all(
3964
4221
  locales.map((locale) => {
3965
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-CPTj6CjC.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
4222
+ 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-CSxLmrh1.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 }) => {
3966
4223
  return {
3967
4224
  data: prefixPluginTranslations(data, PLUGIN_ID),
3968
4225
  locale
@@ -3989,8 +4246,10 @@ export {
3989
4246
  HOOKS as H,
3990
4247
  InjectionZone as I,
3991
4248
  useDocument as J,
3992
- index as K,
3993
- useDocumentActions as L,
4249
+ useGetPreviewUrlQuery as K,
4250
+ index as L,
4251
+ useContentManagerContext as M,
4252
+ useDocumentActions as N,
3994
4253
  Panels as P,
3995
4254
  RelativeTime as R,
3996
4255
  SINGLE_TYPES as S,
@@ -4022,4 +4281,4 @@ export {
4022
4281
  capitalise as y,
4023
4282
  useUpdateContentTypeConfigurationMutation as z
4024
4283
  };
4025
- //# sourceMappingURL=index-BHfS6_D5.mjs.map
4284
+ //# sourceMappingURL=index-DqasUQ6Q.mjs.map