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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-CuWgXugY.mjs → ComponentConfigurationPage-BMajAl1u.mjs} +3 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-CuWgXugY.mjs.map → ComponentConfigurationPage-BMajAl1u.mjs.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-by0e_kNd.js → ComponentConfigurationPage-y_7iLdmB.js} +3 -3
  4. package/dist/_chunks/{ComponentConfigurationPage-by0e_kNd.js.map → ComponentConfigurationPage-y_7iLdmB.js.map} +1 -1
  5. package/dist/_chunks/{EditConfigurationPage-CqBeCPGH.js → EditConfigurationPage-CPVB8Uqc.js} +3 -3
  6. package/dist/_chunks/{EditConfigurationPage-CqBeCPGH.js.map → EditConfigurationPage-CPVB8Uqc.js.map} +1 -1
  7. package/dist/_chunks/{EditConfigurationPage-DbI4KMyz.mjs → EditConfigurationPage-CcOoD26O.mjs} +3 -3
  8. package/dist/_chunks/{EditConfigurationPage-DbI4KMyz.mjs.map → EditConfigurationPage-CcOoD26O.mjs.map} +1 -1
  9. package/dist/_chunks/{EditViewPage-ChgloMyO.js → EditViewPage-CTTDHKkQ.js} +3 -3
  10. package/dist/_chunks/{EditViewPage-ChgloMyO.js.map → EditViewPage-CTTDHKkQ.js.map} +1 -1
  11. package/dist/_chunks/{EditViewPage-dFPBya9U.mjs → EditViewPage-DWb0DE7R.mjs} +3 -3
  12. package/dist/_chunks/{EditViewPage-dFPBya9U.mjs.map → EditViewPage-DWb0DE7R.mjs.map} +1 -1
  13. package/dist/_chunks/{Field-dLk-vgLL.js → Field-C5Z1Ivdv.js} +3 -3
  14. package/dist/_chunks/{Field-dLk-vgLL.js.map → Field-C5Z1Ivdv.js.map} +1 -1
  15. package/dist/_chunks/{Field-C1nUKcdS.mjs → Field-DnStdvQw.mjs} +3 -3
  16. package/dist/_chunks/{Field-C1nUKcdS.mjs.map → Field-DnStdvQw.mjs.map} +1 -1
  17. package/dist/_chunks/{Form-CbXtmHC_.js → Form-B81OtW-k.js} +2 -2
  18. package/dist/_chunks/{Form-CbXtmHC_.js.map → Form-B81OtW-k.js.map} +1 -1
  19. package/dist/_chunks/{Form-DOlpi7Js.mjs → Form-DqGgE55Q.mjs} +2 -2
  20. package/dist/_chunks/{Form-DOlpi7Js.mjs.map → Form-DqGgE55Q.mjs.map} +1 -1
  21. package/dist/_chunks/{History-BjDfohBr.js → History-4NbOq2dX.js} +95 -13
  22. package/dist/_chunks/History-4NbOq2dX.js.map +1 -0
  23. package/dist/_chunks/{History-BFNUAiGc.mjs → History-DS6-HCYX.mjs} +95 -13
  24. package/dist/_chunks/History-DS6-HCYX.mjs.map +1 -0
  25. package/dist/_chunks/{ListConfigurationPage-IQBgWTaa.js → ListConfigurationPage-CpfstlYY.js} +2 -2
  26. package/dist/_chunks/{ListConfigurationPage-IQBgWTaa.js.map → ListConfigurationPage-CpfstlYY.js.map} +1 -1
  27. package/dist/_chunks/{ListConfigurationPage-DDi0KqFm.mjs → ListConfigurationPage-DQJJltko.mjs} +2 -2
  28. package/dist/_chunks/{ListConfigurationPage-DDi0KqFm.mjs.map → ListConfigurationPage-DQJJltko.mjs.map} +1 -1
  29. package/dist/_chunks/{ListViewPage-CZYGqlvF.js → ListViewPage-CA3I75m5.js} +16 -4
  30. package/dist/_chunks/ListViewPage-CA3I75m5.js.map +1 -0
  31. package/dist/_chunks/{ListViewPage-BPjljUsH.mjs → ListViewPage-nQrOQuVo.mjs} +17 -5
  32. package/dist/_chunks/ListViewPage-nQrOQuVo.mjs.map +1 -0
  33. package/dist/_chunks/{NoContentTypePage-DaWw67K-.mjs → NoContentTypePage-DbnHE22g.mjs} +2 -2
  34. package/dist/_chunks/{NoContentTypePage-DaWw67K-.mjs.map → NoContentTypePage-DbnHE22g.mjs.map} +1 -1
  35. package/dist/_chunks/{NoContentTypePage-BOAI6VZ1.js → NoContentTypePage-Dldu-_Mx.js} +2 -2
  36. package/dist/_chunks/{NoContentTypePage-BOAI6VZ1.js.map → NoContentTypePage-Dldu-_Mx.js.map} +1 -1
  37. package/dist/_chunks/{NoPermissionsPage-cYEtLc_e.js → NoPermissionsPage-CO2MK200.js} +2 -2
  38. package/dist/_chunks/{NoPermissionsPage-cYEtLc_e.js.map → NoPermissionsPage-CO2MK200.js.map} +1 -1
  39. package/dist/_chunks/{NoPermissionsPage-CZrJH00p.mjs → NoPermissionsPage-fOIkQM0v.mjs} +2 -2
  40. package/dist/_chunks/{NoPermissionsPage-CZrJH00p.mjs.map → NoPermissionsPage-fOIkQM0v.mjs.map} +1 -1
  41. package/dist/_chunks/{Relations-DTowyge2.mjs → Relations-BDRl99Ux.mjs} +4 -4
  42. package/dist/_chunks/{Relations-DTowyge2.mjs.map → Relations-BDRl99Ux.mjs.map} +1 -1
  43. package/dist/_chunks/{Relations-DU6B7irU.js → Relations-DG2jnOcr.js} +4 -4
  44. package/dist/_chunks/{Relations-DU6B7irU.js.map → Relations-DG2jnOcr.js.map} +1 -1
  45. package/dist/_chunks/{en-GCOTL6jR.mjs → en-Ux26r5pl.mjs} +5 -1
  46. package/dist/_chunks/{en-GCOTL6jR.mjs.map → en-Ux26r5pl.mjs.map} +1 -1
  47. package/dist/_chunks/{en-DTULi5-d.js → en-fbKQxLGn.js} +5 -1
  48. package/dist/_chunks/{en-DTULi5-d.js.map → en-fbKQxLGn.js.map} +1 -1
  49. package/dist/_chunks/{index-CCJeB7Rw.js → index-BZoNZMXL.js} +1290 -833
  50. package/dist/_chunks/index-BZoNZMXL.js.map +1 -0
  51. package/dist/_chunks/{index-BaGHmIir.mjs → index-Drt2DN7v.mjs} +1308 -851
  52. package/dist/_chunks/index-Drt2DN7v.mjs.map +1 -0
  53. package/dist/_chunks/{layout-BinjszSQ.mjs → layout-BzAbmoO6.mjs} +17 -12
  54. package/dist/_chunks/layout-BzAbmoO6.mjs.map +1 -0
  55. package/dist/_chunks/{layout-ni_L9kT1.js → layout-DEYBqgF1.js} +17 -12
  56. package/dist/_chunks/layout-DEYBqgF1.js.map +1 -0
  57. package/dist/_chunks/{relations-CeJAJc5I.js → relations-D0eZ4VWw.js} +2 -2
  58. package/dist/_chunks/{relations-CeJAJc5I.js.map → relations-D0eZ4VWw.js.map} +1 -1
  59. package/dist/_chunks/{relations-c91ji5eR.mjs → relations-D26zVRdi.mjs} +2 -2
  60. package/dist/_chunks/{relations-c91ji5eR.mjs.map → relations-D26zVRdi.mjs.map} +1 -1
  61. package/dist/admin/index.js +1 -1
  62. package/dist/admin/index.mjs +5 -5
  63. package/dist/admin/src/history/components/VersionInputRenderer.d.ts +1 -1
  64. package/dist/admin/src/hooks/useDocumentActions.d.ts +1 -1
  65. package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +14 -0
  66. package/dist/server/index.js +15 -11
  67. package/dist/server/index.js.map +1 -1
  68. package/dist/server/index.mjs +15 -11
  69. package/dist/server/index.mjs.map +1 -1
  70. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  71. package/dist/server/src/controllers/single-types.d.ts.map +1 -1
  72. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  73. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  74. package/package.json +6 -6
  75. package/dist/_chunks/History-BFNUAiGc.mjs.map +0 -1
  76. package/dist/_chunks/History-BjDfohBr.js.map +0 -1
  77. package/dist/_chunks/ListViewPage-BPjljUsH.mjs.map +0 -1
  78. package/dist/_chunks/ListViewPage-CZYGqlvF.js.map +0 -1
  79. package/dist/_chunks/index-BaGHmIir.mjs.map +0 -1
  80. package/dist/_chunks/index-CCJeB7Rw.js.map +0 -1
  81. package/dist/_chunks/layout-BinjszSQ.mjs.map +0 -1
  82. 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 { Menu, VisuallyHidden, Flex, Typography, Dialog, DialogBody, DialogFooter, Button, ModalLayout, ModalHeader, ModalBody, Box, Radio, Status, SingleSelect, SingleSelectOption, ModalFooter, 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";
@@ -927,12 +927,13 @@ const useDocumentActions = () => {
927
927
  );
928
928
  const [discardDocument] = useDiscardDocumentMutation();
929
929
  const discard = React.useCallback(
930
- async ({ collectionType, model, documentId }) => {
930
+ async ({ collectionType, model, documentId, params }) => {
931
931
  try {
932
932
  const res = await discardDocument({
933
933
  collectionType,
934
934
  model,
935
- documentId
935
+ documentId,
936
+ params
936
937
  });
937
938
  if ("error" in res) {
938
939
  toggleNotification({
@@ -1264,7 +1265,7 @@ const useDocumentActions = () => {
1264
1265
  };
1265
1266
  };
1266
1267
  const ProtectedHistoryPage = lazy(
1267
- () => import("./History-BFNUAiGc.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1268
+ () => import("./History-DS6-HCYX.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
1268
1269
  );
1269
1270
  const routes$1 = [
1270
1271
  {
@@ -1277,31 +1278,31 @@ const routes$1 = [
1277
1278
  }
1278
1279
  ];
1279
1280
  const ProtectedEditViewPage = lazy(
1280
- () => import("./EditViewPage-dFPBya9U.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1281
+ () => import("./EditViewPage-DWb0DE7R.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
1281
1282
  );
1282
1283
  const ProtectedListViewPage = lazy(
1283
- () => import("./ListViewPage-BPjljUsH.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1284
+ () => import("./ListViewPage-nQrOQuVo.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
1284
1285
  );
1285
1286
  const ProtectedListConfiguration = lazy(
1286
- () => import("./ListConfigurationPage-DDi0KqFm.mjs").then((mod) => ({
1287
+ () => import("./ListConfigurationPage-DQJJltko.mjs").then((mod) => ({
1287
1288
  default: mod.ProtectedListConfiguration
1288
1289
  }))
1289
1290
  );
1290
1291
  const ProtectedEditConfigurationPage = lazy(
1291
- () => import("./EditConfigurationPage-DbI4KMyz.mjs").then((mod) => ({
1292
+ () => import("./EditConfigurationPage-CcOoD26O.mjs").then((mod) => ({
1292
1293
  default: mod.ProtectedEditConfigurationPage
1293
1294
  }))
1294
1295
  );
1295
1296
  const ProtectedComponentConfigurationPage = lazy(
1296
- () => import("./ComponentConfigurationPage-CuWgXugY.mjs").then((mod) => ({
1297
+ () => import("./ComponentConfigurationPage-BMajAl1u.mjs").then((mod) => ({
1297
1298
  default: mod.ProtectedComponentConfigurationPage
1298
1299
  }))
1299
1300
  );
1300
1301
  const NoPermissions = lazy(
1301
- () => import("./NoPermissionsPage-CZrJH00p.mjs").then((mod) => ({ default: mod.NoPermissions }))
1302
+ () => import("./NoPermissionsPage-fOIkQM0v.mjs").then((mod) => ({ default: mod.NoPermissions }))
1302
1303
  );
1303
1304
  const NoContentType = lazy(
1304
- () => import("./NoContentTypePage-DaWw67K-.mjs").then((mod) => ({ default: mod.NoContentType }))
1305
+ () => import("./NoContentTypePage-DbnHE22g.mjs").then((mod) => ({ default: mod.NoContentType }))
1305
1306
  );
1306
1307
  const CollectionTypePages = () => {
1307
1308
  const { collectionType } = useParams();
@@ -1645,7 +1646,7 @@ const DocumentActionModal = ({
1645
1646
  )
1646
1647
  ] });
1647
1648
  };
1648
- const PublishAction = ({
1649
+ const PublishAction$1 = ({
1649
1650
  activeTab,
1650
1651
  documentId,
1651
1652
  model,
@@ -1730,7 +1731,7 @@ const PublishAction = ({
1730
1731
  }
1731
1732
  };
1732
1733
  };
1733
- PublishAction.type = "publish";
1734
+ PublishAction$1.type = "publish";
1734
1735
  const UpdateAction = ({
1735
1736
  activeTab,
1736
1737
  documentId,
@@ -2036,7 +2037,7 @@ const StyledCrossCircle = styled(CrossCircle)`
2036
2037
  fill: currentColor;
2037
2038
  }
2038
2039
  `;
2039
- const DEFAULT_ACTIONS = [PublishAction, UpdateAction, UnpublishAction$1, DiscardAction];
2040
+ const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
2040
2041
  const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
2041
2042
  const RelativeTime = React.forwardRef(
2042
2043
  ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
@@ -2491,910 +2492,1366 @@ const Panel = React.forwardRef(({ children, title }, ref) => {
2491
2492
  }
2492
2493
  );
2493
2494
  });
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
- ) });
2495
+ const HOOKS = {
2496
+ /**
2497
+ * Hook that allows to mutate the displayed headers of the list view table
2498
+ * @constant
2499
+ * @type {string}
2500
+ */
2501
+ INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
2502
+ /**
2503
+ * Hook that allows to mutate the CM's collection types links pre-set filters
2504
+ * @constant
2505
+ * @type {string}
2506
+ */
2507
+ MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
2508
+ /**
2509
+ * Hook that allows to mutate the CM's edit view layout
2510
+ * @constant
2511
+ * @type {string}
2512
+ */
2513
+ MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
2514
+ /**
2515
+ * Hook that allows to mutate the CM's single types links pre-set filters
2516
+ * @constant
2517
+ * @type {string}
2518
+ */
2519
+ MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
2510
2520
  };
2511
- const BulkActionAction = (action) => {
2512
- const [dialogId, setDialogId] = React.useState(null);
2521
+ const contentTypesApi = contentManagerApi.injectEndpoints({
2522
+ endpoints: (builder) => ({
2523
+ getContentTypeConfiguration: builder.query({
2524
+ query: (uid) => ({
2525
+ url: `/content-manager/content-types/${uid}/configuration`,
2526
+ method: "GET"
2527
+ }),
2528
+ transformResponse: (response) => response.data,
2529
+ providesTags: (_result, _error, uid) => [
2530
+ { type: "ContentTypesConfiguration", id: uid },
2531
+ { type: "ContentTypeSettings", id: "LIST" }
2532
+ ]
2533
+ }),
2534
+ getAllContentTypeSettings: builder.query({
2535
+ query: () => "/content-manager/content-types-settings",
2536
+ transformResponse: (response) => response.data,
2537
+ providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
2538
+ }),
2539
+ updateContentTypeConfiguration: builder.mutation({
2540
+ query: ({ uid, ...body }) => ({
2541
+ url: `/content-manager/content-types/${uid}/configuration`,
2542
+ method: "PUT",
2543
+ data: body
2544
+ }),
2545
+ transformResponse: (response) => response.data,
2546
+ invalidatesTags: (_result, _error, { uid }) => [
2547
+ { type: "ContentTypesConfiguration", id: uid },
2548
+ { type: "ContentTypeSettings", id: "LIST" },
2549
+ // Is this necessary?
2550
+ { type: "InitialData" }
2551
+ ]
2552
+ })
2553
+ })
2554
+ });
2555
+ const {
2556
+ useGetContentTypeConfigurationQuery,
2557
+ useGetAllContentTypeSettingsQuery,
2558
+ useUpdateContentTypeConfigurationMutation
2559
+ } = contentTypesApi;
2560
+ const checkIfAttributeIsDisplayable = (attribute) => {
2561
+ const { type } = attribute;
2562
+ if (type === "relation") {
2563
+ return !attribute.relation.toLowerCase().includes("morph");
2564
+ }
2565
+ return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
2566
+ };
2567
+ const getMainField = (attribute, mainFieldName, { schemas, components }) => {
2568
+ if (!mainFieldName) {
2569
+ return void 0;
2570
+ }
2571
+ const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
2572
+ // @ts-expect-error – `targetModel` does exist on the attribute for a relation.
2573
+ schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
2574
+ );
2575
+ return {
2576
+ name: mainFieldName,
2577
+ type: mainFieldType ?? "string"
2578
+ };
2579
+ };
2580
+ const DEFAULT_SETTINGS = {
2581
+ bulkable: false,
2582
+ filterable: false,
2583
+ searchable: false,
2584
+ pagination: false,
2585
+ defaultSortBy: "",
2586
+ defaultSortOrder: "asc",
2587
+ mainField: "id",
2588
+ pageSize: 10
2589
+ };
2590
+ const useDocumentLayout = (model) => {
2591
+ const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
2592
+ const [{ query }] = useQueryParams();
2593
+ const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
2513
2594
  const { toggleNotification } = useNotification();
2514
- const handleClick = (action2) => (e) => {
2515
- const { onClick, dialog, id } = action2;
2516
- if (onClick) {
2517
- onClick(e);
2595
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
2596
+ const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
2597
+ const {
2598
+ data,
2599
+ isLoading: isLoadingConfigs,
2600
+ error,
2601
+ isFetching: isFetchingConfigs
2602
+ } = useGetContentTypeConfigurationQuery(model);
2603
+ const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
2604
+ React.useEffect(() => {
2605
+ if (error) {
2606
+ toggleNotification({
2607
+ type: "danger",
2608
+ message: formatAPIError(error)
2609
+ });
2518
2610
  }
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
- }
2611
+ }, [error, formatAPIError, toggleNotification]);
2612
+ const editLayout = React.useMemo(
2613
+ () => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
2614
+ layout: [],
2615
+ components: {},
2616
+ metadatas: {},
2617
+ options: {},
2618
+ settings: DEFAULT_SETTINGS
2619
+ },
2620
+ [data, isLoading, schemas, schema, components]
2621
+ );
2622
+ const listLayout = React.useMemo(() => {
2623
+ return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
2624
+ layout: [],
2625
+ metadatas: {},
2626
+ options: {},
2627
+ settings: DEFAULT_SETTINGS
2628
+ };
2629
+ }, [data, isLoading, schemas, schema, components]);
2630
+ const { layout: edit } = React.useMemo(
2631
+ () => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
2632
+ layout: editLayout,
2633
+ query
2634
+ }),
2635
+ [editLayout, query, runHookWaterfall]
2636
+ );
2637
+ return {
2638
+ error,
2639
+ isLoading,
2640
+ edit,
2641
+ list: listLayout
2642
+ };
2643
+ };
2644
+ const useDocLayout = () => {
2645
+ const { model } = useDoc();
2646
+ return useDocumentLayout(model);
2647
+ };
2648
+ const formatEditLayout = (data, {
2649
+ schemas,
2650
+ schema,
2651
+ components
2652
+ }) => {
2653
+ let currentPanelIndex = 0;
2654
+ const panelledEditAttributes = convertEditLayoutToFieldLayouts(
2655
+ data.contentType.layouts.edit,
2656
+ schema?.attributes,
2657
+ data.contentType.metadatas,
2658
+ { configurations: data.components, schemas: components },
2659
+ schemas
2660
+ ).reduce((panels, row) => {
2661
+ if (row.some((field) => field.type === "dynamiczone")) {
2662
+ panels.push([row]);
2663
+ currentPanelIndex += 2;
2664
+ } else {
2665
+ if (!panels[currentPanelIndex]) {
2666
+ panels.push([]);
2535
2667
  }
2668
+ panels[currentPanelIndex].push(row);
2536
2669
  }
2537
- };
2538
- const handleClose = () => {
2539
- setDialogId(null);
2540
- if (action.dialog?.type === "modal" && action.dialog?.onClose) {
2541
- action.dialog.onClose();
2670
+ return panels;
2671
+ }, []);
2672
+ const componentEditAttributes = Object.entries(data.components).reduce(
2673
+ (acc, [uid, configuration]) => {
2674
+ acc[uid] = {
2675
+ layout: convertEditLayoutToFieldLayouts(
2676
+ configuration.layouts.edit,
2677
+ components[uid].attributes,
2678
+ configuration.metadatas
2679
+ ),
2680
+ settings: {
2681
+ ...configuration.settings,
2682
+ icon: components[uid].info.icon,
2683
+ displayName: components[uid].info.displayName
2684
+ }
2685
+ };
2686
+ return acc;
2687
+ },
2688
+ {}
2689
+ );
2690
+ const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
2691
+ (acc, [attribute, metadata]) => {
2692
+ return {
2693
+ ...acc,
2694
+ [attribute]: metadata.edit
2695
+ };
2696
+ },
2697
+ {}
2698
+ );
2699
+ return {
2700
+ layout: panelledEditAttributes,
2701
+ components: componentEditAttributes,
2702
+ metadatas: editMetadatas,
2703
+ settings: {
2704
+ ...data.contentType.settings,
2705
+ displayName: schema?.info.displayName
2706
+ },
2707
+ options: {
2708
+ ...schema?.options,
2709
+ ...schema?.pluginOptions,
2710
+ ...data.contentType.options
2542
2711
  }
2543
2712
  };
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
2713
+ };
2714
+ const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
2715
+ return rows.map(
2716
+ (row) => row.map((field) => {
2717
+ const attribute = attributes[field.name];
2718
+ if (!attribute) {
2719
+ return null;
2570
2720
  }
2571
- ) : null
2572
- ] });
2721
+ const { edit: metadata } = metadatas[field.name];
2722
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2723
+ return {
2724
+ attribute,
2725
+ disabled: !metadata.editable,
2726
+ hint: metadata.description,
2727
+ label: metadata.label ?? "",
2728
+ name: field.name,
2729
+ // @ts-expect-error – mainField does exist on the metadata for a relation.
2730
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2731
+ schemas,
2732
+ components: components?.schemas ?? {}
2733
+ }),
2734
+ placeholder: metadata.placeholder ?? "",
2735
+ required: attribute.required ?? false,
2736
+ size: field.size,
2737
+ unique: "unique" in attribute ? attribute.unique : false,
2738
+ visible: metadata.visible ?? true,
2739
+ type: attribute.type
2740
+ };
2741
+ }).filter((field) => field !== null)
2742
+ );
2573
2743
  };
2574
- const BulkActionConfirmDialog = ({
2575
- onClose,
2576
- onCancel,
2577
- onConfirm,
2578
- title,
2579
- content,
2580
- confirmButton,
2581
- isOpen,
2582
- variant = "secondary"
2744
+ const formatListLayout = (data, {
2745
+ schemas,
2746
+ schema,
2747
+ components
2583
2748
  }) => {
2584
- const { formatMessage } = useIntl();
2585
- const handleClose = async () => {
2586
- if (onCancel) {
2587
- await onCancel();
2749
+ const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
2750
+ (acc, [attribute, metadata]) => {
2751
+ return {
2752
+ ...acc,
2753
+ [attribute]: metadata.list
2754
+ };
2755
+ },
2756
+ {}
2757
+ );
2758
+ const listAttributes = convertListLayoutToFieldLayouts(
2759
+ data.contentType.layouts.list,
2760
+ schema?.attributes,
2761
+ listMetadatas,
2762
+ { configurations: data.components, schemas: components },
2763
+ schemas
2764
+ );
2765
+ return {
2766
+ layout: listAttributes,
2767
+ settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
2768
+ metadatas: listMetadatas,
2769
+ options: {
2770
+ ...schema?.options,
2771
+ ...schema?.pluginOptions,
2772
+ ...data.contentType.options
2588
2773
  }
2589
- onClose();
2590
2774
  };
2591
- const handleConfirm = async () => {
2592
- if (onConfirm) {
2593
- await onConfirm();
2775
+ };
2776
+ const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
2777
+ return columns.map((name) => {
2778
+ const attribute = attributes[name];
2779
+ if (!attribute) {
2780
+ return null;
2594
2781
  }
2595
- onClose();
2596
- };
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,
2782
+ const metadata = metadatas[name];
2783
+ const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
2784
+ return {
2785
+ attribute,
2786
+ label: metadata.label ?? "",
2787
+ mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
2788
+ schemas,
2789
+ components: components?.schemas ?? {}
2790
+ }),
2791
+ name,
2792
+ searchable: metadata.searchable ?? true,
2793
+ sortable: metadata.sortable ?? true
2794
+ };
2795
+ }).filter((field) => field !== null);
2796
+ };
2797
+ const ConfirmBulkActionDialog = ({
2798
+ onToggleDialog,
2799
+ isOpen = false,
2800
+ dialogBody,
2801
+ endAction
2802
+ }) => {
2803
+ const { formatMessage } = useIntl();
2804
+ return /* @__PURE__ */ jsxs(
2805
+ Dialog,
2806
+ {
2807
+ onClose: onToggleDialog,
2808
+ title: formatMessage({
2809
+ id: "app.components.ConfirmDialog.title",
2810
+ defaultMessage: "Confirmation"
2811
+ }),
2812
+ isOpen,
2813
+ children: [
2814
+ /* @__PURE__ */ jsx(DialogBody, { icon: /* @__PURE__ */ jsx(WarningCircle, {}), children: /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: dialogBody }) }),
2815
+ /* @__PURE__ */ jsx(
2816
+ DialogFooter,
2608
2817
  {
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
- })
2818
+ startAction: /* @__PURE__ */ jsx(Button, { onClick: onToggleDialog, variant: "tertiary", children: formatMessage({
2819
+ id: "app.components.Button.cancel",
2820
+ defaultMessage: "Cancel"
2821
+ }) }),
2822
+ endAction
2616
2823
  }
2617
2824
  )
2618
- }
2619
- )
2620
- ] });
2825
+ ]
2826
+ }
2827
+ );
2621
2828
  };
2622
- const BulkActionModal = ({
2829
+ const BoldChunk$1 = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
2830
+ const ConfirmDialogPublishAll = ({
2623
2831
  isOpen,
2624
- title,
2625
- onClose,
2626
- content: Content,
2627
- onModalClose
2832
+ onToggleDialog,
2833
+ isConfirmButtonLoading = false,
2834
+ onConfirm
2628
2835
  }) => {
2629
- const id = React.useId();
2630
- if (!isOpen) {
2631
- return null;
2632
- }
2633
- const handleClose = () => {
2634
- if (onClose) {
2635
- onClose();
2636
- }
2637
- onModalClose();
2638
- };
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
- };
2644
- const DeleteAction = ({ documents, model }) => {
2645
2836
  const { formatMessage } = useIntl();
2646
- const { schema: contentType } = useDoc();
2647
- const selectRow = useTable("DeleteAction", (state) => state.selectRow);
2648
- const hasI18nEnabled = Boolean(contentType?.pluginOptions?.i18n);
2837
+ const selectedEntries = useTable("ConfirmDialogPublishAll", (state) => state.selectedRows);
2838
+ const { toggleNotification } = useNotification();
2839
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
2840
+ const { model, schema } = useDoc();
2649
2841
  const [{ query }] = useQueryParams();
2650
- const params = React.useMemo(() => buildValidParams(query), [query]);
2651
- const hasDeletePermission = useDocumentRBAC("deleteAction", (state) => state.canDelete);
2652
- const { deleteMany: bulkDeleteAction } = useDocumentActions();
2653
- const documentIds = documents.map(({ documentId }) => documentId);
2654
- const handleConfirmBulkDelete = async () => {
2655
- const res = await bulkDeleteAction({
2656
- documentIds,
2842
+ const {
2843
+ data: countDraftRelations = 0,
2844
+ isLoading,
2845
+ error
2846
+ } = useGetManyDraftRelationCountQuery(
2847
+ {
2657
2848
  model,
2658
- params
2659
- });
2660
- if (!("error" in res)) {
2661
- selectRow([]);
2849
+ documentIds: selectedEntries.map((entry) => entry.documentId),
2850
+ locale: query?.plugins?.i18n?.locale
2851
+ },
2852
+ {
2853
+ skip: selectedEntries.length === 0
2662
2854
  }
2663
- };
2664
- if (!hasDeletePermission)
2855
+ );
2856
+ React.useEffect(() => {
2857
+ if (error) {
2858
+ toggleNotification({ type: "danger", message: formatAPIError(error) });
2859
+ }
2860
+ }, [error, formatAPIError, toggleNotification]);
2861
+ if (error) {
2665
2862
  return null;
2666
- return {
2667
- variant: "danger-light",
2668
- label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
2669
- dialog: {
2670
- type: "dialog",
2671
- title: formatMessage({
2672
- id: "app.components.ConfirmDialog.title",
2673
- defaultMessage: "Confirmation"
2674
- }),
2675
- content: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
2676
- /* @__PURE__ */ jsx(Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
2677
- id: "popUpWarning.bodyMessage.contentType.delete.all",
2678
- defaultMessage: "Are you sure you want to delete these entries?"
2679
- }) }),
2680
- hasI18nEnabled && /* @__PURE__ */ jsx(Box, { textAlign: "center", padding: 3, children: /* @__PURE__ */ jsx(Typography, { textColor: "danger500", children: formatMessage(
2863
+ }
2864
+ return /* @__PURE__ */ jsx(
2865
+ ConfirmBulkActionDialog,
2866
+ {
2867
+ isOpen: isOpen && !isLoading,
2868
+ onToggleDialog,
2869
+ dialogBody: /* @__PURE__ */ jsxs(Fragment, { children: [
2870
+ /* @__PURE__ */ jsxs(Typography, { id: "confirm-description", textAlign: "center", children: [
2871
+ countDraftRelations > 0 && formatMessage(
2872
+ {
2873
+ id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
2874
+ 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. "
2875
+ },
2876
+ {
2877
+ b: BoldChunk$1,
2878
+ count: countDraftRelations,
2879
+ entities: selectedEntries.length
2880
+ }
2881
+ ),
2882
+ formatMessage({
2883
+ id: getTranslation("popUpWarning.bodyMessage.contentType.publish.all"),
2884
+ defaultMessage: "Are you sure you want to publish these entries?"
2885
+ })
2886
+ ] }),
2887
+ schema?.pluginOptions && "i18n" in schema.pluginOptions && schema?.pluginOptions.i18n && /* @__PURE__ */ jsx(Typography, { textColor: "danger500", textAlign: "center", children: formatMessage(
2681
2888
  {
2682
- id: getTranslation("Settings.list.actions.deleteAdditionalInfos"),
2683
- defaultMessage: "This will delete the active locale versions <em>(from Internationalization)</em>"
2889
+ id: getTranslation("Settings.list.actions.publishAdditionalInfos"),
2890
+ defaultMessage: "This will publish the active locale versions <em>(from Internationalization)</em>"
2684
2891
  },
2685
2892
  {
2686
2893
  em: Emphasis
2687
2894
  }
2688
- ) }) })
2895
+ ) })
2689
2896
  ] }),
2690
- onConfirm: handleConfirmBulkDelete
2897
+ endAction: /* @__PURE__ */ jsx(
2898
+ Button,
2899
+ {
2900
+ onClick: onConfirm,
2901
+ variant: "secondary",
2902
+ startIcon: /* @__PURE__ */ jsx(Check, {}),
2903
+ loading: isConfirmButtonLoading,
2904
+ children: formatMessage({
2905
+ id: "app.utils.publish",
2906
+ defaultMessage: "Publish"
2907
+ })
2908
+ }
2909
+ )
2691
2910
  }
2692
- };
2911
+ );
2693
2912
  };
2694
- DeleteAction.type = "delete";
2695
- const UnpublishAction = ({ documents, model }) => {
2696
- const { formatMessage } = useIntl();
2697
- const { schema } = useDoc();
2698
- const selectRow = useTable("UnpublishAction", (state) => state.selectRow);
2699
- const hasPublishPermission = useDocumentRBAC("unpublishAction", (state) => state.canPublish);
2700
- const hasI18nEnabled = Boolean(schema?.pluginOptions?.i18n);
2701
- const hasDraftAndPublishEnabled = Boolean(schema?.options?.draftAndPublish);
2702
- const { unpublishMany: bulkUnpublishAction } = useDocumentActions();
2703
- const documentIds = documents.map(({ documentId }) => documentId);
2704
- const [{ query }] = useQueryParams();
2705
- const params = React.useMemo(() => buildValidParams(query), [query]);
2706
- const handleConfirmBulkUnpublish = async () => {
2707
- const data = await bulkUnpublishAction({ documentIds, model, params });
2708
- if (!("error" in data)) {
2709
- selectRow([]);
2710
- }
2711
- };
2712
- const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published");
2713
- if (!showUnpublishButton)
2714
- return null;
2715
- return {
2716
- variant: "tertiary",
2717
- label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
2718
- dialog: {
2719
- type: "dialog",
2720
- title: formatMessage({
2721
- id: "app.components.ConfirmDialog.title",
2722
- defaultMessage: "Confirmation"
2723
- }),
2724
- content: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
2725
- /* @__PURE__ */ jsx(Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
2726
- id: "popUpWarning.bodyMessage.contentType.unpublish.all",
2727
- defaultMessage: "Are you sure you want to unpublish these entries?"
2728
- }) }),
2729
- hasI18nEnabled && /* @__PURE__ */ jsx(Box, { textAlign: "center", padding: 3, children: /* @__PURE__ */ jsx(Typography, { textColor: "danger500", children: formatMessage(
2913
+ const TypographyMaxWidth = styled(Typography)`
2914
+ max-width: 300px;
2915
+ `;
2916
+ const formatErrorMessages = (errors, parentKey, formatMessage) => {
2917
+ const messages = [];
2918
+ Object.entries(errors).forEach(([key, value]) => {
2919
+ const currentKey = parentKey ? `${parentKey}.${key}` : key;
2920
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
2921
+ if ("id" in value && "defaultMessage" in value) {
2922
+ messages.push(
2923
+ formatMessage(
2924
+ {
2925
+ id: `${value.id}.withField`,
2926
+ defaultMessage: value.defaultMessage
2927
+ },
2928
+ { field: currentKey }
2929
+ )
2930
+ );
2931
+ } else {
2932
+ messages.push(...formatErrorMessages(value, currentKey, formatMessage));
2933
+ }
2934
+ } else {
2935
+ messages.push(
2936
+ formatMessage(
2730
2937
  {
2731
- id: getTranslation("Settings.list.actions.unpublishAdditionalInfos"),
2732
- defaultMessage: "This will unpublish the active locale versions <em>(from Internationalization)</em>"
2938
+ id: `${value}.withField`,
2939
+ defaultMessage: value
2733
2940
  },
2734
- {
2735
- em: Emphasis
2736
- }
2737
- ) }) })
2738
- ] }),
2739
- confirmButton: formatMessage({
2740
- id: "app.utils.unpublish",
2741
- defaultMessage: "Unpublish"
2742
- }),
2743
- onConfirm: handleConfirmBulkUnpublish
2941
+ { field: currentKey }
2942
+ )
2943
+ );
2744
2944
  }
2745
- };
2945
+ });
2946
+ return messages;
2746
2947
  };
