@strapi/content-manager 5.0.0-beta.7 → 5.0.0-beta.9

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 (146) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-uTMkLI60.mjs → ComponentConfigurationPage-BMajAl1u.mjs} +3 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-uTMkLI60.mjs.map → ComponentConfigurationPage-BMajAl1u.mjs.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-DMq0wvcL.js → ComponentConfigurationPage-y_7iLdmB.js} +3 -3
  4. package/dist/_chunks/{ComponentConfigurationPage-DMq0wvcL.js.map → ComponentConfigurationPage-y_7iLdmB.js.map} +1 -1
  5. package/dist/_chunks/{ComponentIcon-BBQsYCVn.js → ComponentIcon-BXdiCGQp.js} +8 -2
  6. package/dist/_chunks/ComponentIcon-BXdiCGQp.js.map +1 -0
  7. package/dist/_chunks/{ComponentIcon-BOFnK76n.mjs → ComponentIcon-u4bIXTFY.mjs} +9 -3
  8. package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -0
  9. package/dist/_chunks/{EditConfigurationPage-BFpQwwbc.js → EditConfigurationPage-CPVB8Uqc.js} +3 -3
  10. package/dist/_chunks/{EditConfigurationPage-BFpQwwbc.js.map → EditConfigurationPage-CPVB8Uqc.js.map} +1 -1
  11. package/dist/_chunks/{EditConfigurationPage-B2HhCh-b.mjs → EditConfigurationPage-CcOoD26O.mjs} +3 -3
  12. package/dist/_chunks/{EditConfigurationPage-B2HhCh-b.mjs.map → EditConfigurationPage-CcOoD26O.mjs.map} +1 -1
  13. package/dist/_chunks/{EditViewPage-CXXue16T.js → EditViewPage-CTTDHKkQ.js} +5 -5
  14. package/dist/_chunks/{EditViewPage-CXXue16T.js.map → EditViewPage-CTTDHKkQ.js.map} +1 -1
  15. package/dist/_chunks/{EditViewPage-BVIrgjyG.mjs → EditViewPage-DWb0DE7R.mjs} +5 -5
  16. package/dist/_chunks/{EditViewPage-BVIrgjyG.mjs.map → EditViewPage-DWb0DE7R.mjs.map} +1 -1
  17. package/dist/_chunks/{Field-ZgzKlgxR.js → Field-C5Z1Ivdv.js} +240 -357
  18. package/dist/_chunks/Field-C5Z1Ivdv.js.map +1 -0
  19. package/dist/_chunks/{Field-0_2h1vuK.mjs → Field-DnStdvQw.mjs} +240 -357
  20. package/dist/_chunks/Field-DnStdvQw.mjs.map +1 -0
  21. package/dist/_chunks/{Form-DgTc2qkx.js → Form-B81OtW-k.js} +9 -6
  22. package/dist/_chunks/Form-B81OtW-k.js.map +1 -0
  23. package/dist/_chunks/{Form-B7TUnQDd.mjs → Form-DqGgE55Q.mjs} +9 -6
  24. package/dist/_chunks/Form-DqGgE55Q.mjs.map +1 -0
  25. package/dist/_chunks/{History-DtHjQuqM.js → History-4NbOq2dX.js} +97 -15
  26. package/dist/_chunks/History-4NbOq2dX.js.map +1 -0
  27. package/dist/_chunks/{History-Dug_4HIA.mjs → History-DS6-HCYX.mjs} +97 -15
  28. package/dist/_chunks/History-DS6-HCYX.mjs.map +1 -0
  29. package/dist/_chunks/{ListConfigurationPage-BuSdTjfa.js → ListConfigurationPage-CpfstlYY.js} +2 -2
  30. package/dist/_chunks/{ListConfigurationPage-BuSdTjfa.js.map → ListConfigurationPage-CpfstlYY.js.map} +1 -1
  31. package/dist/_chunks/{ListConfigurationPage-CmEeNg6T.mjs → ListConfigurationPage-DQJJltko.mjs} +2 -2
  32. package/dist/_chunks/{ListConfigurationPage-CmEeNg6T.mjs.map → ListConfigurationPage-DQJJltko.mjs.map} +1 -1
  33. package/dist/_chunks/{ListViewPage-CExWwa4S.js → ListViewPage-CA3I75m5.js} +23 -18
  34. package/dist/_chunks/ListViewPage-CA3I75m5.js.map +1 -0
  35. package/dist/_chunks/{ListViewPage-Dsoa3wEA.mjs → ListViewPage-nQrOQuVo.mjs} +21 -17
  36. package/dist/_chunks/ListViewPage-nQrOQuVo.mjs.map +1 -0
  37. package/dist/_chunks/{NoContentTypePage-Dh38hBXB.mjs → NoContentTypePage-DbnHE22g.mjs} +2 -2
  38. package/dist/_chunks/{NoContentTypePage-Dh38hBXB.mjs.map → NoContentTypePage-DbnHE22g.mjs.map} +1 -1
  39. package/dist/_chunks/{NoContentTypePage-DCUL8gVi.js → NoContentTypePage-Dldu-_Mx.js} +2 -2
  40. package/dist/_chunks/{NoContentTypePage-DCUL8gVi.js.map → NoContentTypePage-Dldu-_Mx.js.map} +1 -1
  41. package/dist/_chunks/{NoPermissionsPage-BK-XCpIy.js → NoPermissionsPage-CO2MK200.js} +2 -2
  42. package/dist/_chunks/{NoPermissionsPage-BK-XCpIy.js.map → NoPermissionsPage-CO2MK200.js.map} +1 -1
  43. package/dist/_chunks/{NoPermissionsPage-Dt2O40ey.mjs → NoPermissionsPage-fOIkQM0v.mjs} +2 -2
  44. package/dist/_chunks/{NoPermissionsPage-Dt2O40ey.mjs.map → NoPermissionsPage-fOIkQM0v.mjs.map} +1 -1
  45. package/dist/_chunks/{Relations-DZyjWZHl.mjs → Relations-BDRl99Ux.mjs} +8 -6
  46. package/dist/_chunks/{Relations-DZyjWZHl.mjs.map → Relations-BDRl99Ux.mjs.map} +1 -1
  47. package/dist/_chunks/{Relations-CNypkp-g.js → Relations-DG2jnOcr.js} +8 -6
  48. package/dist/_chunks/{Relations-CNypkp-g.js.map → Relations-DG2jnOcr.js.map} +1 -1
  49. package/dist/_chunks/{en-MBPul9Su.mjs → en-Ux26r5pl.mjs} +7 -1
  50. package/dist/_chunks/{en-MBPul9Su.mjs.map → en-Ux26r5pl.mjs.map} +1 -1
  51. package/dist/_chunks/{en-C-V1_90f.js → en-fbKQxLGn.js} +7 -1
  52. package/dist/_chunks/{en-C-V1_90f.js.map → en-fbKQxLGn.js.map} +1 -1
  53. package/dist/_chunks/{index-DFK7LwDW.js → index-BZoNZMXL.js} +1528 -779
  54. package/dist/_chunks/index-BZoNZMXL.js.map +1 -0
  55. package/dist/_chunks/{index-B3c-4it4.mjs → index-Drt2DN7v.mjs} +1552 -803
  56. package/dist/_chunks/index-Drt2DN7v.mjs.map +1 -0
  57. package/dist/_chunks/{layout-B5cm7cZj.mjs → layout-BzAbmoO6.mjs} +20 -15
  58. package/dist/_chunks/layout-BzAbmoO6.mjs.map +1 -0
  59. package/dist/_chunks/{layout-DLih5-_W.js → layout-DEYBqgF1.js} +20 -15
  60. package/dist/_chunks/layout-DEYBqgF1.js.map +1 -0
  61. package/dist/_chunks/{relations-CTvkuINQ.js → relations-D0eZ4VWw.js} +2 -2
  62. package/dist/_chunks/{relations-CTvkuINQ.js.map → relations-D0eZ4VWw.js.map} +1 -1
  63. package/dist/_chunks/{relations-BZkrMa2z.mjs → relations-D26zVRdi.mjs} +2 -2
  64. package/dist/_chunks/{relations-BZkrMa2z.mjs.map → relations-D26zVRdi.mjs.map} +1 -1
  65. package/dist/_chunks/usePrev-B9w_-eYc.js +15 -0
  66. package/dist/_chunks/usePrev-B9w_-eYc.js.map +1 -0
  67. package/dist/_chunks/usePrev-DH6iah0A.mjs +16 -0
  68. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +1 -0
  69. package/dist/admin/index.js +2 -1
  70. package/dist/admin/index.js.map +1 -1
  71. package/dist/admin/index.mjs +8 -7
  72. package/dist/admin/src/components/ComponentIcon.d.ts +6 -3
  73. package/dist/admin/src/content-manager.d.ts +3 -3
  74. package/dist/admin/src/exports.d.ts +1 -0
  75. package/dist/admin/src/history/components/VersionInputRenderer.d.ts +1 -1
  76. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  77. package/dist/admin/src/hooks/useDocument.d.ts +5 -8
  78. package/dist/admin/src/hooks/useDocumentActions.d.ts +24 -3
  79. package/dist/admin/src/hooks/useDocumentLayout.d.ts +1 -1
  80. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +3 -1
  81. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksInput.d.ts +3 -3
  82. package/dist/admin/src/pages/EditView/components/FormInputs/Component/Input.d.ts +2 -2
  83. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.d.ts +3 -5
  84. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +1 -1
  85. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +2 -15
  86. package/dist/admin/src/pages/EditView/components/FormInputs/UID.d.ts +2 -2
  87. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/Field.d.ts +2 -2
  88. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +56 -35
  89. package/dist/admin/src/pages/EditView/components/InputRenderer.d.ts +2 -10
  90. package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +9 -26
  91. package/dist/admin/src/services/api.d.ts +2 -3
  92. package/dist/admin/src/services/components.d.ts +2 -2
  93. package/dist/admin/src/services/contentTypes.d.ts +5 -5
  94. package/dist/admin/src/services/documents.d.ts +29 -17
  95. package/dist/admin/src/services/init.d.ts +2 -2
  96. package/dist/admin/src/services/relations.d.ts +3 -3
  97. package/dist/admin/src/services/uid.d.ts +3 -3
  98. package/dist/admin/src/utils/api.d.ts +4 -17
  99. package/dist/admin/src/utils/validation.d.ts +1 -6
  100. package/dist/server/index.js +247 -127
  101. package/dist/server/index.js.map +1 -1
  102. package/dist/server/index.mjs +249 -129
  103. package/dist/server/index.mjs.map +1 -1
  104. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  105. package/dist/server/src/controllers/single-types.d.ts.map +1 -1
  106. package/dist/server/src/controllers/utils/metadata.d.ts +8 -0
  107. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -0
  108. package/dist/server/src/controllers/validation/dimensions.d.ts +9 -0
  109. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -0
  110. package/dist/server/src/history/services/history.d.ts.map +1 -1
  111. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  112. package/dist/server/src/index.d.ts +12 -33
  113. package/dist/server/src/index.d.ts.map +1 -1
  114. package/dist/server/src/services/document-manager.d.ts +6 -6
  115. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  116. package/dist/server/src/services/document-metadata.d.ts +8 -29
  117. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  118. package/dist/server/src/services/index.d.ts +12 -33
  119. package/dist/server/src/services/index.d.ts.map +1 -1
  120. package/dist/server/src/services/utils/populate.d.ts +8 -1
  121. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  122. package/dist/shared/contracts/collection-types.d.ts +11 -5
  123. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  124. package/dist/shared/contracts/relations.d.ts +2 -2
  125. package/dist/shared/contracts/relations.d.ts.map +1 -1
  126. package/package.json +11 -11
  127. package/dist/_chunks/ComponentIcon-BBQsYCVn.js.map +0 -1
  128. package/dist/_chunks/ComponentIcon-BOFnK76n.mjs.map +0 -1
  129. package/dist/_chunks/Field-0_2h1vuK.mjs.map +0 -1
  130. package/dist/_chunks/Field-ZgzKlgxR.js.map +0 -1
  131. package/dist/_chunks/Form-B7TUnQDd.mjs.map +0 -1
  132. package/dist/_chunks/Form-DgTc2qkx.js.map +0 -1
  133. package/dist/_chunks/History-DtHjQuqM.js.map +0 -1
  134. package/dist/_chunks/History-Dug_4HIA.mjs.map +0 -1
  135. package/dist/_chunks/ListViewPage-CExWwa4S.js.map +0 -1
  136. package/dist/_chunks/ListViewPage-Dsoa3wEA.mjs.map +0 -1
  137. package/dist/_chunks/index-B3c-4it4.mjs.map +0 -1
  138. package/dist/_chunks/index-DFK7LwDW.js.map +0 -1
  139. package/dist/_chunks/layout-B5cm7cZj.mjs.map +0 -1
  140. package/dist/_chunks/layout-DLih5-_W.js.map +0 -1
  141. package/dist/_chunks/urls-CbOsUOoW.mjs +0 -7
  142. package/dist/_chunks/urls-CbOsUOoW.mjs.map +0 -1
  143. package/dist/_chunks/urls-DzZya_gm.js +0 -6
  144. package/dist/_chunks/urls-DzZya_gm.js.map +0 -1
  145. package/dist/server/src/controllers/utils/dimensions.d.ts +0 -5
  146. package/dist/server/src/controllers/utils/dimensions.d.ts.map +0 -1
@@ -9,7 +9,6 @@ const React = require("react");
9
9
  const designSystem = require("@strapi/design-system");
10
10
  const styledComponents = require("styled-components");
11
11
  const yup = require("yup");
12
- const react = require("@reduxjs/toolkit/query/react");
13
12
  const pipe = require("lodash/fp/pipe");
14
13
  const dateFns = require("date-fns");
15
14
  const toolkit = require("@reduxjs/toolkit");
