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

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 (194) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-BWQv6yRj.js → ComponentConfigurationPage-CpJNPBgk.js} +4 -4
  2. package/dist/_chunks/{ComponentConfigurationPage-BWQv6yRj.js.map → ComponentConfigurationPage-CpJNPBgk.js.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-C7ImeKGM.mjs → ComponentConfigurationPage-TYDPg5WG.mjs} +4 -4
  4. package/dist/_chunks/{ComponentConfigurationPage-C7ImeKGM.mjs.map → ComponentConfigurationPage-TYDPg5WG.mjs.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-CEGwxV-L.js → EditConfigurationPage-CFDe6SA1.js} +4 -4
  6. package/dist/_chunks/{EditConfigurationPage-CEGwxV-L.js.map → EditConfigurationPage-CFDe6SA1.js.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-MItFGzT9.mjs → EditConfigurationPage-DqL8Pq5r.mjs} +4 -4
  8. package/dist/_chunks/{EditConfigurationPage-MItFGzT9.mjs.map → EditConfigurationPage-DqL8Pq5r.mjs.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-DhmAg0NK.mjs → EditViewPage-RXrFLav2.mjs} +30 -9
  10. package/dist/_chunks/EditViewPage-RXrFLav2.mjs.map +1 -0
  11. package/dist/_chunks/{EditViewPage-CmMi2Xsn.js → EditViewPage-khfP2CR3.js} +30 -9
  12. package/dist/_chunks/EditViewPage-khfP2CR3.js.map +1 -0
  13. package/dist/_chunks/{Field-Cs62u5pl.mjs → Field--rQeS6Zj.mjs} +179 -107
  14. package/dist/_chunks/Field--rQeS6Zj.mjs.map +1 -0
  15. package/dist/_chunks/{Field-1DLtcLAI.js → Field-C1ftmTe9.js} +181 -109
  16. package/dist/_chunks/Field-C1ftmTe9.js.map +1 -0
  17. package/dist/_chunks/{Form-zYHtzGUX.mjs → Form-COtGXyUE.mjs} +36 -17
  18. package/dist/_chunks/Form-COtGXyUE.mjs.map +1 -0
  19. package/dist/_chunks/{Form-CqFA7F_V.js → Form-CwdX5oLw.js} +36 -17
  20. package/dist/_chunks/Form-CwdX5oLw.js.map +1 -0
  21. package/dist/_chunks/{History-DalgFQ3D.mjs → History-BevwkPO1.mjs} +55 -48
  22. package/dist/_chunks/History-BevwkPO1.mjs.map +1 -0
  23. package/dist/_chunks/{History-BblwXv7-.js → History-DKS2aqqM.js} +54 -47
  24. package/dist/_chunks/History-DKS2aqqM.js.map +1 -0
  25. package/dist/_chunks/{ListConfigurationPage-DWy-vRzs.mjs → ListConfigurationPage-DNfZDtDA.mjs} +15 -5
  26. package/dist/_chunks/ListConfigurationPage-DNfZDtDA.mjs.map +1 -0
  27. package/dist/_chunks/{ListConfigurationPage-Cpy4QqNd.js → ListConfigurationPage-LSYSPZHH.js} +15 -5
  28. package/dist/_chunks/ListConfigurationPage-LSYSPZHH.js.map +1 -0
  29. package/dist/_chunks/{ListViewPage-BkAwIW9s.mjs → ListViewPage-C1PyuYRS.mjs} +59 -39
  30. package/dist/_chunks/ListViewPage-C1PyuYRS.mjs.map +1 -0
  31. package/dist/_chunks/{ListViewPage-DFjn1DNW.js → ListViewPage-DlUpqLIo.js} +61 -41
  32. package/dist/_chunks/ListViewPage-DlUpqLIo.js.map +1 -0
  33. package/dist/_chunks/{NoContentTypePage-B9BCNNdL.mjs → NoContentTypePage-C9q744z1.mjs} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-B9BCNNdL.mjs.map → NoContentTypePage-C9q744z1.mjs.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-C-3ykoxs.js → NoContentTypePage-m8wt3sf6.js} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-C-3ykoxs.js.map → NoContentTypePage-m8wt3sf6.js.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-Bt_HWGat.mjs → NoPermissionsPage-8BM-LWta.mjs} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-Bt_HWGat.mjs.map → NoPermissionsPage-8BM-LWta.mjs.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-DKLmDZnZ.js → NoPermissionsPage-DLfPsA0Q.js} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-DKLmDZnZ.js.map → NoPermissionsPage-DLfPsA0Q.js.map} +1 -1
  41. package/dist/_chunks/{Relations-CJmTbZ8T.mjs → Relations-D25xRcFy.mjs} +73 -37
  42. package/dist/_chunks/Relations-D25xRcFy.mjs.map +1 -0
  43. package/dist/_chunks/{Relations-CrxfoH2n.js → Relations-OMriCP_L.js} +72 -36
  44. package/dist/_chunks/Relations-OMriCP_L.js.map +1 -0
  45. package/dist/_chunks/{en-fbKQxLGn.js → en-Bdpa50w3.js} +22 -16
  46. package/dist/_chunks/{en-fbKQxLGn.js.map → en-Bdpa50w3.js.map} +1 -1
  47. package/dist/_chunks/{en-Ux26r5pl.mjs → en-CZw4xdPY.mjs} +22 -16
  48. package/dist/_chunks/{en-Ux26r5pl.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-D1344xdw.mjs → index-BvGihCJp.mjs} +1075 -695
  58. package/dist/_chunks/index-BvGihCJp.mjs.map +1 -0
  59. package/dist/_chunks/{index-Buwn78Rt.js → index-DqZnjo8F.js} +1057 -676
  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-DRuJUpas.js → layout-CmaemAO3.js} +21 -8
  66. package/dist/_chunks/layout-CmaemAO3.js.map +1 -0
  67. package/dist/_chunks/{layout-ChVuUpa1.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-DuoUwyJr.js → relations-D9fKsCLY.js} +3 -7
  74. package/dist/_chunks/relations-D9fKsCLY.js.map +1 -0
  75. package/dist/_chunks/{relations-B-deMCy4.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/index.d.ts +3 -0
  86. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  87. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  88. package/dist/admin/src/index.d.ts +1 -0
  89. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -0
  90. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  91. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  92. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  93. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  94. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  95. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  96. package/dist/admin/src/preview/constants.d.ts +1 -0
  97. package/dist/admin/src/preview/index.d.ts +4 -0
  98. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  99. package/dist/admin/src/services/api.d.ts +1 -1
  100. package/dist/admin/src/services/components.d.ts +2 -2
  101. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  102. package/dist/admin/src/services/documents.d.ts +19 -17
  103. package/dist/admin/src/services/init.d.ts +1 -1
  104. package/dist/admin/src/services/relations.d.ts +2 -2
  105. package/dist/admin/src/services/uid.d.ts +3 -3
  106. package/dist/admin/src/utils/validation.d.ts +4 -1
  107. package/dist/server/index.js +544 -261
  108. package/dist/server/index.js.map +1 -1
  109. package/dist/server/index.mjs +545 -262
  110. package/dist/server/index.mjs.map +1 -1
  111. package/dist/server/src/bootstrap.d.ts.map +1 -1
  112. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  113. package/dist/server/src/controllers/index.d.ts.map +1 -1
  114. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  115. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  116. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  117. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  118. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  119. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  120. package/dist/server/src/history/services/history.d.ts.map +1 -1
  121. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  122. package/dist/server/src/history/services/utils.d.ts +4 -4
  123. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  124. package/dist/server/src/index.d.ts +4 -4
  125. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  126. package/dist/server/src/preview/constants.d.ts +2 -0
  127. package/dist/server/src/preview/constants.d.ts.map +1 -0
  128. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  129. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  130. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  131. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  132. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  133. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  134. package/dist/server/src/preview/index.d.ts +4 -0
  135. package/dist/server/src/preview/index.d.ts.map +1 -0
  136. package/dist/server/src/preview/routes/index.d.ts +8 -0
  137. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  138. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  139. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  140. package/dist/server/src/preview/services/index.d.ts +15 -0
  141. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  142. package/dist/server/src/preview/services/preview-config.d.ts +30 -0
  143. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  144. package/dist/server/src/preview/services/preview.d.ts +12 -0
  145. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  146. package/dist/server/src/preview/utils.d.ts +18 -0
  147. package/dist/server/src/preview/utils.d.ts.map +1 -0
  148. package/dist/server/src/routes/index.d.ts.map +1 -1
  149. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  150. package/dist/server/src/services/document-metadata.d.ts +8 -8
  151. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  152. package/dist/server/src/services/index.d.ts +4 -4
  153. package/dist/server/src/services/index.d.ts.map +1 -1
  154. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  155. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  156. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  157. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  158. package/dist/server/src/utils/index.d.ts +2 -0
  159. package/dist/server/src/utils/index.d.ts.map +1 -1
  160. package/dist/shared/contracts/collection-types.d.ts +3 -1
  161. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  162. package/dist/shared/contracts/index.d.ts +1 -0
  163. package/dist/shared/contracts/index.d.ts.map +1 -1
  164. package/dist/shared/contracts/preview.d.ts +27 -0
  165. package/dist/shared/contracts/preview.d.ts.map +1 -0
  166. package/dist/shared/index.js +4 -0
  167. package/dist/shared/index.js.map +1 -1
  168. package/dist/shared/index.mjs +4 -0
  169. package/dist/shared/index.mjs.map +1 -1
  170. package/package.json +13 -13
  171. package/dist/_chunks/EditViewPage-CmMi2Xsn.js.map +0 -1
  172. package/dist/_chunks/EditViewPage-DhmAg0NK.mjs.map +0 -1
  173. package/dist/_chunks/Field-1DLtcLAI.js.map +0 -1
  174. package/dist/_chunks/Field-Cs62u5pl.mjs.map +0 -1
  175. package/dist/_chunks/Form-CqFA7F_V.js.map +0 -1
  176. package/dist/_chunks/Form-zYHtzGUX.mjs.map +0 -1
  177. package/dist/_chunks/History-BblwXv7-.js.map +0 -1
  178. package/dist/_chunks/History-DalgFQ3D.mjs.map +0 -1
  179. package/dist/_chunks/ListConfigurationPage-Cpy4QqNd.js.map +0 -1
  180. package/dist/_chunks/ListConfigurationPage-DWy-vRzs.mjs.map +0 -1
  181. package/dist/_chunks/ListViewPage-BkAwIW9s.mjs.map +0 -1
  182. package/dist/_chunks/ListViewPage-DFjn1DNW.js.map +0 -1
  183. package/dist/_chunks/Relations-CJmTbZ8T.mjs.map +0 -1
  184. package/dist/_chunks/Relations-CrxfoH2n.js.map +0 -1
  185. package/dist/_chunks/index-Buwn78Rt.js.map +0 -1
  186. package/dist/_chunks/index-D1344xdw.mjs.map +0 -1
  187. package/dist/_chunks/layout-ChVuUpa1.mjs.map +0 -1
  188. package/dist/_chunks/layout-DRuJUpas.js.map +0 -1
  189. package/dist/_chunks/relations-B-deMCy4.mjs.map +0 -1
  190. package/dist/_chunks/relations-DuoUwyJr.js.map +0 -1
  191. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  192. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  193. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  194. package/strapi-server.js +0 -3
@@ -2,15 +2,16 @@
2
2
  const Icons = require("@strapi/icons");
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const strapiAdmin = require("@strapi/admin/strapi-admin");
5
- const qs = require("qs");
6
- const reactIntl = require("react-intl");
7
- const reactRouterDom = require("react-router-dom");
8
5
  const React = require("react");
9
6
  const designSystem = require("@strapi/design-system");
10
- const styledComponents = require("styled-components");
7
+ const mapValues = require("lodash/fp/mapValues");
8
+ const reactIntl = require("react-intl");
9
+ const reactRouterDom = require("react-router-dom");
11
10
  const yup = require("yup");
12
11
  const pipe = require("lodash/fp/pipe");
13
12
  const dateFns = require("date-fns");
13
+ const styledComponents = require("styled-components");
14
+ const qs = require("qs");
14
15
  const toolkit = require("@reduxjs/toolkit");
15
16
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
16
17
  function _interopNamespace(e) {
@@ -32,6 +33,7 @@ function _interopNamespace(e) {
32
33
  return Object.freeze(n);
33
34
  }
34
35
  const React__namespace = /* @__PURE__ */ _interopNamespace(React);
36
+ const mapValues__default = /* @__PURE__ */ _interopDefault(mapValues);
35
37
  const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
36
38
  const pipe__default = /* @__PURE__ */ _interopDefault(pipe);
37
39
  const __variableDynamicImportRuntimeHelper = (glob, path) => {
@@ -70,42 +72,6 @@ const useInjectionZone = (area) => {
70
72
  const [page, position] = area.split(".");
71
73
  return contentManagerPlugin.getInjectedComponents(page, position);
72
74
  };
73
- const HistoryAction = ({ model, document }) => {
74
- const { formatMessage } = reactIntl.useIntl();
75
- const [{ query }] = strapiAdmin.useQueryParams();
76
- const navigate = reactRouterDom.useNavigate();
77
- const pluginsQueryParams = qs.stringify({ plugins: query.plugins }, { encode: false });
78
- if (!window.strapi.features.isEnabled("cms-content-history")) {
79
- return null;
80
- }
81
- return {
82
- icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ClockCounterClockwise, {}),
83
- label: formatMessage({
84
- id: "content-manager.history.document-action",
85
- defaultMessage: "Content History"
86
- }),
87
- onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
88
- disabled: (
89
- /**
90
- * The user is creating a new document.
91
- * It hasn't been saved yet, so there's no history to go to
92
- */
93
- !document || /**
94
- * The document has been created but the current dimension has never been saved.
95
- * For example, the user is creating a new locale in an existing document,
96
- * so there's no history for the document in that locale
97
- */
98
- !document.id || /**
99
- * History is only available for content types created by the user.
100
- * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
101
- * which start with `admin::` or `plugin::`
102
- */
103
- !model.startsWith("api::")
104
- ),
105
- position: "header"
106
- };
107
- };
108
- HistoryAction.type = "history";
109
75
  const ID = "id";
110
76
  const CREATED_BY_ATTRIBUTE_NAME = "createdBy";
111
77
  const UPDATED_BY_ATTRIBUTE_NAME = "updatedBy";
@@ -157,6 +123,7 @@ const DocumentRBAC = ({ children, permissions }) => {
157
123
  if (!slug) {
158
124
  throw new Error("Cannot find the slug param in the URL");
159
125
  }
126
+ const [{ rawQuery }] = strapiAdmin.useQueryParams();
160
127
  const userPermissions = strapiAdmin.useAuth("DocumentRBAC", (state) => state.permissions);
161
128
  const contentTypePermissions = React__namespace.useMemo(() => {
162
129
  const contentTypePermissions2 = userPermissions.filter(
@@ -167,7 +134,14 @@ const DocumentRBAC = ({ children, permissions }) => {
167
134
  return { ...acc, [action]: [permission] };
168
135
  }, {});
169
136
  }, [slug, userPermissions]);
170
- const { isLoading, allowedActions } = strapiAdmin.useRBAC(contentTypePermissions, permissions ?? void 0);
137
+ const { isLoading, allowedActions } = strapiAdmin.useRBAC(
138
+ contentTypePermissions,
139
+ permissions ?? void 0,
140
+ // TODO: useRBAC context should be typed and built differently
141
+ // We are passing raw query as context to the hook so that it can
142
+ // rely on the locale provided from DocumentRBAC for its permission calculations.
143
+ rawQuery
144
+ );
171
145
  const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
172
146
  const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
173
147
  const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
@@ -215,7 +189,8 @@ const contentManagerApi = strapiAdmin.adminApi.enhanceEndpoints({
215
189
  "Document",
216
190
  "InitialData",
217
191
  "HistoryVersion",
218
- "Relations"
192
+ "Relations",
193
+ "UidAvailability"
219
194
  ]
220
195
  });
221
196
  const documentApi = contentManagerApi.injectEndpoints({
@@ -229,7 +204,12 @@ const documentApi = contentManagerApi.injectEndpoints({
229
204
  params: query
230
205
  }
231
206
  }),
232
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
207
+ invalidatesTags: (_result, error, { model }) => {
208
+ if (error) {
209
+ return [];
210
+ }
211
+ return [{ type: "Document", id: `${model}_LIST` }];
212
+ }
233
213
  }),
234
214
  cloneDocument: builder.mutation({
235
215
  query: ({ model, sourceId, data, params }) => ({
@@ -240,7 +220,10 @@ const documentApi = contentManagerApi.injectEndpoints({
240
220
  params
241
221
  }
242
222
  }),
243
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
223
+ invalidatesTags: (_result, _error, { model }) => [
224
+ { type: "Document", id: `${model}_LIST` },
225
+ { type: "UidAvailability", id: model }
226
+ ]
244
227
  }),
245
228
  /**
246
229
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -257,7 +240,8 @@ const documentApi = contentManagerApi.injectEndpoints({
257
240
  }),
258
241
  invalidatesTags: (result, _error, { model }) => [
259
242
  { type: "Document", id: `${model}_LIST` },
260
- "Relations"
243
+ "Relations",
244
+ { type: "UidAvailability", id: model }
261
245
  ]
262
246
  }),
263
247
  deleteDocument: builder.mutation({
@@ -298,7 +282,8 @@ const documentApi = contentManagerApi.injectEndpoints({
298
282
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
299
283
  },
300
284
  { type: "Document", id: `${model}_LIST` },
301
- "Relations"
285
+ "Relations",
286
+ { type: "UidAvailability", id: model }
302
287
  ];
303
288
  }
304
289
  }),
@@ -316,6 +301,7 @@ const documentApi = contentManagerApi.injectEndpoints({
316
301
  }),
317
302
  providesTags: (result, _error, arg) => {
318
303
  return [
304
+ { type: "Document", id: `ALL_LIST` },
319
305
  { type: "Document", id: `${arg.model}_LIST` },
320
306
  ...result?.results.map(({ documentId }) => ({
321
307
  type: "Document",
@@ -354,6 +340,11 @@ const documentApi = contentManagerApi.injectEndpoints({
354
340
  {
355
341
  type: "Document",
356
342
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
343
+ },
344
+ // Make it easy to invalidate all individual documents queries for a model
345
+ {
346
+ type: "Document",
347
+ id: `${model}_ALL_ITEMS`
357
348
  }
358
349
  ];
359
350
  }
@@ -417,8 +408,21 @@ const documentApi = contentManagerApi.injectEndpoints({
417
408
  type: "Document",
418
409
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
419
410
  },
420
- "Relations"
411
+ "Relations",
412
+ { type: "UidAvailability", id: model }
421
413
  ];
414
+ },
415
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
416
+ const patchResult = dispatch(
417
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
418
+ Object.assign(draft.data, data);
419
+ })
420
+ );
421
+ try {
422
+ await queryFulfilled;
423
+ } catch {
424
+ patchResult.undo();
425
+ }
422
426
  }
423
427
  }),
424
428
  unpublishDocument: builder.mutation({
@@ -488,20 +492,39 @@ const buildValidParams = (query) => {
488
492
  const isBaseQueryError = (error) => {
489
493
  return error.name !== void 0;
490
494
  };
491
- const createYupSchema = (attributes = {}, components = {}) => {
495
+ const arrayValidator = (attribute, options) => ({
496
+ message: strapiAdmin.translatedErrors.required,
497
+ test(value) {
498
+ if (options.status === "draft") {
499
+ return true;
500
+ }
501
+ if (!attribute.required) {
502
+ return true;
503
+ }
504
+ if (!value) {
505
+ return false;
506
+ }
507
+ if (Array.isArray(value) && value.length === 0) {
508
+ return false;
509
+ }
510
+ return true;
511
+ }
512
+ });
513
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
492
514
  const createModelSchema = (attributes2) => yup__namespace.object().shape(
493
515
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
494
516
  if (DOCUMENT_META_FIELDS.includes(name)) {
495
517
  return acc;
496
518
  }
497
519
  const validations = [
520
+ addNullableValidation,
498
521
  addRequiredValidation,
499
522
  addMinLengthValidation,
500
523
  addMaxLengthValidation,
501
524
  addMinValidation,
502
525
  addMaxValidation,
503
526
  addRegexValidation
504
- ].map((fn) => fn(attribute));
527
+ ].map((fn) => fn(attribute, options));
505
528
  const transformSchema = pipe__default.default(...validations);
506
529
  switch (attribute.type) {
507
530
  case "component": {
@@ -511,12 +534,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
511
534
  ...acc,
512
535
  [name]: transformSchema(
513
536
  yup__namespace.array().of(createModelSchema(attributes3).nullable(false))
514
- )
537
+ ).test(arrayValidator(attribute, options))
515
538
  };
516
539
  } else {
517
540
  return {
518
541
  ...acc,
519
- [name]: transformSchema(createModelSchema(attributes3))
542
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
520
543
  };
521
544
  }
522
545
  }
@@ -538,7 +561,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
538
561
  }
539
562
  )
540
563
  )
541
- )
564
+ ).test(arrayValidator(attribute, options))
542
565
  };
543
566
  case "relation":
544
567
  return {
@@ -550,7 +573,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
550
573
  } else if (Array.isArray(value)) {
551
574
  return yup__namespace.array().of(
552
575
  yup__namespace.object().shape({
553
- id: yup__namespace.string().required()
576
+ id: yup__namespace.number().required()
554
577
  })
555
578
  );
556
579
  } else if (typeof value === "object") {
@@ -602,6 +625,14 @@ const createAttributeSchema = (attribute) => {
602
625
  if (!value || typeof value === "string" && value.length === 0) {
603
626
  return true;
604
627
  }
628
+ if (typeof value === "object") {
629
+ try {
630
+ JSON.stringify(value);
631
+ return true;
632
+ } catch (err) {
633
+ return false;
634
+ }
635
+ }
605
636
  try {
606
637
  JSON.parse(value);
607
638
  return true;
@@ -620,13 +651,7 @@ const createAttributeSchema = (attribute) => {
620
651
  return yup__namespace.mixed();
621
652
  }
622
653
  };
623
- const addRequiredValidation = (attribute) => (schema) => {
624
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
625
- return schema.min(1, strapiAdmin.translatedErrors.required);
626
- }
627
- if (attribute.required && attribute.type !== "relation") {
628
- return schema.required(strapiAdmin.translatedErrors.required);
629
- }
654
+ const nullableSchema = (schema) => {
630
655
  return schema?.nullable ? schema.nullable() : (
631
656
  // In some cases '.nullable' will not be available on the schema.
632
657
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -634,7 +659,22 @@ const addRequiredValidation = (attribute) => (schema) => {
634
659
  schema
635
660
  );
636
661
  };
637
- const addMinLengthValidation = (attribute) => (schema) => {
662
+ const addNullableValidation = () => (schema) => {
663
+ return nullableSchema(schema);
664
+ };
665
+ const addRequiredValidation = (attribute, options) => (schema) => {
666
+ if (options.status === "draft" || !attribute.required) {
667
+ return schema;
668
+ }
669
+ if (attribute.required && "required" in schema) {
670
+ return schema.required(strapiAdmin.translatedErrors.required);
671
+ }
672
+ return schema;
673
+ };
674
+ const addMinLengthValidation = (attribute, options) => (schema) => {
675
+ if (options.status === "draft") {
676
+ return schema;
677
+ }
638
678
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
639
679
  return schema.min(attribute.minLength, {
640
680
  ...strapiAdmin.translatedErrors.minLength,
@@ -656,32 +696,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
656
696
  }
657
697
  return schema;
658
698
  };
659
- const addMinValidation = (attribute) => (schema) => {
660
- if ("min" in attribute) {
699
+ const addMinValidation = (attribute, options) => (schema) => {
700
+ if (options.status === "draft") {
701
+ return schema;
702
+ }
703
+ if ("min" in attribute && "min" in schema) {
661
704
  const min = toInteger(attribute.min);
662
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
663
- if (!attribute.required && "test" in schema && min) {
664
- return schema.test(
665
- "custom-min",
666
- {
667
- ...strapiAdmin.translatedErrors.min,
668
- values: {
669
- min: attribute.min
670
- }
671
- },
672
- (value) => {
673
- if (!value) {
674
- return true;
675
- }
676
- if (Array.isArray(value) && value.length === 0) {
677
- return true;
678
- }
679
- return value.length >= min;
680
- }
681
- );
682
- }
683
- }
684
- if ("min" in schema && min) {
705
+ if (min) {
685
706
  return schema.min(min, {
686
707
  ...strapiAdmin.translatedErrors.min,
687
708
  values: {
@@ -799,19 +820,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
799
820
  }, {});
800
821
  return componentsByKey;
801
822
  };
802
- const useDocument = (args, opts) => {
823
+ const HOOKS = {
824
+ /**
825
+ * Hook that allows to mutate the displayed headers of the list view table
826
+ * @constant
827
+ * @type {string}
828
+ */
829
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
830
+ /**
831
+ * Hook that allows to mutate the CM's collection types links pre-set filters
832
+ * @constant
833
+ * @type {string}
834
+ */
835
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
836
+ /**
837
+ * Hook that allows to mutate the CM's edit view layout
838
+ * @constant
839
+ * @type {string}
840
+ */
841
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
842
+ /**
843
+ * Hook that allows to mutate the CM's single types links pre-set filters
844
+ * @constant
845
+ * @type {string}
846
+ */
847
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
848
+ };
849
+ const contentTypesApi = contentManagerApi.injectEndpoints({
850
+ endpoints: (builder) => ({
851
+ getContentTypeConfiguration: builder.query({
852
+ query: (uid) => ({
853
+ url: `/content-manager/content-types/${uid}/configuration`,
854
+ method: "GET"
855
+ }),
856
+ transformResponse: (response) => response.data,
857
+ providesTags: (_result, _error, uid) => [
858
+ { type: "ContentTypesConfiguration", id: uid },
859
+ { type: "ContentTypeSettings", id: "LIST" }
860
+ ]
861
+ }),
862
+ getAllContentTypeSettings: builder.query({
863
+ query: () => "/content-manager/content-types-settings",
864
+ transformResponse: (response) => response.data,
865
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
866
+ }),
867
+ updateContentTypeConfiguration: builder.mutation({
868
+ query: ({ uid, ...body }) => ({
869
+ url: `/content-manager/content-types/${uid}/configuration`,
870
+ method: "PUT",
871
+ data: body
872
+ }),
873
+ transformResponse: (response) => response.data,
874
+ invalidatesTags: (_result, _error, { uid }) => [
875
+ { type: "ContentTypesConfiguration", id: uid },
876
+ { type: "ContentTypeSettings", id: "LIST" },
877
+ // Is this necessary?
878
+ { type: "InitialData" }
879
+ ]
880
+ })
881
+ })
882
+ });
883
+ const {
884
+ useGetContentTypeConfigurationQuery,
885
+ useGetAllContentTypeSettingsQuery,
886
+ useUpdateContentTypeConfigurationMutation
887
+ } = contentTypesApi;
888
+ const checkIfAttributeIsDisplayable = (attribute) => {
889
+ const { type } = attribute;
890
+ if (type === "relation") {
891
+ return !attribute.relation.toLowerCase().includes("morph");
892
+ }
893
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
894
+ };
895
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
896
+ if (!mainFieldName) {
897
+ return void 0;
898
+ }
899
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
900
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
901
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
902
+ );
903
+ return {
904
+ name: mainFieldName,
905
+ type: mainFieldType ?? "string"
906
+ };
907
+ };
908
+ const DEFAULT_SETTINGS = {
909
+ bulkable: false,
910
+ filterable: false,
911
+ searchable: false,
912
+ pagination: false,
913
+ defaultSortBy: "",
914
+ defaultSortOrder: "asc",
915
+ mainField: "id",
916
+ pageSize: 10
917
+ };
918
+ const useDocumentLayout = (model) => {
919
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
920
+ const [{ query }] = strapiAdmin.useQueryParams();
921
+ const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
803
922
  const { toggleNotification } = strapiAdmin.useNotification();
804
923
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
924
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
805
925
  const {
806
- currentData: data,
807
- isLoading: isLoadingDocument,
808
- isFetching: isFetchingDocument,
809
- error
810
- } = useGetDocumentQuery(args, {
811
- ...opts,
812
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
813
- });
814
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
926
+ data,
927
+ isLoading: isLoadingConfigs,
928
+ error,
929
+ isFetching: isFetchingConfigs
930
+ } = useGetContentTypeConfigurationQuery(model);
931
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
815
932
  React__namespace.useEffect(() => {
816
933
  if (error) {
817
934
  toggleNotification({
@@ -819,39 +936,255 @@ const useDocument = (args, opts) => {
819
936
  message: formatAPIError(error)
820
937
  });
821
938
  }
822
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
823
- const validationSchema = React__namespace.useMemo(() => {
824
- if (!schema) {
825
- return null;
826
- }
827
- return createYupSchema(schema.attributes, components);
828
- }, [schema, components]);
829
- const validate = React__namespace.useCallback(
830
- (document) => {
831
- if (!validationSchema) {
832
- throw new Error(
833
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
834
- );
835
- }
836
- try {
837
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
838
- return null;
839
- } catch (error2) {
840
- if (error2 instanceof yup.ValidationError) {
841
- return strapiAdmin.getYupValidationErrors(error2);
842
- }
843
- throw error2;
844
- }
939
+ }, [error, formatAPIError, toggleNotification]);
940
+ const editLayout = React__namespace.useMemo(
941
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
942
+ layout: [],
943
+ components: {},
944
+ metadatas: {},
945
+ options: {},
946
+ settings: DEFAULT_SETTINGS
845
947
  },
846
- [validationSchema]
948
+ [data, isLoading, schemas, schema, components]
949
+ );
950
+ const listLayout = React__namespace.useMemo(() => {
951
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
952
+ layout: [],
953
+ metadatas: {},
954
+ options: {},
955
+ settings: DEFAULT_SETTINGS
956
+ };
957
+ }, [data, isLoading, schemas, schema, components]);
958
+ const { layout: edit } = React__namespace.useMemo(
959
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
960
+ layout: editLayout,
961
+ query
962
+ }),
963
+ [editLayout, query, runHookWaterfall]
847
964
  );
