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

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 (196) 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-CnL10QYC.mjs → ComponentConfigurationPage-A5f-t42A.mjs} +4 -4
  4. package/dist/_chunks/{ComponentConfigurationPage-CnL10QYC.mjs.map → ComponentConfigurationPage-A5f-t42A.mjs.map} +1 -1
  5. package/dist/_chunks/{ComponentConfigurationPage-G4EIirP8.js → ComponentConfigurationPage-BtmEfYUS.js} +5 -6
  6. package/dist/_chunks/{ComponentConfigurationPage-G4EIirP8.js.map → ComponentConfigurationPage-BtmEfYUS.js.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-B2AA1kVF.js → EditConfigurationPage-CXLQhPpj.js} +5 -6
  11. package/dist/_chunks/{EditConfigurationPage-B2AA1kVF.js.map → EditConfigurationPage-CXLQhPpj.js.map} +1 -1
  12. package/dist/_chunks/{EditConfigurationPage-I2kKh9dx.mjs → EditConfigurationPage-DN6yaDFZ.mjs} +4 -4
  13. package/dist/_chunks/{EditConfigurationPage-I2kKh9dx.mjs.map → EditConfigurationPage-DN6yaDFZ.mjs.map} +1 -1
  14. package/dist/_chunks/{EditViewPage-CHgoNwlc.js → EditViewPage-BW-BJJVg.js} +50 -11
  15. package/dist/_chunks/EditViewPage-BW-BJJVg.js.map +1 -0
  16. package/dist/_chunks/{EditViewPage-zFjJK0s8.mjs → EditViewPage-ONky_-8U.mjs} +50 -10
  17. package/dist/_chunks/EditViewPage-ONky_-8U.mjs.map +1 -0
  18. package/dist/_chunks/{Field-DPAzUS1M.mjs → Field-B4gAYDmQ.mjs} +215 -92
  19. package/dist/_chunks/Field-B4gAYDmQ.mjs.map +1 -0
  20. package/dist/_chunks/{Field-9DePZh-0.js → Field-BMlzghtV.js} +217 -94
  21. package/dist/_chunks/Field-BMlzghtV.js.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-DQPyv3UY.js} +6 -7
  25. package/dist/_chunks/Form-DQPyv3UY.js.map +1 -0
  26. package/dist/_chunks/{Form-CEkENbkF.mjs → Form-e4RpNQY0.mjs} +4 -4
  27. package/dist/_chunks/Form-e4RpNQY0.mjs.map +1 -0
  28. package/dist/_chunks/{History-utls71em.mjs → History-Bk2VCzmJ.mjs} +43 -100
  29. package/dist/_chunks/History-Bk2VCzmJ.mjs.map +1 -0
  30. package/dist/_chunks/{History-DXSbTWez.js → History-CzkXjAR0.js} +42 -100
  31. package/dist/_chunks/History-CzkXjAR0.js.map +1 -0
  32. package/dist/_chunks/{ListConfigurationPage-D5C7ACZ_.js → ListConfigurationPage-B9TbaEqp.js} +7 -7
  33. package/dist/_chunks/ListConfigurationPage-B9TbaEqp.js.map +1 -0
  34. package/dist/_chunks/{ListConfigurationPage-CuMXWWqb.mjs → ListConfigurationPage-BhNCzkQd.mjs} +7 -6
  35. package/dist/_chunks/ListConfigurationPage-BhNCzkQd.mjs.map +1 -0
  36. package/dist/_chunks/{ListViewPage-DfuwH1tt.js → ListViewPage-CIHO4H2J.js} +64 -42
  37. package/dist/_chunks/ListViewPage-CIHO4H2J.js.map +1 -0
  38. package/dist/_chunks/{ListViewPage-CdKd-PS_.mjs → ListViewPage-mK-sFVGU.mjs} +63 -40
  39. package/dist/_chunks/ListViewPage-mK-sFVGU.mjs.map +1 -0
  40. package/dist/_chunks/{NoContentTypePage-DkToTT7u.mjs → NoContentTypePage-BSVg7nZI.mjs} +2 -2
  41. package/dist/_chunks/{NoContentTypePage-DkToTT7u.mjs.map → NoContentTypePage-BSVg7nZI.mjs.map} +1 -1
  42. package/dist/_chunks/{NoContentTypePage-BIxlkWWi.js → NoContentTypePage-Dbfi49ek.js} +2 -2
  43. package/dist/_chunks/{NoContentTypePage-BIxlkWWi.js.map → NoContentTypePage-Dbfi49ek.js.map} +1 -1
  44. package/dist/_chunks/{NoPermissionsPage-Bu4GWYb-.js → NoPermissionsPage-CDvWOtEy.js} +2 -2
  45. package/dist/_chunks/{NoPermissionsPage-Bu4GWYb-.js.map → NoPermissionsPage-CDvWOtEy.js.map} +1 -1
  46. package/dist/_chunks/{NoPermissionsPage-DlWi4BAH.mjs → NoPermissionsPage-zpYME1_X.mjs} +2 -2
  47. package/dist/_chunks/{NoPermissionsPage-DlWi4BAH.mjs.map → NoPermissionsPage-zpYME1_X.mjs.map} +1 -1
  48. package/dist/_chunks/Preview-8U27vy1U.js +311 -0
  49. package/dist/_chunks/Preview-8U27vy1U.js.map +1 -0
  50. package/dist/_chunks/Preview-Dd3kQluA.mjs +293 -0
  51. package/dist/_chunks/Preview-Dd3kQluA.mjs.map +1 -0
  52. package/dist/_chunks/{Relations-QP5yn9_z.mjs → Relations-BvdARGTL.mjs} +75 -41
  53. package/dist/_chunks/Relations-BvdARGTL.mjs.map +1 -0
  54. package/dist/_chunks/{Relations-CFjTESWQ.js → Relations-CFMS6Dm8.js} +75 -42
  55. package/dist/_chunks/Relations-CFMS6Dm8.js.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-BHfS6_D5.mjs → index-B3tHjkLZ.mjs} +971 -724
  70. package/dist/_chunks/index-B3tHjkLZ.mjs.map +1 -0
  71. package/dist/_chunks/{index-DXiHxy70.js → index-TSBwtMDV.js} +969 -722
  72. package/dist/_chunks/index-TSBwtMDV.js.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-DX_52HSH.mjs → layout-C71zeI19.mjs} +4 -4
  78. package/dist/_chunks/{layout-DX_52HSH.mjs.map → layout-C71zeI19.mjs.map} +1 -1
  79. package/dist/_chunks/{layout-bE-WUnQ0.js → layout-CB2vrWLp.js} +5 -6
  80. package/dist/_chunks/{layout-bE-WUnQ0.js.map → layout-CB2vrWLp.js.map} +1 -1
  81. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  82. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  83. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  84. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  85. package/dist/_chunks/{relations-D706vblp.js → relations-8mON7ZVQ.js} +6 -7
  86. package/dist/_chunks/relations-8mON7ZVQ.js.map +1 -0
  87. package/dist/_chunks/{relations-SCVAL_aJ.mjs → relations-DcEHhh0U.mjs} +6 -7
  88. package/dist/_chunks/relations-DcEHhh0U.mjs.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/Header.d.ts +1 -0
  109. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  110. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  111. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  112. package/dist/admin/src/preview/index.d.ts +4 -0
  113. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  114. package/dist/admin/src/preview/routes.d.ts +3 -0
  115. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  116. package/dist/admin/src/router.d.ts +1 -1
  117. package/dist/admin/src/services/api.d.ts +1 -1
  118. package/dist/admin/src/services/components.d.ts +2 -2
  119. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  120. package/dist/admin/src/services/documents.d.ts +16 -19
  121. package/dist/admin/src/services/init.d.ts +1 -1
  122. package/dist/admin/src/services/relations.d.ts +2 -2
  123. package/dist/admin/src/services/uid.d.ts +3 -3
  124. package/dist/server/index.js +432 -194
  125. package/dist/server/index.js.map +1 -1
  126. package/dist/server/index.mjs +432 -193
  127. package/dist/server/index.mjs.map +1 -1
  128. package/dist/server/src/bootstrap.d.ts.map +1 -1
  129. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  130. package/dist/server/src/controllers/index.d.ts.map +1 -1
  131. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  132. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  133. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  134. package/dist/server/src/history/services/history.d.ts.map +1 -1
  135. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  136. package/dist/server/src/history/services/utils.d.ts +2 -3
  137. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  138. package/dist/server/src/index.d.ts +4 -4
  139. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  140. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  141. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  142. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  143. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  144. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  145. package/dist/server/src/preview/index.d.ts +4 -0
  146. package/dist/server/src/preview/index.d.ts.map +1 -0
  147. package/dist/server/src/preview/routes/index.d.ts +8 -0
  148. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  149. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  150. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  151. package/dist/server/src/preview/services/index.d.ts +16 -0
  152. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  153. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  154. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  155. package/dist/server/src/preview/services/preview.d.ts +12 -0
  156. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  157. package/dist/server/src/preview/utils.d.ts +19 -0
  158. package/dist/server/src/preview/utils.d.ts.map +1 -0
  159. package/dist/server/src/register.d.ts.map +1 -1
  160. package/dist/server/src/routes/index.d.ts.map +1 -1
  161. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  162. package/dist/server/src/services/document-metadata.d.ts +8 -8
  163. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  164. package/dist/server/src/services/index.d.ts +4 -4
  165. package/dist/server/src/services/index.d.ts.map +1 -1
  166. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  167. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  168. package/dist/server/src/utils/index.d.ts +2 -0
  169. package/dist/server/src/utils/index.d.ts.map +1 -1
  170. package/dist/shared/contracts/index.d.ts +1 -0
  171. package/dist/shared/contracts/index.d.ts.map +1 -1
  172. package/dist/shared/contracts/preview.d.ts +27 -0
  173. package/dist/shared/contracts/preview.d.ts.map +1 -0
  174. package/dist/shared/index.js +4 -0
  175. package/dist/shared/index.js.map +1 -1
  176. package/dist/shared/index.mjs +4 -0
  177. package/dist/shared/index.mjs.map +1 -1
  178. package/package.json +14 -12
  179. package/dist/_chunks/EditViewPage-CHgoNwlc.js.map +0 -1
  180. package/dist/_chunks/EditViewPage-zFjJK0s8.mjs.map +0 -1
  181. package/dist/_chunks/Field-9DePZh-0.js.map +0 -1
  182. package/dist/_chunks/Field-DPAzUS1M.mjs.map +0 -1
  183. package/dist/_chunks/Form-CEkENbkF.mjs.map +0 -1
  184. package/dist/_chunks/Form-DPm-KZ1A.js.map +0 -1
  185. package/dist/_chunks/History-DXSbTWez.js.map +0 -1
  186. package/dist/_chunks/History-utls71em.mjs.map +0 -1
  187. package/dist/_chunks/ListConfigurationPage-CuMXWWqb.mjs.map +0 -1
  188. package/dist/_chunks/ListConfigurationPage-D5C7ACZ_.js.map +0 -1
  189. package/dist/_chunks/ListViewPage-CdKd-PS_.mjs.map +0 -1
  190. package/dist/_chunks/ListViewPage-DfuwH1tt.js.map +0 -1
  191. package/dist/_chunks/Relations-CFjTESWQ.js.map +0 -1
  192. package/dist/_chunks/Relations-QP5yn9_z.mjs.map +0 -1
  193. package/dist/_chunks/index-BHfS6_D5.mjs.map +0 -1
  194. package/dist/_chunks/index-DXiHxy70.js.map +0 -1
  195. package/dist/_chunks/relations-D706vblp.js.map +0 -1
  196. 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,7 +228,8 @@ 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 }
