@strapi/content-manager 0.0.0-experimental.a9a5a36dd73072c19eadeff5f387e8286b2a4d22 → 0.0.0-experimental.aa9d061a97e250ce2c7dc88ef3e41668bbf64351

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 (190) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-Bqgx7Mes.js → ComponentConfigurationPage-CpJNPBgk.js} +4 -4
  2. package/dist/_chunks/{ComponentConfigurationPage-Bqgx7Mes.js.map → ComponentConfigurationPage-CpJNPBgk.js.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-B1bIXVuX.mjs → ComponentConfigurationPage-TYDPg5WG.mjs} +4 -4
  4. package/dist/_chunks/{ComponentConfigurationPage-B1bIXVuX.mjs.map → ComponentConfigurationPage-TYDPg5WG.mjs.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-BFEwvdMW.js → EditConfigurationPage-CFDe6SA1.js} +4 -4
  6. package/dist/_chunks/{EditConfigurationPage-BFEwvdMW.js.map → EditConfigurationPage-CFDe6SA1.js.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-ZO0vOO8q.mjs → EditConfigurationPage-DqL8Pq5r.mjs} +4 -4
  8. package/dist/_chunks/{EditConfigurationPage-ZO0vOO8q.mjs.map → EditConfigurationPage-DqL8Pq5r.mjs.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-DlLEyUL6.mjs → EditViewPage-RXrFLav2.mjs} +30 -9
  10. package/dist/_chunks/EditViewPage-RXrFLav2.mjs.map +1 -0
  11. package/dist/_chunks/{EditViewPage-DA95Ha6J.js → EditViewPage-khfP2CR3.js} +30 -9
  12. package/dist/_chunks/EditViewPage-khfP2CR3.js.map +1 -0
  13. package/dist/_chunks/{Field-Dq7bDnuh.mjs → Field--rQeS6Zj.mjs} +179 -107
  14. package/dist/_chunks/Field--rQeS6Zj.mjs.map +1 -0
  15. package/dist/_chunks/{Field-CnK8dO8N.js → Field-C1ftmTe9.js} +181 -109
  16. package/dist/_chunks/Field-C1ftmTe9.js.map +1 -0
  17. package/dist/_chunks/{Form-B_JE0dbz.mjs → Form-COtGXyUE.mjs} +36 -17
  18. package/dist/_chunks/Form-COtGXyUE.mjs.map +1 -0
  19. package/dist/_chunks/{Form-BpiR4piS.js → Form-CwdX5oLw.js} +36 -17
  20. package/dist/_chunks/Form-CwdX5oLw.js.map +1 -0
  21. package/dist/_chunks/{History-CBNGU7a-.mjs → History-BevwkPO1.mjs} +55 -48
  22. package/dist/_chunks/History-BevwkPO1.mjs.map +1 -0
  23. package/dist/_chunks/{History-DdIstl8b.js → History-DKS2aqqM.js} +54 -47
  24. package/dist/_chunks/History-DKS2aqqM.js.map +1 -0
  25. package/dist/_chunks/{ListConfigurationPage-5dr4qpue.mjs → ListConfigurationPage-DNfZDtDA.mjs} +15 -5
  26. package/dist/_chunks/ListConfigurationPage-DNfZDtDA.mjs.map +1 -0
  27. package/dist/_chunks/{ListConfigurationPage-DkKRparB.js → ListConfigurationPage-LSYSPZHH.js} +15 -5
  28. package/dist/_chunks/ListConfigurationPage-LSYSPZHH.js.map +1 -0
  29. package/dist/_chunks/{ListViewPage-DecLrYV6.mjs → ListViewPage-C1PyuYRS.mjs} +59 -39
  30. package/dist/_chunks/ListViewPage-C1PyuYRS.mjs.map +1 -0
  31. package/dist/_chunks/{ListViewPage-wE0lXqoD.js → ListViewPage-DlUpqLIo.js} +61 -41
  32. package/dist/_chunks/ListViewPage-DlUpqLIo.js.map +1 -0
  33. package/dist/_chunks/{NoContentTypePage-CiIcfYsd.mjs → NoContentTypePage-C9q744z1.mjs} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-CiIcfYsd.mjs.map → NoContentTypePage-C9q744z1.mjs.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-DEKR6tf9.js → NoContentTypePage-m8wt3sf6.js} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-DEKR6tf9.js.map → NoContentTypePage-m8wt3sf6.js.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-CM5UD8ee.mjs → NoPermissionsPage-8BM-LWta.mjs} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-CM5UD8ee.mjs.map → NoPermissionsPage-8BM-LWta.mjs.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-DmNfF2Bb.js → NoPermissionsPage-DLfPsA0Q.js} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-DmNfF2Bb.js.map → NoPermissionsPage-DLfPsA0Q.js.map} +1 -1
  41. package/dist/_chunks/{Relations-Dqz0C1fz.mjs → Relations-D25xRcFy.mjs} +73 -37
  42. package/dist/_chunks/Relations-D25xRcFy.mjs.map +1 -0
  43. package/dist/_chunks/{Relations-L0xYRoSQ.js → Relations-OMriCP_L.js} +72 -36
  44. package/dist/_chunks/Relations-OMriCP_L.js.map +1 -0
  45. package/dist/_chunks/{en-uOUIxfcQ.js → en-Bdpa50w3.js} +18 -13
  46. package/dist/_chunks/{en-uOUIxfcQ.js.map → en-Bdpa50w3.js.map} +1 -1
  47. package/dist/_chunks/{en-BrCTWlZv.mjs → en-CZw4xdPY.mjs} +18 -13
  48. package/dist/_chunks/{en-BrCTWlZv.mjs.map → en-CZw4xdPY.mjs.map} +1 -1
  49. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  50. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  51. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  52. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  53. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  54. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  55. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  56. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  57. package/dist/_chunks/{index-BSn97i8U.mjs → index-BvGihCJp.mjs} +947 -660
  58. package/dist/_chunks/index-BvGihCJp.mjs.map +1 -0
  59. package/dist/_chunks/{index-DyvUPg1a.js → index-DqZnjo8F.js} +928 -640
  60. package/dist/_chunks/index-DqZnjo8F.js.map +1 -0
  61. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  62. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  63. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  64. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  65. package/dist/_chunks/{layout-TPqF2oJ5.js → layout-CmaemAO3.js} +21 -8
  66. package/dist/_chunks/layout-CmaemAO3.js.map +1 -0
  67. package/dist/_chunks/{layout-DPaHUusj.mjs → layout-ykHSe2KQ.mjs} +22 -9
  68. package/dist/_chunks/layout-ykHSe2KQ.mjs.map +1 -0
  69. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  70. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  71. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  72. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  73. package/dist/_chunks/{relations-BWYS9gkn.js → relations-D9fKsCLY.js} +3 -7
  74. package/dist/_chunks/relations-D9fKsCLY.js.map +1 -0
  75. package/dist/_chunks/{relations-Ck7-ecDT.mjs → relations-u-Vz51Ea.mjs} +3 -7
  76. package/dist/_chunks/relations-u-Vz51Ea.mjs.map +1 -0
  77. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  78. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  79. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  80. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  81. package/dist/admin/index.js +2 -1
  82. package/dist/admin/index.js.map +1 -1
  83. package/dist/admin/index.mjs +5 -4
  84. package/dist/admin/src/exports.d.ts +1 -1
  85. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  86. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  87. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  88. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  89. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  90. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  91. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  92. package/dist/admin/src/preview/constants.d.ts +1 -0
  93. package/dist/admin/src/preview/index.d.ts +4 -0
  94. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  95. package/dist/admin/src/services/api.d.ts +1 -1
  96. package/dist/admin/src/services/components.d.ts +2 -2
  97. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  98. package/dist/admin/src/services/documents.d.ts +19 -17
  99. package/dist/admin/src/services/init.d.ts +1 -1
  100. package/dist/admin/src/services/relations.d.ts +2 -2
  101. package/dist/admin/src/services/uid.d.ts +3 -3
  102. package/dist/admin/src/utils/validation.d.ts +4 -1
  103. package/dist/server/index.js +544 -261
  104. package/dist/server/index.js.map +1 -1
  105. package/dist/server/index.mjs +545 -262
  106. package/dist/server/index.mjs.map +1 -1
  107. package/dist/server/src/bootstrap.d.ts.map +1 -1
  108. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  109. package/dist/server/src/controllers/index.d.ts.map +1 -1
  110. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  111. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  112. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  113. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  114. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  115. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  116. package/dist/server/src/history/services/history.d.ts.map +1 -1
  117. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  118. package/dist/server/src/history/services/utils.d.ts +4 -4
  119. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  120. package/dist/server/src/index.d.ts +4 -4
  121. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  122. package/dist/server/src/preview/constants.d.ts +2 -0
  123. package/dist/server/src/preview/constants.d.ts.map +1 -0
  124. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  125. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  126. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  127. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  128. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  129. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  130. package/dist/server/src/preview/index.d.ts +4 -0
  131. package/dist/server/src/preview/index.d.ts.map +1 -0
  132. package/dist/server/src/preview/routes/index.d.ts +8 -0
  133. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  134. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  135. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  136. package/dist/server/src/preview/services/index.d.ts +15 -0
  137. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  138. package/dist/server/src/preview/services/preview-config.d.ts +30 -0
  139. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  140. package/dist/server/src/preview/services/preview.d.ts +12 -0
  141. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  142. package/dist/server/src/preview/utils.d.ts +18 -0
  143. package/dist/server/src/preview/utils.d.ts.map +1 -0
  144. package/dist/server/src/routes/index.d.ts.map +1 -1
  145. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  146. package/dist/server/src/services/document-metadata.d.ts +8 -8
  147. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  148. package/dist/server/src/services/index.d.ts +4 -4
  149. package/dist/server/src/services/index.d.ts.map +1 -1
  150. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  151. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  152. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  153. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  154. package/dist/server/src/utils/index.d.ts +2 -0
  155. package/dist/server/src/utils/index.d.ts.map +1 -1
  156. package/dist/shared/contracts/collection-types.d.ts +3 -1
  157. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  158. package/dist/shared/contracts/index.d.ts +1 -0
  159. package/dist/shared/contracts/index.d.ts.map +1 -1
  160. package/dist/shared/contracts/preview.d.ts +27 -0
  161. package/dist/shared/contracts/preview.d.ts.map +1 -0
  162. package/dist/shared/index.js +4 -0
  163. package/dist/shared/index.js.map +1 -1
  164. package/dist/shared/index.mjs +4 -0
  165. package/dist/shared/index.mjs.map +1 -1
  166. package/package.json +13 -13
  167. package/dist/_chunks/EditViewPage-DA95Ha6J.js.map +0 -1
  168. package/dist/_chunks/EditViewPage-DlLEyUL6.mjs.map +0 -1
  169. package/dist/_chunks/Field-CnK8dO8N.js.map +0 -1
  170. package/dist/_chunks/Field-Dq7bDnuh.mjs.map +0 -1
  171. package/dist/_chunks/Form-B_JE0dbz.mjs.map +0 -1
  172. package/dist/_chunks/Form-BpiR4piS.js.map +0 -1
  173. package/dist/_chunks/History-CBNGU7a-.mjs.map +0 -1
  174. package/dist/_chunks/History-DdIstl8b.js.map +0 -1
  175. package/dist/_chunks/ListConfigurationPage-5dr4qpue.mjs.map +0 -1
  176. package/dist/_chunks/ListConfigurationPage-DkKRparB.js.map +0 -1
  177. package/dist/_chunks/ListViewPage-DecLrYV6.mjs.map +0 -1
  178. package/dist/_chunks/ListViewPage-wE0lXqoD.js.map +0 -1
  179. package/dist/_chunks/Relations-Dqz0C1fz.mjs.map +0 -1
  180. package/dist/_chunks/Relations-L0xYRoSQ.js.map +0 -1
  181. package/dist/_chunks/index-BSn97i8U.mjs.map +0 -1
  182. package/dist/_chunks/index-DyvUPg1a.js.map +0 -1
  183. package/dist/_chunks/layout-DPaHUusj.mjs.map +0 -1
  184. package/dist/_chunks/layout-TPqF2oJ5.js.map +0 -1
  185. package/dist/_chunks/relations-BWYS9gkn.js.map +0 -1
  186. package/dist/_chunks/relations-Ck7-ecDT.mjs.map +0 -1
  187. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  188. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  189. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  190. package/strapi-server.js +0 -3
