@strapi/content-manager 5.0.0-beta.8 → 5.0.0-rc.0

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 (100) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-by0e_kNd.js → ComponentConfigurationPage-DVY3LwHo.js} +3 -3
  3. package/dist/_chunks/{ComponentConfigurationPage-by0e_kNd.js.map → ComponentConfigurationPage-DVY3LwHo.js.map} +1 -1
  4. package/dist/_chunks/{ComponentConfigurationPage-CuWgXugY.mjs → ComponentConfigurationPage-Dy9BQQ2V.mjs} +3 -3
  5. package/dist/_chunks/{ComponentConfigurationPage-CuWgXugY.mjs.map → ComponentConfigurationPage-Dy9BQQ2V.mjs.map} +1 -1
  6. package/dist/_chunks/{EditConfigurationPage-DbI4KMyz.mjs → EditConfigurationPage-DbR8mWH5.mjs} +3 -3
  7. package/dist/_chunks/{EditConfigurationPage-DbI4KMyz.mjs.map → EditConfigurationPage-DbR8mWH5.mjs.map} +1 -1
  8. package/dist/_chunks/{EditConfigurationPage-CqBeCPGH.js → EditConfigurationPage-b6V7YHfo.js} +3 -3
  9. package/dist/_chunks/{EditConfigurationPage-CqBeCPGH.js.map → EditConfigurationPage-b6V7YHfo.js.map} +1 -1
  10. package/dist/_chunks/{EditViewPage-dFPBya9U.mjs → EditViewPage-CPrukwtO.mjs} +46 -46
  11. package/dist/_chunks/EditViewPage-CPrukwtO.mjs.map +1 -0
  12. package/dist/_chunks/{EditViewPage-ChgloMyO.js → EditViewPage-DhttErmY.js} +45 -45
  13. package/dist/_chunks/EditViewPage-DhttErmY.js.map +1 -0
  14. package/dist/_chunks/{Field-dLk-vgLL.js → Field-CBRV4uan.js} +416 -136
  15. package/dist/_chunks/Field-CBRV4uan.js.map +1 -0
  16. package/dist/_chunks/{Field-C1nUKcdS.mjs → Field-DrgIgQPw.mjs} +417 -137
  17. package/dist/_chunks/Field-DrgIgQPw.mjs.map +1 -0
  18. package/dist/_chunks/{Form-CbXtmHC_.js → Form-Bk3v7Frl.js} +25 -26
  19. package/dist/_chunks/Form-Bk3v7Frl.js.map +1 -0
  20. package/dist/_chunks/{Form-DOlpi7Js.mjs → Form-Dxk4txLJ.mjs} +27 -28
  21. package/dist/_chunks/Form-Dxk4txLJ.mjs.map +1 -0
  22. package/dist/_chunks/{History-BFNUAiGc.mjs → History-DSU-y4Hg.mjs} +126 -31
  23. package/dist/_chunks/History-DSU-y4Hg.mjs.map +1 -0
  24. package/dist/_chunks/{History-BjDfohBr.js → History-xNH_9UuV.js} +125 -30
  25. package/dist/_chunks/History-xNH_9UuV.js.map +1 -0
  26. package/dist/_chunks/{ListConfigurationPage-IQBgWTaa.js → ListConfigurationPage-BCPzXk5W.js} +48 -47
  27. package/dist/_chunks/ListConfigurationPage-BCPzXk5W.js.map +1 -0
  28. package/dist/_chunks/{ListConfigurationPage-DDi0KqFm.mjs → ListConfigurationPage-BWwZ-uMJ.mjs} +49 -49
  29. package/dist/_chunks/ListConfigurationPage-BWwZ-uMJ.mjs.map +1 -0
  30. package/dist/_chunks/{ListViewPage-BPjljUsH.mjs → ListViewPage-D9UmehuA.mjs} +74 -70
  31. package/dist/_chunks/ListViewPage-D9UmehuA.mjs.map +1 -0
  32. package/dist/_chunks/{ListViewPage-CZYGqlvF.js → ListViewPage-ZIvstfvl.js} +72 -68
  33. package/dist/_chunks/ListViewPage-ZIvstfvl.js.map +1 -0
  34. package/dist/_chunks/{NoContentTypePage-DaWw67K-.mjs → NoContentTypePage-CstnyWv2.mjs} +2 -2
  35. package/dist/_chunks/{NoContentTypePage-DaWw67K-.mjs.map → NoContentTypePage-CstnyWv2.mjs.map} +1 -1
  36. package/dist/_chunks/{NoContentTypePage-BOAI6VZ1.js → NoContentTypePage-h7FcuMjI.js} +2 -2
  37. package/dist/_chunks/{NoContentTypePage-BOAI6VZ1.js.map → NoContentTypePage-h7FcuMjI.js.map} +1 -1
  38. package/dist/_chunks/{NoPermissionsPage-cYEtLc_e.js → NoPermissionsPage-DofU68cO.js} +2 -2
  39. package/dist/_chunks/{NoPermissionsPage-cYEtLc_e.js.map → NoPermissionsPage-DofU68cO.js.map} +1 -1
  40. package/dist/_chunks/{NoPermissionsPage-CZrJH00p.mjs → NoPermissionsPage-aFCCLbsf.mjs} +2 -2
  41. package/dist/_chunks/{NoPermissionsPage-CZrJH00p.mjs.map → NoPermissionsPage-aFCCLbsf.mjs.map} +1 -1
  42. package/dist/_chunks/{Relations-DTowyge2.mjs → Relations-7v66IP7b.mjs} +4 -4
  43. package/dist/_chunks/Relations-7v66IP7b.mjs.map +1 -0
  44. package/dist/_chunks/{Relations-DU6B7irU.js → Relations-DAS_DKG5.js} +4 -4
  45. package/dist/_chunks/Relations-DAS_DKG5.js.map +1 -0
  46. package/dist/_chunks/{en-GCOTL6jR.mjs → en-BrCTWlZv.mjs} +9 -4
  47. package/dist/_chunks/{en-GCOTL6jR.mjs.map → en-BrCTWlZv.mjs.map} +1 -1
  48. package/dist/_chunks/{en-DTULi5-d.js → en-uOUIxfcQ.js} +9 -4
  49. package/dist/_chunks/{en-DTULi5-d.js.map → en-uOUIxfcQ.js.map} +1 -1
  50. package/dist/_chunks/{index-CCJeB7Rw.js → index-CAlLHIrI.js} +1198 -784
  51. package/dist/_chunks/index-CAlLHIrI.js.map +1 -0
  52. package/dist/_chunks/{index-BaGHmIir.mjs → index-DIQ7Io-l.mjs} +1229 -815
  53. package/dist/_chunks/index-DIQ7Io-l.mjs.map +1 -0
  54. package/dist/_chunks/{layout-ni_L9kT1.js → layout-B1ZS-usI.js} +21 -16
  55. package/dist/_chunks/layout-B1ZS-usI.js.map +1 -0
  56. package/dist/_chunks/{layout-BinjszSQ.mjs → layout-DHe2GdT4.mjs} +22 -17
  57. package/dist/_chunks/layout-DHe2GdT4.mjs.map +1 -0
  58. package/dist/_chunks/{relations-c91ji5eR.mjs → relations-BUieBWhT.mjs} +2 -2
  59. package/dist/_chunks/{relations-c91ji5eR.mjs.map → relations-BUieBWhT.mjs.map} +1 -1
  60. package/dist/_chunks/{relations-CeJAJc5I.js → relations-yXHkSG1Z.js} +2 -2
  61. package/dist/_chunks/{relations-CeJAJc5I.js.map → relations-yXHkSG1Z.js.map} +1 -1
  62. package/dist/admin/index.js +1 -1
  63. package/dist/admin/index.mjs +8 -8
  64. package/dist/admin/src/history/components/VersionInputRenderer.d.ts +1 -1
  65. package/dist/admin/src/hooks/useDocumentActions.d.ts +1 -1
  66. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +8 -3
  67. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +4 -0
  68. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  69. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +2 -10
  70. package/dist/admin/src/pages/ListView/components/BulkActions/Actions.d.ts +3 -30
  71. package/dist/admin/src/pages/ListView/components/BulkActions/ConfirmBulkActionDialog.d.ts +2 -2
  72. package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +14 -0
  73. package/dist/server/index.js +26 -17
  74. package/dist/server/index.js.map +1 -1
  75. package/dist/server/index.mjs +26 -17
  76. package/dist/server/index.mjs.map +1 -1
  77. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  78. package/dist/server/src/controllers/single-types.d.ts.map +1 -1
  79. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  80. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  81. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  82. package/package.json +8 -8
  83. package/dist/_chunks/EditViewPage-ChgloMyO.js.map +0 -1
  84. package/dist/_chunks/EditViewPage-dFPBya9U.mjs.map +0 -1
  85. package/dist/_chunks/Field-C1nUKcdS.mjs.map +0 -1
  86. package/dist/_chunks/Field-dLk-vgLL.js.map +0 -1
  87. package/dist/_chunks/Form-CbXtmHC_.js.map +0 -1
  88. package/dist/_chunks/Form-DOlpi7Js.mjs.map +0 -1
  89. package/dist/_chunks/History-BFNUAiGc.mjs.map +0 -1
  90. package/dist/_chunks/History-BjDfohBr.js.map +0 -1
  91. package/dist/_chunks/ListConfigurationPage-DDi0KqFm.mjs.map +0 -1
  92. package/dist/_chunks/ListConfigurationPage-IQBgWTaa.js.map +0 -1
  93. package/dist/_chunks/ListViewPage-BPjljUsH.mjs.map +0 -1
  94. package/dist/_chunks/ListViewPage-CZYGqlvF.js.map +0 -1
  95. package/dist/_chunks/Relations-DTowyge2.mjs.map +0 -1
  96. package/dist/_chunks/Relations-DU6B7irU.js.map +0 -1
  97. package/dist/_chunks/index-BaGHmIir.mjs.map +0 -1
  98. package/dist/_chunks/index-CCJeB7Rw.js.map +0 -1
  99. package/dist/_chunks/layout-BinjszSQ.mjs.map +0 -1
  100. package/dist/_chunks/layout-ni_L9kT1.js.map +0 -1
@@ -1,12 +1,12 @@
1
- import { ClockCounterClockwise, CrossCircle, More, WarningCircle, ListPlus, Pencil, Trash, Check, ChevronRight, Duplicate, Feather } from "@strapi/icons";
1
+ import { ClockCounterClockwise, CrossCircle, More, WarningCircle, ListPlus, Pencil, Trash, Check, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, Feather } from "@strapi/icons";
2
2
  import { jsx, Fragment, jsxs } from "react/jsx-runtime";
