@strapi/i18n 0.0.0-experimental.826f263c58b6886b849d3f03b81f7a530bc51c91 → 0.0.0-experimental.82afe56cecefd0078d534e25909834ecf5fdd404

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 (117) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/{SettingsPage-0FFSTUW2.mjs → SettingsPage-B-KzAqT3.mjs} +95 -108
  3. package/dist/_chunks/SettingsPage-B-KzAqT3.mjs.map +1 -0
  4. package/dist/_chunks/{SettingsPage-DnLLGeBa.js → SettingsPage-hyOi94O9.js} +94 -108
  5. package/dist/_chunks/SettingsPage-hyOi94O9.js.map +1 -0
  6. package/dist/_chunks/{de-DtWiGdHl.js → de-BOhNX_-5.js} +1 -2
  7. package/dist/_chunks/de-BOhNX_-5.js.map +1 -0
  8. package/dist/_chunks/{de-9eCAqqrB.mjs → de-D80IRBP9.mjs} +1 -2
  9. package/dist/_chunks/de-D80IRBP9.mjs.map +1 -0
  10. package/dist/_chunks/{dk-2qBjxt-P.mjs → dk-CJ6Zzz78.mjs} +1 -2
  11. package/dist/_chunks/dk-CJ6Zzz78.mjs.map +1 -0
  12. package/dist/_chunks/{dk-D8C-casx.js → dk-cjXm0p3m.js} +1 -2
  13. package/dist/_chunks/dk-cjXm0p3m.js.map +1 -0
  14. package/dist/_chunks/{en-18tWw4P6.mjs → en-BTyF7WVW.mjs} +12 -4
  15. package/dist/_chunks/en-BTyF7WVW.mjs.map +1 -0
  16. package/dist/_chunks/{en-Kv6y9zPQ.js → en-UlC0jh2t.js} +12 -4
  17. package/dist/_chunks/en-UlC0jh2t.js.map +1 -0
  18. package/dist/_chunks/{es-DlmMVaBG.mjs → es-V8WnPN7w.mjs} +1 -2
  19. package/dist/_chunks/es-V8WnPN7w.mjs.map +1 -0
  20. package/dist/_chunks/{es-DS-XFGSw.js → es-hr9b_HLp.js} +1 -2
  21. package/dist/_chunks/es-hr9b_HLp.js.map +1 -0
  22. package/dist/_chunks/{fr-BTjekDpq.js → fr-BFmBbE0H.js} +1 -2
  23. package/dist/_chunks/fr-BFmBbE0H.js.map +1 -0
  24. package/dist/_chunks/{fr-3S6ke71d.mjs → fr-F94noFiV.mjs} +1 -2
  25. package/dist/_chunks/fr-F94noFiV.mjs.map +1 -0
  26. package/dist/_chunks/{index-BddUXwss.mjs → index-BcYj5jo9.mjs} +476 -275
  27. package/dist/_chunks/index-BcYj5jo9.mjs.map +1 -0
  28. package/dist/_chunks/{index-DtEKsPcR.js → index-C8NzsAKp.js} +483 -283
  29. package/dist/_chunks/index-C8NzsAKp.js.map +1 -0
  30. package/dist/_chunks/{ko-DmcGUBQ3.js → ko-C40pNQ9b.js} +1 -2
  31. package/dist/_chunks/ko-C40pNQ9b.js.map +1 -0
  32. package/dist/_chunks/{ko-qTjQ8IMw.mjs → ko-CF-P3Car.mjs} +1 -2
  33. package/dist/_chunks/ko-CF-P3Car.mjs.map +1 -0
  34. package/dist/_chunks/{pl-B67TSHqT.mjs → pl-Dxr9RUmD.mjs} +1 -2
  35. package/dist/_chunks/pl-Dxr9RUmD.mjs.map +1 -0
  36. package/dist/_chunks/{pl-Cn5RYonZ.js → pl-JtWBy-JQ.js} +1 -2
  37. package/dist/_chunks/pl-JtWBy-JQ.js.map +1 -0
  38. package/dist/_chunks/{ru-hagMa57T.mjs → ru-B-4sVwXN.mjs} +1 -2
  39. package/dist/_chunks/ru-B-4sVwXN.mjs.map +1 -0
  40. package/dist/_chunks/{ru-BMBgVL3s.js → ru-COSWt3Nu.js} +1 -2
  41. package/dist/_chunks/ru-COSWt3Nu.js.map +1 -0
  42. package/dist/_chunks/{tr-CarUU76c.js → tr-BVj1O5ch.js} +1 -2
  43. package/dist/_chunks/tr-BVj1O5ch.js.map +1 -0
  44. package/dist/_chunks/{tr-Dw_jmkG-.mjs → tr-Ccu6Yj11.mjs} +1 -2
  45. package/dist/_chunks/tr-Ccu6Yj11.mjs.map +1 -0
  46. package/dist/_chunks/{zh-57YM4amO.mjs → zh-BIz395Ms.mjs} +1 -2
  47. package/dist/_chunks/zh-BIz395Ms.mjs.map +1 -0
  48. package/dist/_chunks/{zh-Hans-Dyc-aR-h.mjs → zh-Hans-Bfo6_TCM.mjs} +1 -2
  49. package/dist/_chunks/zh-Hans-Bfo6_TCM.mjs.map +1 -0
  50. package/dist/_chunks/{zh-Hans-DSHIXAa3.js → zh-Hans-DIEm_EMC.js} +1 -2
  51. package/dist/_chunks/zh-Hans-DIEm_EMC.js.map +1 -0
  52. package/dist/_chunks/{zh-CukOviB0.js → zh-wkBPBkhc.js} +1 -2
  53. package/dist/_chunks/zh-wkBPBkhc.js.map +1 -0
  54. package/dist/admin/index.js +1 -1
  55. package/dist/admin/index.mjs +1 -1
  56. package/dist/admin/src/components/BulkLocaleActionModal.d.ts +2 -1
  57. package/dist/admin/src/components/CMHeaderActions.d.ts +29 -3
  58. package/dist/admin/src/components/CreateLocale.d.ts +6 -6
  59. package/dist/admin/src/components/EditLocale.d.ts +5 -4
  60. package/dist/admin/src/components/LocaleListCell.d.ts +4 -4
  61. package/dist/admin/src/contentReleasesHooks/releaseDetailsView.d.ts +9 -5
  62. package/dist/admin/src/utils/clean.d.ts +4 -0
  63. package/dist/admin/src/utils/schemas.d.ts +1 -0
  64. package/dist/server/index.js +418 -478
  65. package/dist/server/index.js.map +1 -1
  66. package/dist/server/index.mjs +419 -478
  67. package/dist/server/index.mjs.map +1 -1
  68. package/dist/server/src/bootstrap.d.ts +1 -4
  69. package/dist/server/src/bootstrap.d.ts.map +1 -1
  70. package/dist/server/src/index.d.ts +21 -13
  71. package/dist/server/src/index.d.ts.map +1 -1
  72. package/dist/server/src/register.d.ts.map +1 -1
  73. package/dist/server/src/services/index.d.ts +20 -10
  74. package/dist/server/src/services/index.d.ts.map +1 -1
  75. package/dist/server/src/services/permissions/actions.d.ts +14 -2
  76. package/dist/server/src/services/permissions/actions.d.ts.map +1 -1
  77. package/dist/server/src/services/permissions.d.ts +14 -2
  78. package/dist/server/src/services/permissions.d.ts.map +1 -1
  79. package/dist/server/src/services/sanitize/index.d.ts +11 -0
  80. package/dist/server/src/services/sanitize/index.d.ts.map +1 -0
  81. package/dist/server/src/utils/index.d.ts +2 -2
  82. package/dist/server/src/utils/index.d.ts.map +1 -1
  83. package/package.json +13 -14
  84. package/dist/_chunks/SettingsPage-0FFSTUW2.mjs.map +0 -1
  85. package/dist/_chunks/SettingsPage-DnLLGeBa.js.map +0 -1
  86. package/dist/_chunks/de-9eCAqqrB.mjs.map +0 -1
  87. package/dist/_chunks/de-DtWiGdHl.js.map +0 -1
  88. package/dist/_chunks/dk-2qBjxt-P.mjs.map +0 -1
  89. package/dist/_chunks/dk-D8C-casx.js.map +0 -1
  90. package/dist/_chunks/en-18tWw4P6.mjs.map +0 -1
  91. package/dist/_chunks/en-Kv6y9zPQ.js.map +0 -1
  92. package/dist/_chunks/es-DS-XFGSw.js.map +0 -1
  93. package/dist/_chunks/es-DlmMVaBG.mjs.map +0 -1
  94. package/dist/_chunks/fr-3S6ke71d.mjs.map +0 -1
  95. package/dist/_chunks/fr-BTjekDpq.js.map +0 -1
  96. package/dist/_chunks/index-BddUXwss.mjs.map +0 -1
  97. package/dist/_chunks/index-DtEKsPcR.js.map +0 -1
  98. package/dist/_chunks/ko-DmcGUBQ3.js.map +0 -1
  99. package/dist/_chunks/ko-qTjQ8IMw.mjs.map +0 -1
  100. package/dist/_chunks/pl-B67TSHqT.mjs.map +0 -1
  101. package/dist/_chunks/pl-Cn5RYonZ.js.map +0 -1
  102. package/dist/_chunks/ru-BMBgVL3s.js.map +0 -1
  103. package/dist/_chunks/ru-hagMa57T.mjs.map +0 -1
  104. package/dist/_chunks/tr-CarUU76c.js.map +0 -1
  105. package/dist/_chunks/tr-Dw_jmkG-.mjs.map +0 -1
  106. package/dist/_chunks/zh-57YM4amO.mjs.map +0 -1
  107. package/dist/_chunks/zh-CukOviB0.js.map +0 -1
  108. package/dist/_chunks/zh-Hans-DSHIXAa3.js.map +0 -1
  109. package/dist/_chunks/zh-Hans-Dyc-aR-h.mjs.map +0 -1
  110. package/dist/admin/src/components/Initializer.d.ts +0 -5
  111. package/dist/server/src/migrations/content-type/disable/index.d.ts +0 -3
  112. package/dist/server/src/migrations/content-type/disable/index.d.ts.map +0 -1
  113. package/dist/server/src/migrations/content-type/enable/index.d.ts +0 -3
  114. package/dist/server/src/migrations/content-type/enable/index.d.ts.map +0 -1
  115. package/dist/server/src/services/entity-service-decorator.d.ts +0 -29
  116. package/dist/server/src/services/entity-service-decorator.d.ts.map +0 -1
  117. package/strapi-server.js +0 -3