231
+ { type: "UidAvailability", id: model },
232
+ "RecentDocumentList"
214
233
  ]
215
234
  }),
216
235
  deleteDocument: builder.mutation({
@@ -222,7 +241,8 @@ const documentApi = contentManagerApi.injectEndpoints({
222
241
  }
223
242
  }),
224
243
  invalidatesTags: (_result, _error, { collectionType, model }) => [
225
- { type: "Document", id: collectionType !== SINGLE_TYPES ? `${model}_LIST` : model }
244
+ { type: "Document", id: collectionType !== SINGLE_TYPES ? `${model}_LIST` : model },
245
+ "RecentDocumentList"
226
246
  ]
227
247
  }),
228
248
  deleteManyDocuments: builder.mutation({
@@ -234,7 +254,10 @@ const documentApi = contentManagerApi.injectEndpoints({
234
254
  params
235
255
  }
236
256
  }),
237
- invalidatesTags: (_res, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
257
+ invalidatesTags: (_res, _error, { model }) => [
258
+ { type: "Document", id: `${model}_LIST` },
259
+ "RecentDocumentList"
260
+ ]
238
261
  }),
239
262
  discardDocument: builder.mutation({
240
263
  query: ({ collectionType, model, documentId, params }) => ({
@@ -252,7 +275,8 @@ const documentApi = contentManagerApi.injectEndpoints({
252
275
  },
253
276
  { type: "Document", id: `${model}_LIST` },
254
277
  "Relations",
255
- { type: "UidAvailability", id: model }
278
+ { type: "UidAvailability", id: model },
279
+ "RecentDocumentList"
256
280
  ];
257
281
  }
258
282
  }),
@@ -265,7 +289,7 @@ const documentApi = contentManagerApi.injectEndpoints({
265
289
  url: `/content-manager/collection-types/${model}`,
266
290
  method: "GET",
267
291
  config: {
268
- params
292
+ params: stringify(params, { encode: true })
269
293
  }
270
294
  }),
271
295
  providesTags: (result, _error, arg) => {
@@ -347,7 +371,8 @@ const documentApi = contentManagerApi.injectEndpoints({
347
371
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
348
372
  },
349
373
  { type: "Document", id: `${model}_LIST` },
350
- "Relations"
374
+ "Relations",
375
+ "RecentDocumentList"
351
376
  ];
352
377
  }
353
378
  }),
@@ -378,7 +403,9 @@ const documentApi = contentManagerApi.injectEndpoints({
378
403
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
379
404
  },
380
405
  "Relations",
381
- { type: "UidAvailability", id: model }
406
+ { type: "UidAvailability", id: model },
407
+ "RecentDocumentList",
408
+ "RecentDocumentList"
382
409
  ];
383
410
  },
384
411
  async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
@@ -408,7 +435,8 @@ const documentApi = contentManagerApi.injectEndpoints({
408
435
  {
409
436
  type: "Document",
410
437
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
411
- }
438
+ },
439
+ "RecentDocumentList"
412
440
  ];
413
441
  }
414
442
  }),
@@ -421,7 +449,10 @@ const documentApi = contentManagerApi.injectEndpoints({
421
449
  params
422
450
  }
423
451
  }),
424
- invalidatesTags: (_res, _error, { model, documentIds }) => documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` }))
452
+ invalidatesTags: (_res, _error, { model, documentIds }) => [
453
+ ...documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` })),
454
+ "RecentDocumentList"
455
+ ]
425
456
  })
426
457
  })
427
458
  });
@@ -444,8 +475,7 @@ const {
444
475
  useUnpublishManyDocumentsMutation
445
476
  } = documentApi;
446
477
  const buildValidParams = (query) => {
447
- if (!query)
448
- return query;
478
+ if (!query) return query;
449
479
  const { plugins: _, ...validQueryParams } = {
450
480
  ...query,
451
481
  ...Object.values(query?.plugins ?? {}).reduce(
@@ -453,14 +483,29 @@ const buildValidParams = (query) => {
453
483
  {}
454
484
  )
455
485
  };
456
- if ("_q" in validQueryParams) {
457
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
458
- }
459
486
  return validQueryParams;
460
487
  };
461
488
  const isBaseQueryError = (error) => {
462
489
  return error.name !== void 0;
463
490
  };
491
+ const arrayValidator = (attribute, options) => ({
492
+ message: translatedErrors.required,
493
+ test(value) {
494
+ if (options.status === "draft") {
495
+ return true;
496
+ }
497
+ if (!attribute.required) {
498
+ return true;
499
+ }
500
+ if (!value) {
501
+ return false;
502
+ }
503
+ if (Array.isArray(value) && value.length === 0) {
504
+ return false;
505
+ }
506
+ return true;
507
+ }
508
+ });
464
509
  const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
465
510
  const createModelSchema = (attributes2) => yup.object().shape(
466
511
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
@@ -468,6 +513,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
468
513
  return acc;
469
514
  }
470
515
  const validations = [
516
+ addNullableValidation,
471
517
  addRequiredValidation,
472
518
  addMinLengthValidation,
473
519
  addMaxLengthValidation,
@@ -484,12 +530,12 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
484
530
  ...acc,
485
531
  [name]: transformSchema(
486
532
  yup.array().of(createModelSchema(attributes3).nullable(false))
487
- )
533
+ ).test(arrayValidator(attribute, options))
488
534
  };
489
535
  } else {
490
536
  return {
491
537
  ...acc,
492
- [name]: transformSchema(createModelSchema(attributes3))
538
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
493
539
  };
494
540
  }