3
- import { useStrapiApp, useQueryParams, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useTracking, useForm, BackButton, DescriptionComponentRenderer, useTable } from "@strapi/admin/strapi-admin";
3
+ import { useStrapiApp, useQueryParams, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useTracking, useForm, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
4
4
  import { stringify } from "qs";
5
5
  import { useIntl } from "react-intl";
6
- import { useNavigate, useParams, Navigate, useMatch, NavLink } from "react-router-dom";
6
+ import { useNavigate, useParams, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
7
7
  import * as React from "react";
8
8
  import { lazy } from "react";
9
- import { Menu, VisuallyHidden, Flex, Typography, Dialog, DialogBody, DialogFooter, Button, ModalLayout, ModalHeader, ModalBody, Box, Radio, Status, SingleSelect, SingleSelectOption, LinkButton } from "@strapi/design-system";
9
+ import { Button, Menu, VisuallyHidden, Flex, Box, Typography, Dialog, Modal, Radio, Status, SingleSelect, SingleSelectOption, Loader, IconButton, Tooltip, LinkButton } from "@strapi/design-system";
10
10
  import { styled } from "styled-components";
11
11
  import * as yup from "yup";
12
12
  import { ValidationError } from "yup";
@@ -198,6 +198,7 @@ const contentManagerApi = adminApi.enhanceEndpoints({
198
198
  ]
199
199
  });
200
200
  const documentApi = contentManagerApi.injectEndpoints({
201
+ overrideExisting: true,
201
202
  endpoints: (builder) => ({
202
203
  autoCloneDocument: builder.mutation({
203
204
  query: ({ model, sourceId, query }) => ({
@@ -599,11 +600,11 @@ const createAttributeSchema = (attribute) => {
599
600
  }
600
601
  };
601
602
  const addRequiredValidation = (attribute) => (schema) => {
602
- if (attribute.required) {
603
- return schema.required({
604
- id: translatedErrors.required.id,
605
- defaultMessage: "This field is required."
606
- });
603
+ if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
604
+ return schema.min(1, translatedErrors.required);
605
+ }
606
+ if (attribute.required && attribute.type !== "relation") {
607
+ return schema.required(translatedErrors.required);
607
608
  }
608
609
  return schema?.nullable ? schema.nullable() : (
609
610
  // In some cases '.nullable' will not be available on the schema.
@@ -637,6 +638,28 @@ const addMaxLengthValidation = (attribute) => (schema) => {
637
638
  const addMinValidation = (attribute) => (schema) => {
638
639
  if ("min" in attribute) {
639
640
  const min = toInteger(attribute.min);
641
+ if (attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") {
642
+ if (!attribute.required && "test" in schema && min) {
643
+ return schema.test(
644
+ "custom-min",
645
+ {
646
+ ...translatedErrors.min,
647
+ values: {
648
+ min: attribute.min
649
+ }
650
+ },
651
+ (value) => {
652
+ if (!value) {
653
+ return true;
654
+ }
655
+ if (Array.isArray(value) && value.length === 0) {
656
+ return true;
657
+ }
658
+ return value.length >= min;
659
+ }
660
+ );
661
+ }
662
+ }
640
663
  if ("min" in schema && min) {
641
664
  return schema.min(min, {
642
665
  ...translatedErrors.min,
@@ -763,7 +786,10 @@ const useDocument = (args, opts) => {
763
786
  isLoading: isLoadingDocument,
764
787
  isFetching: isFetchingDocument,
765
788
  error
766
- } = useGetDocumentQuery(args, opts);
789
+ } = useGetDocumentQuery(args, {
790
+ ...opts,
791
+ skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
792
+ });
767
793
  const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
768
794
  React.useEffect(() => {
769
795
  if (error) {
@@ -927,12 +953,13 @@ const useDocumentActions = () => {
927
953
  );
928
954
  const [discardDocument] = useDiscardDocumentMutation();
929
955
  const discard = React.useCallback(
930
- async ({ collectionType, model, documentId }) => {
956
+ async ({ collectionType, model, documentId, params }) => {
931
957
  try {
932
958
  const res = await discardDocument({
933
959
  collectionType,
934
960
  model,
935
- documentId
961
+ documentId,
962
+ params
936
963
  });
937
964
  if ("error" in res) {
938
965
  toggleNotification({
@@ -1264,7 +1291,7 @@ const useDocumentActions = () => {
1264
1291
  };
1265
1292
  };
1266
1293
  const ProtectedHistoryPage = lazy(
1267
- () => import("./History-BFNUAiGc.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1294
+ () => import("./History-DSU-y4Hg.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1268
1295
  );
1269
1296
  const routes$1 = [
1270
1297
  {
@@ -1277,31 +1304,31 @@ const routes$1 = [
1277
1304
  }
1278
1305
  ];
1279
1306
  const ProtectedEditViewPage = lazy(
1280
- () => import("./EditViewPage-dFPBya9U.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1307
+ () => import("./EditViewPage-CPrukwtO.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1281
1308
  );
1282
1309
  const ProtectedListViewPage = lazy(
1283
- () => import("./ListViewPage-BPjljUsH.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1310
+ () => import("./ListViewPage-D9UmehuA.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1284
1311
  );
1285
1312
  const ProtectedListConfiguration = lazy(
1286
- () => import("./ListConfigurationPage-DDi0KqFm.mjs").then((mod) => ({
1313
+ () => import("./ListConfigurationPage-BWwZ-uMJ.mjs").then((mod) => ({
1287
1314
  default: mod.ProtectedListConfiguration
1288
1315
  }))
1289
1316
  );
1290
1317
  const ProtectedEditConfigurationPage = lazy(
1291
- () => import("./EditConfigurationPage-DbI4KMyz.mjs").then((mod) => ({
1318
+ () => import("./EditConfigurationPage-DbR8mWH5.mjs").then((mod) => ({
1292
1319
  default: mod.ProtectedEditConfigurationPage
1293
1320
  }))
1294
1321
  );
1295
1322
  const ProtectedComponentConfigurationPage = lazy(
1296
- () => import("./ComponentConfigurationPage-CuWgXugY.mjs").then((mod) => ({
1323
+ () => import("./ComponentConfigurationPage-Dy9BQQ2V.mjs").then((mod) => ({
1297
1324
  default: mod.ProtectedComponentConfigurationPage
1298
1325
  }))
1299
1326
  );
1300
1327
  const NoPermissions = lazy(
1301
- () => import("./NoPermissionsPage-CZrJH00p.mjs").then((mod) => ({ default: mod.NoPermissions }))
1328
+ () => import("./NoPermissionsPage-aFCCLbsf.mjs").then((mod) => ({ default: mod.NoPermissions }))
1302
1329
  );
1303
1330
  const NoContentType = lazy(
1304
- () => import("./NoContentTypePage-DaWw67K-.mjs").then((mod) => ({ default: mod.NoContentType }))
1331
+ () => import("./NoContentTypePage-CstnyWv2.mjs").then((mod) => ({ default: mod.NoContentType }))
1305
1332
  );
1306
1333
  const CollectionTypePages = () => {
1307
1334
  const { collectionType } = useParams();
@@ -1428,7 +1455,7 @@ const DocumentActionButton = (action) => {
1428
1455
  DocumentActionConfirmDialog,
1429
1456
  {
1430
1457
  ...action.dialog,
1431
- variant: action.variant,
1458
+ variant: action.dialog?.variant ?? action.variant,
1432
1459
  isOpen: dialogId === action.id,
1433
1460
  onClose: handleClose
1434
1461
  }
@@ -1508,7 +1535,7 @@ const DocumentActionsMenu = ({
1508
1535
  display: "block",
1509
1536
  children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
1510
1537
  /* @__PURE__ */ jsxs(Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
1511
- action.icon,
1538
+ /* @__PURE__ */ jsx(Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
1512
1539
  action.label
1513
1540
  ] }),
1514
1541
  action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
@@ -1569,6 +1596,18 @@ const convertActionVariantToColor = (variant = "secondary") => {
1569
1596
  return "primary600";
1570
1597
  }
1571
1598
  };
1599
+ const convertActionVariantToIconColor = (variant = "secondary") => {
1600
+ switch (variant) {
1601
+ case "danger":
1602
+ return "danger600";
1603
+ case "secondary":
1604
+ return "neutral500";
1605
+ case "success":
1606
+ return "success600";
1607
+ default:
1608
+ return "primary600";
1609
+ }
1610
+ };
1572
1611
  const DocumentActionConfirmDialog = ({
1573
1612
  onClose,
1574
1613
  onCancel,
@@ -1591,22 +1630,20 @@ const DocumentActionConfirmDialog = ({
1591
1630
  }
1592
1631
  onClose();
1593
1632
  };
1594
- return /* @__PURE__ */ jsxs(Dialog, { isOpen, title, onClose: handleClose, children: [
1595
- /* @__PURE__ */ jsx(DialogBody, { children: content }),
1596
- /* @__PURE__ */ jsx(
1597
- DialogFooter,
1598
- {
1599
- startAction: /* @__PURE__ */ jsx(Button, { onClick: handleClose, variant: "tertiary", children: formatMessage({
1600
- id: "app.components.Button.cancel",
1601
- defaultMessage: "Cancel"
1602
- }) }),
1603
- endAction: /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
1604
- id: "app.components.Button.confirm",
1605
- defaultMessage: "Confirm"
1606
- }) })
1607
- }
1608
- )
1609
- ] });
1633
+ return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
1634
+ /* @__PURE__ */ jsx(Dialog.Header, { children: title }),
1635
+ /* @__PURE__ */ jsx(Dialog.Body, { children: content }),
1636
+ /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
1637
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
1638
+ id: "app.components.Button.cancel",
1639
+ defaultMessage: "Cancel"
1640
+ }) }) }),
1641
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
1642
+ id: "app.components.Button.confirm",
1643
+ defaultMessage: "Confirm"
1644
+ }) })
1645
+ ] })
1646
+ ] }) });
1610
1647
  };
1611
1648
  const DocumentActionModal = ({
1612
1649
  isOpen,
@@ -1616,36 +1653,19 @@ const DocumentActionModal = ({
1616
1653
  content: Content,
1617
1654
  onModalClose
1618
1655
  }) => {
1619
- const id = React.useId();
1620
- if (!isOpen) {
1621
- return null;
1622
- }
1623
1656
  const handleClose = () => {
1624
1657
  if (onClose) {
1625
1658
  onClose();
1626
1659
  }
1627
1660
  onModalClose();
1628
1661
  };
1629
- return /* @__PURE__ */ jsxs(ModalLayout, { borderRadius: "4px", overflow: "hidden", onClose: handleClose, labelledBy: id, children: [
1630
- /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", textColor: "neutral800", tag: "h2", id, children: title }) }),
1631
- /* @__PURE__ */ jsx(ModalBody, { children: typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content }),
1632
- /* @__PURE__ */ jsx(
1633
- Box,
1634
- {
1635
- paddingTop: 4,
1636
- paddingBottom: 4,
1637
- paddingLeft: 5,
1638
- paddingRight: 5,
1639
- borderWidth: "1px 0 0 0",
1640
- borderStyle: "solid",
1641
- borderColor: "neutral150",
1642
- background: "neutral100",
1643
- children: typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
1644
- }
1645
- )
1646
- ] });
1662
+ return /* @__PURE__ */ jsx(Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
1663
+ /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: title }) }),
1664
+ /* @__PURE__ */ jsx(Modal.Body, { children: typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content }),
1665
+ /* @__PURE__ */ jsx(Modal.Footer, { children: typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer })
1666
+ ] }) });
1647
1667
  };
1648
- const PublishAction = ({
1668
+ const PublishAction$1 = ({
1649
1669
  activeTab,
1650
1670
  documentId,
1651
1671
  model,
@@ -1664,6 +1684,12 @@ const PublishAction = ({
1664
1684
  ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
1665
1685
  );
1666
1686
  const { publish } = useDocumentActions();
1687
+ const [
1688
+ countDraftRelations,
1689
+ { isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
1690
+ ] = useLazyGetDraftRelationCountQuery();
1691
+ const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React.useState(0);
1692
+ const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React.useState(0);
1667
1693
  const [{ query, rawQuery }] = useQueryParams();
1668
1694
  const params = React.useMemo(() => buildValidParams(query), [query]);
1669
1695
  const modified = useForm("PublishAction", ({ modified: modified2 }) => modified2);
@@ -1672,10 +1698,101 @@ const PublishAction = ({
1672
1698
  const validate = useForm("PublishAction", (state) => state.validate);
1673
1699
  const setErrors = useForm("PublishAction", (state) => state.setErrors);
1674
1700
  const formValues = useForm("PublishAction", ({ values }) => values);
1701
+ React.useEffect(() => {
1702
+ if (isErrorDraftRelations) {
1703
+ toggleNotification({
1704
+ type: "danger",
1705
+ message: formatMessage({
1706
+ id: getTranslation("error.records.fetch-draft-relatons"),
1707
+ defaultMessage: "An error occurred while fetching draft relations on this document."
1708
+ })
1709
+ });
1710
+ }
1711
+ }, [isErrorDraftRelations, toggleNotification, formatMessage]);
1712
+ React.useEffect(() => {
1713
+ const localDraftRelations = /* @__PURE__ */ new Set();
1714
+ const extractDraftRelations = (data) => {
1715
+ const relations = data.connect || [];
1716
+ relations.forEach((relation) => {
1717
+ if (relation.status === "draft") {
1718
+ localDraftRelations.add(relation.id);
1719
+ }
1720
+ });
1721
+ };
1722
+ const traverseAndExtract = (data) => {
1723
+ Object.entries(data).forEach(([key, value]) => {
1724
+ if (key === "connect" && Array.isArray(value)) {
1725
+ extractDraftRelations({ connect: value });
1726
+ } else if (typeof value === "object" && value !== null) {
1727
+ traverseAndExtract(value);
1728
+ }
1729
+ });
1730
+ };
1731
+ if (!documentId || modified) {
1732
+ traverseAndExtract(formValues);
1733
+ setLocalCountOfDraftRelations(localDraftRelations.size);
1734
+ }
1735
+ }, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
1736
+ React.useEffect(() => {
1737
+ if (documentId) {
1738
+ const fetchDraftRelationsCount = async () => {
1739
+ const { data, error } = await countDraftRelations({
1740
+ collectionType,
1741
+ model,
1742
+ documentId,
1743
+ params
1744
+ });
1745
+ if (error) {
1746
+ throw error;
1747
+ }
1748
+ if (data) {
1749
+ setServerCountOfDraftRelations(data.data);
1750
+ }
1751
+ };
1752
+ fetchDraftRelationsCount();
1753
+ }
1754
+ }, [documentId, countDraftRelations, collectionType, model, params]);
1675
1755
  const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
1676
1756
  if (!schema?.options?.draftAndPublish) {
1677
1757
  return null;
1678
1758
  }
1759
+ const performPublish = async () => {
1760
+ setSubmitting(true);
1761
+ try {
1762
+ const { errors } = await validate();
1763
+ if (errors) {
1764
+ toggleNotification({
1765
+ type: "danger",
1766
+ message: formatMessage({
1767
+ id: "content-manager.validation.error",
1768
+ defaultMessage: "There are validation errors in your document. Please fix them before saving."
1769
+ })
1770
+ });
1771
+ return;
1772
+ }
1773
+ const res = await publish(
1774
+ {
1775
+ collectionType,
1776
+ model,
1777
+ documentId,
1778
+ params
1779
+ },
1780
+ formValues
1781
+ );
1782
+ if ("data" in res && collectionType !== SINGLE_TYPES) {
1783
+ navigate({
1784
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1785
+ search: rawQuery
1786
+ });
1787
+ } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1788
+ setErrors(formatValidationErrors(res.error));
1789
+ }
1790
+ } finally {
1791
+ setSubmitting(false);
1792
+ }
1793
+ };
1794
+ const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
1795
+ const hasDraftRelations = totalDraftRelations > 0;
1679
1796
  return {
1680
1797
  /**
1681
1798
  * Disabled when:
@@ -1688,49 +1805,41 @@ const PublishAction = ({
1688
1805
  * - the user doesn't have the permission to create a new document
1689
1806
  * - the user doesn't have the permission to update the document
1690
1807
  */
1691
- disabled: isCloning || isSubmitting || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
1808
+ disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
1692
1809
  label: formatMessage({
1693
1810
  id: "app.utils.publish",
1694
1811
  defaultMessage: "Publish"
1695
1812
  }),
1696
1813
  onClick: async () => {
1697
- setSubmitting(true);
1698
- try {
1699
- const { errors } = await validate();
1700
- if (errors) {
1701
- toggleNotification({
1702
- type: "danger",
1703
- message: formatMessage({
1704
- id: "content-manager.validation.error",
1705
- defaultMessage: "There are validation errors in your document. Please fix them before saving."
1706
- })
1707
- });
1708
- return;
1709
- }
1710
- const res = await publish(
1711
- {
1712
- collectionType,
1713
- model,
1714
- documentId,
1715
- params
1716
- },
1717
- formValues
1718
- );
1719
- if ("data" in res && collectionType !== SINGLE_TYPES) {
1720
- navigate({
1721
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1722
- search: rawQuery
1723
- });
1724
- } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1725
- setErrors(formatValidationErrors(res.error));
1814
+ if (hasDraftRelations) {
1815
+ return;
1816
+ }
1817
+ await performPublish();
1818
+ },
1819
+ dialog: hasDraftRelations ? {
1820
+ type: "dialog",
1821
+ variant: "danger",
1822
+ footer: null,
1823
+ title: formatMessage({
1824
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
1825
+ defaultMessage: "Confirmation"
1826
+ }),
1827
+ content: formatMessage(
1828
+ {
1829
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
1830
+ defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
1831
+ },
1832
+ {
1833
+ count: totalDraftRelations
1726
1834
  }
1727
- } finally {
1728
- setSubmitting(false);
1835
+ ),
1836
+ onConfirm: async () => {
1837
+ await performPublish();
1729
1838
  }
1730
- }
1839
+ } : void 0
1731
1840
  };
1732
1841
  };
1733
- PublishAction.type = "publish";
1842
+ PublishAction$1.type = "publish";
1734
1843
  const UpdateAction = ({
1735
1844
  activeTab,
1736
1845
  documentId,
@@ -1826,10 +1935,13 @@ const UpdateAction = ({
1826
1935
  document
1827
1936
  );
1828
1937
  if ("data" in res && collectionType !== SINGLE_TYPES) {
1829
- navigate({
1830
- pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1831
- search: rawQuery
1832
- });
1938
+ navigate(
1939
+ {
1940
+ pathname: `../${collectionType}/${model}/${res.data.documentId}`,
1941
+ search: rawQuery
1942
+ },
1943
+ { replace: true }
1944
+ );
1833
1945
  } else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
1834
1946
  setErrors(formatValidationErrors(res.error));
1835
1947
  }
@@ -1861,10 +1973,8 @@ const UnpublishAction$1 = ({
1861
1973
  const { toggleNotification } = useNotification();
1862
1974
  const [shouldKeepDraft, setShouldKeepDraft] = React.useState(true);
1863
1975
  const isDocumentModified = document?.status === "modified";
1864
- const handleChange = (e) => {
1865
- if ("value" in e.target) {
1866
- setShouldKeepDraft(e.target.value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
1867
- }
1976
+ const handleChange = (value) => {
1977
+ setShouldKeepDraft(value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
1868
1978
  };
1869
1979
  if (!schema?.options?.draftAndPublish) {
1870
1980
  return null;
@@ -1914,40 +2024,24 @@ const UnpublishAction$1 = ({
1914
2024
  }) })
1915
2025
  ] }),
1916
2026
  /* @__PURE__ */ jsxs(
1917
- Flex,
2027
+ Radio.Group,
1918
2028
  {
1919
- onChange: handleChange,
1920
- direction: "column",
1921
- alignItems: "flex-start",
1922
- tag: "fieldset",
1923
- borderWidth: 0,
1924
- gap: 3,
2029
+ defaultValue: UNPUBLISH_DRAFT_OPTIONS.KEEP,
2030
+ name: "discard-options",
2031
+ "aria-label": formatMessage({
2032
+ id: "content-manager.actions.unpublish.dialog.radio-label",
2033
+ defaultMessage: "Choose an option to unpublish the document."
2034
+ }),
2035
+ onValueChange: handleChange,
1925
2036
  children: [
1926
- /* @__PURE__ */ jsx(VisuallyHidden, { tag: "legend" }),
1927
- /* @__PURE__ */ jsx(
1928
- Radio,
1929
- {
1930
- checked: shouldKeepDraft,
1931
- value: UNPUBLISH_DRAFT_OPTIONS.KEEP,
1932
- name: "discard-options",
1933
- children: formatMessage({
1934
- id: "content-manager.actions.unpublish.dialog.option.keep-draft",
1935
- defaultMessage: "Keep draft"
1936
- })
1937
- }
1938
- ),
1939
- /* @__PURE__ */ jsx(
1940
- Radio,
1941
- {
1942
- checked: !shouldKeepDraft,
1943
- value: UNPUBLISH_DRAFT_OPTIONS.DISCARD,
1944
- name: "discard-options",
1945
- children: formatMessage({
1946
- id: "content-manager.actions.unpublish.dialog.option.replace-draft",
1947
- defaultMessage: "Replace draft"
1948
- })
1949
- }
1950
- )
2037
+ /* @__PURE__ */ jsx(Radio.Item, { checked: shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.KEEP, children: formatMessage({
2038
+ id: "content-manager.actions.unpublish.dialog.option.keep-draft",
2039
+ defaultMessage: "Keep draft"
2040
+ }) }),
2041
+ /* @__PURE__ */ jsx(Radio.Item, { checked: !shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.DISCARD, children: formatMessage({
2042
+ id: "content-manager.actions.unpublish.dialog.option.replace-draft",
2043
+ defaultMessage: "Replace draft"
2044
+ }) })
1951
2045
  ]
1952
2046
  }
1953
2047
  )
@@ -2036,7 +2130,7 @@ const StyledCrossCircle = styled(CrossCircle)`
2036
2130
  fill: currentColor;
2037
2131
  }
2038
2132
  `;
2039
- const DEFAULT_ACTIONS = [PublishAction, UpdateAction, UnpublishAction$1, DiscardAction];
2133
+ const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2040
2134
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2041
2135
  const RelativeTime = React.forwardRef(
2042
2136
  ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
@@ -2093,23 +2187,13 @@ const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
2093
2187
  id: "content-manager.containers.edit.title.new",
2094
2188
  defaultMessage: "Create an entry"
2095
2189
  }) : documentTitle;
2096
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 8, paddingBottom: 4, gap: 3, children: [
2190
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
2097
2191
  /* @__PURE__ */ jsx(BackButton, {}),
2098
- /* @__PURE__ */ jsxs(
2099
- Flex,
2100
- {
2101
- width: "100%",
2102
- justifyContent: "space-between",
2103
- paddingTop: 1,
2104
- gap: "80px",
2105
- alignItems: "flex-start",
2106
- children: [
2107
- /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2108
- /* @__PURE__ */ jsx(HeaderToolbar, {})
2109
- ]
2110
- }
2111
- ),
2112
- status ? /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) : null
2192
+ /* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
2193
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
2194
+ /* @__PURE__ */ jsx(HeaderToolbar, {})
2195
+ ] }),
2196
+ status ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
2113
2197
  ] });
2114
2198
  };
2115
2199
  const HeaderToolbar = () => {
@@ -2491,163 +2575,785 @@ const Panel = React.forwardRef(({ children, title }, ref) => {
2491
2575
  }
2492
2576
  );
2493
2577
  });
2494
- const BulkActionsRenderer = () => {
2495
- const plugins = useStrapiApp("BulkActionsRenderer", (state) => state.plugins);
2496
- const { model, collectionType } = useDoc();
2497
- const { selectedRows } = useTable("BulkActionsRenderer", (state) => state);
2498
- return /* @__PURE__ */ jsx(Flex, { gap: 2, children: /* @__PURE__ */ jsx(
2499
- DescriptionComponentRenderer,
2500
- {
2501
- props: {
2502
- model,
2503
- collectionType,
2504
- documents: selectedRows
2505
- },
2506
- descriptions: plugins["content-manager"].apis.getBulkActions(),
2507
- children: (actions2) => actions2.map((action) => /* @__PURE__ */ jsx(BulkActionAction, { ...action }, action.id))
2508
- }
2509
- ) });
2578
+ const HOOKS = {
2579
+ /**
2580
+ * Hook that allows to mutate the displayed headers of the list view table
2581
+ * @constant
2582
+ * @type {string}
2583
+ */
2584
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2585
+ /**
2586
+ * Hook that allows to mutate the CM's collection types links pre-set filters
2587
+ * @constant
2588
+ * @type {string}
2589
+ */
2590
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2591
+ /**
2592
+ * Hook that allows to mutate the CM's edit view layout
2593
+ * @constant
2594
+ * @type {string}
2595
+ */
2596
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2597
+ /**
2598
+ * Hook that allows to mutate the CM's single types links pre-set filters
2599
+ * @constant
2600
+ * @type {string}
2601
+ */
2602
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2510
2603
  };
2511
- const BulkActionAction = (action) => {
2512
- const [dialogId, setDialogId] = React.useState(null);
2513
- const { toggleNotification } = useNotification();
2514
- const handleClick = (action2) => (e) => {
2515
- const { onClick, dialog, id } = action2;
2516
- if (onClick) {
2517
- onClick(e);
2518
- }
2519
- if (dialog) {
2520
- switch (dialog.type) {
2521
- case "notification":
2522
- toggleNotification({
2523
- title: dialog.title,
2524
- message: dialog.content,
2525
- type: dialog.status,
2526
- timeout: dialog.timeout,
2527
- onClose: dialog.onClose
2528
- });
2529
- break;
2530
- case "dialog":
2531
- case "modal": {
2532
- e.preventDefault();
2533
- setDialogId(id);
2534
- }
2535
- }
2536
- }
2604
+ const contentTypesApi = contentManagerApi.injectEndpoints({
2605
+ endpoints: (builder) => ({
2606
+ getContentTypeConfiguration: builder.query({
2607
+ query: (uid) => ({
2608
+ url: `/content-manager/content-types/${uid}/configuration`,
2609
+ method: "GET"
2610
+ }),
2611
+ transformResponse: (response) => response.data,
2612
+ providesTags: (_result, _error, uid) => [
2613
+ { type: "ContentTypesConfiguration", id: uid },
2614
+ { type: "ContentTypeSettings", id: "LIST" }
2615
+ ]
2616
+ }),
2617
+ getAllContentTypeSettings: builder.query({
2618
+ query: () => "/content-manager/content-types-settings",
2619
+ transformResponse: (response) => response.data,
2620
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2621
+ }),
2622
+ updateContentTypeConfiguration: builder.mutation({
2623
+ query: ({ uid, ...body }) => ({
2624
+ url: `/content-manager/content-types/${uid}/configuration`,
2625
+ method: "PUT",
2626
+ data: body
2627
+ }),
2628
+ transformResponse: (response) => response.data,
2629
+ invalidatesTags: (_result, _error, { uid }) => [
2630
+ { type: "ContentTypesConfiguration", id: uid },
2631
+ { type: "ContentTypeSettings", id: "LIST" },
2632
+ // Is this necessary?
2633
+ { type: "InitialData" }
2634
+ ]
2635
+ })
2636
+ })
2637
+ });
2638
+ const {
2639
+ useGetContentTypeConfigurationQuery,
2640
+ useGetAllContentTypeSettingsQuery,
2641
+ useUpdateContentTypeConfigurationMutation
2642
+ } = contentTypesApi;
2643
+ const checkIfAttributeIsDisplayable = (attribute) => {
2644
+ const { type } = attribute;
2645
+ if (type === "relation") {
2646
+ return !attribute.relation.toLowerCase().includes("morph");
2647
+ }
2648
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2649
+ };
2650
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2651
+ if (!mainFieldName) {
2652
+ return void 0;
2653
+ }
2654
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2655
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2656
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2657
+ );
2658
+ return {
2659
+ name: mainFieldName,
2660
+ type: mainFieldType ?? "string"
2537
2661
  };
2538
- const handleClose = () => {
2539
- setDialogId(null);
2540
- if (action.dialog?.type === "modal" && action.dialog?.onClose) {
2541
- action.dialog.onClose();
2662
+ };
2663
+ const DEFAULT_SETTINGS = {
2664
+ bulkable: false,
2665
+ filterable: false,
2666
+ searchable: false,
2667
+ pagination: false,
2668
+ defaultSortBy: "",
2669
+ defaultSortOrder: "asc",
2670
+ mainField: "id",
2671
+ pageSize: 10
2672
+ };
2673
+ const useDocumentLayout = (model) => {
2674
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2675
+ const [{ query }] = useQueryParams();
2676
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2677
+ const { toggleNotification } = useNotification();
2678
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2679
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2680
+ const {
2681
+ data,
2682
+ isLoading: isLoadingConfigs,
2683
+ error,
2684
+ isFetching: isFetchingConfigs
2685
+ } = useGetContentTypeConfigurationQuery(model);
2686
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2687
+ React.useEffect(() => {
2688
+ if (error) {
2689
+ toggleNotification({
2690
+ type: "danger",
2691
+ message: formatAPIError(error)
2692
+ });
2542
2693
  }
2694
+ }, [error, formatAPIError, toggleNotification]);
2695
+ const editLayout = React.useMemo(
2696
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2697
+ layout: [],
2698
+ components: {},
2699
+ metadatas: {},
2700
+ options: {},
2701
+ settings: DEFAULT_SETTINGS
2702
+ },
2703
+ [data, isLoading, schemas, schema, components]
2704
+ );
2705
+ const listLayout = React.useMemo(() => {
2706
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2707
+ layout: [],
2708
+ metadatas: {},
2709
+ options: {},
2710
+ settings: DEFAULT_SETTINGS
2711
+ };
2712
+ }, [data, isLoading, schemas, schema, components]);
2713
+ const { layout: edit } = React.useMemo(
2714
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2715
+ layout: editLayout,
2716
+ query
2717
+ }),
2718
+ [editLayout, query, runHookWaterfall]
2719
+ );
2720
+ return {
2721
+ error,
2722
+ isLoading,
2723
+ edit,
2724
+ list: listLayout
2543
2725
  };
2544
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2545
- /* @__PURE__ */ jsx(
2546
- Button,
2547
- {
2548
- disabled: action.disabled,
2549
- startIcon: action.icon,
2550
- variant: action.variant,
2551
- onClick: handleClick(action),
2552
- children: action.label
2553
- }
2554
- ),
2555
- action.dialog?.type === "dialog" ? /* @__PURE__ */ jsx(
2556
- BulkActionConfirmDialog,
2557
- {
2558
- ...action.dialog,
2559
- variant: action.variant,
2560
- isOpen: dialogId === action.id,
2561
- onClose: handleClose
2562
- }
2563
- ) : null,
2564
- action.dialog?.type === "modal" ? /* @__PURE__ */ jsx(
2565
- BulkActionModal,
2566
- {
2567
- ...action.dialog,
2568
- onModalClose: handleClose,
2569
- isOpen: dialogId === action.id
2570
- }
2571
- ) : null
2572
- ] });
2573
2726
  };
2574
- const BulkActionConfirmDialog = ({
2575
- onClose,
2576
- onCancel,
2577
- onConfirm,
2578
- title,
2579
- content,
2580
- confirmButton,
2581
- isOpen,
2582
- variant = "secondary"
2727
+ const useDocLayout = () => {
2728
+ const { model } = useDoc();
2729
+ return useDocumentLayout(model);
2730
+ };
2731
+ const formatEditLayout = (data, {
2732
+ schemas,
2733
+ schema,
2734
+ components
2583
2735
  }) => {
2584
- const { formatMessage } = useIntl();
2585
- const handleClose = async () => {
2586
- if (onCancel) {
2587
- await onCancel();
2736
+ let currentPanelIndex = 0;
2737
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2738
+ data.contentType.layouts.edit,
2739
+ schema?.attributes,
2740
+ data.contentType.metadatas,
2741
+ { configurations: data.components, schemas: components },
2742
+ schemas
2743
+ ).reduce((panels, row) => {
2744
+ if (row.some((field) => field.type === "dynamiczone")) {
2745
+ panels.push([row]);
2746
+ currentPanelIndex += 2;
2747
+ } else {
2748
+ if (!panels[currentPanelIndex]) {
2749
+ panels.push([]);
2750
+ }
2751
+ panels[currentPanelIndex].push(row);
2588
2752
  }
2589
- onClose();
2590
- };
2591
- const handleConfirm = async () => {
2592
- if (onConfirm) {
2593
- await onConfirm();
2753
+ return panels;
2754
+ }, []);
2755
+ const componentEditAttributes = Object.entries(data.components).reduce(
2756
+ (acc, [uid, configuration]) => {
2757
+ acc[uid] = {
2758
+ layout: convertEditLayoutToFieldLayouts(
2759
+ configuration.layouts.edit,
2760
+ components[uid].attributes,
2761
+ configuration.metadatas
2762
+ ),
2763
+ settings: {
2764
+ ...configuration.settings,
2765
+ icon: components[uid].info.icon,
2766
+ displayName: components[uid].info.displayName
2767
+ }
2768
+ };
2769
+ return acc;
2770
+ },
2771
+ {}
2772
+ );
2773
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2774
+ (acc, [attribute, metadata]) => {
2775
+ return {
2776
+ ...acc,
2777
+ [attribute]: metadata.edit
2778
+ };
2779
+ },
2780
+ {}
2781
+ );
2782
+ return {
2783
+ layout: panelledEditAttributes,
2784
+ components: componentEditAttributes,
2785
+ metadatas: editMetadatas,
2786
+ settings: {
2787
+ ...data.contentType.settings,
2788
+ displayName: schema?.info.displayName
2789
+ },
2790
+ options: {
2791
+ ...schema?.options,
2792
+ ...schema?.pluginOptions,
2793
+ ...data.contentType.options
2594
2794
  }
2595
- onClose();
2596
2795
  };
2597
- return /* @__PURE__ */ jsxs(Dialog, { isOpen, title, onClose: handleClose, children: [
2598
- /* @__PURE__ */ jsx(DialogBody, { icon: /* @__PURE__ */ jsx(WarningCircle, {}), children: content }),
2599
- /* @__PURE__ */ jsx(
2600
- DialogFooter,
2601
- {
2602
- startAction: /* @__PURE__ */ jsx(Button, { onClick: handleClose, variant: "tertiary", children: formatMessage({
2603
- id: "app.components.Button.cancel",
2604
- defaultMessage: "Cancel"
2605
- }) }),
2606
- endAction: /* @__PURE__ */ jsx(
2607
- Button,
2608
- {
2609
- onClick: handleConfirm,
2610
- variant: variant === "danger-light" ? variant : "secondary",
2611
- startIcon: variant === "danger-light" ? /* @__PURE__ */ jsx(Trash, {}) : /* @__PURE__ */ jsx(Check, {}),
2612
- children: confirmButton ? confirmButton : formatMessage({
2613
- id: "app.components.Button.confirm",
2614
- defaultMessage: "Confirm"
2615
- })
2616
- }
2617
- )
2796
+ };
2797
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2798
+ return rows.map(
2799
+ (row) => row.map((field) => {
2800
+ const attribute = attributes[field.name];
2801
+ if (!attribute) {
2802
+ return null;
2618
2803
  }
2619
- )
2620
- ] });
2804
+ const { edit: metadata } = metadatas[field.name];
2805
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2806
+ return {
2807
+ attribute,
2808
+ disabled: !metadata.editable,
2809
+ hint: metadata.description,
2810
+ label: metadata.label ?? "",
2811
+ name: field.name,
2812
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
2813
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2814
+ schemas,
2815
+ components: components?.schemas ?? {}
2816
+ }),
2817
+ placeholder: metadata.placeholder ?? "",
2818
+ required: attribute.required ?? false,
2819
+ size: field.size,
2820
+ unique: "unique" in attribute ? attribute.unique : false,
2821
+ visible: metadata.visible ?? true,
2822
+ type: attribute.type
2823
+ };
2824
+ }).filter((field) => field !== null)
2825
+ );
2621
2826
  };
2622
- const BulkActionModal = ({
2623
- isOpen,
2624
- title,
2625
- onClose,
2626
- content: Content,
2627
- onModalClose
2827
+ const formatListLayout = (data, {
2828
+ schemas,
2829
+ schema,
2830
+ components
2628
2831
  }) => {
2629
- const id = React.useId();
2630
- if (!isOpen) {
2631
- return null;
2632
- }
2633
- const handleClose = () => {
2634
- if (onClose) {
2635
- onClose();
2832
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2833
+ (acc, [attribute, metadata]) => {
2834
+ return {
2835
+ ...acc,
2836
+ [attribute]: metadata.list
2837
+ };
2838
+ },
2839
+ {}
2840
+ );
2841
+ const listAttributes = convertListLayoutToFieldLayouts(
2842
+ data.contentType.layouts.list,
2843
+ schema?.attributes,
2844
+ listMetadatas,
2845
+ { configurations: data.components, schemas: components },
2846
+ schemas
2847
+ );
2848
+ return {
2849
+ layout: listAttributes,
2850
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2851
+ metadatas: listMetadatas,
2852
+ options: {
2853
+ ...schema?.options,
2854
+ ...schema?.pluginOptions,
2855
+ ...data.contentType.options
2636
2856
  }
2637
- onModalClose();
2638
2857
  };
2639
- return /* @__PURE__ */ jsxs(ModalLayout, { borderRadius: "4px", overflow: "hidden", onClose: handleClose, labelledBy: id, children: [
2640
- /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", textColor: "neutral800", tag: "h2", id, children: title }) }),
2641
- /* @__PURE__ */ jsx(Content, { onClose: handleClose })
2642
- ] });
2643
2858
  };
2644
- const DeleteAction = ({ documents, model }) => {
2645
- const { formatMessage } = useIntl();
2646
- const { schema: contentType } = useDoc();
2647
- const selectRow = useTable("DeleteAction", (state) => state.selectRow);
2648
- const hasI18nEnabled = Boolean(contentType?.pluginOptions?.i18n);
2649
- const [{ query }] = useQueryParams();
2650
- const params = React.useMemo(() => buildValidParams(query), [query]);
2859
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2860
+ return columns.map((name) => {
2861
+ const attribute = attributes[name];
2862
+ if (!attribute) {
2863
+ return null;
2864
+ }
2865
+ const metadata = metadatas[name];
2866
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2867
+ return {
2868
+ attribute,
2869
+ label: metadata.label ?? "",
2870
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2871
+ schemas,
2872
+ components: components?.schemas ?? {}
2873
+ }),
2874
+ name,
2875
+ searchable: metadata.searchable ?? true,
2876
+ sortable: metadata.sortable ?? true
2877
+ };
2878
+ }).filter((field) => field !== null);
2879
+ };
2880
+ const ConfirmBulkActionDialog = ({
2881
+ onToggleDialog,
2882
+ isOpen = false,
2883
+ dialogBody,
2884
+ endAction
2885
+ }) => {
2886
+ const { formatMessage } = useIntl();
2887
+ return /* @__PURE__ */ jsx(Dialog.Root, { onOpenChange: onToggleDialog, open: isOpen, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
2888
+ /* @__PURE__ */ jsx(Dialog.Header, { children: formatMessage({
2889
+ id: "app.components.ConfirmDialog.title",
2890
+ defaultMessage: "Confirmation"
2891
+ }) }),
2892
+ /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
2893
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
2894
+ dialogBody
2895
+ ] }) }),
2896
+ /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
2897
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { fullWidth: true, onClick: onToggleDialog, variant: "tertiary", children: formatMessage({
2898
+ id: "app.components.Button.cancel",
2899
+ defaultMessage: "Cancel"
2900
+ }) }) }),
2901
+ endAction
2902
+ ] })
2903
+ ] }) });
2904
+ };
2905
+ const BoldChunk$1 = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
2906
+ const ConfirmDialogPublishAll = ({
2907
+ isOpen,
2908
+ onToggleDialog,
2909
+ isConfirmButtonLoading = false,
2910
+ onConfirm
2911
+ }) => {
2912
+ const { formatMessage } = useIntl();
2913
+ const selectedEntries = useTable("ConfirmDialogPublishAll", (state) => state.selectedRows);
2914
+ const { toggleNotification } = useNotification();
2915
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2916
+ const { model, schema } = useDoc();
2917
+ const [{ query }] = useQueryParams();
2918
+ const {
2919
+ data: countDraftRelations = 0,
2920
+ isLoading,
2921
+ error
2922
+ } = useGetManyDraftRelationCountQuery(
2923
+ {
2924
+ model,
2925
+ documentIds: selectedEntries.map((entry) => entry.documentId),
2926
+ locale: query?.plugins?.i18n?.locale
2927
+ },
2928
+ {
2929
+ skip: selectedEntries.length === 0
2930
+ }
2931
+ );
2932
+ React.useEffect(() => {
2933
+ if (error) {
2934
+ toggleNotification({ type: "danger", message: formatAPIError(error) });
2935
+ }
2936
+ }, [error, formatAPIError, toggleNotification]);
2937
+ if (error) {
2938
+ return null;
2939
+ }
2940
+ return /* @__PURE__ */ jsx(
2941
+ ConfirmBulkActionDialog,
2942
+ {
2943
+ isOpen: isOpen && !isLoading,
2944
+ onToggleDialog,
2945
+ dialogBody: /* @__PURE__ */ jsxs(Fragment, { children: [
2946
+ /* @__PURE__ */ jsxs(Typography, { id: "confirm-description", textAlign: "center", children: [
2947
+ countDraftRelations > 0 && formatMessage(
2948
+ {
2949
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2950
+ 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. "
2951
+ },
2952
+ {
2953
+ b: BoldChunk$1,
2954
+ count: countDraftRelations,
2955
+ entities: selectedEntries.length
2956
+ }
2957
+ ),
2958
+ formatMessage({
2959
+ id: getTranslation("popUpWarning.bodyMessage.contentType.publish.all"),
2960
+ defaultMessage: "Are you sure you want to publish these entries?"
2961
+ })
2962
+ ] }),
2963
+ schema?.pluginOptions && "i18n" in schema.pluginOptions && schema?.pluginOptions.i18n && /* @__PURE__ */ jsx(Typography, { textColor: "danger500", textAlign: "center", children: formatMessage(
2964
+ {
2965
+ id: getTranslation("Settings.list.actions.publishAdditionalInfos"),
2966
+ defaultMessage: "This will publish the active locale versions <em>(from Internationalization)</em>"
2967
+ },
2968
+ {
2969
+ em: Emphasis
2970
+ }
2971
+ ) })
2972
+ ] }),
2973
+ endAction: /* @__PURE__ */ jsx(
2974
+ Button,
2975
+ {
2976
+ onClick: onConfirm,
2977
+ variant: "secondary",
2978
+ startIcon: /* @__PURE__ */ jsx(Check, {}),
2979
+ loading: isConfirmButtonLoading,
2980
+ children: formatMessage({
2981
+ id: "app.utils.publish",
2982
+ defaultMessage: "Publish"
2983
+ })
2984
+ }
2985
+ )
2986
+ }
2987
+ );
2988
+ };
2989
+ const TypographyMaxWidth = styled(Typography)`
2990
+ max-width: 300px;
2991
+ `;
2992
+ const formatErrorMessages = (errors, parentKey, formatMessage) => {
2993
+ const messages = [];
2994
+ Object.entries(errors).forEach(([key, value]) => {
2995
+ const currentKey = parentKey ? `${parentKey}.${key}` : key;
2996
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
2997
+ if ("id" in value && "defaultMessage" in value) {
2998
+ messages.push(
2999
+ formatMessage(
3000
+ {
3001
+ id: `${value.id}.withField`,
3002
+ defaultMessage: value.defaultMessage
3003
+ },
3004
+ { field: currentKey }
3005
+ )
3006
+ );
3007
+ } else {
3008
+ messages.push(
3009
+ ...formatErrorMessages(
3010
+ // @ts-expect-error TODO: check why value is not compatible with FormErrors
3011
+ value,
3012
+ currentKey,
3013
+ formatMessage
3014
+ )
3015
+ );
3016
+ }
3017
+ } else {
3018
+ messages.push(
3019
+ formatMessage(
3020
+ {
3021
+ id: `${value}.withField`,
3022
+ defaultMessage: value
3023
+ },
3024
+ { field: currentKey }
3025
+ )
3026
+ );
3027
+ }
3028
+ });
3029
+ return messages;
3030
+ };
3031
+ const EntryValidationText = ({ validationErrors, status }) => {
3032
+ const { formatMessage } = useIntl();
3033
+ if (validationErrors) {
3034
+ const validationErrorsMessages = formatErrorMessages(validationErrors, "", formatMessage).join(
3035
+ " "
3036
+ );
3037
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3038
+ /* @__PURE__ */ jsx(CrossCircle, { fill: "danger600" }),
3039
+ /* @__PURE__ */ jsx(Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
3040
+ ] });
3041
+ }
3042
+ if (status === "published") {
3043
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3044
+ /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
3045
+ /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
3046
+ id: "content-manager.bulk-publish.already-published",
3047
+ defaultMessage: "Already Published"
3048
+ }) })
3049
+ ] });
3050
+ }
3051
+ if (status === "modified") {
3052
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3053
+ /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
3054
+ /* @__PURE__ */ jsx(Typography, { children: formatMessage({
3055
+ id: "content-manager.bulk-publish.modified",
3056
+ defaultMessage: "Ready to publish changes"
3057
+ }) })
3058
+ ] });
3059
+ }
3060
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3061
+ /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
3062
+ /* @__PURE__ */ jsx(Typography, { children: formatMessage({
3063
+ id: "app.utils.ready-to-publish",
3064
+ defaultMessage: "Ready to publish"
3065
+ }) })
3066
+ ] });
3067
+ };
3068
+ const TABLE_HEADERS = [
3069
+ { name: "id", label: "id" },
3070
+ { name: "name", label: "name" },
3071
+ { name: "status", label: "status" },
3072
+ { name: "publicationStatus", label: "Publication status" }
3073
+ ];
3074
+ const SelectedEntriesTableContent = ({
3075
+ isPublishing,
3076
+ rowsToDisplay = [],
3077
+ entriesToPublish = [],
3078
+ validationErrors = {}
3079
+ }) => {
3080
+ const { pathname } = useLocation();
3081
+ const { formatMessage } = useIntl();
3082
+ const {
3083
+ list: {
3084
+ settings: { mainField }
3085
+ }
3086
+ } = useDocLayout();
3087
+ const shouldDisplayMainField = mainField != null && mainField !== "id";
3088
+ return /* @__PURE__ */ jsxs(Table.Content, { children: [
3089
+ /* @__PURE__ */ jsxs(Table.Head, { children: [
3090
+ /* @__PURE__ */ jsx(Table.HeaderCheckboxCell, {}),
3091
+ TABLE_HEADERS.filter((head) => head.name !== "name" || shouldDisplayMainField).map(
3092
+ (head) => /* @__PURE__ */ jsx(Table.HeaderCell, { ...head }, head.name)
3093
+ )
3094
+ ] }),
3095
+ /* @__PURE__ */ jsx(Table.Loading, {}),
3096
+ /* @__PURE__ */ jsx(Table.Body, { children: rowsToDisplay.map((row, index2) => /* @__PURE__ */ jsxs(Table.Row, { children: [
3097
+ /* @__PURE__ */ jsx(Table.CheckboxCell, { id: row.id }),
3098
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Typography, { children: row.id }) }),
3099
+ shouldDisplayMainField && /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Typography, { children: row[mainField] }) }),
3100
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(DocumentStatus, { status: row.status, maxWidth: "min-content" }) }),
3101
+ /* @__PURE__ */ jsx(Table.Cell, { children: isPublishing && entriesToPublish.includes(row.documentId) ? /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3102
+ /* @__PURE__ */ jsx(Typography, { children: formatMessage({
3103
+ id: "content-manager.success.record.publishing",
3104
+ defaultMessage: "Publishing..."
3105
+ }) }),
3106
+ /* @__PURE__ */ jsx(Loader, { small: true })
3107
+ ] }) : /* @__PURE__ */ jsx(
3108
+ EntryValidationText,
3109
+ {
3110
+ validationErrors: validationErrors[row.documentId],
3111
+ status: row.status
3112
+ }
3113
+ ) }),
3114
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3115
+ IconButton,
3116
+ {
3117
+ tag: Link,
3118
+ to: {
3119
+ pathname: `${pathname}/${row.documentId}`,
3120
+ search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3121
+ },
3122
+ state: { from: pathname },
3123
+ label: formatMessage(
3124
+ { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3125
+ {
3126
+ target: formatMessage(
3127
+ {
3128
+ id: "content-manager.components.ListViewHelperPluginTable.row-line",
3129
+ defaultMessage: "item line {number}"
3130
+ },
3131
+ { number: index2 + 1 }
3132
+ )
3133
+ }
3134
+ ),
3135
+ target: "_blank",
3136
+ marginLeft: "auto",
3137
+ children: /* @__PURE__ */ jsx(Pencil, {})
3138
+ }
3139
+ ) })
3140
+ ] }, row.id)) })
3141
+ ] });
3142
+ };
3143
+ const BoldChunk = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
3144
+ const SelectedEntriesModalContent = ({
3145
+ listViewSelectedEntries,
3146
+ toggleModal,
3147
+ setListViewSelectedDocuments,
3148
+ model
3149
+ }) => {
3150
+ const { formatMessage } = useIntl();
3151
+ const { schema, components } = useContentTypeSchema(model);
3152
+ const documentIds = listViewSelectedEntries.map(({ documentId }) => documentId);
3153
+ const [{ query }] = useQueryParams();
3154
+ const params = React.useMemo(() => buildValidParams(query), [query]);
3155
+ const { data, isLoading, isFetching, refetch } = useGetAllDocumentsQuery(
3156
+ {
3157
+ model,
3158
+ params: {
3159
+ page: "1",
3160
+ pageSize: documentIds.length.toString(),
3161
+ sort: query.sort,
3162
+ filters: {
3163
+ documentId: {
3164
+ $in: documentIds
3165
+ }
3166
+ },
3167
+ locale: query.plugins?.i18n?.locale
3168
+ }
3169
+ },
3170
+ {
3171
+ selectFromResult: ({ data: data2, ...restRes }) => ({ data: data2?.results ?? [], ...restRes })
3172
+ }
3173
+ );
3174
+ const { rows, validationErrors } = React.useMemo(() => {
3175
+ if (data.length > 0 && schema) {
3176
+ const validate = createYupSchema(schema.attributes, components);
3177
+ const validationErrors2 = {};
3178
+ const rows2 = data.map((entry) => {
3179
+ try {
3180
+ validate.validateSync(entry, { abortEarly: false });
3181
+ return entry;
3182
+ } catch (e) {
3183
+ if (e instanceof ValidationError) {
3184
+ validationErrors2[entry.documentId] = getYupValidationErrors(e);
3185
+ }
3186
+ return entry;
3187
+ }
3188
+ });
3189
+ return { rows: rows2, validationErrors: validationErrors2 };
3190
+ }
3191
+ return {
3192
+ rows: [],
3193
+ validationErrors: {}
3194
+ };
3195
+ }, [components, data, schema]);
3196
+ const [publishedCount, setPublishedCount] = React.useState(0);
3197
+ const [isDialogOpen, setIsDialogOpen] = React.useState(false);
3198
+ const { publishMany: bulkPublishAction } = useDocumentActions();
3199
+ const [, { isLoading: isSubmittingForm }] = usePublishManyDocumentsMutation();
3200
+ const selectedRows = useTable("publishAction", (state) => state.selectedRows);
3201
+ const selectedEntries = rows.filter(
3202
+ (entry) => selectedRows.some((selectedEntry) => selectedEntry.documentId === entry.documentId)
3203
+ );
3204
+ const entriesToPublish = selectedEntries.filter((entry) => !validationErrors[entry.documentId]).map((entry) => entry.documentId);
3205
+ const selectedEntriesWithErrorsCount = selectedEntries.filter(
3206
+ ({ documentId }) => validationErrors[documentId]
3207
+ ).length;
3208
+ const selectedEntriesPublished = selectedEntries.filter(
3209
+ ({ status }) => status === "published"
3210
+ ).length;
3211
+ const selectedEntriesWithNoErrorsCount = selectedEntries.length - selectedEntriesWithErrorsCount - selectedEntriesPublished;
3212
+ const toggleDialog = () => setIsDialogOpen((prev) => !prev);
3213
+ const handleConfirmBulkPublish = async () => {
3214
+ toggleDialog();
3215
+ const res = await bulkPublishAction({ model, documentIds: entriesToPublish, params });
3216
+ if (!("error" in res)) {
3217
+ setPublishedCount(res.count);
3218
+ const unpublishedEntries = rows.filter((row) => {
3219
+ return !entriesToPublish.includes(row.documentId);
3220
+ });
3221
+ setListViewSelectedDocuments(unpublishedEntries);
3222
+ }
3223
+ };
3224
+ const getFormattedCountMessage = () => {
3225
+ if (publishedCount) {
3226
+ return formatMessage(
3227
+ {
3228
+ id: getTranslation("containers.list.selectedEntriesModal.publishedCount"),
3229
+ 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."
3230
+ },
3231
+ {
3232
+ publishedCount,
3233
+ withErrorsCount: selectedEntriesWithErrorsCount,
3234
+ b: BoldChunk
3235
+ }
3236
+ );
3237
+ }
3238
+ return formatMessage(
3239
+ {
3240
+ id: getTranslation("containers.list.selectedEntriesModal.selectedCount"),
3241
+ 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."
3242
+ },
3243
+ {
3244
+ readyToPublishCount: selectedEntriesWithNoErrorsCount,
3245
+ withErrorsCount: selectedEntriesWithErrorsCount,
3246
+ alreadyPublishedCount: selectedEntriesPublished,
3247
+ b: BoldChunk
3248
+ }
3249
+ );
3250
+ };
3251
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3252
+ /* @__PURE__ */ jsxs(Modal.Body, { children: [
3253
+ /* @__PURE__ */ jsx(Typography, { children: getFormattedCountMessage() }),
3254
+ /* @__PURE__ */ jsx(Box, { marginTop: 5, children: /* @__PURE__ */ jsx(
3255
+ SelectedEntriesTableContent,
3256
+ {
3257
+ isPublishing: isSubmittingForm,
3258
+ rowsToDisplay: rows,
3259
+ entriesToPublish,
3260
+ validationErrors
3261
+ }
3262
+ ) })
3263
+ ] }),
3264
+ /* @__PURE__ */ jsxs(Modal.Footer, { children: [
3265
+ /* @__PURE__ */ jsx(Button, { onClick: toggleModal, variant: "tertiary", children: formatMessage({
3266
+ id: "app.components.Button.cancel",
3267
+ defaultMessage: "Cancel"
3268
+ }) }),
3269
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3270
+ /* @__PURE__ */ jsx(Button, { onClick: refetch, variant: "tertiary", loading: isFetching, children: formatMessage({ id: "app.utils.refresh", defaultMessage: "Refresh" }) }),
3271
+ /* @__PURE__ */ jsx(
3272
+ Button,
3273
+ {
3274
+ onClick: toggleDialog,
3275
+ disabled: selectedEntries.length === 0 || selectedEntries.length === selectedEntriesWithErrorsCount || selectedEntriesPublished === selectedEntries.length || isLoading,
3276
+ loading: isSubmittingForm,
3277
+ children: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" })
3278
+ }
3279
+ )
3280
+ ] })
3281
+ ] }),
3282
+ /* @__PURE__ */ jsx(
3283
+ ConfirmDialogPublishAll,
3284
+ {
3285
+ isOpen: isDialogOpen,
3286
+ onToggleDialog: toggleDialog,
3287
+ isConfirmButtonLoading: isSubmittingForm,
3288
+ onConfirm: handleConfirmBulkPublish
3289
+ }
3290
+ )
3291
+ ] });
3292
+ };
3293
+ const PublishAction = ({ documents, model }) => {
3294
+ const { formatMessage } = useIntl();
3295
+ const hasPublishPermission = useDocumentRBAC("unpublishAction", (state) => state.canPublish);
3296
+ const showPublishButton = hasPublishPermission && documents.some(({ status }) => status !== "published");
3297
+ const setListViewSelectedDocuments = useTable("publishAction", (state) => state.selectRow);
3298
+ const refetchList = () => {
3299
+ contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3300
+ };
3301
+ if (!showPublishButton)
3302
+ return null;
3303
+ return {
3304
+ actionType: "publish",
3305
+ variant: "tertiary",
3306
+ label: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" }),
3307
+ dialog: {
3308
+ type: "modal",
3309
+ title: formatMessage({
3310
+ id: getTranslation("containers.ListPage.selectedEntriesModal.title"),
3311
+ defaultMessage: "Publish entries"
3312
+ }),
3313
+ content: ({ onClose }) => {
3314
+ return /* @__PURE__ */ jsx(Table.Root, { rows: documents, defaultSelectedRows: documents, headers: TABLE_HEADERS, children: /* @__PURE__ */ jsx(
3315
+ SelectedEntriesModalContent,
3316
+ {
3317
+ listViewSelectedEntries: documents,
3318
+ toggleModal: () => {
3319
+ onClose();
3320
+ refetchList();
3321
+ },
3322
+ setListViewSelectedDocuments,
3323
+ model
3324
+ }
3325
+ ) });
3326
+ },
3327
+ onClose: () => {
3328
+ refetchList();
3329
+ }
3330
+ }
3331
+ };
3332
+ };
3333
+ const BulkActionsRenderer = () => {
3334
+ const plugins = useStrapiApp("BulkActionsRenderer", (state) => state.plugins);
3335
+ const { model, collectionType } = useDoc();
3336
+ const { selectedRows } = useTable("BulkActionsRenderer", (state) => state);
3337
+ return /* @__PURE__ */ jsx(Flex, { gap: 2, children: /* @__PURE__ */ jsx(
3338
+ DescriptionComponentRenderer,
3339
+ {
3340
+ props: {
3341
+ model,
3342
+ collectionType,
3343
+ documents: selectedRows
3344
+ },
3345
+ descriptions: plugins["content-manager"].apis.getBulkActions(),
3346
+ children: (actions2) => actions2.map((action) => /* @__PURE__ */ jsx(DocumentActionButton, { ...action }, action.id))
3347
+ }
3348
+ ) });
3349
+ };
3350
+ const DeleteAction = ({ documents, model }) => {
3351
+ const { formatMessage } = useIntl();
3352
+ const { schema: contentType } = useDoc();
3353
+ const selectRow = useTable("DeleteAction", (state) => state.selectRow);
3354
+ const hasI18nEnabled = Boolean(contentType?.pluginOptions?.i18n);
3355
+ const [{ query }] = useQueryParams();
3356
+ const params = React.useMemo(() => buildValidParams(query), [query]);
2651
3357
  const hasDeletePermission = useDocumentRBAC("deleteAction", (state) => state.canDelete);
2652
3358
  const { deleteMany: bulkDeleteAction } = useDocumentActions();
2653
3359
  const documentIds = documents.map(({ documentId }) => documentId);
@@ -2673,6 +3379,7 @@ const DeleteAction = ({ documents, model }) => {
2673
3379
  defaultMessage: "Confirmation"
2674
3380
  }),
