@strapi/i18n 5.0.0-rc.2 → 5.0.0-rc.21

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 (43) hide show
  1. package/dist/_chunks/{SettingsPage-CsGvujny.mjs → SettingsPage-4-K93FBe.mjs} +6 -6
  2. package/dist/_chunks/SettingsPage-4-K93FBe.mjs.map +1 -0
  3. package/dist/_chunks/{SettingsPage-DA9haizZ.js → SettingsPage-Cfi_zf_I.js} +6 -6
  4. package/dist/_chunks/SettingsPage-Cfi_zf_I.js.map +1 -0
  5. package/dist/_chunks/{en-BsOU9o5z.js → en-B6327hMz.js} +8 -2
  6. package/dist/_chunks/en-B6327hMz.js.map +1 -0
  7. package/dist/_chunks/{en-CM6Pjfyv.mjs → en-DZXpOMHo.mjs} +8 -2
  8. package/dist/_chunks/en-DZXpOMHo.mjs.map +1 -0
  9. package/dist/_chunks/{index-CCZJF_EJ.mjs → index-B2uG3lvD.mjs} +332 -127
  10. package/dist/_chunks/index-B2uG3lvD.mjs.map +1 -0
  11. package/dist/_chunks/{index-DIzVXZoE.js → index-DpT683lz.js} +325 -120
  12. package/dist/_chunks/index-DpT683lz.js.map +1 -0
  13. package/dist/admin/index.js +1 -1
  14. package/dist/admin/index.mjs +1 -1
  15. package/dist/admin/src/components/BulkLocaleActionModal.d.ts +2 -1
  16. package/dist/admin/src/components/CMHeaderActions.d.ts +27 -3
  17. package/dist/admin/src/utils/clean.d.ts +4 -0
  18. package/dist/server/index.js +61 -98
  19. package/dist/server/index.js.map +1 -1
  20. package/dist/server/index.mjs +62 -99
  21. package/dist/server/index.mjs.map +1 -1
  22. package/dist/server/src/bootstrap.d.ts +1 -4
  23. package/dist/server/src/bootstrap.d.ts.map +1 -1
  24. package/dist/server/src/index.d.ts +15 -13
  25. package/dist/server/src/index.d.ts.map +1 -1
  26. package/dist/server/src/services/index.d.ts +14 -10
  27. package/dist/server/src/services/index.d.ts.map +1 -1
  28. package/dist/server/src/services/permissions/actions.d.ts +14 -2
  29. package/dist/server/src/services/permissions/actions.d.ts.map +1 -1
  30. package/dist/server/src/services/permissions.d.ts +14 -2
  31. package/dist/server/src/services/permissions.d.ts.map +1 -1
  32. package/dist/server/src/utils/index.d.ts +0 -2
  33. package/dist/server/src/utils/index.d.ts.map +1 -1
  34. package/package.json +9 -9
  35. package/dist/_chunks/SettingsPage-CsGvujny.mjs.map +0 -1
  36. package/dist/_chunks/SettingsPage-DA9haizZ.js.map +0 -1
  37. package/dist/_chunks/en-BsOU9o5z.js.map +0 -1
  38. package/dist/_chunks/en-CM6Pjfyv.mjs.map +0 -1
  39. package/dist/_chunks/index-CCZJF_EJ.mjs.map +0 -1
  40. package/dist/_chunks/index-DIzVXZoE.js.map +0 -1
  41. package/dist/server/src/services/entity-service-decorator.d.ts +0 -29
  42. package/dist/server/src/services/entity-service-decorator.d.ts.map +0 -1
  43. package/strapi-server.js +0 -3
@@ -1,13 +1,13 @@
1
1
  import get from "lodash/get";
2
2
  import * as yup from "yup";
3
- import { jsxs, jsx } from "react/jsx-runtime";
3
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
4
4
  import * as React from "react";
5
- import { Typography, Dialog, Field, Checkbox, Flex, Button as Button$1, Modal, 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, Download, ListPlus, Cross, Earth, EarthStriked, CaretDown } from "@strapi/icons";
7
7
  import { useIntl } from "react-intl";
8
8
  import { styled } from "styled-components";