@@ -1,16 +1,17 @@
1
- import { CrossCircle, More, WarningCircle, ListPlus, Pencil, Trash, Check, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
1
+ import { More, Cross, WarningCircle, ListPlus, Pencil, Trash, Check, CrossCircle, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Link as Link$1, 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, useClipboard } from "@strapi/admin/strapi-admin";
4
4
  import * as React from "react";
5
5
  import { lazy } from "react";
6
- import { Button, Menu, VisuallyHidden, Flex, Box, Typography, Dialog, Modal, Radio, Status, SingleSelect, SingleSelectOption, Loader, IconButton, Tooltip, LinkButton } from "@strapi/design-system";
6
+ import { Button, Menu, VisuallyHidden, Flex, Typography, Dialog, Modal, 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
- import { useParams, Navigate, useNavigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
9
- import { styled } from "styled-components";
9
+ import { useParams, useNavigate, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
10
10
  import * as yup from "yup";
11
11
  import { ValidationError } from "yup";
12
12
  import pipe from "lodash/fp/pipe";
13
13
  import { intervalToDuration, isPast } from "date-fns";
14
+ import { styled } from "styled-components";
14
15
  import { stringify } from "qs";
15
16
  import { createSlice, combineReducers } from "@reduxjs/toolkit";
16
17
  const __variableDynamicImportRuntimeHelper = (glob, path) => {
@@ -100,6 +101,7 @@ const DocumentRBAC = ({ children, permissions }) => {
100
101
  if (!slug) {
101
102
  throw new Error("Cannot find the slug param in the URL");
102
103
  }
104
+ const [{ rawQuery }] = useQueryParams();
103
105
  const userPermissions = useAuth("DocumentRBAC", (state) => state.permissions);
104
106
  const contentTypePermissions = React.useMemo(() => {
105
107
  const contentTypePermissions2 = userPermissions.filter(
@@ -110,7 +112,14 @@ const DocumentRBAC = ({ children, permissions }) => {
110
112
  return { ...acc, [action]: [permission] };
111
113
  }, {});
112
114
  }, [slug, userPermissions]);
113
- const { isLoading, allowedActions } = useRBAC(contentTypePermissions, permissions ?? void 0);
115
+ const { isLoading, allowedActions } = useRBAC(
116
+ contentTypePermissions,
117
+ permissions ?? void 0,
118
+ // TODO: useRBAC context should be typed and built differently
119
+ // We are passing raw query as context to the hook so that it can
120
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
121
+ rawQuery
122
+ );
114
123
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
115
124
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
116
125
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -158,7 +167,8 @@ const contentManagerApi = adminApi.enhanceEndpoints({
158
167
  "Document",
159
168
  "InitialData",
160
169
  "HistoryVersion",
161
- "Relations"
170
+ "Relations",
171
+ "UidAvailability"
162
172
  ]
163
173
  });
164
174
  const documentApi = contentManagerApi.injectEndpoints({
@@ -172,7 +182,12 @@ const documentApi = contentManagerApi.injectEndpoints({
172
182
  params: query
173
183
  }
174
184
  }),
175
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
185
+ invalidatesTags: (_result, error, { model }) => {
186
+ if (error) {
187
+ return [];
188
+ }
189
+ return [{ type: "Document", id: `${model}_LIST` }];
190
+ }
176
191
  }),
177
192
  cloneDocument: builder.mutation({
178
193
  query: ({ model, sourceId, data, params }) => ({
@@ -183,7 +198,10 @@ const documentApi = contentManagerApi.injectEndpoints({
183
198
  params
184
199
  }
185
200
  }),
186
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
201
+ invalidatesTags: (_result, _error, { model }) => [
202
+ { type: "Document", id: `${model}_LIST` },
203
+ { type: "UidAvailability", id: model }
204
+ ]
187
205
  }),
188
206
  /**
189
207
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -200,7 +218,8 @@ const documentApi = contentManagerApi.injectEndpoints({
200
218
  }),
201
219
  invalidatesTags: (result, _error, { model }) => [
202
220
  { type: "Document", id: `${model}_LIST` },
203
- "Relations"
221
+ "Relations",
222
+ { type: "UidAvailability", id: model }
204
223
  ]
205
224
  }),
206
225
  deleteDocument: builder.mutation({
@@ -241,7 +260,8 @@ const documentApi = contentManagerApi.injectEndpoints({
241
260
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
242
261
  },
243
262
  { type: "Document", id: `${model}_LIST` },
244
- "Relations"
263
+ "Relations",
264
+ { type: "UidAvailability", id: model }
245
265
  ];
246
266
  }
247
267
  }),
@@ -259,6 +279,7 @@ const documentApi = contentManagerApi.injectEndpoints({
259
279
  }),
260
280
  providesTags: (result, _error, arg) => {
261
281
  return [
282
+ { type: "Document", id: `ALL_LIST` },
262
283
  { type: "Document", id: `${arg.model}_LIST` },
263
284
  ...result?.results.map(({ documentId }) => ({
264
285
  type: "Document",
@@ -297,6 +318,11 @@ const documentApi = contentManagerApi.injectEndpoints({
297
318
  {
298
319
  type: "Document",
299
320
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
321
+ },
322
+ // Make it easy to invalidate all individual documents queries for a model
323
+ {
324
+ type: "Document",
325
+ id: `${model}_ALL_ITEMS`
300
326
  }
301
327
  ];
302
328
  }
@@ -360,8 +386,21 @@ const documentApi = contentManagerApi.injectEndpoints({
360
386
  type: "Document",
361
387
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
362
388
  },
363
- "Relations"
389
+ "Relations",
390
+ { type: "UidAvailability", id: model }
364
391
  ];
392
+ },
393
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
394
+ const patchResult = dispatch(
395
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
396
+ Object.assign(draft.data, data);
397
+ })
398
+ );
399
+ try {
400
+ await queryFulfilled;
401
+ } catch {
402
+ patchResult.undo();
403
+ }
365
404
  }
366
405
  }),
367
406
  unpublishDocument: builder.mutation({
@@ -431,20 +470,39 @@ const buildValidParams = (query) => {
431
470
  const isBaseQueryError = (error) => {
432
471
  return error.name !== void 0;
433
472
  };
434
- const createYupSchema = (attributes = {}, components = {}) => {
473
+ const arrayValidator = (attribute, options) => ({
474
+ message: translatedErrors.required,
475
+ test(value) {
476
+ if (options.status === "draft") {
477
+ return true;
478
+ }
479
+ if (!attribute.required) {
480
+ return true;
481
+ }
482
+ if (!value) {
483
+ return false;
484
+ }
485
+ if (Array.isArray(value) && value.length === 0) {
486
+ return false;
487
+ }
488
+ return true;
489
+ }
490
+ });
491
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
435
492
  const createModelSchema = (attributes2) => yup.object().shape(
436
493
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
437
494
  if (DOCUMENT_META_FIELDS.includes(name)) {
438
495
  return acc;
439
496
  }
440
497
  const validations = [
498
+ addNullableValidation,
441
499
  addRequiredValidation,
442
500
  addMinLengthValidation,
443
501
  addMaxLengthValidation,
444
502
  addMinValidation,
445
503
  addMaxValidation,
446
504
  addRegexValidation
447
- ].map((fn) => fn(attribute));
505
+ ].map((fn) => fn(attribute, options));
448
506
  const transformSchema = pipe(...validations);
449
507
  switch (attribute.type) {
450
508
  case "component": {
@@ -454,12 +512,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
454
512
  ...acc,
455
513
  [name]: transformSchema(
456
514
  yup.array().of(createModelSchema(attributes3).nullable(false))
457
- )
515
+ ).test(arrayValidator(attribute, options))
458
516
  };
459
517
  } else {
460
518
  return {
461
519
  ...acc,
462
- [name]: transformSchema(createModelSchema(attributes3))
520
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
463
521
  };
464
522
  }
465
523
  }
@@ -481,7 +539,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
481
539
  }
482
540
  )
483
541
  )
484
- )
542
+ ).test(arrayValidator(attribute, options))
485
543
  };
486
544
  case "relation":
487
545
  return {
@@ -493,7 +551,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
493
551
  } else if (Array.isArray(value)) {
494
552
  return yup.array().of(
495
553
  yup.object().shape({
496
- id: yup.string().required()
554
+ id: yup.number().required()
497
555
  })
498
556
  );
499
557
  } else if (typeof value === "object") {
@@ -545,6 +603,14 @@ const createAttributeSchema = (attribute) => {
545
603
  if (!value || typeof value === "string" && value.length === 0) {
546
604
  return true;
547
605
  }
606
+ if (typeof value === "object") {
607
+ try {
608
+ JSON.stringify(value);
609
+ return true;
610
+ } catch (err) {
611
+ return false;
612
+ }
613
+ }
548
614
  try {
549
615
  JSON.parse(value);
550
616
  return true;
@@ -563,13 +629,7 @@ const createAttributeSchema = (attribute) => {
563
629
  return yup.mixed();
564
630
  }
565
631
  };
566
- const addRequiredValidation = (attribute) => (schema) => {
567
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
568
- return schema.min(1, translatedErrors.required);
569
- }
570
- if (attribute.required && attribute.type !== "relation") {
571
- return schema.required(translatedErrors.required);
572
- }
632
+ const nullableSchema = (schema) => {
573
633
  return schema?.nullable ? schema.nullable() : (
574
634
  // In some cases '.nullable' will not be available on the schema.
575
635
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -577,7 +637,22 @@ const addRequiredValidation = (attribute) => (schema) => {
577
637
  schema
578
638
  );
579
639
  };
580
- const addMinLengthValidation = (attribute) => (schema) => {
640
+ const addNullableValidation = () => (schema) => {
641
+ return nullableSchema(schema);
642
+ };
643
+ const addRequiredValidation = (attribute, options) => (schema) => {
644
+ if (options.status === "draft" || !attribute.required) {
645
+ return schema;
646
+ }
647
+ if (attribute.required && "required" in schema) {
648
+ return schema.required(translatedErrors.required);
649
+ }
650
+ return schema;
651
+ };
652
+ const addMinLengthValidation = (attribute, options) => (schema) => {
653
+ if (options.status === "draft") {
654
+ return schema;
655
+ }
581
656
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
582
657
  return schema.min(attribute.minLength, {
583
658
  ...translatedErrors.minLength,
@@ -599,32 +674,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
599
674
  }
600
675
  return schema;
601
676
  };
602
- const addMinValidation = (attribute) => (schema) => {
603
- if ("min" in attribute) {
677
+ const addMinValidation = (attribute, options) => (schema) => {
678
+ if (options.status === "draft") {
679
+ return schema;
680
+ }
681
+ if ("min" in attribute && "min" in schema) {
604
682
  const min = toInteger(attribute.min);
605
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
606
- if (!attribute.required && "test" in schema && min) {
607
- return schema.test(
608
- "custom-min",
609
- {
610
- ...translatedErrors.min,
611
- values: {
612
- min: attribute.min
613
- }
614
- },
615
- (value) => {
616
- if (!value) {
617
- return true;
618
- }
619
- if (Array.isArray(value) && value.length === 0) {
620
- return true;
621
- }
622
- return value.length >= min;
623
- }
624
- );
625
- }
626
- }
627
- if ("min" in schema && min) {
683
+ if (min) {
628
684
  return schema.min(min, {
629
685
  ...translatedErrors.min,
630
686
  values: {
@@ -742,19 +798,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
742
798
  }, {});
743
799
  return componentsByKey;
744
800
  };
745
- const useDocument = (args, opts) => {
801
+ const HOOKS = {
802
+ /**
803
+ * Hook that allows to mutate the displayed headers of the list view table
804
+ * @constant
805
+ * @type {string}
806
+ */
807
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
808
+ /**
809
+ * Hook that allows to mutate the CM's collection types links pre-set filters
810
+ * @constant
811
+ * @type {string}
812
+ */
813
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
814
+ /**
815
+ * Hook that allows to mutate the CM's edit view layout
816
+ * @constant
817
+ * @type {string}
818
+ */
819
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
820
+ /**
821
+ * Hook that allows to mutate the CM's single types links pre-set filters
822
+ * @constant
823
+ * @type {string}
824
+ */
825
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
826
+ };
827
+ const contentTypesApi = contentManagerApi.injectEndpoints({
828
+ endpoints: (builder) => ({
829
+ getContentTypeConfiguration: builder.query({
830
+ query: (uid) => ({
831
+ url: `/content-manager/content-types/${uid}/configuration`,
832
+ method: "GET"
833
+ }),
834
+ transformResponse: (response) => response.data,
835
+ providesTags: (_result, _error, uid) => [
836
+ { type: "ContentTypesConfiguration", id: uid },
837
+ { type: "ContentTypeSettings", id: "LIST" }
838
+ ]
839
+ }),
840
+ getAllContentTypeSettings: builder.query({
841
+ query: () => "/content-manager/content-types-settings",
842
+ transformResponse: (response) => response.data,
843
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
844
+ }),
845
+ updateContentTypeConfiguration: builder.mutation({
846
+ query: ({ uid, ...body }) => ({
847
+ url: `/content-manager/content-types/${uid}/configuration`,
848
+ method: "PUT",
849
+ data: body
850
+ }),
851
+ transformResponse: (response) => response.data,
852
+ invalidatesTags: (_result, _error, { uid }) => [
853
+ { type: "ContentTypesConfiguration", id: uid },
854
+ { type: "ContentTypeSettings", id: "LIST" },
855
+ // Is this necessary?
856
+ { type: "InitialData" }
857
+ ]
858
+ })
859
+ })
860
+ });
861
+ const {
862
+ useGetContentTypeConfigurationQuery,
863
+ useGetAllContentTypeSettingsQuery,
864
+ useUpdateContentTypeConfigurationMutation
865
+ } = contentTypesApi;
866
+ const checkIfAttributeIsDisplayable = (attribute) => {
867
+ const { type } = attribute;
868
+ if (type === "relation") {
869
+ return !attribute.relation.toLowerCase().includes("morph");
870
+ }
871
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
872
+ };
873
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
874
+ if (!mainFieldName) {
875
+ return void 0;
876
+ }
877
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
878
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
879
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
880
+ );
881
+ return {
882
+ name: mainFieldName,
883
+ type: mainFieldType ?? "string"
884
+ };
885
+ };
886
+ const DEFAULT_SETTINGS = {
887
+ bulkable: false,
888
+ filterable: false,
889
+ searchable: false,
890
+ pagination: false,
891
+ defaultSortBy: "",
892
+ defaultSortOrder: "asc",
893
+ mainField: "id",
894
+ pageSize: 10
895
+ };
896
+ const useDocumentLayout = (model) => {
897
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
898
+ const [{ query }] = useQueryParams();
899
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
746
900
  const { toggleNotification } = useNotification();
747
901
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
902
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
748
903
  const {
749
- currentData: data,
750
- isLoading: isLoadingDocument,
751
- isFetching: isFetchingDocument,
752
- error
753
- } = useGetDocumentQuery(args, {
754
- ...opts,
755
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
756
- });
757
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
904
+ data,
905
+ isLoading: isLoadingConfigs,
906
+ error,
907
+ isFetching: isFetchingConfigs
908
+ } = useGetContentTypeConfigurationQuery(model);
909
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
758
910
  React.useEffect(() => {
759
911
  if (error) {
760
912
  toggleNotification({
@@ -762,83 +914,339 @@ const useDocument = (args, opts) => {
762
914
  message: formatAPIError(error)
763
915
  });
764
916
  }
765
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
766
- const validationSchema = React.useMemo(() => {
767
- if (!schema) {
768
- return null;
769
- }
770
- return createYupSchema(schema.attributes, components);
771
- }, [schema, components]);
772
- const validate = React.useCallback(
773
- (document) => {
774
- if (!validationSchema) {
775
- throw new Error(
776
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
777
- );
778
- }
779
- try {
780
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
781
- return null;
782
- } catch (error2) {
783
- if (error2 instanceof ValidationError) {
784
- return getYupValidationErrors(error2);
785
- }
786
- throw error2;
787
- }
917
+ }, [error, formatAPIError, toggleNotification]);
918
+ const editLayout = React.useMemo(
919
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
920
+ layout: [],
921
+ components: {},
922
+ metadatas: {},
923
+ options: {},
924
+ settings: DEFAULT_SETTINGS
788
925
  },
789
- [validationSchema]
926
+ [data, isLoading, schemas, schema, components]
927
+ );
928
+ const listLayout = React.useMemo(() => {
929
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
930
+ layout: [],
931
+ metadatas: {},
932
+ options: {},
933
+ settings: DEFAULT_SETTINGS
934
+ };
935
+ }, [data, isLoading, schemas, schema, components]);
936
+ const { layout: edit } = React.useMemo(
937
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
938
+ layout: editLayout,
939
+ query
940
+ }),
941
+ [editLayout, query, runHookWaterfall]
790
942
  );
791
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
792
943
  return {
793
- components,
794
- document: data?.data,
795
- meta: data?.meta,
944
+ error,
796
945
  isLoading,
797
- schema,
798
- validate
799
- };
800
- };
801
- const useDoc = () => {
802
- const { id, slug, collectionType, origin } = useParams();
803
- const [{ query }] = useQueryParams();
804
- const params = React.useMemo(() => buildValidParams(query), [query]);
805
- if (!collectionType) {
806
- throw new Error("Could not find collectionType in url params");
807
- }
808
- if (!slug) {
809
- throw new Error("Could not find model in url params");
810
- }
811
- return {
812
- collectionType,
813
- model: slug,
814
- id: origin || id === "create" ? void 0 : id,
815
- ...useDocument(
816
- { documentId: origin || id, model: slug, collectionType, params },
817
- {
818
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
819
- }
820
- )
946
+ edit,
947
+ list: listLayout
821
948
  };
822
949
  };