2675
3381
  content: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3382
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
2676
3383
  /* @__PURE__ */ jsx(Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
2677
3384
  id: "popUpWarning.bodyMessage.contentType.delete.all",
2678
3385
  defaultMessage: "Are you sure you want to delete these entries?"
@@ -2709,7 +3416,7 @@ const UnpublishAction = ({ documents, model }) => {
2709
3416
  selectRow([]);
2710
3417
  }
2711
3418
  };
2712
- const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published");
3419
+ const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
2713
3420
  if (!showUnpublishButton)
2714
3421
  return null;
2715
3422
  return {
@@ -2722,6 +3429,7 @@ const UnpublishAction = ({ documents, model }) => {
2722
3429
  defaultMessage: "Confirmation"
2723
3430
  }),
2724
3431
  content: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3432
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }) }),
2725
3433
  /* @__PURE__ */ jsx(Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
2726
3434
  id: "popUpWarning.bodyMessage.contentType.unpublish.all",
2727
3435
  defaultMessage: "Are you sure you want to unpublish these entries?"
@@ -2746,7 +3454,7 @@ const UnpublishAction = ({ documents, model }) => {
2746
3454
  };
2747
3455
  UnpublishAction.type = "unpublish";
2748
3456
  const Emphasis = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", textColor: "danger500", children: chunks });
2749
- const DEFAULT_BULK_ACTIONS = [UnpublishAction, DeleteAction];
3457
+ const DEFAULT_BULK_ACTIONS = [PublishAction, UnpublishAction, DeleteAction];
2750
3458
  const AutoCloneFailureModalBody = ({ prohibitedFields }) => {
2751
3459
  const { formatMessage } = useIntl();
2752
3460
  const getDefaultErrorMessage = (reason) => {
@@ -2898,503 +3606,201 @@ const CloneAction = ({ model, documentId }) => {
2898
3606
  if (!documentId) {
2899
3607
  console.error(
2900
3608
  "You're trying to clone a document in the table without an id, this is likely a bug with Strapi. Please open an issue."
2901
- );
2902
- toggleNotification({
2903
- message: formatMessage({
2904
- id: "content-manager.actions.clone.error",
2905
- defaultMessage: "An error occurred while trying to clone the document."
2906
- }),
2907
- type: "danger"
2908
- });
2909
- return;
2910
- }
2911
- const res = await autoClone({ model, sourceId: documentId });
2912
- if ("data" in res) {
2913
- navigate(res.data.documentId);
2914
- return true;
2915
- }
2916
- if (isBaseQueryError(res.error) && res.error.details && typeof res.error.details === "object" && "prohibitedFields" in res.error.details && Array.isArray(res.error.details.prohibitedFields)) {
2917
- const prohibitedFields2 = res.error.details.prohibitedFields;
2918
- setProhibitedFields(prohibitedFields2);
2919
- }
2920
- },
2921
- dialog: {
2922
- type: "modal",
2923
- title: formatMessage({
2924
- id: "content-manager.containers.list.autoCloneModal.header",
2925
- defaultMessage: "Duplicate"
2926
- }),
2927
- content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
2928
- footer: ({ onClose }) => {
2929
- return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
2930
- /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
2931
- id: "cancel",
2932
- defaultMessage: "Cancel"
2933
- }) }),
2934
- /* @__PURE__ */ jsx(
2935
- LinkButton,
2936
- {
2937
- tag: NavLink,
2938
- to: {
2939
- pathname: `clone/${documentId}`
2940
- },
2941
- children: formatMessage({
2942
- id: "content-manager.containers.list.autoCloneModal.create",
2943
- defaultMessage: "Create"
2944
- })
2945
- }
2946
- )
2947
- ] });
2948
- }
2949
- }
2950
- };
2951
- };
2952
- CloneAction.type = "clone";
2953
- const StyledDuplicate = styled(Duplicate)`
2954
- path {
2955
- fill: currentColor;
2956
- }
2957
- `;
2958
- const DEFAULT_TABLE_ROW_ACTIONS = [EditAction, CloneAction];
2959
- class ContentManagerPlugin {
2960
- /**
2961
- * The following properties are the stored ones provided by any plugins registering with
2962
- * the content-manager. The function calls however, need to be called at runtime in the
2963
- * application, so instead we collate them and run them later with the complete list incl.
2964
- * ones already registered & the context of the view.
2965
- */
2966
- bulkActions = [...DEFAULT_BULK_ACTIONS];
2967
- documentActions = [
2968
- ...DEFAULT_ACTIONS,
2969
- ...DEFAULT_TABLE_ROW_ACTIONS,
2970
- ...DEFAULT_HEADER_ACTIONS,
2971
- HistoryAction
2972
- ];
2973
- editViewSidePanels = [ActionsPanel];
2974
- headerActions = [];
2975
- constructor() {
2976
- }
2977
- addEditViewSidePanel(panels) {
2978
- if (Array.isArray(panels)) {
2979
- this.editViewSidePanels = [...this.editViewSidePanels, ...panels];
2980
- } else if (typeof panels === "function") {
2981
- this.editViewSidePanels = panels(this.editViewSidePanels);
2982
- } else {
2983
- throw new Error(
2984
- `Expected the \`panels\` passed to \`addEditViewSidePanel\` to be an array or a function, but received ${getPrintableType(
2985
- panels
2986
- )}`
2987
- );
2988
- }
2989
- }
2990
- addDocumentAction(actions2) {
2991
- if (Array.isArray(actions2)) {
2992
- this.documentActions = [...this.documentActions, ...actions2];
2993
- } else if (typeof actions2 === "function") {
2994
- this.documentActions = actions2(this.documentActions);
2995
- } else {
2996
- throw new Error(
2997
- `Expected the \`actions\` passed to \`addDocumentAction\` to be an array or a function, but received ${getPrintableType(
2998
- actions2
2999
- )}`
3000
- );
3001
- }
3002
- }
3003
- addDocumentHeaderAction(actions2) {
3004
- if (Array.isArray(actions2)) {
3005
- this.headerActions = [...this.headerActions, ...actions2];
3006
- } else if (typeof actions2 === "function") {
3007
- this.headerActions = actions2(this.headerActions);
3008
- } else {
3009
- throw new Error(
3010
- `Expected the \`actions\` passed to \`addDocumentHeaderAction\` to be an array or a function, but received ${getPrintableType(
3011
- actions2
3012
- )}`
3013
- );
3014
- }
3015
- }
3016
- addBulkAction(actions2) {
3017
- if (Array.isArray(actions2)) {
3018
- this.bulkActions = [...this.bulkActions, ...actions2];
3019
- } else if (typeof actions2 === "function") {
3020
- this.bulkActions = actions2(this.bulkActions);
3021
- } else {
3022
- throw new Error(
3023
- `Expected the \`actions\` passed to \`addBulkAction\` to be an array or a function, but received ${getPrintableType(
3024
- actions2
3025
- )}`
3026
- );
3027
- }
3028
- }
3029
- get config() {
3030
- return {
3031
- id: PLUGIN_ID,
3032
- name: "Content Manager",
3033
- injectionZones: INJECTION_ZONES,
3034
- apis: {
3035
- addBulkAction: this.addBulkAction.bind(this),
3036
- addDocumentAction: this.addDocumentAction.bind(this),
3037
- addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3038
- addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3039
- getBulkActions: () => this.bulkActions,
3040
- getDocumentActions: () => this.documentActions,
3041
- getEditViewSidePanels: () => this.editViewSidePanels,
3042
- getHeaderActions: () => this.headerActions
3043
- }
3044
- };
3045
- }
3046
- }
3047
- const getPrintableType = (value) => {
3048
- const nativeType = typeof value;
3049
- if (nativeType === "object") {
3050
- if (value === null)
3051
- return "null";
3052
- if (Array.isArray(value))
3053
- return "array";
3054
- if (value instanceof Object && value.constructor.name !== "Object") {
3055
- return value.constructor.name;
3056
- }
3057
- }
3058
- return nativeType;
3059
- };
3060
- const initialState = {
3061
- collectionTypeLinks: [],
3062
- components: [],
3063
- fieldSizes: {},
3064
- models: [],
3065
- singleTypeLinks: [],
3066
- isLoading: true
3067
- };
3068
- const appSlice = createSlice({
3069
- name: "app",
3070
- initialState,
3071
- reducers: {
3072
- setInitialData(state, action) {
3073
- const {
3074
- authorizedCollectionTypeLinks,
3075
- authorizedSingleTypeLinks,
3076
- components,
3077
- contentTypeSchemas,
3078
- fieldSizes
3079
- } = action.payload;
3080
- state.collectionTypeLinks = authorizedCollectionTypeLinks.filter(
3081
- ({ isDisplayed }) => isDisplayed
3082
- );
3083
- state.singleTypeLinks = authorizedSingleTypeLinks.filter(({ isDisplayed }) => isDisplayed);
3084
- state.components = components;
3085
- state.models = contentTypeSchemas;
3086
- state.fieldSizes = fieldSizes;
3087
- state.isLoading = false;
3088
- }
3089
- }
3090
- });
3091
- const { actions, reducer: reducer$1 } = appSlice;
3092
- const { setInitialData } = actions;
3093
- const reducer = combineReducers({
3094
- app: reducer$1
3095
- });
3096
- const HOOKS = {
3097
- /**
3098
- * Hook that allows to mutate the displayed headers of the list view table
3099
- * @constant
3100
- * @type {string}
3101
- */
3102
- INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
3103
- /**
3104
- * Hook that allows to mutate the CM's collection types links pre-set filters
3105
- * @constant
3106
- * @type {string}
3107
- */
3108
- MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
3109
- /**
3110
- * Hook that allows to mutate the CM's edit view layout
3111
- * @constant
3112
- * @type {string}
3113
- */
3114
- MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
3115
- /**
3116
- * Hook that allows to mutate the CM's single types links pre-set filters
3117
- * @constant
3118
- * @type {string}
3119
- */
3120
- MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
3121
- };
3122
- const contentTypesApi = contentManagerApi.injectEndpoints({
3123
- endpoints: (builder) => ({
3124
- getContentTypeConfiguration: builder.query({
3125
- query: (uid) => ({
3126
- url: `/content-manager/content-types/${uid}/configuration`,
3127
- method: "GET"
3128
- }),
3129
- transformResponse: (response) => response.data,
3130
- providesTags: (_result, _error, uid) => [
3131
- { type: "ContentTypesConfiguration", id: uid },
3132
- { type: "ContentTypeSettings", id: "LIST" }
3133
- ]
3134
- }),
3135
- getAllContentTypeSettings: builder.query({
3136
- query: () => "/content-manager/content-types-settings",
3137
- transformResponse: (response) => response.data,
3138
- providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
3139
- }),
3140
- updateContentTypeConfiguration: builder.mutation({
3141
- query: ({ uid, ...body }) => ({
3142
- url: `/content-manager/content-types/${uid}/configuration`,
3143
- method: "PUT",
3144
- data: body
3145
- }),
3146
- transformResponse: (response) => response.data,
3147
- invalidatesTags: (_result, _error, { uid }) => [
3148
- { type: "ContentTypesConfiguration", id: uid },
3149
- { type: "ContentTypeSettings", id: "LIST" },
3150
- // Is this necessary?
3151
- { type: "InitialData" }
3152
- ]
3153
- })
3154
- })
3155
- });
3156
- const {
3157
- useGetContentTypeConfigurationQuery,
3158
- useGetAllContentTypeSettingsQuery,
3159
- useUpdateContentTypeConfigurationMutation
3160
- } = contentTypesApi;
3161
- const checkIfAttributeIsDisplayable = (attribute) => {
3162
- const { type } = attribute;
3163
- if (type === "relation") {
3164
- return !attribute.relation.toLowerCase().includes("morph");
3165
- }
3166
- return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
3167
- };
3168
- const getMainField = (attribute, mainFieldName, { schemas, components }) => {
3169
- if (!mainFieldName) {
3170
- return void 0;
3171
- }
3172
- const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
3173
- // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
3174
- schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
3175
- );
3176
- return {
3177
- name: mainFieldName,
3178
- type: mainFieldType ?? "string"
3179
- };
3180
- };
3181
- const DEFAULT_SETTINGS = {
3182
- bulkable: false,
3183
- filterable: false,
3184
- searchable: false,
3185
- pagination: false,
3186
- defaultSortBy: "",
3187
- defaultSortOrder: "asc",
3188
- mainField: "id",
3189
- pageSize: 10
3190
- };
3191
- const useDocumentLayout = (model) => {
3192
- const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
3193
- const [{ query }] = useQueryParams();
3194
- const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
3195
- const { toggleNotification } = useNotification();
3196
- const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
3197
- const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
3198
- const {
3199
- data,
3200
- isLoading: isLoadingConfigs,
3201
- error,
3202
- isFetching: isFetchingConfigs
3203
- } = useGetContentTypeConfigurationQuery(model);
3204
- const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
3205
- React.useEffect(() => {
3206
- if (error) {
3207
- toggleNotification({
3208
- type: "danger",
3209
- message: formatAPIError(error)
3210
- });
3211
- }
3212
- }, [error, formatAPIError, toggleNotification]);
3213
- const editLayout = React.useMemo(
3214
- () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
3215
- layout: [],
3216
- components: {},
3217
- metadatas: {},
3218
- options: {},
3219
- settings: DEFAULT_SETTINGS
3220
- },
3221
- [data, isLoading, schemas, schema, components]
3222
- );
3223
- const listLayout = React.useMemo(() => {
3224
- return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
3225
- layout: [],
3226
- metadatas: {},
3227
- options: {},
3228
- settings: DEFAULT_SETTINGS
3229
- };
3230
- }, [data, isLoading, schemas, schema, components]);
3231
- const { layout: edit } = React.useMemo(
3232
- () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
3233
- layout: editLayout,
3234
- query
3235
- }),
3236
- [editLayout, query, runHookWaterfall]
3237
- );
3238
- return {
3239
- error,
3240
- isLoading,
3241
- edit,
3242
- list: listLayout
3243
- };
3244
- };
3245
- const useDocLayout = () => {
3246
- const { model } = useDoc();
3247
- return useDocumentLayout(model);
3248
- };
3249
- const formatEditLayout = (data, {
3250
- schemas,
3251
- schema,
3252
- components
3253
- }) => {
3254
- let currentPanelIndex = 0;
3255
- const panelledEditAttributes = convertEditLayoutToFieldLayouts(
3256
- data.contentType.layouts.edit,
3257
- schema?.attributes,
3258
- data.contentType.metadatas,
3259
- { configurations: data.components, schemas: components },
3260
- schemas
3261
- ).reduce((panels, row) => {
3262
- if (row.some((field) => field.type === "dynamiczone")) {
3263
- panels.push([row]);
3264
- currentPanelIndex += 2;
3265
- } else {
3266
- if (!panels[currentPanelIndex]) {
3267
- panels.push([]);
3268
- }
3269
- panels[currentPanelIndex].push(row);
3270
- }
3271
- return panels;
3272
- }, []);
3273
- const componentEditAttributes = Object.entries(data.components).reduce(
3274
- (acc, [uid, configuration]) => {
3275
- acc[uid] = {
3276
- layout: convertEditLayoutToFieldLayouts(
3277
- configuration.layouts.edit,
3278
- components[uid].attributes,
3279
- configuration.metadatas
3280
- ),
3281
- settings: {
3282
- ...configuration.settings,
3283
- icon: components[uid].info.icon,
3284
- displayName: components[uid].info.displayName
3285
- }
3286
- };
3287
- return acc;
3288
- },
3289
- {}
3290
- );
3291
- const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
3292
- (acc, [attribute, metadata]) => {
3293
- return {
3294
- ...acc,
3295
- [attribute]: metadata.edit
3296
- };
3297
- },
3298
- {}
3299
- );
3300
- return {
3301
- layout: panelledEditAttributes,
3302
- components: componentEditAttributes,
3303
- metadatas: editMetadatas,
3304
- settings: {
3305
- ...data.contentType.settings,
3306
- displayName: schema?.info.displayName
3307
- },
3308
- options: {
3309
- ...schema?.options,
3310
- ...schema?.pluginOptions,
3311
- ...data.contentType.options
3312
- }
3313
- };
3314
- };
3315
- const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
3316
- return rows.map(
3317
- (row) => row.map((field) => {
3318
- const attribute = attributes[field.name];
3319
- if (!attribute) {
3320
- return null;
3609
+ );
3610
+ toggleNotification({
3611
+ message: formatMessage({
3612
+ id: "content-manager.actions.clone.error",
3613
+ defaultMessage: "An error occurred while trying to clone the document."
3614
+ }),
3615
+ type: "danger"
3616
+ });
3617
+ return;
3618
+ }
3619
+ const res = await autoClone({ model, sourceId: documentId });
3620
+ if ("data" in res) {
3621
+ navigate(res.data.documentId);
3622
+ return true;
3623
+ }
3624
+ if (isBaseQueryError(res.error) && res.error.details && typeof res.error.details === "object" && "prohibitedFields" in res.error.details && Array.isArray(res.error.details.prohibitedFields)) {
3625
+ const prohibitedFields2 = res.error.details.prohibitedFields;
3626
+ setProhibitedFields(prohibitedFields2);
3321
3627
  }
3322
- const { edit: metadata } = metadatas[field.name];
3323
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
3324
- return {
3325
- attribute,
3326
- disabled: !metadata.editable,
3327
- hint: metadata.description,
3328
- label: metadata.label ?? "",
3329
- name: field.name,
3330
- // @ts-expect-error – mainField does exist on the metadata for a relation.
3331
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
3332
- schemas,
3333
- components: components?.schemas ?? {}
3334
- }),
3335
- placeholder: metadata.placeholder ?? "",
3336
- required: attribute.required ?? false,
3337
- size: field.size,
3338
- unique: "unique" in attribute ? attribute.unique : false,
3339
- visible: metadata.visible ?? true,
3340
- type: attribute.type
3341
- };
3342
- }).filter((field) => field !== null)
3343
- );
3344
- };
3345
- const formatListLayout = (data, {
3346
- schemas,
3347
- schema,
3348
- components
3349
- }) => {
3350
- const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
3351
- (acc, [attribute, metadata]) => {
3352
- return {
3353
- ...acc,
3354
- [attribute]: metadata.list
3355
- };
3356
3628
  },
3357
- {}
3358
- );
3359
- const listAttributes = convertListLayoutToFieldLayouts(
3360
- data.contentType.layouts.list,
3361
- schema?.attributes,
3362
- listMetadatas,
3363
- { configurations: data.components, schemas: components },
3364
- schemas
3365
- );
3366
- return {
3367
- layout: listAttributes,
3368
- settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
3369
- metadatas: listMetadatas,
3370
- options: {
3371
- ...schema?.options,
3372
- ...schema?.pluginOptions,
3373
- ...data.contentType.options
3629
+ dialog: {
3630
+ type: "modal",
3631
+ title: formatMessage({
3632
+ id: "content-manager.containers.list.autoCloneModal.header",
3633
+ defaultMessage: "Duplicate"
3634
+ }),
3635
+ content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3636
+ footer: ({ onClose }) => {
3637
+ return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3638
+ /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3639
+ id: "cancel",
3640
+ defaultMessage: "Cancel"
3641
+ }) }),
3642
+ /* @__PURE__ */ jsx(
3643
+ LinkButton,
3644
+ {
3645
+ tag: NavLink,
3646
+ to: {
3647
+ pathname: `clone/${documentId}`
3648
+ },
3649
+ children: formatMessage({
3650
+ id: "content-manager.containers.list.autoCloneModal.create",
3651
+ defaultMessage: "Create"
3652
+ })
3653
+ }
3654
+ )
3655
+ ] });
3656
+ }
3374
3657
  }
