@strapi/i18n 0.0.0-experimental.f31889311d753b5f7d95198ae84d8fce1d156cd6 → 0.0.0-experimental.f74ae50eea1ce95176f088dba837e95b60fa2a4d

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-DA9haizZ.js → SettingsPage-CJOMVQv5.js} +6 -6
  2. package/dist/_chunks/SettingsPage-CJOMVQv5.js.map +1 -0
  3. package/dist/_chunks/{SettingsPage-CsGvujny.mjs → SettingsPage-CnBFTsrq.mjs} +6 -6
  4. package/dist/_chunks/SettingsPage-CnBFTsrq.mjs.map +1 -0
  5. package/dist/_chunks/{en-CM6Pjfyv.mjs → en-BYRZFDBV.mjs} +9 -2
  6. package/dist/_chunks/en-BYRZFDBV.mjs.map +1 -0
  7. package/dist/_chunks/{en-BsOU9o5z.js → en-Dk9At9_Z.js} +9 -2
  8. package/dist/_chunks/en-Dk9At9_Z.js.map +1 -0
  9. package/dist/_chunks/{index-CCZJF_EJ.mjs → index-BFk3nfTb.mjs} +384 -142
  10. package/dist/_chunks/index-BFk3nfTb.mjs.map +1 -0
  11. package/dist/_chunks/{index-DIzVXZoE.js → index-C5SImSYG.js} +377 -135
  12. package/dist/_chunks/index-C5SImSYG.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 +29 -3
  17. package/dist/admin/src/utils/clean.d.ts +4 -0
  18. package/dist/server/index.js +62 -98
  19. package/dist/server/index.js.map +1 -1
  20. package/dist/server/index.mjs +63 -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, 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
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
  ) })
@@ -371,6 +491,48 @@ const BulkLocaleActionModal = ({
371
491
  ] }) })
372
492
  ] });
373
493
  };