2747
- UnpublishAction.type = "unpublish";
2748
- const Emphasis = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", textColor: "danger500", children: chunks });
2749
- const DEFAULT_BULK_ACTIONS = [UnpublishAction, DeleteAction];
2750
- const AutoCloneFailureModalBody = ({ prohibitedFields }) => {
2948
+ const EntryValidationText = ({ validationErrors, status }) => {
2751
2949
  const { formatMessage } = useIntl();
2752
- const getDefaultErrorMessage = (reason) => {
2753
- switch (reason) {
2754
- case "relation":
2755
- return "Duplicating the relation could remove it from the original entry.";
2756
- case "unique":
2757
- return "Identical values in a unique field are not allowed";
2758
- default:
2759
- return reason;
2950
+ if (validationErrors) {
2951
+ const validationErrorsMessages = formatErrorMessages(validationErrors, "", formatMessage).join(
2952
+ " "
2953
+ );
2954
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
2955
+ /* @__PURE__ */ jsx(CrossCircle, { fill: "danger600" }),
2956
+ /* @__PURE__ */ jsx(Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
2957
+ ] });
2958
+ }
2959
+ if (status === "published") {
2960
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
2961
+ /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
2962
+ /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
2963
+ id: "content-manager.bulk-publish.already-published",
2964
+ defaultMessage: "Already Published"
2965
+ }) })
2966
+ ] });
2967
+ }
2968
+ if (status === "modified") {
2969
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
2970
+ /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
2971
+ /* @__PURE__ */ jsx(Typography, { children: formatMessage({
2972
+ id: "content-manager.bulk-publish.modified",
2973
+ defaultMessage: "Ready to publish changes"
2974
+ }) })
2975
+ ] });
2976
+ }
2977
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
2978
+ /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
2979
+ /* @__PURE__ */ jsx(Typography, { children: formatMessage({
2980
+ id: "app.utils.ready-to-publish",
2981
+ defaultMessage: "Ready to publish"
2982
+ }) })
2983
+ ] });
2984
+ };
2985
+ const TABLE_HEADERS = [
2986
+ { name: "id", label: "id" },
2987
+ { name: "name", label: "name" },
2988
+ { name: "status", label: "status" },
2989
+ { name: "publicationStatus", label: "Publication status" }
2990
+ ];
2991
+ const SelectedEntriesTableContent = ({
2992
+ isPublishing,
2993
+ rowsToDisplay = [],
2994
+ entriesToPublish = [],
2995
+ validationErrors = {}
2996
+ }) => {
2997
+ const { pathname } = useLocation();
2998
+ const { formatMessage } = useIntl();
2999
+ const {
3000
+ list: {
3001
+ settings: { mainField }
2760
3002
  }
2761
- };
2762
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2763
- /* @__PURE__ */ jsx(Typography, { variant: "beta", children: formatMessage({
2764
- id: getTranslation("containers.list.autoCloneModal.title"),
2765
- defaultMessage: "This entry can't be duplicated directly."
2766
- }) }),
2767
- /* @__PURE__ */ jsx(Box, { marginTop: 2, children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: formatMessage({
2768
- id: getTranslation("containers.list.autoCloneModal.description"),
2769
- defaultMessage: "A new entry will be created with the same content, but you'll have to change the following fields to save it."
2770
- }) }) }),
2771
- /* @__PURE__ */ jsx(Flex, { marginTop: 6, gap: 2, direction: "column", alignItems: "stretch", children: prohibitedFields.map(([fieldPath, reason]) => /* @__PURE__ */ jsxs(
2772
- Flex,
2773
- {
2774
- direction: "column",
2775
- gap: 2,
2776
- alignItems: "flex-start",
2777
- borderColor: "neutral200",
2778
- hasRadius: true,
2779
- padding: 6,
2780
- children: [
2781
- /* @__PURE__ */ jsx(Flex, { direction: "row", tag: "ol", children: fieldPath.map((pathSegment, index2) => /* @__PURE__ */ jsxs(Typography, { fontWeight: "semiBold", tag: "li", children: [
2782
- pathSegment,
2783
- index2 !== fieldPath.length - 1 && /* @__PURE__ */ jsx(
2784
- ChevronRight,
2785
- {
2786
- fill: "neutral500",
2787
- height: "0.8rem",
2788
- width: "0.8rem",
2789
- style: { margin: "0 0.8rem" }
2790
- }
2791
- )
2792
- ] }, index2)) }),
2793
- /* @__PURE__ */ jsx(Typography, { tag: "p", textColor: "neutral600", children: formatMessage({
2794
- id: getTranslation(`containers.list.autoCloneModal.error.${reason}`),
2795
- defaultMessage: getDefaultErrorMessage(reason)
2796
- }) })
2797
- ]
2798
- },
2799
- fieldPath.join()
2800
- )) })
3003
+ } = useDocLayout();
3004
+ const shouldDisplayMainField = mainField != null && mainField !== "id";
3005
+ return /* @__PURE__ */ jsxs(Table.Content, { children: [
3006
+ /* @__PURE__ */ jsxs(Table.Head, { children: [
3007
+ /* @__PURE__ */ jsx(Table.HeaderCheckboxCell, {}),
3008
+ TABLE_HEADERS.filter((head) => head.name !== "name" || shouldDisplayMainField).map(
3009
+ (head) => /* @__PURE__ */ jsx(Table.HeaderCell, { ...head }, head.name)
3010
+ )
3011
+ ] }),
3012
+ /* @__PURE__ */ jsx(Table.Loading, {}),
3013
+ /* @__PURE__ */ jsx(Table.Body, { children: rowsToDisplay.map((row, index2) => /* @__PURE__ */ jsxs(Table.Row, { children: [
3014
+ /* @__PURE__ */ jsx(Table.CheckboxCell, { id: row.id }),
3015
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Typography, { children: row.id }) }),
3016
+ shouldDisplayMainField && /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Typography, { children: row[mainField] }) }),
3017
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(DocumentStatus, { status: row.status, maxWidth: "min-content" }) }),
3018
+ /* @__PURE__ */ jsx(Table.Cell, { children: isPublishing && entriesToPublish.includes(row.documentId) ? /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3019
+ /* @__PURE__ */ jsx(Typography, { children: formatMessage({
3020
+ id: "content-manager.success.record.publishing",
3021
+ defaultMessage: "Publishing..."
3022
+ }) }),
3023
+ /* @__PURE__ */ jsx(Loader, { small: true })
3024
+ ] }) : /* @__PURE__ */ jsx(
3025
+ EntryValidationText,
3026
+ {
3027
+ validationErrors: validationErrors[row.documentId],
3028
+ status: row.status
3029
+ }
3030
+ ) }),
3031
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
3032
+ IconButton,
3033
+ {
3034
+ tag: Link,
3035
+ to: {
3036
+ pathname: `${pathname}/${row.documentId}`,
3037
+ search: row.locale && `?plugins[i18n][locale]=${row.locale}`
3038
+ },
3039
+ state: { from: pathname },
3040
+ label: formatMessage(
3041
+ { id: "app.component.HelperPluginTable.edit", defaultMessage: "Edit {target}" },
3042
+ {
3043
+ target: formatMessage(
3044
+ {
3045
+ id: "content-manager.components.ListViewHelperPluginTable.row-line",
3046
+ defaultMessage: "item line {number}"
3047
+ },
3048
+ { number: index2 + 1 }
3049
+ )
3050
+ }
3051
+ ),
3052
+ target: "_blank",
3053
+ marginLeft: "auto",
3054
+ children: /* @__PURE__ */ jsx(Pencil, {})
3055
+ }
3056
+ ) })
3057
+ ] }, row.id)) })
2801
3058
  ] });