3375
3658
  };
3376
3659
  };
3377
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
3378
- return columns.map((name) => {
3379
- const attribute = attributes[name];
3380
- if (!attribute) {
3381
- return null;
3660
+ CloneAction.type = "clone";
3661
+ const StyledDuplicate = styled(Duplicate)`
3662
+ path {
3663
+ fill: currentColor;
3664
+ }
3665
+ `;
3666
+ const DEFAULT_TABLE_ROW_ACTIONS = [EditAction, CloneAction];
3667
+ class ContentManagerPlugin {
3668
+ /**
3669
+ * The following properties are the stored ones provided by any plugins registering with
3670
+ * the content-manager. The function calls however, need to be called at runtime in the
3671
+ * application, so instead we collate them and run them later with the complete list incl.
3672
+ * ones already registered & the context of the view.
3673
+ */
3674
+ bulkActions = [...DEFAULT_BULK_ACTIONS];
3675
+ documentActions = [
3676
+ ...DEFAULT_ACTIONS,
3677
+ ...DEFAULT_TABLE_ROW_ACTIONS,
3678
+ ...DEFAULT_HEADER_ACTIONS,
3679
+ HistoryAction
3680
+ ];
3681
+ editViewSidePanels = [ActionsPanel];
3682
+ headerActions = [];
3683
+ constructor() {
3684
+ }
3685
+ addEditViewSidePanel(panels) {
3686
+ if (Array.isArray(panels)) {
3687
+ this.editViewSidePanels = [...this.editViewSidePanels, ...panels];
3688
+ } else if (typeof panels === "function") {
3689
+ this.editViewSidePanels = panels(this.editViewSidePanels);
3690
+ } else {
3691
+ throw new Error(
3692
+ `Expected the \`panels\` passed to \`addEditViewSidePanel\` to be an array or a function, but received ${getPrintableType(
3693
+ panels
3694
+ )}`
3695
+ );
3382
3696
  }
3383
- const metadata = metadatas[name];
3384
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
3697
+ }
3698
+ addDocumentAction(actions2) {
3699
+ if (Array.isArray(actions2)) {
3700
+ this.documentActions = [...this.documentActions, ...actions2];
3701
+ } else if (typeof actions2 === "function") {
3702
+ this.documentActions = actions2(this.documentActions);
3703
+ } else {
3704
+ throw new Error(
3705
+ `Expected the \`actions\` passed to \`addDocumentAction\` to be an array or a function, but received ${getPrintableType(
3706
+ actions2
3707
+ )}`
3708
+ );
3709
+ }
3710
+ }
3711
+ addDocumentHeaderAction(actions2) {
3712
+ if (Array.isArray(actions2)) {
3713
+ this.headerActions = [...this.headerActions, ...actions2];
3714
+ } else if (typeof actions2 === "function") {
3715
+ this.headerActions = actions2(this.headerActions);
3716
+ } else {
3717
+ throw new Error(
3718
+ `Expected the \`actions\` passed to \`addDocumentHeaderAction\` to be an array or a function, but received ${getPrintableType(
3719
+ actions2
3720
+ )}`
3721
+ );
3722
+ }
3723
+ }
3724
+ addBulkAction(actions2) {
3725
+ if (Array.isArray(actions2)) {
3726
+ this.bulkActions = [...this.bulkActions, ...actions2];
3727
+ } else if (typeof actions2 === "function") {
3728
+ this.bulkActions = actions2(this.bulkActions);
3729
+ } else {
3730
+ throw new Error(
3731
+ `Expected the \`actions\` passed to \`addBulkAction\` to be an array or a function, but received ${getPrintableType(
3732
+ actions2
3733
+ )}`
3734
+ );
3735
+ }
3736
+ }
3737
+ get config() {
3385
3738
  return {
3386
- attribute,
3387
- label: metadata.label ?? "",
3388
- mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
3389
- schemas,
3390
- components: components?.schemas ?? {}
3391
- }),
3392
- name,
3393
- searchable: metadata.searchable ?? true,
3394
- sortable: metadata.sortable ?? true
3739
+ id: PLUGIN_ID,
3740
+ name: "Content Manager",
3741
+ injectionZones: INJECTION_ZONES,
3742
+ apis: {
3743
+ addBulkAction: this.addBulkAction.bind(this),
3744
+ addDocumentAction: this.addDocumentAction.bind(this),
3745
+ addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3746
+ addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3747
+ getBulkActions: () => this.bulkActions,
3748
+ getDocumentActions: () => this.documentActions,
3749
+ getEditViewSidePanels: () => this.editViewSidePanels,
3750
+ getHeaderActions: () => this.headerActions
3751
+ }
3395
3752
  };
3396
- }).filter((field) => field !== null);
3753
+ }
3754
+ }
3755
+ const getPrintableType = (value) => {
3756
+ const nativeType = typeof value;
3757
+ if (nativeType === "object") {
3758
+ if (value === null)
3759
+ return "null";
3760
+ if (Array.isArray(value))
3761
+ return "array";
3762
+ if (value instanceof Object && value.constructor.name !== "Object") {
3763
+ return value.constructor.name;
3764
+ }
3765
+ }
3766
+ return nativeType;
3767
+ };
3768
+ const initialState = {
3769
+ collectionTypeLinks: [],
3770
+ components: [],
3771
+ fieldSizes: {},
3772
+ models: [],
3773
+ singleTypeLinks: [],
3774
+ isLoading: true
3397
3775
  };
