@strapi/content-manager 0.0.0-experimental.545ccead2ee1717bbc7ab950455dbb0ddb9924a3 → 0.0.0-experimental.5788c38836be65c0321a2dcadbdf44f04b798e8a

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 (125) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-Bqgx7Mes.js → ComponentConfigurationPage-B42mQr1K.js} +3 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-Bqgx7Mes.js.map → ComponentConfigurationPage-B42mQr1K.js.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-B1bIXVuX.mjs → ComponentConfigurationPage-D1YuKq8j.mjs} +3 -3
  4. package/dist/_chunks/{ComponentConfigurationPage-B1bIXVuX.mjs.map → ComponentConfigurationPage-D1YuKq8j.mjs.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-ZO0vOO8q.mjs → EditConfigurationPage-C9yiwgI_.mjs} +3 -3
  6. package/dist/_chunks/{EditConfigurationPage-ZO0vOO8q.mjs.map → EditConfigurationPage-C9yiwgI_.mjs.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-BFEwvdMW.js → EditConfigurationPage-NC89F29V.js} +3 -3
  8. package/dist/_chunks/{EditConfigurationPage-BFEwvdMW.js.map → EditConfigurationPage-NC89F29V.js.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-DA95Ha6J.js → EditViewPage-DYDpe5Wi.js} +30 -9
  10. package/dist/_chunks/EditViewPage-DYDpe5Wi.js.map +1 -0
  11. package/dist/_chunks/{EditViewPage-DlLEyUL6.mjs → EditViewPage-k8UcfVwt.mjs} +30 -9
  12. package/dist/_chunks/EditViewPage-k8UcfVwt.mjs.map +1 -0
  13. package/dist/_chunks/{Field-Dq7bDnuh.mjs → Field-BLL5lknV.mjs} +170 -102
  14. package/dist/_chunks/Field-BLL5lknV.mjs.map +1 -0
  15. package/dist/_chunks/{Field-CnK8dO8N.js → Field-Crhugun2.js} +172 -104
  16. package/dist/_chunks/Field-Crhugun2.js.map +1 -0
  17. package/dist/_chunks/{Form-BpiR4piS.js → Form-DUU19g6M.js} +35 -16
  18. package/dist/_chunks/Form-DUU19g6M.js.map +1 -0
  19. package/dist/_chunks/{Form-B_JE0dbz.mjs → Form-UHu2eOuG.mjs} +35 -16
  20. package/dist/_chunks/Form-UHu2eOuG.mjs.map +1 -0
  21. package/dist/_chunks/{History-CBNGU7a-.mjs → History-CpxkZXS3.mjs} +21 -11
  22. package/dist/_chunks/History-CpxkZXS3.mjs.map +1 -0
  23. package/dist/_chunks/{History-DdIstl8b.js → History-CyA8tvJZ.js} +21 -11
  24. package/dist/_chunks/History-CyA8tvJZ.js.map +1 -0
  25. package/dist/_chunks/{ListConfigurationPage-5dr4qpue.mjs → ListConfigurationPage-OUwV8QF1.mjs} +14 -4
  26. package/dist/_chunks/ListConfigurationPage-OUwV8QF1.mjs.map +1 -0
  27. package/dist/_chunks/{ListConfigurationPage-DkKRparB.js → ListConfigurationPage-pJV7aG2V.js} +14 -4
  28. package/dist/_chunks/ListConfigurationPage-pJV7aG2V.js.map +1 -0
  29. package/dist/_chunks/{ListViewPage-wE0lXqoD.js → ListViewPage-BIT0M8VG.js} +49 -40
  30. package/dist/_chunks/ListViewPage-BIT0M8VG.js.map +1 -0
  31. package/dist/_chunks/{ListViewPage-DecLrYV6.mjs → ListViewPage-BOnhCGkE.mjs} +47 -38
  32. package/dist/_chunks/ListViewPage-BOnhCGkE.mjs.map +1 -0
  33. package/dist/_chunks/{NoContentTypePage-CiIcfYsd.mjs → NoContentTypePage-CwjlHGTn.mjs} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-CiIcfYsd.mjs.map → NoContentTypePage-CwjlHGTn.mjs.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-DEKR6tf9.js → NoContentTypePage-uIBsBUmH.js} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-DEKR6tf9.js.map → NoContentTypePage-uIBsBUmH.js.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-DmNfF2Bb.js → NoPermissionsPage-C8wkEaOF.js} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-DmNfF2Bb.js.map → NoPermissionsPage-C8wkEaOF.js.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-CM5UD8ee.mjs → NoPermissionsPage-CcWbyT_z.mjs} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-CM5UD8ee.mjs.map → NoPermissionsPage-CcWbyT_z.mjs.map} +1 -1
  41. package/dist/_chunks/{Relations-L0xYRoSQ.js → Relations-CwRu_eZv.js} +33 -24
  42. package/dist/_chunks/Relations-CwRu_eZv.js.map +1 -0
  43. package/dist/_chunks/{Relations-Dqz0C1fz.mjs → Relations-wIdWJnA9.mjs} +33 -24
  44. package/dist/_chunks/Relations-wIdWJnA9.mjs.map +1 -0
  45. package/dist/_chunks/{en-uOUIxfcQ.js → en-Bm0D0IWz.js} +13 -12
  46. package/dist/_chunks/{en-uOUIxfcQ.js.map → en-Bm0D0IWz.js.map} +1 -1
  47. package/dist/_chunks/{en-BrCTWlZv.mjs → en-DKV44jRb.mjs} +13 -12
  48. package/dist/_chunks/{en-BrCTWlZv.mjs.map → en-DKV44jRb.mjs.map} +1 -1
  49. package/dist/_chunks/{index-DyvUPg1a.js → index-BO-T2BdP.js} +693 -524
  50. package/dist/_chunks/index-BO-T2BdP.js.map +1 -0
  51. package/dist/_chunks/{index-BSn97i8U.mjs → index-BQ8DxaCa.mjs} +713 -544
  52. package/dist/_chunks/index-BQ8DxaCa.mjs.map +1 -0
  53. package/dist/_chunks/{layout-TPqF2oJ5.js → layout-BTB1_M8g.js} +21 -8
  54. package/dist/_chunks/layout-BTB1_M8g.js.map +1 -0
  55. package/dist/_chunks/{layout-DPaHUusj.mjs → layout-N63eyE5E.mjs} +22 -9
  56. package/dist/_chunks/layout-N63eyE5E.mjs.map +1 -0
  57. package/dist/_chunks/{relations-Ck7-ecDT.mjs → relations-Bh9r0CVE.mjs} +2 -2
  58. package/dist/_chunks/{relations-Ck7-ecDT.mjs.map → relations-Bh9r0CVE.mjs.map} +1 -1
  59. package/dist/_chunks/{relations-BWYS9gkn.js → relations-C9AQuM2z.js} +2 -2
  60. package/dist/_chunks/{relations-BWYS9gkn.js.map → relations-C9AQuM2z.js.map} +1 -1
  61. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  62. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  63. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  64. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  65. package/dist/admin/index.js +2 -1
  66. package/dist/admin/index.js.map +1 -1
  67. package/dist/admin/index.mjs +5 -4
  68. package/dist/admin/src/exports.d.ts +1 -1
  69. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  70. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  71. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  72. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  73. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  74. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  75. package/dist/admin/src/services/api.d.ts +1 -1
  76. package/dist/admin/src/services/components.d.ts +2 -2
  77. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  78. package/dist/admin/src/services/documents.d.ts +19 -17
  79. package/dist/admin/src/services/init.d.ts +1 -1
  80. package/dist/admin/src/services/relations.d.ts +2 -2
  81. package/dist/admin/src/services/uid.d.ts +3 -3
  82. package/dist/admin/src/utils/validation.d.ts +4 -1
  83. package/dist/server/index.js +195 -117
  84. package/dist/server/index.js.map +1 -1
  85. package/dist/server/index.mjs +196 -118
  86. package/dist/server/index.mjs.map +1 -1
  87. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  88. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  89. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  90. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  91. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  92. package/dist/server/src/history/services/history.d.ts.map +1 -1
  93. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  94. package/dist/server/src/history/services/utils.d.ts +2 -1
  95. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  96. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  97. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  98. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  99. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  100. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  101. package/dist/shared/contracts/collection-types.d.ts +3 -1
  102. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  103. package/package.json +11 -11
  104. package/dist/_chunks/EditViewPage-DA95Ha6J.js.map +0 -1
  105. package/dist/_chunks/EditViewPage-DlLEyUL6.mjs.map +0 -1
  106. package/dist/_chunks/Field-CnK8dO8N.js.map +0 -1
  107. package/dist/_chunks/Field-Dq7bDnuh.mjs.map +0 -1
  108. package/dist/_chunks/Form-B_JE0dbz.mjs.map +0 -1
  109. package/dist/_chunks/Form-BpiR4piS.js.map +0 -1
  110. package/dist/_chunks/History-CBNGU7a-.mjs.map +0 -1
  111. package/dist/_chunks/History-DdIstl8b.js.map +0 -1
  112. package/dist/_chunks/ListConfigurationPage-5dr4qpue.mjs.map +0 -1
  113. package/dist/_chunks/ListConfigurationPage-DkKRparB.js.map +0 -1
  114. package/dist/_chunks/ListViewPage-DecLrYV6.mjs.map +0 -1
  115. package/dist/_chunks/ListViewPage-wE0lXqoD.js.map +0 -1
  116. package/dist/_chunks/Relations-Dqz0C1fz.mjs.map +0 -1
  117. package/dist/_chunks/Relations-L0xYRoSQ.js.map +0 -1
  118. package/dist/_chunks/index-BSn97i8U.mjs.map +0 -1
  119. package/dist/_chunks/index-DyvUPg1a.js.map +0 -1
  120. package/dist/_chunks/layout-DPaHUusj.mjs.map +0 -1
  121. package/dist/_chunks/layout-TPqF2oJ5.js.map +0 -1
  122. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  123. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  124. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  125. package/strapi-server.js +0 -3
