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

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