@@ -177,9 +176,8 @@ const DocumentRBAC = ({ children, permissions }) => {
177
176
  const name = removeNumericalStrings(fieldName.split("."));
178
177
  const componentFieldNames = fieldsUserCanAction.filter((field) => field.split(".").length > 1);
179
178
  if (fieldType === "component") {
180
- const componentOrDynamicZoneFields = componentFieldNames.map((field) => field.split("."));
181
- return componentOrDynamicZoneFields.some((field) => {
182
- return field.includes(fieldName);
179
+ return componentFieldNames.some((field) => {
180
+ return field.includes(name.join("."));
183
181
  });
184
182
  }
185
183
  if (name.length > 1) {
@@ -209,92 +207,8 @@ const extractAndDedupeFields = (permissions = []) => permissions.flatMap((permis
209
207
  (field, index2, arr) => arr.indexOf(field) === index2 && typeof field === "string"
210
208
  );
211
209
  const removeNumericalStrings = (arr) => arr.filter((item) => isNaN(Number(item)));
212
- const buildValidParams = (query) => {
213
- if (!query)
214
- return query;
215
- const { plugins: _, ...validQueryParams } = {
216
- ...query,
217
- ...Object.values(query?.plugins ?? {}).reduce(
218
- (acc, current) => Object.assign(acc, current),
219
- {}
220
- )
221
- };
222
- if ("_q" in validQueryParams) {
223
- validQueryParams._q = encodeURIComponent(validQueryParams._q);
224
- }
225
- return validQueryParams;
226
- };
227
- const fetchBaseQuery = () => async (query, { signal }) => {
228
- try {
229
- const { get, post, del, put } = strapiAdmin.getFetchClient();
230
- if (typeof query === "string") {
231
- const result = await get(query, {
232
- signal
233
- });
234
- return { data: result.data };
235
- } else {
236
- const { url, method = "GET", data, config } = query;
237
- if (method === "POST") {
238
- const result2 = await post(url, data, {
239
- ...config,
240
- signal
241
- });
242
- return { data: result2.data };
243
- }
244
- if (method === "DELETE") {
245
- const result2 = await del(url, {
246
- ...config,
247
- signal
248
- });
249
- return { data: result2.data };
250
- }
251
- if (method === "PUT") {
252
- const result2 = await put(url, data, {
253
- ...config,
254
- signal
255
- });
256
- return { data: result2.data };
257
- }
258
- const result = await get(url, {
259
- ...config,
260
- signal
261
- });
262
- return { data: result.data };
263
- }
264
- } catch (err) {
265
- if (strapiAdmin.isFetchError(err)) {
266
- if (typeof err.response?.data === "object" && err.response?.data !== null && "error" in err.response?.data) {
267
- return { data: void 0, error: err.response?.data.error };
268
- } else {
269
- return {
270
- data: void 0,
271
- error: {
272
- name: "UnknownError",
273
- message: "There was an unknown error response from the API",
274
- details: err.response,
275
- status: err.status
276
- }
277
- };
278
- }
279
- }
280
- const error = err;
281
- return {
282
- data: void 0,
283
- error: {
284
- name: error.name,
285
- message: error.message,
286
- stack: error.stack
287
- }
288
- };
289
- }
290
- };
291
- const isBaseQueryError = (error) => {
292
- return error.name !== void 0;
293
- };
294
- const contentManagerApi = react.createApi({
295
- reducerPath: "contentManagerApi",
296
- baseQuery: fetchBaseQuery(),
297
- tagTypes: [
210
+ const contentManagerApi = strapiAdmin.adminApi.enhanceEndpoints({
211
+ addTagTypes: [
298
212
  "ComponentConfiguration",
299
213
  "ContentTypesConfiguration",
300
214
  "ContentTypeSettings",
@@ -302,8 +216,7 @@ const contentManagerApi = react.createApi({
302
216
  "InitialData",
303
217
  "HistoryVersion",
304
218
  "Relations"
305
- ],
306
- endpoints: () => ({})
219
+ ]
307
220
  });
308
221
  const documentApi = contentManagerApi.injectEndpoints({
309
222
  endpoints: (builder) => ({
@@ -359,12 +272,15 @@ const documentApi = contentManagerApi.injectEndpoints({
359
272
  ]
360
273
  }),
361
274
  deleteManyDocuments: builder.mutation({
362
- query: ({ model, ...body }) => ({
275
+ query: ({ model, params, ...body }) => ({
363
276
  url: `/content-manager/collection-types/${model}/actions/bulkDelete`,
364
277
  method: "POST",
365
- data: body
278
+ data: body,
279
+ config: {
280
+ params
281
+ }
366
282
  }),
367
- invalidatesTags: (_res, _error, { model, documentIds }) => documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` }))
283
+ invalidatesTags: (_res, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
368
284
  }),
369
285
  discardDocument: builder.mutation({
370
286
  query: ({ collectionType, model, documentId, params }) => ({
@@ -475,10 +391,13 @@ const documentApi = contentManagerApi.injectEndpoints({
475
391
  }
476
392
  }),
477
393
  publishManyDocuments: builder.mutation({
478
- query: ({ model, ...body }) => ({
394
+ query: ({ model, params, ...body }) => ({
479
395
  url: `/content-manager/collection-types/${model}/actions/bulkPublish`,
480
396
  method: "POST",
481
- data: body
397
+ data: body,
398
+ config: {
399
+ params
400
+ }
482
401
  }),
483
402
  invalidatesTags: (_res, _error, { model, documentIds }) => documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` }))
484
403
  }),
@@ -520,10 +439,13 @@ const documentApi = contentManagerApi.injectEndpoints({
520
439
  }
521
440
  }),
522
441
  unpublishManyDocuments: builder.mutation({
523
- query: ({ model, ...body }) => ({
442
+ query: ({ model, params, ...body }) => ({
524
443
  url: `/content-manager/collection-types/${model}/actions/bulkUnpublish`,
525
444
  method: "POST",
526
- data: body
445
+ data: body,
446
+ config: {
447
+ params
448
+ }
527
449
  }),
528
450
  invalidatesTags: (_res, _error, { model, documentIds }) => documentIds.map((id) => ({ type: "Document", id: `${model}_${id}` }))
529
451
  })
@@ -547,6 +469,24 @@ const {
547
469
  useUnpublishDocumentMutation,
548
470
  useUnpublishManyDocumentsMutation
549
471
  } = documentApi;
472
+ const buildValidParams = (query) => {
473
+ if (!query)
474
+ return query;
475
+ const { plugins: _, ...validQueryParams } = {
476
+ ...query,
477
+ ...Object.values(query?.plugins ?? {}).reduce(
478
+ (acc, current) => Object.assign(acc, current),
479
+ {}
480
+ )
481
+ };
482
+ if ("_q" in validQueryParams) {
483
+ validQueryParams._q = encodeURIComponent(validQueryParams._q);
484
+ }
485
+ return validQueryParams;
486
+ };
487
+ const isBaseQueryError = (error) => {
488
+ return error.name !== void 0;
489
+ };
550
490
  const createYupSchema = (attributes = {}, components = {}) => {
551
491
  const createModelSchema = (attributes2) => yup__namespace.object().shape(
552
492
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
@@ -586,10 +526,14 @@ const createYupSchema = (attributes = {}, components = {}) => {
586
526
  yup__namespace.array().of(
587
527
  yup__namespace.lazy(
588
528
  (data) => {
589
- const { attributes: attributes3 } = components[data.__component];
590
- return yup__namespace.object().shape({
529
+ const attributes3 = components?.[data?.__component]?.attributes;
530
+ const validation = yup__namespace.object().shape({
591
531
  __component: yup__namespace.string().required().oneOf(Object.keys(components))
592
- }).nullable(false).concat(createModelSchema(attributes3));
532
+ }).nullable(false);
533
+ if (!attributes3) {
534
+ return validation;
535
+ }
536
+ return validation.concat(createModelSchema(attributes3));
593
537
  }
594
538
  )
595
539
  )
@@ -599,11 +543,25 @@ const createYupSchema = (attributes = {}, components = {}) => {
599
543
  return {
600
544
  ...acc,
601
545
  [name]: transformSchema(
602
- yup__namespace.array().of(
603
- yup__namespace.object().shape({
604
- id: yup__namespace.string().required()
605
- })
606
- )
546
+ yup__namespace.lazy((value) => {
547
+ if (!value) {
548
+ return yup__namespace.mixed().nullable(true);
549
+ } else if (Array.isArray(value)) {
550
+ return yup__namespace.array().of(
551
+ yup__namespace.object().shape({
552
+ id: yup__namespace.string().required()
553
+ })
554
+ );
555
+ } else if (typeof value === "object") {
556
+ return yup__namespace.object();
557
+ } else {
558
+ return yup__namespace.mixed().test(
559
+ "type-error",
560
+ "Relation values must be either null, an array of objects with {id} or an object.",
561
+ () => false
562
+ );
563
+ }
564
+ })
607
565
  )
608
566
  };
609
567
  default:
@@ -668,7 +626,12 @@ const addRequiredValidation = (attribute) => (schema) => {
668
626
  defaultMessage: "This field is required."
669
627
  });
670
628
  }
671
- return schema.nullable();
629
+ return schema?.nullable ? schema.nullable() : (
630
+ // In some cases '.nullable' will not be available on the schema.
631
+ // e.g. when the schema has been built using yup.lazy (e.g. for relations).
632
+ // In these cases we should just return the schema as it is.
633
+ schema
634
+ );
672
635
  };
673
636
  const addMinLengthValidation = (attribute) => (schema) => {
674
637
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
@@ -740,24 +703,6 @@ const addRegexValidation = (attribute) => (schema) => {
740
703
  }
741
704
  return schema;
742
705
  };
743
- const extractValuesFromYupError = (errorType, errorParams) => {
744
- if (!errorType || !errorParams) {
745
- return {};
746
- }
747
- return {
748
- [errorType]: errorParams[errorType]
749
- };
750
- };
751
- const getInnerErrors = (error) => (error?.inner || []).reduce((acc, currentError) => {
752
- if (currentError.path) {
753
- acc[currentError.path.split("[").join(".").split("]").join("")] = {
754
- id: currentError.message,
755
- defaultMessage: currentError.message,
756
- values: extractValuesFromYupError(currentError?.type, currentError?.params)
757
- };
758
- }
759
- return acc;
760
- }, {});
761
706
  const initApi = contentManagerApi.injectEndpoints({
762
707
  endpoints: (builder) => ({
763
708
  getInitialData: builder.query({
@@ -771,27 +716,20 @@ const { useGetInitialDataQuery } = initApi;
771
716
  const useContentTypeSchema = (model) => {
772
717
  const { toggleNotification } = strapiAdmin.useNotification();
773
718
  const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
774
- const { components, contentType, contentTypes, error, isLoading, isFetching } = useGetInitialDataQuery(void 0, {
775
- selectFromResult: (res) => {
776
- const contentType2 = res.data?.contentTypes.find((ct) => ct.uid === model);
777
- const componentsByKey = res.data?.components.reduce(
778
- (acc, component) => {
779
- acc[component.uid] = component;
780
- return acc;
781
- },
782
- {}
783
- );
784
- const components2 = extractContentTypeComponents(contentType2?.attributes, componentsByKey);
785
- return {
786
- isLoading: res.isLoading,
787
- isFetching: res.isFetching,
788
- error: res.error,
789
- components: Object.keys(components2).length === 0 ? void 0 : components2,
790
- contentType: contentType2,
791
- contentTypes: res.data?.contentTypes ?? []
792
- };
793
- }
794
- });
719
+ const { data, error, isLoading, isFetching } = useGetInitialDataQuery(void 0);
720
+ const { components, contentType, contentTypes } = React__namespace.useMemo(() => {
721
+ const contentType2 = data?.contentTypes.find((ct) => ct.uid === model);
722
+ const componentsByKey = data?.components.reduce((acc, component) => {
723
+ acc[component.uid] = component;
724
+ return acc;
725
+ }, {});
726
+ const components2 = extractContentTypeComponents(contentType2?.attributes, componentsByKey);
727
+ return {
728
+ components: Object.keys(components2).length === 0 ? void 0 : components2,
729
+ contentType: contentType2,
730
+ contentTypes: data?.contentTypes ?? []
731
+ };
732
+ }, [model, data]);
795
733
  React__namespace.useEffect(() => {
796
734
  if (error) {
797
735
  toggleNotification({
@@ -874,7 +812,7 @@ const useDocument = (args, opts) => {
874
812
  return null;
875
813
  } catch (error2) {
876
814
  if (error2 instanceof yup.ValidationError) {
877
- return getInnerErrors(error2);
815
+ return strapiAdmin.getYupValidationErrors(error2);
878
816
  }
879
817
  throw error2;
880
818
  }
@@ -970,14 +908,53 @@ const useDocumentActions = () => {
970
908
  },
971
909
  [trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
972
910
  );
911
+ const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
912
+ const deleteMany = React__namespace.useCallback(
913
+ async ({ model, documentIds, params }) => {
914
+ try {
915
+ trackUsage("willBulkDeleteEntries");
916
+ const res = await deleteManyDocuments({
917
+ model,
918
+ documentIds,
919
+ params
920
+ });
921
+ if ("error" in res) {
922
+ toggleNotification({
923
+ type: "danger",
924
+ message: formatAPIError(res.error)
925
+ });
926
+ return { error: res.error };
927
+ }
928
+ toggleNotification({
929
+ type: "success",
930
+ title: formatMessage({
931
+ id: getTranslation("success.records.delete"),
932
+ defaultMessage: "Successfully deleted."
933
+ }),
934
+ message: ""
935
+ });
936
+ trackUsage("didBulkDeleteEntries");
937
+ return res.data;
938
+ } catch (err) {
939
+ toggleNotification({
940
+ type: "danger",
941
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
942
+ });
943
+ trackUsage("didNotBulkDeleteEntries");
944
+ throw err;
945
+ }
946
+ },
947
+ [trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
948
+ );
973
949
  const [discardDocument] = useDiscardDocumentMutation();
974
950
  const discard = React__namespace.useCallback(
975
- async ({ collectionType, model, documentId }) => {
951
+ async ({ collectionType, model, documentId, params }) => {
976
952
  try {
977
953
  const res = await discardDocument({
978
954
  collectionType,
979
955
  model,
980
- documentId
956
+ documentId,
957
+ params
981
958
  });
982
959
  if ("error" in res) {
983
960
  toggleNotification({
@@ -1039,6 +1016,43 @@ const useDocumentActions = () => {
1039
1016
  },
1040
1017
  [trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
1041
1018
  );
1019
+ const [publishManyDocuments] = usePublishManyDocumentsMutation();
1020
+ const publishMany = React__namespace.useCallback(
1021
+ async ({ model, documentIds, params }) => {
1022
+ try {
1023
+ const res = await publishManyDocuments({
1024
+ model,
1025
+ documentIds,
1026
+ params
1027
+ });
1028
+ if ("error" in res) {
1029
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1030
+ return { error: res.error };
1031
+ }
1032
+ toggleNotification({
1033
+ type: "success",
1034
+ message: formatMessage({
1035
+ id: getTranslation("success.record.publish"),
1036
+ defaultMessage: "Published document"
1037
+ })
1038
+ });
1039
+ return res.data;
1040
+ } catch (err) {
1041
+ toggleNotification({
1042
+ type: "danger",
1043
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1044
+ });
1045
+ throw err;
1046
+ }
1047
+ },
1048
+ [
1049
+ // trackUsage,
1050
+ publishManyDocuments,
1051
+ toggleNotification,
1052
+ formatMessage,
1053
+ formatAPIError
1054
+ ]
1055
+ );
1042
1056
  const [updateDocument] = useUpdateDocumentMutation();
1043
1057
  const update = React__namespace.useCallback(
1044
1058
  async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
@@ -1113,6 +1127,41 @@ const useDocumentActions = () => {
1113
1127
  },
1114
1128
  [trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
1115
1129
  );
1130
+ const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
1131
+ const unpublishMany = React__namespace.useCallback(
1132
+ async ({ model, documentIds, params }) => {
1133
+ try {
1134
+ trackUsage("willBulkUnpublishEntries");
1135
+ const res = await unpublishManyDocuments({
1136
+ model,
1137
+ documentIds,
1138
+ params
1139
+ });
1140
+ if ("error" in res) {
1141
+ toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1142
+ return { error: res.error };
1143
+ }
1144
+ trackUsage("didBulkUnpublishEntries");
1145
+ toggleNotification({
1146
+ type: "success",
1147
+ title: formatMessage({
1148
+ id: getTranslation("success.records.unpublish"),
1149
+ defaultMessage: "Successfully unpublished."
1150
+ }),
1151
+ message: ""
1152
+ });
1153
+ return res.data;
1154
+ } catch (err) {
1155
+ toggleNotification({
1156
+ type: "danger",
1157
+ message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
1158
+ });
1159
+ trackUsage("didNotBulkUnpublishEntries");
1160
+ throw err;
1161
+ }
1162
+ },
1163
+ [trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
1164
+ );
1116
1165
  const [createDocument] = useCreateDocumentMutation();
1117
1166
  const create = React__namespace.useCallback(
1118
1167
  async ({ model, params }, data, trackerProperty) => {
@@ -1226,15 +1275,18 @@ const useDocumentActions = () => {
1226
1275
  clone,
1227
1276
  create,
1228
1277
  delete: _delete,
1278
+ deleteMany,
1229
1279
  discard,
1230
1280
  getDocument,
1231
1281
  publish,
1282
+ publishMany,
1232
1283
  unpublish,
1284
+ unpublishMany,
1233
1285
  update
1234
1286
  };
1235
1287
  };
1236
1288
  const ProtectedHistoryPage = React.lazy(
1237
- () => Promise.resolve().then(() => require("./History-DtHjQuqM.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1289
+ () => Promise.resolve().then(() => require("./History-4NbOq2dX.js")).then((mod) => ({ default: mod.ProtectedHistoryPage }))
1238
1290
  );
1239
1291
  const routes$1 = [
1240
1292
  {
@@ -1247,31 +1299,31 @@ const routes$1 = [
1247
1299
  }
1248
1300
  ];
1249
1301
  const ProtectedEditViewPage = React.lazy(
1250
- () => Promise.resolve().then(() => require("./EditViewPage-CXXue16T.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1302
+ () => Promise.resolve().then(() => require("./EditViewPage-CTTDHKkQ.js")).then((mod) => ({ default: mod.ProtectedEditViewPage }))
1251
1303
  );
1252
1304
  const ProtectedListViewPage = React.lazy(
1253
- () => Promise.resolve().then(() => require("./ListViewPage-CExWwa4S.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1305
+ () => Promise.resolve().then(() => require("./ListViewPage-CA3I75m5.js")).then((mod) => ({ default: mod.ProtectedListViewPage }))
1254
1306
  );
1255
1307
  const ProtectedListConfiguration = React.lazy(
1256
- () => Promise.resolve().then(() => require("./ListConfigurationPage-BuSdTjfa.js")).then((mod) => ({
1308
+ () => Promise.resolve().then(() => require("./ListConfigurationPage-CpfstlYY.js")).then((mod) => ({
1257
1309
  default: mod.ProtectedListConfiguration
1258
1310
  }))
1259
1311
  );
1260
1312
  const ProtectedEditConfigurationPage = React.lazy(
1261
- () => Promise.resolve().then(() => require("./EditConfigurationPage-BFpQwwbc.js")).then((mod) => ({
1313
+ () => Promise.resolve().then(() => require("./EditConfigurationPage-CPVB8Uqc.js")).then((mod) => ({
1262
1314
  default: mod.ProtectedEditConfigurationPage
1263
1315
  }))
1264
1316
  );
1265
1317
  const ProtectedComponentConfigurationPage = React.lazy(
1266
- () => Promise.resolve().then(() => require("./ComponentConfigurationPage-DMq0wvcL.js")).then((mod) => ({
1318
+ () => Promise.resolve().then(() => require("./ComponentConfigurationPage-y_7iLdmB.js")).then((mod) => ({
1267
1319
  default: mod.ProtectedComponentConfigurationPage
1268
1320
  }))
1269
1321
  );
1270
1322
  const NoPermissions = React.lazy(
1271
- () => Promise.resolve().then(() => require("./NoPermissionsPage-BK-XCpIy.js")).then((mod) => ({ default: mod.NoPermissions }))
1323
+ () => Promise.resolve().then(() => require("./NoPermissionsPage-CO2MK200.js")).then((mod) => ({ default: mod.NoPermissions }))
1272
1324
  );
1273
1325
  const NoContentType = React.lazy(
1274
- () => Promise.resolve().then(() => require("./NoContentTypePage-DCUL8gVi.js")).then((mod) => ({ default: mod.NoContentType }))
1326
+ () => Promise.resolve().then(() => require("./NoContentTypePage-Dldu-_Mx.js")).then((mod) => ({ default: mod.NoContentType }))
1275
1327
  );
1276
1328
  const CollectionTypePages = () => {
1277
1329
  const { collectionType } = reactRouterDom.useParams();
@@ -1583,7 +1635,7 @@ const DocumentActionModal = ({
1583
1635
  title,
1584
1636
  onClose,
1585
1637
  footer: Footer,
1586
- content,
1638
+ content: Content,
1587
1639
  onModalClose
1588
1640
  }) => {
1589
1641
  const id = React__namespace.useId();
@@ -1598,7 +1650,7 @@ const DocumentActionModal = ({
1598
1650
  };
1599
1651
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ModalLayout, { borderRadius: "4px", overflow: "hidden", onClose: handleClose, labelledBy: id, children: [
1600
1652
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: "neutral800", tag: "h2", id, children: title }) }),
1601
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalBody, { children: content }),
1653
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalBody, { children: typeof Content === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose }) : Content }),
1602
1654
  /* @__PURE__ */ jsxRuntime.jsx(
1603
1655
  designSystem.Box,
1604
1656
  {
@@ -1615,7 +1667,7 @@ const DocumentActionModal = ({
1615
1667
  )
1616
1668
  ] });
1617
1669
  };
1618
- const PublishAction = ({
1670
+ const PublishAction$1 = ({
1619
1671
  activeTab,
1620
1672
  documentId,
1621
1673
  model,
@@ -1700,7 +1752,7 @@ const PublishAction = ({
1700
1752
  }
1701
1753
  };
1702
1754
  };
1703
- PublishAction.type = "publish";
1755
+ PublishAction$1.type = "publish";
1704
1756
  const UpdateAction = ({
1705
1757
  activeTab,
1706
1758
  documentId,
@@ -1815,7 +1867,7 @@ const UNPUBLISH_DRAFT_OPTIONS = {
1815
1867
  KEEP: "keep",
1816
1868
  DISCARD: "discard"
1817
1869
  };
1818
- const UnpublishAction = ({
1870
+ const UnpublishAction$1 = ({
1819
1871
  activeTab,
1820
1872
  documentId,
1821
1873
  model,
@@ -1890,6 +1942,7 @@ const UnpublishAction = ({
1890
1942
  direction: "column",
1891
1943
  alignItems: "flex-start",
1892
1944
  tag: "fieldset",
1945
+ borderWidth: 0,
1893
1946
  gap: 3,
1894
1947
  children: [
1895
1948
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { tag: "legend" }),
@@ -1949,7 +2002,7 @@ const UnpublishAction = ({
1949
2002
  position: ["panel", "table-row"]
1950
2003
  };
1951
2004
  };
1952
- UnpublishAction.type = "unpublish";
2005
+ UnpublishAction$1.type = "unpublish";
1953
2006
  const DiscardAction = ({
1954
2007
  activeTab,
1955
2008
  documentId,
@@ -2005,7 +2058,7 @@ const StyledCrossCircle = styledComponents.styled(Icons.CrossCircle)`
2005
2058
  fill: currentColor;
2006
2059
  }
2007
2060
  `;
2008
- const DEFAULT_ACTIONS = [PublishAction, UpdateAction, UnpublishAction, DiscardAction];
2061
+ const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2009
2062
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2010
2063
  const RelativeTime = React__namespace.forwardRef(
2011
2064
  ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
@@ -2272,7 +2325,7 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2272
2325
  id: "app.links.configure-view",
2273
2326
  defaultMessage: "Configure the view"
2274
2327
  }),
2275
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledCog, {}),
2328
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.ListPlus, {}),
2276
2329
  onClick: () => {
2277
2330
  navigate(`../${collectionType}/${model}/configurations/edit`);
2278
2331
  },
@@ -2280,11 +2333,6 @@ const ConfigureTheViewAction = ({ collectionType, model }) => {
2280
2333
  };
2281
2334
  };
2282
2335
  ConfigureTheViewAction.type = "configure-the-view";
2283
- const StyledCog = styledComponents.styled(Icons.Cog)`
2284
- path {
2285
- fill: currentColor;
2286
- }
2287
- `;
2288
2336
  const EditTheModelAction = ({ model }) => {
2289
2337
  const navigate = reactRouterDom.useNavigate();
2290
2338
  const { formatMessage } = reactIntl.useIntl();
@@ -2293,7 +2341,7 @@ const EditTheModelAction = ({ model }) => {
2293
2341
  id: "content-manager.link-to-ctb",
2294
2342
  defaultMessage: "Edit the model"
2295
2343
  }),
2296
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledPencil$1, {}),
2344
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {}),
2297
2345
  onClick: () => {
2298
2346
  navigate(`/plugins/content-type-builder/content-types/${model}`);
2299
2347
  },
@@ -2301,12 +2349,7 @@ const EditTheModelAction = ({ model }) => {
2301
2349
  };
2302
2350
  };
2303
2351
  EditTheModelAction.type = "edit-the-model";
2304
- const StyledPencil$1 = styledComponents.styled(Icons.Pencil)`
2305
- path {
2306
- fill: currentColor;
2307
- }
2308
- `;
2309
- const DeleteAction = ({ documentId, model, collectionType, document }) => {
2352
+ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2310
2353
  const navigate = reactRouterDom.useNavigate();
2311
2354
  const { formatMessage } = reactIntl.useIntl();
2312
2355
  const listViewPathMatch = reactRouterDom.useMatch(LIST_PATH);
@@ -2320,7 +2363,7 @@ const DeleteAction = ({ documentId, model, collectionType, document }) => {
2320
2363
  id: "content-manager.actions.delete.label",
2321
2364
  defaultMessage: "Delete document"
2322
2365
  }),
2323
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledTrash, {}),
2366
+ icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}),
2324
2367
  dialog: {
2325
2368
  type: "dialog",
2326
2369
  title: formatMessage({
@@ -2374,13 +2417,8 @@ const DeleteAction = ({ documentId, model, collectionType, document }) => {
2374
2417
  position: ["header", "table-row"]
2375
2418
  };
2376
2419
  };
2377
- DeleteAction.type = "delete";
2378
- const StyledTrash = styledComponents.styled(Icons.Trash)`
2379
- path {
2380
- fill: currentColor;
2381
- }
2382
- `;
2383
- const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction];
2420
+ DeleteAction$1.type = "delete";
2421
+ const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
2384
2422
  const Panels = () => {
2385
2423
  const isCloning = reactRouterDom.useMatch(CLONE_PATH) !== null;
2386
2424
  const [
@@ -2475,663 +2513,1372 @@ const Panel = React__namespace.forwardRef(({ children, title }, ref) => {
2475
2513
  }
2476
2514
  );
2477
2515
  });
2478
- const DEFAULT_BULK_ACTIONS = [];
2479
- const AutoCloneFailureModalBody = ({ prohibitedFields }) => {
2480
- const { formatMessage } = reactIntl.useIntl();
2481
- const getDefaultErrorMessage = (reason) => {
2482
- switch (reason) {
2483
- case "relation":
2484
- return "Duplicating the relation could remove it from the original entry.";
2485
- case "unique":
2486
- return "Identical values in a unique field are not allowed";
2487
- default:
2488
- return reason;
2489
- }
2490
- };
2491
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2492
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", children: formatMessage({
2493
- id: getTranslation("containers.list.autoCloneModal.title"),
2494
- defaultMessage: "This entry can't be duplicated directly."
2495
- }) }),
2496
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: formatMessage({
2497
- id: getTranslation("containers.list.autoCloneModal.description"),
2498
- defaultMessage: "A new entry will be created with the same content, but you'll have to change the following fields to save it."
2499
- }) }) }),
2500
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { marginTop: 6, gap: 2, direction: "column", alignItems: "stretch", children: prohibitedFields.map(([fieldPath, reason]) => /* @__PURE__ */ jsxRuntime.jsxs(
2501
- designSystem.Flex,
2502
- {
2503
- direction: "column",
2504
- gap: 2,
2505
- alignItems: "flex-start",
2506
- borderColor: "neutral200",
2507
- hasRadius: true,
2508
- padding: 6,
2509
- children: [
2510
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "row", tag: "ol", children: fieldPath.map((pathSegment, index2) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { fontWeight: "semiBold", tag: "li", children: [
2511
- pathSegment,
2512
- index2 !== fieldPath.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
2513
- Icons.ChevronRight,
2514
- {
2515
- fill: "neutral500",
2516
- height: "0.8rem",
2517
- width: "0.8rem",
2518
- style: { margin: "0 0.8rem" }
2519
- }
2520
- )
2521
- ] }, index2)) }),
2522
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", textColor: "neutral600", children: formatMessage({
2523
- id: getTranslation(`containers.list.autoCloneModal.error.${reason}`),
2524
- defaultMessage: getDefaultErrorMessage(reason)
2525
- }) })
2526
- ]
2527
- },
2528
- fieldPath.join()
2529
- )) })
2530
- ] });
2516
+ const HOOKS = {
2517
+ /**
2518
+ * Hook that allows to mutate the displayed headers of the list view table
2519
+ * @constant
2520
+ * @type {string}
2521
+ */
2522
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2523
+ /**
2524
+ * Hook that allows to mutate the CM's collection types links pre-set filters
2525
+ * @constant
2526
+ * @type {string}
2527
+ */
2528
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2529
+ /**
2530
+ * Hook that allows to mutate the CM's edit view layout
2531
+ * @constant
2532
+ * @type {string}
2533
+ */
2534
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2535
+ /**
2536
+ * Hook that allows to mutate the CM's single types links pre-set filters
2537
+ * @constant
2538
+ * @type {string}
2539
+ */
2540
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2531
2541
  };
2532
- const TableActions = ({ document }) => {
2533
- const { formatMessage } = reactIntl.useIntl();
2534
- const { model, collectionType } = useDoc();
2535
- const plugins = strapiAdmin.useStrapiApp("TableActions", (state) => state.plugins);
2536
- const props = {
2537
- activeTab: null,
2538
- model,
2539
- documentId: document.documentId,
2540
- collectionType,
2541
- document
2542
+ const contentTypesApi = contentManagerApi.injectEndpoints({
2543
+ endpoints: (builder) => ({
2544
+ getContentTypeConfiguration: builder.query({
2545
+ query: (uid) => ({
2546
+ url: `/content-manager/content-types/${uid}/configuration`,
2547
+ method: "GET"
2548
+ }),
2549
+ transformResponse: (response) => response.data,
2550
+ providesTags: (_result, _error, uid) => [
2551
+ { type: "ContentTypesConfiguration", id: uid },
2552
+ { type: "ContentTypeSettings", id: "LIST" }
2553
+ ]
2554
+ }),
2555
+ getAllContentTypeSettings: builder.query({
2556
+ query: () => "/content-manager/content-types-settings",
2557
+ transformResponse: (response) => response.data,
2558
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2559
+ }),
2560
+ updateContentTypeConfiguration: builder.mutation({
2561
+ query: ({ uid, ...body }) => ({
2562
+ url: `/content-manager/content-types/${uid}/configuration`,
2563
+ method: "PUT",
2564
+ data: body
2565
+ }),
2566
+ transformResponse: (response) => response.data,
2567
+ invalidatesTags: (_result, _error, { uid }) => [
2568
+ { type: "ContentTypesConfiguration", id: uid },
2569
+ { type: "ContentTypeSettings", id: "LIST" },
2570
+ // Is this necessary?
2571
+ { type: "InitialData" }
2572
+ ]
2573
+ })
2574
+ })
2575
+ });
2576
+ const {
2577
+ useGetContentTypeConfigurationQuery,
2578
+ useGetAllContentTypeSettingsQuery,
2579
+ useUpdateContentTypeConfigurationMutation
2580
+ } = contentTypesApi;
2581
+ const checkIfAttributeIsDisplayable = (attribute) => {
2582
+ const { type } = attribute;
2583
+ if (type === "relation") {
2584
+ return !attribute.relation.toLowerCase().includes("morph");
2585
+ }
2586
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2587
+ };
2588
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2589
+ if (!mainFieldName) {
2590
+ return void 0;
2591
+ }
2592
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2593
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2594
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2595
+ );
2596
+ return {
2597
+ name: mainFieldName,
2598
+ type: mainFieldType ?? "string"
2542
2599
  };
2543
- return /* @__PURE__ */ jsxRuntime.jsx(
2544
- strapiAdmin.DescriptionComponentRenderer,
2600
+ };
2601
+ const DEFAULT_SETTINGS = {
2602
+ bulkable: false,
2603
+ filterable: false,
2604
+ searchable: false,
2605
+ pagination: false,
2606
+ defaultSortBy: "",
2607
+ defaultSortOrder: "asc",
2608
+ mainField: "id",
2609
+ pageSize: 10
2610
+ };
2611
+ const useDocumentLayout = (model) => {
2612
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2613
+ const [{ query }] = strapiAdmin.useQueryParams();
2614
+ const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2615
+ const { toggleNotification } = strapiAdmin.useNotification();
2616
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
2617
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2618
+ const {
2619
+ data,
2620
+ isLoading: isLoadingConfigs,
2621
+ error,
2622
+ isFetching: isFetchingConfigs
2623
+ } = useGetContentTypeConfigurationQuery(model);
2624
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2625
+ React__namespace.useEffect(() => {
2626
+ if (error) {
2627
+ toggleNotification({
2628
+ type: "danger",
2629
+ message: formatAPIError(error)
2630
+ });
2631
+ }
2632
+ }, [error, formatAPIError, toggleNotification]);
2633
+ const editLayout = React__namespace.useMemo(
2634
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2635
+ layout: [],
2636
+ components: {},
2637
+ metadatas: {},
2638
+ options: {},
2639
+ settings: DEFAULT_SETTINGS
2640
+ },
2641
+ [data, isLoading, schemas, schema, components]
2642
+ );
2643
+ const listLayout = React__namespace.useMemo(() => {
2644
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2645
+ layout: [],
2646
+ metadatas: {},
2647
+ options: {},
2648
+ settings: DEFAULT_SETTINGS
2649
+ };
2650
+ }, [data, isLoading, schemas, schema, components]);
2651
+ const { layout: edit } = React__namespace.useMemo(
2652
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2653
+ layout: editLayout,
2654
+ query
2655
+ }),
2656
+ [editLayout, query, runHookWaterfall]
2657
+ );
2658
+ return {
2659
+ error,
2660
+ isLoading,
2661
+ edit,
2662
+ list: listLayout
2663
+ };
2664
+ };
2665
+ const useDocLayout = () => {
2666
+ const { model } = useDoc();
2667
+ return useDocumentLayout(model);
2668
+ };
2669
+ const formatEditLayout = (data, {
2670
+ schemas,
2671
+ schema,
2672
+ components
2673
+ }) => {
2674
+ let currentPanelIndex = 0;
2675
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2676
+ data.contentType.layouts.edit,
2677
+ schema?.attributes,
2678
+ data.contentType.metadatas,
2679
+ { configurations: data.components, schemas: components },
2680
+ schemas
2681
+ ).reduce((panels, row) => {
2682
+ if (row.some((field) => field.type === "dynamiczone")) {
2683
+ panels.push([row]);
2684
+ currentPanelIndex += 2;
2685
+ } else {
2686
+ if (!panels[currentPanelIndex]) {
2687
+ panels.push([]);
2688
+ }
2689
+ panels[currentPanelIndex].push(row);
2690
+ }
2691
+ return panels;
2692
+ }, []);
2693
+ const componentEditAttributes = Object.entries(data.components).reduce(
2694
+ (acc, [uid, configuration]) => {
2695
+ acc[uid] = {
2696
+ layout: convertEditLayoutToFieldLayouts(
2697
+ configuration.layouts.edit,
2698
+ components[uid].attributes,
2699
+ configuration.metadatas
2700
+ ),
2701
+ settings: {
2702
+ ...configuration.settings,
2703
+ icon: components[uid].info.icon,
2704
+ displayName: components[uid].info.displayName
2705
+ }
2706
+ };
2707
+ return acc;
2708
+ },
2709
+ {}
2710
+ );
2711
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2712
+ (acc, [attribute, metadata]) => {
2713
+ return {
2714
+ ...acc,
2715
+ [attribute]: metadata.edit
2716
+ };
2717
+ },
2718
+ {}
2719
+ );
2720
+ return {
2721
+ layout: panelledEditAttributes,
2722
+ components: componentEditAttributes,
2723
+ metadatas: editMetadatas,
2724
+ settings: {
2725
+ ...data.contentType.settings,
2726
+ displayName: schema?.info.displayName
2727
+ },
2728
+ options: {
2729
+ ...schema?.options,
2730
+ ...schema?.pluginOptions,
2731
+ ...data.contentType.options
2732
+ }
2733
+ };
2734
+ };
2735
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2736
+ return rows.map(
2737
+ (row) => row.map((field) => {
2738
+ const attribute = attributes[field.name];
2739
+ if (!attribute) {
2740
+ return null;
2741
+ }
2742
+ const { edit: metadata } = metadatas[field.name];
2743
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2744
+ return {
2745
+ attribute,
2746
+ disabled: !metadata.editable,
2747
+ hint: metadata.description,
2748
+ label: metadata.label ?? "",
2749
+ name: field.name,
2750
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
2751
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2752
+ schemas,
2753
+ components: components?.schemas ?? {}
2754
+ }),
2755
+ placeholder: metadata.placeholder ?? "",
2756
+ required: attribute.required ?? false,
2757
+ size: field.size,
2758
+ unique: "unique" in attribute ? attribute.unique : false,
2759
+ visible: metadata.visible ?? true,
2760
+ type: attribute.type
2761
+ };
2762
+ }).filter((field) => field !== null)
2763
+ );
2764
+ };
2765
+ const formatListLayout = (data, {
2766
+ schemas,
2767
+ schema,
2768
+ components
2769
+ }) => {
2770
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2771
+ (acc, [attribute, metadata]) => {
2772
+ return {
2773
+ ...acc,
2774
+ [attribute]: metadata.list
2775
+ };
2776
+ },
2777
+ {}
2778
+ );
2779
+ const listAttributes = convertListLayoutToFieldLayouts(
2780
+ data.contentType.layouts.list,
2781
+ schema?.attributes,
2782
+ listMetadatas,
2783
+ { configurations: data.components, schemas: components },
2784
+ schemas
2785
+ );
2786
+ return {
2787
+ layout: listAttributes,
2788
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2789
+ metadatas: listMetadatas,
2790
+ options: {
2791
+ ...schema?.options,
2792
+ ...schema?.pluginOptions,
2793
+ ...data.contentType.options
2794
+ }
2795
+ };
2796
+ };
2797
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2798
+ return columns.map((name) => {
2799
+ const attribute = attributes[name];
2800
+ if (!attribute) {
2801
+ return null;
2802
+ }
2803
+ const metadata = metadatas[name];
2804
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2805
+ return {
2806
+ attribute,
2807
+ label: metadata.label ?? "",
2808
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2809
+ schemas,
2810
+ components: components?.schemas ?? {}
2811
+ }),
2812
+ name,
2813
+ searchable: metadata.searchable ?? true,
2814
+ sortable: metadata.sortable ?? true
2815
+ };
2816
+ }).filter((field) => field !== null);
2817
+ };
2818
+ const ConfirmBulkActionDialog = ({
2819
+ onToggleDialog,
2820
+ isOpen = false,
2821
+ dialogBody,
2822
+ endAction
2823
+ }) => {
2824
+ const { formatMessage } = reactIntl.useIntl();
2825
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2826
+ designSystem.Dialog,
2545
2827
  {
2546
- props,
2547
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2548
- children: (actions2) => {
2549
- const tableRowActions = actions2.filter((action) => {
2550
- const positions = Array.isArray(action.position) ? action.position : [action.position];
2551
- return positions.includes("table-row");
2552
- });
2553
- return /* @__PURE__ */ jsxRuntime.jsx(
2554
- DocumentActionsMenu,
2828
+ onClose: onToggleDialog,
2829
+ title: formatMessage({
2830
+ id: "app.components.ConfirmDialog.title",
2831
+ defaultMessage: "Confirmation"
2832
+ }),
2833
+ isOpen,
2834
+ children: [
2835
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.DialogBody, { icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, {}), children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: dialogBody }) }),
2836
+ /* @__PURE__ */ jsxRuntime.jsx(
2837
+ designSystem.DialogFooter,
2555
2838
  {
2556
- actions: tableRowActions,
2557
- label: formatMessage({
2558
- id: "content-manager.containers.list.table.row-actions",
2559
- defaultMessage: "Row action"
2560
- }),
2561
- variant: "ghost"
2839
+ startAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onToggleDialog, variant: "tertiary", children: formatMessage({
2840
+ id: "app.components.Button.cancel",
2841
+ defaultMessage: "Cancel"
2842
+ }) }),
2843
+ endAction
2562
2844
  }
2563
- );
2564
- }
2845
+ )
2846
+ ]
2565
2847
  }
2566
2848
  );
2567
2849
  };
2568
- const EditAction = ({ documentId }) => {
2569
- const navigate = reactRouterDom.useNavigate();
2850
+ const BoldChunk$1 = (chunks) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: chunks });
2851
+ const ConfirmDialogPublishAll = ({
2852
+ isOpen,
2853
+ onToggleDialog,
2854
+ isConfirmButtonLoading = false,
2855
+ onConfirm
2856
+ }) => {
2570
2857
  const { formatMessage } = reactIntl.useIntl();
2571
- const { canRead } = useDocumentRBAC("EditAction", ({ canRead: canRead2 }) => ({ canRead: canRead2 }));
2858
+ const selectedEntries = strapiAdmin.useTable("ConfirmDialogPublishAll", (state) => state.selectedRows);
2572
2859
  const { toggleNotification } = strapiAdmin.useNotification();
2860
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler(getTranslation);
2861
+ const { model, schema } = useDoc();
2573
2862
  const [{ query }] = strapiAdmin.useQueryParams();
2574
- return {
2575
- disabled: !canRead,
2576
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledPencil, {}),
2577
- label: formatMessage({
2578
- id: "content-manager.actions.edit.label",
2579
- defaultMessage: "Edit"
2580
- }),
2581
- position: "table-row",
2582
- onClick: async () => {
2583
- if (!documentId) {
2584
- console.error(
2585
- "You're trying to edit a document without an id, this is likely a bug with Strapi. Please open an issue."
2863
+ const {
2864
+ data: countDraftRelations = 0,
2865
+ isLoading,
2866
+ error
2867
+ } = useGetManyDraftRelationCountQuery(
2868
+ {
2869
+ model,
2870
+ documentIds: selectedEntries.map((entry) => entry.documentId),
2871
+ locale: query?.plugins?.i18n?.locale
2872
+ },
2873
+ {
2874
+ skip: selectedEntries.length === 0
2875
+ }
2876
+ );
2877
+ React__namespace.useEffect(() => {
2878
+ if (error) {
2879
+ toggleNotification({ type: "danger", message: formatAPIError(error) });
2880
+ }
2881
+ }, [error, formatAPIError, toggleNotification]);
2882
+ if (error) {
2883
+ return null;
2884
+ }
2885
+ return /* @__PURE__ */ jsxRuntime.jsx(
2886
+ ConfirmBulkActionDialog,
2887
+ {
2888
+ isOpen: isOpen && !isLoading,
2889
+ onToggleDialog,
2890
+ dialogBody: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2891
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { id: "confirm-description", textAlign: "center", children: [
2892
+ countDraftRelations > 0 && formatMessage(
2893
+ {
2894
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2895
+ defaultMessage: "<b>{count} {count, plural, one { relation } other { relations } } out of {entities} { entities, plural, one { entry } other { entries } } {count, plural, one { is } other { are } }</b> not published yet and might lead to unexpected behavior. "
2896
+ },
2897
+ {
2898
+ b: BoldChunk$1,
2899
+ count: countDraftRelations,
2900
+ entities: selectedEntries.length
2901
+ }
2902
+ ),
2903
+ formatMessage({
2904
+ id: getTranslation("popUpWarning.bodyMessage.contentType.publish.all"),
2905
+ defaultMessage: "Are you sure you want to publish these entries?"
2906
+ })
2907
+ ] }),
2908
+ schema?.pluginOptions && "i18n" in schema.pluginOptions && schema?.pluginOptions.i18n && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger500", textAlign: "center", children: formatMessage(
2909
+ {
2910
+ id: getTranslation("Settings.list.actions.publishAdditionalInfos"),
2911
+ defaultMessage: "This will publish the active locale versions <em>(from Internationalization)</em>"
2912
+ },
2913
+ {
2914
+ em: Emphasis
2915
+ }
2916
+ ) })
2917
+ ] }),
2918
+ endAction: /* @__PURE__ */ jsxRuntime.jsx(
2919
+ designSystem.Button,
2920
+ {
2921
+ onClick: onConfirm,
2922
+ variant: "secondary",
2923
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(Icons.Check, {}),
2924
+ loading: isConfirmButtonLoading,
2925
+ children: formatMessage({
2926
+ id: "app.utils.publish",
2927
+ defaultMessage: "Publish"
2928
+ })
2929
+ }
2930
+ )
2931
+ }
2932
+ );
2933
+ };
2934
+ const TypographyMaxWidth = styledComponents.styled(designSystem.Typography)`
2935
+ max-width: 300px;
2936
+ `;
2937
+ const formatErrorMessages = (errors, parentKey, formatMessage) => {
2938
+ const messages = [];
2939
+ Object.entries(errors).forEach(([key, value]) => {
2940
+ const currentKey = parentKey ? `${parentKey}.${key}` : key;
2941
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
2942
+ if ("id" in value && "defaultMessage" in value) {
2943
+ messages.push(
2944
+ formatMessage(
2945
+ {
2946
+ id: `${value.id}.withField`,
2947
+ defaultMessage: value.defaultMessage
2948
+ },
2949
+ { field: currentKey }
2950
+ )
2586
2951
  );
2587
- toggleNotification({
2588
- message: formatMessage({
2589
- id: "content-manager.actions.edit.error",
2590
- defaultMessage: "An error occurred while trying to edit the document."
2591
- }),
2592
- type: "danger"
2593
- });
2594
- return;
2952
+ } else {
2953
+ messages.push(...formatErrorMessages(value, currentKey, formatMessage));
2954
+ }
2955
+ } else {
2956
+ messages.push(
2957
+ formatMessage(
2958
+ {
2959
+ id: `${value}.withField`,
2960
+ defaultMessage: value
2961
+ },
2962
+ { field: currentKey }
2963
+ )
2964
+ );
2965
+ }
2966
+ });
2967
+ return messages;
2968
+ };
2969
+ const EntryValidationText = ({ validationErrors, status }) => {
2970
+ const { formatMessage } = reactIntl.useIntl();
2971
+ if (validationErrors) {
2972
+ const validationErrorsMessages = formatErrorMessages(validationErrors, "", formatMessage).join(
2973
+ " "
2974
+ );
2975
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
2976
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.CrossCircle, { fill: "danger600" }),
2977
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsxRuntime.jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
2978
+ ] });
2979
+ }
2980
+ if (status === "published") {
2981
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
2982
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.CheckCircle, { fill: "success600" }),
2983
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
2984
+ id: "content-manager.bulk-publish.already-published",
2985
+ defaultMessage: "Already Published"
2986
+ }) })
2987
+ ] });
2988
+ }
2989
+ if (status === "modified") {
2990
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
2991
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.ArrowsCounterClockwise, { fill: "alternative600" }),
2992
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
2993
+ id: "content-manager.bulk-publish.modified",
2994
+ defaultMessage: "Ready to publish changes"
2995
+ }) })
2996
+ ] });
2997
+ }
2998
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
2999
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.CheckCircle, { fill: "success600" }),
3000
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
3001
+ id: "app.utils.ready-to-publish",
3002
+ defaultMessage: "Ready to publish"
3003
+ }) })
3004
+ ] });
3005
+ };
3006
+ const TABLE_HEADERS = [
3007
+ { name: "id", label: "id" },
3008
+ { name: "name", label: "name" },
3009
+ { name: "status", label: "status" },
3010
+ { name: "publicationStatus", label: "Publication status" }
3011
+ ];
3012
+ const SelectedEntriesTableContent = ({
3013
+ isPublishing,
3014
+ rowsToDisplay = [],
3015
+ entriesToPublish = [],
3016
+ validationErrors = {}
3017
+ }) => {
3018
+ const { pathname } = reactRouterDom.useLocation();
3019
+ const { formatMessage } = reactIntl.useIntl();
3020
+ const {
3021
+ list: {
3022
+ settings: { mainField }
3023
+ }
3024
+ } = useDocLayout();
3025
+ const shouldDisplayMainField = mainField != null && mainField !== "id";
3026
+ return /* @__PURE__ */ jsxRuntime.jsxs(strapiAdmin.Table.Content, { children: [
3027
+ /* @__PURE__ */ jsxRuntime.jsxs(strapiAdmin.Table.Head, { children: [
3028
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.HeaderCheckboxCell, {}),
3029
+ TABLE_HEADERS.filter((head) => head.name !== "name" || shouldDisplayMainField).map(
3030
+ (head) => /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.HeaderCell, { ...head }, head.name)
3031
+ )
3032
+ ] }),
3033
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Loading, {}),
3034
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Body, { children: rowsToDisplay.map((row, index2) => /* @__PURE__ */ jsxRuntime.jsxs(strapiAdmin.Table.Row, { children: [
3035
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.CheckboxCell, { id: row.id }),
3036
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: row.id }) }),
3037
+ shouldDisplayMainField && /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: row[mainField] }) }),
3038
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(DocumentStatus, { status: row.status, maxWidth: "min-content" }) }),
3039
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: isPublishing && entriesToPublish.includes(row.documentId) ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
3040
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
3041
+ id: "content-manager.success.record.publishing",
3042
+ defaultMessage: "Publishing..."
3043
+ }) }),
3044
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true })
3045
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
3046
+ EntryValidationText,
3047
+ {
3048
+ validationErrors: validationErrors[row.documentId],
3049
+ status: row.status
3050
+ }
3051
+ ) }),
3052
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
3053
+ designSystem.IconButton,
3054
+ {
3055
+ tag: reactRouterDom.Link,
3056
+ to: {
3057
+ pathname: `${pathname}/${row.documentId}`,
3058
+ search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3059
+ },
3060
+ state: { from: pathname },
3061
+ label: formatMessage(
3062
+ { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3063
+ {
3064
+ target: formatMessage(
3065
+ {
3066
+ id: "content-manager.components.ListViewHelperPluginTable.row-line",
3067
+ defaultMessage: "item line {number}"
3068
+ },
3069
+ { number: index2 + 1 }
3070
+ )
3071
+ }
3072
+ ),
3073
+ target: "_blank",
3074
+ marginLeft: "auto",
3075
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icons.Pencil, {})
3076
+ }
3077
+ ) })
3078
+ ] }, row.id)) })
3079
+ ] });
3080
+ };
3081
+ const BoldChunk = (chunks) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: chunks });
3082
+ const SelectedEntriesModalContent = ({
3083
+ listViewSelectedEntries,
3084
+ toggleModal,
3085
+ setListViewSelectedDocuments,
3086
+ model
3087
+ }) => {
3088
+ const { formatMessage } = reactIntl.useIntl();
3089
+ const { schema, components } = useContentTypeSchema(model);
3090
+ const documentIds = listViewSelectedEntries.map(({ documentId }) => documentId);
3091
+ const [{ query }] = strapiAdmin.useQueryParams();
3092
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
3093
+ const { data, isLoading, isFetching, refetch } = useGetAllDocumentsQuery(
3094
+ {
3095
+ model,
3096
+ params: {
3097
+ page: "1",
3098
+ pageSize: documentIds.length.toString(),
3099
+ sort: query.sort,
3100
+ filters: {
3101
+ documentId: {
3102
+ $in: documentIds
3103
+ }
3104
+ },
3105
+ locale: query.plugins?.i18n?.locale
2595
3106
  }
2596
- navigate({
2597
- pathname: documentId,
2598
- search: qs.stringify({
2599
- plugins: query.plugins
2600
- })
3107
+ },
3108
+ {
3109
+ selectFromResult: ({ data: data2, ...restRes }) => ({ data: data2?.results ?? [], ...restRes })
3110
+ }
3111
+ );
3112
+ const { rows, validationErrors } = React__namespace.useMemo(() => {
3113
+ if (data.length > 0 && schema) {
3114
+ const validate = createYupSchema(schema.attributes, components);
3115
+ const validationErrors2 = {};
3116
+ const rows2 = data.map((entry) => {
3117
+ try {
3118
+ validate.validateSync(entry, { abortEarly: false });
3119
+ return entry;
3120
+ } catch (e) {
3121
+ if (e instanceof yup.ValidationError) {
3122
+ validationErrors2[entry.documentId] = strapiAdmin.getYupValidationErrors(e);
3123
+ }
3124
+ return entry;
3125
+ }
3126
+ });
3127
+ return { rows: rows2, validationErrors: validationErrors2 };
3128
+ }
3129
+ return {
3130
+ rows: [],
3131
+ validationErrors: {}
3132
+ };
3133
+ }, [components, data, schema]);
3134
+ const [publishedCount, setPublishedCount] = React__namespace.useState(0);
3135
+ const [isDialogOpen, setIsDialogOpen] = React__namespace.useState(false);
3136
+ const { publishMany: bulkPublishAction } = useDocumentActions();
3137
+ const [, { isLoading: isSubmittingForm }] = usePublishManyDocumentsMutation();
3138
+ const selectedRows = strapiAdmin.useTable("publishAction", (state) => state.selectedRows);
3139
+ const selectedEntries = rows.filter(
3140
+ (entry) => selectedRows.some((selectedEntry) => selectedEntry.documentId === entry.documentId)
3141
+ );
3142
+ const entriesToPublish = selectedEntries.filter((entry) => !validationErrors[entry.documentId]).map((entry) => entry.documentId);
3143
+ const selectedEntriesWithErrorsCount = selectedEntries.filter(
3144
+ ({ documentId }) => validationErrors[documentId]
3145
+ ).length;
3146
+ const selectedEntriesPublished = selectedEntries.filter(
3147
+ ({ status }) => status === "published"
3148
+ ).length;
3149
+ const selectedEntriesWithNoErrorsCount = selectedEntries.length - selectedEntriesWithErrorsCount - selectedEntriesPublished;
3150
+ const toggleDialog = () => setIsDialogOpen((prev) => !prev);
3151
+ const handleConfirmBulkPublish = async () => {
3152
+ toggleDialog();
3153
+ const res = await bulkPublishAction({ model, documentIds: entriesToPublish, params });
3154
+ if (!("error" in res)) {
3155
+ setPublishedCount(res.count);
3156
+ const unpublishedEntries = rows.filter((row) => {
3157
+ return !entriesToPublish.includes(row.documentId);
2601
3158
  });
3159
+ setListViewSelectedDocuments(unpublishedEntries);
2602
3160
  }
2603
3161
  };
2604
- };
2605
- EditAction.type = "edit";
2606
- const StyledPencil = styledComponents.styled(Icons.Pencil)`
2607
- path {
2608
- fill: currentColor;
2609
- }
2610
- `;
2611
- const CloneAction = ({ model, documentId }) => {
2612
- const navigate = reactRouterDom.useNavigate();
2613
- const { formatMessage } = reactIntl.useIntl();
2614
- const { canCreate } = useDocumentRBAC("CloneAction", ({ canCreate: canCreate2 }) => ({ canCreate: canCreate2 }));
2615
- const { toggleNotification } = strapiAdmin.useNotification();
2616
- const { autoClone } = useDocumentActions();
2617
- const [prohibitedFields, setProhibitedFields] = React__namespace.useState([]);
2618
- return {
2619
- disabled: !canCreate,
2620
- icon: /* @__PURE__ */ jsxRuntime.jsx(StyledDuplicate, {}),
2621
- label: formatMessage({
2622
- id: "content-manager.actions.clone.label",
2623
- defaultMessage: "Duplicate"
2624
- }),
2625
- position: "table-row",
2626
- onClick: async () => {
2627
- if (!documentId) {
2628
- console.error(
2629
- "You're trying to clone a document in the table without an id, this is likely a bug with Strapi. Please open an issue."
2630
- );
2631
- toggleNotification({
2632
- message: formatMessage({
2633
- id: "content-manager.actions.clone.error",
2634
- defaultMessage: "An error occurred while trying to clone the document."
2635
- }),
2636
- type: "danger"
2637
- });
2638
- return;
3162
+ const getFormattedCountMessage = () => {
3163
+ if (publishedCount) {
3164
+ return formatMessage(
3165
+ {
3166
+ id: getTranslation("containers.list.selectedEntriesModal.publishedCount"),
3167
+ defaultMessage: "<b>{publishedCount}</b> {publishedCount, plural, =0 {entries} one {entry} other {entries}} published. <b>{withErrorsCount}</b> {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action."
3168
+ },
3169
+ {
3170
+ publishedCount,
3171
+ withErrorsCount: selectedEntriesWithErrorsCount,
3172
+ b: BoldChunk
3173
+ }
3174
+ );
3175
+ }
3176
+ return formatMessage(
3177
+ {
3178
+ id: getTranslation("containers.list.selectedEntriesModal.selectedCount"),
3179
+ defaultMessage: "<b>{alreadyPublishedCount}</b> {alreadyPublishedCount, plural, =0 {entries} one {entry} other {entries}} already published. <b>{readyToPublishCount}</b> {readyToPublishCount, plural, =0 {entries} one {entry} other {entries}} ready to publish. <b>{withErrorsCount}</b> {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action."
3180
+ },
3181
+ {
3182
+ readyToPublishCount: selectedEntriesWithNoErrorsCount,
3183
+ withErrorsCount: selectedEntriesWithErrorsCount,
3184
+ alreadyPublishedCount: selectedEntriesPublished,
3185
+ b: BoldChunk
2639
3186
  }
2640
- const res = await autoClone({ model, sourceId: documentId });
2641
- if ("data" in res) {
2642
- navigate(res.data.documentId);
2643
- return true;
3187
+ );
3188
+ };
3189
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3190
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ModalBody, { children: [
3191
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: getFormattedCountMessage() }),
3192
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 5, children: /* @__PURE__ */ jsxRuntime.jsx(
3193
+ SelectedEntriesTableContent,
3194
+ {
3195
+ isPublishing: isSubmittingForm,
3196
+ rowsToDisplay: rows,
3197
+ entriesToPublish,
3198
+ validationErrors
3199
+ }
3200
+ ) })
3201
+ ] }),
3202
+ /* @__PURE__ */ jsxRuntime.jsx(
3203
+ designSystem.ModalFooter,
3204
+ {
3205
+ startActions: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: toggleModal, variant: "tertiary", children: formatMessage({
3206
+ id: "app.components.Button.cancel",
3207
+ defaultMessage: "Cancel"
3208
+ }) }),
3209
+ endActions: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
3210
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: refetch, variant: "tertiary", loading: isFetching, children: formatMessage({ id: "app.utils.refresh", defaultMessage: "Refresh" }) }),
3211
+ /* @__PURE__ */ jsxRuntime.jsx(
3212
+ designSystem.Button,
3213
+ {
3214
+ onClick: toggleDialog,
3215
+ disabled: selectedEntries.length === 0 || selectedEntries.length === selectedEntriesWithErrorsCount || selectedEntriesPublished === selectedEntries.length || isLoading,
3216
+ loading: isSubmittingForm,
3217
+ children: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" })
3218
+ }
3219
+ )
3220
+ ] })
2644
3221
  }