495
541
  }
@@ -511,7 +557,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
511
557
  }
512
558
  )
513
559
  )
514
- )
560
+ ).test(arrayValidator(attribute, options))
515
561
  };
516
562
  case "relation":
517
563
  return {
@@ -523,7 +569,7 @@ const createYupSchema = (attributes = {}, components = {}, options = { status: n
523
569
  } else if (Array.isArray(value)) {
524
570
  return yup.array().of(
525
571
  yup.object().shape({
526
- id: yup.string().required()
572
+ id: yup.number().required()
527
573
  })
528
574
  );
529
575
  } else if (typeof value === "object") {
@@ -609,17 +655,17 @@ const nullableSchema = (schema) => {
609
655
  schema
610
656
  );
611
657
  };
658
+ const addNullableValidation = () => (schema) => {
659
+ return nullableSchema(schema);
660
+ };
612
661
  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);
662
+ if (options.status === "draft" || !attribute.required) {
663
+ return schema;
618
664
  }
619
- if (attribute.required && attribute.type !== "relation") {
665
+ if (attribute.required && "required" in schema) {
620
666
  return schema.required(translatedErrors.required);
621
667
  }
622
- return nullableSchema(schema);
668
+ return schema;
623
669
  };
624
670
  const addMinLengthValidation = (attribute, options) => (schema) => {
625
671
  if (options.status === "draft") {
@@ -647,31 +693,12 @@ const addMaxLengthValidation = (attribute) => (schema) => {
647
693
  return schema;
648
694
  };
649
695
  const addMinValidation = (attribute, options) => (schema) => {
650
- if ("min" in attribute) {
696
+ if (options.status === "draft") {
697
+ return schema;
698
+ }
699
+ if ("min" in attribute && "min" in schema) {
651
700
  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) {
701
+ if (min) {
675
702
  return schema.min(min, {
676
703
  ...translatedErrors.min,
677
704
  values: {
@@ -789,19 +816,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
789
816
  }, {});
790
817
  return componentsByKey;
791
818
  };
792
- const useDocument = (args, opts) => {
819
+ const HOOKS = {
820
+ /**
821
+ * Hook that allows to mutate the displayed headers of the list view table
822
+ * @constant
823
+ * @type {string}
824
+ */
825
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
826
+ /**
827
+ * Hook that allows to mutate the CM's collection types links pre-set filters
828
+ * @constant
829
+ * @type {string}
830
+ */
831
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
832
+ /**
833
+ * Hook that allows to mutate the CM's edit view layout
834
+ * @constant
835
+ * @type {string}
836
+ */
837
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
838
+ /**
839
+ * Hook that allows to mutate the CM's single types links pre-set filters
840
+ * @constant
841
+ * @type {string}
842
+ */
843
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
844
+ };
845
+ const contentTypesApi = contentManagerApi.injectEndpoints({
846
+ endpoints: (builder) => ({
847
+ getContentTypeConfiguration: builder.query({
848
+ query: (uid) => ({
849
+ url: `/content-manager/content-types/${uid}/configuration`,
850
+ method: "GET"
851
+ }),
852
+ transformResponse: (response) => response.data,
853
+ providesTags: (_result, _error, uid) => [
854
+ { type: "ContentTypesConfiguration", id: uid },
855
+ { type: "ContentTypeSettings", id: "LIST" }
856
+ ]
857
+ }),
858
+ getAllContentTypeSettings: builder.query({
859
+ query: () => "/content-manager/content-types-settings",
860
+ transformResponse: (response) => response.data,
861
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
862
+ }),
863
+ updateContentTypeConfiguration: builder.mutation({
864
+ query: ({ uid, ...body }) => ({
865
+ url: `/content-manager/content-types/${uid}/configuration`,
866
+ method: "PUT",
867
+ data: body
868
+ }),
869
+ transformResponse: (response) => response.data,
870
+ invalidatesTags: (_result, _error, { uid }) => [
871
+ { type: "ContentTypesConfiguration", id: uid },
872
+ { type: "ContentTypeSettings", id: "LIST" },
873
+ // Is this necessary?
874
+ { type: "InitialData" }
875
+ ]
876
+ })
877
+ })
878
+ });
879
+ const {
880
+ useGetContentTypeConfigurationQuery,
881
+ useGetAllContentTypeSettingsQuery,
882
+ useUpdateContentTypeConfigurationMutation
883
+ } = contentTypesApi;
884
+ const checkIfAttributeIsDisplayable = (attribute) => {
885
+ const { type } = attribute;
886
+ if (type === "relation") {
887
+ return !attribute.relation.toLowerCase().includes("morph");
888
+ }
889
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
890
+ };
891
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
892
+ if (!mainFieldName) {
893
+ return void 0;
894
+ }
895
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
896
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
897
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
898
+ );
899
+ return {
900
+ name: mainFieldName,
901
+ type: mainFieldType ?? "string"
902
+ };
903
+ };
904
+ const DEFAULT_SETTINGS = {
905
+ bulkable: false,
906
+ filterable: false,
907
+ searchable: false,
908
+ pagination: false,
909
+ defaultSortBy: "",
910
+ defaultSortOrder: "asc",
911
+ mainField: "id",
912
+ pageSize: 10
913
+ };
914
+ const useDocumentLayout = (model) => {
915
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
916
+ const [{ query }] = useQueryParams();
917
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
793
918
  const { toggleNotification } = useNotification();
794
919
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
920
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
795
921
  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);
922
+ data,
923
+ isLoading: isLoadingConfigs,
924
+ error,
925
+ isFetching: isFetchingConfigs
926
+ } = useGetContentTypeConfigurationQuery(model);
927
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
805
928
  React.useEffect(() => {
806
929
  if (error) {
807
930
  toggleNotification({
@@ -809,84 +932,339 @@ const useDocument = (args, opts) => {
809
932
  message: formatAPIError(error)
810
933
  });
811
934
  }
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
- }
935
+ }, [error, formatAPIError, toggleNotification]);
936
+ const editLayout = React.useMemo(
937
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
938
+ layout: [],
939
+ components: {},
940
+ metadatas: {},
941
+ options: {},
942
+ settings: DEFAULT_SETTINGS
835
943
  },
836
- [validationSchema]
944
+ [data, isLoading, schemas, schema, components]
945
+ );
946
+ const listLayout = React.useMemo(() => {
947
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
948
+ layout: [],
949
+ metadatas: {},
950
+ options: {},
951
+ settings: DEFAULT_SETTINGS
952
+ };
953
+ }, [data, isLoading, schemas, schema, components]);
954
+ const { layout: edit } = React.useMemo(
955
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
956
+ layout: editLayout,
957
+ query
958
+ }),
959
+ [editLayout, query, runHookWaterfall]
837
960
  );
838
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
839
961
  return {
840
- components,
841
- document: data?.data,
842
- meta: data?.meta,
962
+ error,
843
963
  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
- )
964
+ edit,
965
+ list: listLayout
868
966
  };
869
967
  };
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"
968
+ const useDocLayout = () => {
969
+ const { model } = useDoc();
970
+ return useDocumentLayout(model);
883
971
  };