9
9
  import { skipToken } from "@reduxjs/toolkit/query";
10
- import { useAuth, adminApi, useTable, Table, useQueryParams, useNotification, useAPIErrorHandler } from "@strapi/admin/strapi-admin";
10
+ import { useAuth, adminApi, useTable, Table, useQueryParams, useForm, useNotification, useAPIErrorHandler } from "@strapi/admin/strapi-admin";
11
11
  import { unstable_useDocument, unstable_useDocumentActions, buildValidParams } from "@strapi/content-manager/strapi-admin";
12
12
  import { useParams, Link, useNavigate, matchPath } from "react-router-dom";
13
13
  import * as qs from "qs";
@@ -78,11 +78,11 @@ const CheckboxConfirmation = ({
78
78
  }) }) })
79
79
  ] }) }),
80
80
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
81
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button$1, { variant: "tertiary", children: formatMessage({
81
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
82
82
  id: "components.popUpWarning.button.cancel",
83
83
  defaultMessage: "No, cancel"
84
84
  }) }) }),
85
- /* @__PURE__ */ jsx(Dialog.Action, { children: /* @__PURE__ */ jsx(Button$1, { variant: "danger-light", onClick: handleConfirm, children: formatMessage({
85
+ /* @__PURE__ */ jsx(Dialog.Action, { children: /* @__PURE__ */ jsx(Button, { variant: "danger-light", onClick: handleConfirm, children: formatMessage({
86
86
  id: getTranslation("CheckboxConfirmation.Modal.button-confirm"),
87
87
  defaultMessage: "Yes, disable"
88
88
  }) }) })
@@ -217,10 +217,94 @@ const relationsApi = i18nApi.injectEndpoints({
217
217
  })
218
218
  });
219
219
  const { useGetManyDraftRelationCountQuery } = relationsApi;
220
+ const cleanData = (data, schema, components) => {
221
+ const cleanedData = removeFields(data, [
222
+ "createdAt",
223
+ "createdBy",
224
+ "updatedAt",
225
+ "updatedBy",
226
+ "id",
227
+ "documentId",
228
+ "publishedAt",
229
+ "strapi_stage",
230
+ "strapi_assignee",
231
+ "locale",
232
+ "status"
233
+ ]);
234
+ const cleanedDataWithoutPasswordAndRelation = recursiveRemoveFieldTypes(
235
+ cleanedData,
236
+ schema,
237
+ components,
238
+ ["relation", "password"]
239
+ );
240
+ return cleanedDataWithoutPasswordAndRelation;
241
+ };
242
+ const removeFields = (data, fields) => {
243
+ return Object.keys(data).reduce((acc, current) => {
244
+ if (fields.includes(current)) {
245
+ return acc;
246
+ }
247
+ acc[current] = data[current];
248
+ return acc;
249
+ }, {});
250
+ };
251
+ const recursiveRemoveFieldTypes = (data, schema, components, fields) => {
252
+ return Object.keys(data).reduce((acc, current) => {
253
+ const attribute = schema.attributes[current] ?? { type: void 0 };
254
+ if (fields.includes(attribute.type)) {
255
+ return acc;
256
+ }
257
+ if (attribute.type === "dynamiczone") {
258
+ acc[current] = data[current].map((componentValue, index2) => {
259
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(
260
+ componentValue,
261
+ components[componentValue.__component],
262
+ components,
263
+ fields
264
+ );
265
+ return {
266
+ ...rest,
267
+ __temp_key__: index2 + 1
268
+ };
269
+ });
270
+ } else if (attribute.type === "component") {
271
+ const { repeatable, component } = attribute;
272
+ if (repeatable) {
273
+ acc[current] = (data[current] ?? []).map((compoData, index2) => {
274
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(
275
+ compoData,
276
+ components[component],
277
+ components,
278
+ fields
279
+ );
280
+ return {
281
+ ...rest,
282
+ __temp_key__: index2 + 1
283
+ };
284
+ });
285
+ } else {
286
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(
287
+ data[current] ?? {},
288
+ components[component],
289
+ components,
290
+ fields
291
+ );
292
+ acc[current] = rest;
293
+ }
294
+ } else {
295
+ acc[current] = data[current];
296
+ }
297
+ return acc;
298
+ }, {});
299
+ };
220
300
  const isErrorMessageDescriptor = (object) => {
221
301
  return typeof object === "object" && object !== null && "id" in object && "defaultMessage" in object;
222
302
  };
223
- const EntryValidationText = ({ status = "draft", validationErrors }) => {
303
+ const EntryValidationText = ({
304
+ status = "draft",
305
+ validationErrors,
306
+ action
307
+ }) => {
224
308
  const { formatMessage } = useIntl();
225
309
  const getErrorStr = (key, value) => {
226
310
  if (typeof value === "string") {
@@ -254,30 +338,63 @@ const EntryValidationText = ({ status = "draft", validationErrors }) => {
254
338
  ) })
255
339
  ] });
256
340
  }
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
- }
341
+ const getStatusMessage = () => {
342
+ if (action === "bulk-publish") {
343
+ if (status === "published") {
344
+ return {
345
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
346
+ text: formatMessage({
347
+ id: "content-manager.bulk-publish.already-published",
348
+ defaultMessage: "Already Published"
349
+ }),
350
+ textColor: "success600",
351
+ fontWeight: "bold"
352
+ };
353
+ } else if (status === "modified") {
354
+ return {
355
+ icon: /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
356
+ text: formatMessage({
357
+ id: "app.utils.ready-to-publish-changes",
358
+ defaultMessage: "Ready to publish changes"
359
+ })
360
+ };
361
+ } else {
362
+ return {
363
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
364
+ text: formatMessage({
365
+ id: "app.utils.ready-to-publish",
366
+ defaultMessage: "Ready to publish"
367
+ })
368
+ };
369
+ }
370
+ } else {
371
+ if (status === "draft") {
372
+ return {
373
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
374
+ text: formatMessage({
375
+ id: "content-manager.bulk-unpublish.already-unpublished",
376
+ defaultMessage: "Already Unpublished"
377
+ }),
378
+ textColor: "success600",
379
+ fontWeight: "bold"
380
+ };
381
+ } else {
382
+ return {
383
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
384
+ text: formatMessage({
385
+ id: "app.utils.ready-to-unpublish-changes",
386
+ defaultMessage: "Ready to unpublish"
387
+ }),
388
+ textColor: "success600",
389
+ fontWeight: "bold"
390
+ };
391
+ }
392
+ }
393
+ };
394
+ const { icon, text, textColor = "success600", fontWeight = "normal" } = getStatusMessage();
275
395
  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
- }) })
396
+ icon,
397
+ /* @__PURE__ */ jsx(Typography, { textColor, fontWeight, children: text })
281
398
  ] });
