@strapi/content-manager 0.0.0-experimental.9df68962083938acba06546a7901c68a63266aec → 0.0.0-experimental.a13c58eec89ab119f0e381fb79c0252979e9c125

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 (205) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-C-49MccQ.js → ComponentConfigurationPage-CB0JNNKL.js} +4 -4
  3. package/dist/_chunks/{ComponentConfigurationPage-C-49MccQ.js.map → ComponentConfigurationPage-CB0JNNKL.js.map} +1 -1
  4. package/dist/_chunks/{ComponentConfigurationPage-DmwmiFQy.mjs → ComponentConfigurationPage-CTCh51QS.mjs} +4 -4
  5. package/dist/_chunks/{ComponentConfigurationPage-DmwmiFQy.mjs.map → ComponentConfigurationPage-CTCh51QS.mjs.map} +1 -1
  6. package/dist/_chunks/{EditConfigurationPage-DjFJw56M.js → EditConfigurationPage-BxTVjxBG.js} +4 -4
  7. package/dist/_chunks/{EditConfigurationPage-DjFJw56M.js.map → EditConfigurationPage-BxTVjxBG.js.map} +1 -1
  8. package/dist/_chunks/{EditConfigurationPage-JT3E7NZy.mjs → EditConfigurationPage-CVyURlPG.mjs} +4 -4
  9. package/dist/_chunks/{EditConfigurationPage-JT3E7NZy.mjs.map → EditConfigurationPage-CVyURlPG.mjs.map} +1 -1
  10. package/dist/_chunks/{EditViewPage-zT3fBr4Y.js → EditViewPage-Cg78RUyS.js} +62 -11
  11. package/dist/_chunks/EditViewPage-Cg78RUyS.js.map +1 -0
  12. package/dist/_chunks/{EditViewPage-CPj61RMh.mjs → EditViewPage-M1OkTFHV.mjs} +63 -12
  13. package/dist/_chunks/EditViewPage-M1OkTFHV.mjs.map +1 -0
  14. package/dist/_chunks/{Field-Boxf9Ajp.js → Field-BQoMs_At.js} +271 -154
  15. package/dist/_chunks/Field-BQoMs_At.js.map +1 -0
  16. package/dist/_chunks/{Field-dha5VnIQ.mjs → Field-D1UGGscZ.mjs} +269 -152
  17. package/dist/_chunks/Field-D1UGGscZ.mjs.map +1 -0
  18. package/dist/_chunks/{Form-y5g1SRsh.js → Form-2uPHazrg.js} +36 -17
  19. package/dist/_chunks/Form-2uPHazrg.js.map +1 -0
  20. package/dist/_chunks/{Form-DHrru2AV.mjs → Form-JOjqloMg.mjs} +36 -17
  21. package/dist/_chunks/Form-JOjqloMg.mjs.map +1 -0
  22. package/dist/_chunks/{History-Bru_KoeP.mjs → History-D6LWunSa.mjs} +77 -62
  23. package/dist/_chunks/History-D6LWunSa.mjs.map +1 -0
  24. package/dist/_chunks/{History-CqN6K7SX.js → History-DoIdauuE.js} +76 -61
  25. package/dist/_chunks/History-DoIdauuE.js.map +1 -0
  26. package/dist/_chunks/{ListConfigurationPage-R_p-SbHZ.js → ListConfigurationPage-C1eYSCV7.js} +21 -9
  27. package/dist/_chunks/ListConfigurationPage-C1eYSCV7.js.map +1 -0
  28. package/dist/_chunks/{ListConfigurationPage-D8wGABj0.mjs → ListConfigurationPage-C8J_y4eX.mjs} +21 -9
  29. package/dist/_chunks/ListConfigurationPage-C8J_y4eX.mjs.map +1 -0
  30. package/dist/_chunks/{ListViewPage-pEw_zug9.js → ListViewPage-Nd8_ZHjF.js} +86 -46
  31. package/dist/_chunks/ListViewPage-Nd8_ZHjF.js.map +1 -0
  32. package/dist/_chunks/{ListViewPage-SID6TRb9.mjs → ListViewPage-vkipJOxG.mjs} +84 -44
  33. package/dist/_chunks/ListViewPage-vkipJOxG.mjs.map +1 -0
  34. package/dist/_chunks/{NoContentTypePage-CJ7UXwrQ.mjs → NoContentTypePage-CAxkF2Vg.mjs} +2 -2
  35. package/dist/_chunks/{NoContentTypePage-CJ7UXwrQ.mjs.map → NoContentTypePage-CAxkF2Vg.mjs.map} +1 -1
  36. package/dist/_chunks/{NoContentTypePage-C5dcQojD.js → NoContentTypePage-CsI4DiJk.js} +2 -2
  37. package/dist/_chunks/{NoContentTypePage-C5dcQojD.js.map → NoContentTypePage-CsI4DiJk.js.map} +1 -1
  38. package/dist/_chunks/{NoPermissionsPage-BtPrImPP.js → NoPermissionsPage-D7c29n-i.js} +2 -2
  39. package/dist/_chunks/{NoPermissionsPage-BtPrImPP.js.map → NoPermissionsPage-D7c29n-i.js.map} +1 -1
  40. package/dist/_chunks/{NoPermissionsPage-B7syEq5E.mjs → NoPermissionsPage-DA5ACuoK.mjs} +2 -2
  41. package/dist/_chunks/{NoPermissionsPage-B7syEq5E.mjs.map → NoPermissionsPage-DA5ACuoK.mjs.map} +1 -1
  42. package/dist/_chunks/Preview-Bwo9ft4j.mjs +288 -0
  43. package/dist/_chunks/Preview-Bwo9ft4j.mjs.map +1 -0
  44. package/dist/_chunks/Preview-DLxVMS5f.js +307 -0
  45. package/dist/_chunks/Preview-DLxVMS5f.js.map +1 -0
  46. package/dist/_chunks/{Relations-B9Crnhnn.mjs → Relations-Bddj_dxS.mjs} +73 -37
  47. package/dist/_chunks/Relations-Bddj_dxS.mjs.map +1 -0
  48. package/dist/_chunks/{Relations-DjTQ5kGB.js → Relations-CdFEx0mp.js} +72 -36
  49. package/dist/_chunks/Relations-CdFEx0mp.js.map +1 -0
  50. package/dist/_chunks/{en-fbKQxLGn.js → en-CHOp_xJv.js} +27 -16
  51. package/dist/_chunks/{en-fbKQxLGn.js.map → en-CHOp_xJv.js.map} +1 -1
  52. package/dist/_chunks/{en-Ux26r5pl.mjs → en-D_BMf0hT.mjs} +27 -16
  53. package/dist/_chunks/{en-Ux26r5pl.mjs.map → en-D_BMf0hT.mjs.map} +1 -1
  54. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  55. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  56. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  57. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  58. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  59. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  60. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  61. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  62. package/dist/_chunks/{index-DVPWZkbS.js → index-CltLAahy.js} +1066 -658
  63. package/dist/_chunks/index-CltLAahy.js.map +1 -0
  64. package/dist/_chunks/{index-DJXJw9V5.mjs → index-DmNeuXH0.mjs} +1086 -679
  65. package/dist/_chunks/index-DmNeuXH0.mjs.map +1 -0
  66. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  67. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  68. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  69. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  70. package/dist/_chunks/{layout-Bau7ZfLV.mjs → layout-D63qmUOu.mjs} +25 -12
  71. package/dist/_chunks/layout-D63qmUOu.mjs.map +1 -0
  72. package/dist/_chunks/{layout-Dm6fbiQj.js → layout-DslPJR5e.js} +24 -11
  73. package/dist/_chunks/layout-DslPJR5e.js.map +1 -0
  74. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  75. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  76. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  77. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  78. package/dist/_chunks/{relations-CKnpRgrN.js → relations-B2h_O2Xw.js} +6 -7
  79. package/dist/_chunks/relations-B2h_O2Xw.js.map +1 -0
  80. package/dist/_chunks/{relations-BH_kBSJ0.mjs → relations-X3M9aO-m.mjs} +6 -7
  81. package/dist/_chunks/relations-X3M9aO-m.mjs.map +1 -0
  82. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  83. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  84. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  85. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  86. package/dist/admin/index.js +2 -1
  87. package/dist/admin/index.js.map +1 -1
  88. package/dist/admin/index.mjs +5 -4
  89. package/dist/admin/src/exports.d.ts +1 -1
  90. package/dist/admin/src/history/index.d.ts +3 -0
  91. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  92. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  93. package/dist/admin/src/index.d.ts +1 -0
  94. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  95. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +1 -0
  96. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +1 -1
  97. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  98. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  99. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  100. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  101. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  102. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  103. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  104. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  105. package/dist/admin/src/preview/constants.d.ts +1 -0
  106. package/dist/admin/src/preview/index.d.ts +4 -0
  107. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  108. package/dist/admin/src/preview/routes.d.ts +3 -0
  109. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  110. package/dist/admin/src/services/api.d.ts +1 -1
  111. package/dist/admin/src/services/components.d.ts +2 -2
  112. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  113. package/dist/admin/src/services/documents.d.ts +19 -17
  114. package/dist/admin/src/services/init.d.ts +1 -1
  115. package/dist/admin/src/services/relations.d.ts +2 -2
  116. package/dist/admin/src/services/uid.d.ts +3 -3
  117. package/dist/admin/src/utils/validation.d.ts +4 -1
  118. package/dist/server/index.js +548 -261
  119. package/dist/server/index.js.map +1 -1
  120. package/dist/server/index.mjs +549 -262
  121. package/dist/server/index.mjs.map +1 -1
  122. package/dist/server/src/bootstrap.d.ts.map +1 -1
  123. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  124. package/dist/server/src/controllers/index.d.ts.map +1 -1
  125. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  126. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  127. package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
  128. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  129. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  130. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  131. package/dist/server/src/history/services/history.d.ts.map +1 -1
  132. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  133. package/dist/server/src/history/services/utils.d.ts +4 -4
  134. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  135. package/dist/server/src/index.d.ts +4 -4
  136. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  137. package/dist/server/src/preview/constants.d.ts +2 -0
  138. package/dist/server/src/preview/constants.d.ts.map +1 -0
  139. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  140. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  141. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  142. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  143. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  144. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  145. package/dist/server/src/preview/index.d.ts +4 -0
  146. package/dist/server/src/preview/index.d.ts.map +1 -0
  147. package/dist/server/src/preview/routes/index.d.ts +8 -0
  148. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  149. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  150. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  151. package/dist/server/src/preview/services/index.d.ts +15 -0
  152. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  153. package/dist/server/src/preview/services/preview-config.d.ts +30 -0
  154. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  155. package/dist/server/src/preview/services/preview.d.ts +12 -0
  156. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  157. package/dist/server/src/preview/utils.d.ts +18 -0
  158. package/dist/server/src/preview/utils.d.ts.map +1 -0
  159. package/dist/server/src/routes/index.d.ts.map +1 -1
  160. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  161. package/dist/server/src/services/document-metadata.d.ts +8 -8
  162. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  163. package/dist/server/src/services/index.d.ts +4 -4
  164. package/dist/server/src/services/index.d.ts.map +1 -1
  165. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  166. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  167. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  168. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  169. package/dist/server/src/utils/index.d.ts +2 -0
  170. package/dist/server/src/utils/index.d.ts.map +1 -1
  171. package/dist/shared/contracts/collection-types.d.ts +3 -1
  172. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  173. package/dist/shared/contracts/index.d.ts +1 -0
  174. package/dist/shared/contracts/index.d.ts.map +1 -1
  175. package/dist/shared/contracts/preview.d.ts +27 -0
  176. package/dist/shared/contracts/preview.d.ts.map +1 -0
  177. package/dist/shared/index.js +4 -0
  178. package/dist/shared/index.js.map +1 -1
  179. package/dist/shared/index.mjs +4 -0
  180. package/dist/shared/index.mjs.map +1 -1
  181. package/package.json +13 -13
  182. package/dist/_chunks/EditViewPage-CPj61RMh.mjs.map +0 -1
  183. package/dist/_chunks/EditViewPage-zT3fBr4Y.js.map +0 -1
  184. package/dist/_chunks/Field-Boxf9Ajp.js.map +0 -1
  185. package/dist/_chunks/Field-dha5VnIQ.mjs.map +0 -1
  186. package/dist/_chunks/Form-DHrru2AV.mjs.map +0 -1
  187. package/dist/_chunks/Form-y5g1SRsh.js.map +0 -1
  188. package/dist/_chunks/History-Bru_KoeP.mjs.map +0 -1
  189. package/dist/_chunks/History-CqN6K7SX.js.map +0 -1
  190. package/dist/_chunks/ListConfigurationPage-D8wGABj0.mjs.map +0 -1
  191. package/dist/_chunks/ListConfigurationPage-R_p-SbHZ.js.map +0 -1
  192. package/dist/_chunks/ListViewPage-SID6TRb9.mjs.map +0 -1
  193. package/dist/_chunks/ListViewPage-pEw_zug9.js.map +0 -1
  194. package/dist/_chunks/Relations-B9Crnhnn.mjs.map +0 -1
  195. package/dist/_chunks/Relations-DjTQ5kGB.js.map +0 -1
  196. package/dist/_chunks/index-DJXJw9V5.mjs.map +0 -1
  197. package/dist/_chunks/index-DVPWZkbS.js.map +0 -1
  198. package/dist/_chunks/layout-Bau7ZfLV.mjs.map +0 -1
  199. package/dist/_chunks/layout-Dm6fbiQj.js.map +0 -1
  200. package/dist/_chunks/relations-BH_kBSJ0.mjs.map +0 -1
  201. package/dist/_chunks/relations-CKnpRgrN.js.map +0 -1
  202. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  203. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  204. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  205. 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.required) {
625
- return schema.required({
626
- id: strapiAdmin.translatedErrors.required.id,
627
- defaultMessage: "This field is required."
628
- });
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,10 +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 ("min" in schema && min) {
705
+ if (min) {
663
706
  return schema.min(min, {
664
707
  ...strapiAdmin.translatedErrors.min,
665
708
  values: {
@@ -777,16 +820,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
777
820
  }, {});
778
821
  return componentsByKey;
779
822
  };
780
- 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);
781
922
  const { toggleNotification } = strapiAdmin.useNotification();
782
923
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
924
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
783
925
  const {
784
- currentData: data,
785
- isLoading: isLoadingDocument,
786
- isFetching: isFetchingDocument,
787
- error
788
- } = useGetDocumentQuery(args, opts);
789
- 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;
790
932
  React__namespace.useEffect(() => {
791
933
  if (error) {
792
934
  toggleNotification({
@@ -794,68 +936,322 @@ const useDocument = (args, opts) => {
794
936
  message: formatAPIError(error)
795
937
  });
796
938
  }
797
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
798
- const validationSchema = React__namespace.useMemo(() => {
799
- if (!schema) {
800
- return null;
801
- }
802
- return createYupSchema(schema.attributes, components);
803
- }, [schema, components]);
804
- const validate = React__namespace.useCallback(
805
- (document) => {
806
- if (!validationSchema) {
807
- throw new Error(
808
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
809
- );
810
- }
811
- try {
812
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
813
- return null;
814
- } catch (error2) {
815
- if (error2 instanceof yup.ValidationError) {
816
- return strapiAdmin.getYupValidationErrors(error2);
817
- }
818
- throw error2;
819
- }
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
820
947
  },
821
- [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]
822
964
  );
823
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
824
965
  return {
825
- components,
826
- document: data?.data,
827
- meta: data?.meta,
966
+ error,
828
967
  isLoading,
829
- schema,
830
- validate
968
+ edit,
969
+ list: listLayout
831
970
  };
832
971
  };
833
- const useDoc = () => {
834
- const { id, slug, collectionType, origin } = reactRouterDom.useParams();
835
- const [{ query }] = strapiAdmin.useQueryParams();
836
- const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
837
- if (!collectionType) {
838
- throw new Error("Could not find collectionType in url params");
839
- }
840
- if (!slug) {
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,
1182
+ document: data?.data,
1183
+ meta: data?.meta,
1184
+ isLoading,
1185
+ hasError,
1186
+ schema,
1187
+ schemas,
1188
+ validate
1189
+ };
1190
+ };
1191
+ const useDoc = () => {
1192
+ const { id, slug, collectionType, origin } = reactRouterDom.useParams();
1193
+ const [{ query }] = strapiAdmin.useQueryParams();
1194
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1195
+ if (!collectionType) {
1196
+ throw new Error("Could not find collectionType in url params");
1197
+ }
1198
+ if (!slug) {
841
1199
  throw new Error("Could not find model in url params");
842
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;
843
1208
  return {
844
1209
  collectionType,
845
1210
  model: slug,
846
- id: origin || id === "create" ? void 0 : id,
847
- ...useDocument(
848
- { documentId: origin || id, model: slug, collectionType, params },
849
- {
850
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
851
- }
852
- )
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
853
1252
  };
854
1253
  };
855
1254
  const prefixPluginTranslations = (trad, pluginId) => {
856
- if (!pluginId) {
857
- throw new TypeError("pluginId can't be empty");
858
- }
859
1255
  return Object.keys(trad).reduce((acc, current) => {
860
1256
  acc[`${pluginId}.${current}`] = trad[current];
861
1257
  return acc;
@@ -871,6 +1267,8 @@ const useDocumentActions = () => {
871
1267
  const { formatMessage } = reactIntl.useIntl();
872
1268
  const { trackUsage } = strapiAdmin.useTracking();
873
1269
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1270
+ const navigate = reactRouterDom.useNavigate();
1271
+ const setCurrentStep = strapiAdmin.useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
874
1272
  const [deleteDocument] = useDeleteDocumentMutation();
875
1273
  const _delete = React__namespace.useCallback(
876
1274
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1185,6 +1583,7 @@ const useDocumentActions = () => {
1185
1583
  defaultMessage: "Saved document"
1186
1584
  })
1187
1585
  });
1586
+ setCurrentStep("contentManager.success");
1188
1587
  return res.data;
1189
1588
  } catch (err) {
1190
1589
  toggleNotification({
@@ -1206,7 +1605,6 @@ const useDocumentActions = () => {
1206
1605
  sourceId
1207
1606
  });
1208
1607
  if ("error" in res) {
1209
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1210
1608
  return { error: res.error };
1211
1609
  }
1212
1610
  toggleNotification({
@@ -1225,7 +1623,7 @@ const useDocumentActions = () => {
1225
1623
  throw err;
1226
1624
  }
1227
1625
  },
1228
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1626
+ [autoCloneDocument, formatMessage, toggleNotification]
1229
1627
  );
1230
1628
  const [cloneDocument] = useCloneDocumentMutation();
1231
1629
  const clone = React__namespace.useCallback(
@@ -1251,6 +1649,7 @@ const useDocumentActions = () => {
1251
1649
  defaultMessage: "Cloned document"
1252
1650
  })
1253
1651
  });
1652
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1254
1653
  return res.data;
1255
1654
  } catch (err) {
1256
1655
  toggleNotification({
@@ -1261,7 +1660,7 @@ const useDocumentActions = () => {
1261
1660
  throw err;
1262
1661
  }
1263
1662
  },
1264
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1663
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1265
1664
  );
1266
1665
  const [getDoc] = useLazyGetDocumentQuery();
1267
1666
  const getDocument = React__namespace.useCallback(
@@ -1286,10 +1685,10 @@ const useDocumentActions = () => {
1286
1685
  update
1287
1686
  };
1288
1687
  };
1289
- const ProtectedHistoryPage = React.lazy(
1290
- () => Promise.resolve().then(() => require("./History-CqN6K7SX.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1688
+ const ProtectedHistoryPage = React__namespace.lazy(
1689
+ () => Promise.resolve().then(() => require("./History-DoIdauuE.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1291
1690
  );
1292
- const routes$1 = [
1691
+ const routes$2 = [
1293
1692
  {
1294
1693
  path: ":collectionType/:slug/:id/history",
1295
1694
  Component: ProtectedHistoryPage
@@ -1299,32 +1698,45 @@ const routes$1 = [
1299
1698
  Component: ProtectedHistoryPage
1300
1699
  }
1301
1700
  ];
1701
+ const ProtectedPreviewPage = React__namespace.lazy(
1702
+ () => Promise.resolve().then(() => require("./Preview-DLxVMS5f.js")).then((mod) => ({ default: mod.ProtectedPreviewPage }))
1703
+ );
1704
+ const routes$1 = [
1705
+ {
1706
+ path: ":collectionType/:slug/:id/preview",
1707
+ Component: ProtectedPreviewPage
1708
+ },
1709
+ {
1710
+ path: ":collectionType/:slug/preview",
1711
+ Component: ProtectedPreviewPage
1712
+ }
1713
+ ];
1302
1714
  const ProtectedEditViewPage = React.lazy(
1303
- () => Promise.resolve().then(() => require("./EditViewPage-zT3fBr4Y.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1715
+ () => Promise.resolve().then(() => require("./EditViewPage-Cg78RUyS.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1304
1716
  );
1305
1717
  const ProtectedListViewPage = React.lazy(
1306
- () => Promise.resolve().then(() => require("./ListViewPage-pEw_zug9.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1718
+ () => Promise.resolve().then(() => require("./ListViewPage-Nd8_ZHjF.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1307
1719
  );
1308
1720
  const ProtectedListConfiguration = React.lazy(
1309
- () => Promise.resolve().then(() => require("./ListConfigurationPage-R_p-SbHZ.js")).then((mod) => ({
1721
+ () => Promise.resolve().then(() => require("./ListConfigurationPage-C1eYSCV7.js")).then((mod) => ({
1310
1722
  default: mod.ProtectedListConfiguration
1311
1723
  }))
1312
1724
  );
1313
1725
  const ProtectedEditConfigurationPage = React.lazy(
1314
- () => Promise.resolve().then(() => require("./EditConfigurationPage-DjFJw56M.js")).then((mod) => ({
1726
+ () => Promise.resolve().then(() => require("./EditConfigurationPage-BxTVjxBG.js")).then((mod) => ({
1315
1727
  default: mod.ProtectedEditConfigurationPage
1316
1728
  }))
1317
1729
  );
1318
1730
  const ProtectedComponentConfigurationPage = React.lazy(
1319
- () => Promise.resolve().then(() => require("./ComponentConfigurationPage-C-49MccQ.js")).then((mod) => ({
1731
+ () => Promise.resolve().then(() => require("./ComponentConfigurationPage-CB0JNNKL.js")).then((mod) => ({
1320
1732
  default: mod.ProtectedComponentConfigurationPage
1321
1733
  }))
1322
1734
  );
1323
1735
  const NoPermissions = React.lazy(
1324
- () => Promise.resolve().then(() => require("./NoPermissionsPage-BtPrImPP.js")).then((mod) => ({ default: mod.NoPermissions }))
1736
+ () => Promise.resolve().then(() => require("./NoPermissionsPage-D7c29n-i.js")).then((mod) => ({ default: mod.NoPermissions }))
1325
1737
  );
1326
1738
  const NoContentType = React.lazy(
1327
- () => Promise.resolve().then(() => require("./NoContentTypePage-C5dcQojD.js")).then((mod) => ({ default: mod.NoContentType }))
1739
+ () => Promise.resolve().then(() => require("./NoContentTypePage-CsI4DiJk.js")).then((mod) => ({ default: mod.NoContentType }))
1328
1740
  );
1329
1741
  const CollectionTypePages = () => {
1330
1742
  const { collectionType } = reactRouterDom.useParams();
@@ -1370,6 +1782,7 @@ const routes = [
1370
1782
  path: "no-content-types",
1371
1783
  Component: NoContentType
1372
1784
  },
1785
+ ...routes$2,
1373
1786
  ...routes$1
1374
1787
  ];
1375
1788
  const DocumentActions = ({ actions: actions2 }) => {
@@ -1438,12 +1851,14 @@ const DocumentActionButton = (action) => {
1438
1851
  /* @__PURE__ */ jsxRuntime.jsx(
1439
1852
  designSystem.Button,
1440
1853
  {
1441
- flex: 1,
1854
+ flex: "auto",
1442
1855
  startIcon: action.icon,
1443
1856
  disabled: action.disabled,
1444
1857
  onClick: handleClick(action),
1445
1858
  justifyContent: "center",
1446
1859
  variant: action.variant || "default",
1860
+ paddingTop: "7px",
1861
+ paddingBottom: "7px",
1447
1862
  children: action.label
1448
1863
  }
1449
1864
  ),
@@ -1451,7 +1866,7 @@ const DocumentActionButton = (action) => {
1451
1866
  DocumentActionConfirmDialog,
1452
1867
  {
1453
1868
  ...action.dialog,
1454
- variant: action.variant,
1869
+ variant: action.dialog?.variant ?? action.variant,
1455
1870
  isOpen: dialogId === action.id,
1456
1871
  onClose: handleClose
1457
1872
  }
@@ -1508,9 +1923,9 @@ const DocumentActionsMenu = ({
1508
1923
  disabled: isDisabled,
1509
1924
  size: "S",
1510
1925
  endIcon: null,
1511
- paddingTop: "7px",
1512
- paddingLeft: "9px",
1513
- paddingRight: "9px",
1926
+ paddingTop: "4px",
1927
+ paddingLeft: "7px",
1928
+ paddingRight: "7px",
1514
1929
  variant,
1515
1930
  children: [
1516
1931
  /* @__PURE__ */ jsxRuntime.jsx(Icons.More, { "aria-hidden": true, focusable: false }),
@@ -1521,7 +1936,7 @@ const DocumentActionsMenu = ({
1521
1936
  ]
1522
1937
  }
1523
1938
  ),
1524
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1939
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1525
1940
  actions2.map((action) => {
1526
1941
  return /* @__PURE__ */ jsxRuntime.jsx(
1527
1942
  designSystem.Menu.Item,
@@ -1530,10 +1945,25 @@ const DocumentActionsMenu = ({
1530
1945
  onSelect: handleClick(action),
1531
1946
  display: "block",
1532
1947
  children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", gap: 4, children: [
1533
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1534
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1535
- action.label
1536
- ] }),
1948
+ /* @__PURE__ */ jsxRuntime.jsxs(
1949
+ designSystem.Flex,
1950
+ {
1951
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1952
+ gap: 2,
1953
+ tag: "span",
1954
+ children: [
1955
+ /* @__PURE__ */ jsxRuntime.jsx(
1956
+ designSystem.Flex,
1957
+ {
1958
+ tag: "span",
1959
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1960
+ children: action.icon
1961
+ }
1962
+ ),
1963
+ action.label
1964
+ ]
1965
+ }
1966
+ ),
1537
1967
  action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsxRuntime.jsx(
1538
1968
  designSystem.Flex,
1539
1969
  {
@@ -1630,11 +2060,11 @@ const DocumentActionConfirmDialog = ({
1630
2060
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
1631
2061
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Body, { children: content }),
1632
2062
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Footer, { children: [
1633
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", children: formatMessage({
2063
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Cancel, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1634
2064
  id: "app.components.Button.cancel",
1635
2065
  defaultMessage: "Cancel"
1636
2066
  }) }) }),
1637
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, children: formatMessage({
2067
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1638
2068
  id: "app.components.Button.confirm",
1639
2069
  defaultMessage: "Confirm"
1640
2070
  }) })
@@ -1661,6 +2091,18 @@ const DocumentActionModal = ({
1661
2091
  typeof Footer === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Footer, { onClose: handleClose }) : Footer
1662
2092
  ] }) });
1663
2093
  };
2094
+ const transformData = (data) => {
2095
+ if (Array.isArray(data)) {
2096
+ return data.map(transformData);
2097
+ }
2098
+ if (typeof data === "object" && data !== null) {
2099
+ if ("apiData" in data) {
2100
+ return data.apiData;
2101
+ }
2102
+ return mapValues__default.default(transformData)(data);
2103
+ }
2104
+ return data;
2105
+ };
1664
2106
  const PublishAction$1 = ({
1665
2107
  activeTab,
1666
2108
  documentId,
@@ -1673,13 +2115,17 @@ const PublishAction$1 = ({
1673
2115
  const navigate = reactRouterDom.useNavigate();
1674
2116
  const { toggleNotification } = strapiAdmin.useNotification();
1675
2117
  const { _unstableFormatValidationErrors: formatValidationErrors } = strapiAdmin.useAPIErrorHandler();
2118
+ const isListView = reactRouterDom.useMatch(LIST_PATH) !== null;
1676
2119
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
1677
2120
  const { formatMessage } = reactIntl.useIntl();
1678
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1679
- "PublishAction",
1680
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1681
- );
2121
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1682
2122
  const { publish } = useDocumentActions();
2123
+ const [
2124
+ countDraftRelations,
2125
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
2126
+ ] = useLazyGetDraftRelationCountQuery();
2127
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React__namespace.useState(0);
2128
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React__namespace.useState(0);
1683
2129
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1684
2130
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
1685
2131
  const modified = strapiAdmin.useForm("PublishAction", ({ modified: modified2 }) => modified2);
@@ -1688,10 +2134,105 @@ const PublishAction$1 = ({
1688
2134
  const validate = strapiAdmin.useForm("PublishAction", (state) => state.validate);
1689
2135
  const setErrors = strapiAdmin.useForm("PublishAction", (state) => state.setErrors);
1690
2136
  const formValues = strapiAdmin.useForm("PublishAction", ({ values }) => values);
2137
+ React__namespace.useEffect(() => {
2138
+ if (isErrorDraftRelations) {
2139
+ toggleNotification({
2140
+ type: "danger",
2141
+ message: formatMessage({
2142
+ id: getTranslation("error.records.fetch-draft-relatons"),
2143
+ defaultMessage: "An error occurred while fetching draft relations on this document."
2144
+ })
2145
+ });
2146
+ }
2147
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
2148
+ React__namespace.useEffect(() => {
2149
+ const localDraftRelations = /* @__PURE__ */ new Set();
2150
+ const extractDraftRelations = (data) => {
2151
+ const relations = data.connect || [];
2152
+ relations.forEach((relation) => {
2153
+ if (relation.status === "draft") {
2154
+ localDraftRelations.add(relation.id);
2155
+ }
2156
+ });
2157
+ };
2158
+ const traverseAndExtract = (data) => {
2159
+ Object.entries(data).forEach(([key, value]) => {
2160
+ if (key === "connect" && Array.isArray(value)) {
2161
+ extractDraftRelations({ connect: value });
2162
+ } else if (typeof value === "object" && value !== null) {
2163
+ traverseAndExtract(value);
2164
+ }
2165
+ });
2166
+ };
2167
+ if (!documentId || modified) {
2168
+ traverseAndExtract(formValues);
2169
+ setLocalCountOfDraftRelations(localDraftRelations.size);
2170
+ }
2171
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
2172
+ React__namespace.useEffect(() => {
2173
+ if (!document || !document.documentId || isListView) {
2174
+ return;
2175
+ }
2176
+ const fetchDraftRelationsCount = async () => {
2177
+ const { data, error } = await countDraftRelations({
2178
+ collectionType,
2179
+ model,
2180
+ documentId,
2181
+ params
2182
+ });
2183
+ if (error) {
2184
+ throw error;
2185
+ }
2186
+ if (data) {
2187
+ setServerCountOfDraftRelations(data.data);
2188
+ }
2189
+ };
2190
+ fetchDraftRelationsCount();
2191
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1691
2192
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1692
2193
  if (!schema?.options?.draftAndPublish) {
1693
2194
  return null;
1694
2195
  }
2196
+ const performPublish = async () => {
2197
+ setSubmitting(true);
2198
+ try {
2199
+ const { errors } = await validate(true, {
2200
+ status: "published"
2201
+ });
2202
+ if (errors) {
2203
+ toggleNotification({
2204
+ type: "danger",
2205
+ message: formatMessage({
2206
+ id: "content-manager.validation.error",
2207
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
2208
+ })
2209
+ });
2210
+ return;
2211
+ }
2212
+ const res = await publish(
2213
+ {
2214
+ collectionType,
2215
+ model,
2216
+ documentId,
2217
+ params
2218
+ },
2219
+ transformData(formValues)
2220
+ );
2221
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
2222
+ navigate({
2223
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2224
+ search: rawQuery
2225
+ });
2226
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
2227
+ setErrors(formatValidationErrors(res.error));
2228
+ }
2229
+ } finally {
2230
+ setSubmitting(false);
2231
+ }
2232
+ };
2233
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
2234
+ const enableDraftRelationsCount = false;
2235
+ const hasDraftRelations = enableDraftRelationsCount;
1695
2236
  return {
1696
2237
  /**
1697
2238
  * Disabled when:
@@ -1701,49 +2242,36 @@ const PublishAction$1 = ({
1701
2242
  * - the document is already published & not modified
1702
2243
  * - the document is being created & not modified
1703
2244
  * - the user doesn't have the permission to publish
1704
- * - the user doesn't have the permission to create a new document
1705
- * - the user doesn't have the permission to update the document
1706
2245
  */
1707
- disabled: isCloning || isSubmitting || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2246
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1708
2247
  label: formatMessage({
1709
2248
  id: "app.utils.publish",
1710
2249
  defaultMessage: "Publish"
1711
2250
  }),
1712
2251
  onClick: async () => {
1713
- setSubmitting(true);
1714
- try {
1715
- const { errors } = await validate();
1716
- if (errors) {
1717
- toggleNotification({
1718
- type: "danger",
1719
- message: formatMessage({
1720
- id: "content-manager.validation.error",
1721
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1722
- })
1723
- });
1724
- return;
1725
- }
1726
- const res = await publish(
1727
- {
1728
- collectionType,
1729
- model,
1730
- documentId,
1731
- params
1732
- },
1733
- formValues
1734
- );
1735
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1736
- navigate({
1737
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1738
- search: rawQuery
1739
- });
1740
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1741
- setErrors(formatValidationErrors(res.error));
2252
+ await performPublish();
2253
+ },
2254
+ dialog: hasDraftRelations ? {
2255
+ type: "dialog",
2256
+ variant: "danger",
2257
+ footer: null,
2258
+ title: formatMessage({
2259
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
2260
+ defaultMessage: "Confirmation"
2261
+ }),
2262
+ content: formatMessage(
2263
+ {
2264
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2265
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
2266
+ },
2267
+ {
2268
+ count: totalDraftRelations
1742
2269
  }
1743
- } finally {
1744
- setSubmitting(false);
2270
+ ),
2271
+ onConfirm: async () => {
2272
+ await performPublish();
1745
2273
  }
1746
- }
2274
+ } : void 0
1747
2275
  };
1748
2276
  };
1749
2277
  PublishAction$1.type = "publish";
@@ -1759,10 +2287,6 @@ const UpdateAction = ({
1759
2287
  const cloneMatch = reactRouterDom.useMatch(CLONE_PATH);
1760
2288
  const isCloning = cloneMatch !== null;
1761
2289
  const { formatMessage } = reactIntl.useIntl();
1762
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1763
- canCreate: canCreate2,
1764
- canUpdate: canUpdate2
1765
- }));
1766
2290
  const { create, update, clone } = useDocumentActions();
1767
2291
  const [{ query, rawQuery }] = strapiAdmin.useQueryParams();
1768
2292
  const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
@@ -1779,10 +2303,8 @@ const UpdateAction = ({
1779
2303
  * - the form is submitting
1780
2304
  * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1781
2305
  * - the active tab is the published tab
1782
- * - the user doesn't have the permission to create a new document
1783
- * - the user doesn't have the permission to update the document
1784
2306
  */
1785
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
2307
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1786
2308
  label: formatMessage({
1787
2309
  id: "content-manager.containers.Edit.save",
1788
2310
  defaultMessage: "Save"
@@ -1790,7 +2312,9 @@ const UpdateAction = ({
1790
2312
  onClick: async () => {
1791
2313
  setSubmitting(true);
1792
2314
  try {
1793
- const { errors } = await validate();
2315
+ const { errors } = await validate(true, {
2316
+ status: "draft"
2317
+ });
1794
2318
  if (errors) {
1795
2319
  toggleNotification({
1796
2320
  type: "danger",
@@ -1808,13 +2332,16 @@ const UpdateAction = ({
1808
2332
  documentId: cloneMatch.params.origin,
1809
2333
  params
1810
2334
  },
1811
- document
2335
+ transformData(document)
1812
2336
  );
1813
2337
  if ("data" in res) {
1814
- navigate({
1815
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1816
- search: rawQuery
1817
- });
2338
+ navigate(
2339
+ {
2340
+ pathname: `../${res.data.documentId}`,
2341
+ search: rawQuery
2342
+ },
2343
+ { relative: "path" }
2344
+ );
1818
2345
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1819
2346
  setErrors(formatValidationErrors(res.error));
1820
2347
  }
@@ -1826,7 +2353,7 @@ const UpdateAction = ({
1826
2353
  documentId,
1827
2354
  params
1828
2355
  },
1829
- document
2356
+ transformData(document)
1830
2357
  );
1831
2358
  if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1832
2359
  setErrors(formatValidationErrors(res.error));
@@ -1839,13 +2366,16 @@ const UpdateAction = ({
1839
2366
  model,
1840
2367
  params
1841
2368
  },
1842
- document
2369
+ transformData(document)
1843
2370
  );
1844
2371
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1845
- navigate({
1846
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1847
- search: rawQuery
1848
- });
2372
+ navigate(
2373
+ {
2374
+ pathname: `../${res.data.documentId}`,
2375
+ search: rawQuery
2376
+ },
2377
+ { replace: true, relative: "path" }
2378
+ );
1849
2379
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1850
2380
  setErrors(formatValidationErrors(res.error));
1851
2381
  }
@@ -1889,7 +2419,7 @@ const UnpublishAction$1 = ({
1889
2419
  id: "app.utils.unpublish",
1890
2420
  defaultMessage: "Unpublish"
1891
2421
  }),
1892
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2422
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
1893
2423
  onClick: async () => {
1894
2424
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1895
2425
  if (!documentId) {
@@ -2001,7 +2531,7 @@ const DiscardAction = ({
2001
2531
  id: "content-manager.actions.discard.label",
2002
2532
  defaultMessage: "Discard changes"
2003
2533
  }),
2004
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCrossCircle, {}),
2534
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Cross, {}),
2005
2535
  position: ["panel", "table-row"],
2006
2536
  variant: "danger",
2007
2537
  dialog: {
@@ -2029,11 +2559,6 @@ const DiscardAction = ({
2029
2559
  };
2030
2560
  };
2031
2561
  DiscardAction.type = "discard";
2032
- const StyledCrossCircle = styledComponents.styled(Icons.CrossCircle)`
2033
- path {
2034
- fill: currentColor;
2035
- }
2036
- `;
2037
2562
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2038
2563
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2039
2564
  const RelativeTime = React__namespace.forwardRef(
@@ -2081,8 +2606,12 @@ const getDisplayName = ({
2081
2606
  };
2082
2607
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2083
2608
  const DocumentStatus = ({ status = "draft", ...restProps }) => {
2084
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2085
- 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) }) });
2609
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2610
+ const { formatMessage } = reactIntl.useIntl();
2611
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Status, { ...restProps, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: formatMessage({
2612
+ id: `content-manager.containers.List.${status}`,
2613
+ defaultMessage: capitalise(status)
2614
+ }) }) });
2086
2615
  };
2087
2616
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2088
2617
  const { formatMessage } = reactIntl.useIntl();
@@ -2091,23 +2620,13 @@ const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2091
2620
  id: "content-manager.containers.edit.title.new",
2092
2621
  defaultMessage: "Create an entry"
2093
2622
  }) : documentTitle;
2094
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 8, paddingBottom: 4, gap: 3, children: [
2623
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2095
2624
  /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.BackButton, {}),
2096
- /* @__PURE__ */ jsxRuntime.jsxs(
2097
- designSystem.Flex,
2098
- {
2099
- width: "100%",
2100
- justifyContent: "space-between",
2101
- paddingTop: 1,
2102
- gap: "80px",
2103
- alignItems: "flex-start",
2104
- children: [
2105
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2106
- /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2107
- ]
2108
- }
2109
- ),
2110
- status ? /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) : null
2625
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2626
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: title }),
2627
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderToolbar, {})
2628
+ ] }),
2629
+ status ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2111
2630
  ] });
2112
2631
  };
2113
2632
  const HeaderToolbar = () => {
@@ -2190,12 +2709,12 @@ const Information = ({ activeTab }) => {
2190
2709
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2191
2710
  label: formatMessage({
2192
2711
  id: "content-manager.containers.edit.information.last-published.label",
2193
- defaultMessage: "Last published"
2712
+ defaultMessage: "Published"
2194
2713
  }),
2195
2714
  value: formatMessage(
2196
2715
  {
2197
2716
  id: "content-manager.containers.edit.information.last-published.value",
2198
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2717
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2199
2718
  },
2200
2719
  {
2201
2720
  time: /* @__PURE__ */ jsxRuntime.jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2208,12 +2727,12 @@ const Information = ({ activeTab }) => {
2208
2727
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2209
2728
  label: formatMessage({
2210
2729
  id: "content-manager.containers.edit.information.last-draft.label",
2211
- defaultMessage: "Last draft"
2730
+ defaultMessage: "Updated"
2212
2731
  }),
2213
2732
  value: formatMessage(
2214
2733
  {
2215
2734
  id: "content-manager.containers.edit.information.last-draft.value",
2216
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2735
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2217
2736
  },
2218
2737
  {
2219
2738
  time: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2231,12 +2750,12 @@ const Information = ({ activeTab }) => {
2231
2750
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2232
2751
  label: formatMessage({
2233
2752
  id: "content-manager.containers.edit.information.document.label",
2234
- defaultMessage: "Document"
2753
+ defaultMessage: "Created"
2235
2754
  }),
2236
2755
  value: formatMessage(
2237
2756
  {
2238
2757
  id: "content-manager.containers.edit.information.document.value",
2239
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2758
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2240
2759
  },
2241
2760
  {
2242
2761
  time: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2274,25 +2793,77 @@ const Information = ({ activeTab }) => {
2274
2793
  );
2275
2794
  };
2276
2795
  const HeaderActions = ({ actions: actions2 }) => {
2277
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: actions2.map((action) => {
2278
- if ("options" in action) {
2796
+ const [dialogId, setDialogId] = React__namespace.useState(null);
2797
+ const handleClick = (action) => async (e) => {
2798
+ if (!("options" in action)) {
2799
+ const { onClick = () => false, dialog, id } = action;
2800
+ const muteDialog = await onClick(e);
2801
+ if (dialog && !muteDialog) {
2802
+ e.preventDefault();
2803
+ setDialogId(id);
2804
+ }
2805
+ }
2806
+ };
2807
+ const handleClose = () => {
2808
+ setDialogId(null);
2809
+ };
2810
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, children: actions2.map((action) => {
2811
+ if (action.options) {
2279
2812
  return /* @__PURE__ */ jsxRuntime.jsx(
2280
2813
  designSystem.SingleSelect,
2281
2814
  {
2282
2815
  size: "S",
2283
- disabled: action.disabled,
2284
- "aria-label": action.label,
2285
2816
  onChange: action.onSelect,
2286
- value: action.value,
2817
+ "aria-label": action.label,
2818
+ ...action,
2287
2819
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { ...option, children: label }, option.value))
2288
2820
  },
2289
2821
  action.id
2290
2822
  );
2291
2823
  } else {
2292
- return null;
2824
+ if (action.type === "icon") {
2825
+ return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
2826
+ /* @__PURE__ */ jsxRuntime.jsx(
2827
+ designSystem.IconButton,
2828
+ {
2829
+ disabled: action.disabled,
2830
+ label: action.label,
2831
+ size: "S",
2832
+ onClick: handleClick(action),
2833
+ children: action.icon
2834
+ }
2835
+ ),
2836
+ action.dialog ? /* @__PURE__ */ jsxRuntime.jsx(
2837
+ HeaderActionDialog,
2838
+ {
2839
+ ...action.dialog,
2840
+ isOpen: dialogId === action.id,
2841
+ onClose: handleClose
2842
+ }
2843
+ ) : null
2844
+ ] }, action.id);
2845
+ }
2293
2846
  }
2294
2847
  }) });
2295
2848
  };
2849
+ const HeaderActionDialog = ({
2850
+ onClose,
2851
+ onCancel,
2852
+ title,
2853
+ content: Content,
2854
+ isOpen
2855
+ }) => {
2856
+ const handleClose = async () => {
2857
+ if (onCancel) {
2858
+ await onCancel();
2859
+ }
2860
+ onClose();
2861
+ };
2862
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2863
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: title }),
2864
+ typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content
2865
+ ] }) });
2866
+ };
2296
2867
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2297
2868
  const navigate = reactRouterDom.useNavigate();
2298
2869
  const { formatMessage } = reactIntl.useIntl();
@@ -2333,12 +2904,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2333
2904
  const { delete: deleteAction } = useDocumentActions();
2334
2905
  const { toggleNotification } = strapiAdmin.useNotification();
2335
2906
  const setSubmitting = strapiAdmin.useForm("DeleteAction", (state) => state.setSubmitting);
2907
+ const isLocalized = document?.locale != null;
2336
2908
  return {
2337
2909
  disabled: !canDelete || !document,
2338
- label: formatMessage({
2339
- id: "content-manager.actions.delete.label",
2340
- defaultMessage: "Delete document"
2341
- }),
2910
+ label: formatMessage(
2911
+ {
2912
+ id: "content-manager.actions.delete.label",
2913
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2914
+ },
2915
+ { isLocalized }
2916
+ ),
2342
2917
  icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2343
2918
  dialog: {
2344
2919
  type: "dialog",
@@ -2381,416 +2956,114 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2381
2956
  });
2382
2957
  if (!("error" in res)) {
2383
2958
  navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
2384
- }
2385
- } finally {
2386
- if (!listViewPathMatch) {
2387
- setSubmitting(false);
2388
- }
2389
- }
2390
- }
2391
- },
2392
- variant: "danger",
2393
- position: ["header", "table-row"]
2394
- };
2395
- };
2396
- DeleteAction$1.type = "delete";
2397
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2398
- const Panels = () => {
2399
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2400
- const [
2401
- {
2402
- query: { status }
2403
- }
2404
- ] = strapiAdmin.useQueryParams({
2405
- status: "draft"
2406
- });
2407
- const { model, id, document, meta, collectionType } = useDoc();
2408
- const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2409
- const props = {
2410
- activeTab: status,
2411
- model,
2412
- documentId: id,
2413
- document: isCloning ? void 0 : document,
2414
- meta: isCloning ? void 0 : meta,
2415
- collectionType
2416
- };
2417
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2418
- strapiAdmin.DescriptionComponentRenderer,
2419
- {
2420
- props,
2421
- descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2422
- children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2423
- }
2424
- ) });
2425
- };
2426
- const ActionsPanel = () => {
2427
- const { formatMessage } = reactIntl.useIntl();
2428
- return {
2429
- title: formatMessage({
2430
- id: "content-manager.containers.edit.panels.default.title",
2431
- defaultMessage: "Document"
2432
- }),
2433
- content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2434
- };
2435
- };
2436
- ActionsPanel.type = "actions";
2437
- const ActionsPanelContent = () => {
2438
- const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2439
- const [
2440
- {
2441
- query: { status = "draft" }
2442
- }
2443
- ] = strapiAdmin.useQueryParams();
2444
- const { model, id, document, meta, collectionType } = useDoc();
2445
- const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
2446
- const props = {
2447
- activeTab: status,
2448
- model,
2449
- documentId: id,
2450
- document: isCloning ? void 0 : document,
2451
- meta: isCloning ? void 0 : meta,
2452
- collectionType
2453
- };
2454
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
2455
- /* @__PURE__ */ jsxRuntime.jsx(
2456
- strapiAdmin.DescriptionComponentRenderer,
2457
- {
2458
- props,
2459
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2460
- children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
2461
- }
2462
- ),
2463
- /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
2464
- ] });
2465
- };
2466
- const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
2467
- return /* @__PURE__ */ jsxRuntime.jsxs(
2468
- designSystem.Flex,
2469
- {
2470
- ref,
2471
- tag: "aside",
2472
- "aria-labelledby": "additional-information",
2473
- background: "neutral0",
2474
- borderColor: "neutral150",
2475
- hasRadius: true,
2476
- paddingBottom: 4,
2477
- paddingLeft: 4,
2478
- paddingRight: 4,
2479
- paddingTop: 4,
2480
- shadow: "tableShadow",
2481
- gap: 3,
2482
- direction: "column",
2483
- justifyContent: "stretch",
2484
- alignItems: "flex-start",
2485
- children: [
2486
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
2487
- children
2488
- ]
2489
- }
2490
- );
2491
- });
2492
- const HOOKS = {
2493
- /**
2494
- * Hook that allows to mutate the displayed headers of the list view table
2495
- * @constant
2496
- * @type {string}
2497
- */
2498
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2499
- /**
2500
- * Hook that allows to mutate the CM's collection types links pre-set filters
2501
- * @constant
2502
- * @type {string}
2503
- */
2504
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2505
- /**
2506
- * Hook that allows to mutate the CM's edit view layout
2507
- * @constant
2508
- * @type {string}
2509
- */
2510
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2511
- /**
2512
- * Hook that allows to mutate the CM's single types links pre-set filters
2513
- * @constant
2514
- * @type {string}
2515
- */
2516
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2517
- };
2518
- const contentTypesApi = contentManagerApi.injectEndpoints({
2519
- endpoints: (builder) => ({
2520
- getContentTypeConfiguration: builder.query({
2521
- query: (uid) => ({
2522
- url: `/content-manager/content-types/${uid}/configuration`,
2523
- method: "GET"
2524
- }),
2525
- transformResponse: (response) => response.data,
2526
- providesTags: (_result, _error, uid) => [
2527
- { type: "ContentTypesConfiguration", id: uid },
2528
- { type: "ContentTypeSettings", id: "LIST" }
2529
- ]
2530
- }),
2531
- getAllContentTypeSettings: builder.query({
2532
- query: () => "/content-manager/content-types-settings",
2533
- transformResponse: (response) => response.data,
2534
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2535
- }),
2536
- updateContentTypeConfiguration: builder.mutation({
2537
- query: ({ uid, ...body }) => ({
2538
- url: `/content-manager/content-types/${uid}/configuration`,
2539
- method: "PUT",
2540
- data: body
2541
- }),
2542
- transformResponse: (response) => response.data,
2543
- invalidatesTags: (_result, _error, { uid }) => [
2544
- { type: "ContentTypesConfiguration", id: uid },
2545
- { type: "ContentTypeSettings", id: "LIST" },
2546
- // Is this necessary?
2547
- { type: "InitialData" }
2548
- ]
2549
- })
2550
- })
2551
- });
2552
- const {
2553
- useGetContentTypeConfigurationQuery,
2554
- useGetAllContentTypeSettingsQuery,
2555
- useUpdateContentTypeConfigurationMutation
2556
- } = contentTypesApi;
2557
- const checkIfAttributeIsDisplayable = (attribute) => {
2558
- const { type } = attribute;
2559
- if (type === "relation") {
2560
- return !attribute.relation.toLowerCase().includes("morph");
2561
- }
2562
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2563
- };
2564
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2565
- if (!mainFieldName) {
2566
- return void 0;
2567
- }
2568
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2569
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2570
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2571
- );
2572
- return {
2573
- name: mainFieldName,
2574
- type: mainFieldType ?? "string"
2575
- };
2576
- };
2577
- const DEFAULT_SETTINGS = {
2578
- bulkable: false,
2579
- filterable: false,
2580
- searchable: false,
2581
- pagination: false,
2582
- defaultSortBy: "",
2583
- defaultSortOrder: "asc",
2584
- mainField: "id",
2585
- pageSize: 10
2586
- };
2587
- const useDocumentLayout = (model) => {
2588
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2589
- const [{ query }] = strapiAdmin.useQueryParams();
2590
- const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2591
- const { toggleNotification } = strapiAdmin.useNotification();
2592
- const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
2593
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2594
- const {
2595
- data,
2596
- isLoading: isLoadingConfigs,
2597
- error,
2598
- isFetching: isFetchingConfigs
2599
- } = useGetContentTypeConfigurationQuery(model);
2600
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2601
- React__namespace.useEffect(() => {
2602
- if (error) {
2603
- toggleNotification({
2604
- type: "danger",
2605
- message: formatAPIError(error)
2606
- });
2607
- }
2608
- }, [error, formatAPIError, toggleNotification]);
2609
- const editLayout = React__namespace.useMemo(
2610
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2611
- layout: [],
2612
- components: {},
2613
- metadatas: {},
2614
- options: {},
2615
- settings: DEFAULT_SETTINGS
2616
- },
2617
- [data, isLoading, schemas, schema, components]
2618
- );
2619
- const listLayout = React__namespace.useMemo(() => {
2620
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2621
- layout: [],
2622
- metadatas: {},
2623
- options: {},
2624
- settings: DEFAULT_SETTINGS
2625
- };
2626
- }, [data, isLoading, schemas, schema, components]);
2627
- const { layout: edit } = React__namespace.useMemo(
2628
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2629
- layout: editLayout,
2630
- query
2631
- }),
2632
- [editLayout, query, runHookWaterfall]
2633
- );
2634
- return {
2635
- error,
2636
- isLoading,
2637
- edit,
2638
- list: listLayout
2639
- };
2640
- };
2641
- const useDocLayout = () => {
2642
- const { model } = useDoc();
2643
- return useDocumentLayout(model);
2644
- };
2645
- const formatEditLayout = (data, {
2646
- schemas,
2647
- schema,
2648
- components
2649
- }) => {
2650
- let currentPanelIndex = 0;
2651
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2652
- data.contentType.layouts.edit,
2653
- schema?.attributes,
2654
- data.contentType.metadatas,
2655
- { configurations: data.components, schemas: components },
2656
- schemas
2657
- ).reduce((panels, row) => {
2658
- if (row.some((field) => field.type === "dynamiczone")) {
2659
- panels.push([row]);
2660
- currentPanelIndex += 2;
2661
- } else {
2662
- if (!panels[currentPanelIndex]) {
2663
- panels.push([]);
2664
- }
2665
- panels[currentPanelIndex].push(row);
2666
- }
2667
- return panels;
2668
- }, []);
2669
- const componentEditAttributes = Object.entries(data.components).reduce(
2670
- (acc, [uid, configuration]) => {
2671
- acc[uid] = {
2672
- layout: convertEditLayoutToFieldLayouts(
2673
- configuration.layouts.edit,
2674
- components[uid].attributes,
2675
- configuration.metadatas
2676
- ),
2677
- settings: {
2678
- ...configuration.settings,
2679
- icon: components[uid].info.icon,
2680
- displayName: components[uid].info.displayName
2681
- }
2682
- };
2683
- return acc;
2684
- },
2685
- {}
2686
- );
2687
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2688
- (acc, [attribute, metadata]) => {
2689
- return {
2690
- ...acc,
2691
- [attribute]: metadata.edit
2692
- };
2693
- },
2694
- {}
2695
- );
2696
- return {
2697
- layout: panelledEditAttributes,
2698
- components: componentEditAttributes,
2699
- metadatas: editMetadatas,
2700
- settings: {
2701
- ...data.contentType.settings,
2702
- displayName: schema?.info.displayName
2959
+ }
2960
+ } finally {
2961
+ if (!listViewPathMatch) {
2962
+ setSubmitting(false);
2963
+ }
2964
+ }
2965
+ }
2703
2966
  },
2704
- options: {
2705
- ...schema?.options,
2706
- ...schema?.pluginOptions,
2707
- ...data.contentType.options
2708
- }
2967
+ variant: "danger",
2968
+ position: ["header", "table-row"]
2709
2969
  };
2710
2970
  };
2711
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2712
- return rows.map(
2713
- (row) => row.map((field) => {
2714
- const attribute = attributes[field.name];
2715
- if (!attribute) {
2716
- return null;
2717
- }
2718
- const { edit: metadata } = metadatas[field.name];
2719
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2720
- return {
2721
- attribute,
2722
- disabled: !metadata.editable,
2723
- hint: metadata.description,
2724
- label: metadata.label ?? "",
2725
- name: field.name,
2726
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2727
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2728
- schemas,
2729
- components: components?.schemas ?? {}
2730
- }),
2731
- placeholder: metadata.placeholder ?? "",
2732
- required: attribute.required ?? false,
2733
- size: field.size,
2734
- unique: "unique" in attribute ? attribute.unique : false,
2735
- visible: metadata.visible ?? true,
2736
- type: attribute.type
2737
- };
2738
- }).filter((field) => field !== null)
2739
- );
2971
+ DeleteAction$1.type = "delete";
2972
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2973
+ const Panels = () => {
2974
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2975
+ const [
2976
+ {
2977
+ query: { status }
2978
+ }
2979
+ ] = strapiAdmin.useQueryParams({
2980
+ status: "draft"
2981
+ });
2982
+ const { model, id, document, meta, collectionType } = useDoc();
2983
+ const plugins = strapiAdmin.useStrapiApp("Panels", (state) => state.plugins);
2984
+ const props = {
2985
+ activeTab: status,
2986
+ model,
2987
+ documentId: id,
2988
+ document: isCloning ? void 0 : document,
2989
+ meta: isCloning ? void 0 : meta,
2990
+ collectionType
2991
+ };
2992
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
2993
+ strapiAdmin.DescriptionComponentRenderer,
2994
+ {
2995
+ props,
2996
+ descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
2997
+ children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsxRuntime.jsx(Panel, { ...description, children: content }, id2))
2998
+ }
2999
+ ) });
2740
3000
  };