@@ -1,24 +1,32 @@
1
1
  import get from "lodash/get";
2
2
  import * as yup from "yup";
3
- import { jsxs, Fragment, jsx } from "react/jsx-runtime";
3
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
4
4
  import * as React from "react";
5
- import { Typography, Field, Checkbox, Dialog, DialogBody, Flex, DialogFooter, Button as Button$1, Box, Status, IconButton, Tooltip, SingleSelect, SingleSelectOption, VisuallyHidden, useCollator, Popover } from "@strapi/design-system";
6
- import { WarningCircle, Pencil, CrossCircle, CheckCircle, ArrowsCounterClockwise, Trash, ListPlus, Earth, EarthStriked, CaretDown } from "@strapi/icons";
5
+ import { Typography, Dialog, Field, Checkbox, Flex, Button, Modal, Box, Status, IconButton, Tooltip, SingleSelect, SingleSelectOption, VisuallyHidden, useCollator, Popover } from "@strapi/design-system";
6
+ import { WarningCircle, Pencil, CrossCircle, CheckCircle, ArrowsCounterClockwise, Trash, Plus, Download, ListPlus, Cross, Earth, EarthStriked, CaretDown } from "@strapi/icons";
7
7
  import { useIntl } from "react-intl";
8
8
  import { styled } from "styled-components";
9
- import { useAuth, adminApi, useTable, Table, useQueryParams, useNotification, useAPIErrorHandler } from "@strapi/admin/strapi-admin";
9
+ import { skipToken } from "@reduxjs/toolkit/query";
10
+ import { useAuth, adminApi, useTable, Table, useQueryParams, useForm, useNotification, useAPIErrorHandler } from "@strapi/admin/strapi-admin";
10
11
  import { unstable_useDocument, unstable_useDocumentActions, buildValidParams } from "@strapi/content-manager/strapi-admin";
11
12
  import { useParams, Link, useNavigate, matchPath } from "react-router-dom";
12
13
  import * as qs from "qs";
13
14
  import { stringify } from "qs";
14
15
  import omit from "lodash/omit";
15
- const __variableDynamicImportRuntimeHelper = (glob, path) => {
16
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
16
17
  const v = glob[path];
17
18
  if (v) {
18
19
  return typeof v === "function" ? v() : Promise.resolve(v);
19
20
  }
20
21
  return new Promise((_, reject) => {
21
- (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(reject.bind(null, new Error("Unknown variable dynamic import: " + path)));
22
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
23
+ reject.bind(
24
+ null,
25
+ new Error(
26
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
27
+ )
28
+ )
29
+ );
22
30
  });
23
31
  };
24
32
  const pluginId = "i18n";
@@ -47,9 +55,7 @@ const CheckboxConfirmation = ({
47
55
  };
48
56
  const handleConfirm = () => {
49
57
  onChange({ target: { name, value: false, type: "checkbox" } });
50
- setIsOpen(false);
51
58
  };
52
- const handleToggle = () => setIsOpen((prev) => !prev);
53
59
  const label = intlLabel.id ? formatMessage(
54
60
  { id: intlLabel.id, defaultMessage: intlLabel.defaultMessage },
55
61
  { ...intlLabel.values }
@@ -58,35 +64,36 @@ const CheckboxConfirmation = ({
58
64
  { id: description.id, defaultMessage: description.defaultMessage },
59
65
  { ...description.values }
60
66
  ) : "";
61
- return /* @__PURE__ */ jsxs(Fragment, { children: [
67
+ return /* @__PURE__ */ jsxs(Dialog.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
62
68
  /* @__PURE__ */ jsxs(Field.Root, { hint, name, children: [
63
- /* @__PURE__ */ jsx(Checkbox, { onValueChange: handleChange, value, type: "checkbox", children: label }),
69
+ /* @__PURE__ */ jsx(Checkbox, { onCheckedChange: handleChange, checked: value, children: label }),
64
70
  /* @__PURE__ */ jsx(Field.Hint, {})
65
71
  ] }),
66
- isOpen && /* @__PURE__ */ jsxs(Dialog, { onClose: handleToggle, title: "Confirmation", isOpen, children: [
67
- /* @__PURE__ */ jsx(DialogBody, { icon: /* @__PURE__ */ jsx(WarningCircle, {}), children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
68
- /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(TextAlignTypography, { id: "confirm-description", children: formatMessage({
72
+ /* @__PURE__ */ jsxs(Dialog.Content, { children: [
73
+ /* @__PURE__ */ jsx(Dialog.Header, { children: formatMessage({
74
+ id: getTranslation("CheckboxConfirmation.Modal.title"),
75
+ defaultMessage: "Disable localization"
76
+ }) }),
77
+ /* @__PURE__ */ jsx(Dialog.Body, { icon: /* @__PURE__ */ jsx(WarningCircle, {}), children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
78
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(TextAlignTypography, { children: formatMessage({
69
79
  id: getTranslation("CheckboxConfirmation.Modal.content"),
70
80
  defaultMessage: "Disabling localization will engender the deletion of all your content but the one associated to your default locale (if existing)."
71
81
  }) }) }),
72
- /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", id: "confirm-description", children: formatMessage({
82
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: formatMessage({
73
83
  id: getTranslation("CheckboxConfirmation.Modal.body"),
74
84
  defaultMessage: "Do you want to disable it?"
75
85
  }) }) })
76
86
  ] }) }),
77
- /* @__PURE__ */ jsx(
78
- DialogFooter,
79
- {
80
- startAction: /* @__PURE__ */ jsx(Button$1, { onClick: handleToggle, variant: "tertiary", children: formatMessage({
81
- id: "components.popUpWarning.button.cancel",
82
- defaultMessage: "No, cancel"
83
- }) }),
84
- endAction: /* @__PURE__ */ jsx(Button$1, { variant: "danger-light", onClick: handleConfirm, children: formatMessage({
85
- id: getTranslation("CheckboxConfirmation.Modal.button-confirm"),
86
- defaultMessage: "Yes, disable"
87
- }) })
88
- }
89
- )
87
+ /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
88
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
89
+ id: "components.popUpWarning.button.cancel",
90
+ defaultMessage: "No, cancel"
91
+ }) }) }),
92
+ /* @__PURE__ */ jsx(Dialog.Action, { children: /* @__PURE__ */ jsx(Button, { variant: "danger-light", onClick: handleConfirm, children: formatMessage({
93
+ id: getTranslation("CheckboxConfirmation.Modal.button-confirm"),
94
+ defaultMessage: "Yes, disable"
95
+ }) }) })
96
+ ] })
90
97
  ] })