282
399
  };
283
400
  const BoldChunk = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
@@ -285,7 +402,8 @@ const BulkLocaleActionModal = ({
285
402
  headers,
286
403
  rows,
287
404
  localesMetadata,
288
- validationErrors = {}
405
+ validationErrors = {},
406
+ action
289
407
  }) => {
290
408
  const { formatMessage } = useIntl();
291
409
  const selectedRows = useTable(
@@ -298,22 +416,24 @@ const BulkLocaleActionModal = ({
298
416
  return acc;
299
417
  }, {});
300
418
  const localesWithErrors = Object.keys(validationErrors);
301
- const alreadyPublishedCount = selectedRows.filter(
419
+ const publishedCount = selectedRows.filter(
302
420
  ({ locale }) => currentStatusByLocale[locale] === "published"
303
421
  ).length;
304
- const readyToPublishCount = selectedRows.filter(
422
+ const draftCount = selectedRows.filter(
305
423
  ({ locale }) => (currentStatusByLocale[locale] === "draft" || currentStatusByLocale[locale] === "modified") && !localesWithErrors.includes(locale)
306
424
  ).length;
307
425
  const withErrorsCount = localesWithErrors.length;
426
+ const messageId = action === "bulk-publish" ? "content-manager.containers.list.selectedEntriesModal.selectedCount.publish" : "content-manager.containers.list.selectedEntriesModal.selectedCount.unpublish";
427
+ 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
428
  return formatMessage(
309
429
  {
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."
430
+ id: messageId,
431
+ defaultMessage
312
432
  },
313
433
  {
314
434
  withErrorsCount,
315
- readyToPublishCount,
316
- alreadyPublishedCount,
435
+ draftCount,
436
+ publishedCount,
317
437
  b: BoldChunk
318
438
  }
319
439
  );
@@ -345,7 +465,7 @@ const BulkLocaleActionModal = ({
345
465
  children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
346
466
  }
347
467
  ) }) }),