2741
- const formatListLayout = (data, {
2742
- schemas,
2743
- schema,
2744
- components
2745
- }) => {
2746
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2747
- (acc, [attribute, metadata]) => {
2748
- return {
2749
- ...acc,
2750
- [attribute]: metadata.list
2751
- };
2752
- },
2753
- {}
2754
- );
2755
- const listAttributes = convertListLayoutToFieldLayouts(
2756
- data.contentType.layouts.list,
2757
- schema?.attributes,
2758
- listMetadatas,
2759
- { configurations: data.components, schemas: components },
2760
- schemas
2761
- );
3001
+ const ActionsPanel = () => {
3002
+ const { formatMessage } = reactIntl.useIntl();
2762
3003
  return {
2763
- layout: listAttributes,
2764
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2765
- metadatas: listMetadatas,
2766
- options: {
2767
- ...schema?.options,
2768
- ...schema?.pluginOptions,
2769
- ...data.contentType.options
2770
- }
3004
+ title: formatMessage({
3005
+ id: "content-manager.containers.edit.panels.default.title",
3006
+ defaultMessage: "Entry"
3007
+ }),
3008
+ content: /* @__PURE__ */ jsxRuntime.jsx(ActionsPanelContent, {})
2771
3009
  };
2772
3010
  };