848
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
849
965
  return {
850
- components,
966
+ error,
967
+ isLoading,
968
+ edit,
969
+ list: listLayout
970
+ };
971
+ };
972
+ const useDocLayout = () => {
973
+ const { model } = useDoc();
974
+ return useDocumentLayout(model);
975
+ };
976
+ const formatEditLayout = (data, {
977
+ schemas,
978
+ schema,
979
+ components
980
+ }) => {
981
+ let currentPanelIndex = 0;
982
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
983
+ data.contentType.layouts.edit,
984
+ schema?.attributes,
985
+ data.contentType.metadatas,
986
+ { configurations: data.components, schemas: components },
987
+ schemas
988
+ ).reduce((panels, row) => {
989
+ if (row.some((field) => field.type === "dynamiczone")) {
990
+ panels.push([row]);
991
+ currentPanelIndex += 2;
992
+ } else {
993
+ if (!panels[currentPanelIndex]) {
994
+ panels.push([row]);
995
+ } else {
996
+ panels[currentPanelIndex].push(row);
997
+ }
998
+ }
999
+ return panels;
1000
+ }, []);
1001
+ const componentEditAttributes = Object.entries(data.components).reduce(
1002
+ (acc, [uid, configuration]) => {
1003
+ acc[uid] = {
1004
+ layout: convertEditLayoutToFieldLayouts(
1005
+ configuration.layouts.edit,
1006
+ components[uid].attributes,
1007
+ configuration.metadatas,
1008
+ { configurations: data.components, schemas: components }
1009
+ ),
1010
+ settings: {
1011
+ ...configuration.settings,
1012
+ icon: components[uid].info.icon,
1013
+ displayName: components[uid].info.displayName
1014
+ }
1015
+ };
1016
+ return acc;
1017
+ },
1018
+ {}
1019
+ );
1020
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
1021
+ (acc, [attribute, metadata]) => {
1022
+ return {
1023
+ ...acc,
1024
+ [attribute]: metadata.edit
1025
+ };
1026
+ },
1027
+ {}
1028
+ );
1029
+ return {
1030
+ layout: panelledEditAttributes,
1031
+ components: componentEditAttributes,
1032
+ metadatas: editMetadatas,
1033
+ settings: {
1034
+ ...data.contentType.settings,
1035
+ displayName: schema?.info.displayName
1036
+ },
1037
+ options: {
1038
+ ...schema?.options,
1039
+ ...schema?.pluginOptions,
1040
+ ...data.contentType.options
1041
+ }
1042
+ };
1043
+ };
1044
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1045
+ return rows.map(
1046
+ (row) => row.map((field) => {
1047
+ const attribute = attributes[field.name];
1048
+ if (!attribute) {
1049
+ return null;
1050
+ }
1051
+ const { edit: metadata } = metadatas[field.name];
1052
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1053
+ return {
1054
+ attribute,
1055
+ disabled: !metadata.editable,
1056
+ hint: metadata.description,
1057
+ label: metadata.label ?? "",
1058
+ name: field.name,
1059
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1060
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1061
+ schemas,
1062
+ components: components?.schemas ?? {}
1063
+ }),
1064
+ placeholder: metadata.placeholder ?? "",
1065
+ required: attribute.required ?? false,
1066
+ size: field.size,
1067
+ unique: "unique" in attribute ? attribute.unique : false,
1068
+ visible: metadata.visible ?? true,
1069
+ type: attribute.type
1070
+ };
1071
+ }).filter((field) => field !== null)
1072
+ );
1073
+ };
1074
+ const formatListLayout = (data, {
1075
+ schemas,
1076
+ schema,
1077
+ components
1078
+ }) => {
1079
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1080
+ (acc, [attribute, metadata]) => {
1081
+ return {
1082
+ ...acc,
1083
+ [attribute]: metadata.list
1084
+ };
1085
+ },
1086
+ {}
1087
+ );
1088
+ const listAttributes = convertListLayoutToFieldLayouts(
1089
+ data.contentType.layouts.list,
1090
+ schema?.attributes,
1091
+ listMetadatas,
1092
+ { configurations: data.components, schemas: components },
1093
+ schemas
1094
+ );
1095
+ return {
1096
+ layout: listAttributes,
1097
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1098
+ metadatas: listMetadatas,
1099
+ options: {
1100
+ ...schema?.options,
1101
+ ...schema?.pluginOptions,
1102
+ ...data.contentType.options
1103
+ }
1104
+ };
1105
+ };
1106
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1107
+ return columns.map((name) => {
1108
+ const attribute = attributes[name];
1109
+ if (!attribute) {
1110
+ return null;
1111
+ }
1112
+ const metadata = metadatas[name];
1113
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1114
+ return {
1115
+ attribute,
1116
+ label: metadata.label ?? "",
1117
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1118
+ schemas,
1119
+ components: components?.schemas ?? {}
1120
+ }),
1121
+ name,
1122
+ searchable: metadata.searchable ?? true,
1123
+ sortable: metadata.sortable ?? true
1124
+ };
1125
+ }).filter((field) => field !== null);
1126
+ };
1127
+ const useDocument = (args, opts) => {
1128
+ const { toggleNotification } = strapiAdmin.useNotification();
1129
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1130
+ const {
1131
+ currentData: data,
1132
+ isLoading: isLoadingDocument,
1133
+ isFetching: isFetchingDocument,
1134
+ error
1135
+ } = useGetDocumentQuery(args, {
1136
+ ...opts,
1137
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1138
+ });
1139
+ const {
1140
+ components,
1141
+ schema,
1142
+ schemas,
1143
+ isLoading: isLoadingSchema
1144
+ } = useContentTypeSchema(args.model);
1145
+ React__namespace.useEffect(() => {
1146
+ if (error) {
1147
+ toggleNotification({
1148
+ type: "danger",
1149
+ message: formatAPIError(error)
1150
+ });
1151
+ }
1152
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1153
+ const validationSchema = React__namespace.useMemo(() => {
1154
+ if (!schema) {
1155
+ return null;
1156
+ }
1157
+ return createYupSchema(schema.attributes, components);
1158
+ }, [schema, components]);
1159
+ const validate = React__namespace.useCallback(
1160
+ (document) => {
1161
+ if (!validationSchema) {
1162
+ throw new Error(
1163
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1164
+ );
1165
+ }
1166
+ try {
1167
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1168
+ return null;
1169
+ } catch (error2) {
1170
+ if (error2 instanceof yup.ValidationError) {
1171
+ return strapiAdmin.getYupValidationErrors(error2);
1172
+ }
1173
+ throw error2;
1174
+ }
1175
+ },
1176
+ [validationSchema]
1177
+ );
1178
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1179
+ const hasError = !!error;
1180
+ return {
1181
+ components,
851
1182
  document: data?.data,
852
1183
  meta: data?.meta,
853
1184
  isLoading,
1185
+ hasError,
854
1186
  schema,
1187
+ schemas,
855
1188
  validate
856
1189
  };
857
1190
  };
@@ -865,22 +1198,60 @@ const useDoc = () => {
865
1198
  if (!slug) {
866
1199
  throw new Error("Could not find model in url params");
867
1200
  }
1201
+ const document = useDocument(
1202
+ { documentId: origin || id, model: slug, collectionType, params },
1203
+ {
1204
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1205
+ }
1206
+ );
1207
+ const returnId = origin || id === "create" ? void 0 : id;
868
1208
  return {
869
1209
  collectionType,
870
1210
  model: slug,
871
- id: origin || id === "create" ? void 0 : id,
872
- ...useDocument(
873
- { documentId: origin || id, model: slug, collectionType, params },
874
- {
875
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
876
- }
877
- )
1211
+ id: returnId,
1212
+ ...document
1213
+ };
1214
+ };
1215
+ const useContentManagerContext = () => {
1216
+ const {
1217
+ collectionType,
1218
+ model,
1219
+ id,
1220
+ components,
1221
+ isLoading: isLoadingDoc,
1222
+ schema,
1223
+ schemas
1224
+ } = useDoc();
1225
+ const layout = useDocumentLayout(model);
1226
+ const form = strapiAdmin.useForm("useContentManagerContext", (state) => state);
1227
+ const isSingleType = collectionType === SINGLE_TYPES;
1228
+ const slug = model;
1229
+ const isCreatingEntry = id === "create";
1230
+ useContentTypeSchema();
1231
+ const isLoading = isLoadingDoc || layout.isLoading;
1232
+ const error = layout.error;
1233
+ return {
1234
+ error,
1235
+ isLoading,
1236
+ // Base metadata
1237
+ model,
1238
+ collectionType,
1239
+ id,
1240
+ slug,
1241
+ isCreatingEntry,
1242
+ isSingleType,
1243
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1244
+ // All schema infos
1245
+ components,
1246
+ contentType: schema,
1247
+ contentTypes: schemas,
1248
+ // Form state
1249
+ form,
1250
+ // layout infos
1251
+ layout
878
1252
  };
879
1253
  };
880
1254
  const prefixPluginTranslations = (trad, pluginId) => {
881
- if (!pluginId) {
882
- throw new TypeError("pluginId can't be empty");
883
- }
884
1255
  return Object.keys(trad).reduce((acc, current) => {
885
1256
  acc[`${pluginId}.${current}`] = trad[current];
886
1257
  return acc;
@@ -896,6 +1267,8 @@ const useDocumentActions = () => {
896
1267
  const { formatMessage } = reactIntl.useIntl();
897
1268
  const { trackUsage } = strapiAdmin.useTracking();
898
1269
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1270
+ const navigate = reactRouterDom.useNavigate();
1271
+ const setCurrentStep = strapiAdmin.useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
899
1272
  const [deleteDocument] = useDeleteDocumentMutation();
900
1273
  const _delete = React__namespace.useCallback(
901
1274
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1210,6 +1583,7 @@ const useDocumentActions = () => {
1210
1583
  defaultMessage: "Saved document"
1211
1584
  })
1212
1585
  });
1586
+ setCurrentStep("contentManager.success");
1213
1587
  return res.data;
1214
1588
  } catch (err) {
1215
1589
  toggleNotification({
@@ -1231,7 +1605,6 @@ const useDocumentActions = () => {
1231
1605
  sourceId
1232
1606
  });
1233
1607
  if ("error" in res) {
1234
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1235
1608
  return { error: res.error };
1236
1609
  }
1237
1610
  toggleNotification({
@@ -1250,7 +1623,7 @@ const useDocumentActions = () => {
1250
1623
  throw err;
1251
1624
  }
1252
1625
  },
1253
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1626
+ [autoCloneDocument, formatMessage, toggleNotification]
1254
1627
  );
1255
1628
  const [cloneDocument] = useCloneDocumentMutation();
1256
1629
  const clone = React__namespace.useCallback(
@@ -1276,6 +1649,7 @@ const useDocumentActions = () => {
1276
1649
  defaultMessage: "Cloned document"
1277
1650
  })
1278
1651
  });
1652
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1279
1653
  return res.data;
1280
1654
  } catch (err) {
1281
1655
  toggleNotification({
@@ -1286,7 +1660,7 @@ const useDocumentActions = () => {
1286
1660
  throw err;
1287
1661
  }
1288
1662
  },
1289
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1663
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1290
1664
  );
1291
1665
  const [getDoc] = useLazyGetDocumentQuery();
1292
1666
  const getDocument = React__namespace.useCallback(
@@ -1312,7 +1686,7 @@ const useDocumentActions = () => {
1312
1686
  };
1313
1687
  };
1314
1688
  const ProtectedHistoryPage = React.lazy(
1315
- () => Promise.resolve().then(() => require("./History-BblwXv7-.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1689
+ () => Promise.resolve().then(() => require("./History-DKS2aqqM.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1316
1690
  );
1317
1691
  const routes$1 = [
1318
1692
  {
@@ -1325,31 +1699,31 @@ const routes$1 = [
1325
1699
  }
1326
1700
  ];
1327
1701
  const ProtectedEditViewPage = React.lazy(
1328
- () => Promise.resolve().then(() => require("./EditViewPage-CmMi2Xsn.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1702
+ () => Promise.resolve().then(() => require("./EditViewPage-khfP2CR3.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1329
1703
  );
1330
1704
  const ProtectedListViewPage = React.lazy(
1331
- () => Promise.resolve().then(() => require("./ListViewPage-DFjn1DNW.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1705
+ () => Promise.resolve().then(() => require("./ListViewPage-DlUpqLIo.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1332
1706
  );
1333
1707
  const ProtectedListConfiguration = React.lazy(
1334
- () => Promise.resolve().then(() => require("./ListConfigurationPage-Cpy4QqNd.js")).then((mod) => ({
1708
+ () => Promise.resolve().then(() => require("./ListConfigurationPage-LSYSPZHH.js")).then((mod) => ({
1335
1709
  default: mod.ProtectedListConfiguration
1336
1710
  }))
1337
1711
  );
1338
1712
  const ProtectedEditConfigurationPage = React.lazy(
1339
- () => Promise.resolve().then(() => require("./EditConfigurationPage-CEGwxV-L.js")).then((mod) => ({
1713
+ () => Promise.resolve().then(() => require("./EditConfigurationPage-CFDe6SA1.js")).then((mod) => ({
1340
1714
  default: mod.ProtectedEditConfigurationPage
1341
1715
  }))
1342
1716
  );
1343
1717
  const ProtectedComponentConfigurationPage = React.lazy(
1344
- () => Promise.resolve().then(() => require("./ComponentConfigurationPage-BWQv6yRj.js")).then((mod) => ({
1718
+ () => Promise.resolve().then(() => require("./ComponentConfigurationPage-CpJNPBgk.js")).then((mod) => ({
1345
1719
  default: mod.ProtectedComponentConfigurationPage
1346
1720
  }))
1347
1721
  );
1348
1722
  const NoPermissions = React.lazy(
1349
- () => Promise.resolve().then(() => require("./NoPermissionsPage-DKLmDZnZ.js")).then((mod) => ({ default: mod.NoPermissions }))
1723
+ () => Promise.resolve().then(() => require("./NoPermissionsPage-DLfPsA0Q.js")).then((mod) => ({ default: mod.NoPermissions }))
1350
1724
  );
1351
1725
  const NoContentType = React.lazy(
1352
- () => Promise.resolve().then(() => require("./NoContentTypePage-C-3ykoxs.js")).then((mod) => ({ default: mod.NoContentType }))
1726
+ () => Promise.resolve().then(() => require("./NoContentTypePage-m8wt3sf6.js")).then((mod) => ({ default: mod.NoContentType }))
1353
1727
  );
1354
1728
  const CollectionTypePages = () => {
1355
1729
  const { collectionType } = reactRouterDom.useParams();
@@ -1463,12 +1837,14 @@ const DocumentActionButton = (action) => {
1463
1837
  /* @__PURE__ */ jsxRuntime.jsx(
1464
1838
  designSystem.Button,
1465
1839
  {
1466
- flex: 1,
1840
+ flex: "auto",
1467
1841
  startIcon: action.icon,
1468
1842
  disabled: action.disabled,
1469
1843
  onClick: handleClick(action),
1470
1844
  justifyContent: "center",
1471
1845
  variant: action.variant || "default",
1846
+ paddingTop: "7px",
1847
+ paddingBottom: "7px",
1472
1848
  children: action.label
1473
1849
  }
1474
1850
  ),
@@ -1476,7 +1852,7 @@ const DocumentActionButton = (action) => {
1476
1852
  DocumentActionConfirmDialog,
1477
1853
  {
1478
1854
  ...action.dialog,
1479
- variant: action.variant,
1855
+ variant: action.dialog?.variant ?? action.variant,
1480
1856
  isOpen: dialogId === action.id,
1481
1857
  onClose: handleClose
1482
1858
  }
@@ -1533,9 +1909,9 @@ const DocumentActionsMenu = ({
1533
1909
  disabled: isDisabled,
1534
1910
  size: "S",
1535
1911
  endIcon: null,
1536
- paddingTop: "7px",
1537
- paddingLeft: "9px",
1538
- paddingRight: "9px",
1912
+ paddingTop: "4px",
1913
+ paddingLeft: "7px",
1914
+ paddingRight: "7px",
1539
1915
  variant,
1540
1916
  children: [
1541
1917
  /* @__PURE__ */ jsxRuntime.jsx(Icons.More, { "aria-hidden": true, focusable: false }),
@@ -1546,7 +1922,7 @@ const DocumentActionsMenu = ({
1546
1922
  ]
1547
1923
  }
1548
1924
  ),
1549
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1925
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1550
1926
  actions2.map((action) => {
1551
1927
  return /* @__PURE__ */ jsxRuntime.jsx(
1552
1928
  designSystem.Menu.Item,
@@ -1555,10 +1931,25 @@ const DocumentActionsMenu = ({
1555
1931
  onSelect: handleClick(action),
1556
1932
  display: "block",
1557
1933
  children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", gap: 4, children: [
1558
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1559
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1560
- action.label
1561
- ] }),
1934
+ /* @__PURE__ */ jsxRuntime.jsxs(
1935
+ designSystem.Flex,
1936
+ {
1937
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1938
+ gap: 2,
1939
+ tag: "span",
1940
+ children: [
1941
+ /* @__PURE__ */ jsxRuntime.jsx(
1942
+ designSystem.Flex,
1943
+ {
1944
+ tag: "span",
1945
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1946
+ children: action.icon
1947
+ }
1948
+ ),
1949
+ action.label
1950
+ ]
1951
+ }
1952
+ ),
1562
1953
  action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsxRuntime.jsx(
1563
1954
  designSystem.Flex,
1564
1955
  {
@@ -1655,11 +2046,11 @@ const DocumentActionConfirmDialog = ({
1655
2046
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
1656
2047
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: content }),
1657
2048
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
1658
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", children: formatMessage({
2049
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1659
2050
  id: "app.components.Button.cancel",
1660
2051
  defaultMessage: "Cancel"
1661
2052
  }) }) }),
1662
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, children: formatMessage({
2053
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1663
2054
  id: "app.components.Button.confirm",
1664
2055
  defaultMessage: "Confirm"
1665
2056
  }) })
@@ -1682,10 +2073,22 @@ const DocumentActionModal = ({
1682
2073
  };
1683
2074
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
1684
2075
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: title }) }),
1685
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content }),
1686
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Footer, { children: typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer })
2076
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: Content }),
2077
+ typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer
1687
2078
  ] }) });
1688
2079
  };
2080
+ const transformData = (data) => {
2081
+ if (Array.isArray(data)) {
2082
+ return data.map(transformData);
2083
+ }
2084
+ if (typeof data === "object" && data !== null) {
2085
+ if ("apiData" in data) {
2086
+ return data.apiData;
2087
+ }
2088
+ return mapValues__default.default(transformData)(data);
2089
+ }
2090
+ return data;
2091
+ };
1689
2092
  const PublishAction$1 = ({
1690
2093
  activeTab,
1691
2094
  documentId,
@@ -1698,13 +2101,17 @@ const PublishAction$1 = ({
1698
2101
  const navigate = reactRouterDom.useNavigate();
1699
2102
  const { toggleNotification } = strapiAdmin.useNotification();
1700
2103
  const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
2104
+ const isListView = reactRouterDom.useMatch(LIST_PATH) !== null;
1701
2105
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
1702
2106
  const { formatMessage } = reactIntl.useIntl();
1703
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1704
- "PublishAction",
1705
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1706
- );
2107
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1707
2108
  const { publish } = useDocumentActions();
2109
+ const [
2110
+ countDraftRelations,
2111
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2112
+ ] = useLazyGetDraftRelationCountQuery();
2113
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React__namespace.useState(0);
2114
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React__namespace.useState(0);
1708
2115
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1709
2116
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1710
2117
  const modified = strapiAdmin.useForm("PublishAction", ({ modified: modified2 }) => modified2);
@@ -1713,62 +2120,144 @@ const PublishAction$1 = ({
1713
2120
  const validate = strapiAdmin.useForm("PublishAction", (state) => state.validate);
1714
2121
  const setErrors = strapiAdmin.useForm("PublishAction", (state) => state.setErrors);
1715
2122
  const formValues = strapiAdmin.useForm("PublishAction", ({ values }) => values);
1716
- const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1717
- if (!schema?.options?.draftAndPublish) {
1718
- return null;
1719
- }
1720
- return {
1721
- /**
1722
- * Disabled when:
1723
- * - currently if you're cloning a document we don't support publish & clone at the same time.
1724
- * - the form is submitting
1725
- * - the active tab is the published tab
1726
- * - the document is already published & not modified
1727
- * - the document is being created & not modified
1728
- * - the user doesn't have the permission to publish
1729
- * - the user doesn't have the permission to create a new document
1730
- * - the user doesn't have the permission to update the document
1731
- */
1732
- disabled: isCloning || isSubmitting || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2123
+ React__namespace.useEffect(() => {
2124
+ if (isErrorDraftRelations) {
2125
+ toggleNotification({
2126
+ type: "danger",
2127
+ message: formatMessage({
2128
+ id: getTranslation("error.records.fetch-draft-relatons"),
2129
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2130
+ })
2131
+ });
2132
+ }
2133
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2134
+ React__namespace.useEffect(() => {
2135
+ const localDraftRelations = /* @__PURE__ */ new Set();
2136
+ const extractDraftRelations = (data) => {
2137
+ const relations = data.connect || [];
2138
+ relations.forEach((relation) => {
2139
+ if (relation.status === "draft") {
2140
+ localDraftRelations.add(relation.id);
2141
+ }
2142
+ });
2143
+ };
2144
+ const traverseAndExtract = (data) => {
2145
+ Object.entries(data).forEach(([key, value]) => {
2146
+ if (key === "connect" && Array.isArray(value)) {
2147
+ extractDraftRelations({ connect: value });
2148
+ } else if (typeof value === "object" && value !== null) {
2149
+ traverseAndExtract(value);
2150
+ }
2151
+ });
2152
+ };
2153
+ if (!documentId || modified) {
2154
+ traverseAndExtract(formValues);
2155
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2156
+ }
2157
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2158
+ React__namespace.useEffect(() => {
2159
+ if (!document || !document.documentId || isListView) {
2160
+ return;
2161
+ }
2162
+ const fetchDraftRelationsCount = async () => {
2163
+ const { data, error } = await countDraftRelations({
2164
+ collectionType,
2165
+ model,
2166
+ documentId,
2167
+ params
2168
+ });
2169
+ if (error) {
2170
+ throw error;
2171
+ }
2172
+ if (data) {
2173
+ setServerCountOfDraftRelations(data.data);
2174
+ }
2175
+ };
2176
+ fetchDraftRelationsCount();
2177
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
2178
+ const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
2179
+ if (!schema?.options?.draftAndPublish) {
2180
+ return null;
2181
+ }
2182
+ const performPublish = async () => {
2183
+ setSubmitting(true);
2184
+ try {
2185
+ const { errors } = await validate(true, {
2186
+ status: "published"
2187
+ });
2188
+ if (errors) {
2189
+ toggleNotification({
2190
+ type: "danger",
2191
+ message: formatMessage({
2192
+ id: "content-manager.validation.error",
2193
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2194
+ })
2195
+ });
2196
+ return;
2197
+ }
2198
+ const res = await publish(
2199
+ {
2200
+ collectionType,
2201
+ model,
2202
+ documentId,
2203
+ params
2204
+ },
2205
+ transformData(formValues)
2206
+ );
2207
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2208
+ navigate({
2209
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2210
+ search: rawQuery
2211
+ });
2212
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2213
+ setErrors(formatValidationErrors(res.error));
2214
+ }
2215
+ } finally {
2216
+ setSubmitting(false);
2217
+ }
2218
+ };
2219
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2220
+ const enableDraftRelationsCount = false;
2221
+ const hasDraftRelations = enableDraftRelationsCount;
2222
+ return {
2223
+ /**
2224
+ * Disabled when:
2225
+ * - currently if you're cloning a document we don't support publish & clone at the same time.
2226
+ * - the form is submitting
2227
+ * - the active tab is the published tab
2228
+ * - the document is already published & not modified
2229
+ * - the document is being created & not modified
2230
+ * - the user doesn't have the permission to publish
2231
+ */
2232
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1733
2233
  label: formatMessage({
1734
2234
  id: "app.utils.publish",
1735
2235
  defaultMessage: "Publish"
1736
2236
  }),
1737
2237
  onClick: async () => {
1738
- setSubmitting(true);
1739
- try {
1740
- const { errors } = await validate();
1741
- if (errors) {
1742
- toggleNotification({
1743
- type: "danger",
1744
- message: formatMessage({
1745
- id: "content-manager.validation.error",
1746
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1747
- })
1748
- });
1749
- return;
1750
- }
1751
- const res = await publish(
1752
- {
1753
- collectionType,
1754
- model,
1755
- documentId,
1756
- params
1757
- },
1758
- formValues
1759
- );
1760
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1761
- navigate({
1762
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1763
- search: rawQuery
1764
- });
1765
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1766
- setErrors(formatValidationErrors(res.error));
2238
+ await performPublish();
2239
+ },
2240
+ dialog: hasDraftRelations ? {
2241
+ type: "dialog",
2242
+ variant: "danger",
2243
+ footer: null,
2244
+ title: formatMessage({
2245
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2246
+ defaultMessage: "Confirmation"
2247
+ }),
2248
+ content: formatMessage(
2249
+ {
2250
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2251
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2252
+ },
2253
+ {
2254
+ count: totalDraftRelations
1767
2255
  }
1768
- } finally {
1769
- setSubmitting(false);
2256
+ ),
2257
+ onConfirm: async () => {
2258
+ await performPublish();
1770
2259
  }
1771
- }
2260
+ } : void 0
1772
2261
  };
1773
2262
  };
1774
2263
  PublishAction$1.type = "publish";
@@ -1784,10 +2273,6 @@ const UpdateAction = ({
1784
2273
  const cloneMatch = reactRouterDom.useMatch(CLONE_PATH);
1785
2274
  const isCloning = cloneMatch !== null;
1786
2275
  const { formatMessage } = reactIntl.useIntl();
1787
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1788
- canCreate: canCreate2,
1789
- canUpdate: canUpdate2
1790
- }));
1791
2276
  const { create, update, clone } = useDocumentActions();
1792
2277
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1793
2278
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
@@ -1804,10 +2289,8 @@ const UpdateAction = ({
1804
2289
  * - the form is submitting
1805
2290
  * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1806
2291
  * - the active tab is the published tab
1807
- * - the user doesn't have the permission to create a new document
1808
- * - the user doesn't have the permission to update the document
1809
2292
  */
1810
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
2293
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1811
2294
  label: formatMessage({
1812
2295
  id: "content-manager.containers.Edit.save",
1813
2296
  defaultMessage: "Save"
@@ -1815,7 +2298,9 @@ const UpdateAction = ({
1815
2298
  onClick: async () => {
1816
2299
  setSubmitting(true);
1817
2300
  try {
1818
- const { errors } = await validate();
2301
+ const { errors } = await validate(true, {
2302
+ status: "draft"
2303
+ });
1819
2304
  if (errors) {
1820
2305
  toggleNotification({
1821
2306
  type: "danger",
@@ -1833,13 +2318,16 @@ const UpdateAction = ({
1833
2318
  documentId: cloneMatch.params.origin,
1834
2319
  params
1835
2320
  },
1836
- document
2321
+ transformData(document)
1837
2322
  );
1838
2323
  if ("data" in res) {
1839
- navigate({
1840
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1841
- search: rawQuery
1842
- });
2324
+ navigate(
2325
+ {
2326
+ pathname: `../${res.data.documentId}`,
2327
+ search: rawQuery
2328
+ },
2329
+ { relative: "path" }
2330
+ );
1843
2331
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1844
2332
  setErrors(formatValidationErrors(res.error));
1845
2333
  }
@@ -1851,7 +2339,7 @@ const UpdateAction = ({
1851
2339
  documentId,
1852
2340
  params
1853
2341
  },
1854
- document
2342
+ transformData(document)
1855
2343
  );
1856
2344
  if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1857
2345
  setErrors(formatValidationErrors(res.error));
@@ -1864,15 +2352,15 @@ const UpdateAction = ({
1864
2352
  model,
1865
2353
  params
1866
2354
  },
1867
- document
2355
+ transformData(document)
1868
2356
  );
1869
2357
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1870
2358
  navigate(
1871
2359
  {
1872
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2360
+ pathname: `../${res.data.documentId}`,
1873
2361
  search: rawQuery
1874
2362
  },
1875
- { replace: true }
2363
+ { replace: true, relative: "path" }
1876
2364
  );
1877
2365
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1878
2366
  setErrors(formatValidationErrors(res.error));
@@ -1917,7 +2405,7 @@ const UnpublishAction$1 = ({
1917
2405
  id: "app.utils.unpublish",
1918
2406
  defaultMessage: "Unpublish"
1919
2407
  }),
1920
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2408
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
1921
2409
  onClick: async () => {
1922
2410
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1923
2411
  if (!documentId) {
@@ -2029,7 +2517,7 @@ const DiscardAction = ({
2029
2517
  id: "content-manager.actions.discard.label",
2030
2518
  defaultMessage: "Discard changes"
2031
2519
  }),
2032
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2520
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
2033
2521
  position: ["panel", "table-row"],
2034
2522
  variant: "danger",
2035
2523
  dialog: {
@@ -2057,11 +2545,6 @@ const DiscardAction = ({
2057
2545
  };
2058
2546
  };
2059
2547
  DiscardAction.type = "discard";
2060
- const StyledCrossCircle = styledComponents.styled(Icons.CrossCircle)`
2061
- path {
2062
- fill: currentColor;
2063
- }
2064
- `;
2065
2548
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2066
2549
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2067
2550
  const RelativeTime = React__namespace.forwardRef(
@@ -2109,7 +2592,7 @@ const getDisplayName = ({
2109
2592
  };
2110
2593
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2111
2594
  const DocumentStatus = ({ status = "draft", ...restProps }) => {
2112
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2595
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2113
2596
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2114
2597
  };
2115
2598
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
@@ -2119,23 +2602,13 @@ const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2119
2602
  id: "content-manager.containers.edit.title.new",
2120
2603
  defaultMessage: "Create an entry"
2121
2604
  }) : documentTitle;
2122
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 8, paddingBottom: 4, gap: 3, children: [
2605
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2123
2606
  /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.BackButton, {}),
2124
- /* @__PURE__ */ jsxRuntime.jsxs(
2125
- designSystem.Flex,
2126
- {
2127
- width: "100%",
2128
- justifyContent: "space-between",
2129
- paddingTop: 1,
2130
- gap: "80px",
2131
- alignItems: "flex-start",
2132
- children: [
2133
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2134
- /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2135
- ]
2136
- }
2137
- ),
2138
- status ? /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) : null
2607
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2608
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2609
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2610
+ ] }),
2611
+ status ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2139
2612
  ] });
2140
2613
  };
2141
2614
  const HeaderToolbar = () => {
@@ -2218,12 +2691,12 @@ const Information = ({ activeTab }) => {
2218
2691
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2219
2692
  label: formatMessage({
2220
2693
  id: "content-manager.containers.edit.information.last-published.label",
2221
- defaultMessage: "Last published"
2694
+ defaultMessage: "Published"
2222
2695
  }),
2223
2696
  value: formatMessage(
2224
2697
  {
2225
2698
  id: "content-manager.containers.edit.information.last-published.value",
2226
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2699
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2227
2700
  },
2228
2701
  {
2229
2702
  time: /* @__PURE__ */ jsxRuntime.jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2236,12 +2709,12 @@ const Information = ({ activeTab }) => {
2236
2709
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2237
2710
  label: formatMessage({
2238
2711
  id: "content-manager.containers.edit.information.last-draft.label",
2239
- defaultMessage: "Last draft"
2712
+ defaultMessage: "Updated"
2240
2713
  }),
2241
2714
  value: formatMessage(
2242
2715
  {
2243
2716
  id: "content-manager.containers.edit.information.last-draft.value",
2244
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2717
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2245
2718
  },
2246
2719
  {
2247
2720
  time: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2259,12 +2732,12 @@ const Information = ({ activeTab }) => {
2259
2732
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2260
2733
  label: formatMessage({
2261
2734
  id: "content-manager.containers.edit.information.document.label",
2262
- defaultMessage: "Document"
2735
+ defaultMessage: "Created"
2263
2736
  }),
2264
2737
  value: formatMessage(
2265
2738
  {
2266
2739
  id: "content-manager.containers.edit.information.document.value",
2267
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2740
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2268
2741
  },
2269
2742
  {
2270
2743
  time: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2302,25 +2775,77 @@ const Information = ({ activeTab }) => {
2302
2775
  );
2303
2776
  };
2304
2777
  const HeaderActions = ({ actions: actions2 }) => {
2305
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: actions2.map((action) => {
2306
- if ("options" in action) {
2778
+ const [dialogId, setDialogId] = React__namespace.useState(null);
2779
+ const handleClick = (action) => async (e) => {
2780
+ if (!("options" in action)) {
2781
+ const { onClick = () => false, dialog, id } = action;
2782
+ const muteDialog = await onClick(e);
2783
+ if (dialog && !muteDialog) {
2784
+ e.preventDefault();
2785
+ setDialogId(id);
2786
+ }
2787
+ }
2788
+ };
2789
+ const handleClose = () => {
2790
+ setDialogId(null);
2791
+ };
2792
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, children: actions2.map((action) => {
2793
+ if (action.options) {
2307
2794
  return /* @__PURE__ */ jsxRuntime.jsx(
2308
2795
  designSystem.SingleSelect,
2309
2796
  {
2310
2797
  size: "S",
2311
- disabled: action.disabled,
2312
- "aria-label": action.label,
2313
2798
  onChange: action.onSelect,
2314
- value: action.value,
2799
+ "aria-label": action.label,
2800
+ ...action,
2315
2801
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { ...option, children: label }, option.value))
2316
2802
  },
2317
2803
  action.id
2318
2804
  );
2319
2805
  } else {
2320
- return null;
2806
+ if (action.type === "icon") {
2807
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
2808
+ /* @__PURE__ */ jsxRuntime.jsx(
2809
+ designSystem.IconButton,
2810
+ {
2811
+ disabled: action.disabled,
2812
+ label: action.label,
2813
+ size: "S",
2814
+ onClick: handleClick(action),
2815
+ children: action.icon
2816
+ }
2817
+ ),
2818
+ action.dialog ? /* @__PURE__ */ jsxRuntime.jsx(
2819
+ HeaderActionDialog,
2820
+ {
2821
+ ...action.dialog,
2822
+ isOpen: dialogId === action.id,
2823
+ onClose: handleClose
2824
+ }
2825
+ ) : null
2826
+ ] }, action.id);
2827
+ }
2321
2828
  }
2322
2829
  }) });
2323
2830
  };
2831
+ const HeaderActionDialog = ({
2832
+ onClose,
2833
+ onCancel,
2834
+ title,
2835
+ content: Content,
2836
+ isOpen
2837
+ }) => {
2838
+ const handleClose = async () => {
2839
+ if (onCancel) {
2840
+ await onCancel();
2841
+ }
2842
+ onClose();
2843
+ };
2844
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2845
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2846
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content
2847
+ ] }) });
2848
+ };
2324
2849
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2325
2850
  const navigate = reactRouterDom.useNavigate();
2326
2851
  const { formatMessage } = reactIntl.useIntl();
@@ -2361,12 +2886,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2361
2886
  const { delete: deleteAction } = useDocumentActions();
2362
2887
  const { toggleNotification } = strapiAdmin.useNotification();
2363
2888
  const setSubmitting = strapiAdmin.useForm("DeleteAction", (state) => state.setSubmitting);
2889
+ const isLocalized = document?.locale != null;
2364
2890
  return {
2365
2891
  disabled: !canDelete || !document,
2366
- label: formatMessage({
2367
- id: "content-manager.actions.delete.label",
2368
- defaultMessage: "Delete document"
2369
- }),
2892
+ label: formatMessage(
2893
+ {
2894
+ id: "content-manager.actions.delete.label",
2895
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2896
+ },
2897
+ { isLocalized }
2898
+ ),
2370
2899
  icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2371
2900
  dialog: {
2372
2901
  type: "dialog",
@@ -2415,410 +2944,108 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2415
2944
  setSubmitting(false);
2416
2945
  }
2417
2946
  }
2418
- }
2419
- },
2420
- variant: "danger",
2421
- position: ["header", "table-row"]
2422
- };
2423
- };
2424
- DeleteAction$1.type = "delete";
2425
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2426
- const Panels = () => {
2427
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2428
- const [
2429
- {
2430
- query: { status }
2431
- }
2432
- ] = strapiAdmin.useQueryParams({
2433
- status: "draft"
2434
- });
2435
- const { model, id, document, meta, collectionType } = useDoc();
2436
- const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2437
- const props = {
2438
- activeTab: status,
2439
- model,
2440
- documentId: id,
2441
- document: isCloning ? void 0 : document,
2442
- meta: isCloning ? void 0 : meta,
2443
- collectionType
2444
- };
2445
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2446
- strapiAdmin.DescriptionComponentRenderer,
2447
- {
2448
- props,
2449
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2450
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2451
- }
2452
- ) });
2453
- };
2454
- const ActionsPanel = () => {
2455
- const { formatMessage } = reactIntl.useIntl();
2456
- return {
2457
- title: formatMessage({
2458
- id: "content-manager.containers.edit.panels.default.title",
2459
- defaultMessage: "Document"
2460
- }),
2461
- content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2462
- };
2463
- };
2464
- ActionsPanel.type = "actions";
2465
- const ActionsPanelContent = () => {
2466
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2467
- const [
2468
- {
2469
- query: { status = "draft" }
2470
- }
2471
- ] = strapiAdmin.useQueryParams();
2472
- const { model, id, document, meta, collectionType } = useDoc();
2473
- const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
2474
- const props = {
2475
- activeTab: status,
2476
- model,
2477
- documentId: id,
2478
- document: isCloning ? void 0 : document,
2479
- meta: isCloning ? void 0 : meta,
2480
- collectionType
2481
- };
2482
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
2483
- /* @__PURE__ */ jsxRuntime.jsx(
2484
- strapiAdmin.DescriptionComponentRenderer,
2485
- {
2486
- props,
2487
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2488
- children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
2489
- }
2490
- ),
2491
- /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
2492
- ] });
2493
- };
2494
- const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
2495
- return /* @__PURE__ */ jsxRuntime.jsxs(
2496
- designSystem.Flex,
2497
- {
2498
- ref,
2499
- tag: "aside",
2500
- "aria-labelledby": "additional-information",
2501
- background: "neutral0",
2502
- borderColor: "neutral150",
2503
- hasRadius: true,
2504
- paddingBottom: 4,
2505
- paddingLeft: 4,
2506
- paddingRight: 4,
2507
- paddingTop: 4,
2508
- shadow: "tableShadow",
2509
- gap: 3,
2510
- direction: "column",
2511
- justifyContent: "stretch",
2512
- alignItems: "flex-start",
2513
- children: [
2514
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2515
- children
2516
- ]
2517
- }
2518
- );
2519
- });
2520
- const HOOKS = {
2521
- /**
2522
- * Hook that allows to mutate the displayed headers of the list view table
2523
- * @constant
2524
- * @type {string}
2525
- */
2526
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2527
- /**
2528
- * Hook that allows to mutate the CM's collection types links pre-set filters
2529
- * @constant
2530
- * @type {string}
2531
- */
2532
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2533
- /**
2534
- * Hook that allows to mutate the CM's edit view layout
2535
- * @constant
2536
- * @type {string}
2537
- */
2538
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2539
- /**
2540
- * Hook that allows to mutate the CM's single types links pre-set filters
2541
- * @constant
2542
- * @type {string}
2543
- */
2544
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2545
- };
2546
- const contentTypesApi = contentManagerApi.injectEndpoints({
2547
- endpoints: (builder) => ({
2548
- getContentTypeConfiguration: builder.query({
2549
- query: (uid) => ({
2550
- url: `/content-manager/content-types/${uid}/configuration`,
2551
- method: "GET"
2552
- }),
2553
- transformResponse: (response) => response.data,
2554
- providesTags: (_result, _error, uid) => [
2555
- { type: "ContentTypesConfiguration", id: uid },
2556
- { type: "ContentTypeSettings", id: "LIST" }
2557
- ]
2558
- }),
2559
- getAllContentTypeSettings: builder.query({
2560
- query: () => "/content-manager/content-types-settings",
2561
- transformResponse: (response) => response.data,
2562
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2563
- }),
2564
- updateContentTypeConfiguration: builder.mutation({
2565
- query: ({ uid, ...body }) => ({
2566
- url: `/content-manager/content-types/${uid}/configuration`,
2567
- method: "PUT",
2568
- data: body
2569
- }),
2570
- transformResponse: (response) => response.data,
2571
- invalidatesTags: (_result, _error, { uid }) => [
2572
- { type: "ContentTypesConfiguration", id: uid },
2573
- { type: "ContentTypeSettings", id: "LIST" },
2574
- // Is this necessary?
2575
- { type: "InitialData" }
2576
- ]
2577
- })
2578
- })
2579
- });
2580
- const {
2581
- useGetContentTypeConfigurationQuery,
2582
- useGetAllContentTypeSettingsQuery,
2583
- useUpdateContentTypeConfigurationMutation
2584
- } = contentTypesApi;
2585
- const checkIfAttributeIsDisplayable = (attribute) => {
2586
- const { type } = attribute;
2587
- if (type === "relation") {
2588
- return !attribute.relation.toLowerCase().includes("morph");
2589
- }
2590
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2591
- };
2592
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2593
- if (!mainFieldName) {
2594
- return void 0;
2595
- }
2596
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2597
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2598
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2599
- );
2600
- return {
2601
- name: mainFieldName,
2602
- type: mainFieldType ?? "string"
2603
- };
2604
- };
2605
- const DEFAULT_SETTINGS = {
2606
- bulkable: false,
2607
- filterable: false,
2608
- searchable: false,
2609
- pagination: false,
2610
- defaultSortBy: "",
2611
- defaultSortOrder: "asc",
2612
- mainField: "id",
2613
- pageSize: 10
2614
- };
2615
- const useDocumentLayout = (model) => {
2616
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2617
- const [{ query }] = strapiAdmin.useQueryParams();
2618
- const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2619
- const { toggleNotification } = strapiAdmin.useNotification();
2620
- const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
2621
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2622
- const {
2623
- data,
2624
- isLoading: isLoadingConfigs,
2625
- error,
2626
- isFetching: isFetchingConfigs
2627
- } = useGetContentTypeConfigurationQuery(model);
2628
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2629
- React__namespace.useEffect(() => {
2630
- if (error) {
2631
- toggleNotification({
2632
- type: "danger",
2633
- message: formatAPIError(error)
2634
- });
2635
- }
2636
- }, [error, formatAPIError, toggleNotification]);
2637
- const editLayout = React__namespace.useMemo(
2638
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2639
- layout: [],
2640
- components: {},
2641
- metadatas: {},
2642
- options: {},
2643
- settings: DEFAULT_SETTINGS
2644
- },
2645
- [data, isLoading, schemas, schema, components]
2646
- );
2647
- const listLayout = React__namespace.useMemo(() => {
2648
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2649
- layout: [],
2650
- metadatas: {},
2651
- options: {},
2652
- settings: DEFAULT_SETTINGS
2653
- };
2654
- }, [data, isLoading, schemas, schema, components]);
2655
- const { layout: edit } = React__namespace.useMemo(
2656
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2657
- layout: editLayout,
2658
- query
2659
- }),
2660
- [editLayout, query, runHookWaterfall]
2661
- );
2662
- return {
2663
- error,
2664
- isLoading,
2665
- edit,
2666
- list: listLayout
2667
- };
2668
- };
2669
- const useDocLayout = () => {
2670
- const { model } = useDoc();
2671
- return useDocumentLayout(model);
2672
- };
2673
- const formatEditLayout = (data, {
2674
- schemas,
2675
- schema,
2676
- components
2677
- }) => {
2678
- let currentPanelIndex = 0;
2679
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2680
- data.contentType.layouts.edit,
2681
- schema?.attributes,
2682
- data.contentType.metadatas,
2683
- { configurations: data.components, schemas: components },
2684
- schemas
2685
- ).reduce((panels, row) => {
2686
- if (row.some((field) => field.type === "dynamiczone")) {
2687
- panels.push([row]);
2688
- currentPanelIndex += 2;
2689
- } else {
2690
- if (!panels[currentPanelIndex]) {
2691
- panels.push([]);
2692
- }
2693
- panels[currentPanelIndex].push(row);
2694
- }
2695
- return panels;
2696
- }, []);
2697
- const componentEditAttributes = Object.entries(data.components).reduce(
2698
- (acc, [uid, configuration]) => {
2699
- acc[uid] = {
2700
- layout: convertEditLayoutToFieldLayouts(
2701
- configuration.layouts.edit,
2702
- components[uid].attributes,
2703
- configuration.metadatas
2704
- ),
2705
- settings: {
2706
- ...configuration.settings,
2707
- icon: components[uid].info.icon,
2708
- displayName: components[uid].info.displayName
2709
- }
2710
- };
2711
- return acc;
2712
- },
2713
- {}
2714
- );
2715
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2716
- (acc, [attribute, metadata]) => {
2717
- return {
2718
- ...acc,
2719
- [attribute]: metadata.edit
2720
- };
2721
- },
2722
- {}
2723
- );
2724
- return {
2725
- layout: panelledEditAttributes,
2726
- components: componentEditAttributes,
2727
- metadatas: editMetadatas,
2728
- settings: {
2729
- ...data.contentType.settings,
2730
- displayName: schema?.info.displayName
2731
- },
2732
- options: {
2733
- ...schema?.options,
2734
- ...schema?.pluginOptions,
2735
- ...data.contentType.options
2736
- }
2947
+ }
2948
+ },
2949
+ variant: "danger",
2950
+ position: ["header", "table-row"]
2737
2951
  };
2738
2952
  };
2739
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2740
- return rows.map(
2741
- (row) => row.map((field) => {
2742
- const attribute = attributes[field.name];
2743
- if (!attribute) {
2744
- return null;
2745
- }
2746
- const { edit: metadata } = metadatas[field.name];
2747
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2748
- return {
2749
- attribute,
2750
- disabled: !metadata.editable,
2751
- hint: metadata.description,
2752
- label: metadata.label ?? "",
2753
- name: field.name,
2754
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2755
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2756
- schemas,
2757
- components: components?.schemas ?? {}
2758
- }),
2759
- placeholder: metadata.placeholder ?? "",
2760
- required: attribute.required ?? false,
2761
- size: field.size,
2762
- unique: "unique" in attribute ? attribute.unique : false,
2763
- visible: metadata.visible ?? true,
2764
- type: attribute.type
2765
- };
2766
- }).filter((field) => field !== null)
2767
- );
2953
+ DeleteAction$1.type = "delete";
2954
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2955
+ const Panels = () => {
2956
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2957
+ const [
2958
+ {
2959
+ query: { status }
2960
+ }
2961
+ ] = strapiAdmin.useQueryParams({
2962
+ status: "draft"
2963
+ });
2964
+ const { model, id, document, meta, collectionType } = useDoc();
2965
+ const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2966
+ const props = {
2967
+ activeTab: status,
2968
+ model,
2969
+ documentId: id,
2970
+ document: isCloning ? void 0 : document,
2971
+ meta: isCloning ? void 0 : meta,
2972
+ collectionType
2973
+ };
2974
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2975
+ strapiAdmin.DescriptionComponentRenderer,
2976
+ {
2977
+ props,
2978
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2979
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2980
+ }
2981
+ ) });
2768
2982
  };