2645
- if (isBaseQueryError(res.error) && res.error.details && typeof res.error.details === "object" && "prohibitedFields" in res.error.details && Array.isArray(res.error.details.prohibitedFields)) {
2646
- const prohibitedFields2 = res.error.details.prohibitedFields;
2647
- setProhibitedFields(prohibitedFields2);
3222
+ ),
3223
+ /* @__PURE__ */ jsxRuntime.jsx(
3224
+ ConfirmDialogPublishAll,
3225
+ {
3226
+ isOpen: isDialogOpen,
3227
+ onToggleDialog: toggleDialog,
3228
+ isConfirmButtonLoading: isSubmittingForm,
3229
+ onConfirm: handleConfirmBulkPublish
2648
3230
  }
2649
- },
3231
+ )
3232
+ ] });
3233
+ };
3234
+ const PublishAction = ({ documents, model }) => {
3235
+ const { formatMessage } = reactIntl.useIntl();
3236
+ const hasPublishPermission = useDocumentRBAC("unpublishAction", (state) => state.canPublish);
3237
+ const showPublishButton = hasPublishPermission && documents.some(({ status }) => status !== "published");
3238
+ const setListViewSelectedDocuments = strapiAdmin.useTable("publishAction", (state) => state.selectRow);
3239
+ const refetchList = () => {
3240
+ contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3241
+ };
3242
+ if (!showPublishButton)
3243
+ return null;
3244
+ return {
3245
+ actionType: "publish",
3246
+ variant: "tertiary",
3247
+ label: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" }),
2650
3248
  dialog: {
2651
3249
  type: "modal",
2652
3250
  title: formatMessage({
2653
- id: "content-manager.containers.list.autoCloneModal.header",
2654
- defaultMessage: "Duplicate"
3251
+ id: getTranslation("containers.ListPage.selectedEntriesModal.title"),
3252
+ defaultMessage: "Publish entries"
2655
3253
  }),
2656
- content: /* @__PURE__ */ jsxRuntime.jsx(AutoCloneFailureModalBody, { prohibitedFields }),
2657
- footer: ({ onClose }) => {
2658
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
2659
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
2660
- id: "cancel",
2661
- defaultMessage: "Cancel"
2662
- }) }),
2663
- /* @__PURE__ */ jsxRuntime.jsx(
2664
- designSystem.LinkButton,
2665
- {
2666
- tag: reactRouterDom.NavLink,
2667
- to: {
2668
- pathname: `clone/${documentId}`
2669
- },
2670
- children: formatMessage({
2671
- id: "content-manager.containers.list.autoCloneModal.create",
2672
- defaultMessage: "Create"
2673
- })
2674
- }
2675
- )
2676
- ] });
3254
+ content: ({ onClose }) => {
3255
+ return /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin.Table.Root, { rows: documents, defaultSelectedRows: documents, headers: TABLE_HEADERS, children: /* @__PURE__ */ jsxRuntime.jsx(
3256
+ SelectedEntriesModalContent,
3257
+ {
3258
+ listViewSelectedEntries: documents,
3259
+ toggleModal: () => {
3260
+ onClose();
3261
+ refetchList();
3262
+ },
3263
+ setListViewSelectedDocuments,
3264
+ model
3265
+ }
3266
+ ) });
3267
+ },
3268
+ onClose: () => {
3269
+ refetchList();
2677
3270
  }