2802
3059
  };
2803
- const TableActions = ({ document }) => {
3060
+ const BoldChunk = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
3061
+ const SelectedEntriesModalContent = ({
3062
+ listViewSelectedEntries,
3063
+ toggleModal,
3064
+ setListViewSelectedDocuments,
3065
+ model
3066
+ }) => {
2804
3067
  const { formatMessage } = useIntl();
2805
- const { model, collectionType } = useDoc();
2806
- const plugins = useStrapiApp("TableActions", (state) => state.plugins);
2807
- const props = {
2808
- activeTab: null,
2809
- model,
2810
- documentId: document.documentId,
2811
- collectionType,
2812
- document
2813
- };
2814
- return /* @__PURE__ */ jsx(
2815
- DescriptionComponentRenderer,
3068
+ const { schema, components } = useContentTypeSchema(model);
3069
+ const documentIds = listViewSelectedEntries.map(({ documentId }) => documentId);
3070
+ const [{ query }] = useQueryParams();
3071
+ const params = React.useMemo(() => buildValidParams(query), [query]);
3072
+ const { data, isLoading, isFetching, refetch } = useGetAllDocumentsQuery(
2816
3073
  {
2817
- props,
2818
- descriptions: plugins["content-manager"].apis.getDocumentActions(),
2819
- children: (actions2) => {
2820
- const tableRowActions = actions2.filter((action) => {
2821
- const positions = Array.isArray(action.position) ? action.position : [action.position];
2822
- return positions.includes("table-row");
2823
- });
2824
- return /* @__PURE__ */ jsx(
2825
- DocumentActionsMenu,
2826
- {
2827
- actions: tableRowActions,
2828
- label: formatMessage({
2829
- id: "content-manager.containers.list.table.row-actions",
2830
- defaultMessage: "Row action"
2831
- }),
2832
- variant: "ghost"
3074
+ model,
3075
+ params: {
3076
+ page: "1",
3077
+ pageSize: documentIds.length.toString(),
3078
+ sort: query.sort,
3079
+ filters: {
3080
+ documentId: {
3081
+ $in: documentIds
2833
3082
  }
2834
- );
3083
+ },
3084
+ locale: query.plugins?.i18n?.locale
2835
3085
  }
3086
+ },
3087
+ {
3088
+ selectFromResult: ({ data: data2, ...restRes }) => ({ data: data2?.results ?? [], ...restRes })
3089
+ }
3090
+ );
3091
+ const { rows, validationErrors } = React.useMemo(() => {
3092
+ if (data.length > 0 && schema) {
3093
+ const validate = createYupSchema(schema.attributes, components);
3094
+ const validationErrors2 = {};
3095
+ const rows2 = data.map((entry) => {
3096
+ try {
3097
+ validate.validateSync(entry, { abortEarly: false });
3098
+ return entry;
3099
+ } catch (e) {
3100
+ if (e instanceof ValidationError) {
3101
+ validationErrors2[entry.documentId] = getYupValidationErrors(e);
3102
+ }
3103
+ return entry;
3104
+ }
3105
+ });
3106
+ return { rows: rows2, validationErrors: validationErrors2 };
2836
3107
  }
3108
+ return {
3109
+ rows: [],
3110
+ validationErrors: {}
3111
+ };
3112
+ }, [components, data, schema]);
3113
+ const [publishedCount, setPublishedCount] = React.useState(0);
3114
+ const [isDialogOpen, setIsDialogOpen] = React.useState(false);
3115
+ const { publishMany: bulkPublishAction } = useDocumentActions();
3116
+ const [, { isLoading: isSubmittingForm }] = usePublishManyDocumentsMutation();
3117
+ const selectedRows = useTable("publishAction", (state) => state.selectedRows);
3118
+ const selectedEntries = rows.filter(
3119
+ (entry) => selectedRows.some((selectedEntry) => selectedEntry.documentId === entry.documentId)
2837
3120
  );
3121
+ const entriesToPublish = selectedEntries.filter((entry) => !validationErrors[entry.documentId]).map((entry) => entry.documentId);
3122
+ const selectedEntriesWithErrorsCount = selectedEntries.filter(
3123
+ ({ documentId }) => validationErrors[documentId]
3124
+ ).length;
3125
+ const selectedEntriesPublished = selectedEntries.filter(
3126
+ ({ status }) => status === "published"
3127
+ ).length;
3128
+ const selectedEntriesWithNoErrorsCount = selectedEntries.length - selectedEntriesWithErrorsCount - selectedEntriesPublished;
3129
+ const toggleDialog = () => setIsDialogOpen((prev) => !prev);
3130
+ const handleConfirmBulkPublish = async () => {
3131
+ toggleDialog();
3132
+ const res = await bulkPublishAction({ model, documentIds: entriesToPublish, params });
3133
+ if (!("error" in res)) {
3134
+ setPublishedCount(res.count);
3135
+ const unpublishedEntries = rows.filter((row) => {
3136
+ return !entriesToPublish.includes(row.documentId);
3137
+ });
3138
+ setListViewSelectedDocuments(unpublishedEntries);
3139
+ }
3140
+ };
3141
+ const getFormattedCountMessage = () => {
3142
+ if (publishedCount) {
3143
+ return formatMessage(
3144
+ {
3145
+ id: getTranslation("containers.list.selectedEntriesModal.publishedCount"),
3146
+ 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."
3147
+ },
3148
+ {
3149
+ publishedCount,
3150
+ withErrorsCount: selectedEntriesWithErrorsCount,
3151
+ b: BoldChunk
3152
+ }
3153
+ );
3154
+ }
3155
+ return formatMessage(
3156
+ {
3157
+ id: getTranslation("containers.list.selectedEntriesModal.selectedCount"),
3158
+ 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."
3159
+ },
3160
+ {
3161
+ readyToPublishCount: selectedEntriesWithNoErrorsCount,
3162
+ withErrorsCount: selectedEntriesWithErrorsCount,
3163
+ alreadyPublishedCount: selectedEntriesPublished,
3164
+ b: BoldChunk
3165
+ }
3166
+ );
3167
+ };
3168
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3169
+ /* @__PURE__ */ jsxs(ModalBody, { children: [
3170
+ /* @__PURE__ */ jsx(Typography, { children: getFormattedCountMessage() }),
3171
+ /* @__PURE__ */ jsx(Box, { marginTop: 5, children: /* @__PURE__ */ jsx(
3172
+ SelectedEntriesTableContent,
3173
+ {
3174
+ isPublishing: isSubmittingForm,
3175
+ rowsToDisplay: rows,
3176
+ entriesToPublish,
3177
+ validationErrors
3178
+ }
3179
+ ) })
3180
+ ] }),
3181
+ /* @__PURE__ */ jsx(
3182
+ ModalFooter,
3183
+ {
3184
+ startActions: /* @__PURE__ */ jsx(Button, { onClick: toggleModal, variant: "tertiary", children: formatMessage({
3185
+ id: "app.components.Button.cancel",
3186
+ defaultMessage: "Cancel"
3187
+ }) }),
3188
+ endActions: /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
3189
+ /* @__PURE__ */ jsx(Button, { onClick: refetch, variant: "tertiary", loading: isFetching, children: formatMessage({ id: "app.utils.refresh", defaultMessage: "Refresh" }) }),
3190
+ /* @__PURE__ */ jsx(
3191
+ Button,
3192
+ {
3193
+ onClick: toggleDialog,
3194
+ disabled: selectedEntries.length === 0 || selectedEntries.length === selectedEntriesWithErrorsCount || selectedEntriesPublished === selectedEntries.length || isLoading,
3195
+ loading: isSubmittingForm,
3196
+ children: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" })
3197
+ }
3198
+ )
3199
+ ] })
3200
+ }
3201
+ ),
3202
+ /* @__PURE__ */ jsx(
3203
+ ConfirmDialogPublishAll,
3204
+ {
3205
+ isOpen: isDialogOpen,
3206
+ onToggleDialog: toggleDialog,
3207
+ isConfirmButtonLoading: isSubmittingForm,
3208
+ onConfirm: handleConfirmBulkPublish
3209
+ }
3210
+ )
3211
+ ] });
2838
3212
  };