2773
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2774
- return columns.map((name) => {
2775
- const attribute = attributes[name];
2776
- if (!attribute) {
2777
- return null;
3011
+ ActionsPanel.type = "actions";
3012
+ const ActionsPanelContent = () => {
3013
+ const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
3014
+ const [
3015
+ {
3016
+ query: { status = "draft" }
2778
3017
  }
2779
- const metadata = metadatas[name];
2780
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2781
- return {
2782
- attribute,
2783
- label: metadata.label ?? "",
2784
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2785
- schemas,
2786
- components: components?.schemas ?? {}
2787
- }),
2788
- name,
2789
- searchable: metadata.searchable ?? true,
2790
- sortable: metadata.sortable ?? true
2791
- };
2792
- }).filter((field) => field !== null);
3018
+ ] = strapiAdmin.useQueryParams();
3019
+ const { model, id, document, meta, collectionType } = useDoc();
3020
+ const plugins = strapiAdmin.useStrapiApp("ActionsPanel", (state) => state.plugins);
3021
+ const props = {
3022
+ activeTab: status,
3023
+ model,
3024
+ documentId: id,
3025
+ document: isCloning ? void 0 : document,
3026
+ meta: isCloning ? void 0 : meta,
3027
+ collectionType
3028
+ };
3029
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, width: "100%", children: [
3030
+ /* @__PURE__ */ jsxRuntime.jsx(
3031
+ strapiAdmin.DescriptionComponentRenderer,
3032
+ {
3033
+ props,
3034
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
3035
+ children: (actions2) => /* @__PURE__ */ jsxRuntime.jsx(DocumentActions, { actions: actions2 })
3036
+ }
3037
+ ),
3038
+ /* @__PURE__ */ jsxRuntime.jsx(InjectionZone, { area: "editView.right-links", slug: model })
3039
+ ] });
2793
3040
  };
