@strapi/i18n 0.0.0-experimental.60f3ded53a22a24d208ebf6df9b84c118aa97abf → 0.0.0-experimental.626cf5324f21d318dee435c11cb3e08bb4c414b7

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 (40) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/{SettingsPage-B5sTcP82.mjs → SettingsPage-BjxjwEOb.mjs} +6 -6
  3. package/dist/_chunks/SettingsPage-BjxjwEOb.mjs.map +1 -0
  4. package/dist/_chunks/{SettingsPage-DT1sxWa2.js → SettingsPage-CfTmCkup.js} +6 -6
  5. package/dist/_chunks/SettingsPage-CfTmCkup.js.map +1 -0
  6. package/dist/_chunks/{en-CM6Pjfyv.mjs → en-2xztdZE1.mjs} +7 -1
  7. package/dist/_chunks/en-2xztdZE1.mjs.map +1 -0
  8. package/dist/_chunks/{en-BsOU9o5z.js → en-DWpfm8h5.js} +7 -1
  9. package/dist/_chunks/en-DWpfm8h5.js.map +1 -0
  10. package/dist/_chunks/{index-jMrzaEb9.js → index-5XLZwzwx.js} +341 -146
  11. package/dist/_chunks/index-5XLZwzwx.js.map +1 -0
  12. package/dist/_chunks/{index-DsNqyQKx.mjs → index-D-qx3tz4.mjs} +337 -142
  13. package/dist/_chunks/index-D-qx3tz4.mjs.map +1 -0
  14. package/dist/admin/index.js +1 -1
  15. package/dist/admin/index.mjs +1 -1
  16. package/dist/admin/src/components/BulkLocaleActionModal.d.ts +2 -1
  17. package/dist/admin/src/components/CMHeaderActions.d.ts +27 -3
  18. package/dist/admin/src/utils/clean.d.ts +4 -0
  19. package/dist/admin/src/utils/schemas.d.ts +1 -0
  20. package/dist/server/index.js +38 -9
  21. package/dist/server/index.js.map +1 -1
  22. package/dist/server/index.mjs +39 -10
  23. package/dist/server/index.mjs.map +1 -1
  24. package/dist/server/src/bootstrap.d.ts.map +1 -1
  25. package/dist/server/src/index.d.ts +14 -2
  26. package/dist/server/src/index.d.ts.map +1 -1
  27. package/dist/server/src/services/index.d.ts +14 -2
  28. package/dist/server/src/services/index.d.ts.map +1 -1
  29. package/dist/server/src/services/permissions/actions.d.ts +14 -2
  30. package/dist/server/src/services/permissions/actions.d.ts.map +1 -1
  31. package/dist/server/src/services/permissions.d.ts +14 -2
  32. package/dist/server/src/services/permissions.d.ts.map +1 -1
  33. package/package.json +9 -9
  34. package/dist/_chunks/SettingsPage-B5sTcP82.mjs.map +0 -1
  35. package/dist/_chunks/SettingsPage-DT1sxWa2.js.map +0 -1
  36. package/dist/_chunks/en-BsOU9o5z.js.map +0 -1
  37. package/dist/_chunks/en-CM6Pjfyv.mjs.map +0 -1
  38. package/dist/_chunks/index-DsNqyQKx.mjs.map +0 -1
  39. package/dist/_chunks/index-jMrzaEb9.js.map +0 -1
  40. package/dist/admin/src/components/Initializer.d.ts +0 -5
@@ -1,12 +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, 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
- import { useAuth, adminApi, useTable, Table, useQueryParams, useNotification, useAPIErrorHandler } from "@strapi/admin/strapi-admin";
9
+ import { skipToken } from "@reduxjs/toolkit/query";
10
+ import { useAuth, adminApi, useTable, Table, useQueryParams, useForm, useNotification, useAPIErrorHandler } from "@strapi/admin/strapi-admin";
10
11
  import { unstable_useDocument, unstable_useDocumentActions, buildValidParams } from "@strapi/content-manager/strapi-admin";