2839
- const EditAction = ({ documentId }) => {
2840
- const navigate = useNavigate();
3213
+ const PublishAction = ({ documents, model }) => {
2841
3214
  const { formatMessage } = useIntl();
2842
- const { canRead } = useDocumentRBAC("EditAction", ({ canRead: canRead2 }) => ({ canRead: canRead2 }));
2843
- const { toggleNotification } = useNotification();
2844
- const [{ query }] = useQueryParams();
3215
+ const hasPublishPermission = useDocumentRBAC("unpublishAction", (state) => state.canPublish);
3216
+ const showPublishButton = hasPublishPermission && documents.some(({ status }) => status !== "published");
3217
+ const setListViewSelectedDocuments = useTable("publishAction", (state) => state.selectRow);
3218
+ const refetchList = () => {
3219
+ contentManagerApi.util.invalidateTags([{ type: "Document", id: `${model}_LIST` }]);
3220
+ };
3221
+ if (!showPublishButton)
3222
+ return null;
2845
3223
  return {
2846
- disabled: !canRead,
2847
- icon: /* @__PURE__ */ jsx(StyledPencil, {}),
2848
- label: formatMessage({
2849
- id: "content-manager.actions.edit.label",
2850
- defaultMessage: "Edit"
2851
- }),
2852
- position: "table-row",
2853
- onClick: async () => {
2854
- if (!documentId) {
2855
- console.error(
2856
- "You're trying to edit a document without an id, this is likely a bug with Strapi. Please open an issue."
2857
- );
2858
- toggleNotification({
2859
- message: formatMessage({
2860
- id: "content-manager.actions.edit.error",
2861
- defaultMessage: "An error occurred while trying to edit the document."
2862
- }),
2863
- type: "danger"
2864
- });
2865
- return;
3224
+ actionType: "publish",
3225
+ variant: "tertiary",
3226
+ label: formatMessage({ id: "app.utils.publish", defaultMessage: "Publish" }),
3227
+ dialog: {
3228
+ type: "modal",
3229
+ title: formatMessage({
3230
+ id: getTranslation("containers.ListPage.selectedEntriesModal.title"),
3231
+ defaultMessage: "Publish entries"
3232
+ }),
3233
+ content: ({ onClose }) => {
3234
+ return /* @__PURE__ */ jsx(Table.Root, { rows: documents, defaultSelectedRows: documents, headers: TABLE_HEADERS, children: /* @__PURE__ */ jsx(
3235
+ SelectedEntriesModalContent,
3236
+ {
3237
+ listViewSelectedEntries: documents,
3238
+ toggleModal: () => {
3239
+ onClose();
3240
+ refetchList();
3241
+ },
3242
+ setListViewSelectedDocuments,
3243
+ model
3244
+ }
3245
+ ) });
3246
+ },
3247
+ onClose: () => {
3248
+ refetchList();
2866
3249
  }
2867
- navigate({
2868
- pathname: documentId,
2869
- search: stringify({
2870
- plugins: query.plugins
2871
- })
2872
- });
2873
3250
  }
2874
3251
  };
2875
3252
  };
2876
- EditAction.type = "edit";
2877
- const StyledPencil = styled(Pencil)`
2878
- path {
2879
- fill: currentColor;
2880
- }
2881
- `;
2882
- const CloneAction = ({ model, documentId }) => {
2883
- const navigate = useNavigate();
2884
- const { formatMessage } = useIntl();
2885
- const { canCreate } = useDocumentRBAC("CloneAction", ({ canCreate: canCreate2 }) => ({ canCreate: canCreate2 }));
2886
- const { toggleNotification } = useNotification();
2887
- const { autoClone } = useDocumentActions();
2888
- const [prohibitedFields, setProhibitedFields] = React.useState([]);
2889
- return {
2890
- disabled: !canCreate,
2891
- icon: /* @__PURE__ */ jsx(StyledDuplicate, {}),
2892
- label: formatMessage({
2893
- id: "content-manager.actions.clone.label",
2894
- defaultMessage: "Duplicate"
2895
- }),
2896
- position: "table-row",
2897
- onClick: async () => {
2898
- if (!documentId) {
2899
- console.error(
2900
- "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;
3253
+ const BulkActionsRenderer = () => {
3254
+ const plugins = useStrapiApp("BulkActionsRenderer", (state) => state.plugins);
3255
+ const { model, collectionType } = useDoc();
3256
+ const { selectedRows } = useTable("BulkActionsRenderer", (state) => state);
3257
+ return /* @__PURE__ */ jsx(Flex, { gap: 2, children: /* @__PURE__ */ jsx(
3258
+ DescriptionComponentRenderer,
3259
+ {
3260
+ props: {
3261
+ model,
3262
+ collectionType,
3263
+ documents: selectedRows
3264
+ },
3265
+ descriptions: plugins["content-manager"].apis.getBulkActions(),
3266
+ children: (actions2) => actions2.map((action) => /* @__PURE__ */ jsx(BulkActionAction, { ...action }, action.id))
3267
+ }
3268
+ ) });
3269
+ };
3270
+ const BulkActionAction = (action) => {
3271
+ const [dialogId, setDialogId] = React.useState(null);
3272
+ const { toggleNotification } = useNotification();
3273
+ const handleClick = (action2) => (e) => {
3274
+ const { onClick, dialog, id } = action2;
3275
+ if (onClick) {
3276
+ onClick(e);
3277
+ }
3278
+ if (dialog) {
3279
+ switch (dialog.type) {
3280
+ case "notification":
3281
+ toggleNotification({
3282
+ title: dialog.title,
3283
+ message: dialog.content,
3284
+ type: dialog.status,
3285
+ timeout: dialog.timeout,
3286
+ onClose: dialog.onClose
3287
+ });
3288
+ break;
3289
+ case "dialog":
3290
+ case "modal": {
3291
+ e.preventDefault();
3292
+ setDialogId(id);
3293
+ }
2910
3294
  }
2911
- const res = await autoClone({ model, sourceId: documentId });
2912
- if ("data" in res) {
2913
- navigate(res.data.documentId);
2914
- return true;
3295
+ }
3296
+ };
3297
+ const handleClose = () => {
3298
+ setDialogId(null);
3299
+ if (action.dialog?.type === "modal" && action.dialog?.onClose) {
3300
+ action.dialog.onClose();
3301
+ }
3302
+ };
3303
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3304
+ /* @__PURE__ */ jsx(
3305
+ Button,
3306
+ {
3307
+ disabled: action.disabled,
3308
+ startIcon: action.icon,
3309
+ variant: action.variant,
3310
+ onClick: handleClick(action),
3311
+ children: action.label
2915
3312
  }
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);
3313
+ ),
3314
+ action.dialog?.type === "dialog" ? /* @__PURE__ */ jsx(
3315
+ BulkActionConfirmDialog,
3316
+ {
3317
+ ...action.dialog,
3318
+ variant: action.variant,
3319
+ isOpen: dialogId === action.id,
3320
+ onClose: handleClose
2919
3321
  }
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
- ] });
3322
+ ) : null,
3323
+ action.dialog?.type === "modal" ? /* @__PURE__ */ jsx(
3324
+ BulkActionModal,
3325
+ {
3326
+ ...action.dialog,
3327
+ onModalClose: handleClose,
3328
+ isOpen: dialogId === action.id
2948
3329
  }
2949
- }
2950
- };
3330
+ ) : null
3331
+ ] });
2951
3332
  };
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
- );
3333
+ const BulkActionConfirmDialog = ({
3334
+ onClose,
3335
+ onCancel,
3336
+ onConfirm,
3337
+ title,
3338
+ content,
3339
+ confirmButton,
3340
+ isOpen,
3341
+ variant = "secondary"
3342
+ }) => {
3343
+ const { formatMessage } = useIntl();
3344
+ const handleClose = async () => {
3345
+ if (onCancel) {
3346
+ await onCancel();
2988
3347
  }
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
- );
3348
+ onClose();
3349
+ };
3350
+ const handleConfirm = async () => {
3351
+ if (onConfirm) {
3352
+ await onConfirm();
3001
3353
  }
3354
+ onClose();
3355
+ };
3356
+ return /* @__PURE__ */ jsxs(Dialog, { isOpen, title, onClose: handleClose, children: [
3357
+ /* @__PURE__ */ jsx(DialogBody, { icon: /* @__PURE__ */ jsx(WarningCircle, {}), children: content }),
3358
+ /* @__PURE__ */ jsx(
3359
+ DialogFooter,
3360
+ {
3361
+ startAction: /* @__PURE__ */ jsx(Button, { onClick: handleClose, variant: "tertiary", children: formatMessage({
3362
+ id: "app.components.Button.cancel",
3363
+ defaultMessage: "Cancel"
3364
+ }) }),
3365
+ endAction: /* @__PURE__ */ jsx(
3366
+ Button,
3367
+ {
3368
+ onClick: handleConfirm,
3369
+ variant: variant === "danger-light" ? variant : "secondary",
3370
+ startIcon: variant === "danger-light" ? /* @__PURE__ */ jsx(Trash, {}) : /* @__PURE__ */ jsx(Check, {}),
3371
+ children: confirmButton ? confirmButton : formatMessage({
3372
+ id: "app.components.Button.confirm",
3373
+ defaultMessage: "Confirm"
3374
+ })
3375
+ }
3376
+ )
3377
+ }
3378
+ )
3379
+ ] });
3380
+ };
3381
+ const BulkActionModal = ({
3382
+ isOpen,
3383
+ title,
3384
+ onClose,
3385
+ content: Content,
3386
+ onModalClose
3387
+ }) => {
3388
+ const id = React.useId();
3389
+ if (!isOpen) {
3390
+ return null;
3002
3391
  }
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
- );
3392
+ const handleClose = () => {
3393
+ if (onClose) {
3394
+ onClose();
3014
3395
  }
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
3396
+ onModalClose();
3397
+ };
3398
+ return /* @__PURE__ */ jsxs(ModalLayout, { borderRadius: "4px", overflow: "hidden", onClose: handleClose, labelledBy: id, children: [
3399
+ /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", textColor: "neutral800", tag: "h2", id, children: title }) }),
3400
+ /* @__PURE__ */ jsx(Content, { onClose: handleClose })
3401
+ ] });
3067
3402
  };
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;
3403
+ const DeleteAction = ({ documents, model }) => {
3404
+ const { formatMessage } = useIntl();
3405
+ const { schema: contentType } = useDoc();
3406
+ const selectRow = useTable("DeleteAction", (state) => state.selectRow);
3407
+ const hasI18nEnabled = Boolean(contentType?.pluginOptions?.i18n);
3408
+ const [{ query }] = useQueryParams();
3409
+ const params = React.useMemo(() => buildValidParams(query), [query]);
3410
+ const hasDeletePermission = useDocumentRBAC("deleteAction", (state) => state.canDelete);
3411
+ const { deleteMany: bulkDeleteAction } = useDocumentActions();
3412
+ const documentIds = documents.map(({ documentId }) => documentId);
3413
+ const handleConfirmBulkDelete = async () => {
3414
+ const res = await bulkDeleteAction({
3415
+ documentIds,
3416
+ model,
3417
+ params
3418
+ });
3419
+ if (!("error" in res)) {
3420
+ selectRow([]);
3088
3421
  }
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
- );
3422
+ };
3423
+ if (!hasDeletePermission)
3424
+ return null;
3176
3425
  return {
3177
- name: mainFieldName,
3178
- type: mainFieldType ?? "string"
3426
+ variant: "danger-light",
3427
+ label: formatMessage({ id: "global.delete", defaultMessage: "Delete" }),
3428
+ dialog: {
3429
+ type: "dialog",
3430
+ title: formatMessage({
3431
+ id: "app.components.ConfirmDialog.title",
3432
+ defaultMessage: "Confirmation"
3433
+ }),
3434
+ content: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3435
+ /* @__PURE__ */ jsx(Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
3436
+ id: "popUpWarning.bodyMessage.contentType.delete.all",
3437
+ defaultMessage: "Are you sure you want to delete these entries?"
3438
+ }) }),
3439
+ hasI18nEnabled && /* @__PURE__ */ jsx(Box, { textAlign: "center", padding: 3, children: /* @__PURE__ */ jsx(Typography, { textColor: "danger500", children: formatMessage(
3440
+ {
3441
+ id: getTranslation("Settings.list.actions.deleteAdditionalInfos"),
3442
+ defaultMessage: "This will delete the active locale versions <em>(from Internationalization)</em>"
3443
+ },
3444
+ {
3445
+ em: Emphasis
3446
+ }
3447
+ ) }) })
3448
+ ] }),
3449
+ onConfirm: handleConfirmBulkDelete
3450
+ }
3179
3451
  };