884
- const useDocumentActions = () => {
885
- const { toggleNotification } = useNotification();
886
- const { formatMessage } = useIntl();
887
- const { trackUsage } = useTracking();
888
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
889
- const navigate = useNavigate();
972
+ const formatEditLayout = (data, {
973
+ schemas,
974
+ schema,
975
+ components
976
+ }) => {
977
+ let currentPanelIndex = 0;
978
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
979
+ data.contentType.layouts.edit,
980
+ schema?.attributes,
981
+ data.contentType.metadatas,
982
+ { configurations: data.components, schemas: components },
983
+ schemas
984
+ ).reduce((panels, row) => {
985
+ if (row.some((field) => field.type === "dynamiczone")) {
986
+ panels.push([row]);
987
+ currentPanelIndex += 2;
988
+ } else {
989
+ if (!panels[currentPanelIndex]) {
990
+ panels.push([row]);
991
+ } else {
992
+ panels[currentPanelIndex].push(row);
993
+ }
994
+ }
995
+ return panels;
996
+ }, []);
997
+ const componentEditAttributes = Object.entries(data.components).reduce(
998
+ (acc, [uid, configuration]) => {
999
+ acc[uid] = {
1000
+ layout: convertEditLayoutToFieldLayouts(
1001
+ configuration.layouts.edit,
1002
+ components[uid].attributes,
1003
+ configuration.metadatas,
1004
+ { configurations: data.components, schemas: components }
1005
+ ),
1006
+ settings: {
1007
+ ...configuration.settings,
1008
+ icon: components[uid].info.icon,
1009
+ displayName: components[uid].info.displayName
1010
+ }
1011
+ };
1012
+ return acc;
1013
+ },
1014
+ {}
1015
+ );
1016
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1017
+ (acc, [attribute, metadata]) => {
1018
+ return {
1019
+ ...acc,
1020
+ [attribute]: metadata.edit
1021
+ };
1022
+ },
1023
+ {}
1024
+ );
1025
+ return {
1026
+ layout: panelledEditAttributes,
1027
+ components: componentEditAttributes,
1028
+ metadatas: editMetadatas,
1029
+ settings: {
1030
+ ...data.contentType.settings,
1031
+ displayName: schema?.info.displayName
1032
+ },
1033
+ options: {
1034
+ ...schema?.options,
1035
+ ...schema?.pluginOptions,
1036
+ ...data.contentType.options
1037
+ }
1038
+ };
1039
+ };
1040
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1041
+ return rows.map(
1042
+ (row) => row.map((field) => {
1043
+ const attribute = attributes[field.name];
1044
+ if (!attribute) {
1045
+ return null;
1046
+ }
1047
+ const { edit: metadata } = metadatas[field.name];
1048
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1049
+ return {
1050
+ attribute,
1051
+ disabled: !metadata.editable,
1052
+ hint: metadata.description,
1053
+ label: metadata.label ?? "",
1054
+ name: field.name,
1055
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1056
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1057
+ schemas,
1058
+ components: components?.schemas ?? {}
1059
+ }),
1060
+ placeholder: metadata.placeholder ?? "",
1061
+ required: attribute.required ?? false,
1062
+ size: field.size,
1063
+ unique: "unique" in attribute ? attribute.unique : false,
1064
+ visible: metadata.visible ?? true,
1065
+ type: attribute.type
1066
+ };
1067
+ }).filter((field) => field !== null)
1068
+ );
1069
+ };
1070
+ const formatListLayout = (data, {
1071
+ schemas,
1072
+ schema,
1073
+ components
1074
+ }) => {
1075
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1076
+ (acc, [attribute, metadata]) => {
1077
+ return {
1078
+ ...acc,
1079
+ [attribute]: metadata.list
1080
+ };
1081
+ },
1082
+ {}
1083
+ );
1084
+ const listAttributes = convertListLayoutToFieldLayouts(
1085
+ data.contentType.layouts.list,
1086
+ schema?.attributes,
1087
+ listMetadatas,
1088
+ { configurations: data.components, schemas: components },
1089
+ schemas
1090
+ );
1091
+ return {
1092
+ layout: listAttributes,
1093
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1094
+ metadatas: listMetadatas,
1095
+ options: {
1096
+ ...schema?.options,
1097
+ ...schema?.pluginOptions,
1098
+ ...data.contentType.options
1099
+ }
1100
+ };
1101
+ };
1102
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1103
+ return columns.map((name) => {
1104
+ const attribute = attributes[name];
1105
+ if (!attribute) {
1106
+ return null;
1107
+ }
1108
+ const metadata = metadatas[name];
1109
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1110
+ return {
1111
+ attribute,
1112
+ label: metadata.label ?? "",
1113
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1114
+ schemas,
1115
+ components: components?.schemas ?? {}
1116
+ }),
1117
+ name,
1118
+ searchable: metadata.searchable ?? true,
1119
+ sortable: metadata.sortable ?? true
1120
+ };
1121
+ }).filter((field) => field !== null);
1122
+ };
1123
+ const useDocument = (args, opts) => {
1124
+ const { toggleNotification } = useNotification();
1125
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1126
+ const {
1127
+ currentData: data,
1128
+ isLoading: isLoadingDocument,
1129
+ isFetching: isFetchingDocument,
1130
+ error
1131
+ } = useGetDocumentQuery(args, {
1132
+ ...opts,
1133
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1134
+ });
1135
+ const {
1136
+ components,
1137
+ schema,
1138
+ schemas,
1139
+ isLoading: isLoadingSchema
1140
+ } = useContentTypeSchema(args.model);
1141
+ React.useEffect(() => {
1142
+ if (error) {
1143
+ toggleNotification({
1144
+ type: "danger",
1145
+ message: formatAPIError(error)
1146
+ });
1147
+ }
1148
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1149
+ const validationSchema = React.useMemo(() => {
1150
+ if (!schema) {
1151
+ return null;
1152
+ }
1153
+ return createYupSchema(schema.attributes, components);
1154
+ }, [schema, components]);
1155
+ const validate = React.useCallback(
1156
+ (document) => {
1157
+ if (!validationSchema) {
1158
+ throw new Error(
1159
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1160
+ );
1161
+ }
1162
+ try {
1163
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1164
+ return null;
1165
+ } catch (error2) {
1166
+ if (error2 instanceof ValidationError) {
1167
+ return getYupValidationErrors(error2);
1168
+ }
1169
+ throw error2;
1170
+ }
1171
+ },
1172
+ [validationSchema]
1173
+ );
1174
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1175
+ const hasError = !!error;
1176
+ return {
1177
+ components,
1178
+ document: data?.data,
1179
+ meta: data?.meta,
1180
+ isLoading,
1181
+ hasError,
1182
+ schema,
1183
+ schemas,
1184
+ validate
1185
+ };
1186
+ };
1187
+ const useDoc = () => {
1188
+ const { id, slug, collectionType, origin } = useParams();
1189
+ const [{ query }] = useQueryParams();
1190
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1191
+ if (!collectionType) {
1192
+ throw new Error("Could not find collectionType in url params");
1193
+ }
1194
+ if (!slug) {
1195
+ throw new Error("Could not find model in url params");
1196
+ }
1197
+ const document = useDocument(
1198
+ { documentId: origin || id, model: slug, collectionType, params },
1199
+ {
1200
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1201
+ }
1202
+ );
1203
+ const returnId = origin || id === "create" ? void 0 : id;
1204
+ return {
1205
+ collectionType,
1206
+ model: slug,
1207
+ id: returnId,
1208
+ ...document
1209
+ };
1210
+ };
1211
+ const useContentManagerContext = () => {
1212
+ const {
1213
+ collectionType,
1214
+ model,
1215
+ id,
1216
+ components,
1217
+ isLoading: isLoadingDoc,
1218
+ schema,
1219
+ schemas
1220
+ } = useDoc();
1221
+ const layout = useDocumentLayout(model);
1222
+ const form = useForm("useContentManagerContext", (state) => state);
1223
+ const isSingleType = collectionType === SINGLE_TYPES;
1224
+ const slug = model;
1225
+ const isCreatingEntry = id === "create";
1226
+ useContentTypeSchema();
1227
+ const isLoading = isLoadingDoc || layout.isLoading;
1228
+ const error = layout.error;
1229
+ return {
1230
+ error,
1231
+ isLoading,
1232
+ // Base metadata
1233
+ model,
1234
+ collectionType,
1235
+ id,
1236
+ slug,
1237
+ isCreatingEntry,
1238
+ isSingleType,
1239
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1240
+ // All schema infos
1241
+ components,
1242
+ contentType: schema,
1243
+ contentTypes: schemas,
1244
+ // Form state
1245
+ form,
1246
+ // layout infos
1247
+ layout
1248
+ };
1249
+ };
1250
+ const prefixPluginTranslations = (trad, pluginId) => {
1251
+ return Object.keys(trad).reduce((acc, current) => {
1252
+ acc[`${pluginId}.${current}`] = trad[current];
1253
+ return acc;
1254
+ }, {});
1255
+ };
1256
+ const getTranslation = (id) => `content-manager.${id}`;
1257
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1258
+ id: "notification.error",
1259
+ defaultMessage: "An error occurred, please try again"
1260
+ };
1261
+ const useDocumentActions = () => {
1262
+ const { toggleNotification } = useNotification();
1263
+ const { formatMessage } = useIntl();
1264
+ const { trackUsage } = useTracking();
1265
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1266
+ const navigate = useNavigate();
1267
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
890
1268
  const [deleteDocument] = useDeleteDocumentMutation();
891
1269
  const _delete = React.useCallback(
892
1270
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1201,6 +1579,7 @@ const useDocumentActions = () => {
1201
1579
  defaultMessage: "Saved document"
1202
1580
  })
1203
1581
  });
1582
+ setCurrentStep("contentManager.success");
1204
1583
  return res.data;