11
12
  import { useParams, Link, useNavigate, matchPath } from "react-router-dom";
12
13
  import * as qs from "qs";
@@ -77,11 +78,11 @@ const CheckboxConfirmation = ({
77
78
  }) }) })
78
79
  ] }) }),
79
80
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
80
- /* @__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({
81
82
  id: "components.popUpWarning.button.cancel",
82
83
  defaultMessage: "No, cancel"
83
84
  }) }) }),
84
- /* @__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({
85
86
  id: getTranslation("CheckboxConfirmation.Modal.button-confirm"),
86
87
  defaultMessage: "Yes, disable"
87
88
  }) }) })
@@ -136,7 +137,7 @@ const useI18n = () => {
136
137
  model: params.slug
137
138
  },
138
139
  {
139
- skip: !params.slug || !params.collectionType
140
+ skip: true
140
141
  }
141
142
  );
142
143
  if (doesPluginOptionsHaveI18nLocalized(schema?.pluginOptions)) {
@@ -216,10 +217,93 @@ const relationsApi = i18nApi.injectEndpoints({
216
217
  })
217
218
  });
218
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
+ };
219
299
  const isErrorMessageDescriptor = (object) => {
220
300
  return typeof object === "object" && object !== null && "id" in object && "defaultMessage" in object;
221
301
  };
222
- const EntryValidationText = ({ status = "draft", validationErrors }) => {
302
+ const EntryValidationText = ({
303
+ status = "draft",
304
+ validationErrors,
305
+ action
306
+ }) => {
223
307
  const { formatMessage } = useIntl();
224
308
  const getErrorStr = (key, value) => {
225
309
  if (typeof value === "string") {
@@ -253,30 +337,63 @@ const EntryValidationText = ({ status = "draft", validationErrors }) => {
253
337
  ) })
254
338
  ] });
255
339
  }
256
- if (status === "published") {
257
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
258
- /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
259
- /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
260
- id: "content-manager.bulk-publish.already-published",
261
- defaultMessage: "Already Published"
262
- }) })
263
- ] });
264
- }
265
- if (status === "modified") {
266
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
267
- /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
268
- /* @__PURE__ */ jsx(Typography, { children: formatMessage({
269
- id: "app.utils.ready-to-publish-changes",
270
- defaultMessage: "Ready to publish changes"
271
- }) })
272
- ] });
273
- }
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();
274
394
  return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
275
- /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
276
- /* @__PURE__ */ jsx(Typography, { children: formatMessage({
277
- id: "app.utils.ready-to-publish",
278
- defaultMessage: "Ready to publish"
279
- }) })
395
+ icon,
396
+ /* @__PURE__ */ jsx(Typography, { textColor, fontWeight, children: text })
280
397
  ] });
281
398
  };
282
399
  const BoldChunk = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