3180
3452
  };
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
- );
3453
+ DeleteAction.type = "delete";
3454
+ const UnpublishAction = ({ documents, model }) => {
3455
+ const { formatMessage } = useIntl();
3456
+ const { schema } = useDoc();
3457
+ const selectRow = useTable("UnpublishAction", (state) => state.selectRow);
3458
+ const hasPublishPermission = useDocumentRBAC("unpublishAction", (state) => state.canPublish);
3459
+ const hasI18nEnabled = Boolean(schema?.pluginOptions?.i18n);
3460
+ const hasDraftAndPublishEnabled = Boolean(schema?.options?.draftAndPublish);
3461
+ const { unpublishMany: bulkUnpublishAction } = useDocumentActions();
3462
+ const documentIds = documents.map(({ documentId }) => documentId);
3463
+ const [{ query }] = useQueryParams();
3464
+ const params = React.useMemo(() => buildValidParams(query), [query]);
3465
+ const handleConfirmBulkUnpublish = async () => {
3466
+ const data = await bulkUnpublishAction({ documentIds, model, params });
3467
+ if (!("error" in data)) {
3468
+ selectRow([]);
3469
+ }
3470
+ };
3471
+ const showUnpublishButton = hasDraftAndPublishEnabled && hasPublishPermission && documents.some((entry) => entry.status === "published" || entry.status === "modified");
3472
+ if (!showUnpublishButton)
3473
+ return null;
3238
3474
  return {
3239
- error,
3240
- isLoading,
3241
- edit,
3242
- list: listLayout
3475
+ variant: "tertiary",
3476
+ label: formatMessage({ id: "app.utils.unpublish", defaultMessage: "Unpublish" }),
3477
+ dialog: {
3478
+ type: "dialog",
3479
+ title: formatMessage({
3480
+ id: "app.components.ConfirmDialog.title",
3481
+ defaultMessage: "Confirmation"
3482
+ }),
3483
+ content: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
3484
+ /* @__PURE__ */ jsx(Typography, { id: "confirm-description", textAlign: "center", children: formatMessage({
3485
+ id: "popUpWarning.bodyMessage.contentType.unpublish.all",
3486
+ defaultMessage: "Are you sure you want to unpublish these entries?"
3487
+ }) }),
3488
+ hasI18nEnabled && /* @__PURE__ */ jsx(Box, { textAlign: "center", padding: 3, children: /* @__PURE__ */ jsx(Typography, { textColor: "danger500", children: formatMessage(
3489
+ {
3490
+ id: getTranslation("Settings.list.actions.unpublishAdditionalInfos"),
3491
+ defaultMessage: "This will unpublish the active locale versions <em>(from Internationalization)</em>"
3492
+ },
3493
+ {
3494
+ em: Emphasis
3495
+ }
3496
+ ) }) })
3497
+ ] }),
3498
+ confirmButton: formatMessage({
3499
+ id: "app.utils.unpublish",
3500
+ defaultMessage: "Unpublish"
3501
+ }),
3502
+ onConfirm: handleConfirmBulkUnpublish
3503
+ }
3243
3504
  };