2678
3271
  }
2679
3272
  };
2680
3273
  };
2681
- CloneAction.type = "clone";
2682
- const StyledDuplicate = styledComponents.styled(Icons.Duplicate)`
2683
- path {
2684
- fill: currentColor;
2685
- }
2686
- `;
2687
- const DEFAULT_TABLE_ROW_ACTIONS = [EditAction, CloneAction];
2688
- class ContentManagerPlugin {
2689
- /**
2690
- * The following properties are the stored ones provided by any plugins registering with
2691
- * the content-manager. The function calls however, need to be called at runtime in the
2692
- * application, so instead we collate them and run them later with the complete list incl.
2693
- * ones already registered & the context of the view.
2694
- */
2695
- bulkActions = [...DEFAULT_BULK_ACTIONS];
2696
- documentActions = [
2697
- ...DEFAULT_ACTIONS,
2698
- ...DEFAULT_TABLE_ROW_ACTIONS,
2699
- ...DEFAULT_HEADER_ACTIONS,
2700
- HistoryAction
2701
- ];
2702
- editViewSidePanels = [ActionsPanel];
2703
- headerActions = [];
2704
- constructor() {
2705
- }
2706
- addEditViewSidePanel(panels) {
2707
- if (Array.isArray(panels)) {
2708
- this.editViewSidePanels = [...this.editViewSidePanels, ...panels];
2709
- } else if (typeof panels === "function") {
2710
- this.editViewSidePanels = panels(this.editViewSidePanels);
2711
- } else {
2712
- throw new Error(
2713
- `Expected the \`panels\` passed to \`addEditViewSidePanel\` to be an array or a function, but received ${getPrintableType(
2714
- panels
2715
- )}`
2716
- );
3274
+ const BulkActionsRenderer = () => {
3275
+ const plugins = strapiAdmin.useStrapiApp("BulkActionsRenderer", (state) => state.plugins);
3276
+ const { model, collectionType } = useDoc();
3277
+ const { selectedRows } = strapiAdmin.useTable("BulkActionsRenderer", (state) => state);
3278
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(
3279
+ strapiAdmin.DescriptionComponentRenderer,
3280
+ {
3281
+ props: {
3282
+ model,
3283
+ collectionType,
3284
+ documents: selectedRows
3285
+ },
3286
+ descriptions: plugins["content-manager"].apis.getBulkActions(),
3287
+ children: (actions2) => actions2.map((action) => /* @__PURE__ */ jsxRuntime.jsx(BulkActionAction, { ...action }, action.id))
2717
3288
  }
2718
- }
2719
- addDocumentAction(actions2) {
2720
- if (Array.isArray(actions2)) {
2721
- this.documentActions = [...this.documentActions, ...actions2];
2722
- } else if (typeof actions2 === "function") {
2723
- this.documentActions = actions2(this.documentActions);
2724
- } else {
2725
- throw new Error(
2726
- `Expected the \`actions\` passed to \`addDocumentAction\` to be an array or a function, but received ${getPrintableType(
2727
- actions2
2728
- )}`
2729
- );
3289
+ ) });
3290
+ };
3291
+ const BulkActionAction = (action) => {
3292
+ const [dialogId, setDialogId] = React__namespace.useState(null);
3293
+ const { toggleNotification } = strapiAdmin.useNotification();
3294
+ const handleClick = (action2) => (e) => {
3295
+ const { onClick, dialog, id } = action2;
3296
+ if (onClick) {
3297
+ onClick(e);
2730
3298
  }
2731
- }
2732
- addDocumentHeaderAction(actions2) {
2733
- if (Array.isArray(actions2)) {
2734
- this.headerActions = [...this.headerActions, ...actions2];
2735
- } else if (typeof actions2 === "function") {
2736
- this.headerActions = actions2(this.headerActions);
2737
- } else {
2738
- throw new Error(
2739
- `Expected the \`actions\` passed to \`addDocumentHeaderAction\` to be an array or a function, but received ${getPrintableType(
2740
- actions2
2741
- )}`
2742
- );
3299
+ if (dialog) {
3300
+ switch (dialog.type) {
3301
+ case "notification":
3302
+ toggleNotification({
3303
+ title: dialog.title,
3304
+ message: dialog.content,
3305
+ type: dialog.status,
3306
+ timeout: dialog.timeout,
3307
+ onClose: dialog.onClose
3308
+ });
3309
+ break;
3310
+ case "dialog":
3311
+ case "modal": {
3312
+ e.preventDefault();
3313
+ setDialogId(id);
3314
+ }
3315
+ }
3316
+ }
3317
+ };
3318
+ const handleClose = () => {
3319
+ setDialogId(null);
3320
+ if (action.dialog?.type === "modal" && action.dialog?.onClose) {
3321
+ action.dialog.onClose();
3322
+ }
3323
+ };
3324
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3325
+ /* @__PURE__ */ jsxRuntime.jsx(
3326
+ designSystem.Button,
3327
+ {
3328
+ disabled: action.disabled,
3329
+ startIcon: action.icon,
3330
+ variant: action.variant,
3331
+ onClick: handleClick(action),
3332
+ children: action.label
3333
+ }
3334
+ ),
3335
+ action.dialog?.type === "dialog" ? /* @__PURE__ */ jsxRuntime.jsx(
3336
+ BulkActionConfirmDialog,
3337
+ {
3338
+ ...action.dialog,
3339
+ variant: action.variant,
3340
+ isOpen: dialogId === action.id,
3341
+ onClose: handleClose
3342
+ }
3343
+ ) : null,
3344
+ action.dialog?.type === "modal" ? /* @__PURE__ */ jsxRuntime.jsx(
3345
+ BulkActionModal,
3346
+ {
3347
+ ...action.dialog,
3348
+ onModalClose: handleClose,
3349
+ isOpen: dialogId === action.id
3350
+ }
3351
+ ) : null
3352
+ ] });
3353
+ };
3354
+ const BulkActionConfirmDialog = ({
3355
+ onClose,
3356
+ onCancel,
3357
+ onConfirm,
3358
+ title,
3359
+ content,
3360
+ confirmButton,
3361
+ isOpen,
3362
+ variant = "secondary"
3363
+ }) => {
3364
+ const { formatMessage } = reactIntl.useIntl();
3365
+ const handleClose = async () => {
3366
+ if (onCancel) {
3367
+ await onCancel();
2743
3368
  }
2744
- }
2745
- addBulkAction(actions2) {
2746
- if (Array.isArray(actions2)) {
2747
- this.bulkActions = [...this.bulkActions, ...actions2];
2748
- } else if (typeof actions2 === "function") {
2749
- this.bulkActions = actions2(this.bulkActions);
2750
- } else {
2751
- throw new Error(
2752
- `Expected the \`actions\` passed to \`addBulkAction\` to be an array or a function, but received ${getPrintableType(
2753
- actions2
2754
- )}`
2755
- );
3369
+ onClose();
3370
+ };
3371
+ const handleConfirm = async () => {
3372
+ if (onConfirm) {
3373
+ await onConfirm();
2756
3374
  }
2757
- }
2758
- get config() {
2759
- return {
2760
- id: PLUGIN_ID,
2761
- name: "Content Manager",
2762
- injectionZones: INJECTION_ZONES,
2763
- apis: {
2764
- addBulkAction: this.addBulkAction.bind(this),
2765
- addDocumentAction: this.addDocumentAction.bind(this),
2766
- addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
2767
- addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
2768
- getBulkActions: () => this.bulkActions,
2769
- getDocumentActions: () => this.documentActions,
2770
- getEditViewSidePanels: () => this.editViewSidePanels,
2771
- getHeaderActions: () => this.headerActions
3375
+ onClose();
3376
+ };
3377
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Dialog, { isOpen, title, onClose: handleClose, children: [
3378
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.DialogBody, { icon: /* @__PURE__ */ jsxRuntime.jsx(Icons.WarningCircle, {}), children: content }),
3379
+ /* @__PURE__ */ jsxRuntime.jsx(
3380
+ designSystem.DialogFooter,
3381
+ {
3382
+ startAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleClose, variant: "tertiary", children: formatMessage({
3383
+ id: "app.components.Button.cancel",
3384
+ defaultMessage: "Cancel"
3385
+ }) }),
3386
+ endAction: /* @__PURE__ */ jsxRuntime.jsx(
3387
+ designSystem.Button,
3388
+ {
3389
+ onClick: handleConfirm,
3390
+ variant: variant === "danger-light" ? variant : "secondary",
3391
+ startIcon: variant === "danger-light" ? /* @__PURE__ */ jsxRuntime.jsx(Icons.Trash, {}) : /* @__PURE__ */ jsxRuntime.jsx(Icons.Check, {}),
3392
+ children: confirmButton ? confirmButton : formatMessage({
3393
+ id: "app.components.Button.confirm",
3394
+ defaultMessage: "Confirm"
3395
+ })
3396
+ }
3397
+ )
2772
3398
  }
2773
- };
3399
+ )
3400
+ ] });
3401
+ };
3402
+ const BulkActionModal = ({
3403
+ isOpen,
3404
+ title,
3405
+ onClose,
3406
+ content: Content,
3407
+ onModalClose
3408
+ }) => {
3409
+ const id = React__namespace.useId();
3410
+ if (!isOpen) {
3411
+ return null;
2774
3412
  }
2775
- }
2776
- const getPrintableType = (value) => {
2777
- const nativeType = typeof value;
2778
- if (nativeType === "object") {
2779
- if (value === null)
2780
- return "null";
2781
- if (Array.isArray(value))
2782
- return "array";
2783
- if (value instanceof Object && value.constructor.name !== "Object") {
2784
- return value.constructor.name;
3413
+ const handleClose = () => {
3414
+ if (onClose) {
3415
+ onClose();
2785
3416
  }
2786
- }
2787
- return nativeType;
2788
- };
2789
- const initialState = {
2790
- collectionTypeLinks: [],
2791
- components: [],
2792
- fieldSizes: {},
2793
- models: [],
2794
- singleTypeLinks: [],
2795
- isLoading: true
3417
+ onModalClose();
3418
+ };
3419
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ModalLayout, { borderRadius: "4px", overflow: "hidden", onClose: handleClose, labelledBy: id, children: [
3420
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: "neutral800", tag: "h2", id, children: title }) }),
3421
+ /* @__PURE__ */ jsxRuntime.jsx(Content, { onClose: handleClose })
3422
+ ] });
2796
3423
  };