91
98
  ] });
92
99
  };
@@ -137,7 +144,7 @@ const useI18n = () => {
137
144
  model: params.slug
138
145
  },
139
146
  {
140
- skip: !params.slug || !params.collectionType
147
+ skip: true
141
148
  }
142
149
  );
143
150
  if (doesPluginOptionsHaveI18nLocalized(schema?.pluginOptions)) {
@@ -217,10 +224,94 @@ const relationsApi = i18nApi.injectEndpoints({
217
224
  })
218
225
  });
219
226
  const { useGetManyDraftRelationCountQuery } = relationsApi;
227
+ const cleanData = (data, schema, components) => {
228
+ const cleanedData = removeFields(data, [
229
+ "createdAt",
230
+ "createdBy",
231
+ "updatedAt",
232
+ "updatedBy",
233
+ "id",
234
+ "documentId",
235
+ "publishedAt",
236
+ "strapi_stage",
237
+ "strapi_assignee",
238
+ "locale",
239
+ "status"
240
+ ]);
241
+ const cleanedDataWithoutPasswordAndRelation = recursiveRemoveFieldTypes(
242
+ cleanedData,
243
+ schema,
244
+ components,
245
+ ["relation", "password"]
246
+ );
247
+ return cleanedDataWithoutPasswordAndRelation;
248
+ };
249
+ const removeFields = (data, fields) => {
250
+ return Object.keys(data).reduce((acc, current) => {
251
+ if (fields.includes(current)) {
252
+ return acc;
253
+ }
254
+ acc[current] = data[current];
255
+ return acc;
256
+ }, {});
257
+ };
258
+ const recursiveRemoveFieldTypes = (data, schema, components, fields) => {
259
+ return Object.keys(data).reduce((acc, current) => {
260
+ const attribute = schema.attributes[current] ?? { type: void 0 };
261
+ if (fields.includes(attribute.type)) {
262
+ return acc;
263
+ }
264
+ if (attribute.type === "dynamiczone") {
265
+ acc[current] = data[current].map((componentValue, index2) => {
266
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(
267
+ componentValue,
268
+ components[componentValue.__component],
269
+ components,
270
+ fields
271
+ );
272
+ return {
273
+ ...rest,
274
+ __temp_key__: index2 + 1
275
+ };
276
+ });
277
+ } else if (attribute.type === "component") {
278
+ const { repeatable, component } = attribute;
279
+ if (repeatable) {
280
+ acc[current] = (data[current] ?? []).map((compoData, index2) => {
281
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(
282
+ compoData,
283
+ components[component],
284
+ components,
285
+ fields
286
+ );
287
+ return {
288
+ ...rest,
289
+ __temp_key__: index2 + 1
290
+ };
291
+ });
292
+ } else {
293
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(
294
+ data[current] ?? {},
295
+ components[component],
296
+ components,
297
+ fields
298
+ );
299
+ acc[current] = rest;
300
+ }
301
+ } else {
302
+ acc[current] = data[current];
303
+ }
304
+ return acc;
305
+ }, {});
306
+ };
220
307
  const isErrorMessageDescriptor = (object) => {
221
308
  return typeof object === "object" && object !== null && "id" in object && "defaultMessage" in object;
222
309
  };
223
- const EntryValidationText = ({ status = "draft", validationErrors }) => {
310
+ const EntryValidationText = ({
311
+ status = "draft",
312
+ validationErrors,
313
+ action
314
+ }) => {
224
315
  const { formatMessage } = useIntl();
225
316
  const getErrorStr = (key, value) => {
226
317
  if (typeof value === "string") {
@@ -254,30 +345,63 @@ const EntryValidationText = ({ status = "draft", validationErrors }) => {
254
345
  ) })
255
346
  ] });
256
347
  }
257
- if (status === "published") {
258
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
259
- /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
260
- /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
261
- id: "content-manager.bulk-publish.already-published",
262
- defaultMessage: "Already Published"
263
- }) })
264
- ] });
265
- }
266
- if (status === "modified") {
267
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
268
- /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
269
- /* @__PURE__ */ jsx(Typography, { children: formatMessage({
270
- id: "app.utils.ready-to-publish-changes",
271
- defaultMessage: "Ready to publish changes"
272
- }) })
273
- ] });
274
- }
348
+ const getStatusMessage = () => {
349
+ if (action === "bulk-publish") {
350
+ if (status === "published") {
351
+ return {
352
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
353
+ text: formatMessage({
354
+ id: "content-manager.bulk-publish.already-published",
355
+ defaultMessage: "Already Published"
356
+ }),
357
+ textColor: "success600",
358
+ fontWeight: "bold"
359
+ };
360
+ } else if (status === "modified") {
361
+ return {
362
+ icon: /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
363
+ text: formatMessage({
364
+ id: "app.utils.ready-to-publish-changes",
365
+ defaultMessage: "Ready to publish changes"
366
+ })
367
+ };
368
+ } else {
369
+ return {
370
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
371
+ text: formatMessage({
372
+ id: "app.utils.ready-to-publish",
373
+ defaultMessage: "Ready to publish"
374
+ })
375
+ };
376
+ }
377
+ } else {
378
+ if (status === "draft") {
379
+ return {
380
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
381
+ text: formatMessage({
382
+ id: "content-manager.bulk-unpublish.already-unpublished",
383
+ defaultMessage: "Already Unpublished"
384
+ }),
385
+ textColor: "success600",
386
+ fontWeight: "bold"
387
+ };
388
+ } else {
389
+ return {
390
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
391
+ text: formatMessage({
392
+ id: "app.utils.ready-to-unpublish-changes",
393
+ defaultMessage: "Ready to unpublish"
394
+ }),
395
+ textColor: "success600",
396
+ fontWeight: "bold"
397
+ };
398
+ }
399
+ }
400
+ };
401
+ const { icon, text, textColor = "success600", fontWeight = "normal" } = getStatusMessage();
275
402
  return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
276
- /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
277
- /* @__PURE__ */ jsx(Typography, { children: formatMessage({
278
- id: "app.utils.ready-to-publish",
279
- defaultMessage: "Ready to publish"
280
- }) })
403
+ icon,
404
+ /* @__PURE__ */ jsx(Typography, { textColor, fontWeight, children: text })
281
405
  ] });
282
406
  };
283
407
  const BoldChunk = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