823
- const prefixPluginTranslations = (trad, pluginId) => {
824
- if (!pluginId) {
825
- throw new TypeError("pluginId can't be empty");
826
- }
827
- return Object.keys(trad).reduce((acc, current) => {
828
- acc[`${pluginId}.${current}`] = trad[current];
829
- return acc;
830
- }, {});
831
- };
832
- const getTranslation = (id) => `content-manager.${id}`;
833
- const DEFAULT_UNEXPECTED_ERROR_MSG = {
834
- id: "notification.error",
835
- defaultMessage: "An error occurred, please try again"
950
+ const useDocLayout = () => {
951
+ const { model } = useDoc();
952
+ return useDocumentLayout(model);
836
953
  };
837
- const useDocumentActions = () => {
838
- const { toggleNotification } = useNotification();
954
+ const formatEditLayout = (data, {
955
+ schemas,
956
+ schema,
957
+ components
958
+ }) => {
959
+ let currentPanelIndex = 0;
960
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
961
+ data.contentType.layouts.edit,
962
+ schema?.attributes,
963
+ data.contentType.metadatas,
964
+ { configurations: data.components, schemas: components },
965
+ schemas
966
+ ).reduce((panels, row) => {
967
+ if (row.some((field) => field.type === "dynamiczone")) {
968
+ panels.push([row]);
969
+ currentPanelIndex += 2;
970
+ } else {
971
+ if (!panels[currentPanelIndex]) {
972
+ panels.push([row]);
973
+ } else {
974
+ panels[currentPanelIndex].push(row);
975
+ }
976
+ }
977
+ return panels;
978
+ }, []);
979
+ const componentEditAttributes = Object.entries(data.components).reduce(
980
+ (acc, [uid, configuration]) => {
981
+ acc[uid] = {
982
+ layout: convertEditLayoutToFieldLayouts(
983
+ configuration.layouts.edit,
984
+ components[uid].attributes,
985
+ configuration.metadatas,
986
+ { configurations: data.components, schemas: components }
987
+ ),
988
+ settings: {
989
+ ...configuration.settings,
990
+ icon: components[uid].info.icon,
991
+ displayName: components[uid].info.displayName
992
+ }
993
+ };
994
+ return acc;
995
+ },
996
+ {}
997
+ );
998
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
999
+ (acc, [attribute, metadata]) => {
1000
+ return {
1001
+ ...acc,
1002
+ [attribute]: metadata.edit
1003
+ };
1004
+ },
1005
+ {}
1006
+ );
1007
+ return {
1008
+ layout: panelledEditAttributes,
1009
+ components: componentEditAttributes,
1010
+ metadatas: editMetadatas,
1011
+ settings: {
1012
+ ...data.contentType.settings,
1013
+ displayName: schema?.info.displayName
1014
+ },
1015
+ options: {
1016
+ ...schema?.options,
1017
+ ...schema?.pluginOptions,
1018
+ ...data.contentType.options
1019
+ }
1020
+ };
1021
+ };
1022
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1023
+ return rows.map(
1024
+ (row) => row.map((field) => {
1025
+ const attribute = attributes[field.name];
1026
+ if (!attribute) {
1027
+ return null;
1028
+ }
1029
+ const { edit: metadata } = metadatas[field.name];
1030
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1031
+ return {
1032
+ attribute,
1033
+ disabled: !metadata.editable,
1034
+ hint: metadata.description,
1035
+ label: metadata.label ?? "",
1036
+ name: field.name,
1037
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1038
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1039
+ schemas,
1040
+ components: components?.schemas ?? {}
1041
+ }),
1042
+ placeholder: metadata.placeholder ?? "",
1043
+ required: attribute.required ?? false,
1044
+ size: field.size,
1045
+ unique: "unique" in attribute ? attribute.unique : false,
1046
+ visible: metadata.visible ?? true,
1047
+ type: attribute.type
1048
+ };
1049
+ }).filter((field) => field !== null)
1050
+ );
1051
+ };
1052
+ const formatListLayout = (data, {
1053
+ schemas,
1054
+ schema,
1055
+ components
1056
+ }) => {
1057
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1058
+ (acc, [attribute, metadata]) => {
1059
+ return {
1060
+ ...acc,
1061
+ [attribute]: metadata.list
1062
+ };
1063
+ },
1064
+ {}
1065
+ );
1066
+ const listAttributes = convertListLayoutToFieldLayouts(
1067
+ data.contentType.layouts.list,
1068
+ schema?.attributes,
1069
+ listMetadatas,
1070
+ { configurations: data.components, schemas: components },
1071
+ schemas
1072
+ );
1073
+ return {
1074
+ layout: listAttributes,
1075
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1076
+ metadatas: listMetadatas,
1077
+ options: {
1078
+ ...schema?.options,
1079
+ ...schema?.pluginOptions,
1080
+ ...data.contentType.options
1081
+ }
1082
+ };
1083
+ };
1084
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1085
+ return columns.map((name) => {
1086
+ const attribute = attributes[name];
1087
+ if (!attribute) {
1088
+ return null;
1089
+ }
1090
+ const metadata = metadatas[name];
1091
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1092
+ return {
1093
+ attribute,
1094
+ label: metadata.label ?? "",
1095
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1096
+ schemas,
1097
+ components: components?.schemas ?? {}
1098
+ }),
1099
+ name,
1100
+ searchable: metadata.searchable ?? true,
1101
+ sortable: metadata.sortable ?? true
1102
+ };
1103
+ }).filter((field) => field !== null);
1104
+ };
1105
+ const useDocument = (args, opts) => {
1106
+ const { toggleNotification } = useNotification();
1107
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1108
+ const {
1109
+ currentData: data,
1110
+ isLoading: isLoadingDocument,
1111
+ isFetching: isFetchingDocument,
1112
+ error
1113
+ } = useGetDocumentQuery(args, {
1114
+ ...opts,
1115
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1116
+ });
1117
+ const {
1118
+ components,
1119
+ schema,
1120
+ schemas,
1121
+ isLoading: isLoadingSchema
1122
+ } = useContentTypeSchema(args.model);
1123
+ React.useEffect(() => {
1124
+ if (error) {
1125
+ toggleNotification({
1126
+ type: "danger",
1127
+ message: formatAPIError(error)
1128
+ });
1129
+ }
1130
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1131
+ const validationSchema = React.useMemo(() => {
1132
+ if (!schema) {
1133
+ return null;
1134
+ }
1135
+ return createYupSchema(schema.attributes, components);
1136
+ }, [schema, components]);
1137
+ const validate = React.useCallback(
1138
+ (document) => {
1139
+ if (!validationSchema) {
1140
+ throw new Error(
1141
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1142
+ );
1143
+ }
1144
+ try {
1145
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1146
+ return null;
1147
+ } catch (error2) {
1148
+ if (error2 instanceof ValidationError) {
1149
+ return getYupValidationErrors(error2);
1150
+ }
1151
+ throw error2;
1152
+ }
1153
+ },
1154
+ [validationSchema]
1155
+ );
1156
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1157
+ const hasError = !!error;
1158
+ return {
1159
+ components,
1160
+ document: data?.data,
1161
+ meta: data?.meta,
1162
+ isLoading,
1163
+ hasError,
1164
+ schema,
1165
+ schemas,
1166
+ validate
1167
+ };
1168
+ };
1169
+ const useDoc = () => {
1170
+ const { id, slug, collectionType, origin } = useParams();
1171
+ const [{ query }] = useQueryParams();
1172
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1173
+ if (!collectionType) {
1174
+ throw new Error("Could not find collectionType in url params");
1175
+ }
1176
+ if (!slug) {
1177
+ throw new Error("Could not find model in url params");
1178
+ }
1179
+ const document = useDocument(
1180
+ { documentId: origin || id, model: slug, collectionType, params },
1181
+ {
1182
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1183
+ }
1184
+ );
1185
+ const returnId = origin || id === "create" ? void 0 : id;
1186
+ return {
1187
+ collectionType,
1188
+ model: slug,
1189
+ id: returnId,
1190
+ ...document
1191
+ };
1192
+ };
1193
+ const useContentManagerContext = () => {
1194
+ const {
1195
+ collectionType,
1196
+ model,
1197
+ id,
1198
+ components,
1199
+ isLoading: isLoadingDoc,
1200
+ schema,
1201
+ schemas
1202
+ } = useDoc();
1203
+ const layout = useDocumentLayout(model);
1204
+ const form = useForm("useContentManagerContext", (state) => state);
1205
+ const isSingleType = collectionType === SINGLE_TYPES;
1206
+ const slug = model;
1207
+ const isCreatingEntry = id === "create";
1208
+ useContentTypeSchema();
1209
+ const isLoading = isLoadingDoc || layout.isLoading;
1210
+ const error = layout.error;
1211
+ return {
1212
+ error,
1213
+ isLoading,
1214
+ // Base metadata
1215
+ model,
1216
+ collectionType,
1217
+ id,
1218
+ slug,
1219
+ isCreatingEntry,
1220
+ isSingleType,
1221
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1222
+ // All schema infos
1223
+ components,
1224
+ contentType: schema,
1225
+ contentTypes: schemas,
1226
+ // Form state
1227
+ form,
1228
+ // layout infos
1229
+ layout
1230
+ };
1231
+ };
1232
+ const prefixPluginTranslations = (trad, pluginId) => {
1233
+ return Object.keys(trad).reduce((acc, current) => {
1234
+ acc[`${pluginId}.${current}`] = trad[current];
1235
+ return acc;
1236
+ }, {});
1237
+ };
1238
+ const getTranslation = (id) => `content-manager.${id}`;
1239
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1240
+ id: "notification.error",
1241
+ defaultMessage: "An error occurred, please try again"
1242
+ };
1243
+ const useDocumentActions = () => {
1244
+ const { toggleNotification } = useNotification();
839
1245
  const { formatMessage } = useIntl();
840
1246
  const { trackUsage } = useTracking();
841
1247
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1248
+ const navigate = useNavigate();
1249
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
842
1250
  const [deleteDocument] = useDeleteDocumentMutation();
843
1251
  const _delete = React.useCallback(
844
1252
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1153,6 +1561,7 @@ const useDocumentActions = () => {
1153
1561
  defaultMessage: "Saved document"
1154
1562
  })
1155
1563
  });