2769
- const formatListLayout = (data, {
2770
- schemas,
2771
- schema,
2772
- components
2773
- }) => {
2774
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2775
- (acc, [attribute, metadata]) => {
2776
- return {
2777
- ...acc,
2778
- [attribute]: metadata.list
2779
- };
2780
- },
2781
- {}
2782
- );
2783
- const listAttributes = convertListLayoutToFieldLayouts(
2784
- data.contentType.layouts.list,
2785
- schema?.attributes,
2786
- listMetadatas,
2787
- { configurations: data.components, schemas: components },
2788
- schemas
2789
- );
2983
+ const ActionsPanel = () => {
2984
+ const { formatMessage } = reactIntl.useIntl();
2790
2985
  return {
2791
- layout: listAttributes,
2792
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2793
- metadatas: listMetadatas,
2794
- options: {
2795
- ...schema?.options,
2796
- ...schema?.pluginOptions,
2797
- ...data.contentType.options
2798
- }
2986
+ title: formatMessage({
2987
+ id: "content-manager.containers.edit.panels.default.title",
2988
+ defaultMessage: "Entry"
2989
+ }),
2990
+ content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2799
2991
  };
2800
2992
  };
2801
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2802
- return columns.map((name) => {
2803
- const attribute = attributes[name];
2804
- if (!attribute) {
2805
- return null;
2993
+ ActionsPanel.type = "actions";
2994
+ const ActionsPanelContent = () => {
2995
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2996
+ const [
2997
+ {
2998
+ query: { status = "draft" }
2806
2999
  }
2807
- const metadata = metadatas[name];
2808
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2809
- return {
2810
- attribute,
2811
- label: metadata.label ?? "",
2812
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2813
- schemas,
2814
- components: components?.schemas ?? {}
2815
- }),
2816
- name,
2817
- searchable: metadata.searchable ?? true,
2818
- sortable: metadata.sortable ?? true
2819
- };
2820
- }).filter((field) => field !== null);
3000
+ ] = strapiAdmin.useQueryParams();
3001
+ const { model, id, document, meta, collectionType } = useDoc();
3002
+ const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
3003
+ const props = {
3004
+ activeTab: status,
3005
+ model,
3006
+ documentId: id,
3007
+ document: isCloning ? void 0 : document,
3008
+ meta: isCloning ? void 0 : meta,
3009
+ collectionType
3010
+ };
3011
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
3012
+ /* @__PURE__ */ jsxRuntime.jsx(
3013
+ strapiAdmin.DescriptionComponentRenderer,
3014
+ {
3015
+ props,
3016
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
3017
+ children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
3018
+ }
3019
+ ),
3020
+ /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
3021
+ ] });
2821
3022
  };