2797
- const appSlice = toolkit.createSlice({
2798
- name: "app",
2799
- initialState,
2800
- reducers: {
2801
- setInitialData(state, action) {
2802
- const {
2803
- authorizedCollectionTypeLinks,
2804
- authorizedSingleTypeLinks,
2805
- components,
2806
- contentTypeSchemas,
2807
- fieldSizes
2808
- } = action.payload;
2809
- state.collectionTypeLinks = authorizedCollectionTypeLinks.filter(
2810
- ({ isDisplayed }) => isDisplayed
2811
- );
2812
- state.singleTypeLinks = authorizedSingleTypeLinks.filter(({ isDisplayed }) => isDisplayed);
2813
- state.components = components;
2814
- state.models = contentTypeSchemas;
2815
- state.fieldSizes = fieldSizes;
2816
- state.isLoading = false;
3424
+ const DeleteAction = ({ documents, model }) => {
3425
+ const { formatMessage } = reactIntl.useIntl();
3426
+ const { schema: contentType } = useDoc();
3427
+ const selectRow = strapiAdmin.useTable("DeleteAction", (state) => state.selectRow);
3428
+ const hasI18nEnabled = Boolean(contentType?.pluginOptions?.i18n);
3429
+ const [{ query }] = strapiAdmin.useQueryParams();
3430
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
3431
+ const hasDeletePermission = useDocumentRBAC("deleteAction", (state) => state.canDelete);
3432
+ const { deleteMany: bulkDeleteAction } = useDocumentActions();
3433
+ const documentIds = documents.map(({ documentId }) => documentId);
3434
+ const handleConfirmBulkDelete = async () => {
3435
+ const res = await bulkDeleteAction({
3436
+ documentIds,
3437
+ model,
3438
+ params
3439
+ });
3440
+ if (!("error" in res)) {
3441
+ selectRow([]);
2817
3442
  }
2818
- }
2819
- });
2820
- const { actions, reducer: reducer$1 } = appSlice;
2821
- const { setInitialData } = actions;
2822
- const reducer = toolkit.combineReducers({
2823
- app: reducer$1
2824
- });
2825
- const HOOKS = {
2826
- /**
2827
- * Hook that allows to mutate the displayed headers of the list view table
2828
- * @constant
2829
- * @type {string}
2830
- */
2831
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2832
- /**
2833
- * Hook that allows to mutate the CM's collection types links pre-set filters
2834
- * @constant
2835
- * @type {string}
2836
- */
2837
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2838
- /**
2839
- * Hook that allows to mutate the CM's edit view layout
2840
- * @constant
2841
- * @type {string}
2842
- */
2843
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2844
- /**
2845
- * Hook that allows to mutate the CM's single types links pre-set filters
2846
- * @constant
2847
- * @type {string}
2848
- */
2849
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2850
- };
2851
- const contentTypesApi = contentManagerApi.injectEndpoints({
2852
- endpoints: (builder) => ({
2853
- getContentTypeConfiguration: builder.query({
2854
- query: (uid) => ({
2855
- url: `/content-manager/content-types/${uid}/configuration`,
2856
- method: "GET"
2857
- }),
2858
- transformResponse: (response) => response.data,
2859
- providesTags: (_result, _error, uid) => [
2860
- { type: "ContentTypesConfiguration", id: uid },
2861
- { type: "ContentTypeSettings", id: "LIST" }
2862
- ]
2863
- }),
2864
- getAllContentTypeSettings: builder.query({
2865
- query: () => "/content-manager/content-types-settings",
2866
- transformResponse: (response) => response.data,
2867
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2868
- }),
2869
- updateContentTypeConfiguration: builder.mutation({
2870
- query: ({ uid, ...body }) => ({
2871
- url: `/content-manager/content-types/${uid}/configuration`,
2872
- method: "PUT",
2873
- data: body
2874
- }),
2875
- transformResponse: (response) => response.data,
2876
- invalidatesTags: (_result, _error, { uid }) => [
2877
- { type: "ContentTypesConfiguration", id: uid },
2878
- { type: "ContentTypeSettings", id: "LIST" },
2879
- // Is this necessary?
2880
- { type: "InitialData" }
2881
- ]
2882
- })
2883
- })
2884
- });
2885
- const {
2886
- useGetContentTypeConfigurationQuery,
2887
- useGetAllContentTypeSettingsQuery,
2888
- useUpdateContentTypeConfigurationMutation
2889
- } = contentTypesApi;
2890
- const checkIfAttributeIsDisplayable = (attribute) => {
2891
- const { type } = attribute;
2892
- if (type === "relation") {
2893
- return !attribute.relation.toLowerCase().includes("morph");
2894
- }
2895
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2896
- };
2897
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2898
- if (!mainFieldName) {
2899
- return void 0;
2900
- }
2901
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2902
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2903
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2904
- );
3443
+ };
3444
+ if (!hasDeletePermission)
3445
+ return null;
2905
3446
  return {
2906
- name: mainFieldName,
2907
- type: mainFieldType ?? "string"
3447
+ variant: "danger-light",
3448
+ label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
3449
+ dialog: {
3450
+ type: "dialog",
3451
+ title: formatMessage({
3452
+ id: "app.components.ConfirmDialog.title",
3453
+ defaultMessage: "Confirmation"
3454
+ }),
3455
+ content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3456
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
3457
+ id: "popUpWarning.bodyMessage.contentType.delete.all",
3458
+ defaultMessage: "Are you sure you want to delete these entries?"
3459
+ }) }),
3460
+ hasI18nEnabled && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { textAlign: "center", padding: 3, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger500", children: formatMessage(
3461
+ {
3462
+ id: getTranslation("Settings.list.actions.deleteAdditionalInfos"),
3463
+ defaultMessage: "This will delete the active locale versions <em>(from Internationalization)</em>"
3464
+ },
3465
+ {
3466
+ em: Emphasis
3467
+ }
3468
+ ) }) })
3469
+ ] }),
3470
+ onConfirm: handleConfirmBulkDelete
3471
+ }
2908
3472
  };