1564
+ setCurrentStep("contentManager.success");
1156
1565
  return res.data;
1157
1566
  } catch (err) {
1158
1567
  toggleNotification({
@@ -1174,7 +1583,6 @@ const useDocumentActions = () => {
1174
1583
  sourceId
1175
1584
  });
1176
1585
  if ("error" in res) {
1177
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1178
1586
  return { error: res.error };
1179
1587
  }
1180
1588
  toggleNotification({
@@ -1193,7 +1601,7 @@ const useDocumentActions = () => {
1193
1601
  throw err;
1194
1602
  }
1195
1603
  },
1196
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1604
+ [autoCloneDocument, formatMessage, toggleNotification]
1197
1605
  );
1198
1606
  const [cloneDocument] = useCloneDocumentMutation();
1199
1607
  const clone = React.useCallback(
@@ -1219,6 +1627,7 @@ const useDocumentActions = () => {
1219
1627
  defaultMessage: "Cloned document"
1220
1628
  })
1221
1629
  });
1630
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1222
1631
  return res.data;
1223
1632
  } catch (err) {
1224
1633
  toggleNotification({
@@ -1229,7 +1638,7 @@ const useDocumentActions = () => {
1229
1638
  throw err;
1230
1639
  }
1231
1640
  },
1232
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1641
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1233
1642
  );
1234
1643
  const [getDoc] = useLazyGetDocumentQuery();
1235
1644
  const getDocument = React.useCallback(
@@ -1255,7 +1664,7 @@ const useDocumentActions = () => {
1255
1664
  };
1256
1665
  };
1257
1666
  const ProtectedHistoryPage = lazy(
1258
- () => import("./History-CBNGU7a-.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1667
+ () => import("./History-BevwkPO1.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1259
1668
  );
1260
1669
  const routes$1 = [
1261
1670
  {
@@ -1268,31 +1677,31 @@ const routes$1 = [
1268
1677
  }
1269
1678
  ];
1270
1679
  const ProtectedEditViewPage = lazy(
1271
- () => import("./EditViewPage-DlLEyUL6.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1680
+ () => import("./EditViewPage-RXrFLav2.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1272
1681
  );
1273
1682
  const ProtectedListViewPage = lazy(
1274
- () => import("./ListViewPage-DecLrYV6.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1683
+ () => import("./ListViewPage-C1PyuYRS.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1275
1684
  );
1276
1685
  const ProtectedListConfiguration = lazy(
1277
- () => import("./ListConfigurationPage-5dr4qpue.mjs").then((mod) => ({
1686
+ () => import("./ListConfigurationPage-DNfZDtDA.mjs").then((mod) => ({
1278
1687
  default: mod.ProtectedListConfiguration
1279
1688
  }))
1280
1689
  );
1281
1690
  const ProtectedEditConfigurationPage = lazy(
1282
- () => import("./EditConfigurationPage-ZO0vOO8q.mjs").then((mod) => ({
1691
+ () => import("./EditConfigurationPage-DqL8Pq5r.mjs").then((mod) => ({
1283
1692
  default: mod.ProtectedEditConfigurationPage
1284
1693
  }))
1285
1694
  );
1286
1695
  const ProtectedComponentConfigurationPage = lazy(
1287
- () => import("./ComponentConfigurationPage-B1bIXVuX.mjs").then((mod) => ({
1696
+ () => import("./ComponentConfigurationPage-TYDPg5WG.mjs").then((mod) => ({
1288
1697
  default: mod.ProtectedComponentConfigurationPage
1289
1698
  }))
1290
1699
  );
1291
1700
  const NoPermissions = lazy(
1292
- () => import("./NoPermissionsPage-CM5UD8ee.mjs").then((mod) => ({ default: mod.NoPermissions }))
1701
+ () => import("./NoPermissionsPage-8BM-LWta.mjs").then((mod) => ({ default: mod.NoPermissions }))
1293
1702
  );
1294
1703
  const NoContentType = lazy(
1295
- () => import("./NoContentTypePage-CiIcfYsd.mjs").then((mod) => ({ default: mod.NoContentType }))
1704
+ () => import("./NoContentTypePage-C9q744z1.mjs").then((mod) => ({ default: mod.NoContentType }))
1296
1705
  );
1297
1706
  const CollectionTypePages = () => {
1298
1707
  const { collectionType } = useParams();
@@ -1406,12 +1815,14 @@ const DocumentActionButton = (action) => {
1406
1815
  /* @__PURE__ */ jsx(
1407
1816
  Button,
1408
1817
  {
1409
- flex: 1,
1818
+ flex: "auto",
1410
1819
  startIcon: action.icon,
1411
1820
  disabled: action.disabled,
1412
1821
  onClick: handleClick(action),
1413
1822
  justifyContent: "center",
1414
1823
  variant: action.variant || "default",
1824
+ paddingTop: "7px",
1825
+ paddingBottom: "7px",
1415
1826
  children: action.label
1416
1827
  }
1417
1828
  ),
@@ -1476,9 +1887,9 @@ const DocumentActionsMenu = ({
1476
1887
  disabled: isDisabled,
1477
1888
  size: "S",
1478
1889
  endIcon: null,
1479
- paddingTop: "7px",
1480
- paddingLeft: "9px",
1481
- paddingRight: "9px",
1890
+ paddingTop: "4px",
1891
+ paddingLeft: "7px",
1892
+ paddingRight: "7px",
1482
1893
  variant,
1483
1894
  children: [
1484
1895
  /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
@@ -1489,7 +1900,7 @@ const DocumentActionsMenu = ({
1489
1900
  ]
1490
1901
  }
1491
1902
  ),
1492
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1903
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1493
1904
  actions2.map((action) => {
1494
1905
  return /* @__PURE__ */ jsx(
1495
1906
  Menu.Item,
@@ -1498,14 +1909,29 @@ const DocumentActionsMenu = ({
1498
1909
  onSelect: handleClick(action),
1499
1910
  display: "block",
1500
1911
  children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1501
- /* @__PURE__ */ jsxs(Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1502
- /* @__PURE__ */ jsx(Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1503
- action.label
1504
- ] }),
1505
- action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1912
+ /* @__PURE__ */ jsxs(
1506
1913
  Flex,
1507
1914
  {
1508
- alignItems: "center",
1915
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1916
+ gap: 2,
1917
+ tag: "span",
1918
+ children: [
1919
+ /* @__PURE__ */ jsx(
1920
+ Flex,
1921
+ {
1922
+ tag: "span",
1923
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1924
+ children: action.icon
1925
+ }
1926
+ ),
1927
+ action.label
1928
+ ]
1929
+ }
1930
+ ),
1931
+ action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1932
+ Flex,
1933
+ {
1934
+ alignItems: "center",
1509
1935
  background: "alternative100",
1510
1936
  borderStyle: "solid",
1511
1937
  borderColor: "alternative200",
@@ -1598,11 +2024,11 @@ const DocumentActionConfirmDialog = ({
1598
2024
  /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1599
2025
  /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1600
2026
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1601
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
2027
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1602
2028
  id: "app.components.Button.cancel",
1603
2029
  defaultMessage: "Cancel"
1604
2030
  }) }) }),
1605
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
2031
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1606
2032
  id: "app.components.Button.confirm",
1607
2033
  defaultMessage: "Confirm"
1608
2034
  }) })
@@ -1625,10 +2051,22 @@ const DocumentActionModal = ({
1625
2051
  };
1626
2052
  return /* @__PURE__ */ jsx(Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
1627
2053
  /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: title }) }),
1628
- /* @__PURE__ */ jsx(Modal.Body, { children: typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content }),
1629
- /* @__PURE__ */ jsx(Modal.Footer, { children: typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer })
2054
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsx(Modal.Body, { children: Content }),
2055
+ typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1630
2056
  ] }) });
1631
2057
  };
2058
+ const transformData = (data) => {
2059
+ if (Array.isArray(data)) {
2060
+ return data.map(transformData);
2061
+ }
2062
+ if (typeof data === "object" && data !== null) {
2063
+ if ("apiData" in data) {
2064
+ return data.apiData;
2065
+ }
2066
+ return mapValues(transformData)(data);
2067
+ }
2068
+ return data;
2069
+ };
1632
2070
  const PublishAction$1 = ({
1633
2071
  activeTab,
1634
2072
  documentId,
@@ -1641,12 +2079,10 @@ const PublishAction$1 = ({
1641
2079
  const navigate = useNavigate();
1642
2080
  const { toggleNotification } = useNotification();
1643
2081
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2082
+ const isListView = useMatch(LIST_PATH) !== null;
1644
2083
  const isCloning = useMatch(CLONE_PATH) !== null;
1645
2084
  const { formatMessage } = useIntl();
1646
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1647
- "PublishAction",
1648
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1649
- );
2085
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1650
2086
  const { publish } = useDocumentActions();
1651
2087
  const [
1652
2088
  countDraftRelations,
@@ -1698,24 +2134,25 @@ const PublishAction$1 = ({
1698
2134
  }
1699
2135
  }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1700
2136
  React.useEffect(() => {
1701
- if (documentId) {
1702
- const fetchDraftRelationsCount = async () => {
1703
- const { data, error } = await countDraftRelations({
1704
- collectionType,
1705
- model,
1706
- documentId,
1707
- params
1708
- });
1709
- if (error) {
1710
- throw error;
1711
- }
1712
- if (data) {
1713
- setServerCountOfDraftRelations(data.data);
1714
- }
1715
- };
1716
- fetchDraftRelationsCount();
2137
+ if (!document || !document.documentId || isListView) {
2138
+ return;
1717
2139
  }
1718
- }, [documentId, countDraftRelations, collectionType, model, params]);
2140
+ const fetchDraftRelationsCount = async () => {
2141
+ const { data, error } = await countDraftRelations({
2142
+ collectionType,
2143
+ model,
2144
+ documentId,
2145
+ params
2146
+ });
2147
+ if (error) {
2148
+ throw error;
2149
+ }
2150
+ if (data) {
2151
+ setServerCountOfDraftRelations(data.data);
2152
+ }
2153
+ };
2154
+ fetchDraftRelationsCount();
2155
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1719
2156
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1720
2157
  if (!schema?.options?.draftAndPublish) {
1721
2158
  return null;
@@ -1723,7 +2160,9 @@ const PublishAction$1 = ({
1723
2160
  const performPublish = async () => {
1724
2161
  setSubmitting(true);
1725
2162
  try {
1726
- const { errors } = await validate();
2163
+ const { errors } = await validate(true, {
2164
+ status: "published"
2165
+ });
1727
2166
  if (errors) {
1728
2167
  toggleNotification({
1729
2168
  type: "danger",
@@ -1741,7 +2180,7 @@ const PublishAction$1 = ({
1741
2180
  documentId,
1742
2181
  params
1743
2182
  },
1744
- formValues
2183
+ transformData(formValues)
1745
2184
  );
1746
2185
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1747
2186
  navigate({
@@ -1756,7 +2195,8 @@ const PublishAction$1 = ({
1756
2195
  }
1757
2196
  };
1758
2197
  const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1759
- const hasDraftRelations = totalDraftRelations > 0;
2198
+ const enableDraftRelationsCount = false;
2199
+ const hasDraftRelations = enableDraftRelationsCount;
1760
2200
  return {
1761
2201
  /**
1762
2202
  * Disabled when:
@@ -1766,18 +2206,13 @@ const PublishAction$1 = ({
1766
2206
  * - the document is already published & not modified
1767
2207
  * - the document is being created & not modified
1768
2208
  * - the user doesn't have the permission to publish
1769
- * - the user doesn't have the permission to create a new document
1770
- * - the user doesn't have the permission to update the document
1771
2209
  */
1772
- disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2210
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1773
2211
  label: formatMessage({
1774
2212
  id: "app.utils.publish",
1775
2213
  defaultMessage: "Publish"
1776
2214
  }),
1777
2215
  onClick: async () => {
1778
- if (hasDraftRelations) {
1779
- return;
1780
- }
1781
2216
  await performPublish();
1782
2217
  },
1783
2218
  dialog: hasDraftRelations ? {
@@ -1816,10 +2251,6 @@ const UpdateAction = ({
1816
2251
  const cloneMatch = useMatch(CLONE_PATH);
1817
2252
  const isCloning = cloneMatch !== null;
1818
2253
  const { formatMessage } = useIntl();
1819
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1820
- canCreate: canCreate2,
1821
- canUpdate: canUpdate2
1822
- }));
1823
2254
  const { create, update, clone } = useDocumentActions();
1824
2255
  const [{ query, rawQuery }] = useQueryParams();
1825
2256
  const params = React.useMemo(() => buildValidParams(query), [query]);
@@ -1836,10 +2267,8 @@ const UpdateAction = ({
1836
2267
  * - the form is submitting
1837
2268
  * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1838
2269
  * - the active tab is the published tab
1839
- * - the user doesn't have the permission to create a new document
1840
- * - the user doesn't have the permission to update the document
1841
2270
  */
1842
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
2271
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1843
2272
  label: formatMessage({
1844
2273
  id: "content-manager.containers.Edit.save",
1845
2274
  defaultMessage: "Save"
@@ -1847,7 +2276,9 @@ const UpdateAction = ({
1847
2276
  onClick: async () => {
1848
2277
  setSubmitting(true);
1849
2278
  try {
1850
- const { errors } = await validate();
2279
+ const { errors } = await validate(true, {
2280
+ status: "draft"
2281
+ });
1851
2282
  if (errors) {
1852
2283
  toggleNotification({
1853
2284
  type: "danger",
@@ -1865,13 +2296,16 @@ const UpdateAction = ({
1865
2296
  documentId: cloneMatch.params.origin,
1866
2297
  params
1867
2298
  },
1868
- document
2299
+ transformData(document)
1869
2300
  );
1870
2301
  if ("data" in res) {
1871
- navigate({
1872
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1873
- search: rawQuery
1874
- });
2302
+ navigate(
2303
+ {
2304
+ pathname: `../${res.data.documentId}`,
2305
+ search: rawQuery
2306
+ },
2307
+ { relative: "path" }
2308
+ );
1875
2309
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1876
2310
  setErrors(formatValidationErrors(res.error));
1877
2311
  }
@@ -1883,7 +2317,7 @@ const UpdateAction = ({
1883
2317
  documentId,
1884
2318
  params
1885
2319
  },
1886
- document
2320
+ transformData(document)
1887
2321
  );
1888
2322
  if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1889
2323
  setErrors(formatValidationErrors(res.error));
@@ -1896,15 +2330,15 @@ const UpdateAction = ({
1896
2330
  model,
1897
2331
  params
1898
2332
  },
1899
- document
2333
+ transformData(document)
1900
2334
  );
1901
2335
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1902
2336
  navigate(
1903
2337
  {
1904
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2338
+ pathname: `../${res.data.documentId}`,
1905
2339
  search: rawQuery
1906
2340
  },
1907
- { replace: true }
2341
+ { replace: true, relative: "path" }
1908
2342
  );
1909
2343
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1910
2344
  setErrors(formatValidationErrors(res.error));
@@ -1949,7 +2383,7 @@ const UnpublishAction$1 = ({
1949
2383
  id: "app.utils.unpublish",
1950
2384
  defaultMessage: "Unpublish"
1951
2385
  }),
1952
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2386
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1953
2387
  onClick: async () => {
1954
2388
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1955
2389
  if (!documentId) {
@@ -2061,7 +2495,7 @@ const DiscardAction = ({
2061
2495
  id: "content-manager.actions.discard.label",
2062
2496
  defaultMessage: "Discard changes"
2063
2497
  }),
2064
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2498
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2065
2499
  position: ["panel", "table-row"],
2066
2500
  variant: "danger",
2067
2501
  dialog: {
@@ -2089,11 +2523,6 @@ const DiscardAction = ({
2089
2523
  };
2090
2524
  };
2091
2525
  DiscardAction.type = "discard";
2092
- const StyledCrossCircle = styled(CrossCircle)`
2093
- path {
2094
- fill: currentColor;
2095
- }
2096
- `;
2097
2526
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2098
2527
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2099
2528
  const RelativeTime = React.forwardRef(
@@ -2141,7 +2570,7 @@ const getDisplayName = ({
2141
2570
  };
2142
2571
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2143
2572
  const DocumentStatus = ({ status = "draft", ...restProps }) => {
2144
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2573
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2145
2574
  return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2146
2575
  };
2147
2576
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
@@ -2240,12 +2669,12 @@ const Information = ({ activeTab }) => {
2240
2669
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2241
2670
  label: formatMessage({
2242
2671
  id: "content-manager.containers.edit.information.last-published.label",
2243
- defaultMessage: "Last published"
2672
+ defaultMessage: "Published"
2244
2673
  }),
2245
2674
  value: formatMessage(
2246
2675
  {
2247
2676
  id: "content-manager.containers.edit.information.last-published.value",
2248
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2677
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2249
2678
  },
2250
2679
  {
2251
2680
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2258,12 +2687,12 @@ const Information = ({ activeTab }) => {
2258
2687
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2259
2688
  label: formatMessage({
2260
2689
  id: "content-manager.containers.edit.information.last-draft.label",
2261
- defaultMessage: "Last draft"
2690
+ defaultMessage: "Updated"
2262
2691
  }),
2263
2692
  value: formatMessage(
2264
2693
  {
2265
2694
  id: "content-manager.containers.edit.information.last-draft.value",
2266
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2695
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2267
2696
  },
2268
2697
  {
2269
2698
  time: /* @__PURE__ */ jsx(
@@ -2281,12 +2710,12 @@ const Information = ({ activeTab }) => {
2281
2710
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2282
2711
  label: formatMessage({
2283
2712
  id: "content-manager.containers.edit.information.document.label",
2284
- defaultMessage: "Document"
2713
+ defaultMessage: "Created"
2285
2714
  }),
2286
2715
  value: formatMessage(
2287
2716
  {
2288
2717
  id: "content-manager.containers.edit.information.document.value",
2289
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2718
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2290
2719
  },
2291
2720
  {
2292
2721
  time: /* @__PURE__ */ jsx(
@@ -2324,25 +2753,77 @@ const Information = ({ activeTab }) => {
2324
2753
  );
2325
2754
  };
2326
2755
  const HeaderActions = ({ actions: actions2 }) => {
2327
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2328
- if ("options" in action) {
2756
+ const [dialogId, setDialogId] = React.useState(null);
2757
+ const handleClick = (action) => async (e) => {
2758
+ if (!("options" in action)) {
2759
+ const { onClick = () => false, dialog, id } = action;
2760
+ const muteDialog = await onClick(e);
2761
+ if (dialog && !muteDialog) {
2762
+ e.preventDefault();
2763
+ setDialogId(id);
2764
+ }
2765
+ }
2766
+ };
2767
+ const handleClose = () => {
2768
+ setDialogId(null);
2769
+ };
2770
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2771
+ if (action.options) {
2329
2772
  return /* @__PURE__ */ jsx(
2330
2773
  SingleSelect,
2331
2774
  {
2332
2775
  size: "S",
2333
- disabled: action.disabled,
2334
- "aria-label": action.label,
2335
2776
  onChange: action.onSelect,
2336
- value: action.value,
2777
+ "aria-label": action.label,
2778
+ ...action,
2337
2779
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2338
2780
  },
2339
2781
  action.id
2340
2782
  );
2341
2783
  } else {
2342
- return null;
2784
+ if (action.type === "icon") {
2785
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
2786
+ /* @__PURE__ */ jsx(
2787
+ IconButton,
2788
+ {
2789
+ disabled: action.disabled,
2790
+ label: action.label,
2791
+ size: "S",
2792
+ onClick: handleClick(action),
2793
+ children: action.icon
2794
+ }
2795
+ ),
2796
+ action.dialog ? /* @__PURE__ */ jsx(
2797
+ HeaderActionDialog,
2798
+ {
2799
+ ...action.dialog,
2800
+ isOpen: dialogId === action.id,
2801
+ onClose: handleClose
2802
+ }
2803
+ ) : null
2804
+ ] }, action.id);
2805
+ }
2343
2806
  }
2344
2807
  }) });
2345
2808
  };
2809
+ const HeaderActionDialog = ({
2810
+ onClose,
2811
+ onCancel,
2812
+ title,
2813
+ content: Content,
2814
+ isOpen
2815
+ }) => {
2816
+ const handleClose = async () => {
2817
+ if (onCancel) {
2818
+ await onCancel();
2819
+ }
2820
+ onClose();
2821
+ };
2822
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2823
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2824
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
2825
+ ] }) });
2826
+ };
2346
2827
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2347
2828
  const navigate = useNavigate();
2348
2829
  const { formatMessage } = useIntl();
@@ -2383,12 +2864,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2383
2864
  const { delete: deleteAction } = useDocumentActions();
2384
2865
  const { toggleNotification } = useNotification();
2385
2866
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2867
+ const isLocalized = document?.locale != null;
2386
2868
  return {
2387
2869
  disabled: !canDelete || !document,
2388
- label: formatMessage({
2389
- id: "content-manager.actions.delete.label",
2390
- defaultMessage: "Delete document"
2391
- }),
2870
+ label: formatMessage(
2871
+ {
2872
+ id: "content-manager.actions.delete.label",
2873
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2874
+ },
2875
+ { isLocalized }
2876
+ ),
2392
2877
  icon: /* @__PURE__ */ jsx(Trash, {}),
2393
2878
  dialog: {
2394
2879
  type: "dialog",
@@ -2422,425 +2907,123 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2422
2907
  return;
2423
2908
  }
2424
2909
  const res = await deleteAction({
2425
- documentId,
2426
- model,
2427
- collectionType,
2428
- params: {
2429
- locale: "*"
2430
- }
2431
- });
2432
- if (!("error" in res)) {
2433
- navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2434
- }
2435
- } finally {
2436
- if (!listViewPathMatch) {
2437
- setSubmitting(false);
2438
- }
2439
- }
2440
- }
2441
- },
2442
- variant: "danger",
2443
- position: ["header", "table-row"]
2444
- };
2445
- };
2446
- DeleteAction$1.type = "delete";
2447
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2448
- const Panels = () => {
2449
- const isCloning = useMatch(CLONE_PATH) !== null;
2450
- const [
2451
- {
2452
- query: { status }
2453
- }
2454
- ] = useQueryParams({
2455
- status: "draft"
2456
- });
2457
- const { model, id, document, meta, collectionType } = useDoc();
2458
- const plugins = useStrapiApp("Panels", (state) => state.plugins);
2459
- const props = {
2460
- activeTab: status,
2461
- model,
2462
- documentId: id,
2463
- document: isCloning ? void 0 : document,
2464
- meta: isCloning ? void 0 : meta,
2465
- collectionType
2466
- };
2467
- return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2468
- DescriptionComponentRenderer,
2469
- {
2470
- props,
2471
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2472
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2473
- }
2474
- ) });
2475
- };
2476
- const ActionsPanel = () => {
2477
- const { formatMessage } = useIntl();
2478
- return {
2479
- title: formatMessage({
2480
- id: "content-manager.containers.edit.panels.default.title",
2481
- defaultMessage: "Document"
2482
- }),
2483
- content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2484
- };
2485
- };
2486
- ActionsPanel.type = "actions";
2487
- const ActionsPanelContent = () => {
2488
- const isCloning = useMatch(CLONE_PATH) !== null;
2489
- const [
2490
- {
2491
- query: { status = "draft" }
2492
- }
2493
- ] = useQueryParams();
2494
- const { model, id, document, meta, collectionType } = useDoc();
2495
- const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2496
- const props = {
2497
- activeTab: status,
2498
- model,
2499
- documentId: id,
2500
- document: isCloning ? void 0 : document,
2501
- meta: isCloning ? void 0 : meta,
2502
- collectionType
2503
- };
2504
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2505
- /* @__PURE__ */ jsx(
2506
- DescriptionComponentRenderer,
2507
- {
2508
- props,
2509
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2510
- children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2511
- }
2512
- ),
2513
- /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2514
- ] });
2515
- };
2516
- const Panel = React.forwardRef(({ children, title }, ref) => {
2517
- return /* @__PURE__ */ jsxs(
2518
- Flex,
2519
- {
2520
- ref,
2521
- tag: "aside",
2522
- "aria-labelledby": "additional-information",
2523
- background: "neutral0",
2524
- borderColor: "neutral150",
2525
- hasRadius: true,
2526
- paddingBottom: 4,
2527
- paddingLeft: 4,
2528
- paddingRight: 4,
2529
- paddingTop: 4,
2530
- shadow: "tableShadow",
2531
- gap: 3,
2532
- direction: "column",
2533
- justifyContent: "stretch",
2534
- alignItems: "flex-start",
2535
- children: [
2536
- /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2537
- children
2538
- ]
2539
- }
2540
- );
2541
- });
2542
- const HOOKS = {
2543
- /**
2544
- * Hook that allows to mutate the displayed headers of the list view table
2545
- * @constant
2546
- * @type {string}
2547
- */
2548
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2549
- /**
2550
- * Hook that allows to mutate the CM's collection types links pre-set filters
2551
- * @constant
2552
- * @type {string}
2553
- */
2554
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2555
- /**
2556
- * Hook that allows to mutate the CM's edit view layout
2557
- * @constant
2558
- * @type {string}
2559
- */
2560
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2561
- /**
2562
- * Hook that allows to mutate the CM's single types links pre-set filters
2563
- * @constant
2564
- * @type {string}
2565
- */
2566
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2567
- };
2568
- const contentTypesApi = contentManagerApi.injectEndpoints({
2569
- endpoints: (builder) => ({
2570
- getContentTypeConfiguration: builder.query({
2571
- query: (uid) => ({
2572
- url: `/content-manager/content-types/${uid}/configuration`,
2573
- method: "GET"
2574
- }),
2575
- transformResponse: (response) => response.data,
2576
- providesTags: (_result, _error, uid) => [
2577
- { type: "ContentTypesConfiguration", id: uid },
2578
- { type: "ContentTypeSettings", id: "LIST" }
2579
- ]
2580
- }),
2581
- getAllContentTypeSettings: builder.query({
2582
- query: () => "/content-manager/content-types-settings",
2583
- transformResponse: (response) => response.data,
2584
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2585
- }),
2586
- updateContentTypeConfiguration: builder.mutation({
2587
- query: ({ uid, ...body }) => ({
2588
- url: `/content-manager/content-types/${uid}/configuration`,
2589
- method: "PUT",
2590
- data: body
2591
- }),
2592
- transformResponse: (response) => response.data,
2593
- invalidatesTags: (_result, _error, { uid }) => [
2594
- { type: "ContentTypesConfiguration", id: uid },
2595
- { type: "ContentTypeSettings", id: "LIST" },
2596
- // Is this necessary?
2597
- { type: "InitialData" }
2598
- ]
2599
- })
2600
- })
2601
- });
2602
- const {
2603
- useGetContentTypeConfigurationQuery,
2604
- useGetAllContentTypeSettingsQuery,
2605
- useUpdateContentTypeConfigurationMutation
2606
- } = contentTypesApi;
2607
- const checkIfAttributeIsDisplayable = (attribute) => {
2608
- const { type } = attribute;
2609
- if (type === "relation") {
2610
- return !attribute.relation.toLowerCase().includes("morph");
2611
- }
2612
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2613
- };
2614
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2615
- if (!mainFieldName) {
2616
- return void 0;
2617
- }
2618
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2619
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2620
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2621
- );
2622
- return {
2623
- name: mainFieldName,
2624
- type: mainFieldType ?? "string"
2625
- };
2626
- };
2627
- const DEFAULT_SETTINGS = {
2628
- bulkable: false,
2629
- filterable: false,
2630
- searchable: false,
2631
- pagination: false,
2632
- defaultSortBy: "",
2633
- defaultSortOrder: "asc",
2634
- mainField: "id",
2635
- pageSize: 10
2636
- };
2637
- const useDocumentLayout = (model) => {
2638
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2639
- const [{ query }] = useQueryParams();
2640
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2641
- const { toggleNotification } = useNotification();
2642
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2643
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2644
- const {
2645
- data,
2646
- isLoading: isLoadingConfigs,
2647
- error,
2648
- isFetching: isFetchingConfigs
2649
- } = useGetContentTypeConfigurationQuery(model);
2650
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2651
- React.useEffect(() => {
2652
- if (error) {
2653
- toggleNotification({
2654
- type: "danger",
2655
- message: formatAPIError(error)
2656
- });
2657
- }
2658
- }, [error, formatAPIError, toggleNotification]);
2659
- const editLayout = React.useMemo(
2660
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2661
- layout: [],
2662
- components: {},
2663
- metadatas: {},
2664
- options: {},
2665
- settings: DEFAULT_SETTINGS
2666
- },
2667
- [data, isLoading, schemas, schema, components]
2668
- );
2669
- const listLayout = React.useMemo(() => {
2670
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2671
- layout: [],
2672
- metadatas: {},
2673
- options: {},
2674
- settings: DEFAULT_SETTINGS
2675
- };
2676
- }, [data, isLoading, schemas, schema, components]);
2677
- const { layout: edit } = React.useMemo(
2678
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2679
- layout: editLayout,
2680
- query
2681
- }),
2682
- [editLayout, query, runHookWaterfall]
2683
- );
2684
- return {
2685
- error,
2686
- isLoading,
2687
- edit,
2688
- list: listLayout
2689
- };
2690
- };
2691
- const useDocLayout = () => {
2692
- const { model } = useDoc();
2693
- return useDocumentLayout(model);
2694
- };
2695
- const formatEditLayout = (data, {
2696
- schemas,
2697
- schema,
2698
- components
2699
- }) => {
2700
- let currentPanelIndex = 0;
2701
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2702
- data.contentType.layouts.edit,
2703
- schema?.attributes,
2704
- data.contentType.metadatas,
2705
- { configurations: data.components, schemas: components },
2706
- schemas
2707
- ).reduce((panels, row) => {
2708
- if (row.some((field) => field.type === "dynamiczone")) {
2709
- panels.push([row]);
2710
- currentPanelIndex += 2;
2711
- } else {
2712
- if (!panels[currentPanelIndex]) {
2713
- panels.push([]);
2714
- }
2715
- panels[currentPanelIndex].push(row);
2716
- }
2717
- return panels;
2718
- }, []);
2719
- const componentEditAttributes = Object.entries(data.components).reduce(
2720
- (acc, [uid, configuration]) => {
2721
- acc[uid] = {
2722
- layout: convertEditLayoutToFieldLayouts(
2723
- configuration.layouts.edit,
2724
- components[uid].attributes,
2725
- configuration.metadatas
2726
- ),
2727
- settings: {
2728
- ...configuration.settings,
2729
- icon: components[uid].info.icon,
2730
- displayName: components[uid].info.displayName
2910
+ documentId,
2911
+ model,
2912
+ collectionType,
2913
+ params: {
2914
+ locale: "*"
2915
+ }
2916
+ });
2917
+ if (!("error" in res)) {
2918
+ navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2919
+ }
2920
+ } finally {
2921
+ if (!listViewPathMatch) {
2922
+ setSubmitting(false);
2923
+ }
2731
2924
  }
2732
- };
2733
- return acc;
2734
- },
2735
- {}
2736
- );
2737
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2738
- (acc, [attribute, metadata]) => {
2739
- return {
2740
- ...acc,
2741
- [attribute]: metadata.edit
2742
- };
2743
- },
2744
- {}
2745
- );
2746
- return {
2747
- layout: panelledEditAttributes,
2748
- components: componentEditAttributes,
2749
- metadatas: editMetadatas,
2750
- settings: {
2751
- ...data.contentType.settings,
2752
- displayName: schema?.info.displayName
2925
+ }
2753
2926
  },
2754
- options: {
2755
- ...schema?.options,
2756
- ...schema?.pluginOptions,
2757
- ...data.contentType.options
2758
- }
2927
+ variant: "danger",
2928
+ position: ["header", "table-row"]
2759
2929
  };
2760
2930
  };
2761
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2762
- return rows.map(
2763
- (row) => row.map((field) => {
2764
- const attribute = attributes[field.name];
2765
- if (!attribute) {
2766
- return null;
2767
- }
2768
- const { edit: metadata } = metadatas[field.name];
2769
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2770
- return {
2771
- attribute,
2772
- disabled: !metadata.editable,
2773
- hint: metadata.description,
2774
- label: metadata.label ?? "",
2775
- name: field.name,
2776
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2777
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2778
- schemas,
2779
- components: components?.schemas ?? {}
2780
- }),
2781
- placeholder: metadata.placeholder ?? "",
2782
- required: attribute.required ?? false,
2783
- size: field.size,
2784
- unique: "unique" in attribute ? attribute.unique : false,
2785
- visible: metadata.visible ?? true,
2786
- type: attribute.type
2787
- };
2788
- }).filter((field) => field !== null)
2789
- );
2931
+ DeleteAction$1.type = "delete";
2932
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2933
+ const Panels = () => {
2934
+ const isCloning = useMatch(CLONE_PATH) !== null;
2935
+ const [
2936
+ {
2937
+ query: { status }
2938
+ }
2939
+ ] = useQueryParams({
2940
+ status: "draft"
2941
+ });
2942
+ const { model, id, document, meta, collectionType } = useDoc();
2943
+ const plugins = useStrapiApp("Panels", (state) => state.plugins);
2944
+ const props = {
2945
+ activeTab: status,
2946
+ model,
2947
+ documentId: id,
2948
+ document: isCloning ? void 0 : document,
2949
+ meta: isCloning ? void 0 : meta,
2950
+ collectionType
2951
+ };
2952
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
2953
+ DescriptionComponentRenderer,
2954
+ {
2955
+ props,
2956
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2957
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
2958
+ }
2959
+ ) });
2790
2960
  };
2791
- const formatListLayout = (data, {
2792
- schemas,
2793
- schema,
2794
- components
2795
- }) => {
2796
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2797
- (acc, [attribute, metadata]) => {
2798
- return {
2799
- ...acc,
2800
- [attribute]: metadata.list
2801
- };
2802
- },
2803
- {}
2804
- );
2805
- const listAttributes = convertListLayoutToFieldLayouts(
2806
- data.contentType.layouts.list,
2807
- schema?.attributes,
2808
- listMetadatas,
2809
- { configurations: data.components, schemas: components },
2810
- schemas
2811
- );
2961
+ const ActionsPanel = () => {
2962
+ const { formatMessage } = useIntl();
2812
2963
  return {
2813
- layout: listAttributes,
2814
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2815
- metadatas: listMetadatas,
2816
- options: {
2817
- ...schema?.options,
2818
- ...schema?.pluginOptions,
2819
- ...data.contentType.options
2820
- }
2964
+ title: formatMessage({
2965
+ id: "content-manager.containers.edit.panels.default.title",
2966
+ defaultMessage: "Entry"
2967
+ }),
2968
+ content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2821
2969
  };
2822
2970
  };
2823
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2824
- return columns.map((name) => {
2825
- const attribute = attributes[name];
2826
- if (!attribute) {
2827
- return null;
2971
+ ActionsPanel.type = "actions";
2972
+ const ActionsPanelContent = () => {
2973
+ const isCloning = useMatch(CLONE_PATH) !== null;
2974
+ const [
2975
+ {
2976
+ query: { status = "draft" }
2828
2977
  }
2829
- const metadata = metadatas[name];
2830
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2831
- return {
2832
- attribute,
2833
- label: metadata.label ?? "",
2834
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2835
- schemas,
2836
- components: components?.schemas ?? {}
2837
- }),
2838
- name,
2839
- searchable: metadata.searchable ?? true,
2840
- sortable: metadata.sortable ?? true
2841
- };
2842
- }).filter((field) => field !== null);
2978
+ ] = useQueryParams();
2979
+ const { model, id, document, meta, collectionType } = useDoc();
2980
+ const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
2981
+ const props = {
2982
+ activeTab: status,
2983
+ model,
2984
+ documentId: id,
2985
+ document: isCloning ? void 0 : document,
2986
+ meta: isCloning ? void 0 : meta,
2987
+ collectionType
2988
+ };
2989
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
2990
+ /* @__PURE__ */ jsx(
2991
+ DescriptionComponentRenderer,
2992
+ {
2993
+ props,
2994
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
2995
+ children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
2996
+ }
2997
+ ),
2998
+ /* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
2999
+ ] });
2843
3000
  };
3001
+ const Panel = React.forwardRef(({ children, title }, ref) => {
3002
+ return /* @__PURE__ */ jsxs(
3003
+ Flex,
3004
+ {
3005
+ ref,
3006
+ tag: "aside",
3007
+ "aria-labelledby": "additional-information",
3008
+ background: "neutral0",
3009
+ borderColor: "neutral150",
3010
+ hasRadius: true,
3011
+ paddingBottom: 4,
3012
+ paddingLeft: 4,
3013
+ paddingRight: 4,
3014
+ paddingTop: 4,
3015
+ shadow: "tableShadow",
3016
+ gap: 3,
3017
+ direction: "column",
3018
+ justifyContent: "stretch",
3019
+ alignItems: "flex-start",
3020
+ children: [
3021
+ /* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3022
+ children
3023
+ ]
3024
+ }
3025
+ );
3026
+ });
2844
3027
  const ConfirmBulkActionDialog = ({
2845
3028
  onToggleDialog,
2846
3029
  isOpen = false,
@@ -2879,6 +3062,7 @@ const ConfirmDialogPublishAll = ({
2879
3062
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2880
3063
  const { model, schema } = useDoc();
2881
3064
  const [{ query }] = useQueryParams();
3065
+ const enableDraftRelationsCount = false;
2882
3066
  const {
2883
3067
  data: countDraftRelations = 0,
2884
3068
  isLoading,
@@ -2890,7 +3074,7 @@ const ConfirmDialogPublishAll = ({
2890
3074
  locale: query?.plugins?.i18n?.locale
2891
3075
  },
2892
3076
  {
2893
- skip: selectedEntries.length === 0
3077
+ skip: !enableDraftRelationsCount
2894
3078
  }
2895
3079
  );
2896
3080
  React.useEffect(() => {
@@ -3075,7 +3259,7 @@ const SelectedEntriesTableContent = ({
3075
3259
  status: row.status
3076
3260
  }
3077
3261
  ) }),
3078
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3262
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3079
3263
  IconButton,
3080
3264
  {
3081
3265
  tag: Link,
@@ -3098,9 +3282,10 @@ const SelectedEntriesTableContent = ({
3098
3282
  ),
3099
3283
  target: "_blank",
3100
3284
  marginLeft: "auto",
3101
- children: /* @__PURE__ */ jsx(Pencil, {})
3285
+ variant: "ghost",
3286
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3102
3287
  }
3103
- ) })
3288
+ ) }) })
3104
3289
  ] }, row.id)) })
3105
3290
  ] });