3023
+ const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
3024
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3025
+ designSystem.Flex,
3026
+ {
3027
+ ref,
3028
+ tag: "aside",
3029
+ "aria-labelledby": "additional-information",
3030
+ background: "neutral0",
3031
+ borderColor: "neutral150",
3032
+ hasRadius: true,
3033
+ paddingBottom: 4,
3034
+ paddingLeft: 4,
3035
+ paddingRight: 4,
3036
+ paddingTop: 4,
3037
+ shadow: "tableShadow",
3038
+ gap: 3,
3039
+ direction: "column",
3040
+ justifyContent: "stretch",
3041
+ alignItems: "flex-start",
3042
+ children: [
3043
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3044
+ children
3045
+ ]
3046
+ }
3047
+ );
3048
+ });
2822
3049
  const ConfirmBulkActionDialog = ({
2823
3050
  onToggleDialog,
2824
3051
  isOpen = false,
@@ -2826,7 +3053,7 @@ const ConfirmBulkActionDialog = ({
2826
3053
  endAction
2827
3054
  }) => {
2828
3055
  const { formatMessage } = reactIntl.useIntl();
2829
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { onOpenChange: onToggleDialog, open: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
3056
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2830
3057
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage({
2831
3058
  id: "app.components.ConfirmDialog.title",
2832
3059
  defaultMessage: "Confirmation"
@@ -2857,6 +3084,7 @@ const ConfirmDialogPublishAll = ({
2857
3084
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler(getTranslation);
2858
3085
  const { model, schema } = useDoc();
2859
3086
  const [{ query }] = strapiAdmin.useQueryParams();
3087
+ const enableDraftRelationsCount = false;
2860
3088
  const {
2861
3089
  data: countDraftRelations = 0,
2862
3090
  isLoading,
@@ -2868,7 +3096,7 @@ const ConfirmDialogPublishAll = ({
2868
3096
  locale: query?.plugins?.i18n?.locale
2869
3097
  },
2870
3098
  {
2871
- skip: selectedEntries.length === 0
3099
+ skip: !enableDraftRelationsCount
2872
3100
  }
2873
3101
  );
2874
3102
  React__namespace.useEffect(() => {
@@ -3053,7 +3281,7 @@ const SelectedEntriesTableContent = ({
3053
3281
  status: row.status
3054
3282
  }
3055
3283
  ) }),
3056
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
3284
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(
3057
3285
  designSystem.IconButton,
3058
3286
  {
3059
3287
  tag: reactRouterDom.Link,
@@ -3076,9 +3304,10 @@ const SelectedEntriesTableContent = ({
3076
3304
  ),
3077
3305
  target: "_blank",
3078
3306
  marginLeft: "auto",
3079
- children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {})
3307
+ variant: "ghost",
3308
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, { width: "1.6rem", height: "1.6rem" })
3080
3309
  }
3081
- ) })
3310
+ ) }) })
3082
3311
  ] }, row.id)) })