1205
1584
  } catch (err) {
1206
1585
  toggleNotification({
@@ -1302,10 +1681,10 @@ const useDocumentActions = () => {
1302
1681
  update
1303
1682
  };
1304
1683
  };
1305
- const ProtectedHistoryPage = lazy(
1306
- () => import("./History-utls71em.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1684
+ const ProtectedHistoryPage = React.lazy(
1685
+ () => import("./History-Bk2VCzmJ.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1307
1686
  );
1308
- const routes$1 = [
1687
+ const routes$2 = [
1309
1688
  {
1310
1689
  path: ":collectionType/:slug/:id/history",
1311
1690
  Component: ProtectedHistoryPage
@@ -1315,32 +1694,45 @@ const routes$1 = [
1315
1694
  Component: ProtectedHistoryPage
1316
1695
  }
1317
1696
  ];
1697
+ const ProtectedPreviewPage = React.lazy(
1698
+ () => import("./Preview-Dd3kQluA.mjs").then((mod) => ({ default: mod.ProtectedPreviewPage }))
1699
+ );
1700
+ const routes$1 = [
1701
+ {
1702
+ path: ":collectionType/:slug/:id/preview",
1703
+ Component: ProtectedPreviewPage
1704
+ },
1705
+ {
1706
+ path: ":collectionType/:slug/preview",
1707
+ Component: ProtectedPreviewPage
1708
+ }
1709
+ ];
1318
1710
  const ProtectedEditViewPage = lazy(
1319
- () => import("./EditViewPage-zFjJK0s8.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1711
+ () => import("./EditViewPage-ONky_-8U.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1320
1712
  );
1321
1713
  const ProtectedListViewPage = lazy(
1322
- () => import("./ListViewPage-CdKd-PS_.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1714
+ () => import("./ListViewPage-mK-sFVGU.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1323
1715
  );
1324
1716
  const ProtectedListConfiguration = lazy(
1325
- () => import("./ListConfigurationPage-CuMXWWqb.mjs").then((mod) => ({
1717
+ () => import("./ListConfigurationPage-BhNCzkQd.mjs").then((mod) => ({
1326
1718
  default: mod.ProtectedListConfiguration
1327
1719
  }))
1328
1720
  );
1329
1721
  const ProtectedEditConfigurationPage = lazy(
1330
- () => import("./EditConfigurationPage-I2kKh9dx.mjs").then((mod) => ({
1722
+ () => import("./EditConfigurationPage-DN6yaDFZ.mjs").then((mod) => ({
1331
1723
  default: mod.ProtectedEditConfigurationPage
1332
1724
  }))
1333
1725
  );
1334
1726
  const ProtectedComponentConfigurationPage = lazy(
1335
- () => import("./ComponentConfigurationPage-CnL10QYC.mjs").then((mod) => ({
1727
+ () => import("./ComponentConfigurationPage-A5f-t42A.mjs").then((mod) => ({
1336
1728
  default: mod.ProtectedComponentConfigurationPage
1337
1729
  }))
1338
1730
  );
1339
1731
  const NoPermissions = lazy(
1340
- () => import("./NoPermissionsPage-DlWi4BAH.mjs").then((mod) => ({ default: mod.NoPermissions }))
1732
+ () => import("./NoPermissionsPage-zpYME1_X.mjs").then((mod) => ({ default: mod.NoPermissions }))
1341
1733
  );
1342
1734
  const NoContentType = lazy(
1343
- () => import("./NoContentTypePage-DkToTT7u.mjs").then((mod) => ({ default: mod.NoContentType }))
1735
+ () => import("./NoContentTypePage-BSVg7nZI.mjs").then((mod) => ({ default: mod.NoContentType }))
1344
1736
  );
1345
1737
  const CollectionTypePages = () => {
1346
1738
  const { collectionType } = useParams();
@@ -1352,7 +1744,7 @@ const CollectionTypePages = () => {
1352
1744
  const CLONE_RELATIVE_PATH = ":collectionType/:slug/clone/:origin";
1353
1745
  const CLONE_PATH = `/content-manager/${CLONE_RELATIVE_PATH}`;
1354
1746
  const LIST_RELATIVE_PATH = ":collectionType/:slug";
1355
- const LIST_PATH = `/content-manager/${LIST_RELATIVE_PATH}`;
1747
+ const LIST_PATH = `/content-manager/collection-types/:slug`;
1356
1748
  const routes = [
1357
1749
  {
1358
1750
  path: LIST_RELATIVE_PATH,
@@ -1386,6 +1778,7 @@ const routes = [
1386
1778
  path: "no-content-types",
1387
1779
  Component: NoContentType
1388
1780
  },
1781
+ ...routes$2,
1389
1782
  ...routes$1
1390
1783
  ];
1391
1784
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1484,6 +1877,11 @@ const DocumentActionButton = (action) => {
1484
1877
  ) : null
1485
1878
  ] });
1486
1879
  };
1880
+ const MenuItem = styled(Menu.Item)`
1881
+ &:hover {
1882
+ background: ${({ theme, isVariantDanger, isDisabled }) => isVariantDanger && !isDisabled ? theme.colors.danger100 : "neutral"};
1883
+ }
1884
+ `;
1487
1885
  const DocumentActionsMenu = ({
1488
1886
  actions: actions2,
1489
1887
  children,
@@ -1542,48 +1940,32 @@ const DocumentActionsMenu = ({
1542
1940
  /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1543
1941
  actions2.map((action) => {
1544
1942
  return /* @__PURE__ */ jsx(
1545
- Menu.Item,
1943
+ MenuItem,
1546
1944
  {
1547
1945
  disabled: action.disabled,
1548
1946
  onSelect: handleClick(action),
1549
1947
  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
- ] })
1948
+ isVariantDanger: action.variant === "danger",
1949
+ isDisabled: action.disabled,
1950
+ children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", gap: 4, children: /* @__PURE__ */ jsxs(
1951
+ Flex,
1952
+ {
1953
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1954
+ gap: 2,
1955
+ tag: "span",
1956
+ children: [
1957
+ /* @__PURE__ */ jsx(
1958
+ Flex,
1959
+ {
1960
+ tag: "span",
1961
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1962
+ children: action.icon
1963
+ }
1964
+ ),
1965
+ action.label
1966
+ ]
1967
+ }
1968
+ ) })
1587
1969
  },
1588
1970
  action.id
1589
1971
  );
@@ -1694,6 +2076,18 @@ const DocumentActionModal = ({
1694
2076
  typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1695
2077
  ] }) });
1696
2078
  };
2079
+ const transformData = (data) => {
2080
+ if (Array.isArray(data)) {
2081
+ return data.map(transformData);
2082
+ }
2083
+ if (typeof data === "object" && data !== null) {
2084
+ if ("apiData" in data) {
2085
+ return data.apiData;
2086
+ }
2087
+ return mapValues(transformData)(data);
2088
+ }
2089
+ return data;
2090
+ };
1697
2091
  const PublishAction$1 = ({
1698
2092
  activeTab,
1699
2093
  documentId,
@@ -1708,6 +2102,7 @@ const PublishAction$1 = ({
1708
2102
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
1709
2103
  const isListView = useMatch(LIST_PATH) !== null;
1710
2104
  const isCloning = useMatch(CLONE_PATH) !== null;
2105
+ const { id } = useParams();
1711
2106
  const { formatMessage } = useIntl();
1712
2107
  const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1713
2108
  const { publish } = useDocumentActions();
@@ -1787,7 +2182,9 @@ const PublishAction$1 = ({
1787
2182
  const performPublish = async () => {
1788
2183
  setSubmitting(true);
1789
2184
  try {
1790
- const { errors } = await validate();
2185
+ const { errors } = await validate(true, {
2186
+ status: "published"
2187
+ });
1791
2188
  if (errors) {
1792
2189
  toggleNotification({
1793
2190
  type: "danger",
@@ -1805,13 +2202,15 @@ const PublishAction$1 = ({
1805
2202
  documentId,
1806
2203
  params
1807
2204
  },
1808
- formValues
2205
+ transformData(formValues)
1809
2206
  );
1810
2207
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1811
- navigate({
1812
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1813
- search: rawQuery
1814
- });
2208
+ if (id === "create") {
2209
+ navigate({
2210
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2211
+ search: rawQuery
2212
+ });
2213
+ }
1815
2214
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1816
2215
  setErrors(formatValidationErrors(res.error));
1817
2216
  }
@@ -1864,6 +2263,7 @@ const PublishAction$1 = ({
1864
2263
  };
1865
2264
  };
1866
2265
  PublishAction$1.type = "publish";
2266
+ PublishAction$1.position = "panel";
1867
2267
  const UpdateAction = ({
1868
2268
  activeTab,
1869
2269
  documentId,
@@ -1886,6 +2286,117 @@ const UpdateAction = ({
1886
2286
  const validate = useForm("UpdateAction", (state) => state.validate);
1887
2287
  const setErrors = useForm("UpdateAction", (state) => state.setErrors);
1888
2288
  const resetForm = useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
2289
+ const handleUpdate = React.useCallback(async () => {
2290
+ setSubmitting(true);
2291
+ try {
2292
+ if (!modified) {
2293
+ return;
2294
+ }
2295
+ const { errors } = await validate(true, {
2296
+ status: "draft"
2297
+ });
2298
+ if (errors) {
2299
+ toggleNotification({
2300
+ type: "danger",
2301
+ message: formatMessage({
2302
+ id: "content-manager.validation.error",
2303
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2304
+ })
2305
+ });
2306
+ return;
2307
+ }
2308
+ if (isCloning) {
2309
+ const res = await clone(
2310
+ {
2311
+ model,
2312
+ documentId: cloneMatch.params.origin,
2313
+ params
2314
+ },
2315
+ transformData(document)
2316
+ );
2317
+ if ("data" in res) {
2318
+ navigate(
2319
+ {
2320
+ pathname: `../${res.data.documentId}`,
2321
+ search: rawQuery
2322
+ },
2323
+ { relative: "path" }
2324
+ );
2325
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2326
+ setErrors(formatValidationErrors(res.error));
2327
+ }
2328
+ } else if (documentId || collectionType === SINGLE_TYPES) {
2329
+ const res = await update(
2330
+ {
2331
+ collectionType,
2332
+ model,
2333
+ documentId,
2334
+ params
2335
+ },
2336
+ transformData(document)
2337
+ );
2338
+ if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2339
+ setErrors(formatValidationErrors(res.error));
2340
+ } else {
2341
+ resetForm();
2342
+ }
2343
+ } else {
2344
+ const res = await create(
2345
+ {
2346
+ model,
2347
+ params
2348
+ },
2349
+ transformData(document)
2350
+ );
2351
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2352
+ navigate(
2353
+ {
2354
+ pathname: `../${res.data.documentId}`,
2355
+ search: rawQuery
2356
+ },
2357
+ { replace: true, relative: "path" }
2358
+ );
2359
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2360
+ setErrors(formatValidationErrors(res.error));
2361
+ }
2362
+ }
2363
+ } finally {
2364
+ setSubmitting(false);
2365
+ }
2366
+ }, [
2367
+ clone,
2368
+ cloneMatch?.params.origin,
2369
+ collectionType,
2370
+ create,
2371
+ document,
2372
+ documentId,
2373
+ formatMessage,
2374
+ formatValidationErrors,
2375
+ isCloning,
2376
+ model,
2377
+ modified,
2378
+ navigate,
2379
+ params,
2380
+ rawQuery,
2381
+ resetForm,
2382
+ setErrors,
2383
+ setSubmitting,
2384
+ toggleNotification,
2385
+ update,
2386
+ validate
2387
+ ]);
2388
+ React.useEffect(() => {
2389
+ const handleKeyDown = (e) => {
2390
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
2391
+ e.preventDefault();
2392
+ handleUpdate();
2393
+ }
2394
+ };
2395
+ window.addEventListener("keydown", handleKeyDown);
2396
+ return () => {
2397
+ window.removeEventListener("keydown", handleKeyDown);
2398
+ };
2399
+ }, [handleUpdate]);
1889
2400
  return {
1890
2401
  /**
1891
2402
  * Disabled when:
@@ -1895,87 +2406,14 @@ const UpdateAction = ({
1895
2406
  */
1896
2407
  disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1897
2408
  label: formatMessage({
1898
- id: "content-manager.containers.Edit.save",
2409
+ id: "global.save",
1899
2410
  defaultMessage: "Save"
1900
2411
  }),
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
- }
2412
+ onClick: handleUpdate
1976
2413
  };
1977
2414
  };
1978
2415
  UpdateAction.type = "update";
2416
+ UpdateAction.position = "panel";
1979
2417
  const UNPUBLISH_DRAFT_OPTIONS = {
1980
2418
  KEEP: "keep",
1981
2419
  DISCARD: "discard"
@@ -2098,6 +2536,7 @@ const UnpublishAction$1 = ({
2098
2536
  };
2099
2537
  };
2100
2538
  UnpublishAction$1.type = "unpublish";
2539
+ UnpublishAction$1.position = "panel";
2101
2540
  const DiscardAction = ({
2102
2541
  activeTab,
2103
2542
  documentId,
@@ -2148,6 +2587,7 @@ const DiscardAction = ({
2148
2587
  };
2149
2588
  };
2150
2589
  DiscardAction.type = "discard";
2590
+ DiscardAction.position = "panel";
2151
2591
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2152
2592
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2153
2593
  const RelativeTime = React.forwardRef(
@@ -2160,7 +2600,7 @@ const RelativeTime = React.forwardRef(
2160
2600
  });
2161
2601
  const unit = intervals.find((intervalUnit) => {
2162
2602
  return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
2163
- });
2603
+ }) ?? "seconds";
2164
2604
  const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
2165
2605
  const customInterval = customIntervals.find(
2166
2606
  (custom) => interval[custom.unit] < custom.threshold
@@ -2194,19 +2634,29 @@ const getDisplayName = ({
2194
2634
  return email ?? "";
2195
2635
  };
2196
2636
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2197
- const DocumentStatus = ({ status = "draft", ...restProps }) => {
2637
+ const DocumentStatus = ({ status = "draft", size = "S", ...restProps }) => {
2198
2638
  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) }) });
2639
+ const { formatMessage } = useIntl();
2640
+ return /* @__PURE__ */ jsx(Status, { ...restProps, size, variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2641
+ id: `content-manager.containers.List.${status}`,
2642
+ defaultMessage: capitalise(status)
2643
+ }) }) });
2200
2644
  };
2201
2645
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2202
2646
  const { formatMessage } = useIntl();
2203
2647
  const isCloning = useMatch(CLONE_PATH) !== null;
2648
+ const params = useParams();
2204
2649
  const title = isCreating ? formatMessage({
2205
2650
  id: "content-manager.containers.edit.title.new",
2206
2651
  defaultMessage: "Create an entry"
2207
2652
  }) : documentTitle;
2208
2653
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2209
- /* @__PURE__ */ jsx(BackButton, {}),
2654
+ /* @__PURE__ */ jsx(
2655
+ BackButton,
2656
+ {
2657
+ fallback: params.collectionType === SINGLE_TYPES ? void 0 : `../${COLLECTION_TYPES}/${params.slug}`
2658
+ }
2659
+ ),
2210
2660
  /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2211
2661
  /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2212
2662
  /* @__PURE__ */ jsx(HeaderToolbar, {})
@@ -2257,7 +2707,7 @@ const HeaderToolbar = () => {
2257
2707
  meta: isCloning ? void 0 : meta,
2258
2708
  collectionType
2259
2709
  },
2260
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2710
+ descriptions: plugins["content-manager"].apis.getDocumentActions("header"),
2261
2711
  children: (actions2) => {
2262
2712
  const headerActions = actions2.filter((action) => {
2263
2713
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -2294,12 +2744,12 @@ const Information = ({ activeTab }) => {
2294
2744
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2295
2745
  label: formatMessage({
2296
2746
  id: "content-manager.containers.edit.information.last-published.label",
2297
- defaultMessage: "Last published"
2747
+ defaultMessage: "Published"
2298
2748
  }),
2299
2749
  value: formatMessage(
2300
2750
  {
2301
2751
  id: "content-manager.containers.edit.information.last-published.value",
2302
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2752
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2303
2753
  },
2304
2754
  {
2305
2755
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2312,12 +2762,12 @@ const Information = ({ activeTab }) => {
2312
2762
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2313
2763
  label: formatMessage({
2314
2764
  id: "content-manager.containers.edit.information.last-draft.label",
2315
- defaultMessage: "Last draft"
2765
+ defaultMessage: "Updated"
2316
2766
  }),
2317
2767
  value: formatMessage(
2318
2768
  {
2319
2769
  id: "content-manager.containers.edit.information.last-draft.value",
2320
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2770
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2321
2771
  },
2322
2772
  {
2323
2773
  time: /* @__PURE__ */ jsx(
@@ -2335,12 +2785,12 @@ const Information = ({ activeTab }) => {
2335
2785
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2336
2786
  label: formatMessage({
2337
2787
  id: "content-manager.containers.edit.information.document.label",
2338
- defaultMessage: "Document"
2788
+ defaultMessage: "Created"
2339
2789
  }),
2340
2790
  value: formatMessage(
2341
2791
  {
2342
2792
  id: "content-manager.containers.edit.information.document.value",
2343
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2793
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2344
2794
  },
2345
2795
  {
2346
2796
  time: /* @__PURE__ */ jsx(
@@ -2398,10 +2848,9 @@ const HeaderActions = ({ actions: actions2 }) => {
2398
2848
  SingleSelect,
2399
2849
  {
2400
2850
  size: "S",
2401
- disabled: action.disabled,
2402
- "aria-label": action.label,
2403
2851
  onChange: action.onSelect,
2404
- value: action.value,
2852
+ "aria-label": action.label,
2853
+ ...action,
2405
2854
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2406
2855
  },
2407
2856
  action.id
@@ -2466,6 +2915,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2466
2915
  };
2467
2916
  };
2468
2917
  ConfigureTheViewAction.type = "configure-the-view";
2918
+ ConfigureTheViewAction.position = "header";
2469
2919
  const EditTheModelAction = ({ model }) => {
2470
2920
  const navigate = useNavigate();
2471
2921
  const { formatMessage } = useIntl();
@@ -2482,6 +2932,7 @@ const EditTheModelAction = ({ model }) => {
2482
2932
  };
2483
2933
  };
2484
2934
  EditTheModelAction.type = "edit-the-model";
2935
+ EditTheModelAction.position = "header";
2485
2936
  const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2486
2937
  const navigate = useNavigate();
2487
2938
  const { formatMessage } = useIntl();
@@ -2490,12 +2941,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2490
2941
  const { delete: deleteAction } = useDocumentActions();
2491
2942
  const { toggleNotification } = useNotification();
2492
2943
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2944
+ const isLocalized = document?.locale != null;
2493
2945
  return {
2494
2946
  disabled: !canDelete || !document,
2495
- label: formatMessage({
2496
- id: "content-manager.actions.delete.label",
2497
- defaultMessage: "Delete document"
2498
- }),
2947
+ label: formatMessage(
2948
+ {
2949
+ id: "content-manager.actions.delete.label",
2950
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2951
+ },
2952
+ { isLocalized }
2953
+ ),
2499
2954
  icon: /* @__PURE__ */ jsx(Trash, {}),
2500
2955
  dialog: {
2501
2956
  type: "dialog",
@@ -2534,421 +2989,119 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2534
2989
  collectionType,
2535
2990
  params: {
2536
2991
  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
2992
+ }
2993
+ });
2994
+ if (!("error" in res)) {
2995
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2996
+ }
2997
+ } finally {
2998
+ if (!listViewPathMatch) {
2999
+ setSubmitting(false);
3000
+ }
2839
3001
  }
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
3002
+ }
2861
3003
  },
2862
- options: {
2863
- ...schema?.options,
2864
- ...schema?.pluginOptions,
2865
- ...data.contentType.options
2866
- }
3004
+ variant: "danger",
3005
+ position: ["header", "table-row"]
2867
3006
  };
2868
3007
  };
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
- );
3008
+ DeleteAction$1.type = "delete";
3009
+ DeleteAction$1.position = ["header", "table-row"];
3010
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
3011
+ const Panels = () => {
3012
+ const isCloning = useMatch(CLONE_PATH) !== null;
3013
+ const [
3014
+ {
3015
+ query: { status }
3016
+ }
3017
+ ] = useQueryParams({
3018
+ status: "draft"
3019
+ });
3020
+ const { model, id, document, meta, collectionType } = useDoc();
3021
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
3022
+ const props = {
3023
+ activeTab: status,
3024
+ model,
3025
+ documentId: id,
3026
+ document: isCloning ? void 0 : document,
3027
+ meta: isCloning ? void 0 : meta,
3028
+ collectionType
3029
+ };
3030
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
3031
+ DescriptionComponentRenderer,
3032
+ {
3033
+ props,
3034
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
3035
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
3036
+ }
3037
+ ) });
2898
3038
  };
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
- );
3039
+ const ActionsPanel = () => {
3040
+ const { formatMessage } = useIntl();
2920
3041
  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
- }
3042
+ title: formatMessage({
3043
+ id: "content-manager.containers.edit.panels.default.title",
3044
+ defaultMessage: "Entry"
3045
+ }),
3046
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2929
3047
  };
2930
3048
  };
2931
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2932
- return columns.map((name) => {
2933
- const attribute = attributes[name];
2934
- if (!attribute) {
2935
- return null;
3049
+ ActionsPanel.type = "actions";
3050
+ const ActionsPanelContent = () => {
3051
+ const isCloning = useMatch(CLONE_PATH) !== null;
3052
+ const [
3053
+ {
3054
+ query: { status = "draft" }
2936
3055
  }
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);
3056
+ ] = useQueryParams();
3057
+ const { model, id, document, meta, collectionType } = useDoc();
3058
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
3059
+ const props = {
3060
+ activeTab: status,
3061
+ model,
3062
+ documentId: id,
3063
+ document: isCloning ? void 0 : document,
3064
+ meta: isCloning ? void 0 : meta,
3065
+ collectionType
3066
+ };
3067
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
3068
+ /* @__PURE__ */ jsx(
3069
+ DescriptionComponentRenderer,
3070
+ {
3071
+ props,
3072
+ descriptions: plugins["content-manager"].apis.getDocumentActions("panel"),
3073
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
3074
+ }
3075
+ ),
3076
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
3077
+ ] });
2951
3078
  };
3079
+ const Panel = React.forwardRef(({ children, title }, ref) => {
3080
+ return /* @__PURE__ */ jsxs(
3081
+ Flex,
3082
+ {
3083
+ ref,
3084
+ tag: "aside",
3085
+ "aria-labelledby": "additional-information",
3086
+ background: "neutral0",
3087
+ borderColor: "neutral150",
3088
+ hasRadius: true,
3089
+ paddingBottom: 4,
3090
+ paddingLeft: 4,
3091
+ paddingRight: 4,
3092
+ paddingTop: 4,
3093
+ shadow: "tableShadow",
3094
+ gap: 3,
3095
+ direction: "column",
3096
+ justifyContent: "stretch",
3097
+ alignItems: "flex-start",
3098
+ children: [
3099
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3100
+ children
3101
+ ]
3102
+ }
3103
+ );
3104
+ });
2952
3105
  const ConfirmBulkActionDialog = ({
2953
3106
  onToggleDialog,
2954
3107
  isOpen = false,
@@ -3193,18 +3346,10 @@ const SelectedEntriesTableContent = ({
3193
3346
  search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3194
3347
  },
3195
3348
  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
- ),
3349
+ label: formatMessage({
3350
+ id: "content-manager.bulk-publish.edit",
3351
+ defaultMessage: "Edit"
3352
+ }),
3208
3353
  target: "_blank",
3209
3354
  marginLeft: "auto",
3210
3355
  variant: "ghost",
@@ -3378,8 +3523,7 @@ const PublishAction = ({ documents, model }) => {
3378
3523
  const refetchList = () => {
3379
3524
  contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3380
3525
  };
3381
- if (!showPublishButton)
3382
- return null;
3526
+ if (!showPublishButton) return null;
3383
3527
  return {
3384
3528
  actionType: "publish",
3385
3529
  variant: "tertiary",
@@ -3447,8 +3591,7 @@ const DeleteAction = ({ documents, model }) => {
3447
3591
  selectRow([]);
3448
3592
  }
3449
3593
  };
3450
- if (!hasDeletePermission)
3451
- return null;
3594
+ if (!hasDeletePermission) return null;
3452
3595
  return {
3453
3596
  variant: "danger-light",
3454
3597
  label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
@@ -3497,8 +3640,7 @@ const UnpublishAction = ({ documents, model }) => {
3497
3640
  }
3498
3641
  };
3499
3642
  const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3500
- if (!showUnpublishButton)
3501
- return null;
3643
+ if (!showUnpublishButton) return null;
3502
3644
  return {
3503
3645
  variant: "tertiary",
3504
3646
  label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
@@ -3603,7 +3745,7 @@ const TableActions = ({ document }) => {
3603
3745
  DescriptionComponentRenderer,
3604
3746
  {
3605
3747
  props,
3606
- descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3748
+ descriptions: plugins["content-manager"].apis.getDocumentActions("table-row").filter((action) => action.name !== "PublishAction"),
3607
3749
  children: (actions2) => {
3608
3750
  const tableRowActions = actions2.filter((action) => {
3609
3751
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3662,6 +3804,7 @@ const EditAction = ({ documentId }) => {
3662
3804
  };
3663
3805
  };
3664
3806
  EditAction.type = "edit";
3807
+ EditAction.position = "table-row";
3665
3808
  const StyledPencil = styled(Pencil)`
3666
3809
  path {
3667
3810
  fill: currentColor;
@@ -3738,6 +3881,7 @@ const CloneAction = ({ model, documentId }) => {
3738
3881
  };
3739
3882
  };
3740
3883
  CloneAction.type = "clone";
3884
+ CloneAction.position = "table-row";
3741
3885
  const StyledDuplicate = styled(Duplicate)`
3742
3886
  path {
3743
3887
  fill: currentColor;
@@ -3824,7 +3968,14 @@ class ContentManagerPlugin {
3824
3968
  addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3825
3969
  addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3826
3970
  getBulkActions: () => this.bulkActions,
3827
- getDocumentActions: () => this.documentActions,
3971
+ getDocumentActions: (position) => {
3972
+ if (position) {
3973
+ return this.documentActions.filter(
3974
+ (action) => action.position == void 0 || [action.position].flat().includes(position)
3975
+ );
3976
+ }
3977
+ return this.documentActions;
3978
+ },
3828
3979
  getEditViewSidePanels: () => this.editViewSidePanels,
3829
3980
  getHeaderActions: () => this.headerActions
3830
3981
  }
@@ -3834,10 +3985,8 @@ class ContentManagerPlugin {
3834
3985
  const getPrintableType = (value) => {
3835
3986
  const nativeType = typeof value;
3836
3987
  if (nativeType === "object") {
3837
- if (value === null)
3838
- return "null";
3839
- if (Array.isArray(value))
3840
- return "array";
3988
+ if (value === null) return "null";
3989
+ if (Array.isArray(value)) return "array";
3841
3990
  if (value instanceof Object && value.constructor.name !== "Object") {
3842
3991
  return value.constructor.name;
3843
3992
  }
@@ -3848,17 +3997,27 @@ const HistoryAction = ({ model, document }) => {
3848
3997
  const { formatMessage } = useIntl();
3849
3998
  const [{ query }] = useQueryParams();
3850
3999
  const navigate = useNavigate();
4000
+ const { trackUsage } = useTracking();
4001
+ const { pathname } = useLocation();
3851
4002
  const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });
3852
4003
  if (!window.strapi.features.isEnabled("cms-content-history")) {
3853
4004
  return null;
3854
4005
  }
4006
+ const handleOnClick = () => {
4007
+ const destination = { pathname: "history", search: pluginsQueryParams };
4008
+ trackUsage("willNavigate", {
4009
+ from: pathname,
4010
+ to: `${pathname}/${destination.pathname}`
4011
+ });
4012
+ navigate(destination);
4013
+ };
3855
4014
  return {
3856
4015
  icon: /* @__PURE__ */ jsx(ClockCounterClockwise, {}),
3857
4016
  label: formatMessage({
3858
4017
  id: "content-manager.history.document-action",
3859
4018
  defaultMessage: "Content History"
3860
4019
  }),
3861
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
4020
+ onClick: handleOnClick,
3862
4021
  disabled: (
3863
4022
  /**
3864
4023
  * The user is creating a new document.
@@ -3880,6 +4039,7 @@ const HistoryAction = ({ model, document }) => {
3880
4039
  };
3881
4040
  };
3882
4041
  HistoryAction.type = "history";
4042
+ HistoryAction.position = "header";
3883
4043
  const historyAdmin = {
3884
4044
  bootstrap(app) {
3885
4045
  const { addDocumentAction } = app.getPlugin("content-manager").apis;
@@ -3926,6 +4086,88 @@ const { setInitialData } = actions;
3926
4086
  const reducer = combineReducers({
3927
4087
  app: reducer$1
3928
4088
  });
4089
+ const previewApi = contentManagerApi.injectEndpoints({
4090
+ endpoints: (builder) => ({
4091
+ getPreviewUrl: builder.query({
4092
+ query({ query, params }) {
4093
+ return {
4094
+ url: `/content-manager/preview/url/${params.contentType}`,
4095
+ method: "GET",
4096
+ config: {
4097
+ params: query
4098
+ }
4099
+ };
4100
+ }
4101
+ })
4102
+ })
4103
+ });
4104
+ const { useGetPreviewUrlQuery } = previewApi;
4105
+ const ConditionalTooltip = ({ isShown, label, children }) => {
4106
+ if (isShown) {
4107
+ return /* @__PURE__ */ jsx(Tooltip, { label, children });
4108
+ }
4109
+ return children;
4110
+ };
4111
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4112
+ const { formatMessage } = useIntl();
4113
+ const { trackUsage } = useTracking();
4114
+ const { pathname } = useLocation();
4115
+ const [{ query }] = useQueryParams();
4116
+ const isModified = useForm("PreviewSidePanel", (state) => state.modified);
4117
+ const { data, error } = useGetPreviewUrlQuery({
4118
+ params: {
4119
+ contentType: model
4120
+ },
4121
+ query: {
4122
+ documentId,
4123
+ locale: document?.locale,
4124
+ status: document?.status
4125
+ }
4126
+ });
4127
+ if (!data?.data?.url || error) {
4128
+ return null;
4129
+ }
4130
+ const trackNavigation = () => {
4131
+ const destinationPathname = pathname.replace(/\/$/, "") + "/preview";
4132
+ trackUsage("willNavigate", { from: pathname, to: destinationPathname });
4133
+ };
4134
+ return {
4135
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4136
+ content: /* @__PURE__ */ jsx(
4137
+ ConditionalTooltip,
4138
+ {
4139
+ label: formatMessage({
4140
+ id: "content-manager.preview.panel.button-disabled-tooltip",
4141
+ defaultMessage: "Please save to open the preview"
4142
+ }),
4143
+ isShown: isModified,
4144
+ children: /* @__PURE__ */ jsx(Box, { cursor: "not-allowed", width: "100%", children: /* @__PURE__ */ jsx(
4145
+ Button,
4146
+ {
4147
+ variant: "tertiary",
4148
+ tag: Link,
4149
+ to: { pathname: "preview", search: stringify(query, { encode: false }) },
4150
+ onClick: trackNavigation,
4151
+ width: "100%",
4152
+ disabled: isModified,
4153
+ pointerEvents: isModified ? "none" : void 0,
4154
+ tabIndex: isModified ? -1 : void 0,
4155
+ children: formatMessage({
4156
+ id: "content-manager.preview.panel.button",
4157
+ defaultMessage: "Open preview"
4158
+ })
4159
+ }
4160
+ ) })
4161
+ }
4162
+ )
4163
+ };
4164
+ };
4165
+ const previewAdmin = {
4166
+ bootstrap(app) {
4167
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4168
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4169
+ }
4170
+ };
3929
4171
  const index = {
3930
4172
  register(app) {
3931
4173
  const cm = new ContentManagerPlugin();
@@ -3945,7 +4187,7 @@ const index = {
3945
4187
  app.router.addRoute({
3946
4188
  path: "content-manager/*",
3947
4189
  lazy: async () => {
3948
- const { Layout } = await import("./layout-DX_52HSH.mjs");
4190
+ const { Layout } = await import("./layout-C71zeI19.mjs");
3949
4191
  return {
3950
4192
  Component: Layout
3951
4193
  };
@@ -3958,11 +4200,14 @@ const index = {
3958
4200
  if (typeof historyAdmin.bootstrap === "function") {
3959
4201
  historyAdmin.bootstrap(app);
3960
4202
  }
4203
+ if (typeof previewAdmin.bootstrap === "function") {
4204
+ previewAdmin.bootstrap(app);
4205
+ }
3961
4206
  },
3962
4207
  async registerTrads({ locales }) {
3963
4208
  const importedTrads = await Promise.all(
3964
4209
  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 }) => {
4210
+ 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
4211
  return {
3967
4212
  data: prefixPluginTranslations(data, PLUGIN_ID),
3968
4213
  locale
@@ -3989,8 +4234,10 @@ export {
3989
4234
  HOOKS as H,
3990
4235
  InjectionZone as I,
3991
4236
  useDocument as J,
3992
- index as K,
3993
- useDocumentActions as L,
4237
+ useGetPreviewUrlQuery as K,
4238
+ index as L,
4239
+ useContentManagerContext as M,
4240
+ useDocumentActions as N,
3994
4241
  Panels as P,
3995
4242
  RelativeTime as R,
3996
4243
  SINGLE_TYPES as S,
@@ -4022,4 +4269,4 @@ export {
4022
4269
  capitalise as y,
4023
4270
  useUpdateContentTypeConfigurationMutation as z
4024
4271
  };
4025
- //# sourceMappingURL=index-BHfS6_D5.mjs.map
4272
+ //# sourceMappingURL=index-B3tHjkLZ.mjs.map