3776
+ const appSlice = createSlice({
3777
+ name: "app",
3778
+ initialState,
3779
+ reducers: {
3780
+ setInitialData(state, action) {
3781
+ const {
3782
+ authorizedCollectionTypeLinks,
3783
+ authorizedSingleTypeLinks,
3784
+ components,
3785
+ contentTypeSchemas,
3786
+ fieldSizes
3787
+ } = action.payload;
3788
+ state.collectionTypeLinks = authorizedCollectionTypeLinks.filter(
3789
+ ({ isDisplayed }) => isDisplayed
3790
+ );
3791
+ state.singleTypeLinks = authorizedSingleTypeLinks.filter(({ isDisplayed }) => isDisplayed);
3792
+ state.components = components;
3793
+ state.models = contentTypeSchemas;
3794
+ state.fieldSizes = fieldSizes;
3795
+ state.isLoading = false;
3796
+ }
3797
+ }
3798
+ });
3799
+ const { actions, reducer: reducer$1 } = appSlice;
3800
+ const { setInitialData } = actions;
3801
+ const reducer = combineReducers({
3802
+ app: reducer$1
3803
+ });
3398
3804
  const index = {
3399
3805
  register(app) {
3400
3806
  const cm = new ContentManagerPlugin();
@@ -3409,15 +3815,24 @@ const index = {
3409
3815
  defaultMessage: "Content Manager"
3410
3816
  },
3411
3817
  permissions: [],
3412
- Component: () => import("./layout-BinjszSQ.mjs").then((mod) => ({ default: mod.Layout })),
3413
3818
  position: 1
3414
3819
  });