2909
3473
  };
2910
- const DEFAULT_SETTINGS = {
2911
- bulkable: false,
2912
- filterable: false,
2913
- searchable: false,
2914
- pagination: false,
2915
- defaultSortBy: "",
2916
- defaultSortOrder: "asc",
2917
- mainField: "id",
2918
- pageSize: 10
2919
- };
2920
- const useDocumentLayout = (model) => {
2921
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
3474
+ DeleteAction.type = "delete";
3475
+ const UnpublishAction = ({ documents, model }) => {
3476
+ const { formatMessage } = reactIntl.useIntl();
3477
+ const { schema } = useDoc();
3478
+ const selectRow = strapiAdmin.useTable("UnpublishAction", (state) => state.selectRow);
3479
+ const hasPublishPermission = useDocumentRBAC("unpublishAction", (state) => state.canPublish);
3480
+ const hasI18nEnabled = Boolean(schema?.pluginOptions?.i18n);
3481
+ const hasDraftAndPublishEnabled = Boolean(schema?.options?.draftAndPublish);
3482
+ const { unpublishMany: bulkUnpublishAction } = useDocumentActions();
3483
+ const documentIds = documents.map(({ documentId }) => documentId);
2922
3484
  const [{ query }] = strapiAdmin.useQueryParams();
2923
- const runHookWaterfall = strapiAdmin.useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2924
- const { toggleNotification } = strapiAdmin.useNotification();
2925
- const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
2926
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2927
- const {
2928
- data,
2929
- isLoading: isLoadingConfigs,
2930
- error,
2931
- isFetching: isFetchingConfigs
2932
- } = useGetContentTypeConfigurationQuery(model);
2933
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2934
- React__namespace.useEffect(() => {
2935
- if (error) {
2936
- toggleNotification({
2937
- type: "danger",
2938
- message: formatAPIError(error)
2939
- });
3485
+ const params = React__namespace.useMemo(() => buildValidParams(query), [query]);
3486
+ const handleConfirmBulkUnpublish = async () => {
3487
+ const data = await bulkUnpublishAction({ documentIds, model, params });
3488
+ if (!("error" in data)) {
3489
+ selectRow([]);
2940
3490
  }
2941
- }, [error, formatAPIError, toggleNotification]);
2942
- const editLayout = React__namespace.useMemo(
2943
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2944
- layout: [],
2945
- components: {},
2946
- metadatas: {},
2947
- options: {},
2948
- settings: DEFAULT_SETTINGS
2949
- },
2950
- [data, isLoading, schemas, schema, components]
2951
- );
2952
- const listLayout = React__namespace.useMemo(() => {
2953
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2954
- layout: [],
2955
- metadatas: {},
2956
- options: {},
2957
- settings: DEFAULT_SETTINGS
2958
- };
2959
- }, [data, isLoading, schemas, schema, components]);
2960
- const { layout: edit } = React__namespace.useMemo(
2961
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2962
- layout: editLayout,
2963
- query
2964
- }),
2965
- [editLayout, query, runHookWaterfall]
2966
- );
3491
+ };
3492
+ const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3493
+ if (!showUnpublishButton)
3494
+ return null;
2967
3495
  return {
2968
- error,
2969
- isLoading,
2970
- edit,
2971
- list: listLayout
3496
+ variant: "tertiary",
3497
+ label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
3498
+ dialog: {
3499
+ type: "dialog",
3500
+ title: formatMessage({
3501
+ id: "app.components.ConfirmDialog.title",
3502
+ defaultMessage: "Confirmation"
3503
+ }),
3504
+ content: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3505
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
3506
+ id: "popUpWarning.bodyMessage.contentType.unpublish.all",
3507
+ defaultMessage: "Are you sure you want to unpublish these entries?"
3508
+ }) }),
3509
+ hasI18nEnabled && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { textAlign: "center", padding: 3, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger500", children: formatMessage(
3510
+ {
3511
+ id: getTranslation("Settings.list.actions.unpublishAdditionalInfos"),
3512
+ defaultMessage: "This will unpublish the active locale versions <em>(from Internationalization)</em>"
3513
+ },
3514
+ {
3515
+ em: Emphasis
3516
+ }
3517
+ ) }) })
3518
+ ] }),
3519
+ confirmButton: formatMessage({
3520
+ id: "app.utils.unpublish",
3521
+ defaultMessage: "Unpublish"
3522
+ }),
3523
+ onConfirm: handleConfirmBulkUnpublish
3524
+ }
2972
3525
  };