348
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status }) }),
468
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status, action }) }),
349
469
  /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
350
470
  IconButton,
351
471
  {
@@ -362,7 +482,7 @@ const BulkLocaleActionModal = ({
362
482
  name: locale
363
483
  }
364
484
  ),
365
- borderWidth: 0,
485
+ variant: "ghost",
366
486
  children: /* @__PURE__ */ jsx(Pencil, {})
367
487
  }
368
488
  ) })
@@ -382,7 +502,13 @@ const LocalePickerAction = ({
382
502
  const [{ query }, setQuery] = useQueryParams();
383
503
  const { hasI18n, canCreate, canRead } = useI18n();
384
504
  const { data: locales = [] } = useGetLocalesQuery();
385
- const { schema } = unstable_useDocument({ model, collectionType, documentId });
505
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
506
+ const { schema } = unstable_useDocument({
507
+ model,
508
+ collectionType,
509
+ documentId,
510
+ params: { locale: currentDesiredLocale }
511
+ });
386
512
  const handleSelect = React.useCallback(
387
513
  (value) => {
388
514
  setQuery({
@@ -400,21 +526,20 @@ const LocalePickerAction = ({
400
526
  if (!Array.isArray(locales) || !hasI18n) {
401
527
  return;
402
528
  }
403
- const currentDesiredLocale = query.plugins?.i18n?.locale;
404
529
  const doesLocaleExist = locales.find((loc) => loc.code === currentDesiredLocale);
405
530
  const defaultLocale = locales.find((locale) => locale.isDefault);
406
531
  if (!doesLocaleExist && defaultLocale?.code) {
407
532
  handleSelect(defaultLocale.code);
408
533
  }
409
- }, [handleSelect, hasI18n, locales, query.plugins?.i18n?.locale]);
410
- if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
411
- return null;
412
- }
413
- const currentLocale = query.plugins?.i18n?.locale || locales.find((loc) => loc.isDefault)?.code;
534
+ }, [handleSelect, hasI18n, locales, currentDesiredLocale]);
535
+ const currentLocale = Array.isArray(locales) ? locales.find((locale) => locale.code === currentDesiredLocale)?.code : void 0;
414
536
  const allCurrentLocales = [
415
537
  { status: getDocumentStatus(document, meta), locale: currentLocale },
416
538
  ...meta?.availableLocales ?? []
417
539
  ];
540
+ if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
541
+ return null;
542
+ }
418
543
  return {
419
544
  label: formatMessage({
420
545
  id: getTranslation("Settings.locales.modal.locales.label"),
@@ -426,7 +551,7 @@ const LocalePickerAction = ({
426
551
  );
427
552
  const status = currentLocaleDoc?.status ?? "draft";
428
553
  const permissionsToCheck = currentLocaleDoc ? canCreate : canRead;
429
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
554
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
430
555
  return {
431
556
  disabled: !permissionsToCheck.includes(locale.code),
432
557
  value: locale.code,
@@ -462,6 +587,95 @@ const getDocumentStatus = (document, meta) => {
462
587
  }
463
588
  return docStatus;
464
589
  };
590
+ const FillFromAnotherLocaleAction = ({
591
+ documentId,
592
+ meta,
593
+ model,
594
+ collectionType
595
+ }) => {
596
+ const { formatMessage } = useIntl();
597
+ const [{ query }] = useQueryParams();
598
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
599
+ const [localeSelected, setLocaleSelected] = React.useState(null);
600
+ const setValues = useForm("FillFromAnotherLocale", (state) => state.setValues);
601
+ const { getDocument } = unstable_useDocumentActions();
602
+ const { schema, components } = unstable_useDocument({
603
+ model,
604
+ documentId,
605
+ collectionType,
606
+ params: { locale: currentDesiredLocale }
607
+ });
608
+ const { data: locales = [] } = useGetLocalesQuery();
609
+ const availableLocales = Array.isArray(locales) ? locales.filter((locale) => meta?.availableLocales.some((l) => l.locale === locale.code)) : [];
610
+ const fillFromLocale = (onClose) => async () => {
611
+ const response = await getDocument({
612
+ collectionType,
613
+ model,
614
+ documentId,
615
+ params: { locale: localeSelected }
616
+ });
617
+ if (!response || !schema) {
618
+ return;
619
+ }
620
+ const { data } = response;
621
+ const cleanedData = cleanData(data, schema, components);
622
+ setValues(cleanedData);
623
+ onClose();
624
+ };
625
+ return {
626
+ type: "icon",
627
+ icon: /* @__PURE__ */ jsx(Download, {}),
628
+ disabled: availableLocales.length === 0,
629
+ label: formatMessage({
630
+ id: getTranslation("CMEditViewCopyLocale.copy-text"),
631
+ defaultMessage: "Fill in from another locale"
632
+ }),
633
+ dialog: {
634
+ type: "dialog",
635
+ title: formatMessage({
636
+ id: getTranslation("CMEditViewCopyLocale.dialog.title"),
637
+ defaultMessage: "Confirmation"
638
+ }),
639
+ content: ({ onClose }) => /* @__PURE__ */ jsxs(Fragment, { children: [
640
+ /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
641
+ /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
642
+ /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
643
+ id: getTranslation("CMEditViewCopyLocale.dialog.body"),
644
+ defaultMessage: "Your current content will be erased and filled by the content of the selected locale:"
645
+ }) }),
646
+ /* @__PURE__ */ jsxs(Field.Root, { width: "100%", children: [
647
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
648
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.label"),
649
+ defaultMessage: "Locale"
650
+ }) }),
651
+ /* @__PURE__ */ jsx(
652
+ SingleSelect,
653
+ {
654
+ value: localeSelected,
655
+ placeholder: formatMessage({
656
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.placeholder"),
657
+ defaultMessage: "Select one locale..."
658
+ }),
659
+ onChange: (value) => setLocaleSelected(value),
660
+ children: availableLocales.map((locale) => /* @__PURE__ */ jsx(SingleSelectOption, { value: locale.code, children: locale.name }, locale.code))
661
+ }
662
+ )
663
+ ] })
664
+ ] }) }),
665
+ /* @__PURE__ */ jsx(Dialog.Footer, { children: /* @__PURE__ */ jsxs(Flex, { gap: 2, width: "100%", children: [
666
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "tertiary", onClick: onClose, children: formatMessage({
667
+ id: getTranslation("CMEditViewCopyLocale.cancel-text"),
668
+ defaultMessage: "No, cancel"
669
+ }) }),
670
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "success", onClick: fillFromLocale(onClose), children: formatMessage({
671
+ id: getTranslation("CMEditViewCopyLocale.submit-text"),
672
+ defaultMessage: "Yes, fill in"
673
+ }) })
674
+ ] }) })
675
+ ] })
676
+ }
677
+ };
678
+ };
465
679
  const DeleteLocaleAction = ({
466
680
  document,
467
681
  documentId,
@@ -473,16 +687,23 @@ const DeleteLocaleAction = ({
473
687
  const { toggleNotification } = useNotification();
474
688
  const { delete: deleteAction } = unstable_useDocumentActions();
475
689
  const { hasI18n, canDelete } = useI18n();
690
+ const [{ query }] = useQueryParams();
691
+ const { data: locales = [] } = useGetLocalesQuery();
692
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
693
+ const locale = !("error" in locales) && locales.find((loc) => loc.code === currentDesiredLocale);
476
694
  if (!hasI18n) {
477
695
  return null;
478
696
  }
479
697
  return {
480
698
  disabled: document?.locale && !canDelete.includes(document.locale) || !document || !document.id,
481
699
  position: ["header", "table-row"],
482
- label: formatMessage({
483
- id: getTranslation("actions.delete.label"),
484
- defaultMessage: "Delete locale"
485
- }),
700
+ label: formatMessage(
701
+ {
702
+ id: getTranslation("actions.delete.label"),
703
+ defaultMessage: "Delete entry ({locale})"
704
+ },
705
+ { locale: locale && locale.name }
706
+ ),
486
707
  icon: /* @__PURE__ */ jsx(StyledTrash, {}),
487
708
  variant: "danger",
488
709
  dialog: {
@@ -525,23 +746,24 @@ const DeleteLocaleAction = ({
525
746
  }
526
747
  };
527
748
  };
528
- const BulkLocalePublishAction = ({
749
+ const BulkLocaleAction = ({
529
750
  document: baseDocument,
530
751
  documentId,
531
752
  model,
532
- collectionType
753
+ collectionType,
754
+ action
533
755
  }) => {
534
756
  const baseLocale = baseDocument?.locale ?? null;
535
757
  const [{ query }] = useQueryParams();
536
758
  const params = React.useMemo(() => buildValidParams(query), [query]);
537
- const isPublishedTab = query.status === "published";
759
+ const isOnPublishedTab = query.status === "published";
538
760
  const { formatMessage } = useIntl();
539
761
  const { hasI18n, canPublish } = useI18n();
540
762
  const { toggleNotification } = useNotification();
541
763
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
542
764
  const [selectedRows, setSelectedRows] = React.useState([]);
543
765
  const [isDraftRelationConfirmationOpen, setIsDraftRelationConfirmationOpen] = React.useState(false);
544
- const { publishMany: publishManyAction } = unstable_useDocumentActions();
766
+ const { publishMany: publishManyAction, unpublishMany: unpublishManyAction } = unstable_useDocumentActions();
545
767
  const {
546
768
  document,
547
769
  meta: documentMeta,
@@ -557,7 +779,7 @@ const BulkLocalePublishAction = ({
557
779
  }
558
780
  },
559
781
  {
560
- skip: !hasI18n
782
+ skip: !hasI18n || !baseLocale
561
783
  }
562
784
  );
563
785
  const { data: localesMetadata = [] } = useGetLocalesQuery(hasI18n ? void 0 : skipToken);
@@ -609,12 +831,19 @@ const BulkLocalePublishAction = ({
609
831
  }, {});
610
832
  return [rowsFromMeta, errors];
611
833
  }, [document, documentMeta?.availableLocales, validate]);
612
- const localesToPublish = selectedRows.reduce((acc, selectedRow) => {
613
- if (selectedRow.status !== "published" && !Object.keys(validationErrors).includes(selectedRow.locale)) {
834
+ const isBulkPublish = action === "bulk-publish";
835
+ const localesForAction = selectedRows.reduce((acc, selectedRow) => {
836
+ const isValidLocale = (
837
+ // Validation errors are irrelevant if we are trying to unpublish
838
+ !isBulkPublish || !Object.keys(validationErrors).includes(selectedRow.locale)
839
+ );
840
+ const shouldAddLocale = isBulkPublish ? selectedRow.status !== "published" && isValidLocale : selectedRow.status !== "draft" && isValidLocale;
841
+ if (shouldAddLocale) {
614
842
  acc.push(selectedRow.locale);
615
843
  }
616
844
  return acc;
617
845
  }, []);
846
+ const enableDraftRelationsCount = false;
618
847
  const {
619
848
  data: draftRelationsCount = 0,
620
849
  isLoading: isDraftRelationsLoading,
@@ -623,10 +852,10 @@ const BulkLocalePublishAction = ({
623
852
  {
624
853
  model,
625
854
  documentIds: [documentId],
626
- locale: localesToPublish
855
+ locale: localesForAction
627
856
  },
628
857
  {
629
- skip: !documentId || localesToPublish.length === 0
858
+ skip: !enableDraftRelationsCount
630
859
  }
631
860
  );
632
861
  React.useEffect(() => {
@@ -652,7 +881,18 @@ const BulkLocalePublishAction = ({
652
881
  documentIds: [documentId],
653
882
  params: {
654
883
  ...params,
655
- locale: localesToPublish
884
+ locale: localesForAction
885
+ }
886
+ });
887
+ setSelectedRows([]);
888
+ };
889
+ const unpublish = async () => {
890
+ await unpublishManyAction({
891
+ model,
892
+ documentIds: [documentId],
893
+ params: {
894
+ ...params,
895
+ locale: localesForAction
656
896
  }
657
897
  });
658
898
  setSelectedRows([]);
@@ -660,14 +900,12 @@ const BulkLocalePublishAction = ({
660
900
  const handleAction = async () => {
661
901
  if (draftRelationsCount > 0) {
662
902
  setIsDraftRelationConfirmationOpen(true);
663
- } else {
903
+ } else if (isBulkPublish) {
664
904
  await publish();
905
+ } else {
906
+ await unpublish();
665
907
  }
666
908
  };
667
- const isUnpublish = document?.status === "published";
668
- if (isUnpublish) {
669
- console.warn(["I18N"], "Bulk locale unpublish modal not implemented");
670
- }
671
909
  if (isDraftRelationConfirmationOpen) {
672
910
  return {
673
911
  label: formatMessage({
@@ -705,18 +943,18 @@ const BulkLocalePublishAction = ({
705
943
  const hasPermission = selectedRows.map(({ locale }) => locale).every((locale) => canPublish.includes(locale));
706
944
  return {
707
945
  label: formatMessage({
708
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
709
- defaultMessage: "Publish Multiple Locales"
946
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
947
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
710
948
  }),
711
- icon: /* @__PURE__ */ jsx(ListPlus, {}),
712
- disabled: isPublishedTab || canPublish.length === 0,
949
+ variant: isBulkPublish ? "secondary" : "danger",
950
+ icon: isBulkPublish ? /* @__PURE__ */ jsx(ListPlus, {}) : /* @__PURE__ */ jsx(Cross, {}),
951
+ disabled: isOnPublishedTab || canPublish.length === 0,
713
952
  position: ["panel"],
714
- variant: "secondary",
715
953
  dialog: {
716
954
  type: "modal",
717
955
  title: formatMessage({
718
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
719
- defaultMessage: "Publish Multiple Locales"
956
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
957
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
720
958
  }),
721
959
  content: () => {
722
960
  return /* @__PURE__ */ jsx(
@@ -735,28 +973,35 @@ const BulkLocalePublishAction = ({
735
973
  validationErrors,
736
974
  headers,
737
975
  rows,
738
- localesMetadata
976
+ localesMetadata,
977
+ action: action ?? "bulk-publish"
739
978
  }
740
979
  )
741
980
  }
742
981
  );
743
982
  },
744
983
  footer: () => /* @__PURE__ */ jsx(Modal.Footer, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
745
- Button$1,
984
+ Button,
746
985
  {
747
986
  loading: isDraftRelationsLoading,
748
- disabled: !hasPermission || localesToPublish.length === 0,
987
+ disabled: !hasPermission || localesForAction.length === 0,
749
988
  variant: "default",
750
989
  onClick: handleAction,
751
990
  children: formatMessage({
752
- id: "app.utils.publish",
753
- defaultMessage: "Publish"
991
+ id: isBulkPublish ? "app.utils.publish" : "app.utils.unpublish",
992
+ defaultMessage: isBulkPublish ? "Publish" : "Unpublish"
754
993
  })
755
994
  }
756
995
  ) })
757
996
  }
758
997
  };
759
998
  };
999
+ const BulkLocalePublishAction = (props) => {
1000
+ return BulkLocaleAction({ action: "bulk-publish", ...props });
1001
+ };
1002
+ const BulkLocaleUnpublishAction = (props) => {
1003
+ return BulkLocaleAction({ action: "bulk-unpublish", ...props });
1004
+ };
760
1005
  const StyledTrash = styled(Trash)`
761
1006
  path {
762
1007
  fill: currentColor;
@@ -976,54 +1221,13 @@ const LocaleListCell = ({
976
1221
  return locale.name;
977
1222
  }).toSorted((a, b) => formatter.compare(a, b));
978
1223
  return /* @__PURE__ */ jsxs(Popover.Root, { children: [
979
- /* @__PURE__ */ jsx(Popover.Trigger, { children: /* @__PURE__ */ jsx(Button, { type: "button", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs(
980
- ActionWrapper,
981
- {
982
- minWidth: "100%",
983
- alignItems: "center",
984
- justifyContent: "center",
985
- height: "3.2rem",
986
- width: "3.2rem",
987
- children: [
988
- /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", ellipsis: true, children: localesForDocument.join(", ") }),
989
- /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(CaretDown, {}) })
990
- ]
991
- }
992
- ) }) }),
1224
+ /* @__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: [
1225
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", ellipsis: true, marginRight: 2, children: localesForDocument.join(", ") }),
1226
+ /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(CaretDown, { width: "1.2rem", height: "1.2rem" }) })
1227
+ ] }) }) }),
993
1228
  /* @__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)) }) })
994
1229
  ] });
995
1230
  };
996
- const Button = styled.button`
997
- width: 100%;
998
-
999
- svg {
1000
- > g,
1001
- path {
1002
- fill: ${({ theme }) => theme.colors.neutral500};
1003
- }
1004
- }
1005
- &:hover {
1006
- svg {
1007
- > g,
1008
- path {
1009
- fill: ${({ theme }) => theme.colors.neutral600};
1010
- }
1011
- }
1012
- }
1013
- &:active {
1014
- svg {
1015
- > g,
1016
- path {
1017
- fill: ${({ theme }) => theme.colors.neutral400};
1018
- }
1019
- }
1020
- }
1021
- `;
1022
- const ActionWrapper = styled(Flex)`
1023
- svg {
1024
- height: 0.4rem;
1025
- }
1026
- `;
1027
1231
  const addColumnToTableHook = ({ displayedHeaders, layout }) => {
1028
1232
  const { options } = layout;
1029
1233
  const isFieldLocalized = doesPluginOptionsHaveI18nLocalized(options) ? options.i18n.localized : false;
@@ -1221,11 +1425,11 @@ const index = {
1221
1425
  },
1222
1426
  id: "internationalization",
1223
1427
  to: "internationalization",
1224
- Component: () => import("./SettingsPage-CsGvujny.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1428
+ Component: () => import("./SettingsPage-4-K93FBe.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1225
1429
  permissions: PERMISSIONS.accessMain
1226
1430
  });
1227
1431
  const contentManager = app.getPlugin("content-manager");
1228
- contentManager.apis.addDocumentHeaderAction([LocalePickerAction]);
1432
+ contentManager.apis.addDocumentHeaderAction([LocalePickerAction, FillFromAnotherLocaleAction]);
1229
1433
  contentManager.apis.addDocumentAction((actions) => {
1230
1434
  const indexOfDeleteAction = actions.findIndex((action) => action.type === "delete");
1231
1435
  actions.splice(indexOfDeleteAction, 0, DeleteLocaleAction);
@@ -1233,6 +1437,7 @@ const index = {
1233
1437
  });
1234
1438
  contentManager.apis.addDocumentAction((actions) => {
1235
1439
  actions.splice(2, 0, BulkLocalePublishAction);
1440
+ actions.splice(5, 0, BulkLocaleUnpublishAction);
1236
1441
  return actions;
1237
1442
  });
1238
1443
  contentManager.injectComponent("listView", "actions", {
@@ -1338,7 +1543,7 @@ const index = {
1338
1543
  async registerTrads({ locales }) {
1339
1544
  const importedTrads = await Promise.all(
1340
1545
  locales.map((locale) => {
1341
- 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-CM6Pjfyv.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 }) => {
1546
+ 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-DZXpOMHo.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 }) => {
1342
1547
  return {
1343
1548
  data: prefixPluginTranslations(data, pluginId),
1344
1549
  locale
@@ -1364,4 +1569,4 @@ export {
1364
1569
  index as i,
1365
1570
  useCreateLocaleMutation as u
1366
1571
  };
1367
- //# sourceMappingURL=index-CCZJF_EJ.mjs.map
1572
+ //# sourceMappingURL=index-B2uG3lvD.mjs.map