3244
3505
  };
3245
- const useDocLayout = () => {
3246
- const { model } = useDoc();
3247
- return useDocumentLayout(model);
3506
+ UnpublishAction.type = "unpublish";
3507
+ const Emphasis = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", textColor: "danger500", children: chunks });
3508
+ const DEFAULT_BULK_ACTIONS = [PublishAction, UnpublishAction, DeleteAction];
3509
+ const AutoCloneFailureModalBody = ({ prohibitedFields }) => {
3510
+ const { formatMessage } = useIntl();
3511
+ const getDefaultErrorMessage = (reason) => {
3512
+ switch (reason) {
3513
+ case "relation":
3514
+ return "Duplicating the relation could remove it from the original entry.";
3515
+ case "unique":
3516
+ return "Identical values in a unique field are not allowed";
3517
+ default:
3518
+ return reason;
3519
+ }
3520
+ };
3521
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3522
+ /* @__PURE__ */ jsx(Typography, { variant: "beta", children: formatMessage({
3523
+ id: getTranslation("containers.list.autoCloneModal.title"),
3524
+ defaultMessage: "This entry can't be duplicated directly."
3525
+ }) }),
3526
+ /* @__PURE__ */ jsx(Box, { marginTop: 2, children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: formatMessage({
3527
+ id: getTranslation("containers.list.autoCloneModal.description"),
3528
+ defaultMessage: "A new entry will be created with the same content, but you'll have to change the following fields to save it."
3529
+ }) }) }),
3530
+ /* @__PURE__ */ jsx(Flex, { marginTop: 6, gap: 2, direction: "column", alignItems: "stretch", children: prohibitedFields.map(([fieldPath, reason]) => /* @__PURE__ */ jsxs(
3531
+ Flex,
3532
+ {
3533
+ direction: "column",
3534
+ gap: 2,
3535
+ alignItems: "flex-start",
3536
+ borderColor: "neutral200",
3537
+ hasRadius: true,
3538
+ padding: 6,
3539
+ children: [
3540
+ /* @__PURE__ */ jsx(Flex, { direction: "row", tag: "ol", children: fieldPath.map((pathSegment, index2) => /* @__PURE__ */ jsxs(Typography, { fontWeight: "semiBold", tag: "li", children: [
3541
+ pathSegment,
3542
+ index2 !== fieldPath.length - 1 && /* @__PURE__ */ jsx(
3543
+ ChevronRight,
3544
+ {
3545
+ fill: "neutral500",
3546
+ height: "0.8rem",
3547
+ width: "0.8rem",
3548
+ style: { margin: "0 0.8rem" }
3549
+ }
3550
+ )
3551
+ ] }, index2)) }),
3552
+ /* @__PURE__ */ jsx(Typography, { tag: "p", textColor: "neutral600", children: formatMessage({
3553
+ id: getTranslation(`containers.list.autoCloneModal.error.${reason}`),
3554
+ defaultMessage: getDefaultErrorMessage(reason)
3555
+ }) })
3556
+ ]
3557
+ },
3558
+ fieldPath.join()
3559
+ )) })
3560
+ ] });
3248
3561
  };
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([]);
3562
+ const TableActions = ({ document }) => {
3563
+ const { formatMessage } = useIntl();
3564
+ const { model, collectionType } = useDoc();
3565
+ const plugins = useStrapiApp("TableActions", (state) => state.plugins);
3566
+ const props = {
3567
+ activeTab: null,
3568
+ model,
3569
+ documentId: document.documentId,
3570
+ collectionType,
3571
+ document
3572
+ };
3573
+ return /* @__PURE__ */ jsx(
3574
+ DescriptionComponentRenderer,
3575
+ {
3576
+ props,
3577
+ descriptions: plugins["content-manager"].apis.getDocumentActions(),
3578
+ children: (actions2) => {
3579
+ const tableRowActions = actions2.filter((action) => {
3580
+ const positions = Array.isArray(action.position) ? action.position : [action.position];
3581
+ return positions.includes("table-row");
3582
+ });
3583
+ return /* @__PURE__ */ jsx(
3584
+ DocumentActionsMenu,
3585
+ {
3586
+ actions: tableRowActions,
3587
+ label: formatMessage({
3588
+ id: "content-manager.containers.list.table.row-actions",
3589
+ defaultMessage: "Row action"
3590
+ }),
3591
+ variant: "ghost"
3592
+ }
3593
+ );
3268
3594
  }
3269
- panels[currentPanelIndex].push(row);
3270
3595
  }
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
3596
  );
3597
+ };
3598
+ const EditAction = ({ documentId }) => {
3599
+ const navigate = useNavigate();
3600
+ const { formatMessage } = useIntl();
3601
+ const { canRead } = useDocumentRBAC("EditAction", ({ canRead: canRead2 }) => ({ canRead: canRead2 }));
3602
+ const { toggleNotification } = useNotification();
3603
+ const [{ query }] = useQueryParams();
3300
3604
  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
3605
+ disabled: !canRead,
3606
+ icon: /* @__PURE__ */ jsx(StyledPencil, {}),
3607
+ label: formatMessage({
3608
+ id: "content-manager.actions.edit.label",
3609
+ defaultMessage: "Edit"
3610
+ }),
3611
+ position: "table-row",
3612
+ onClick: async () => {
3613
+ if (!documentId) {
3614
+ console.error(
3615
+ "You're trying to edit a document without an id, this is likely a bug with Strapi. Please open an issue."
3616
+ );
3617
+ toggleNotification({
3618
+ message: formatMessage({
3619
+ id: "content-manager.actions.edit.error",
3620
+ defaultMessage: "An error occurred while trying to edit the document."
3621
+ }),
3622
+ type: "danger"
3623
+ });
3624
+ return;
3625
+ }
3626
+ navigate({
3627
+ pathname: documentId,
3628
+ search: stringify({
3629
+ plugins: query.plugins
3630
+ })
3631
+ });
3312
3632
  }
3313
3633
  };