3041
+ const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
3042
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3043
+ designSystem.Flex,
3044
+ {
3045
+ ref,
3046
+ tag: "aside",
3047
+ "aria-labelledby": "additional-information",
3048
+ background: "neutral0",
3049
+ borderColor: "neutral150",
3050
+ hasRadius: true,
3051
+ paddingBottom: 4,
3052
+ paddingLeft: 4,
3053
+ paddingRight: 4,
3054
+ paddingTop: 4,
3055
+ shadow: "tableShadow",
3056
+ gap: 3,
3057
+ direction: "column",
3058
+ justifyContent: "stretch",
3059
+ alignItems: "flex-start",
3060
+ children: [
3061
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
3062
+ children
3063
+ ]
3064
+ }
3065
+ );
3066
+ });
2794
3067
  const ConfirmBulkActionDialog = ({
2795
3068
  onToggleDialog,
2796
3069
  isOpen = false,
@@ -2798,7 +3071,7 @@ const ConfirmBulkActionDialog = ({
2798
3071
  endAction
2799
3072
  }) => {
2800
3073
  const { formatMessage } = reactIntl.useIntl();
2801
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { onOpenChange: onToggleDialog, open: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
3074
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: isOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog.Content, { children: [
2802
3075
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Header, { children: formatMessage({
2803
3076
  id: "app.components.ConfirmDialog.title",
2804
3077
  defaultMessage: "Confirmation"
@@ -2829,6 +3102,7 @@ const ConfirmDialogPublishAll = ({
2829
3102
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler(getTranslation);
2830
3103
  const { model, schema } = useDoc();
2831
3104
  const [{ query }] = strapiAdmin.useQueryParams();
3105
+ const enableDraftRelationsCount = false;
2832
3106
  const {
2833
3107
  data: countDraftRelations = 0,
2834
3108
  isLoading,
@@ -2840,7 +3114,7 @@ const ConfirmDialogPublishAll = ({
2840
3114
  locale: query?.plugins?.i18n?.locale
2841
3115
  },
2842
3116
  {
2843
- skip: selectedEntries.length === 0
3117
+ skip: !enableDraftRelationsCount
2844
3118
  }
2845
3119
  );
2846
3120
  React__namespace.useEffect(() => {
@@ -2919,7 +3193,14 @@ const formatErrorMessages = (errors, parentKey, formatMessage) => {
2919
3193
  )
2920
3194
  );
2921
3195
  } else {
2922
- messages.push(...formatErrorMessages(value, currentKey, formatMessage));
3196
+ messages.push(
3197
+ ...formatErrorMessages(
3198
+ // @ts-expect-error TODO: check why value is not compatible with FormErrors
3199
+ value,
3200
+ currentKey,
3201
+ formatMessage
3202
+ )
3203
+ );
2923
3204
  }
2924
3205
  } else {
2925
3206
  messages.push(
@@ -3018,7 +3299,7 @@ const SelectedEntriesTableContent = ({
3018
3299
  status: row.status
3019
3300
  }
3020
3301
  ) }),
3021
- /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
3302
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(
3022
3303
  designSystem.IconButton,
3023
3304
  {
3024
3305
  tag: reactRouterDom.Link,
@@ -3041,9 +3322,10 @@ const SelectedEntriesTableContent = ({
3041
3322
  ),
3042
3323
  target: "_blank",
3043
3324
  marginLeft: "auto",
3044
- children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {})
3325
+ variant: "ghost",
3326
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, { width: "1.6rem", height: "1.6rem" })
3045
3327
  }
3046
- ) })
3328
+ ) }) })
3047
3329
  ] }, row.id)) })