2973
3526
  };
2974
- const useDocLayout = () => {
2975
- const { model } = useDoc();
2976
- return useDocumentLayout(model);
3527
+ UnpublishAction.type = "unpublish";
3528
+ const Emphasis = (chunks) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", textColor: "danger500", children: chunks });
3529
+ const DEFAULT_BULK_ACTIONS = [PublishAction, UnpublishAction, DeleteAction];
3530
+ const AutoCloneFailureModalBody = ({ prohibitedFields }) => {
3531
+ const { formatMessage } = reactIntl.useIntl();
3532
+ const getDefaultErrorMessage = (reason) => {
3533
+ switch (reason) {
3534
+ case "relation":
3535
+ return "Duplicating the relation could remove it from the original entry.";
3536
+ case "unique":
3537
+ return "Identical values in a unique field are not allowed";
3538
+ default:
3539
+ return reason;
3540
+ }
3541
+ };
3542
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3543
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", children: formatMessage({
3544
+ id: getTranslation("containers.list.autoCloneModal.title"),
3545
+ defaultMessage: "This entry can't be duplicated directly."
3546
+ }) }),
3547
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: formatMessage({
3548
+ id: getTranslation("containers.list.autoCloneModal.description"),
3549
+ defaultMessage: "A new entry will be created with the same content, but you'll have to change the following fields to save it."
3550
+ }) }) }),
3551
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { marginTop: 6, gap: 2, direction: "column", alignItems: "stretch", children: prohibitedFields.map(([fieldPath, reason]) => /* @__PURE__ */ jsxRuntime.jsxs(
3552
+ designSystem.Flex,
3553
+ {
3554
+ direction: "column",
3555
+ gap: 2,
3556
+ alignItems: "flex-start",
3557
+ borderColor: "neutral200",
3558
+ hasRadius: true,
3559
+ padding: 6,
3560
+ children: [
3561
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "row", tag: "ol", children: fieldPath.map((pathSegment, index2) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { fontWeight: "semiBold", tag: "li", children: [
3562
+ pathSegment,
3563
+ index2 !== fieldPath.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
3564
+ Icons.ChevronRight,
3565
+ {
3566
+ fill: "neutral500",
3567
+ height: "0.8rem",
3568
+ width: "0.8rem",
3569
+ style: { margin: "0 0.8rem" }
3570
+ }
3571
+ )
3572
+ ] }, index2)) }),
3573
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { tag: "p", textColor: "neutral600", children: formatMessage({
3574
+ id: getTranslation(`containers.list.autoCloneModal.error.${reason}`),
3575
+ defaultMessage: getDefaultErrorMessage(reason)
3576
+ }) })
3577
+ ]
3578
+ },
3579
+ fieldPath.join()
3580
+ )) })
3581
+ ] });
2977
3582
  };