@@ -1,16 +1,16 @@
1
- import { CrossCircle, More, WarningCircle, ListPlus, Pencil, Trash, Check, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
1
+ import { More, Cross, WarningCircle, ListPlus, Pencil, Trash, Check, CrossCircle, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
2
2
  import { jsx, Fragment, jsxs } from "react/jsx-runtime";
3
- import { useStrapiApp, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useQueryParams, useTracking, useForm, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
3
+ import { useStrapiApp, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, useQueryParams, getYupValidationErrors, useForm, useTracking, useGuidedTour, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
4
4
  import * as React from "react";
5
5
  import { lazy } from "react";
6
- import { Button, Menu, VisuallyHidden, Flex, Box, Typography, Dialog, Modal, Radio, Status, SingleSelect, SingleSelectOption, Loader, IconButton, Tooltip, LinkButton } from "@strapi/design-system";
6
+ import { Button, Menu, VisuallyHidden, Flex, Typography, Dialog, Modal, Radio, Status, Box, SingleSelect, SingleSelectOption, IconButton, Loader, Tooltip, LinkButton } from "@strapi/design-system";
7
7
  import { useIntl } from "react-intl";
8
- import { useParams, Navigate, useNavigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
9
- import { styled } from "styled-components";
8
+ import { useParams, useNavigate, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
10
9
  import * as yup from "yup";
11
10
  import { ValidationError } from "yup";
12
11
  import pipe from "lodash/fp/pipe";
13
12
  import { intervalToDuration, isPast } from "date-fns";
13
+ import { styled } from "styled-components";
14
14
  import { stringify } from "qs";
15
15
  import { createSlice, combineReducers } from "@reduxjs/toolkit";
16
16
  const __variableDynamicImportRuntimeHelper = (glob, path) => {
@@ -158,7 +158,8 @@ const contentManagerApi = adminApi.enhanceEndpoints({
158
158
  "Document",
159
159
  "InitialData",
160
160
  "HistoryVersion",
161
- "Relations"
161
+ "Relations",
162
+ "UidAvailability"
162
163
  ]
163
164
  });
164
165
  const documentApi = contentManagerApi.injectEndpoints({
@@ -172,7 +173,12 @@ const documentApi = contentManagerApi.injectEndpoints({
172
173
  params: query
173
174
  }
174
175
  }),
175
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
176
+ invalidatesTags: (_result, error, { model }) => {
177
+ if (error) {
178
+ return [];
179
+ }
180
+ return [{ type: "Document", id: `${model}_LIST` }];
181
+ }
176
182
  }),
177
183
  cloneDocument: builder.mutation({
178
184
  query: ({ model, sourceId, data, params }) => ({
@@ -183,7 +189,10 @@ const documentApi = contentManagerApi.injectEndpoints({
183
189
  params
184
190
  }
185
191
  }),
186
- invalidatesTags: (_result, _error, { model }) => [{ type: "Document", id: `${model}_LIST` }]
192
+ invalidatesTags: (_result, _error, { model }) => [
193
+ { type: "Document", id: `${model}_LIST` },
194
+ { type: "UidAvailability", id: model }
195
+ ]
187
196
  }),
188
197
  /**
189
198
  * Creates a new collection-type document. This should ONLY be used for collection-types.
@@ -200,7 +209,8 @@ const documentApi = contentManagerApi.injectEndpoints({
200
209
  }),
201
210
  invalidatesTags: (result, _error, { model }) => [
202
211
  { type: "Document", id: `${model}_LIST` },
203
- "Relations"
212
+ "Relations",
213
+ { type: "UidAvailability", id: model }
204
214
  ]
205
215
  }),
206
216
  deleteDocument: builder.mutation({
@@ -241,7 +251,8 @@ const documentApi = contentManagerApi.injectEndpoints({
241
251
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
242
252
  },
243
253
  { type: "Document", id: `${model}_LIST` },
244
- "Relations"
254
+ "Relations",
255
+ { type: "UidAvailability", id: model }
245
256
  ];
246
257
  }
247
258
  }),
@@ -259,6 +270,7 @@ const documentApi = contentManagerApi.injectEndpoints({
259
270
  }),
260
271
  providesTags: (result, _error, arg) => {
261
272
  return [
273
+ { type: "Document", id: `ALL_LIST` },
262
274
  { type: "Document", id: `${arg.model}_LIST` },
263
275
  ...result?.results.map(({ documentId }) => ({
264
276
  type: "Document",
@@ -297,6 +309,11 @@ const documentApi = contentManagerApi.injectEndpoints({
297
309
  {
298
310
  type: "Document",
299
311
  id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
312
+ },
313
+ // Make it easy to invalidate all individual documents queries for a model
314
+ {
315
+ type: "Document",
316
+ id: `${model}_ALL_ITEMS`
300
317
  }
301
318
  ];
302
319
  }
@@ -360,8 +377,21 @@ const documentApi = contentManagerApi.injectEndpoints({
360
377
  type: "Document",
361
378
  id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
362
379
  },
363
- "Relations"
380
+ "Relations",
381
+ { type: "UidAvailability", id: model }
364
382
  ];
383
+ },
384
+ async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
385
+ const patchResult = dispatch(
386
+ documentApi.util.updateQueryData("getDocument", patch, (draft) => {
387
+ Object.assign(draft.data, data);
388
+ })
389
+ );
390
+ try {
391
+ await queryFulfilled;
392
+ } catch {
393
+ patchResult.undo();
394
+ }
365
395
  }
366
396
  }),
367
397
  unpublishDocument: builder.mutation({
@@ -431,20 +461,36 @@ const buildValidParams = (query) => {
431
461
  const isBaseQueryError = (error) => {
432
462
  return error.name !== void 0;
433
463
  };
434
- const createYupSchema = (attributes = {}, components = {}) => {
464
+ const arrayValidator = (options) => ({
465
+ message: translatedErrors.required,
466
+ test(value) {
467
+ if (options.status === "draft") {
468
+ return true;
469
+ }
470
+ if (!value) {
471
+ return false;
472
+ }
473
+ if (Array.isArray(value) && value.length === 0) {
474
+ return false;
475
+ }
476
+ return true;
477
+ }
478
+ });
479
+ const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
435
480
  const createModelSchema = (attributes2) => yup.object().shape(
436
481
  Object.entries(attributes2).reduce((acc, [name, attribute]) => {
437
482
  if (DOCUMENT_META_FIELDS.includes(name)) {
438
483
  return acc;
439
484
  }
440
485
  const validations = [
486
+ addNullableValidation,
441
487
  addRequiredValidation,
442
488
  addMinLengthValidation,
443
489
  addMaxLengthValidation,
444
490
  addMinValidation,
445
491
  addMaxValidation,
446
492
  addRegexValidation
447
- ].map((fn) => fn(attribute));
493
+ ].map((fn) => fn(attribute, options));
448
494
  const transformSchema = pipe(...validations);
449
495
  switch (attribute.type) {
450
496
  case "component": {
@@ -454,12 +500,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
454
500
  ...acc,
455
501
  [name]: transformSchema(
456
502
  yup.array().of(createModelSchema(attributes3).nullable(false))
457
- )
503
+ ).test(arrayValidator(options))
458
504
  };
459
505
  } else {
460
506
  return {
461
507
  ...acc,
462
- [name]: transformSchema(createModelSchema(attributes3))
508
+ [name]: transformSchema(createModelSchema(attributes3).nullable())
463
509
  };
464
510
  }
465
511
  }
@@ -481,7 +527,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
481
527
  }
482
528
  )
483
529
  )
484
- )
530
+ ).test(arrayValidator(options))
485
531
  };
486
532
  case "relation":
487
533
  return {
@@ -493,7 +539,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
493
539
  } else if (Array.isArray(value)) {
494
540
  return yup.array().of(
495
541
  yup.object().shape({
496
- id: yup.string().required()
542
+ id: yup.number().required()
497
543
  })
498
544
  );
499
545
  } else if (typeof value === "object") {
@@ -545,6 +591,14 @@ const createAttributeSchema = (attribute) => {
545
591
  if (!value || typeof value === "string" && value.length === 0) {
546
592
  return true;
547
593
  }
594
+ if (typeof value === "object") {
595
+ try {
596
+ JSON.stringify(value);
597
+ return true;
598
+ } catch (err) {
599
+ return false;
600
+ }
601
+ }
548
602
  try {
549
603
  JSON.parse(value);
550
604
  return true;
@@ -563,13 +617,7 @@ const createAttributeSchema = (attribute) => {
563
617
  return yup.mixed();
564
618
  }
565
619
  };
566
- const addRequiredValidation = (attribute) => (schema) => {
567
- if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
568
- return schema.min(1, translatedErrors.required);
569
- }
570
- if (attribute.required && attribute.type !== "relation") {
571
- return schema.required(translatedErrors.required);
572
- }
620
+ const nullableSchema = (schema) => {
573
621
  return schema?.nullable ? schema.nullable() : (
574
622
  // In some cases '.nullable' will not be available on the schema.
575
623
  // e.g. when the schema has been built using yup.lazy (e.g. for relations).
@@ -577,7 +625,22 @@ const addRequiredValidation = (attribute) => (schema) => {
577
625
  schema
578
626
  );
579
627
  };
580
- const addMinLengthValidation = (attribute) => (schema) => {
628
+ const addNullableValidation = () => (schema) => {
629
+ return nullableSchema(schema);
630
+ };
631
+ const addRequiredValidation = (attribute, options) => (schema) => {
632
+ if (options.status === "draft" || !attribute.required) {
633
+ return schema;
634
+ }
635
+ if (attribute.required && "required" in schema) {
636
+ return schema.required(translatedErrors.required);
637
+ }
638
+ return schema;
639
+ };
640
+ const addMinLengthValidation = (attribute, options) => (schema) => {
641
+ if (options.status === "draft") {
642
+ return schema;
643
+ }
581
644
  if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
582
645
  return schema.min(attribute.minLength, {
583
646
  ...translatedErrors.minLength,
@@ -599,32 +662,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
599
662
  }
600
663
  return schema;
601
664
  };
602
- const addMinValidation = (attribute) => (schema) => {
603
- if ("min" in attribute) {
665
+ const addMinValidation = (attribute, options) => (schema) => {
666
+ if (options.status === "draft") {
667
+ return schema;
668
+ }
669
+ if ("min" in attribute && "min" in schema) {
604
670
  const min = toInteger(attribute.min);
605
- if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
606
- if (!attribute.required && "test" in schema && min) {
607
- return schema.test(
608
- "custom-min",
609
- {
610
- ...translatedErrors.min,
611
- values: {
612
- min: attribute.min
613
- }
614
- },
615
- (value) => {
616
- if (!value) {
617
- return true;
618
- }
619
- if (Array.isArray(value) && value.length === 0) {
620
- return true;
621
- }
622
- return value.length >= min;
623
- }
624
- );
625
- }
626
- }
627
- if ("min" in schema && min) {
671
+ if (min) {
628
672
  return schema.min(min, {
629
673
  ...translatedErrors.min,
630
674
  values: {
@@ -742,19 +786,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
742
786
  }, {});
743
787
  return componentsByKey;
744
788
  };
745
- const useDocument = (args, opts) => {
789
+ const HOOKS = {
790
+ /**
791
+ * Hook that allows to mutate the displayed headers of the list view table
792
+ * @constant
793
+ * @type {string}
794
+ */
795
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
796
+ /**
797
+ * Hook that allows to mutate the CM's collection types links pre-set filters
798
+ * @constant
799
+ * @type {string}
800
+ */
801
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
802
+ /**
803
+ * Hook that allows to mutate the CM's edit view layout
804
+ * @constant
805
+ * @type {string}
806
+ */
807
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
808
+ /**
809
+ * Hook that allows to mutate the CM's single types links pre-set filters
810
+ * @constant
811
+ * @type {string}
812
+ */
813
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
814
+ };
815
+ const contentTypesApi = contentManagerApi.injectEndpoints({
816
+ endpoints: (builder) => ({
817
+ getContentTypeConfiguration: builder.query({
818
+ query: (uid) => ({
819
+ url: `/content-manager/content-types/${uid}/configuration`,
820
+ method: "GET"
821
+ }),
822
+ transformResponse: (response) => response.data,
823
+ providesTags: (_result, _error, uid) => [
824
+ { type: "ContentTypesConfiguration", id: uid },
825
+ { type: "ContentTypeSettings", id: "LIST" }
826
+ ]
827
+ }),
828
+ getAllContentTypeSettings: builder.query({
829
+ query: () => "/content-manager/content-types-settings",
830
+ transformResponse: (response) => response.data,
831
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
832
+ }),
833
+ updateContentTypeConfiguration: builder.mutation({
834
+ query: ({ uid, ...body }) => ({
835
+ url: `/content-manager/content-types/${uid}/configuration`,
836
+ method: "PUT",
837
+ data: body
838
+ }),
839
+ transformResponse: (response) => response.data,
840
+ invalidatesTags: (_result, _error, { uid }) => [
841
+ { type: "ContentTypesConfiguration", id: uid },
842
+ { type: "ContentTypeSettings", id: "LIST" },
843
+ // Is this necessary?
844
+ { type: "InitialData" }
845
+ ]
846
+ })
847
+ })
848
+ });
849
+ const {
850
+ useGetContentTypeConfigurationQuery,
851
+ useGetAllContentTypeSettingsQuery,
852
+ useUpdateContentTypeConfigurationMutation
853
+ } = contentTypesApi;
854
+ const checkIfAttributeIsDisplayable = (attribute) => {
855
+ const { type } = attribute;
856
+ if (type === "relation") {
857
+ return !attribute.relation.toLowerCase().includes("morph");
858
+ }
859
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
860
+ };
861
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
862
+ if (!mainFieldName) {
863
+ return void 0;
864
+ }
865
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
866
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
867
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
868
+ );
869
+ return {
870
+ name: mainFieldName,
871
+ type: mainFieldType ?? "string"
872
+ };
873
+ };
874
+ const DEFAULT_SETTINGS = {
875
+ bulkable: false,
876
+ filterable: false,
877
+ searchable: false,
878
+ pagination: false,
879
+ defaultSortBy: "",
880
+ defaultSortOrder: "asc",
881
+ mainField: "id",
882
+ pageSize: 10
883
+ };
884
+ const useDocumentLayout = (model) => {
885
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
886
+ const [{ query }] = useQueryParams();
887
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
746
888
  const { toggleNotification } = useNotification();
747
889
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
890
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
748
891
  const {
749
- currentData: data,
750
- isLoading: isLoadingDocument,
751
- isFetching: isFetchingDocument,
752
- error
753
- } = useGetDocumentQuery(args, {
754
- ...opts,
755
- skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
756
- });
757
- const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
892
+ data,
893
+ isLoading: isLoadingConfigs,
894
+ error,
895
+ isFetching: isFetchingConfigs
896
+ } = useGetContentTypeConfigurationQuery(model);
897
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
758
898
  React.useEffect(() => {
759
899
  if (error) {
760
900
  toggleNotification({
@@ -762,83 +902,339 @@ const useDocument = (args, opts) => {
762
902
  message: formatAPIError(error)
763
903
  });
764
904
  }
765
- }, [toggleNotification, error, formatAPIError, args.collectionType]);
766
- const validationSchema = React.useMemo(() => {
767
- if (!schema) {
768
- return null;
769
- }
770
- return createYupSchema(schema.attributes, components);
771
- }, [schema, components]);
772
- const validate = React.useCallback(
773
- (document) => {
774
- if (!validationSchema) {
775
- throw new Error(
776
- "There is no validation schema generated, this is likely due to the schema not being loaded yet."
777
- );
778
- }
779
- try {
780
- validationSchema.validateSync(document, { abortEarly: false, strict: true });
781
- return null;
782
- } catch (error2) {
783
- if (error2 instanceof ValidationError) {
784
- return getYupValidationErrors(error2);
785
- }
786
- throw error2;
787
- }
905
+ }, [error, formatAPIError, toggleNotification]);
906
+ const editLayout = React.useMemo(
907
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
908
+ layout: [],
909
+ components: {},
910
+ metadatas: {},
911
+ options: {},
912
+ settings: DEFAULT_SETTINGS
788
913
  },
789
- [validationSchema]
914
+ [data, isLoading, schemas, schema, components]
915
+ );
916
+ const listLayout = React.useMemo(() => {
917
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
918
+ layout: [],
919
+ metadatas: {},
920
+ options: {},
921
+ settings: DEFAULT_SETTINGS
922
+ };
923
+ }, [data, isLoading, schemas, schema, components]);
924
+ const { layout: edit } = React.useMemo(
925
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
926
+ layout: editLayout,
927
+ query
928
+ }),
929
+ [editLayout, query, runHookWaterfall]
790
930
  );