3083
3312
  ] });
3084
3313
  };
@@ -3115,7 +3344,13 @@ const SelectedEntriesModalContent = ({
3115
3344
  );
3116
3345
  const { rows, validationErrors } = React__namespace.useMemo(() => {
3117
3346
  if (data.length > 0 && schema) {
3118
- const validate = createYupSchema(schema.attributes, components);
3347
+ const validate = createYupSchema(
3348
+ schema.attributes,
3349
+ components,
3350
+ // Since this is the "Publish" action, the validation
3351
+ // schema must enforce the rules for published entities
3352
+ { status: "published" }
3353
+ );
3119
3354
  const validationErrors2 = {};
3120
3355
  const rows2 = data.map((entry) => {
3121
3356
  try {
@@ -3465,7 +3700,7 @@ const TableActions = ({ document }) => {
3465
3700
  strapiAdmin.DescriptionComponentRenderer,
3466
3701
  {
3467
3702
  props,
3468
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3703
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3469
3704
  children: (actions2) => {
3470
3705
  const tableRowActions = actions2.filter((action) => {
3471
3706
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3576,7 +3811,7 @@ const CloneAction = ({ model, documentId }) => {
3576
3811
  }),
3577
3812
  content: /* @__PURE__ */ jsxRuntime.jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3578
3813
  footer: ({ onClose }) => {
3579
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
3814
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
3580
3815
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3581
3816
  id: "cancel",
3582
3817
  defaultMessage: "Cancel"
@@ -3617,8 +3852,7 @@ class ContentManagerPlugin {
3617
3852
  documentActions = [
3618
3853
  ...DEFAULT_ACTIONS,
3619
3854
  ...DEFAULT_TABLE_ROW_ACTIONS,
3620
- ...DEFAULT_HEADER_ACTIONS,
3621
- HistoryAction
3855
+ ...DEFAULT_HEADER_ACTIONS
3622
3856
  ];
3623
3857
  editViewSidePanels = [ActionsPanel];
3624
3858
  headerActions = [];
@@ -3707,6 +3941,52 @@ const getPrintableType = (value) => {
3707
3941
  }
3708
3942
  return nativeType;
3709
3943
  };
3944
+ const HistoryAction = ({ model, document }) => {
3945
+ const { formatMessage } = reactIntl.useIntl();
3946
+ const [{ query }] = strapiAdmin.useQueryParams();
3947
+ const navigate = reactRouterDom.useNavigate();
3948
+ const pluginsQueryParams = qs.stringify({ plugins: query.plugins }, { encode: false });
3949
+ if (!window.strapi.features.isEnabled("cms-content-history")) {
3950
+ return null;
3951
+ }
3952
+ return {
3953
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ClockCounterClockwise, {}),
3954
+ label: formatMessage({
3955
+ id: "content-manager.history.document-action",
3956
+ defaultMessage: "Content History"
3957
+ }),
3958
+ onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
3959
+ disabled: (
3960
+ /**
3961
+ * The user is creating a new document.
3962
+ * It hasn't been saved yet, so there's no history to go to
3963
+ */
3964
+ !document || /**
3965
+ * The document has been created but the current dimension has never been saved.
3966
+ * For example, the user is creating a new locale in an existing document,
3967
+ * so there's no history for the document in that locale
3968
+ */
3969
+ !document.id || /**
3970
+ * History is only available for content types created by the user.
3971
+ * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
3972
+ * which start with `admin::` or `plugin::`
3973
+ */
3974
+ !model.startsWith("api::")
3975
+ ),
3976
+ position: "header"
3977
+ };
3978
+ };
3979
+ HistoryAction.type = "history";
3980
+ const historyAdmin = {
3981
+ bootstrap(app) {
3982
+ const { addDocumentAction } = app.getPlugin("content-manager").apis;
3983
+ addDocumentAction((actions2) => {
3984
+ const indexOfDeleteAction = actions2.findIndex((action) => action.type === "delete");
3985
+ actions2.splice(indexOfDeleteAction, 0, HistoryAction);
3986
+ return actions2;
3987
+ });
3988
+ }
3989
+ };
3710
3990
  const initialState = {
3711
3991
  collectionTypeLinks: [],
3712
3992
  components: [],
@@ -3743,6 +4023,97 @@ const { setInitialData } = actions;
3743
4023
  const reducer = toolkit.combineReducers({
3744
4024
  app: reducer$1
3745
4025
  });
4026
+ const previewApi = contentManagerApi.injectEndpoints({
4027
+ endpoints: (builder) => ({
4028
+ getPreviewUrl: builder.query({
4029
+ query({ query, params }) {
4030
+ return {
4031
+ url: `/content-manager/preview/url/${params.contentType}`,
4032
+ method: "GET",
4033
+ config: {
4034
+ params: query
4035
+ }
4036
+ };
4037
+ }
4038
+ })
4039
+ })
4040
+ });
4041
+ const { useGetPreviewUrlQuery } = previewApi;
4042
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4043
+ const { formatMessage } = reactIntl.useIntl();
4044
+ const { toggleNotification } = strapiAdmin.useNotification();
4045
+ const { copy } = strapiAdmin.useClipboard();
4046
+ const { trackUsage } = strapiAdmin.useTracking();
4047
+ const { data, error } = useGetPreviewUrlQuery({
4048
+ params: {
4049
+ contentType: model
4050
+ },
4051
+ query: {
4052
+ documentId,
4053
+ locale: document?.locale,
4054
+ status: document?.status
4055
+ }
4056
+ });
4057
+ if (!data?.data?.url || error) {
4058
+ return null;
4059
+ }
4060
+ const { url } = data.data;
4061
+ const handleCopyLink = () => {
4062
+ copy(url);
4063
+ toggleNotification({
4064
+ message: formatMessage({
4065
+ id: "content-manager.preview.copy.success",
4066
+ defaultMessage: "Copied preview link"
4067
+ }),
4068
+ type: "success"
4069
+ });
4070
+ };
4071
+ const handleClick = () => {
4072
+ trackUsage("willOpenPreview");
4073
+ };
4074
+ return {
4075
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4076
+ content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, width: "100%", children: [
4077
+ /* @__PURE__ */ jsxRuntime.jsx(
4078
+ designSystem.Button,
4079
+ {
4080
+ variant: "tertiary",
4081
+ tag: reactRouterDom.Link,
4082
+ to: url,
4083
+ onClick: handleClick,
4084
+ target: "_blank",
4085
+ flex: "auto",
4086
+ children: formatMessage({
4087
+ id: "content-manager.preview.panel.button",
4088
+ defaultMessage: "Open preview"
4089
+ })
4090
+ }
4091
+ ),
4092
+ /* @__PURE__ */ jsxRuntime.jsx(
4093
+ designSystem.IconButton,
4094
+ {
4095
+ type: "button",
4096
+ label: formatMessage({
4097
+ id: "preview.copy.label",
4098
+ defaultMessage: "Copy preview link"
4099
+ }),
4100
+ onClick: handleCopyLink,
4101
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Link, {})
4102
+ }
4103
+ )
4104
+ ] })
4105
+ };
4106
+ };
4107
+ const FEATURE_ID = "preview";
4108
+ const previewAdmin = {
4109
+ bootstrap(app) {
4110
+ if (!window.strapi.future.isEnabled(FEATURE_ID)) {
4111
+ return;
4112
+ }
4113
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4114
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4115
+ }
4116
+ };
3746
4117
  const index = {
3747
4118
  register(app) {
3748
4119
  const cm = new ContentManagerPlugin();
@@ -3762,7 +4133,7 @@ const index = {
3762
4133
  app.router.addRoute({
3763
4134
  path: "content-manager/*",
3764
4135
  lazy: async () => {
3765
- const { Layout } = await Promise.resolve().then(() => require("./layout-DRuJUpas.js"));
4136
+ const { Layout } = await Promise.resolve().then(() => require("./layout-CmaemAO3.js"));
3766
4137
  return {
3767
4138
  Component: Layout
3768
4139
  };
@@ -3771,10 +4142,18 @@ const index = {
3771
4142
  });
3772
4143
  app.registerPlugin(cm.config);
3773
4144
  },
4145
+ bootstrap(app) {
4146
+ if (typeof historyAdmin.bootstrap === "function") {
4147
+ historyAdmin.bootstrap(app);
4148
+ }
4149
+ if (typeof previewAdmin.bootstrap === "function") {
4150
+ previewAdmin.bootstrap(app);
4151
+ }
4152
+ },
3774
4153
  async registerTrads({ locales }) {
3775
4154
  const importedTrads = await Promise.all(
3776
4155
  locales.map((locale) => {
3777
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => Promise.resolve().then(() => require("./ar-BUUWXIYu.js")), "./translations/ca.json": () => Promise.resolve().then(() => require("./ca-Cmk45QO6.js")), "./translations/cs.json": () => Promise.resolve().then(() => require("./cs-CkJy6B2v.js")), "./translations/de.json": () => Promise.resolve().then(() => require("./de-CCEmbAah.js")), "./translations/en.json": () => Promise.resolve().then(() => require("./en-fbKQxLGn.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-EUonQTon.js")), "./translations/eu.json": () => Promise.resolve().then(() => require("./eu-VDH-3ovk.js")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-B7kGGg3E.js")), "./translations/gu.json": () => Promise.resolve().then(() => require("./gu-BRmF601H.js")), "./translations/hi.json": () => Promise.resolve().then(() => require("./hi-CCJBptSq.js")), "./translations/hu.json": () => Promise.resolve().then(() => require("./hu-sNV_yLYy.js")), "./translations/id.json": () => Promise.resolve().then(() => require("./id-B5Ser98A.js")), "./translations/it.json": () => Promise.resolve().then(() => require("./it-DkBIs7vD.js")), "./translations/ja.json": () => Promise.resolve().then(() => require("./ja-CcFe8diO.js")), "./translations/ko.json": () => Promise.resolve().then(() => require("./ko-woFZPmLk.js")), "./translations/ml.json": () => Promise.resolve().then(() => require("./ml-C2W8N8k1.js")), "./translations/ms.json": () => Promise.resolve().then(() => require("./ms-BuFotyP_.js")), "./translations/nl.json": () => Promise.resolve().then(() => require("./nl-bbEOHChV.js")), "./translations/pl.json": () => Promise.resolve().then(() => require("./pl-uzwG-hk7.js")), "./translations/pt-BR.json": () => Promise.resolve().then(() => require("./pt-BR-BiOz37D9.js")), "./translations/pt.json": () => Promise.resolve().then(() => require("./pt-CeXQuq50.js")), "./translations/ru.json": () => Promise.resolve().then(() => require("./ru-BT3ybNny.js")), "./translations/sa.json": () => Promise.resolve().then(() => require("./sa-CcvkYInH.js")), "./translations/sk.json": () => Promise.resolve().then(() => require("./sk-CvY09Xjv.js")), "./translations/sv.json": () => Promise.resolve().then(() => require("./sv-MYDuzgvT.js")), "./translations/th.json": () => Promise.resolve().then(() => require("./th-D9_GfAjc.js")), "./translations/tr.json": () => Promise.resolve().then(() => require("./tr-D9UH-O_R.js")), "./translations/uk.json": () => Promise.resolve().then(() => require("./uk-C8EiqJY7.js")), "./translations/vi.json": () => Promise.resolve().then(() => require("./vi-CJlYDheJ.js")), "./translations/zh-Hans.json": () => Promise.resolve().then(() => require("./zh-Hans-9kOncHGw.js")), "./translations/zh.json": () => Promise.resolve().then(() => require("./zh-CQQfszqR.js")) }), `./translations/${locale}.json`).then(({ default: data }) => {
4156
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => Promise.resolve().then(() => require("./ar-BUUWXIYu.js")), "./translations/ca.json": () => Promise.resolve().then(() => require("./ca-Cmk45QO6.js")), "./translations/cs.json": () => Promise.resolve().then(() => require("./cs-CkJy6B2v.js")), "./translations/de.json": () => Promise.resolve().then(() => require("./de-CCEmbAah.js")), "./translations/en.json": () => Promise.resolve().then(() => require("./en-Bdpa50w3.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-9K52xZIr.js")), "./translations/eu.json": () => Promise.resolve().then(() => require("./eu-VDH-3ovk.js")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-B2Kyv8Z9.js")), "./translations/gu.json": () => Promise.resolve().then(() => require("./gu-BRmF601H.js")), "./translations/hi.json": () => Promise.resolve().then(() => require("./hi-CCJBptSq.js")), "./translations/hu.json": () => Promise.resolve().then(() => require("./hu-sNV_yLYy.js")), "./translations/id.json": () => Promise.resolve().then(() => require("./id-B5Ser98A.js")), "./translations/it.json": () => Promise.resolve().then(() => require("./it-DkBIs7vD.js")), "./translations/ja.json": () => Promise.resolve().then(() => require("./ja-7sfIbjxE.js")), "./translations/ko.json": () => Promise.resolve().then(() => require("./ko-woFZPmLk.js")), "./translations/ml.json": () => Promise.resolve().then(() => require("./ml-C2W8N8k1.js")), "./translations/ms.json": () => Promise.resolve().then(() => require("./ms-BuFotyP_.js")), "./translations/nl.json": () => Promise.resolve().then(() => require("./nl-bbEOHChV.js")), "./translations/pl.json": () => Promise.resolve().then(() => require("./pl-uzwG-hk7.js")), "./translations/pt-BR.json": () => Promise.resolve().then(() => require("./pt-BR-BiOz37D9.js")), "./translations/pt.json": () => Promise.resolve().then(() => require("./pt-CeXQuq50.js")), "./translations/ru.json": () => Promise.resolve().then(() => require("./ru-BT3ybNny.js")), "./translations/sa.json": () => Promise.resolve().then(() => require("./sa-CcvkYInH.js")), "./translations/sk.json": () => Promise.resolve().then(() => require("./sk-CvY09Xjv.js")), "./translations/sv.json": () => Promise.resolve().then(() => require("./sv-MYDuzgvT.js")), "./translations/th.json": () => Promise.resolve().then(() => require("./th-D9_GfAjc.js")), "./translations/tr.json": () => Promise.resolve().then(() => require("./tr-D9UH-O_R.js")), "./translations/uk.json": () => Promise.resolve().then(() => require("./uk-C8EiqJY7.js")), "./translations/vi.json": () => Promise.resolve().then(() => require("./vi-CJlYDheJ.js")), "./translations/zh-Hans.json": () => Promise.resolve().then(() => require("./zh-Hans-9kOncHGw.js")), "./translations/zh.json": () => Promise.resolve().then(() => require("./zh-CQQfszqR.js")) }), `./translations/${locale}.json`).then(({ default: data }) => {
3778
4157
  return {
3779
4158
  data: prefixPluginTranslations(data, PLUGIN_ID),
3780
4159
  locale
@@ -3792,6 +4171,7 @@ const index = {
3792
4171
  };
3793
4172
  exports.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD;
3794
4173
  exports.BulkActionsRenderer = BulkActionsRenderer;
4174
+ exports.CLONE_PATH = CLONE_PATH;
3795
4175
  exports.COLLECTION_TYPES = COLLECTION_TYPES;
3796
4176
  exports.CREATOR_FIELDS = CREATOR_FIELDS;
3797
4177
  exports.DEFAULT_SETTINGS = DEFAULT_SETTINGS;
@@ -3819,6 +4199,7 @@ exports.getMainField = getMainField;
3819
4199
  exports.getTranslation = getTranslation;
3820
4200
  exports.index = index;
3821
4201
  exports.setInitialData = setInitialData;
4202
+ exports.useContentManagerContext = useContentManagerContext;
3822
4203
  exports.useContentTypeSchema = useContentTypeSchema;
3823
4204
  exports.useDoc = useDoc;
3824
4205
  exports.useDocLayout = useDocLayout;
@@ -3831,4 +4212,4 @@ exports.useGetAllDocumentsQuery = useGetAllDocumentsQuery;
3831
4212
  exports.useGetContentTypeConfigurationQuery = useGetContentTypeConfigurationQuery;
3832
4213
  exports.useGetInitialDataQuery = useGetInitialDataQuery;
3833
4214
  exports.useUpdateContentTypeConfigurationMutation = useUpdateContentTypeConfigurationMutation;
3834
- //# sourceMappingURL=index-Buwn78Rt.js.map
4215
+ //# sourceMappingURL=index-DqZnjo8F.js.map