@@ -284,7 +401,8 @@ const BulkLocaleActionModal = ({
284
401
  headers,
285
402
  rows,
286
403
  localesMetadata,
287
- validationErrors = {}
404
+ validationErrors = {},
405
+ action
288
406
  }) => {
289
407
  const { formatMessage } = useIntl();
290
408
  const selectedRows = useTable(
@@ -297,27 +415,29 @@ const BulkLocaleActionModal = ({
297
415
  return acc;
298
416
  }, {});
299
417
  const localesWithErrors = Object.keys(validationErrors);
300
- const alreadyPublishedCount = selectedRows.filter(
418
+ const publishedCount = selectedRows.filter(
301
419
  ({ locale }) => currentStatusByLocale[locale] === "published"
302
420
  ).length;
303
- const readyToPublishCount = selectedRows.filter(
421
+ const draftCount = selectedRows.filter(
304
422
  ({ locale }) => (currentStatusByLocale[locale] === "draft" || currentStatusByLocale[locale] === "modified") && !localesWithErrors.includes(locale)
305
423
  ).length;
306
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.";
307
427
  return formatMessage(
308
428
  {
309
- id: "content-manager.containers.list.selectedEntriesModal.selectedCount",
310
- 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
311
431
  },
312
432
  {
313
433
  withErrorsCount,
314
- readyToPublishCount,
315
- alreadyPublishedCount,
434
+ draftCount,
435
+ publishedCount,
316
436
  b: BoldChunk
317
437
  }
318
438
  );
319
439
  };
320
- return /* @__PURE__ */ jsxs(React.Fragment, { children: [
440
+ return /* @__PURE__ */ jsxs(Modal.Body, { children: [
321
441
  /* @__PURE__ */ jsx(Typography, { children: getFormattedCountMessage() }),
322
442
  /* @__PURE__ */ jsx(Box, { marginTop: 5, children: /* @__PURE__ */ jsxs(Table.Content, { children: [
323
443
  /* @__PURE__ */ jsxs(Table.Head, { children: [
@@ -344,7 +464,7 @@ const BulkLocaleActionModal = ({
344
464
  children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
345
465
  }
346
466
  ) }) }),
347
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status }) }),
467
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status, action }) }),
348
468
  /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
349
469
  IconButton,
350
470
  {
@@ -361,7 +481,7 @@ const BulkLocaleActionModal = ({
361
481
  name: locale
362
482
  }
363
483
  ),
364
- borderWidth: 0,
484
+ variant: "ghost",
365
485
  children: /* @__PURE__ */ jsx(Pencil, {})
366
486
  }
367
487
  ) })
@@ -381,7 +501,13 @@ const LocalePickerAction = ({
381
501
  const [{ query }, setQuery] = useQueryParams();
382
502
  const { hasI18n, canCreate, canRead } = useI18n();
383
503
  const { data: locales = [] } = useGetLocalesQuery();
384
- 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
+ });
385
511
  const handleSelect = React.useCallback(
386
512
  (value) => {
387
513
  setQuery({
@@ -399,21 +525,20 @@ const LocalePickerAction = ({
399
525
  if (!Array.isArray(locales) || !hasI18n) {
400
526
  return;
401
527
  }
402
- const currentDesiredLocale = query.plugins?.i18n?.locale;
403
528
  const doesLocaleExist = locales.find((loc) => loc.code === currentDesiredLocale);
404
529
  const defaultLocale = locales.find((locale) => locale.isDefault);
405
530
  if (!doesLocaleExist && defaultLocale?.code) {
406
531
  handleSelect(defaultLocale.code);
407
532
  }
408
- }, [handleSelect, hasI18n, locales, query.plugins?.i18n?.locale]);
409
- if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
410
- return null;
411
- }
412
- 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;
413
535
  const allCurrentLocales = [
414
536
  { status: getDocumentStatus(document, meta), locale: currentLocale },
415
537
  ...meta?.availableLocales ?? []
416
538
  ];
539
+ if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
540
+ return null;
541
+ }
417
542
  return {
418
543
  label: formatMessage({
419
544
  id: getTranslation("Settings.locales.modal.locales.label"),
@@ -425,7 +550,7 @@ const LocalePickerAction = ({
425
550
  );
426
551
  const status = currentLocaleDoc?.status ?? "draft";
427
552
  const permissionsToCheck = currentLocaleDoc ? canCreate : canRead;
428
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
553
+ const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
429
554
  return {
430
555
  disabled: !permissionsToCheck.includes(locale.code),
431
556
  value: locale.code,
@@ -461,6 +586,95 @@ const getDocumentStatus = (document, meta) => {
461
586
  }
462
587
  return docStatus;
463
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
+ };
464
678
  const DeleteLocaleAction = ({
465
679
  document,
466
680
  documentId,
@@ -524,37 +738,43 @@ const DeleteLocaleAction = ({
524
738
  }
525
739
  };
526
740
  };
527
- const BulkLocalePublishAction = ({
741
+ const BulkLocaleAction = ({
528
742
  document: baseDocument,
529
743
  documentId,
530
744
  model,
531
- collectionType
745
+ collectionType,
746
+ action
532
747
  }) => {
533
748
  const baseLocale = baseDocument?.locale ?? null;
534
749
  const [{ query }] = useQueryParams();
535
750
  const params = React.useMemo(() => buildValidParams(query), [query]);
536
- const isPublishedTab = query.status === "published";
751
+ const isOnPublishedTab = query.status === "published";
537
752
  const { formatMessage } = useIntl();
538
753
  const { hasI18n, canPublish } = useI18n();
539
754
  const { toggleNotification } = useNotification();
540
755
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
541
756
  const [selectedRows, setSelectedRows] = React.useState([]);
542
757
  const [isDraftRelationConfirmationOpen, setIsDraftRelationConfirmationOpen] = React.useState(false);
543
- const { publishMany: publishManyAction } = unstable_useDocumentActions();
758
+ const { publishMany: publishManyAction, unpublishMany: unpublishManyAction } = unstable_useDocumentActions();
544
759
  const {
545
760
  document,
546
761
  meta: documentMeta,
547
762
  schema,
548
763
  validate
549
- } = unstable_useDocument({
550
- model,
551
- collectionType,
552
- documentId,
553
- params: {
554
- locale: baseLocale
764
+ } = unstable_useDocument(
765
+ {
766
+ model,
767
+ collectionType,
768
+ documentId,
769
+ params: {
770
+ locale: baseLocale
771
+ }
772
+ },
773
+ {
774
+ skip: !hasI18n || !baseLocale
555
775
  }
556
- });
557
- const { data: localesMetadata = [] } = useGetLocalesQuery();
776
+ );
777
+ const { data: localesMetadata = [] } = useGetLocalesQuery(hasI18n ? void 0 : skipToken);
558
778
  const headers = [
559
779
  {
560
780
  label: formatMessage({
@@ -603,12 +823,19 @@ const BulkLocalePublishAction = ({
603
823
  }, {});
604
824
  return [rowsFromMeta, errors];
605
825
  }, [document, documentMeta?.availableLocales, validate]);
606
- const localesToPublish = selectedRows.reduce((acc, selectedRow) => {
607
- if (selectedRow.status !== "published" && !Object.keys(validationErrors).includes(selectedRow.locale)) {
826
+ const isBulkPublish = action === "bulk-publish";
827
+ const localesForAction = selectedRows.reduce((acc, selectedRow) => {
828
+ const isValidLocale = (
829
+ // Validation errors are irrelevant if we are trying to unpublish
830
+ !isBulkPublish || !Object.keys(validationErrors).includes(selectedRow.locale)
831
+ );
832
+ const shouldAddLocale = isBulkPublish ? selectedRow.status !== "published" && isValidLocale : selectedRow.status !== "draft" && isValidLocale;
833
+ if (shouldAddLocale) {
608
834
  acc.push(selectedRow.locale);
609
835
  }
610
836
  return acc;
611
837
  }, []);
838
+ const enableDraftRelationsCount = false;
612
839
  const {
613
840
  data: draftRelationsCount = 0,
614
841
  isLoading: isDraftRelationsLoading,
@@ -617,10 +844,10 @@ const BulkLocalePublishAction = ({
617
844
  {
618
845
  model,
619
846
  documentIds: [documentId],
620
- locale: localesToPublish
847
+ locale: localesForAction
621
848
  },
622
849
  {
623
- skip: !documentId || localesToPublish.length === 0
850
+ skip: !enableDraftRelationsCount
624
851
  }
625
852
  );
626
853
  React.useEffect(() => {
@@ -646,7 +873,18 @@ const BulkLocalePublishAction = ({
646
873
  documentIds: [documentId],
647
874
  params: {
648
875
  ...params,
649
- locale: localesToPublish
876
+ locale: localesForAction
877
+ }
878
+ });
879
+ setSelectedRows([]);
880
+ };
881
+ const unpublish = async () => {
882
+ await unpublishManyAction({
883
+ model,
884
+ documentIds: [documentId],
885
+ params: {
886
+ ...params,
887
+ locale: localesForAction
650
888
  }
651
889
  });
652
890
  setSelectedRows([]);
@@ -654,14 +892,12 @@ const BulkLocalePublishAction = ({
654
892
  const handleAction = async () => {
655
893
  if (draftRelationsCount > 0) {
656
894
  setIsDraftRelationConfirmationOpen(true);
657
- } else {
895
+ } else if (isBulkPublish) {
658
896
  await publish();
897
+ } else {
898
+ await unpublish();
659
899
  }
660
900
  };
661
- const isUnpublish = document?.status === "published";
662
- if (isUnpublish) {
663
- console.warn(["I18N"], "Bulk locale unpublish modal not implemented");
664
- }
665
901
  if (isDraftRelationConfirmationOpen) {
666
902
  return {
667
903
  label: formatMessage({
@@ -696,20 +932,21 @@ const BulkLocalePublishAction = ({
696
932
  }
697
933
  };
698
934
  }
935
+ const hasPermission = selectedRows.map(({ locale }) => locale).every((locale) => canPublish.includes(locale));
699
936
  return {
700
937
  label: formatMessage({
701
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
702
- defaultMessage: "Publish Multiple Locales"
938
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
939
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
703
940
  }),
704
- icon: /* @__PURE__ */ jsx(ListPlus, {}),
705
- disabled: isPublishedTab || !canPublish,
941
+ variant: isBulkPublish ? "secondary" : "danger",
942
+ icon: isBulkPublish ? /* @__PURE__ */ jsx(ListPlus, {}) : /* @__PURE__ */ jsx(Cross, {}),
943
+ disabled: isOnPublishedTab || canPublish.length === 0,
706
944
  position: ["panel"],
707
- variant: "secondary",
708
945
  dialog: {
709
946
  type: "modal",
710
947
  title: formatMessage({
711
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
712
- defaultMessage: "Publish Multiple Locales"
948
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
949
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
713
950
  }),
714
951
  content: () => {
715
952
  return /* @__PURE__ */ jsx(
@@ -728,28 +965,35 @@ const BulkLocalePublishAction = ({
728
965
  validationErrors,
729
966
  headers,
730
967
  rows,
731
- localesMetadata
968
+ localesMetadata,
969
+ action: action ?? "bulk-publish"
732
970
  }
733
971
  )
734
972
  }
735
973
  );
736
974
  },
737
- footer: () => /* @__PURE__ */ jsx(Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
738
- Button$1,
975
+ footer: () => /* @__PURE__ */ jsx(Modal.Footer, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
976
+ Button,
739
977
  {
740
978
  loading: isDraftRelationsLoading,
741
- disabled: localesToPublish.length === 0,
979
+ disabled: !hasPermission || localesForAction.length === 0,
742
980
  variant: "default",
743
981
  onClick: handleAction,
744
982
  children: formatMessage({
745
- id: "app.utils.publish",
746
- defaultMessage: "Publish"
983
+ id: isBulkPublish ? "app.utils.publish" : "app.utils.unpublish",
984
+ defaultMessage: isBulkPublish ? "Publish" : "Unpublish"
747
985
  })
748
986
  }
749
987
  ) })
750
988
  }
751
989
  };
752
990
  };
991
+ const BulkLocalePublishAction = (props) => {
992
+ return BulkLocaleAction({ action: "bulk-publish", ...props });
993
+ };
994
+ const BulkLocaleUnpublishAction = (props) => {
995
+ return BulkLocaleAction({ action: "bulk-unpublish", ...props });
996
+ };
753
997
  const StyledTrash = styled(Trash)`
754
998
  path {
755
999
  fill: currentColor;
@@ -806,13 +1050,6 @@ const UnpublishModalAdditionalInfo = () => {
806
1050
  }
807
1051
  ) });
808
1052
  };
809
- const Initializer = ({ setPlugin }) => {
810
- const setPluginRef = React.useRef(setPlugin);
811
- React.useEffect(() => {
812
- setPluginRef.current(pluginId);
813
- }, []);
814
- return null;
815
- };
816
1053
  const LocalePicker = () => {
817
1054
  const { formatMessage } = useIntl();
818
1055
  const [{ query }, setQuery] = useQueryParams();
@@ -976,54 +1213,13 @@ const LocaleListCell = ({
976
1213
  return locale.name;
977
1214
  }).toSorted((a, b) => formatter.compare(a, b));
978
1215
  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
- ) }) }),
1216
+ /* @__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: [
1217
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", ellipsis: true, marginRight: 2, children: localesForDocument.join(", ") }),
1218
+ /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(CaretDown, { width: "1.2rem", height: "1.2rem" }) })
1219
+ ] }) }) }),
993
1220
  /* @__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
1221
  ] });
995
1222
  };
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
1223
  const addColumnToTableHook = ({ displayedHeaders, layout }) => {
1028
1224
  const { options } = layout;
1029
1225
  const isFieldLocalized = doesPluginOptionsHaveI18nLocalized(options) ? options.i18n.localized : false;
@@ -1204,8 +1400,6 @@ const index = {
1204
1400
  app.addRBACMiddleware([localeMiddleware]);
1205
1401
  app.registerPlugin({
1206
1402
  id: pluginId,
1207
- initializer: Initializer,
1208
- isReady: false,
1209
1403
  name: pluginId
1210
1404
  });
1211
1405
  },
@@ -1223,11 +1417,11 @@ const index = {
1223
1417
  },
1224
1418
  id: "internationalization",
1225
1419
  to: "internationalization",
1226
- Component: () => import("./SettingsPage-B5sTcP82.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1420
+ Component: () => import("./SettingsPage-BjxjwEOb.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1227
1421
  permissions: PERMISSIONS.accessMain
1228
1422
  });
1229
1423
  const contentManager = app.getPlugin("content-manager");
1230
- contentManager.apis.addDocumentHeaderAction([LocalePickerAction]);
1424
+ contentManager.apis.addDocumentHeaderAction([LocalePickerAction, FillFromAnotherLocaleAction]);
1231
1425
  contentManager.apis.addDocumentAction((actions) => {
1232
1426
  const indexOfDeleteAction = actions.findIndex((action) => action.type === "delete");
1233
1427
  actions.splice(indexOfDeleteAction, 0, DeleteLocaleAction);
@@ -1235,6 +1429,7 @@ const index = {
1235
1429
  });
1236
1430
  contentManager.apis.addDocumentAction((actions) => {
1237
1431
  actions.splice(2, 0, BulkLocalePublishAction);
1432
+ actions.splice(5, 0, BulkLocaleUnpublishAction);
1238
1433
  return actions;
1239
1434
  });
1240
1435
  contentManager.injectComponent("listView", "actions", {
@@ -1340,7 +1535,7 @@ const index = {
1340
1535
  async registerTrads({ locales }) {
1341
1536
  const importedTrads = await Promise.all(
1342
1537
  locales.map((locale) => {
1343
- 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 }) => {
1538
+ 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-2xztdZE1.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 }) => {
1344
1539
  return {
1345
1540
  data: prefixPluginTranslations(data, pluginId),
1346
1541
  locale
@@ -1366,4 +1561,4 @@ export {
1366
1561
  index as i,
1367
1562
  useCreateLocaleMutation as u
1368
1563
  };
1369
- //# sourceMappingURL=index-DsNqyQKx.mjs.map
1564
+ //# sourceMappingURL=index-D-qx3tz4.mjs.map