494
+ const statusVariants = {
495
+ draft: "secondary",
496
+ published: "success",
497
+ modified: "alternative"
498
+ };
499
+ const LocaleOption = ({
500
+ isDraftAndPublishEnabled,
501
+ locale,
502
+ status,
503
+ entryExists
504
+ }) => {
505
+ const { formatMessage } = useIntl();
506
+ if (!entryExists) {
507
+ return formatMessage(
508
+ {
509
+ id: getTranslation("CMEditViewLocalePicker.locale.create"),
510
+ defaultMessage: "Create <bold>{locale}</bold> locale"
511
+ },
512
+ {
513
+ bold: (locale2) => /* @__PURE__ */ jsx("b", { children: locale2 }),
514
+ locale: locale.name
515
+ }
516
+ );
517
+ }
518
+ return /* @__PURE__ */ jsxs(Flex, { width: "100%", gap: 1, justifyContent: "space-between", children: [
519
+ /* @__PURE__ */ jsx(Typography, { children: locale.name }),
520
+ isDraftAndPublishEnabled ? /* @__PURE__ */ jsx(
521
+ Status,
522
+ {
523
+ display: "flex",
524
+ paddingLeft: "6px",
525
+ paddingRight: "6px",
526
+ paddingTop: "2px",
527
+ paddingBottom: "2px",
528
+ showBullet: false,
529
+ size: "S",
530
+ variant: statusVariants[status],
531
+ children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
532
+ }
533
+ ) : null
534
+ ] });
535
+ };
374
536
  const LocalePickerAction = ({
375
537
  document,
376
538
  meta,
@@ -382,7 +544,13 @@ const LocalePickerAction = ({
382
544
  const [{ query }, setQuery] = useQueryParams();
383
545
  const { hasI18n, canCreate, canRead } = useI18n();
384
546
  const { data: locales = [] } = useGetLocalesQuery();
385
- const { schema } = unstable_useDocument({ model, collectionType, documentId });
547
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
548
+ const { schema } = unstable_useDocument({
549
+ model,
550
+ collectionType,
551
+ documentId,
552
+ params: { locale: currentDesiredLocale }
553
+ });
386
554
  const handleSelect = React.useCallback(
387
555
  (value) => {
388
556
  setQuery({
@@ -400,53 +568,47 @@ const LocalePickerAction = ({
400
568
  if (!Array.isArray(locales) || !hasI18n) {
401
569
  return;
402
570
  }
403
- const currentDesiredLocale = query.plugins?.i18n?.locale;
404
571
  const doesLocaleExist = locales.find((loc) => loc.code === currentDesiredLocale);
405
572
  const defaultLocale = locales.find((locale) => locale.isDefault);
406
573
  if (!doesLocaleExist && defaultLocale?.code) {
407
574
  handleSelect(defaultLocale.code);
408
575
  }
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;
576
+ }, [handleSelect, hasI18n, locales, currentDesiredLocale]);
577
+ const currentLocale = Array.isArray(locales) ? locales.find((locale) => locale.code === currentDesiredLocale) : void 0;
414
578
  const allCurrentLocales = [
415
- { status: getDocumentStatus(document, meta), locale: currentLocale },
579
+ { status: getDocumentStatus(document, meta), locale: currentLocale?.code },
416
580
  ...meta?.availableLocales ?? []
417
581
  ];
582
+ if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
583
+ return null;
584
+ }
418
585
  return {
419
586
  label: formatMessage({
420
587
  id: getTranslation("Settings.locales.modal.locales.label"),
421
588
  defaultMessage: "Locales"
422
589
  }),
423
590
  options: locales.map((locale) => {
591
+ const entryWithLocaleExists = allCurrentLocales.some((doc) => doc.locale === locale.code);
424
592
  const currentLocaleDoc = allCurrentLocales.find(
425
593
  (doc) => "locale" in doc ? doc.locale === locale.code : false
426
594
  );
427
- const status = currentLocaleDoc?.status ?? "draft";
428
595
  const permissionsToCheck = currentLocaleDoc ? canCreate : canRead;
429
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
430
596
  return {
431
597
  disabled: !permissionsToCheck.includes(locale.code),
432
598
  value: locale.code,
433
- label: locale.name,
434
- startIcon: schema?.options?.draftAndPublish ? /* @__PURE__ */ jsx(
435
- Status,
599
+ label: /* @__PURE__ */ jsx(
600
+ LocaleOption,
436
601
  {
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) })
602
+ isDraftAndPublishEnabled: !!schema?.options?.draftAndPublish,
603
+ locale,
604
+ status: currentLocaleDoc?.status,
605
+ entryExists: entryWithLocaleExists
446
606
  }
447
- ) : null
607
+ ),
608
+ startIcon: !entryWithLocaleExists ? /* @__PURE__ */ jsx(Plus, {}) : null
448
609
  };
449
610
  }),
611
+ customizeContent: () => currentLocale?.name,
450
612
  onSelect: handleSelect,
451
613
  value: currentLocale
452
614
  };
@@ -462,6 +624,95 @@ const getDocumentStatus = (document, meta) => {
462
624
  }
463
625
  return docStatus;
464
626
  };
627
+ const FillFromAnotherLocaleAction = ({
628
+ documentId,
629
+ meta,
630
+ model,
631
+ collectionType
632
+ }) => {
633
+ const { formatMessage } = useIntl();
634
+ const [{ query }] = useQueryParams();
635
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
636
+ const [localeSelected, setLocaleSelected] = React.useState(null);
637
+ const setValues = useForm("FillFromAnotherLocale", (state) => state.setValues);
638
+ const { getDocument } = unstable_useDocumentActions();
639
+ const { schema, components } = unstable_useDocument({
640
+ model,
641
+ documentId,
642
+ collectionType,
643
+ params: { locale: currentDesiredLocale }
644
+ });
645
+ const { data: locales = [] } = useGetLocalesQuery();
646
+ const availableLocales = Array.isArray(locales) ? locales.filter((locale) => meta?.availableLocales.some((l) => l.locale === locale.code)) : [];
647
+ const fillFromLocale = (onClose) => async () => {
648
+ const response = await getDocument({
649
+ collectionType,
650
+ model,
651
+ documentId,
652
+ params: { locale: localeSelected }
653
+ });
654
+ if (!response || !schema) {
655
+ return;
656
+ }
657
+ const { data } = response;
658
+ const cleanedData = cleanData(data, schema, components);
659
+ setValues(cleanedData);
660
+ onClose();
661
+ };
662
+ return {
663
+ type: "icon",
664
+ icon: /* @__PURE__ */ jsx(Download, {}),
665
+ disabled: availableLocales.length === 0,
666
+ label: formatMessage({
667
+ id: getTranslation("CMEditViewCopyLocale.copy-text"),
668
+ defaultMessage: "Fill in from another locale"
669
+ }),
670
+ dialog: {
671
+ type: "dialog",
672
+ title: formatMessage({
673
+ id: getTranslation("CMEditViewCopyLocale.dialog.title"),
674
+ defaultMessage: "Confirmation"
675
+ }),
676
+ content: ({ onClose }) => /* @__PURE__ */ jsxs(Fragment, { children: [
677
+ /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
678
+ /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
679
+ /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
680
+ id: getTranslation("CMEditViewCopyLocale.dialog.body"),
681
+ defaultMessage: "Your current content will be erased and filled by the content of the selected locale:"
682
+ }) }),
683
+ /* @__PURE__ */ jsxs(Field.Root, { width: "100%", children: [
684
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
685
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.label"),
686
+ defaultMessage: "Locale"
687
+ }) }),
688
+ /* @__PURE__ */ jsx(
689
+ SingleSelect,
690
+ {
691
+ value: localeSelected,
692
+ placeholder: formatMessage({
693
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.placeholder"),
694
+ defaultMessage: "Select one locale..."
695
+ }),
696
+ onChange: (value) => setLocaleSelected(value),
697
+ children: availableLocales.map((locale) => /* @__PURE__ */ jsx(SingleSelectOption, { value: locale.code, children: locale.name }, locale.code))
698
+ }
699
+ )
700
+ ] })
701
+ ] }) }),
702
+ /* @__PURE__ */ jsx(Dialog.Footer, { children: /* @__PURE__ */ jsxs(Flex, { gap: 2, width: "100%", children: [
703
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "tertiary", onClick: onClose, children: formatMessage({
704
+ id: getTranslation("CMEditViewCopyLocale.cancel-text"),
705
+ defaultMessage: "No, cancel"
706
+ }) }),
707
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "success", onClick: fillFromLocale(onClose), children: formatMessage({
708
+ id: getTranslation("CMEditViewCopyLocale.submit-text"),
709
+ defaultMessage: "Yes, fill in"
710
+ }) })
711
+ ] }) })
712
+ ] })
713
+ }
714
+ };
715
+ };
465
716
  const DeleteLocaleAction = ({
466
717
  document,
467
718
  documentId,
@@ -473,16 +724,23 @@ const DeleteLocaleAction = ({
473
724
  const { toggleNotification } = useNotification();
474
725
  const { delete: deleteAction } = unstable_useDocumentActions();
475
726
  const { hasI18n, canDelete } = useI18n();
727
+ const [{ query }] = useQueryParams();
728
+ const { data: locales = [] } = useGetLocalesQuery();
729
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
730
+ const locale = !("error" in locales) && locales.find((loc) => loc.code === currentDesiredLocale);
476
731
  if (!hasI18n) {
477
732
  return null;
478
733
  }
479
734
  return {
480
735
  disabled: document?.locale && !canDelete.includes(document.locale) || !document || !document.id,
481
736
  position: ["header", "table-row"],
482
- label: formatMessage({
483
- id: getTranslation("actions.delete.label"),
484
- defaultMessage: "Delete locale"
485
- }),
737
+ label: formatMessage(
738
+ {
739
+ id: getTranslation("actions.delete.label"),
740
+ defaultMessage: "Delete entry ({locale})"
741
+ },
742
+ { locale: locale && locale.name }
743
+ ),
486
744
  icon: /* @__PURE__ */ jsx(StyledTrash, {}),
487
745
  variant: "danger",
488
746
  dialog: {
@@ -525,23 +783,24 @@ const DeleteLocaleAction = ({
525
783
  }
526
784
  };
527
785
  };
528
- const BulkLocalePublishAction = ({
786
+ const BulkLocaleAction = ({
529
787
  document: baseDocument,
530
788
  documentId,
531
789
  model,
532
- collectionType
790
+ collectionType,
791
+ action
533
792
  }) => {
534
793
  const baseLocale = baseDocument?.locale ?? null;
535
794
  const [{ query }] = useQueryParams();
536
795
  const params = React.useMemo(() => buildValidParams(query), [query]);
537
- const isPublishedTab = query.status === "published";
796
+ const isOnPublishedTab = query.status === "published";
538
797
  const { formatMessage } = useIntl();
539
798
  const { hasI18n, canPublish } = useI18n();
540
799
  const { toggleNotification } = useNotification();
541
800
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
542
801
  const [selectedRows, setSelectedRows] = React.useState([]);
543
802
  const [isDraftRelationConfirmationOpen, setIsDraftRelationConfirmationOpen] = React.useState(false);
544
- const { publishMany: publishManyAction } = unstable_useDocumentActions();
803
+ const { publishMany: publishManyAction, unpublishMany: unpublishManyAction } = unstable_useDocumentActions();
545
804
  const {
546
805
  document,
547
806
  meta: documentMeta,
@@ -557,7 +816,7 @@ const BulkLocalePublishAction = ({
557
816
  }
558
817
  },
559
818
  {
560
- skip: !hasI18n
819
+ skip: !hasI18n || !baseLocale
561
820
  }
562
821
  );
563
822
  const { data: localesMetadata = [] } = useGetLocalesQuery(hasI18n ? void 0 : skipToken);
@@ -609,12 +868,19 @@ const BulkLocalePublishAction = ({
609
868
  }, {});
610
869
  return [rowsFromMeta, errors];
611
870
  }, [document, documentMeta?.availableLocales, validate]);
612
- const localesToPublish = selectedRows.reduce((acc, selectedRow) => {
613
- if (selectedRow.status !== "published" && !Object.keys(validationErrors).includes(selectedRow.locale)) {
871
+ const isBulkPublish = action === "bulk-publish";
872
+ const localesForAction = selectedRows.reduce((acc, selectedRow) => {
873
+ const isValidLocale = (
874
+ // Validation errors are irrelevant if we are trying to unpublish
875
+ !isBulkPublish || !Object.keys(validationErrors).includes(selectedRow.locale)
876
+ );
877
+ const shouldAddLocale = isBulkPublish ? selectedRow.status !== "published" && isValidLocale : selectedRow.status !== "draft" && isValidLocale;
878
+ if (shouldAddLocale) {
614
879
  acc.push(selectedRow.locale);
615
880
  }
616
881
  return acc;
617
882
  }, []);
883
+ const enableDraftRelationsCount = false;
618
884
  const {
619
885
  data: draftRelationsCount = 0,
620
886
  isLoading: isDraftRelationsLoading,
@@ -623,10 +889,10 @@ const BulkLocalePublishAction = ({
623
889
  {
624
890
  model,
625
891
  documentIds: [documentId],
626
- locale: localesToPublish
892
+ locale: localesForAction
627
893
  },
628
894
  {
629
- skip: !documentId || localesToPublish.length === 0
895
+ skip: !enableDraftRelationsCount
630
896
  }
631
897
  );
632
898
  React.useEffect(() => {
@@ -652,7 +918,18 @@ const BulkLocalePublishAction = ({
652
918
  documentIds: [documentId],
653
919
  params: {
654
920
  ...params,
655
- locale: localesToPublish
921
+ locale: localesForAction
922
+ }
923
+ });
924
+ setSelectedRows([]);
925
+ };
926
+ const unpublish = async () => {
927
+ await unpublishManyAction({
928
+ model,
929
+ documentIds: [documentId],
930
+ params: {
931
+ ...params,
932
+ locale: localesForAction
656
933
  }
657
934
  });
658
935
  setSelectedRows([]);
@@ -660,14 +937,12 @@ const BulkLocalePublishAction = ({
660
937
  const handleAction = async () => {
661
938
  if (draftRelationsCount > 0) {
662
939
  setIsDraftRelationConfirmationOpen(true);
663
- } else {
940
+ } else if (isBulkPublish) {
664
941
  await publish();
942
+ } else {
943
+ await unpublish();
665
944
  }
666
945
  };
667
- const isUnpublish = document?.status === "published";
668
- if (isUnpublish) {
669
- console.warn(["I18N"], "Bulk locale unpublish modal not implemented");
670
- }
671
946
  if (isDraftRelationConfirmationOpen) {
672
947
  return {
673
948
  label: formatMessage({
@@ -705,18 +980,18 @@ const BulkLocalePublishAction = ({
705
980
  const hasPermission = selectedRows.map(({ locale }) => locale).every((locale) => canPublish.includes(locale));
706
981
  return {
707
982
  label: formatMessage({
708
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
709
- defaultMessage: "Publish Multiple Locales"
983
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
984
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
710
985
  }),
711
- icon: /* @__PURE__ */ jsx(ListPlus, {}),
712
- disabled: isPublishedTab || canPublish.length === 0,
986
+ variant: isBulkPublish ? "secondary" : "danger",
987
+ icon: isBulkPublish ? /* @__PURE__ */ jsx(ListPlus, {}) : /* @__PURE__ */ jsx(Cross, {}),
988
+ disabled: isOnPublishedTab || canPublish.length === 0,
713
989
  position: ["panel"],
714
- variant: "secondary",
715
990
  dialog: {
716
991
  type: "modal",
717
992
  title: formatMessage({
718
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
719
- defaultMessage: "Publish Multiple Locales"
993
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
994
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
720
995
  }),
721
996
  content: () => {
722
997
  return /* @__PURE__ */ jsx(
@@ -735,28 +1010,35 @@ const BulkLocalePublishAction = ({
735
1010
  validationErrors,
736
1011
  headers,
737
1012
  rows,
738
- localesMetadata
1013
+ localesMetadata,
1014
+ action: action ?? "bulk-publish"
739
1015
  }
740
1016
  )
741
1017
  }
742
1018
  );
743
1019
  },
744
1020
  footer: () => /* @__PURE__ */ jsx(Modal.Footer, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
745
- Button$1,
1021
+ Button,
746
1022
  {
747
1023
  loading: isDraftRelationsLoading,
748
- disabled: !hasPermission || localesToPublish.length === 0,
1024
+ disabled: !hasPermission || localesForAction.length === 0,
749
1025
  variant: "default",
750
1026
  onClick: handleAction,
751
1027
  children: formatMessage({
752
- id: "app.utils.publish",
753
- defaultMessage: "Publish"
1028
+ id: isBulkPublish ? "app.utils.publish" : "app.utils.unpublish",
1029
+ defaultMessage: isBulkPublish ? "Publish" : "Unpublish"
754
1030
  })
755
1031
  }
756
1032
  ) })
757
1033
  }
758
1034
  };
759
1035
  };
1036
+ const BulkLocalePublishAction = (props) => {
1037
+ return BulkLocaleAction({ action: "bulk-publish", ...props });
1038
+ };
1039
+ const BulkLocaleUnpublishAction = (props) => {
1040
+ return BulkLocaleAction({ action: "bulk-unpublish", ...props });
1041
+ };
760
1042
  const StyledTrash = styled(Trash)`
761
1043
  path {
762
1044
  fill: currentColor;
@@ -976,54 +1258,13 @@ const LocaleListCell = ({
976
1258
  return locale.name;
977
1259
  }).toSorted((a, b) => formatter.compare(a, b));
978
1260
  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
- ) }) }),
1261
+ /* @__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: [
1262
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", ellipsis: true, marginRight: 2, children: localesForDocument.join(", ") }),
1263
+ /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(CaretDown, { width: "1.2rem", height: "1.2rem" }) })
1264
+ ] }) }) }),
993
1265
  /* @__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
1266
  ] });
995
1267
  };
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
1268
  const addColumnToTableHook = ({ displayedHeaders, layout }) => {
1028
1269
  const { options } = layout;
1029
1270
  const isFieldLocalized = doesPluginOptionsHaveI18nLocalized(options) ? options.i18n.localized : false;
@@ -1221,11 +1462,11 @@ const index = {
1221
1462
  },
1222
1463
  id: "internationalization",
1223
1464
  to: "internationalization",
1224
- Component: () => import("./SettingsPage-CsGvujny.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1465
+ Component: () => import("./SettingsPage-CnBFTsrq.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1225
1466
  permissions: PERMISSIONS.accessMain
1226
1467
  });
1227
1468
  const contentManager = app.getPlugin("content-manager");
1228
- contentManager.apis.addDocumentHeaderAction([LocalePickerAction]);
1469
+ contentManager.apis.addDocumentHeaderAction([LocalePickerAction, FillFromAnotherLocaleAction]);
1229
1470
  contentManager.apis.addDocumentAction((actions) => {
1230
1471
  const indexOfDeleteAction = actions.findIndex((action) => action.type === "delete");
1231
1472
  actions.splice(indexOfDeleteAction, 0, DeleteLocaleAction);
@@ -1233,6 +1474,7 @@ const index = {
1233
1474
  });
1234
1475
  contentManager.apis.addDocumentAction((actions) => {
1235
1476
  actions.splice(2, 0, BulkLocalePublishAction);
1477
+ actions.splice(5, 0, BulkLocaleUnpublishAction);
1236
1478
  return actions;
1237
1479
  });
1238
1480
  contentManager.injectComponent("listView", "actions", {
@@ -1338,7 +1580,7 @@ const index = {
1338
1580
  async registerTrads({ locales }) {
1339
1581
  const importedTrads = await Promise.all(
1340
1582
  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 }) => {
1583
+ 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-BYRZFDBV.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
1584
  return {
1343
1585
  data: prefixPluginTranslations(data, pluginId),
1344
1586
  locale
@@ -1364,4 +1606,4 @@ export {
1364
1606
  index as i,
1365
1607
  useCreateLocaleMutation as u
1366
1608
  };
1367
- //# sourceMappingURL=index-CCZJF_EJ.mjs.map
1609
+ //# sourceMappingURL=index-BFk3nfTb.mjs.map