791
- const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
792
931
  return {
793
- components,
794
- document: data?.data,
795
- meta: data?.meta,
932
+ error,
796
933
  isLoading,
797
- schema,
798
- validate
799
- };
800
- };
801
- const useDoc = () => {
802
- const { id, slug, collectionType, origin } = useParams();
803
- const [{ query }] = useQueryParams();
804
- const params = React.useMemo(() => buildValidParams(query), [query]);
805
- if (!collectionType) {
806
- throw new Error("Could not find collectionType in url params");
807
- }
808
- if (!slug) {
809
- throw new Error("Could not find model in url params");
810
- }
811
- return {
812
- collectionType,
813
- model: slug,
814
- id: origin || id === "create" ? void 0 : id,
815
- ...useDocument(
816
- { documentId: origin || id, model: slug, collectionType, params },
817
- {
818
- skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
819
- }
820
- )
934
+ edit,
935
+ list: listLayout
821
936
  };
822
937
  };
823
- const prefixPluginTranslations = (trad, pluginId) => {
824
- if (!pluginId) {
825
- throw new TypeError("pluginId can't be empty");
826
- }
827
- return Object.keys(trad).reduce((acc, current) => {
828
- acc[`${pluginId}.${current}`] = trad[current];
829
- return acc;
830
- }, {});
938
+ const useDocLayout = () => {
939
+ const { model } = useDoc();
940
+ return useDocumentLayout(model);
831
941
  };
832
- const getTranslation = (id) => `content-manager.${id}`;
833
- const DEFAULT_UNEXPECTED_ERROR_MSG = {
834
- id: "notification.error",
835
- defaultMessage: "An error occurred, please try again"
942
+ const formatEditLayout = (data, {
943
+ schemas,
944
+ schema,
945
+ components
946
+ }) => {
947
+ let currentPanelIndex = 0;
948
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
949
+ data.contentType.layouts.edit,
950
+ schema?.attributes,
951
+ data.contentType.metadatas,
952
+ { configurations: data.components, schemas: components },
953
+ schemas
954
+ ).reduce((panels, row) => {
955
+ if (row.some((field) => field.type === "dynamiczone")) {
956
+ panels.push([row]);
957
+ currentPanelIndex += 2;
958
+ } else {
959
+ if (!panels[currentPanelIndex]) {
960
+ panels.push([]);
961
+ }
962
+ panels[currentPanelIndex].push(row);
963
+ }
964
+ return panels;
965
+ }, []);
966
+ const componentEditAttributes = Object.entries(data.components).reduce(
967
+ (acc, [uid, configuration]) => {
968
+ acc[uid] = {
969
+ layout: convertEditLayoutToFieldLayouts(
970
+ configuration.layouts.edit,
971
+ components[uid].attributes,
972
+ configuration.metadatas,
973
+ { configurations: data.components, schemas: components }
974
+ ),
975
+ settings: {
976
+ ...configuration.settings,
977
+ icon: components[uid].info.icon,
978
+ displayName: components[uid].info.displayName
979
+ }
980
+ };
981
+ return acc;
982
+ },
983
+ {}
984
+ );
985
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
986
+ (acc, [attribute, metadata]) => {
987
+ return {
988
+ ...acc,
989
+ [attribute]: metadata.edit
990
+ };
991
+ },
992
+ {}
993
+ );
994
+ return {
995
+ layout: panelledEditAttributes,
996
+ components: componentEditAttributes,
997
+ metadatas: editMetadatas,
998
+ settings: {
999
+ ...data.contentType.settings,
1000
+ displayName: schema?.info.displayName
1001
+ },
1002
+ options: {
1003
+ ...schema?.options,
1004
+ ...schema?.pluginOptions,
1005
+ ...data.contentType.options
1006
+ }
1007
+ };
1008
+ };
1009
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
1010
+ return rows.map(
1011
+ (row) => row.map((field) => {
1012
+ const attribute = attributes[field.name];
1013
+ if (!attribute) {
1014
+ return null;
1015
+ }
1016
+ const { edit: metadata } = metadatas[field.name];
1017
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1018
+ return {
1019
+ attribute,
1020
+ disabled: !metadata.editable,
1021
+ hint: metadata.description,
1022
+ label: metadata.label ?? "",
1023
+ name: field.name,
1024
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
1025
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1026
+ schemas,
1027
+ components: components?.schemas ?? {}
1028
+ }),
1029
+ placeholder: metadata.placeholder ?? "",
1030
+ required: attribute.required ?? false,
1031
+ size: field.size,
1032
+ unique: "unique" in attribute ? attribute.unique : false,
1033
+ visible: metadata.visible ?? true,
1034
+ type: attribute.type
1035
+ };
1036
+ }).filter((field) => field !== null)
1037
+ );
1038
+ };
1039
+ const formatListLayout = (data, {
1040
+ schemas,
1041
+ schema,
1042
+ components
1043
+ }) => {
1044
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
1045
+ (acc, [attribute, metadata]) => {
1046
+ return {
1047
+ ...acc,
1048
+ [attribute]: metadata.list
1049
+ };
1050
+ },
1051
+ {}
1052
+ );
1053
+ const listAttributes = convertListLayoutToFieldLayouts(
1054
+ data.contentType.layouts.list,
1055
+ schema?.attributes,
1056
+ listMetadatas,
1057
+ { configurations: data.components, schemas: components },
1058
+ schemas
1059
+ );
1060
+ return {
1061
+ layout: listAttributes,
1062
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
1063
+ metadatas: listMetadatas,
1064
+ options: {
1065
+ ...schema?.options,
1066
+ ...schema?.pluginOptions,
1067
+ ...data.contentType.options
1068
+ }
1069
+ };
1070
+ };
1071
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
1072
+ return columns.map((name) => {
1073
+ const attribute = attributes[name];
1074
+ if (!attribute) {
1075
+ return null;
1076
+ }
1077
+ const metadata = metadatas[name];
1078
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
1079
+ return {
1080
+ attribute,
1081
+ label: metadata.label ?? "",
1082
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
1083
+ schemas,
1084
+ components: components?.schemas ?? {}
1085
+ }),
1086
+ name,
1087
+ searchable: metadata.searchable ?? true,
1088
+ sortable: metadata.sortable ?? true
1089
+ };
1090
+ }).filter((field) => field !== null);
1091
+ };
1092
+ const useDocument = (args, opts) => {
1093
+ const { toggleNotification } = useNotification();
1094
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1095
+ const {
1096
+ currentData: data,
1097
+ isLoading: isLoadingDocument,
1098
+ isFetching: isFetchingDocument,
1099
+ error
1100
+ } = useGetDocumentQuery(args, {
1101
+ ...opts,
1102
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
1103
+ });
1104
+ const {
1105
+ components,
1106
+ schema,
1107
+ schemas,
1108
+ isLoading: isLoadingSchema
1109
+ } = useContentTypeSchema(args.model);
1110
+ React.useEffect(() => {
1111
+ if (error) {
1112
+ toggleNotification({
1113
+ type: "danger",
1114
+ message: formatAPIError(error)
1115
+ });
1116
+ }
1117
+ }, [toggleNotification, error, formatAPIError, args.collectionType]);
1118
+ const validationSchema = React.useMemo(() => {
1119
+ if (!schema) {
1120
+ return null;
1121
+ }
1122
+ return createYupSchema(schema.attributes, components);
1123
+ }, [schema, components]);
1124
+ const validate = React.useCallback(
1125
+ (document) => {
1126
+ if (!validationSchema) {
1127
+ throw new Error(
1128
+ "There is no validation schema generated, this is likely due to the schema not being loaded yet."
1129
+ );
1130
+ }
1131
+ try {
1132
+ validationSchema.validateSync(document, { abortEarly: false, strict: true });
1133
+ return null;
1134
+ } catch (error2) {
1135
+ if (error2 instanceof ValidationError) {
1136
+ return getYupValidationErrors(error2);
1137
+ }
1138
+ throw error2;
1139
+ }
1140
+ },
1141
+ [validationSchema]
1142
+ );
1143
+ const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
1144
+ const hasError = !!error;
1145
+ return {
1146
+ components,
1147
+ document: data?.data,
1148
+ meta: data?.meta,
1149
+ isLoading,
1150
+ hasError,
1151
+ schema,
1152
+ schemas,
1153
+ validate
1154
+ };
1155
+ };
1156
+ const useDoc = () => {
1157
+ const { id, slug, collectionType, origin } = useParams();
1158
+ const [{ query }] = useQueryParams();
1159
+ const params = React.useMemo(() => buildValidParams(query), [query]);
1160
+ if (!collectionType) {
1161
+ throw new Error("Could not find collectionType in url params");
1162
+ }
1163
+ if (!slug) {
1164
+ throw new Error("Could not find model in url params");
1165
+ }
1166
+ return {
1167
+ collectionType,
1168
+ model: slug,
1169
+ id: origin || id === "create" ? void 0 : id,
1170
+ ...useDocument(
1171
+ { documentId: origin || id, model: slug, collectionType, params },
1172
+ {
1173
+ skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
1174
+ }
1175
+ )
1176
+ };
1177
+ };
1178
+ const useContentManagerContext = () => {
1179
+ const {
1180
+ collectionType,
1181
+ model,
1182
+ id,
1183
+ components,
1184
+ isLoading: isLoadingDoc,
1185
+ schema,
1186
+ schemas
1187
+ } = useDoc();
1188
+ const layout = useDocumentLayout(model);
1189
+ const form = useForm("useContentManagerContext", (state) => state);
1190
+ const isSingleType = collectionType === SINGLE_TYPES;
1191
+ const slug = model;
1192
+ const isCreatingEntry = id === "create";
1193
+ useContentTypeSchema();
1194
+ const isLoading = isLoadingDoc || layout.isLoading;
1195
+ const error = layout.error;
1196
+ return {
1197
+ error,
1198
+ isLoading,
1199
+ // Base metadata
1200
+ model,
1201
+ collectionType,
1202
+ id,
1203
+ slug,
1204
+ isCreatingEntry,
1205
+ isSingleType,
1206
+ hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
1207
+ // All schema infos
1208
+ components,
1209
+ contentType: schema,
1210
+ contentTypes: schemas,
1211
+ // Form state
1212
+ form,
1213
+ // layout infos
1214
+ layout
1215
+ };
1216
+ };
1217
+ const prefixPluginTranslations = (trad, pluginId) => {
1218
+ if (!pluginId) {
1219
+ throw new TypeError("pluginId can't be empty");
1220
+ }
1221
+ return Object.keys(trad).reduce((acc, current) => {
1222
+ acc[`${pluginId}.${current}`] = trad[current];
1223
+ return acc;
1224
+ }, {});
1225
+ };
1226
+ const getTranslation = (id) => `content-manager.${id}`;
1227
+ const DEFAULT_UNEXPECTED_ERROR_MSG = {
1228
+ id: "notification.error",
1229
+ defaultMessage: "An error occurred, please try again"
836
1230
  };