@@ -285,7 +409,8 @@ const BulkLocaleActionModal = ({
285
409
  headers,
286
410
  rows,
287
411
  localesMetadata,
288
- validationErrors = {}
412
+ validationErrors = {},
413
+ action
289
414
  }) => {
290
415
  const { formatMessage } = useIntl();
291
416
  const selectedRows = useTable(
@@ -298,27 +423,29 @@ const BulkLocaleActionModal = ({
298
423
  return acc;
299
424
  }, {});
300
425
  const localesWithErrors = Object.keys(validationErrors);
301
- const alreadyPublishedCount = selectedRows.filter(
426
+ const publishedCount = selectedRows.filter(
302
427
  ({ locale }) => currentStatusByLocale[locale] === "published"
303
428
  ).length;
304
- const readyToPublishCount = selectedRows.filter(
429
+ const draftCount = selectedRows.filter(
305
430
  ({ locale }) => (currentStatusByLocale[locale] === "draft" || currentStatusByLocale[locale] === "modified") && !localesWithErrors.includes(locale)
306
431
  ).length;
307
432
  const withErrorsCount = localesWithErrors.length;
433
+ const messageId = action === "bulk-publish" ? "content-manager.containers.list.selectedEntriesModal.selectedCount.publish" : "content-manager.containers.list.selectedEntriesModal.selectedCount.unpublish";
434
+ const defaultMessage = action === "bulk-publish" ? "<b>{publishedCount}</b> {publishedCount, plural, =0 {entries} one {entry} other {entries}} already published. <b>{draftCount}</b> {draftCount, plural, =0 {entries} one {entry} other {entries}} ready to publish. <b>{withErrorsCount}</b> {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action." : "<b>{draftCount}</b> {draftCount, plural, =0 {entries} one {entry} other {entries}} already unpublished. <b>{publishedCount}</b> {publishedCount, plural, =0 {entries} one {entry} other {entries}} ready to unpublish.";
308
435
  return formatMessage(
309
436
  {
310
- id: "content-manager.containers.list.selectedEntriesModal.selectedCount",
311
- 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."
437
+ id: messageId,
438
+ defaultMessage
312
439
  },
313
440
  {
314
441
  withErrorsCount,
315
- readyToPublishCount,
316
- alreadyPublishedCount,
442
+ draftCount,
443
+ publishedCount,
317
444
  b: BoldChunk
318
445
  }
319
446
  );
320
447
  };
321
- return /* @__PURE__ */ jsxs(React.Fragment, { children: [
448
+ return /* @__PURE__ */ jsxs(Modal.Body, { children: [
322
449
  /* @__PURE__ */ jsx(Typography, { children: getFormattedCountMessage() }),
323
450
  /* @__PURE__ */ jsx(Box, { marginTop: 5, children: /* @__PURE__ */ jsxs(Table.Content, { children: [
324
451
  /* @__PURE__ */ jsxs(Table.Head, { children: [
@@ -339,13 +466,12 @@ const BulkLocaleActionModal = ({
339
466
  paddingRight: "6px",
340
467
  paddingTop: "2px",
341
468
  paddingBottom: "2px",
342
- showBullet: false,
343
469
  size: "S",
344
470
  variant: statusVariant,
345
471
  children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
346
472
  }
347
473
  ) }) }),
348
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status }) }),
474
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status, action }) }),
349
475
  /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
350
476
  IconButton,
351
477
  {
@@ -362,7 +488,7 @@ const BulkLocaleActionModal = ({
362
488
  name: locale
363
489
  }
364
490
  ),
365
- borderWidth: 0,
491
+ variant: "ghost",
366
492
  children: /* @__PURE__ */ jsx(Pencil, {})
367
493
  }
368
494
  ) })
@@ -371,6 +497,47 @@ const BulkLocaleActionModal = ({
371
497
  ] }) })
372
498
  ] });
373
499
  };