3106
3291
  };
@@ -3137,7 +3322,13 @@ const SelectedEntriesModalContent = ({
3137
3322
  );
3138
3323
  const { rows, validationErrors } = React.useMemo(() => {
3139
3324
  if (data.length > 0 && schema) {
3140
- const validate = createYupSchema(schema.attributes, components);
3325
+ const validate = createYupSchema(
3326
+ schema.attributes,
3327
+ components,
3328
+ // Since this is the "Publish" action, the validation
3329
+ // schema must enforce the rules for published entities
3330
+ { status: "published" }
3331
+ );
3141
3332
  const validationErrors2 = {};
3142
3333
  const rows2 = data.map((entry) => {
3143
3334
  try {
@@ -3487,7 +3678,7 @@ const TableActions = ({ document }) => {
3487
3678
  DescriptionComponentRenderer,
3488
3679
  {
3489
3680
  props,
3490
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3681
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3491
3682
  children: (actions2) => {
3492
3683
  const tableRowActions = actions2.filter((action) => {
3493
3684
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3598,7 +3789,7 @@ const CloneAction = ({ model, documentId }) => {
3598
3789
  }),
3599
3790
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3600
3791
  footer: ({ onClose }) => {
3601
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3792
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3602
3793
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3603
3794
  id: "cancel",
3604
3795
  defaultMessage: "Cancel"
@@ -3810,6 +4001,97 @@ const { setInitialData } = actions;
3810
4001
  const reducer = combineReducers({
3811
4002
  app: reducer$1
3812
4003
  });
4004
+ const previewApi = contentManagerApi.injectEndpoints({
4005
+ endpoints: (builder) => ({
4006
+ getPreviewUrl: builder.query({
4007
+ query({ query, params }) {
4008
+ return {
4009
+ url: `/content-manager/preview/url/${params.contentType}`,
4010
+ method: "GET",
4011
+ config: {
4012
+ params: query
4013
+ }
4014
+ };
4015
+ }
4016
+ })
4017
+ })
4018
+ });
4019
+ const { useGetPreviewUrlQuery } = previewApi;
4020
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4021
+ const { formatMessage } = useIntl();
4022
+ const { toggleNotification } = useNotification();
4023
+ const { copy } = useClipboard();
4024
+ const { trackUsage } = useTracking();
4025
+ const { data, error } = useGetPreviewUrlQuery({
4026
+ params: {
4027
+ contentType: model
4028
+ },
4029
+ query: {
4030
+ documentId,
4031
+ locale: document?.locale,
4032
+ status: document?.status
4033
+ }
4034
+ });
4035
+ if (!data?.data?.url || error) {
4036
+ return null;
4037
+ }
4038
+ const { url } = data.data;
4039
+ const handleCopyLink = () => {
4040
+ copy(url);
4041
+ toggleNotification({
4042
+ message: formatMessage({
4043
+ id: "content-manager.preview.copy.success",
4044
+ defaultMessage: "Copied preview link"
4045
+ }),
4046
+ type: "success"
4047
+ });
4048
+ };
4049
+ const handleClick = () => {
4050
+ trackUsage("willOpenPreview");
4051
+ };
4052
+ return {
4053
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4054
+ content: /* @__PURE__ */ jsxs(Flex, { gap: 2, width: "100%", children: [
4055
+ /* @__PURE__ */ jsx(
4056
+ Button,
4057
+ {
4058
+ variant: "tertiary",
4059
+ tag: Link,
4060
+ to: url,
4061
+ onClick: handleClick,
4062
+ target: "_blank",
4063
+ flex: "auto",
4064
+ children: formatMessage({
4065
+ id: "content-manager.preview.panel.button",
4066
+ defaultMessage: "Open preview"
4067
+ })
4068
+ }
4069
+ ),
4070
+ /* @__PURE__ */ jsx(
4071
+ IconButton,
4072
+ {
4073
+ type: "button",
4074
+ label: formatMessage({
4075
+ id: "preview.copy.label",
4076
+ defaultMessage: "Copy preview link"
4077
+ }),
4078
+ onClick: handleCopyLink,
4079
+ children: /* @__PURE__ */ jsx(Link$1, {})
4080
+ }
4081
+ )
4082
+ ] })
4083
+ };
4084
+ };
4085
+ const FEATURE_ID = "preview";
4086
+ const previewAdmin = {
4087
+ bootstrap(app) {
4088
+ if (!window.strapi.future.isEnabled(FEATURE_ID)) {
4089
+ return;
4090
+ }
4091
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4092
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4093
+ }
4094
+ };
3813
4095
  const index = {
3814
4096
  register(app) {
3815
4097
  const cm = new ContentManagerPlugin();
@@ -3829,7 +4111,7 @@ const index = {
3829
4111
  app.router.addRoute({
3830
4112
  path: "content-manager/*",
3831
4113
  lazy: async () => {
3832
- const { Layout } = await import("./layout-DPaHUusj.mjs");
4114
+ const { Layout } = await import("./layout-ykHSe2KQ.mjs");
3833
4115
  return {
3834
4116
  Component: Layout
3835
4117
  };
@@ -3842,11 +4124,14 @@ const index = {
3842
4124
  if (typeof historyAdmin.bootstrap === "function") {
3843
4125
  historyAdmin.bootstrap(app);
3844
4126
  }
4127
+ if (typeof previewAdmin.bootstrap === "function") {
4128
+ previewAdmin.bootstrap(app);
4129
+ }
3845
4130
  },
3846
4131
  async registerTrads({ locales }) {
3847
4132
  const importedTrads = await Promise.all(
3848
4133
  locales.map((locale) => {
3849
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-BrCTWlZv.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
4134
+ 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-CZw4xdPY.mjs"), "./translations/es.json": () => import("./es-D34tqjMw.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr--pg5jUbt.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-BHqhDq4V.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
3850
4135
  return {
3851
4136
  data: prefixPluginTranslations(data, PLUGIN_ID),
3852
4137
  locale
@@ -3867,13 +4152,15 @@ export {
3867
4152
  BulkActionsRenderer as B,
3868
4153
  COLLECTION_TYPES as C,
3869
4154
  DocumentStatus as D,
3870
- DEFAULT_SETTINGS as E,
3871
- convertEditLayoutToFieldLayouts as F,
3872
- useDocument as G,
4155
+ extractContentTypeComponents as E,
4156
+ DEFAULT_SETTINGS as F,
4157
+ convertEditLayoutToFieldLayouts as G,
3873
4158
  HOOKS as H,
3874
4159
  InjectionZone as I,
3875
- index as J,
3876
- useDocumentActions as K,
4160
+ useDocument as J,
4161
+ index as K,
4162
+ useContentManagerContext as L,
4163
+ useDocumentActions as M,
3877
4164
  Panels as P,
3878
4165
  RelativeTime as R,
3879
4166
  SINGLE_TYPES as S,
@@ -3891,18 +4178,18 @@ export {
3891
4178
  PERMISSIONS as k,
3892
4179
  DocumentRBAC as l,
3893
4180
  DOCUMENT_META_FIELDS as m,
3894
- useDocLayout as n,
3895
- useGetContentTypeConfigurationQuery as o,
3896
- CREATOR_FIELDS as p,
3897
- getMainField as q,
3898
- getDisplayName as r,
4181
+ CLONE_PATH as n,
4182
+ useDocLayout as o,
4183
+ useGetContentTypeConfigurationQuery as p,
4184
+ CREATOR_FIELDS as q,
4185
+ getMainField as r,
3899
4186
  setInitialData as s,
3900
- checkIfAttributeIsDisplayable as t,
4187
+ getDisplayName as t,
3901
4188
  useContentTypeSchema as u,
3902
- useGetAllDocumentsQuery as v,
3903
- convertListLayoutToFieldLayouts as w,
3904
- capitalise as x,
3905
- useUpdateContentTypeConfigurationMutation as y,
3906
- extractContentTypeComponents as z
4189
+ checkIfAttributeIsDisplayable as v,
4190
+ useGetAllDocumentsQuery as w,
4191
+ convertListLayoutToFieldLayouts as x,
4192
+ capitalise as y,
4193
+ useUpdateContentTypeConfigurationMutation as z
3907
4194
  };
3908
- //# sourceMappingURL=index-BSn97i8U.mjs.map
4195
+ //# sourceMappingURL=index-BvGihCJp.mjs.map