837
1231
  const useDocumentActions = () => {
838
1232
  const { toggleNotification } = useNotification();
839
1233
  const { formatMessage } = useIntl();
840
1234
  const { trackUsage } = useTracking();
841
1235
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
1236
+ const navigate = useNavigate();
1237
+ const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
842
1238
  const [deleteDocument] = useDeleteDocumentMutation();
843
1239
  const _delete = React.useCallback(
844
1240
  async ({ collectionType, model, documentId, params }, trackerProperty) => {
@@ -1153,6 +1549,7 @@ const useDocumentActions = () => {
1153
1549
  defaultMessage: "Saved document"
1154
1550
  })
1155
1551
  });
1552
+ setCurrentStep("contentManager.success");
1156
1553
  return res.data;
1157
1554
  } catch (err) {
1158
1555
  toggleNotification({
@@ -1174,7 +1571,6 @@ const useDocumentActions = () => {
1174
1571
  sourceId
1175
1572
  });
1176
1573
  if ("error" in res) {
1177
- toggleNotification({ type: "danger", message: formatAPIError(res.error) });
1178
1574
  return { error: res.error };
1179
1575
  }
1180
1576
  toggleNotification({
@@ -1193,7 +1589,7 @@ const useDocumentActions = () => {
1193
1589
  throw err;
1194
1590
  }
1195
1591
  },
1196
- [autoCloneDocument, formatAPIError, formatMessage, toggleNotification]
1592
+ [autoCloneDocument, formatMessage, toggleNotification]
1197
1593
  );
1198
1594
  const [cloneDocument] = useCloneDocumentMutation();
1199
1595
  const clone = React.useCallback(
@@ -1219,6 +1615,7 @@ const useDocumentActions = () => {
1219
1615
  defaultMessage: "Cloned document"
1220
1616
  })
1221
1617
  });