3314
3634
  };
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;
3635
+ EditAction.type = "edit";
3636
+ const StyledPencil = styled(Pencil)`
3637
+ path {
3638
+ fill: currentColor;
3639
+ }
3640
+ `;
3641
+ const CloneAction = ({ model, documentId }) => {
3642
+ const navigate = useNavigate();
3643
+ const { formatMessage } = useIntl();
3644
+ const { canCreate } = useDocumentRBAC("CloneAction", ({ canCreate: canCreate2 }) => ({ canCreate: canCreate2 }));
3645
+ const { toggleNotification } = useNotification();
3646
+ const { autoClone } = useDocumentActions();
3647
+ const [prohibitedFields, setProhibitedFields] = React.useState([]);
3648
+ return {
3649
+ disabled: !canCreate,
3650
+ icon: /* @__PURE__ */ jsx(StyledDuplicate, {}),
3651
+ label: formatMessage({
3652
+ id: "content-manager.actions.clone.label",
3653
+ defaultMessage: "Duplicate"
3654
+ }),
3655
+ position: "table-row",
3656
+ onClick: async () => {
3657
+ if (!documentId) {
3658
+ console.error(
3659
+ "You're trying to clone a document in the table without an id, this is likely a bug with Strapi. Please open an issue."
3660
+ );
3661
+ toggleNotification({
3662
+ message: formatMessage({
3663
+ id: "content-manager.actions.clone.error",
3664
+ defaultMessage: "An error occurred while trying to clone the document."
3665
+ }),
3666
+ type: "danger"
3667
+ });
3668
+ return;
3669
+ }
3670
+ const res = await autoClone({ model, sourceId: documentId });
3671
+ if ("data" in res) {
3672
+ navigate(res.data.documentId);
3673
+ return true;
3674
+ }
3675
+ if (isBaseQueryError(res.error) && res.error.details && typeof res.error.details === "object" && "prohibitedFields" in res.error.details && Array.isArray(res.error.details.prohibitedFields)) {
3676
+ const prohibitedFields2 = res.error.details.prohibitedFields;
3677
+ setProhibitedFields(prohibitedFields2);
3321
3678
  }
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
3679
  },
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
3680
+ dialog: {
3681
+ type: "modal",
3682
+ title: formatMessage({
3683
+ id: "content-manager.containers.list.autoCloneModal.header",
3684
+ defaultMessage: "Duplicate"
3685
+ }),
3686
+ content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
3687
+ footer: ({ onClose }) => {
3688
+ return /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
3689
+ /* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
3690
+ id: "cancel",
3691
+ defaultMessage: "Cancel"
3692
+ }) }),
3693
+ /* @__PURE__ */ jsx(
3694
+ LinkButton,
3695
+ {
3696
+ tag: NavLink,
3697
+ to: {
3698
+ pathname: `clone/${documentId}`
3699
+ },
3700
+ children: formatMessage({
3701
+ id: "content-manager.containers.list.autoCloneModal.create",
3702
+ defaultMessage: "Create"
3703
+ })
3704
+ }
3705
+ )
3706
+ ] });
3707
+ }
3374
3708
  }