500
+ const statusVariants = {
501
+ draft: "secondary",
502
+ published: "success",
503
+ modified: "alternative"
504
+ };
505
+ const LocaleOption = ({
506
+ isDraftAndPublishEnabled,
507
+ locale,
508
+ status,
509
+ entryExists
510
+ }) => {
511
+ const { formatMessage } = useIntl();
512
+ if (!entryExists) {
513
+ return formatMessage(
514
+ {
515
+ id: getTranslation("CMEditViewLocalePicker.locale.create"),
516
+ defaultMessage: "Create <bold>{locale}</bold> locale"
517
+ },
518
+ {
519
+ bold: (locale2) => /* @__PURE__ */ jsx("b", { children: locale2 }),
520
+ locale: locale.name
521
+ }
522
+ );
523
+ }
524
+ return /* @__PURE__ */ jsxs(Flex, { width: "100%", gap: 1, justifyContent: "space-between", children: [
525
+ /* @__PURE__ */ jsx(Typography, { children: locale.name }),
526
+ isDraftAndPublishEnabled ? /* @__PURE__ */ jsx(
527
+ Status,
528
+ {
529
+ display: "flex",
530
+ paddingLeft: "6px",
531
+ paddingRight: "6px",
532
+ paddingTop: "2px",
533
+ paddingBottom: "2px",
534
+ size: "S",
535
+ variant: statusVariants[status],
536
+ children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
537
+ }
538
+ ) : null
539
+ ] });
540
+ };
374
541
  const LocalePickerAction = ({
375
542
  document,
376
543
  meta,
@@ -382,7 +549,13 @@ const LocalePickerAction = ({
382
549
  const [{ query }, setQuery] = useQueryParams();
383
550
  const { hasI18n, canCreate, canRead } = useI18n();
384
551
  const { data: locales = [] } = useGetLocalesQuery();
385
- const { schema } = unstable_useDocument({ model, collectionType, documentId });
552
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
553
+ const { schema } = unstable_useDocument({
554
+ model,
555
+ collectionType,
556
+ documentId,
557
+ params: { locale: currentDesiredLocale }
558
+ });
386
559
  const handleSelect = React.useCallback(
387
560
  (value) => {
388
561
  setQuery({
@@ -400,53 +573,50 @@ const LocalePickerAction = ({
400
573
  if (!Array.isArray(locales) || !hasI18n) {
401
574
  return;
402
575
  }
403
- const currentDesiredLocale = query.plugins?.i18n?.locale;
404
576
  const doesLocaleExist = locales.find((loc) => loc.code === currentDesiredLocale);
405
577
  const defaultLocale = locales.find((locale) => locale.isDefault);
406
578
  if (!doesLocaleExist && defaultLocale?.code) {
407
579
  handleSelect(defaultLocale.code);
408
580
  }
409
- }, [handleSelect, hasI18n, locales, query.plugins?.i18n?.locale]);
581
+ }, [handleSelect, hasI18n, locales, currentDesiredLocale]);
582
+ const currentLocale = Array.isArray(locales) ? locales.find((locale) => locale.code === currentDesiredLocale) : void 0;
583
+ const allCurrentLocales = [
584
+ { status: getDocumentStatus(document, meta), locale: currentLocale?.code },
585
+ ...document?.localizations ?? []
586
+ ];
410
587
  if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
411
588
  return null;
412
589
  }
413
- const currentLocale = query.plugins?.i18n?.locale || locales.find((loc) => loc.isDefault)?.code;
414
- const allCurrentLocales = [
415
- { status: getDocumentStatus(document, meta), locale: currentLocale },
416
- ...meta?.availableLocales ?? []
417
- ];
590
+ const displayedLocales = locales.filter((locale) => {
591
+ return canRead.includes(locale.code);
592
+ });
418
593
  return {
419
594
  label: formatMessage({
420
595
  id: getTranslation("Settings.locales.modal.locales.label"),
421
596
  defaultMessage: "Locales"
422
597
  }),
423
- options: locales.map((locale) => {
598
+ options: displayedLocales.map((locale) => {
599
+ const entryWithLocaleExists = allCurrentLocales.some((doc) => doc.locale === locale.code);
424
600
  const currentLocaleDoc = allCurrentLocales.find(
425
601
  (doc) => "locale" in doc ? doc.locale === locale.code : false
426
602
  );
427
- const status = currentLocaleDoc?.status ?? "draft";
428
- const permissionsToCheck = currentLocaleDoc ? canCreate : canRead;
429
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
603
+ const permissionsToCheck = currentLocaleDoc ? canRead : canCreate;
430
604
  return {
431
605
  disabled: !permissionsToCheck.includes(locale.code),
432
606
  value: locale.code,
433
- label: locale.name,
434
- startIcon: schema?.options?.draftAndPublish ? /* @__PURE__ */ jsx(
435
- Status,
607
+ label: /* @__PURE__ */ jsx(
608
+ LocaleOption,
436
609
  {
437
- display: "flex",
438
- paddingLeft: "6px",
439
- paddingRight: "6px",
440
- paddingTop: "2px",
441
- paddingBottom: "2px",
442
- showBullet: false,
443
- size: "S",
444
- variant: statusVariant,
445
- children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
610
+ isDraftAndPublishEnabled: !!schema?.options?.draftAndPublish,
611
+ locale,
612
+ status: currentLocaleDoc?.status,
613
+ entryExists: entryWithLocaleExists
446
614
  }
447
- ) : null
615
+ ),
616
+ startIcon: !entryWithLocaleExists ? /* @__PURE__ */ jsx(Plus, {}) : null
448
617
  };
449
618
  }),
619
+ customizeContent: () => currentLocale?.name,
450
620
  onSelect: handleSelect,
451
621
  value: currentLocale
452
622
  };
@@ -462,6 +632,99 @@ const getDocumentStatus = (document, meta) => {
462
632
  }
463
633
  return docStatus;
464
634
  };
635
+ const FillFromAnotherLocaleAction = ({
636
+ documentId,
637
+ meta,
638
+ model,
639
+ collectionType
640
+ }) => {
641
+ const { formatMessage } = useIntl();
642
+ const [{ query }] = useQueryParams();
643
+ const { hasI18n } = useI18n();
644
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
645
+ const [localeSelected, setLocaleSelected] = React.useState(null);
646
+ const setValues = useForm("FillFromAnotherLocale", (state) => state.setValues);
647
+ const { getDocument } = unstable_useDocumentActions();
648
+ const { schema, components } = unstable_useDocument({
649
+ model,
650
+ documentId,
651
+ collectionType,
652
+ params: { locale: currentDesiredLocale }
653
+ });
654
+ const { data: locales = [] } = useGetLocalesQuery();
655
+ const availableLocales = Array.isArray(locales) ? locales.filter((locale) => meta?.availableLocales.some((l) => l.locale === locale.code)) : [];
656
+ const fillFromLocale = (onClose) => async () => {
657
+ const response = await getDocument({
658
+ collectionType,
659
+ model,
660
+ documentId,
661
+ params: { locale: localeSelected }
662
+ });
663
+ if (!response || !schema) {
664
+ return;
665
+ }
666
+ const { data } = response;
667
+ const cleanedData = cleanData(data, schema, components);
668
+ setValues(cleanedData);
669
+ onClose();
670
+ };
671
+ if (!hasI18n) {
672
+ return null;
673
+ }
674
+ return {
675
+ type: "icon",
676
+ icon: /* @__PURE__ */ jsx(Download, {}),
677
+ disabled: availableLocales.length === 0,
678
+ label: formatMessage({
679
+ id: getTranslation("CMEditViewCopyLocale.copy-text"),
680
+ defaultMessage: "Fill in from another locale"
681
+ }),
682
+ dialog: {
683
+ type: "dialog",
684
+ title: formatMessage({
685
+ id: getTranslation("CMEditViewCopyLocale.dialog.title"),
686
+ defaultMessage: "Confirmation"
687
+ }),
688
+ content: ({ onClose }) => /* @__PURE__ */ jsxs(Fragment, { children: [
689
+ /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
690
+ /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
691
+ /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
692
+ id: getTranslation("CMEditViewCopyLocale.dialog.body"),
693
+ defaultMessage: "Your current content will be erased and filled by the content of the selected locale:"
694
+ }) }),
695
+ /* @__PURE__ */ jsxs(Field.Root, { width: "100%", children: [
696
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
697
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.label"),
698
+ defaultMessage: "Locale"
699
+ }) }),
700
+ /* @__PURE__ */ jsx(
701
+ SingleSelect,
702
+ {
703
+ value: localeSelected,
704
+ placeholder: formatMessage({
705
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.placeholder"),
706
+ defaultMessage: "Select one locale..."
707
+ }),
708
+ onChange: (value) => setLocaleSelected(value),
709
+ children: availableLocales.map((locale) => /* @__PURE__ */ jsx(SingleSelectOption, { value: locale.code, children: locale.name }, locale.code))
710
+ }
711
+ )
712
+ ] })
713
+ ] }) }),
714
+ /* @__PURE__ */ jsx(Dialog.Footer, { children: /* @__PURE__ */ jsxs(Flex, { gap: 2, width: "100%", children: [
715
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "tertiary", onClick: onClose, children: formatMessage({
716
+ id: getTranslation("CMEditViewCopyLocale.cancel-text"),
717
+ defaultMessage: "No, cancel"
718
+ }) }),
719
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "success", onClick: fillFromLocale(onClose), children: formatMessage({
720
+ id: getTranslation("CMEditViewCopyLocale.submit-text"),
721
+ defaultMessage: "Yes, fill in"
722
+ }) })
723
+ ] }) })
724
+ ] })
725
+ }
726
+ };
727
+ };
465
728
  const DeleteLocaleAction = ({
466
729
  document,
467
730
  documentId,
@@ -473,16 +736,23 @@ const DeleteLocaleAction = ({
473
736
  const { toggleNotification } = useNotification();
474
737
  const { delete: deleteAction } = unstable_useDocumentActions();
475
738
  const { hasI18n, canDelete } = useI18n();
739
+ const [{ query }] = useQueryParams();
740
+ const { data: locales = [] } = useGetLocalesQuery();
741
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
742
+ const locale = !("error" in locales) && locales.find((loc) => loc.code === currentDesiredLocale);
476
743
  if (!hasI18n) {
477
744
  return null;
478
745
  }
479
746
  return {
480
747
  disabled: document?.locale && !canDelete.includes(document.locale) || !document || !document.id,
481
748
  position: ["header", "table-row"],
482
- label: formatMessage({
483
- id: getTranslation("actions.delete.label"),
484
- defaultMessage: "Delete locale"
485
- }),
749
+ label: formatMessage(
750
+ {
751
+ id: getTranslation("actions.delete.label"),
752
+ defaultMessage: "Delete entry ({locale})"
753
+ },
754
+ { locale: locale && locale.name }
755
+ ),
486
756
  icon: /* @__PURE__ */ jsx(StyledTrash, {}),
487
757
  variant: "danger",
488
758
  dialog: {
@@ -499,7 +769,12 @@ const DeleteLocaleAction = ({
499
769
  }) })
500
770
  ] }),
501
771
  onConfirm: async () => {
502
- if (!documentId || !document?.locale) {
772
+ const unableToDelete = (
773
+ // We are unable to delete a collection type without a document ID
774
+ // & unable to delete generally if there is no document locale
775
+ collectionType !== "single-types" && !documentId || !document?.locale
776
+ );
777
+ if (unableToDelete) {
503
778
  console.error(
504
779
  "You're trying to delete a document without an id or locale, this is likely a bug with Strapi. Please open an issue."
505
780
  );
@@ -525,37 +800,39 @@ const DeleteLocaleAction = ({
525
800
  }
526
801
  };
527
802
  };
528
- const BulkLocalePublishAction = ({
529
- document: baseDocument,
803
+ const BulkLocaleAction = ({
804
+ document,
530
805
  documentId,
531
806
  model,
532
- collectionType
807
+ collectionType,
808
+ action
533
809
  }) => {
534
- const baseLocale = baseDocument?.locale ?? null;
810
+ const locale = document?.locale ?? null;
535
811
  const [{ query }] = useQueryParams();
536
812
  const params = React.useMemo(() => buildValidParams(query), [query]);
537
- const isPublishedTab = query.status === "published";
813
+ const isOnPublishedTab = query.status === "published";
538
814
  const { formatMessage } = useIntl();
539
815
  const { hasI18n, canPublish } = useI18n();
540
816
  const { toggleNotification } = useNotification();
541
817
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
542
818
  const [selectedRows, setSelectedRows] = React.useState([]);
543
- const [isConfirmationOpen, setIsConfirmationOpen] = React.useState(false);
544
- const { publishMany: publishManyAction } = unstable_useDocumentActions();
545
- const {
546
- document,
547
- meta: documentMeta,
548
- schema,
549
- validate
550
- } = unstable_useDocument({
551
- model,
552
- collectionType,
553
- documentId,
554
- params: {
555
- locale: baseLocale
819
+ const [isDraftRelationConfirmationOpen, setIsDraftRelationConfirmationOpen] = React.useState(false);
820
+ const { publishMany: publishManyAction, unpublishMany: unpublishManyAction } = unstable_useDocumentActions();
821
+ const { schema, validate } = unstable_useDocument(
822
+ {
823
+ model,
824
+ collectionType,
825
+ documentId,
826
+ params: {
827
+ locale
828
+ }
829
+ },
830
+ {
831
+ // No need to fetch the document, the data is already available in the `document` prop
832
+ skip: true
556
833
  }
557
- });
558
- const { data: localesMetadata = [] } = useGetLocalesQuery();
834
+ );
835
+ const { data: localesMetadata = [] } = useGetLocalesQuery(hasI18n ? void 0 : skipToken);
559
836
  const headers = [
560
837
  {
561
838
  label: formatMessage({
@@ -580,18 +857,19 @@ const BulkLocalePublishAction = ({
580
857
  }
581
858
  ];
582
859
  const [rows, validationErrors] = React.useMemo(() => {
583
- if (!document || !documentMeta?.availableLocales) {
860
+ if (!document) {
584
861
  return [[], {}];
585
862
  }
586
- const rowsFromMeta = documentMeta?.availableLocales.map((doc) => {
587
- const { locale, status } = doc;
588
- return { locale, status };
863
+ const localizations = document.localizations ?? [];
864
+ const locales = localizations.map((doc) => {
865
+ const { locale: locale2, status } = doc;
866
+ return { locale: locale2, status };
589
867
  });
590
- rowsFromMeta.unshift({
868
+ locales.unshift({
591
869
  locale: document.locale,
592
870
  status: document.status
593
871
  });
594
- const allDocuments = [document, ...documentMeta?.availableLocales ?? []];
872
+ const allDocuments = [document, ...localizations];
595
873
  const errors = allDocuments.reduce((errs, document2) => {
596
874
  if (!document2) {
597
875
  return errs;
@@ -602,14 +880,21 @@ const BulkLocalePublishAction = ({
602
880
  }
603
881
  return errs;
604
882
  }, {});
605
- return [rowsFromMeta, errors];
606
- }, [document, documentMeta?.availableLocales, validate]);
607
- const localesToPublish = selectedRows.reduce((acc, selectedRow) => {
608
- if (selectedRow.status !== "published" && !Object.keys(validationErrors).includes(selectedRow.locale)) {
883
+ return [locales, errors];
884
+ }, [document, validate]);
885
+ const isBulkPublish = action === "bulk-publish";
886
+ const localesForAction = selectedRows.reduce((acc, selectedRow) => {
887
+ const isValidLocale = (
888
+ // Validation errors are irrelevant if we are trying to unpublish
889
+ !isBulkPublish || !Object.keys(validationErrors).includes(selectedRow.locale)
890
+ );
891
+ const shouldAddLocale = isBulkPublish ? selectedRow.status !== "published" && isValidLocale : selectedRow.status !== "draft" && isValidLocale;
892
+ if (shouldAddLocale) {
609
893
  acc.push(selectedRow.locale);
610
894
  }
611
895
  return acc;
612
896
  }, []);
897
+ const enableDraftRelationsCount = false;
613
898
  const {
614
899
  data: draftRelationsCount = 0,
615
900
  isLoading: isDraftRelationsLoading,
@@ -618,10 +903,10 @@ const BulkLocalePublishAction = ({
618
903
  {
619
904
  model,
620
905
  documentIds: [documentId],
621
- locale: localesToPublish
906
+ locale: localesForAction
622
907
  },
623
908
  {
624
- skip: !documentId || localesToPublish.length === 0
909
+ skip: !enableDraftRelationsCount
625
910
  }
626
911
  );
627
912
  React.useEffect(() => {
@@ -647,23 +932,32 @@ const BulkLocalePublishAction = ({
647
932
  documentIds: [documentId],
648
933
  params: {
649
934
  ...params,
650
- locale: localesToPublish
935
+ locale: localesForAction
936
+ }
937
+ });
938
+ setSelectedRows([]);
939
+ };
940
+ const unpublish = async () => {
941
+ await unpublishManyAction({
942
+ model,
943
+ documentIds: [documentId],
944
+ params: {
945
+ ...params,
946
+ locale: localesForAction
651
947
  }
652
948
  });
653
949
  setSelectedRows([]);
654
950
  };
655
951
  const handleAction = async () => {
656
952
  if (draftRelationsCount > 0) {
657
- setIsConfirmationOpen(true);
658
- } else {
953
+ setIsDraftRelationConfirmationOpen(true);
954
+ } else if (isBulkPublish) {
659
955
  await publish();
956
+ } else {
957
+ await unpublish();
660
958
  }
661
959
  };
662
- const isUnpublish = document?.status === "published";
663
- if (isUnpublish) {
664
- console.warn(["I18N"], "Bulk locale unpublish modal not implemented");
665
- }
666
- if (isConfirmationOpen) {
960
+ if (isDraftRelationConfirmationOpen) {
667
961
  return {
668
962
  label: formatMessage({
669
963
  id: "app.components.ConfirmDialog.title",
@@ -672,11 +966,11 @@ const BulkLocalePublishAction = ({
672
966
  variant: "danger",
673
967
  dialog: {
674
968
  onCancel: () => {
675
- setIsConfirmationOpen(false);
969
+ setIsDraftRelationConfirmationOpen(false);
676
970
  },
677
971
  onConfirm: async () => {
678
972
  await publish();
679
- setIsConfirmationOpen(false);
973
+ setIsDraftRelationConfirmationOpen(false);
680
974
  },
681
975
  type: "dialog",
682
976
  title: formatMessage({
@@ -686,27 +980,32 @@ const BulkLocalePublishAction = ({
686
980
  content: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "center", gap: 2, children: [
687
981
  /* @__PURE__ */ jsx(WarningCircle, { width: "2.4rem", height: "2.4rem", fill: "danger600" }),
688
982
  /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
689
- id: "content-manager.actions.discard.dialog.body",
690
- defaultMessage: "Are you sure you want to discard the changes? This action is irreversible."
983
+ id: getTranslation("CMEditViewBulkLocale.draft-relation-warning"),
984
+ defaultMessage: "Some locales are related to draft entries. Publishing them could leave broken links in your app."
985
+ }) }),
986
+ /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
987
+ id: getTranslation("CMEditViewBulkLocale.continue-confirmation"),
988
+ defaultMessage: "Are you sure you want to continue?"
691
989
  }) })
692
990
  ] })
693
991
  }
694
992
  };
695
993
  }
994
+ const hasPermission = selectedRows.map(({ locale: locale2 }) => locale2).every((locale2) => canPublish.includes(locale2));
696
995
  return {
697
996
  label: formatMessage({
698
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
699
- defaultMessage: "Publish Multiple Locales"
997
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
998
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
700
999
  }),
701
- icon: /* @__PURE__ */ jsx(ListPlus, {}),
702
- disabled: isPublishedTab || !canPublish,
1000
+ variant: isBulkPublish ? "secondary" : "danger",
1001
+ icon: isBulkPublish ? /* @__PURE__ */ jsx(ListPlus, {}) : /* @__PURE__ */ jsx(Cross, {}),
1002
+ disabled: isOnPublishedTab || canPublish.length === 0,
703
1003
  position: ["panel"],
704
- variant: "secondary",
705
1004
  dialog: {
706
1005
  type: "modal",
707
1006
  title: formatMessage({
708
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
709
- defaultMessage: "Publish Multiple Locales"
1007
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
1008
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
710
1009
  }),
711
1010
  content: () => {
712
1011
  return /* @__PURE__ */ jsx(
@@ -725,28 +1024,35 @@ const BulkLocalePublishAction = ({
725
1024
  validationErrors,
726
1025
  headers,
727
1026
  rows,
728
- localesMetadata
1027
+ localesMetadata,
1028
+ action: action ?? "bulk-publish"
729
1029
  }
730
1030
  )
731
1031
  }
732
1032
  );
733
1033
  },
734
- footer: () => /* @__PURE__ */ jsx(Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
735
- Button$1,
1034
+ footer: () => /* @__PURE__ */ jsx(Modal.Footer, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
1035
+ Button,
736
1036
  {
737
1037
  loading: isDraftRelationsLoading,
738
- disabled: localesToPublish.length === 0,
1038
+ disabled: !hasPermission || localesForAction.length === 0,
739
1039
  variant: "default",
740
1040
  onClick: handleAction,
741
1041
  children: formatMessage({
742
- id: "app.utils.publish",
743
- defaultMessage: "Publish"
1042
+ id: isBulkPublish ? "app.utils.publish" : "app.utils.unpublish",
1043
+ defaultMessage: isBulkPublish ? "Publish" : "Unpublish"
744
1044
  })
745
1045
  }
746
1046
  ) })
747
1047
  }
748
1048
  };
749
1049
  };
1050
+ const BulkLocalePublishAction = (props) => {
1051
+ return BulkLocaleAction({ action: "bulk-publish", ...props });
1052
+ };
1053
+ const BulkLocaleUnpublishAction = (props) => {
1054
+ return BulkLocaleAction({ action: "bulk-unpublish", ...props });
1055
+ };
750
1056
  const StyledTrash = styled(Trash)`
751
1057
  path {
752
1058
  fill: currentColor;
@@ -803,13 +1109,6 @@ const UnpublishModalAdditionalInfo = () => {
803
1109
  }
804
1110
  ) });
805
1111
  };
806
- const Initializer = ({ setPlugin }) => {
807
- const setPluginRef = React.useRef(setPlugin);
808
- React.useEffect(() => {
809
- setPluginRef.current(pluginId);
810
- }, []);
811
- return null;
812
- };
813
1112
  const LocalePicker = () => {
814
1113
  const { formatMessage } = useIntl();
815
1114
  const [{ query }, setQuery] = useQueryParams();
@@ -869,7 +1168,7 @@ const PERMISSIONS = {
869
1168
  read: [{ action: "plugin::i18n.locale.read", subject: null }]
870
1169
  };
871
1170
  const mutateEditViewHook = ({ layout }) => {
872
- if ("i18n" in layout.options && typeof layout.options.i18n === "object" && layout.options.i18n !== null && "localized" in layout.options.i18n && !layout.options.i18n.localized) {
1171
+ if (!("i18n" in layout.options) || typeof layout.options.i18n === "object" && layout.options.i18n !== null && "localized" in layout.options.i18n && !layout.options.i18n.localized) {
873
1172
  return { layout };
874
1173
  }
875
1174
  const components = Object.entries(layout.components).reduce(
@@ -915,7 +1214,7 @@ const doesFieldHaveI18nPluginOpt = (pluginOpts) => {
915
1214
  const LabelAction = ({ title, icon }) => {
916
1215
  const { formatMessage } = useIntl();
917
1216
  return /* @__PURE__ */ jsxs(Span, { tag: "span", children: [
918
- /* @__PURE__ */ jsx(VisuallyHidden, { tag: "span", children: `(${formatMessage(title)})` }),
1217
+ /* @__PURE__ */ jsx(VisuallyHidden, { tag: "span", children: formatMessage(title) }),
919
1218
  React.cloneElement(icon, {
920
1219
  "aria-hidden": true,
921
1220
  focusable: false
@@ -935,35 +1234,16 @@ const Span = styled(Flex)`
935
1234
  }
936
1235
  }
937
1236
  `;
938
- const LocaleListCell = ({
939
- documentId,
940
- locale: currentLocale,
941
- collectionType,
942
- model
943
- }) => {
944
- const { meta, isLoading } = unstable_useDocument({
945
- documentId,
946
- collectionType,
947
- model,
948
- params: {
949
- locale: currentLocale
950
- }
951
- });
1237
+ const LocaleListCell = ({ locale: currentLocale, localizations }) => {
952
1238
  const { locale: language } = useIntl();
953
- const [visible, setVisible] = React.useState(false);
954
- const buttonRef = React.useRef(null);
955
1239
  const { data: locales = [] } = useGetLocalesQuery();
956
- const handleTogglePopover = (e) => {
957
- e.stopPropagation();
958
- setVisible((prev) => !prev);
959
- };
960
1240
  const formatter = useCollator(language, {
961
1241
  sensitivity: "base"
962
1242
  });
963
- if (!Array.isArray(locales) || isLoading) {
1243
+ if (!Array.isArray(locales) || !localizations) {
964
1244
  return null;
965
1245
  }
966
- const availableLocales = meta?.availableLocales.map((doc) => doc.locale) ?? [];
1246
+ const availableLocales = localizations.map((loc) => loc.locale);
967
1247
  const localesForDocument = locales.reduce((acc, locale) => {
968
1248
  const createdLocale = [currentLocale, ...availableLocales].find((loc) => {
969
1249
  return loc === locale.code;
@@ -978,64 +1258,14 @@ const LocaleListCell = ({
978
1258
  }
979
1259
  return locale.name;
980
1260
  }).toSorted((a, b) => formatter.compare(a, b));
981
- return /* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleTogglePopover, ref: buttonRef, children: [
982
- /* @__PURE__ */ jsxs(
983
- ActionWrapper,
984
- {
985
- minWidth: "100%",
986
- alignItems: "center",
987
- justifyContent: "center",
988
- height: "3.2rem",
989
- width: "3.2rem",
990
- children: [
991
- /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", ellipsis: true, children: localesForDocument.join(", ") }),
992
- /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(CaretDown, {}) })
993
- ]
994
- }
995
- ),
996
- visible && /* @__PURE__ */ jsx(
997
- Popover,
998
- {
999
- onDismiss: () => setVisible(false),
1000
- source: buttonRef,
1001
- spacing: 16,
1002
- centered: true,
1003
- children: /* @__PURE__ */ jsx("ul", { children: localesForDocument.map((name) => /* @__PURE__ */ jsx(Box, { padding: 3, tag: "li", children: /* @__PURE__ */ jsx(Typography, { children: name }) }, name)) })
1004
- }
1005
- )
1261
+ return /* @__PURE__ */ jsxs(Popover.Root, { children: [
1262
+ /* @__PURE__ */ jsx(Popover.Trigger, { children: /* @__PURE__ */ jsx(Button, { variant: "ghost", type: "button", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs(Flex, { minWidth: "100%", alignItems: "center", justifyContent: "center", fontWeight: "regular", children: [
1263
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", ellipsis: true, marginRight: 2, children: localesForDocument.join(", ") }),
1264
+ /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(CaretDown, { width: "1.2rem", height: "1.2rem" }) })
1265
+ ] }) }) }),
1266
+ /* @__PURE__ */ jsx(Popover.Content, { sideOffset: 16, children: /* @__PURE__ */ jsx("ul", { children: localesForDocument.map((name) => /* @__PURE__ */ jsx(Box, { padding: 3, tag: "li", children: /* @__PURE__ */ jsx(Typography, { children: name }) }, name)) }) })
1006
1267
  ] });
1007
1268
  };
1008
- const Button = styled.button`
1009
- width: 100%;
1010
-
1011
- svg {
1012
- > g,
1013
- path {
1014
- fill: ${({ theme }) => theme.colors.neutral500};
1015
- }
1016
- }
1017
- &:hover {
1018
- svg {
1019
- > g,
1020
- path {
1021
- fill: ${({ theme }) => theme.colors.neutral600};
1022
- }
1023
- }
1024
- }
1025
- &:active {
1026
- svg {
1027
- > g,
1028
- path {
1029
- fill: ${({ theme }) => theme.colors.neutral400};
1030
- }
1031
- }
1032
- }
1033
- `;
1034
- const ActionWrapper = styled(Flex)`
1035
- svg {
1036
- height: 0.4rem;
1037
- }
1038
- `;
1039
1269
  const addColumnToTableHook = ({ displayedHeaders, layout }) => {
1040
1270
  const { options } = layout;
1041
1271
  const isFieldLocalized = doesPluginOptionsHaveI18nLocalized(options) ? options.i18n.localized : false;
@@ -1064,18 +1294,11 @@ const addColumnToTableHook = ({ displayedHeaders, layout }) => {
1064
1294
  const addLocaleToReleasesHook = ({ displayedHeaders = [] }) => {
1065
1295
  return {
1066
1296
  displayedHeaders: [
1067
- // TODO: Fix when migrating to v5
1068
- // ...displayedHeaders,
1297
+ ...displayedHeaders,
1069
1298
  {
1070
- key: "__locale__",
1071
- fieldSchema: { type: "string" },
1072
- metadatas: {
1073
- label: {
1074
- id: "content-releases.page.ReleaseDetails.table.header.label.locale",
1075
- defaultMessage: "locale"
1076
- },
1077
- searchable: false,
1078
- sortable: false
1299
+ label: {
1300
+ id: "content-releases.page.ReleaseDetails.table.header.label.locale",
1301
+ defaultMessage: "locale"
1079
1302
  },
1080
1303
  name: "locale"
1081
1304
  }
@@ -1168,9 +1391,6 @@ const localeMiddleware = (ctx) => (next) => (permissions) => {
1168
1391
  return next(revisedPermissions);
1169
1392
  };
1170
1393
  const prefixPluginTranslations = (trad, pluginId2) => {
1171
- if (!pluginId2) {
1172
- throw new TypeError("pluginId can't be empty");
1173
- }
1174
1394
  return Object.keys(trad).reduce((acc, current) => {
1175
1395
  acc[`${pluginId2}.${current}`] = trad[current];
1176
1396
  return acc;
@@ -1223,8 +1443,6 @@ const index = {
1223
1443
  app.addRBACMiddleware([localeMiddleware]);
1224
1444
  app.registerPlugin({
1225
1445
  id: pluginId,
1226
- initializer: Initializer,
1227
- isReady: false,
1228
1446
  name: pluginId
1229
1447
  });
1230
1448
  },
@@ -1242,11 +1460,11 @@ const index = {
1242
1460
  },
1243
1461
  id: "internationalization",
1244
1462
  to: "internationalization",
1245
- Component: () => import("./SettingsPage-0FFSTUW2.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1463
+ Component: () => import("./SettingsPage-B-KzAqT3.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1246
1464
  permissions: PERMISSIONS.accessMain
1247
1465
  });
1248
1466
  const contentManager = app.getPlugin("content-manager");
1249
- contentManager.apis.addDocumentHeaderAction([LocalePickerAction]);
1467
+ contentManager.apis.addDocumentHeaderAction([LocalePickerAction, FillFromAnotherLocaleAction]);
1250
1468
  contentManager.apis.addDocumentAction((actions) => {
1251
1469
  const indexOfDeleteAction = actions.findIndex((action) => action.type === "delete");
1252
1470
  actions.splice(indexOfDeleteAction, 0, DeleteLocaleAction);
@@ -1254,6 +1472,7 @@ const index = {
1254
1472
  });
1255
1473
  contentManager.apis.addDocumentAction((actions) => {
1256
1474
  actions.splice(2, 0, BulkLocalePublishAction);
1475
+ actions.splice(5, 0, BulkLocaleUnpublishAction);
1257
1476
  return actions;
1258
1477
  });
1259
1478
  contentManager.injectComponent("listView", "actions", {
@@ -1303,24 +1522,6 @@ const index = {
1303
1522
  }
1304
1523
  });
1305
1524
  ctbFormsAPI.extendFields(LOCALIZED_FIELDS, {
1306
- validator: (args) => ({
1307
- i18n: yup.object().shape({
1308
- localized: yup.bool().test({
1309
- name: "ensure-unique-localization",
1310
- message: getTranslation("plugin.schema.i18n.ensure-unique-localization"),
1311
- test(value) {
1312
- if (value === void 0 || value) {
1313
- return true;
1314
- }
1315
- const unique = get(args, ["3", "modifiedData", "unique"], null);
1316
- if (unique && !value) {
1317
- return false;
1318
- }
1319
- return true;
1320
- }
1321
- })
1322
- })
1323
- }),
1324
1525
  form: {
1325
1526
  advanced({ contentTypeSchema, forTarget, type, step }) {
1326
1527
  if (forTarget !== "contentType") {
@@ -1359,7 +1560,7 @@ const index = {
1359
1560
  async registerTrads({ locales }) {
1360
1561
  const importedTrads = await Promise.all(
1361
1562
  locales.map((locale) => {
1362
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/de.json": () => import("./de-9eCAqqrB.mjs"), "./translations/dk.json": () => import("./dk-2qBjxt-P.mjs"), "./translations/en.json": () => import("./en-18tWw4P6.mjs"), "./translations/es.json": () => import("./es-DlmMVaBG.mjs"), "./translations/fr.json": () => import("./fr-3S6ke71d.mjs"), "./translations/ko.json": () => import("./ko-qTjQ8IMw.mjs"), "./translations/pl.json": () => import("./pl-B67TSHqT.mjs"), "./translations/ru.json": () => import("./ru-hagMa57T.mjs"), "./translations/tr.json": () => import("./tr-Dw_jmkG-.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-Dyc-aR-h.mjs"), "./translations/zh.json": () => import("./zh-57YM4amO.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
1563
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/de.json": () => import("./de-D80IRBP9.mjs"), "./translations/dk.json": () => import("./dk-CJ6Zzz78.mjs"), "./translations/en.json": () => import("./en-BTyF7WVW.mjs"), "./translations/es.json": () => import("./es-V8WnPN7w.mjs"), "./translations/fr.json": () => import("./fr-F94noFiV.mjs"), "./translations/ko.json": () => import("./ko-CF-P3Car.mjs"), "./translations/pl.json": () => import("./pl-Dxr9RUmD.mjs"), "./translations/ru.json": () => import("./ru-B-4sVwXN.mjs"), "./translations/tr.json": () => import("./tr-Ccu6Yj11.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-Bfo6_TCM.mjs"), "./translations/zh.json": () => import("./zh-BIz395Ms.mjs") }), `./translations/${locale}.json`, 3).then(({ default: data }) => {
1363
1564
  return {
1364
1565
  data: prefixPluginTranslations(data, pluginId),
1365
1566
  locale
@@ -1385,4 +1586,4 @@ export {
1385
1586
  index as i,
1386
1587
  useCreateLocaleMutation as u
1387
1588
  };
1388
- //# sourceMappingURL=index-BddUXwss.mjs.map
1589
+ //# sourceMappingURL=index-BcYj5jo9.mjs.map