1618
+ navigate(`../../${res.data.data.documentId}`, { relative: "path" });
1222
1619
  return res.data;
1223
1620
  } catch (err) {
1224
1621
  toggleNotification({
@@ -1229,7 +1626,7 @@ const useDocumentActions = () => {
1229
1626
  throw err;
1230
1627
  }
1231
1628
  },
1232
- [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
1629
+ [cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
1233
1630
  );
1234
1631
  const [getDoc] = useLazyGetDocumentQuery();
1235
1632
  const getDocument = React.useCallback(
@@ -1255,7 +1652,7 @@ const useDocumentActions = () => {
1255
1652
  };
1256
1653
  };
1257
1654
  const ProtectedHistoryPage = lazy(
1258
- () => import("./History-CBNGU7a-.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1655
+ () => import("./History-CpxkZXS3.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1259
1656
  );
1260
1657
  const routes$1 = [
1261
1658
  {
@@ -1268,31 +1665,31 @@ const routes$1 = [
1268
1665
  }
1269
1666
  ];
1270
1667
  const ProtectedEditViewPage = lazy(
1271
- () => import("./EditViewPage-DlLEyUL6.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1668
+ () => import("./EditViewPage-k8UcfVwt.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1272
1669
  );
1273
1670
  const ProtectedListViewPage = lazy(
1274
- () => import("./ListViewPage-DecLrYV6.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1671
+ () => import("./ListViewPage-BOnhCGkE.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1275
1672
  );
1276
1673
  const ProtectedListConfiguration = lazy(
1277
- () => import("./ListConfigurationPage-5dr4qpue.mjs").then((mod) => ({
1674
+ () => import("./ListConfigurationPage-OUwV8QF1.mjs").then((mod) => ({
1278
1675
  default: mod.ProtectedListConfiguration
1279
1676
  }))
1280
1677
  );
1281
1678
  const ProtectedEditConfigurationPage = lazy(
1282
- () => import("./EditConfigurationPage-ZO0vOO8q.mjs").then((mod) => ({
1679
+ () => import("./EditConfigurationPage-C9yiwgI_.mjs").then((mod) => ({
1283
1680
  default: mod.ProtectedEditConfigurationPage
1284
1681
  }))
1285
1682
  );
1286
1683
  const ProtectedComponentConfigurationPage = lazy(
1287
- () => import("./ComponentConfigurationPage-B1bIXVuX.mjs").then((mod) => ({
1684
+ () => import("./ComponentConfigurationPage-D1YuKq8j.mjs").then((mod) => ({
1288
1685
  default: mod.ProtectedComponentConfigurationPage
1289
1686
  }))
1290
1687
  );
1291
1688
  const NoPermissions = lazy(
1292
- () => import("./NoPermissionsPage-CM5UD8ee.mjs").then((mod) => ({ default: mod.NoPermissions }))
1689
+ () => import("./NoPermissionsPage-CcWbyT_z.mjs").then((mod) => ({ default: mod.NoPermissions }))
1293
1690
  );
1294
1691
  const NoContentType = lazy(
1295
- () => import("./NoContentTypePage-CiIcfYsd.mjs").then((mod) => ({ default: mod.NoContentType }))
1692
+ () => import("./NoContentTypePage-CwjlHGTn.mjs").then((mod) => ({ default: mod.NoContentType }))
1296
1693
  );
1297
1694
  const CollectionTypePages = () => {
1298
1695
  const { collectionType } = useParams();
@@ -1406,12 +1803,14 @@ const DocumentActionButton = (action) => {
1406
1803
  /* @__PURE__ */ jsx(
1407
1804
  Button,
1408
1805
  {
1409
- flex: 1,
1806
+ flex: "auto",
1410
1807
  startIcon: action.icon,
1411
1808
  disabled: action.disabled,
1412
1809
  onClick: handleClick(action),
1413
1810
  justifyContent: "center",
1414
1811
  variant: action.variant || "default",
1812
+ paddingTop: "7px",
1813
+ paddingBottom: "7px",
1415
1814
  children: action.label
1416
1815
  }
1417
1816
  ),
@@ -1476,9 +1875,9 @@ const DocumentActionsMenu = ({
1476
1875
  disabled: isDisabled,
1477
1876
  size: "S",
1478
1877
  endIcon: null,
1479
- paddingTop: "7px",
1480
- paddingLeft: "9px",
1481
- paddingRight: "9px",
1878
+ paddingTop: "4px",
1879
+ paddingLeft: "7px",
1880
+ paddingRight: "7px",
1482
1881
  variant,
1483
1882
  children: [
1484
1883
  /* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
@@ -1489,7 +1888,7 @@ const DocumentActionsMenu = ({
1489
1888
  ]
1490
1889
  }
1491
1890
  ),
1492
- /* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1891
+ /* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
1493
1892
  actions2.map((action) => {
1494
1893
  return /* @__PURE__ */ jsx(
1495
1894
  Menu.Item,
@@ -1498,10 +1897,25 @@ const DocumentActionsMenu = ({
1498
1897
  onSelect: handleClick(action),
1499
1898
  display: "block",
1500
1899
  children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1501
- /* @__PURE__ */ jsxs(Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1502
- /* @__PURE__ */ jsx(Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1503
- action.label
1504
- ] }),
1900
+ /* @__PURE__ */ jsxs(
1901
+ Flex,
1902
+ {
1903
+ color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
1904
+ gap: 2,
1905
+ tag: "span",
1906
+ children: [
1907
+ /* @__PURE__ */ jsx(
1908
+ Flex,
1909
+ {
1910
+ tag: "span",
1911
+ color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
1912
+ children: action.icon
1913
+ }
1914
+ ),
1915
+ action.label
1916
+ ]
1917
+ }
1918
+ ),
1505
1919
  action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
1506
1920
  Flex,
1507
1921
  {
@@ -1598,11 +2012,11 @@ const DocumentActionConfirmDialog = ({
1598
2012
  /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1599
2013
  /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1600
2014
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1601
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
2015
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
1602
2016
  id: "app.components.Button.cancel",
1603
2017
  defaultMessage: "Cancel"
1604
2018
  }) }) }),
1605
- /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
2019
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
1606
2020
  id: "app.components.Button.confirm",
1607
2021
  defaultMessage: "Confirm"
1608
2022
  }) })
@@ -1625,8 +2039,8 @@ const DocumentActionModal = ({
1625
2039
  };
1626
2040
  return /* @__PURE__ */ jsx(Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
1627
2041
  /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: title }) }),
1628
- /* @__PURE__ */ jsx(Modal.Body, { children: typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content }),
1629
- /* @__PURE__ */ jsx(Modal.Footer, { children: typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer })
2042
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsx(Modal.Body, { children: Content }),
2043
+ typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1630
2044
  ] }) });
1631
2045
  };
1632
2046
  const PublishAction$1 = ({
@@ -1641,12 +2055,10 @@ const PublishAction$1 = ({
1641
2055
  const navigate = useNavigate();
1642
2056
  const { toggleNotification } = useNotification();
1643
2057
  const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
2058
+ const isListView = useMatch(LIST_PATH) !== null;
1644
2059
  const isCloning = useMatch(CLONE_PATH) !== null;
1645
2060
  const { formatMessage } = useIntl();
1646
- const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
1647
- "PublishAction",
1648
- ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1649
- );
2061
+ const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
1650
2062
  const { publish } = useDocumentActions();
1651
2063
  const [
1652
2064
  countDraftRelations,
@@ -1698,24 +2110,25 @@ const PublishAction$1 = ({
1698
2110
  }
1699
2111
  }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1700
2112
  React.useEffect(() => {
1701
- if (documentId) {
1702
- const fetchDraftRelationsCount = async () => {
1703
- const { data, error } = await countDraftRelations({
1704
- collectionType,
1705
- model,
1706
- documentId,
1707
- params
1708
- });
1709
- if (error) {
1710
- throw error;
1711
- }
1712
- if (data) {
1713
- setServerCountOfDraftRelations(data.data);
1714
- }
1715
- };
1716
- fetchDraftRelationsCount();
2113
+ if (!document || !document.documentId || isListView) {
2114
+ return;
1717
2115
  }
1718
- }, [documentId, countDraftRelations, collectionType, model, params]);
2116
+ const fetchDraftRelationsCount = async () => {
2117
+ const { data, error } = await countDraftRelations({
2118
+ collectionType,
2119
+ model,
2120
+ documentId,
2121
+ params
2122
+ });
2123
+ if (error) {
2124
+ throw error;
2125
+ }
2126
+ if (data) {
2127
+ setServerCountOfDraftRelations(data.data);
2128
+ }
2129
+ };
2130
+ fetchDraftRelationsCount();
2131
+ }, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
1719
2132
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1720
2133
  if (!schema?.options?.draftAndPublish) {
1721
2134
  return null;
@@ -1723,7 +2136,9 @@ const PublishAction$1 = ({
1723
2136
  const performPublish = async () => {
1724
2137
  setSubmitting(true);
1725
2138
  try {
1726
- const { errors } = await validate();
2139
+ const { errors } = await validate(true, {
2140
+ status: "published"
2141
+ });
1727
2142
  if (errors) {
1728
2143
  toggleNotification({
1729
2144
  type: "danger",
@@ -1756,7 +2171,8 @@ const PublishAction$1 = ({
1756
2171
  }
1757
2172
  };
1758
2173
  const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1759
- const hasDraftRelations = totalDraftRelations > 0;
2174
+ const enableDraftRelationsCount = false;
2175
+ const hasDraftRelations = enableDraftRelationsCount;
1760
2176
  return {
1761
2177
  /**
1762
2178
  * Disabled when:
@@ -1766,18 +2182,13 @@ const PublishAction$1 = ({
1766
2182
  * - the document is already published & not modified
1767
2183
  * - the document is being created & not modified
1768
2184
  * - the user doesn't have the permission to publish
1769
- * - the user doesn't have the permission to create a new document
1770
- * - the user doesn't have the permission to update the document
1771
2185
  */
1772
- disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
2186
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
1773
2187
  label: formatMessage({
1774
2188
  id: "app.utils.publish",
1775
2189
  defaultMessage: "Publish"
1776
2190
  }),
1777
2191
  onClick: async () => {
1778
- if (hasDraftRelations) {
1779
- return;
1780
- }
1781
2192
  await performPublish();
1782
2193
  },
1783
2194
  dialog: hasDraftRelations ? {
@@ -1816,10 +2227,6 @@ const UpdateAction = ({
1816
2227
  const cloneMatch = useMatch(CLONE_PATH);
1817
2228
  const isCloning = cloneMatch !== null;
1818
2229
  const { formatMessage } = useIntl();
1819
- const { canCreate, canUpdate } = useDocumentRBAC("UpdateAction", ({ canCreate: canCreate2, canUpdate: canUpdate2 }) => ({
1820
- canCreate: canCreate2,
1821
- canUpdate: canUpdate2
1822
- }));
1823
2230
  const { create, update, clone } = useDocumentActions();
1824
2231
  const [{ query, rawQuery }] = useQueryParams();
1825
2232
  const params = React.useMemo(() => buildValidParams(query), [query]);
@@ -1836,10 +2243,8 @@ const UpdateAction = ({
1836
2243
  * - the form is submitting
1837
2244
  * - the document is not modified & we're not cloning (you can save a clone entity straight away)
1838
2245
  * - the active tab is the published tab
1839
- * - the user doesn't have the permission to create a new document
1840
- * - the user doesn't have the permission to update the document
1841
2246
  */
1842
- disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
2247
+ disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
1843
2248
  label: formatMessage({
1844
2249
  id: "content-manager.containers.Edit.save",
1845
2250
  defaultMessage: "Save"
@@ -1847,7 +2252,9 @@ const UpdateAction = ({
1847
2252
  onClick: async () => {
1848
2253
  setSubmitting(true);
1849
2254
  try {
1850
- const { errors } = await validate();
2255
+ const { errors } = await validate(true, {
2256
+ status: "draft"
2257
+ });
1851
2258
  if (errors) {
1852
2259
  toggleNotification({
1853
2260
  type: "danger",
@@ -1868,10 +2275,13 @@ const UpdateAction = ({
1868
2275
  document
1869
2276
  );
1870
2277
  if ("data" in res) {
1871
- navigate({
1872
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1873
- search: rawQuery
1874
- });
2278
+ navigate(
2279
+ {
2280
+ pathname: `../${res.data.documentId}`,
2281
+ search: rawQuery
2282
+ },
2283
+ { relative: "path" }
2284
+ );
1875
2285
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1876
2286
  setErrors(formatValidationErrors(res.error));
1877
2287
  }
@@ -1901,10 +2311,10 @@ const UpdateAction = ({
1901
2311
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1902
2312
  navigate(
1903
2313
  {
1904
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
2314
+ pathname: `../${res.data.documentId}`,
1905
2315
  search: rawQuery
1906
2316
  },
1907
- { replace: true }
2317
+ { replace: true, relative: "path" }
1908
2318
  );
1909
2319
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1910
2320
  setErrors(formatValidationErrors(res.error));
@@ -1949,7 +2359,7 @@ const UnpublishAction$1 = ({
1949
2359
  id: "app.utils.unpublish",
1950
2360
  defaultMessage: "Unpublish"
1951
2361
  }),
1952
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2362
+ icon: /* @__PURE__ */ jsx(Cross, {}),
1953
2363
  onClick: async () => {
1954
2364
  if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
1955
2365
  if (!documentId) {
@@ -2061,7 +2471,7 @@ const DiscardAction = ({
2061
2471
  id: "content-manager.actions.discard.label",
2062
2472
  defaultMessage: "Discard changes"
2063
2473
  }),
2064
- icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
2474
+ icon: /* @__PURE__ */ jsx(Cross, {}),
2065
2475
  position: ["panel", "table-row"],
2066
2476
  variant: "danger",
2067
2477
  dialog: {
@@ -2089,11 +2499,6 @@ const DiscardAction = ({
2089
2499
  };
2090
2500
  };
2091
2501
  DiscardAction.type = "discard";
2092
- const StyledCrossCircle = styled(CrossCircle)`
2093
- path {
2094
- fill: currentColor;
2095
- }
2096
- `;
2097
2502
  const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2098
2503
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2099
2504
  const RelativeTime = React.forwardRef(
@@ -2141,7 +2546,7 @@ const getDisplayName = ({
2141
2546
  };
2142
2547
  const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2143
2548
  const DocumentStatus = ({ status = "draft", ...restProps }) => {
2144
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
2549
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
2145
2550
  return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
2146
2551
  };
2147
2552
  const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
@@ -2240,12 +2645,12 @@ const Information = ({ activeTab }) => {
2240
2645
  isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
2241
2646
  label: formatMessage({
2242
2647
  id: "content-manager.containers.edit.information.last-published.label",
2243
- defaultMessage: "Last published"
2648
+ defaultMessage: "Published"
2244
2649
  }),
2245
2650
  value: formatMessage(
2246
2651
  {
2247
2652
  id: "content-manager.containers.edit.information.last-published.value",
2248
- defaultMessage: `Published {time}{isAnonymous, select, true {} other { by {author}}}`
2653
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2249
2654
  },
2250
2655
  {
2251
2656
  time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
@@ -2258,12 +2663,12 @@ const Information = ({ activeTab }) => {
2258
2663
  isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
2259
2664
  label: formatMessage({
2260
2665
  id: "content-manager.containers.edit.information.last-draft.label",
2261
- defaultMessage: "Last draft"
2666
+ defaultMessage: "Updated"
2262
2667
  }),
2263
2668
  value: formatMessage(
2264
2669
  {
2265
2670
  id: "content-manager.containers.edit.information.last-draft.value",
2266
- defaultMessage: `Modified {time}{isAnonymous, select, true {} other { by {author}}}`
2671
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2267
2672
  },
2268
2673
  {
2269
2674
  time: /* @__PURE__ */ jsx(
@@ -2281,12 +2686,12 @@ const Information = ({ activeTab }) => {
2281
2686
  isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
2282
2687
  label: formatMessage({
2283
2688
  id: "content-manager.containers.edit.information.document.label",
2284
- defaultMessage: "Document"
2689
+ defaultMessage: "Created"
2285
2690
  }),
2286
2691
  value: formatMessage(
2287
2692
  {
2288
2693
  id: "content-manager.containers.edit.information.document.value",
2289
- defaultMessage: `Created {time}{isAnonymous, select, true {} other { by {author}}}`
2694
+ defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
2290
2695
  },
2291
2696
  {
2292
2697
  time: /* @__PURE__ */ jsx(
@@ -2324,25 +2729,77 @@ const Information = ({ activeTab }) => {
2324
2729
  );
2325
2730
  };
2326
2731
  const HeaderActions = ({ actions: actions2 }) => {
2327
- return /* @__PURE__ */ jsx(Flex, { children: actions2.map((action) => {
2328
- if ("options" in action) {
2732
+ const [dialogId, setDialogId] = React.useState(null);
2733
+ const handleClick = (action) => async (e) => {
2734
+ if (!("options" in action)) {
2735
+ const { onClick = () => false, dialog, id } = action;
2736
+ const muteDialog = await onClick(e);
2737
+ if (dialog && !muteDialog) {
2738
+ e.preventDefault();
2739
+ setDialogId(id);
2740
+ }
2741
+ }
2742
+ };
2743
+ const handleClose = () => {
2744
+ setDialogId(null);
2745
+ };
2746
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
2747
+ if (action.options) {
2329
2748
  return /* @__PURE__ */ jsx(
2330
2749
  SingleSelect,
2331
2750
  {
2332
2751
  size: "S",
2333
- disabled: action.disabled,
2334
- "aria-label": action.label,
2335
2752
  onChange: action.onSelect,
2336
- value: action.value,
2753
+ "aria-label": action.label,
2754
+ ...action,
2337
2755
  children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
2338
2756
  },
2339
2757
  action.id
2340
2758
  );
2341
2759
  } else {
2342
- return null;
2760
+ if (action.type === "icon") {
2761
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
2762
+ /* @__PURE__ */ jsx(
2763
+ IconButton,
2764
+ {
2765
+ disabled: action.disabled,
2766
+ label: action.label,
2767
+ size: "S",
2768
+ onClick: handleClick(action),
2769
+ children: action.icon
2770
+ }
2771
+ ),
2772
+ action.dialog ? /* @__PURE__ */ jsx(
2773
+ HeaderActionDialog,
2774
+ {
2775
+ ...action.dialog,
2776
+ isOpen: dialogId === action.id,
2777
+ onClose: handleClose
2778
+ }
2779
+ ) : null
2780
+ ] }, action.id);
2781
+ }
2343
2782
  }
2344
2783
  }) });
2345
2784
  };
2785
+ const HeaderActionDialog = ({
2786
+ onClose,
2787
+ onCancel,
2788
+ title,
2789
+ content: Content,
2790
+ isOpen
2791
+ }) => {
2792
+ const handleClose = async () => {
2793
+ if (onCancel) {
2794
+ await onCancel();
2795
+ }
2796
+ onClose();
2797
+ };
2798
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2799
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
2800
+ typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
2801
+ ] }) });
2802
+ };
2346
2803
  const ConfigureTheViewAction = ({ collectionType, model }) => {
2347
2804
  const navigate = useNavigate();
2348
2805
  const { formatMessage } = useIntl();
@@ -2383,12 +2840,16 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
2383
2840
  const { delete: deleteAction } = useDocumentActions();
2384
2841
  const { toggleNotification } = useNotification();
2385
2842
  const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
2843
+ const isLocalized = document?.locale != null;
2386
2844
  return {
2387
2845
  disabled: !canDelete || !document,
2388
- label: formatMessage({
2389
- id: "content-manager.actions.delete.label",
2390
- defaultMessage: "Delete document"
2391
- }),
2846
+ label: formatMessage(
2847
+ {
2848
+ id: "content-manager.actions.delete.label",
2849
+ defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
2850
+ },
2851
+ { isLocalized }
2852
+ ),
2392
2853
  icon: /* @__PURE__ */ jsx(Trash, {}),
2393
2854
  dialog: {
2394
2855
  type: "dialog",
@@ -2478,7 +2939,7 @@ const ActionsPanel = () => {
2478
2939
  return {
2479
2940
  title: formatMessage({
2480
2941
  id: "content-manager.containers.edit.panels.default.title",
2481
- defaultMessage: "Document"
2942
+ defaultMessage: "Entry"
2482
2943
  }),
2483
2944
  content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
2484
2945
  };
@@ -2539,308 +3000,6 @@ const Panel = React.forwardRef(({ children, title }, ref) => {
2539
3000
  }
2540
3001
  );
2541
3002
  });
2542
- const HOOKS = {
2543
- /**
2544
- * Hook that allows to mutate the displayed headers of the list view table
2545
- * @constant
2546
- * @type {string}
2547
- */
2548
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2549
- /**
2550
- * Hook that allows to mutate the CM's collection types links pre-set filters
2551
- * @constant
2552
- * @type {string}
2553
- */
2554
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2555
- /**
2556
- * Hook that allows to mutate the CM's edit view layout
2557
- * @constant
2558
- * @type {string}
2559
- */
2560
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2561
- /**
2562
- * Hook that allows to mutate the CM's single types links pre-set filters
2563
- * @constant
2564
- * @type {string}
2565
- */
2566
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2567
- };
2568
- const contentTypesApi = contentManagerApi.injectEndpoints({
2569
- endpoints: (builder) => ({
2570
- getContentTypeConfiguration: builder.query({
2571
- query: (uid) => ({
2572
- url: `/content-manager/content-types/${uid}/configuration`,
2573
- method: "GET"
2574
- }),
2575
- transformResponse: (response) => response.data,
2576
- providesTags: (_result, _error, uid) => [
2577
- { type: "ContentTypesConfiguration", id: uid },
2578
- { type: "ContentTypeSettings", id: "LIST" }
2579
- ]
2580
- }),
2581
- getAllContentTypeSettings: builder.query({
2582
- query: () => "/content-manager/content-types-settings",
2583
- transformResponse: (response) => response.data,
2584
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2585
- }),
2586
- updateContentTypeConfiguration: builder.mutation({
2587
- query: ({ uid, ...body }) => ({
2588
- url: `/content-manager/content-types/${uid}/configuration`,
2589
- method: "PUT",
2590
- data: body
2591
- }),
2592
- transformResponse: (response) => response.data,
2593
- invalidatesTags: (_result, _error, { uid }) => [
2594
- { type: "ContentTypesConfiguration", id: uid },
2595
- { type: "ContentTypeSettings", id: "LIST" },
2596
- // Is this necessary?
2597
- { type: "InitialData" }
2598
- ]
2599
- })
2600
- })
2601
- });
2602
- const {
2603
- useGetContentTypeConfigurationQuery,
2604
- useGetAllContentTypeSettingsQuery,
2605
- useUpdateContentTypeConfigurationMutation
2606
- } = contentTypesApi;
2607
- const checkIfAttributeIsDisplayable = (attribute) => {
2608
- const { type } = attribute;
2609
- if (type === "relation") {
2610
- return !attribute.relation.toLowerCase().includes("morph");
2611
- }
2612
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2613
- };
2614
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2615
- if (!mainFieldName) {
2616
- return void 0;
2617
- }
2618
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2619
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2620
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2621
- );
2622
- return {
2623
- name: mainFieldName,
2624
- type: mainFieldType ?? "string"
2625
- };
2626
- };
2627
- const DEFAULT_SETTINGS = {
2628
- bulkable: false,
2629
- filterable: false,
2630
- searchable: false,
2631
- pagination: false,
2632
- defaultSortBy: "",
2633
- defaultSortOrder: "asc",
2634
- mainField: "id",
2635
- pageSize: 10
2636
- };
2637
- const useDocumentLayout = (model) => {
2638
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2639
- const [{ query }] = useQueryParams();
2640
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2641
- const { toggleNotification } = useNotification();
2642
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2643
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2644
- const {
2645
- data,
2646
- isLoading: isLoadingConfigs,
2647
- error,
2648
- isFetching: isFetchingConfigs
2649
- } = useGetContentTypeConfigurationQuery(model);
2650
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2651
- React.useEffect(() => {
2652
- if (error) {
2653
- toggleNotification({
2654
- type: "danger",
2655
- message: formatAPIError(error)
2656
- });
2657
- }
2658
- }, [error, formatAPIError, toggleNotification]);
2659
- const editLayout = React.useMemo(
2660
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2661
- layout: [],
2662
- components: {},
2663
- metadatas: {},
2664
- options: {},
2665
- settings: DEFAULT_SETTINGS
2666
- },
2667
- [data, isLoading, schemas, schema, components]
2668
- );
2669
- const listLayout = React.useMemo(() => {
2670
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2671
- layout: [],
2672
- metadatas: {},
2673
- options: {},
2674
- settings: DEFAULT_SETTINGS
2675
- };
2676
- }, [data, isLoading, schemas, schema, components]);
2677
- const { layout: edit } = React.useMemo(
2678
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2679
- layout: editLayout,
2680
- query
2681
- }),
2682
- [editLayout, query, runHookWaterfall]
2683
- );
2684
- return {
2685
- error,
2686
- isLoading,
2687
- edit,
2688
- list: listLayout
2689
- };
2690
- };
2691
- const useDocLayout = () => {
2692
- const { model } = useDoc();
2693
- return useDocumentLayout(model);
2694
- };
2695
- const formatEditLayout = (data, {
2696
- schemas,
2697
- schema,
2698
- components
2699
- }) => {
2700
- let currentPanelIndex = 0;
2701
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2702
- data.contentType.layouts.edit,
2703
- schema?.attributes,
2704
- data.contentType.metadatas,
2705
- { configurations: data.components, schemas: components },
2706
- schemas
2707
- ).reduce((panels, row) => {
2708
- if (row.some((field) => field.type === "dynamiczone")) {
2709
- panels.push([row]);
2710
- currentPanelIndex += 2;
2711
- } else {
2712
- if (!panels[currentPanelIndex]) {
2713
- panels.push([]);
2714
- }
2715
- panels[currentPanelIndex].push(row);
2716
- }
2717
- return panels;
2718
- }, []);
2719
- const componentEditAttributes = Object.entries(data.components).reduce(
2720
- (acc, [uid, configuration]) => {
2721
- acc[uid] = {
2722
- layout: convertEditLayoutToFieldLayouts(
2723
- configuration.layouts.edit,
2724
- components[uid].attributes,
2725
- configuration.metadatas
2726
- ),
2727
- settings: {
2728
- ...configuration.settings,
2729
- icon: components[uid].info.icon,
2730
- displayName: components[uid].info.displayName
2731
- }
2732
- };
2733
- return acc;
2734
- },
2735
- {}
2736
- );
2737
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2738
- (acc, [attribute, metadata]) => {
2739
- return {
2740
- ...acc,
2741
- [attribute]: metadata.edit
2742
- };
2743
- },
2744
- {}
2745
- );
2746
- return {
2747
- layout: panelledEditAttributes,
2748
- components: componentEditAttributes,
2749
- metadatas: editMetadatas,
2750
- settings: {
2751
- ...data.contentType.settings,
2752
- displayName: schema?.info.displayName
2753
- },
2754
- options: {
2755
- ...schema?.options,
2756
- ...schema?.pluginOptions,
2757
- ...data.contentType.options
2758
- }
2759
- };
2760
- };
2761
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2762
- return rows.map(
2763
- (row) => row.map((field) => {
2764
- const attribute = attributes[field.name];
2765
- if (!attribute) {
2766
- return null;
2767
- }
2768
- const { edit: metadata } = metadatas[field.name];
2769
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2770
- return {
2771
- attribute,
2772
- disabled: !metadata.editable,
2773
- hint: metadata.description,
2774
- label: metadata.label ?? "",
2775
- name: field.name,
2776
- // @ts-expect-error – mainField does exist on the metadata for a relation.
2777
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2778
- schemas,
2779
- components: components?.schemas ?? {}
2780
- }),
2781
- placeholder: metadata.placeholder ?? "",
2782
- required: attribute.required ?? false,
2783
- size: field.size,
2784
- unique: "unique" in attribute ? attribute.unique : false,
2785
- visible: metadata.visible ?? true,
2786
- type: attribute.type
2787
- };
2788
- }).filter((field) => field !== null)
2789
- );
2790
- };
2791
- const formatListLayout = (data, {
2792
- schemas,
2793
- schema,
2794
- components
2795
- }) => {
2796
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2797
- (acc, [attribute, metadata]) => {
2798
- return {
2799
- ...acc,
2800
- [attribute]: metadata.list
2801
- };
2802
- },
2803
- {}
2804
- );
2805
- const listAttributes = convertListLayoutToFieldLayouts(
2806
- data.contentType.layouts.list,
2807
- schema?.attributes,
2808
- listMetadatas,
2809
- { configurations: data.components, schemas: components },
2810
- schemas
2811
- );
2812
- return {
2813
- layout: listAttributes,
2814
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2815
- metadatas: listMetadatas,
2816
- options: {
2817
- ...schema?.options,
2818
- ...schema?.pluginOptions,
2819
- ...data.contentType.options
2820
- }
2821
- };
2822
- };
2823
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2824
- return columns.map((name) => {
2825
- const attribute = attributes[name];
2826
- if (!attribute) {
2827
- return null;
2828
- }
2829
- const metadata = metadatas[name];
2830
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2831
- return {
2832
- attribute,
2833
- label: metadata.label ?? "",
2834
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2835
- schemas,
2836
- components: components?.schemas ?? {}
2837
- }),
2838
- name,
2839
- searchable: metadata.searchable ?? true,
2840
- sortable: metadata.sortable ?? true
2841
- };
2842
- }).filter((field) => field !== null);
2843
- };
2844
3003
  const ConfirmBulkActionDialog = ({
2845
3004
  onToggleDialog,
2846
3005
  isOpen = false,
@@ -2879,6 +3038,7 @@ const ConfirmDialogPublishAll = ({
2879
3038
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2880
3039
  const { model, schema } = useDoc();
2881
3040
  const [{ query }] = useQueryParams();
3041
+ const enableDraftRelationsCount = false;
2882
3042
  const {
2883
3043
  data: countDraftRelations = 0,
2884
3044
  isLoading,
@@ -2890,7 +3050,7 @@ const ConfirmDialogPublishAll = ({
2890
3050
  locale: query?.plugins?.i18n?.locale
2891
3051
  },
2892
3052
  {
2893
- skip: selectedEntries.length === 0
3053
+ skip: !enableDraftRelationsCount
2894
3054
  }
2895
3055
  );
2896
3056
  React.useEffect(() => {
@@ -3075,7 +3235,7 @@ const SelectedEntriesTableContent = ({
3075
3235
  status: row.status
3076
3236
  }
3077
3237
  ) }),
3078
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3238
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
3079
3239
  IconButton,
3080
3240
  {
3081
3241
  tag: Link,
@@ -3098,9 +3258,10 @@ const SelectedEntriesTableContent = ({
3098
3258
  ),
3099
3259
  target: "_blank",
3100
3260
  marginLeft: "auto",
3101
- children: /* @__PURE__ */ jsx(Pencil, {})
3261
+ variant: "ghost",
3262
+ children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
3102
3263
  }
3103
- ) })
3264
+ ) }) })
3104
3265
  ] }, row.id)) })
3105
3266
  ] });
3106
3267
  };
@@ -3137,7 +3298,13 @@ const SelectedEntriesModalContent = ({
3137
3298
  );
3138
3299
  const { rows, validationErrors } = React.useMemo(() => {
3139
3300
  if (data.length > 0 && schema) {
3140
- const validate = createYupSchema(schema.attributes, components);
3301
+ const validate = createYupSchema(
3302
+ schema.attributes,
3303
+ components,
3304
+ // Since this is the "Publish" action, the validation
3305
+ // schema must enforce the rules for published entities
3306
+ { status: "published" }
3307
+ );
3141
3308
  const validationErrors2 = {};
3142
3309
  const rows2 = data.map((entry) => {
3143
3310
  try {
@@ -3487,7 +3654,7 @@ const TableActions = ({ document }) => {
3487
3654
  DescriptionComponentRenderer,
3488
3655
  {
3489
3656
  props,
3490
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
3657
+ descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
3491
3658
  children: (actions2) => {
3492
3659
  const tableRowActions = actions2.filter((action) => {
3493
3660
  const positions = Array.isArray(action.position) ? action.position : [action.position];
@@ -3598,7 +3765,7 @@ const CloneAction = ({ model, documentId }) => {
3598
3765
  }),
3599
3766
  content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3600
3767
  footer: ({ onClose }) => {
3601
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3768
+ return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3602
3769
  /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3603
3770
  id: "cancel",
3604
3771
  defaultMessage: "Cancel"
@@ -3829,7 +3996,7 @@ const index = {
3829
3996
  app.router.addRoute({
3830
3997
  path: "content-manager/*",
3831
3998
  lazy: async () => {
3832
- const { Layout } = await import("./layout-DPaHUusj.mjs");
3999
+ const { Layout } = await import("./layout-N63eyE5E.mjs");
3833
4000
  return {
3834
4001
  Component: Layout
3835
4002
  };
@@ -3846,7 +4013,7 @@ const index = {
3846
4013
  async registerTrads({ locales }) {
3847
4014
  const importedTrads = await Promise.all(
3848
4015
  locales.map((locale) => {
3849
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-BrCTWlZv.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
4016
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-DKV44jRb.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
3850
4017
  return {
3851
4018
  data: prefixPluginTranslations(data, PLUGIN_ID),
3852
4019
  locale
@@ -3867,13 +4034,15 @@ export {
3867
4034
  BulkActionsRenderer as B,
3868
4035
  COLLECTION_TYPES as C,
3869
4036
  DocumentStatus as D,
3870
- DEFAULT_SETTINGS as E,
3871
- convertEditLayoutToFieldLayouts as F,
3872
- useDocument as G,
4037
+ extractContentTypeComponents as E,
4038
+ DEFAULT_SETTINGS as F,
4039
+ convertEditLayoutToFieldLayouts as G,
3873
4040
  HOOKS as H,
3874
4041
  InjectionZone as I,
3875
- index as J,
3876
- useDocumentActions as K,
4042
+ useDocument as J,
4043
+ index as K,
4044
+ useContentManagerContext as L,
4045
+ useDocumentActions as M,
3877
4046
  Panels as P,
3878
4047
  RelativeTime as R,
3879
4048
  SINGLE_TYPES as S,
@@ -3891,18 +4060,18 @@ export {
3891
4060
  PERMISSIONS as k,
3892
4061
  DocumentRBAC as l,
3893
4062
  DOCUMENT_META_FIELDS as m,
3894
- useDocLayout as n,
3895
- useGetContentTypeConfigurationQuery as o,
3896
- CREATOR_FIELDS as p,
3897
- getMainField as q,
3898
- getDisplayName as r,
4063
+ CLONE_PATH as n,
4064
+ useDocLayout as o,
4065
+ useGetContentTypeConfigurationQuery as p,
4066
+ CREATOR_FIELDS as q,
4067
+ getMainField as r,
3899
4068
  setInitialData as s,
3900
- checkIfAttributeIsDisplayable as t,
4069
+ getDisplayName as t,
3901
4070
  useContentTypeSchema as u,
3902
- useGetAllDocumentsQuery as v,
3903
- convertListLayoutToFieldLayouts as w,
3904
- capitalise as x,
3905
- useUpdateContentTypeConfigurationMutation as y,
3906
- extractContentTypeComponents as z
4071
+ checkIfAttributeIsDisplayable as v,
4072
+ useGetAllDocumentsQuery as w,
4073
+ convertListLayoutToFieldLayouts as x,
4074
+ capitalise as y,
4075
+ useUpdateContentTypeConfigurationMutation as z
3907
4076
  };
3908
- //# sourceMappingURL=index-BSn97i8U.mjs.map
4077
+ //# sourceMappingURL=index-BQ8DxaCa.mjs.map