3375
3709
  };
3376
3710
  };
3377
- const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
3378
- return columns.map((name) => {
3379
- const attribute = attributes[name];
3380
- if (!attribute) {
3381
- return null;
3711
+ CloneAction.type = "clone";
3712
+ const StyledDuplicate = styled(Duplicate)`
3713
+ path {
3714
+ fill: currentColor;
3715
+ }
3716
+ `;
3717
+ const DEFAULT_TABLE_ROW_ACTIONS = [EditAction, CloneAction];
3718
+ class ContentManagerPlugin {
3719
+ /**
3720
+ * The following properties are the stored ones provided by any plugins registering with
3721
+ * the content-manager. The function calls however, need to be called at runtime in the
3722
+ * application, so instead we collate them and run them later with the complete list incl.
3723
+ * ones already registered & the context of the view.
3724
+ */
3725
+ bulkActions = [...DEFAULT_BULK_ACTIONS];
3726
+ documentActions = [
3727
+ ...DEFAULT_ACTIONS,
3728
+ ...DEFAULT_TABLE_ROW_ACTIONS,
3729
+ ...DEFAULT_HEADER_ACTIONS,
3730
+ HistoryAction
3731
+ ];
3732
+ editViewSidePanels = [ActionsPanel];
3733
+ headerActions = [];
3734
+ constructor() {
3735
+ }
3736
+ addEditViewSidePanel(panels) {
3737
+ if (Array.isArray(panels)) {
3738
+ this.editViewSidePanels = [...this.editViewSidePanels, ...panels];
3739
+ } else if (typeof panels === "function") {
3740
+ this.editViewSidePanels = panels(this.editViewSidePanels);
3741
+ } else {
3742
+ throw new Error(
3743
+ `Expected the \`panels\` passed to \`addEditViewSidePanel\` to be an array or a function, but received ${getPrintableType(
3744
+ panels
3745
+ )}`
3746
+ );
3382
3747
  }
3383
- const metadata = metadatas[name];
3384
- const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
3748
+ }
3749
+ addDocumentAction(actions2) {
3750
+ if (Array.isArray(actions2)) {
3751
+ this.documentActions = [...this.documentActions, ...actions2];
3752
+ } else if (typeof actions2 === "function") {
3753
+ this.documentActions = actions2(this.documentActions);
3754
+ } else {
3755
+ throw new Error(
3756
+ `Expected the \`actions\` passed to \`addDocumentAction\` to be an array or a function, but received ${getPrintableType(
3757
+ actions2
3758
+ )}`
3759
+ );
3760
+ }
3761
+ }
3762
+ addDocumentHeaderAction(actions2) {
3763
+ if (Array.isArray(actions2)) {
3764
+ this.headerActions = [...this.headerActions, ...actions2];
3765
+ } else if (typeof actions2 === "function") {
3766
+ this.headerActions = actions2(this.headerActions);
3767
+ } else {
3768
+ throw new Error(
3769
+ `Expected the \`actions\` passed to \`addDocumentHeaderAction\` to be an array or a function, but received ${getPrintableType(
3770
+ actions2
3771
+ )}`
3772
+ );
3773
+ }
3774
+ }
3775
+ addBulkAction(actions2) {
3776
+ if (Array.isArray(actions2)) {
3777
+ this.bulkActions = [...this.bulkActions, ...actions2];
3778
+ } else if (typeof actions2 === "function") {
3779
+ this.bulkActions = actions2(this.bulkActions);
3780
+ } else {
3781
+ throw new Error(
3782
+ `Expected the \`actions\` passed to \`addBulkAction\` to be an array or a function, but received ${getPrintableType(
3783
+ actions2
3784
+ )}`
3785
+ );
3786
+ }
3787
+ }
3788
+ get config() {
3385
3789
  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
3790
+ id: PLUGIN_ID,
3791
+ name: "Content Manager",
3792
+ injectionZones: INJECTION_ZONES,
3793
+ apis: {
3794
+ addBulkAction: this.addBulkAction.bind(this),
3795
+ addDocumentAction: this.addDocumentAction.bind(this),
3796
+ addDocumentHeaderAction: this.addDocumentHeaderAction.bind(this),
3797
+ addEditViewSidePanel: this.addEditViewSidePanel.bind(this),
3798
+ getBulkActions: () => this.bulkActions,
3799
+ getDocumentActions: () => this.documentActions,
3800
+ getEditViewSidePanels: () => this.editViewSidePanels,
3801
+ getHeaderActions: () => this.headerActions
3802
+ }
3395
3803
  };
3396
- }).filter((field) => field !== null);
3804
+ }
3805
+ }
3806
+ const getPrintableType = (value) => {
3807
+ const nativeType = typeof value;
3808
+ if (nativeType === "object") {
3809
+ if (value === null)
3810
+ return "null";
3811
+ if (Array.isArray(value))
3812
+ return "array";
3813
+ if (value instanceof Object && value.constructor.name !== "Object") {
3814
+ return value.constructor.name;
3815
+ }
3816
+ }
3817
+ return nativeType;
3818
+ };
3819
+ const initialState = {
3820
+ collectionTypeLinks: [],
3821
+ components: [],
3822
+ fieldSizes: {},
3823
+ models: [],
3824
+ singleTypeLinks: [],
3825
+ isLoading: true
3397
3826
  };
3827
+ const appSlice = createSlice({
3828
+ name: "app",
3829
+ initialState,
3830
+ reducers: {
3831
+ setInitialData(state, action) {
3832
+ const {
3833
+ authorizedCollectionTypeLinks,
3834
+ authorizedSingleTypeLinks,
3835
+ components,
3836
+ contentTypeSchemas,
3837
+ fieldSizes
3838
+ } = action.payload;
3839
+ state.collectionTypeLinks = authorizedCollectionTypeLinks.filter(
3840
+ ({ isDisplayed }) => isDisplayed
3841
+ );
3842
+ state.singleTypeLinks = authorizedSingleTypeLinks.filter(({ isDisplayed }) => isDisplayed);
3843
+ state.components = components;
3844
+ state.models = contentTypeSchemas;
3845
+ state.fieldSizes = fieldSizes;
3846
+ state.isLoading = false;
3847
+ }
3848
+ }
3849
+ });
3850
+ const { actions, reducer: reducer$1 } = appSlice;
3851
+ const { setInitialData } = actions;
3852
+ const reducer = combineReducers({
3853
+ app: reducer$1
3854
+ });
3398
3855
  const index = {
3399
3856
  register(app) {
3400
3857
  const cm = new ContentManagerPlugin();
@@ -3409,7 +3866,7 @@ const index = {
3409
3866
  defaultMessage: "Content Manager"
3410
3867
  },
3411
3868
  permissions: [],
3412
- Component: () => import("./layout-BinjszSQ.mjs").then((mod) => ({ default: mod.Layout })),
3869
+ Component: () => import("./layout-BzAbmoO6.mjs").then((mod) => ({ default: mod.Layout })),
3413
3870
  position: 1
3414
3871
  });
3415
3872
  app.registerPlugin(cm.config);
@@ -3417,7 +3874,7 @@ const index = {
3417
3874
  async registerTrads({ locales }) {
3418
3875
  const importedTrads = await Promise.all(
3419
3876
  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 }) => {
3877
+ 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-Ux26r5pl.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
3878
  return {
3422
3879
  data: prefixPluginTranslations(data, PLUGIN_ID),
3423
3880
  locale
@@ -3450,31 +3907,31 @@ export {
3450
3907
  RelativeTime as R,
3451
3908
  SINGLE_TYPES as S,
3452
3909
  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,
3910
+ useGetInitialDataQuery as a,
3911
+ useGetAllContentTypeSettingsQuery as b,
3912
+ useDoc as c,
3913
+ buildValidParams as d,
3914
+ contentManagerApi as e,
3915
+ useDocumentRBAC as f,
3459
3916
  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,
3917
+ useDocumentLayout as h,
3918
+ createYupSchema as i,
3919
+ Header as j,
3920
+ PERMISSIONS as k,
3921
+ DocumentRBAC as l,
3922
+ DOCUMENT_META_FIELDS as m,
3923
+ useDocLayout as n,
3467
3924
  useGetContentTypeConfigurationQuery as o,
3468
3925
  CREATOR_FIELDS as p,
3469
3926
  getMainField as q,
3470
3927
  routes as r,
3471
3928
  setInitialData as s,
3472
3929
  getDisplayName as t,
3473
- useGetInitialDataQuery as u,
3930
+ useContentTypeSchema as u,
3474
3931
  checkIfAttributeIsDisplayable as v,
3475
3932
  useGetAllDocumentsQuery as w,
3476
3933
  convertListLayoutToFieldLayouts as x,
3477
3934
  capitalise as y,
3478
3935
  useUpdateContentTypeConfigurationMutation as z
3479
3936
  };
3480
- //# sourceMappingURL=index-BaGHmIir.mjs.map
3937
+ //# sourceMappingURL=index-Drt2DN7v.mjs.map