2978
- const formatEditLayout = (data, {
2979
- schemas,
2980
- schema,
2981
- components
2982
- }) => {
2983
- let currentPanelIndex = 0;
2984
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2985
- data.contentType.layouts.edit,
2986
- schema?.attributes,
2987
- data.contentType.metadatas,
2988
- { configurations: data.components, schemas: components },
2989
- schemas
2990
- ).reduce((panels, row) => {
2991
- if (row.some((field) => field.type === "dynamiczone")) {
2992
- panels.push([row]);
2993
- currentPanelIndex += 2;
2994
- } else {
2995
- if (!panels[currentPanelIndex]) {
2996
- panels.push([]);
3583
+ const TableActions = ({ document }) => {
3584
+ const { formatMessage } = reactIntl.useIntl();
3585
+ const { model, collectionType } = useDoc();
3586
+ const plugins = strapiAdmin.useStrapiApp("TableActions", (state) => state.plugins);
3587
+ const props = {
3588
+ activeTab: null,
3589
+ model,
3590
+ documentId: document.documentId,
3591
+ collectionType,
3592
+ document
3593
+ };
3594
+ return /* @__PURE__ */ jsxRuntime.jsx(
3595
+ strapiAdmin.DescriptionComponentRenderer,
3596
+ {
3597
+ props,
3598
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
3599
+ children: (actions2) => {
3600
+ const tableRowActions = actions2.filter((action) => {
3601
+ const positions = Array.isArray(action.position) ? action.position : [action.position];
3602
+ return positions.includes("table-row");
3603
+ });
3604
+ return /* @__PURE__ */ jsxRuntime.jsx(
3605
+ DocumentActionsMenu,
3606
+ {
3607
+ actions: tableRowActions,
3608
+ label: formatMessage({
3609
+ id: "content-manager.containers.list.table.row-actions",
3610
+ defaultMessage: "Row action"
3611
+ }),
3612
+ variant: "ghost"
3613
+ }
3614
+ );
2997
3615
  }
2998
- panels[currentPanelIndex].push(row);
2999
3616
  }
3000
- return panels;
3001
- }, []);
3002
- const componentEditAttributes = Object.entries(data.components).reduce(
3003
- (acc, [uid, configuration]) => {
3004
- acc[uid] = {
3005
- layout: convertEditLayoutToFieldLayouts(
3006
- configuration.layouts.edit,
3007
- components[uid].attributes,
3008
- configuration.metadatas
3009
- ),
3010
- settings: {
3011
- ...configuration.settings,
3012
- icon: components[uid].info.icon,
3013
- displayName: components[uid].info.displayName
3014
- }
3015
- };
3016
- return acc;
3017
- },
3018
- {}
3019
- );
3020
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
3021
- (acc, [attribute, metadata]) => {
3022
- return {
3023
- ...acc,
3024
- [attribute]: metadata.edit
3025
- };
3026
- },
3027
- {}
3028
3617
  );
3618
+ };
3619
+ const EditAction = ({ documentId }) => {
3620
+ const navigate = reactRouterDom.useNavigate();
3621
+ const { formatMessage } = reactIntl.useIntl();
3622
+ const { canRead } = useDocumentRBAC("EditAction", ({ canRead: canRead2 }) => ({ canRead: canRead2 }));
3623
+ const { toggleNotification } = strapiAdmin.useNotification();
3624
+ const [{ query }] = strapiAdmin.useQueryParams();
3029
3625
  return {
3030
- layout: panelledEditAttributes,
3031
- components: componentEditAttributes,
3032
- metadatas: editMetadatas,
3033
- settings: {
3034
- ...data.contentType.settings,
3035
- displayName: schema?.info.displayName
3036
- },
3037
- options: {
3038
- ...schema?.options,
3039
- ...schema?.pluginOptions,
3040
- ...data.contentType.options
3626
+ disabled: !canRead,
3627
+ icon: /* @__PURE__ */ jsxRuntime.jsx(StyledPencil, {}),
3628
+ label: formatMessage({
3629
+ id: "content-manager.actions.edit.label",
3630
+ defaultMessage: "Edit"
3631
+ }),
3632
+ position: "table-row",
3633
+ onClick: async () => {
3634
+ if (!documentId) {
3635
+ console.error(
3636
+ "You're trying to edit a document without an id, this is likely a bug with Strapi. Please open an issue."
3637
+ );
3638
+ toggleNotification({
3639
+ message: formatMessage({
3640
+ id: "content-manager.actions.edit.error",
3641
+ defaultMessage: "An error occurred while trying to edit the document."
3642
+ }),
3643
+ type: "danger"
3644
+ });
3645
+ return;
3646
+ }
3647
+ navigate({
3648
+ pathname: documentId,
3649
+ search: qs.stringify({
3650
+ plugins: query.plugins
3651
+ })
3652
+ });
3041
3653
  }
3042
3654
  };
3043
3655
  };
3044
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
3045
- return rows.map(
3046
- (row) => row.map((field) => {
3047
- const attribute = attributes[field.name];
3048
- if (!attribute) {
3049
- return null;
3656
+ EditAction.type = "edit";
3657
+ const StyledPencil = styledComponents.styled(Icons.Pencil)`
3658
+ path {
3659
+ fill: currentColor;
3660
+ }
3661
+ `;
3662
+ const CloneAction = ({ model, documentId }) => {
3663
+ const navigate = reactRouterDom.useNavigate();
3664
+ const { formatMessage } = reactIntl.useIntl();
3665
+ const { canCreate } = useDocumentRBAC("CloneAction", ({ canCreate: canCreate2 }) => ({ canCreate: canCreate2 }));
3666
+ const { toggleNotification } = strapiAdmin.useNotification();
3667
+ const { autoClone } = useDocumentActions();
3668
+ const [prohibitedFields, setProhibitedFields] = React__namespace.useState([]);
3669
+ return {
3670
+ disabled: !canCreate,
3671
+ icon: /* @__PURE__ */ jsxRuntime.jsx(StyledDuplicate, {}),
3672
+ label: formatMessage({
3673
+ id: "content-manager.actions.clone.label",
3674
+ defaultMessage: "Duplicate"
3675
+ }),
3676
+ position: "table-row",
3677
+ onClick: async () => {
3678
+ if (!documentId) {
3679
+ console.error(
3680
+ "You're trying to clone a document in the table without an id, this is likely a bug with Strapi. Please open an issue."
3681
+ );
3682
+ toggleNotification({
3683
+ message: formatMessage({
3684
+ id: "content-manager.actions.clone.error",
3685
+ defaultMessage: "An error occurred while trying to clone the document."
3686
+ }),
3687
+ type: "danger"
3688
+ });
3689
+ return;
3690
+ }
3691
+ const res = await autoClone({ model, sourceId: documentId });
3692
+ if ("data" in res) {
3693
+ navigate(res.data.documentId);
3694
+ return true;
3695
+ }
3696
+ if (isBaseQueryError(res.error) && res.error.details && typeof res.error.details === "object" && "prohibitedFields" in res.error.details && Array.isArray(res.error.details.prohibitedFields)) {
3697
+ const prohibitedFields2 = res.error.details.prohibitedFields;
3698
+ setProhibitedFields(prohibitedFields2);
3050
3699
  }
3051
- const { edit: metadata } = metadatas[field.name];
3052
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
3053
- return {
3054
- attribute,
3055
- disabled: !metadata.editable,
3056
- hint: metadata.description,
3057
- label: metadata.label ?? "",
3058
- name: field.name,
3059
- // @ts-expect-error – mainField does exist on the metadata for a relation.
3060
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
3061
- schemas,
3062
- components: components?.schemas ?? {}
3063
- }),
3064
- placeholder: metadata.placeholder ?? "",
3065
- required: attribute.required ?? false,
3066
- size: field.size,
3067
- unique: "unique" in attribute ? attribute.unique : false,
3068
- visible: metadata.visible ?? true,
3069
- type: attribute.type
3070
- };
3071
- }).filter((field) => field !== null)
3072
- );
3073
- };
3074
- const formatListLayout = (data, {
3075
- schemas,
3076
- schema,
3077
- components
3078
- }) => {
3079
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
3080
- (acc, [attribute, metadata]) => {
3081
- return {
3082
- ...acc,
3083
- [attribute]: metadata.list
3084
- };
3085
3700
  },
3086
- {}
3087
- );
3088
- const listAttributes = convertListLayoutToFieldLayouts(
3089
- data.contentType.layouts.list,
3090
- schema?.attributes,
3091
- listMetadatas,
3092
- { configurations: data.components, schemas: components },
3093
- schemas
3094
- );
3095
- return {
3096
- layout: listAttributes,
3097
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
3098
- metadatas: listMetadatas,
3099
- options: {
3100
- ...schema?.options,
3101
- ...schema?.pluginOptions,
3102
- ...data.contentType.options
3701
+ dialog: {
3702
+ type: "modal",
3703
+ title: formatMessage({
3704
+ id: "content-manager.containers.list.autoCloneModal.header",
3705
+ defaultMessage: "Duplicate"
3706
+ }),
3707
+ content: /* @__PURE__ */ jsxRuntime.jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3708
+ footer: ({ onClose }) => {
3709
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", children: [
3710
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3711
+ id: "cancel",
3712
+ defaultMessage: "Cancel"
3713
+ }) }),
3714
+ /* @__PURE__ */ jsxRuntime.jsx(
3715
+ designSystem.LinkButton,
3716
+ {
3717
+ tag: reactRouterDom.NavLink,
3718
+ to: {
3719
+ pathname: `clone/${documentId}`
3720
+ },
3721
+ children: formatMessage({
3722
+ id: "content-manager.containers.list.autoCloneModal.create",
3723
+ defaultMessage: "Create"
3724
+ })
3725
+ }
3726
+ )
3727
+ ] });
3728
+ }
3103
3729
  }
3104
3730
  };
3105
3731
  };
3106
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
3107
- return columns.map((name) => {
3108
- const attribute = attributes[name];
3109
- if (!attribute) {
3110
- return null;
3732
+ CloneAction.type = "clone";
3733
+ const StyledDuplicate = styledComponents.styled(Icons.Duplicate)`
3734
+ path {
3735
+ fill: currentColor;
3736
+ }
3737
+ `;
3738
+ const DEFAULT_TABLE_ROW_ACTIONS = [EditAction, CloneAction];
3739
+ class ContentManagerPlugin {
3740
+ /**
3741
+ * The following properties are the stored ones provided by any plugins registering with
3742
+ * the content-manager. The function calls however, need to be called at runtime in the
3743
+ * application, so instead we collate them and run them later with the complete list incl.
3744
+ * ones already registered & the context of the view.
3745
+ */
3746
+ bulkActions = [...DEFAULT_BULK_ACTIONS];
3747
+ documentActions = [
3748
+ ...DEFAULT_ACTIONS,
3749
+ ...DEFAULT_TABLE_ROW_ACTIONS,
3750
+ ...DEFAULT_HEADER_ACTIONS,
3751
+ HistoryAction
3752
+ ];
3753
+ editViewSidePanels = [ActionsPanel];
3754
+ headerActions = [];
3755
+ constructor() {
3756
+ }
3757
+ addEditViewSidePanel(panels) {
3758
+ if (Array.isArray(panels)) {
3759
+ this.editViewSidePanels = [...this.editViewSidePanels, ...panels];
3760
+ } else if (typeof panels === "function") {
3761
+ this.editViewSidePanels = panels(this.editViewSidePanels);
3762
+ } else {
3763
+ throw new Error(
3764
+ `Expected the \`panels\` passed to \`addEditViewSidePanel\` to be an array or a function, but received ${getPrintableType(
3765
+ panels
3766
+ )}`
3767
+ );
3111
3768
  }
3112
- const metadata = metadatas[name];
3113
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
3769
+ }
3770
+ addDocumentAction(actions2) {
3771
+ if (Array.isArray(actions2)) {
3772
+ this.documentActions = [...this.documentActions, ...actions2];
3773
+ } else if (typeof actions2 === "function") {
3774
+ this.documentActions = actions2(this.documentActions);
3775
+ } else {
3776
+ throw new Error(
3777
+ `Expected the \`actions\` passed to \`addDocumentAction\` to be an array or a function, but received ${getPrintableType(
3778
+ actions2
3779
+ )}`
3780
+ );
3781
+ }
3782
+ }
3783
+ addDocumentHeaderAction(actions2) {
3784
+ if (Array.isArray(actions2)) {
3785
+ this.headerActions = [...this.headerActions, ...actions2];
3786
+ } else if (typeof actions2 === "function") {
3787
+ this.headerActions = actions2(this.headerActions);
3788
+ } else {
3789
+ throw new Error(
3790
+ `Expected the \`actions\` passed to \`addDocumentHeaderAction\` to be an array or a function, but received ${getPrintableType(
3791
+ actions2
3792
+ )}`
3793
+ );
3794
+ }
3795
+ }
3796
+ addBulkAction(actions2) {
3797
+ if (Array.isArray(actions2)) {
3798
+ this.bulkActions = [...this.bulkActions, ...actions2];
3799
+ } else if (typeof actions2 === "function") {
3800
+ this.bulkActions = actions2(this.bulkActions);
3801
+ } else {
3802
+ throw new Error(
3803
+ `Expected the \`actions\` passed to \`addBulkAction\` to be an array or a function, but received ${getPrintableType(
3804
+ actions2
3805
+ )}`
3806
+ );
3807
+ }
3808
+ }
3809
+ get config() {
3114
3810
  return {
3115
- attribute,
3116
- label: metadata.label ?? "",
3117
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
3118
- schemas,
3119
- components: components?.schemas ?? {}
3120
- }),
3121
- name,
3122
- searchable: metadata.searchable ?? true,
3123
- sortable: metadata.sortable ?? true
3811
+ id: PLUGIN_ID,
3812
+ name: "Content Manager",
3813
+ injectionZones: INJECTION_ZONES,
3814
+ apis: {
3815
+ addBulkAction: this.addBulkAction.bind(this),
3816
+ addDocumentAction: this.addDocumentAction.bind(this),
3817
+ addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3818
+ addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3819
+ getBulkActions: () => this.bulkActions,
3820
+ getDocumentActions: () => this.documentActions,
3821
+ getEditViewSidePanels: () => this.editViewSidePanels,
3822
+ getHeaderActions: () => this.headerActions
3823
+ }
3124
3824
  };
3125
- }).filter((field) => field !== null);
3825
+ }
3826
+ }
3827
+ const getPrintableType = (value) => {
3828
+ const nativeType = typeof value;
3829
+ if (nativeType === "object") {
3830
+ if (value === null)
3831
+ return "null";
3832
+ if (Array.isArray(value))
3833
+ return "array";
3834
+ if (value instanceof Object && value.constructor.name !== "Object") {
3835
+ return value.constructor.name;
3836
+ }
3837
+ }
3838
+ return nativeType;
3839
+ };
3840
+ const initialState = {
3841
+ collectionTypeLinks: [],
3842
+ components: [],
3843
+ fieldSizes: {},
3844
+ models: [],
3845
+ singleTypeLinks: [],
3846
+ isLoading: true
3126
3847
  };
3848
+ const appSlice = toolkit.createSlice({
3849
+ name: "app",
3850
+ initialState,
3851
+ reducers: {
3852
+ setInitialData(state, action) {
3853
+ const {
3854
+ authorizedCollectionTypeLinks,
3855
+ authorizedSingleTypeLinks,
3856
+ components,
3857
+ contentTypeSchemas,
3858
+ fieldSizes
3859
+ } = action.payload;
3860
+ state.collectionTypeLinks = authorizedCollectionTypeLinks.filter(
3861
+ ({ isDisplayed }) => isDisplayed
3862
+ );
3863
+ state.singleTypeLinks = authorizedSingleTypeLinks.filter(({ isDisplayed }) => isDisplayed);
3864
+ state.components = components;
3865
+ state.models = contentTypeSchemas;
3866
+ state.fieldSizes = fieldSizes;
3867
+ state.isLoading = false;
3868
+ }
3869
+ }
3870
+ });
3871
+ const { actions, reducer: reducer$1 } = appSlice;
3872
+ const { setInitialData } = actions;
3873
+ const reducer = toolkit.combineReducers({
3874
+ app: reducer$1
3875
+ });
3127
3876
  const index = {
3128
3877
  register(app) {
3129
3878
  const cm = new ContentManagerPlugin();
3130
3879
  app.addReducers({
3131
- [contentManagerApi.reducerPath]: contentManagerApi.reducer,
3132
3880
  [PLUGIN_ID]: reducer
3133
3881
  });
3134
- app.addMiddlewares([() => contentManagerApi.middleware]);
3135
3882
  app.addMenuLink({
3136
3883
  to: PLUGIN_ID,
3137
3884
  icon: Icons.Feather,
@@ -3140,14 +3887,15 @@ const index = {
3140
3887
  defaultMessage: "Content Manager"
3141
3888
  },
3142
3889
  permissions: [],
3143
- Component: () => Promise.resolve().then(() => require("./layout-DLih5-_W.js")).then((mod) => ({ default: mod.Layout }))
3890
+ Component: () => Promise.resolve().then(() => require("./layout-DEYBqgF1.js")).then((mod) => ({ default: mod.Layout })),
3891
+ position: 1
3144
3892
  });
3145
3893
  app.registerPlugin(cm.config);
3146
3894
  },
3147
3895
  async registerTrads({ locales }) {
3148
3896
  const importedTrads = await Promise.all(
3149
3897
  locales.map((locale) => {
3150
- 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-C-V1_90f.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 }) => {
3898
+ 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 }) => {
3151
3899
  return {
3152
3900
  data: prefixPluginTranslations(data, PLUGIN_ID),
3153
3901
  locale
@@ -3164,6 +3912,7 @@ const index = {
3164
3912
  }
3165
3913
  };
3166
3914
  exports.ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD = ATTRIBUTE_TYPES_THAT_CANNOT_BE_MAIN_FIELD;
3915
+ exports.BulkActionsRenderer = BulkActionsRenderer;
3167
3916
  exports.COLLECTION_TYPES = COLLECTION_TYPES;
3168
3917
  exports.CREATOR_FIELDS = CREATOR_FIELDS;
3169
3918
  exports.DEFAULT_SETTINGS = DEFAULT_SETTINGS;
@@ -3204,4 +3953,4 @@ exports.useGetAllDocumentsQuery = useGetAllDocumentsQuery;
3204
3953
  exports.useGetContentTypeConfigurationQuery = useGetContentTypeConfigurationQuery;
3205
3954
  exports.useGetInitialDataQuery = useGetInitialDataQuery;
3206
3955
  exports.useUpdateContentTypeConfigurationMutation = useUpdateContentTypeConfigurationMutation;
3207
- //# sourceMappingURL=index-DFK7LwDW.js.map
3956
+ //# sourceMappingURL=index-BZoNZMXL.js.map