3820
+ app.router.addRoute({
3821
+ path: "content-manager/*",
3822
+ lazy: async () => {
3823
+ const { Layout } = await import("./layout-DHe2GdT4.mjs");
3824
+ return {
3825
+ Component: Layout
3826
+ };
3827
+ },
3828
+ children: routes
3829
+ });
3415
3830
  app.registerPlugin(cm.config);
3416
3831
  },
3417
3832
  async registerTrads({ locales }) {
3418
3833
  const importedTrads = await Promise.all(
3419
3834
  locales.map((locale) => {
3420
- 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-GCOTL6jR.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 }) => {
3835
+ 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 }) => {
3421
3836
  return {
3422
3837
  data: prefixPluginTranslations(data, PLUGIN_ID),
3423
3838
  locale
@@ -3438,43 +3853,42 @@ export {
3438
3853
  BulkActionsRenderer as B,
3439
3854
  COLLECTION_TYPES as C,
3440
3855
  DocumentStatus as D,
3441
- extractContentTypeComponents as E,
3442
- DEFAULT_SETTINGS as F,
3443
- convertEditLayoutToFieldLayouts as G,
3856
+ DEFAULT_SETTINGS as E,
3857
+ convertEditLayoutToFieldLayouts as F,
3858
+ useDocument as G,
3444
3859
  HOOKS as H,
3445
3860
  InjectionZone as I,
3446
- useDocument as J,
3447
- index as K,
3448
- useDocumentActions as L,
3861
+ index as J,
3862
+ useDocumentActions as K,
3449
3863
  Panels as P,
3450
3864
  RelativeTime as R,
3451
3865
  SINGLE_TYPES as S,
3452
3866
  TableActions as T,
3453
- useGetAllContentTypeSettingsQuery as a,
3454
- useDoc as b,
3455
- buildValidParams as c,
3456
- contentManagerApi as d,
3457
- useDocumentRBAC as e,
3458
- useDocumentLayout as f,
3867
+ useGetInitialDataQuery as a,
3868
+ useGetAllContentTypeSettingsQuery as b,
3869
+ useDoc as c,
3870
+ buildValidParams as d,
3871
+ contentManagerApi as e,
3872
+ useDocumentRBAC as f,
3459
3873
  getTranslation as g,
3460
- createYupSchema as h,
3461
- Header as i,
3462
- PERMISSIONS as j,
3463
- DocumentRBAC as k,
3464
- DOCUMENT_META_FIELDS as l,
3465
- useDocLayout as m,
3466
- useContentTypeSchema as n,
3874
+ useDocumentLayout as h,
3875
+ createYupSchema as i,
3876
+ Header as j,
3877
+ PERMISSIONS as k,
3878
+ DocumentRBAC as l,
3879
+ DOCUMENT_META_FIELDS as m,
3880
+ useDocLayout as n,
3467
3881
  useGetContentTypeConfigurationQuery as o,
3468
3882
  CREATOR_FIELDS as p,
3469
3883
  getMainField as q,
3470
- routes as r,
3884
+ getDisplayName as r,
3471
3885
  setInitialData as s,
3472
- getDisplayName as t,
3473
- useGetInitialDataQuery as u,
3474
- checkIfAttributeIsDisplayable as v,
3475
- useGetAllDocumentsQuery as w,
3476
- convertListLayoutToFieldLayouts as x,
3477
- capitalise as y,
3478
- useUpdateContentTypeConfigurationMutation as z
3479
- };
3480
- //# sourceMappingURL=index-BaGHmIir.mjs.map
3886
+ checkIfAttributeIsDisplayable as t,
3887
+ useContentTypeSchema as u,
3888
+ useGetAllDocumentsQuery as v,
3889
+ convertListLayoutToFieldLayouts as w,
3890
+ capitalise as x,
3891
+ useUpdateContentTypeConfigurationMutation as y,
3892
+ extractContentTypeComponents as z
3893
+ };
3894
+ //# sourceMappingURL=index-DIQ7Io-l.mjs.map