3048
3330
  ] });
3049
3331
  };
@@ -3080,7 +3362,13 @@ const SelectedEntriesModalContent = ({
3080
3362
  );
3081
3363
  const { rows, validationErrors } = React__namespace.useMemo(() => {
3082
3364
  if (data.length > 0 && schema) {
3083
- const validate = createYupSchema(schema.attributes, components);
3365
+ const validate = createYupSchema(
3366
+ schema.attributes,
3367
+ components,
3368
+ // Since this is the "Publish" action, the validation
3369
+ // schema must enforce the rules for published entities
3370
+ { status: "published" }
3371
+ );
3084
3372
  const validationErrors2 = {};
3085
3373
  const rows2 = data.map((entry) => {
3086
3374
  try {
@@ -3430,7 +3718,7 @@ const TableActions = ({ document }) => {
3430
3718
  strapiAdmin.DescriptionComponentRenderer,
3431
3719
  {
3432
3720
  props,
3433
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3721
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3434
3722
  children: (actions2) => {
3435
3723
  const tableRowActions = actions2.filter((action) => {
3436
3724
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3541,7 +3829,7 @@ const CloneAction = ({ model, documentId }) => {
3541
3829
  }),
3542
3830
  content: /* @__PURE__ */ jsxRuntime.jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3543
3831
  footer: ({ onClose }) => {
3544
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
3832
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
3545
3833
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3546
3834
  id: "cancel",
3547
3835
  defaultMessage: "Cancel"
@@ -3582,8 +3870,7 @@ class ContentManagerPlugin {
3582
3870
  documentActions = [
3583
3871
  ...DEFAULT_ACTIONS,
3584
3872
  ...DEFAULT_TABLE_ROW_ACTIONS,
3585
- ...DEFAULT_HEADER_ACTIONS,
3586
- HistoryAction
3873
+ ...DEFAULT_HEADER_ACTIONS
3587
3874
  ];
3588
3875
  editViewSidePanels = [ActionsPanel];
3589
3876
  headerActions = [];
@@ -3672,6 +3959,52 @@ const getPrintableType = (value) => {
3672
3959
  }
3673
3960
  return nativeType;
3674
3961
  };
3962
+ const HistoryAction = ({ model, document }) => {
3963
+ const { formatMessage } = reactIntl.useIntl();
3964
+ const [{ query }] = strapiAdmin.useQueryParams();
3965
+ const navigate = reactRouterDom.useNavigate();
3966
+ const pluginsQueryParams = qs.stringify({ plugins: query.plugins }, { encode: false });
3967
+ if (!window.strapi.features.isEnabled("cms-content-history")) {
3968
+ return null;
3969
+ }
3970
+ return {
3971
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ClockCounterClockwise, {}),
3972
+ label: formatMessage({
3973
+ id: "content-manager.history.document-action",
3974
+ defaultMessage: "Content History"
3975
+ }),
3976
+ onClick: () => navigate({ pathname: "history", search: pluginsQueryParams }),
3977
+ disabled: (
3978
+ /**
3979
+ * The user is creating a new document.
3980
+ * It hasn't been saved yet, so there's no history to go to
3981
+ */
3982
+ !document || /**
3983
+ * The document has been created but the current dimension has never been saved.
3984
+ * For example, the user is creating a new locale in an existing document,
3985
+ * so there's no history for the document in that locale
3986
+ */
3987
+ !document.id || /**
3988
+ * History is only available for content types created by the user.
3989
+ * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
3990
+ * which start with `admin::` or `plugin::`
3991
+ */
3992
+ !model.startsWith("api::")
3993
+ ),
3994
+ position: "header"
3995
+ };
3996
+ };
3997
+ HistoryAction.type = "history";
3998
+ const historyAdmin = {
3999
+ bootstrap(app) {
4000
+ const { addDocumentAction } = app.getPlugin("content-manager").apis;
4001
+ addDocumentAction((actions2) => {
4002
+ const indexOfDeleteAction = actions2.findIndex((action) => action.type === "delete");
4003
+ actions2.splice(indexOfDeleteAction, 0, HistoryAction);
4004
+ return actions2;
4005
+ });
4006
+ }
4007
+ };
3675
4008
  const initialState = {
3676
4009
  collectionTypeLinks: [],
3677
4010
  components: [],
@@ -3708,6 +4041,70 @@ const { setInitialData } = actions;
3708
4041
  const reducer = toolkit.combineReducers({
3709
4042
  app: reducer$1
3710
4043
  });
4044
+ const previewApi = contentManagerApi.injectEndpoints({
4045
+ endpoints: (builder) => ({
4046
+ getPreviewUrl: builder.query({
4047
+ query({ query, params }) {
4048
+ return {
4049
+ url: `/content-manager/preview/url/${params.contentType}`,
4050
+ method: "GET",
4051
+ config: {
4052
+ params: query
4053
+ }
4054
+ };
4055
+ }
4056
+ })
4057
+ })
4058
+ });
4059
+ const { useGetPreviewUrlQuery } = previewApi;
4060
+ const PreviewSidePanel = ({ model, documentId, document }) => {
4061
+ const { formatMessage } = reactIntl.useIntl();
4062
+ const { trackUsage } = strapiAdmin.useTracking();
4063
+ const [{ query }] = strapiAdmin.useQueryParams();
4064
+ const { data, error } = useGetPreviewUrlQuery({
4065
+ params: {
4066
+ contentType: model
4067
+ },
4068
+ query: {
4069
+ documentId,
4070
+ locale: document?.locale,
4071
+ status: document?.status
4072
+ }
4073
+ });
4074
+ if (!data?.data?.url || error) {
4075
+ return null;
4076
+ }
4077
+ const handleClick = () => {
4078
+ trackUsage("willOpenPreview");
4079
+ };
4080
+ return {
4081
+ title: formatMessage({ id: "content-manager.preview.panel.title", defaultMessage: "Preview" }),
4082
+ content: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
4083
+ designSystem.Button,
4084
+ {
4085
+ variant: "tertiary",
4086
+ tag: reactRouterDom.Link,
4087
+ to: { pathname: "preview", search: qs.stringify(query, { encode: false }) },
4088
+ onClick: handleClick,
4089
+ flex: "auto",
4090
+ children: formatMessage({
4091
+ id: "content-manager.preview.panel.button",
4092
+ defaultMessage: "Open preview"
4093
+ })
4094
+ }
4095
+ ) })
4096
+ };
4097
+ };
4098
+ const FEATURE_ID = "preview";
4099
+ const previewAdmin = {
4100
+ bootstrap(app) {
4101
+ if (!window.strapi.future.isEnabled(FEATURE_ID)) {
4102
+ return;
4103
+ }
4104
+ const contentManagerPluginApis = app.getPlugin("content-manager").apis;
4105
+ contentManagerPluginApis.addEditViewSidePanel([PreviewSidePanel]);
4106
+ }
4107
+ };
3711
4108
  const index = {
3712
4109
  register(app) {
3713
4110
  const cm = new ContentManagerPlugin();
@@ -3727,7 +4124,7 @@ const index = {
3727
4124
  app.router.addRoute({
3728
4125
  path: "content-manager/*",
3729
4126
  lazy: async () => {
3730
- const { Layout } = await Promise.resolve().then(() => require("./layout-Dm6fbiQj.js"));
4127
+ const { Layout } = await Promise.resolve().then(() => require("./layout-DslPJR5e.js"));
3731
4128
  return {
3732
4129
  Component: Layout
3733
4130
  };
@@ -3736,10 +4133,18 @@ const index = {
3736
4133
  });
3737
4134
  app.registerPlugin(cm.config);
3738
4135
  },
4136
+ bootstrap(app) {
4137
+ if (typeof historyAdmin.bootstrap === "function") {
4138
+ historyAdmin.bootstrap(app);
4139
+ }
4140
+ if (typeof previewAdmin.bootstrap === "function") {
4141
+ previewAdmin.bootstrap(app);
4142
+ }
4143
+ },
3739
4144
  async registerTrads({ locales }) {
3740
4145
  const importedTrads = await Promise.all(
3741
4146
  locales.map((locale) => {
3742
- 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 }) => {
4147
+ 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-CHOp_xJv.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 }) => {
3743
4148
  return {
3744
4149
  data: prefixPluginTranslations(data, PLUGIN_ID),
3745
4150
  locale
@@ -3757,6 +4162,7 @@ const index = {
3757
4162
  };
3758
4163
  exports.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD;
3759
4164
  exports.BulkActionsRenderer = BulkActionsRenderer;
4165
+ exports.CLONE_PATH = CLONE_PATH;
3760
4166
  exports.COLLECTION_TYPES = COLLECTION_TYPES;
3761
4167
  exports.CREATOR_FIELDS = CREATOR_FIELDS;
3762
4168
  exports.DEFAULT_SETTINGS = DEFAULT_SETTINGS;
@@ -3784,6 +4190,7 @@ exports.getMainField = getMainField;
3784
4190
  exports.getTranslation = getTranslation;
3785
4191
  exports.index = index;
3786
4192
  exports.setInitialData = setInitialData;
4193
+ exports.useContentManagerContext = useContentManagerContext;
3787
4194
  exports.useContentTypeSchema = useContentTypeSchema;
3788
4195
  exports.useDoc = useDoc;
3789
4196
  exports.useDocLayout = useDocLayout;
@@ -3795,5 +4202,6 @@ exports.useGetAllContentTypeSettingsQuery = useGetAllContentTypeSettingsQuery;
3795
4202
  exports.useGetAllDocumentsQuery = useGetAllDocumentsQuery;
3796
4203
  exports.useGetContentTypeConfigurationQuery = useGetContentTypeConfigurationQuery;
3797
4204
  exports.useGetInitialDataQuery = useGetInitialDataQuery;
4205
+ exports.useGetPreviewUrlQuery = useGetPreviewUrlQuery;
3798
4206
  exports.useUpdateContentTypeConfigurationMutation = useUpdateContentTypeConfigurationMutation;
3799
- //# sourceMappingURL=index-DVPWZkbS.js.map
4207
+ //# sourceMappingURL=index-CltLAahy.js.map