@strapi/i18n 0.0.0-experimental.25e22c6cc9bc6b35392bb55d09f641a0a65e7403 → 0.0.0-experimental.2cfaca2410c03f1dee31ca18c06aedfb313e0fb4

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 (47) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/{SettingsPage-Dsi2qGtq.mjs → SettingsPage-BIZrSFGY.mjs} +15 -10
  3. package/dist/_chunks/SettingsPage-BIZrSFGY.mjs.map +1 -0
  4. package/dist/_chunks/{SettingsPage-VN7sTzkb.js → SettingsPage-kMDCxWLw.js} +15 -10
  5. package/dist/_chunks/SettingsPage-kMDCxWLw.js.map +1 -0
  6. package/dist/_chunks/{en-Kv6y9zPQ.js → en-B6327hMz.js} +10 -2
  7. package/dist/_chunks/en-B6327hMz.js.map +1 -0
  8. package/dist/_chunks/{en-18tWw4P6.mjs → en-DZXpOMHo.mjs} +10 -2
  9. package/dist/_chunks/en-DZXpOMHo.mjs.map +1 -0
  10. package/dist/_chunks/{index-DhtjJYrx.mjs → index-DXrgAtCA.mjs} +363 -164
  11. package/dist/_chunks/index-DXrgAtCA.mjs.map +1 -0
  12. package/dist/_chunks/{index-kedPlCo6.js → index-Dncj9Inq.js} +367 -168
  13. package/dist/_chunks/index-Dncj9Inq.js.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/contentReleasesHooks/releaseDetailsView.d.ts +9 -5
  19. package/dist/admin/src/utils/clean.d.ts +4 -0
  20. package/dist/admin/src/utils/schemas.d.ts +1 -0
  21. package/dist/server/index.js +61 -98
  22. package/dist/server/index.js.map +1 -1
  23. package/dist/server/index.mjs +62 -99
  24. package/dist/server/index.mjs.map +1 -1
  25. package/dist/server/src/bootstrap.d.ts +1 -4
  26. package/dist/server/src/bootstrap.d.ts.map +1 -1
  27. package/dist/server/src/index.d.ts +15 -13
  28. package/dist/server/src/index.d.ts.map +1 -1
  29. package/dist/server/src/services/index.d.ts +14 -10
  30. package/dist/server/src/services/index.d.ts.map +1 -1
  31. package/dist/server/src/services/permissions/actions.d.ts +14 -2
  32. package/dist/server/src/services/permissions/actions.d.ts.map +1 -1
  33. package/dist/server/src/services/permissions.d.ts +14 -2
  34. package/dist/server/src/services/permissions.d.ts.map +1 -1
  35. package/dist/server/src/utils/index.d.ts +0 -2
  36. package/dist/server/src/utils/index.d.ts.map +1 -1
  37. package/package.json +9 -9
  38. package/dist/_chunks/SettingsPage-Dsi2qGtq.mjs.map +0 -1
  39. package/dist/_chunks/SettingsPage-VN7sTzkb.js.map +0 -1
  40. package/dist/_chunks/en-18tWw4P6.mjs.map +0 -1
  41. package/dist/_chunks/en-Kv6y9zPQ.js.map +0 -1
  42. package/dist/_chunks/index-DhtjJYrx.mjs.map +0 -1
  43. package/dist/_chunks/index-kedPlCo6.js.map +0 -1
  44. package/dist/admin/src/components/Initializer.d.ts +0 -5
  45. package/dist/server/src/services/entity-service-decorator.d.ts +0 -29
  46. package/dist/server/src/services/entity-service-decorator.d.ts.map +0 -1
  47. package/strapi-server.js +0 -3
@@ -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,
@@ -472,16 +686,23 @@ const DeleteLocaleAction = ({
472
686
  const { toggleNotification } = useNotification();
473
687
  const { delete: deleteAction } = unstable_useDocumentActions();
474
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);
475
693
  if (!hasI18n) {
476
694
  return null;
477
695
  }
478
696
  return {
479
697
  disabled: document?.locale && !canDelete.includes(document.locale) || !document || !document.id,
480
698
  position: ["header", "table-row"],
481
- label: formatMessage({
482
- id: getTranslation("actions.delete.label"),
483
- defaultMessage: "Delete locale"
484
- }),
699
+ label: formatMessage(
700
+ {
701
+ id: getTranslation("actions.delete.label"),
702
+ defaultMessage: "Delete entry ({locale})"
703
+ },
704
+ { locale: locale && locale.name }
705
+ ),
485
706
  icon: /* @__PURE__ */ jsx(StyledTrash, {}),
486
707
  variant: "danger",
487
708
  dialog: {
@@ -524,37 +745,43 @@ const DeleteLocaleAction = ({
524
745
  }
525
746
  };
526
747
  };
527
- const BulkLocalePublishAction = ({
748
+ const BulkLocaleAction = ({
528
749
  document: baseDocument,
529
750
  documentId,
530
751
  model,
531
- collectionType
752
+ collectionType,
753
+ action
532
754
  }) => {
533
755
  const baseLocale = baseDocument?.locale ?? null;
534
756
  const [{ query }] = useQueryParams();
535
757
  const params = React.useMemo(() => buildValidParams(query), [query]);
536
- const isPublishedTab = query.status === "published";
758
+ const isOnPublishedTab = query.status === "published";
537
759
  const { formatMessage } = useIntl();
538
760
  const { hasI18n, canPublish } = useI18n();
539
761
  const { toggleNotification } = useNotification();
540
762
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
541
763
  const [selectedRows, setSelectedRows] = React.useState([]);
542
- const [isConfirmationOpen, setIsConfirmationOpen] = React.useState(false);
543
- const { publishMany: publishManyAction } = unstable_useDocumentActions();
764
+ const [isDraftRelationConfirmationOpen, setIsDraftRelationConfirmationOpen] = React.useState(false);
765
+ const { publishMany: publishManyAction, unpublishMany: unpublishManyAction } = unstable_useDocumentActions();
544
766
  const {
545
767
  document,
546
768
  meta: documentMeta,
547
769
  schema,
548
770
  validate
549
- } = unstable_useDocument({
550
- model,
551
- collectionType,
552
- documentId,
553
- params: {
554
- locale: baseLocale
771
+ } = unstable_useDocument(
772
+ {
773
+ model,
774
+ collectionType,
775
+ documentId,
776
+ params: {
777
+ locale: baseLocale
778
+ }
779
+ },
780
+ {
781
+ skip: !hasI18n || !baseLocale
555
782
  }
556
- });
557
- const { data: localesMetadata = [] } = useGetLocalesQuery();
783
+ );
784
+ const { data: localesMetadata = [] } = useGetLocalesQuery(hasI18n ? void 0 : skipToken);
558
785
  const headers = [
559
786
  {
560
787
  label: formatMessage({
@@ -603,12 +830,19 @@ const BulkLocalePublishAction = ({
603
830
  }, {});
604
831
  return [rowsFromMeta, errors];
605
832
  }, [document, documentMeta?.availableLocales, validate]);
606
- const localesToPublish = selectedRows.reduce((acc, selectedRow) => {
607
- 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) {
608
841
  acc.push(selectedRow.locale);
609
842
  }
610
843
  return acc;
611
844
  }, []);
845
+ const enableDraftRelationsCount = false;
612
846
  const {
613
847
  data: draftRelationsCount = 0,
614
848
  isLoading: isDraftRelationsLoading,
@@ -617,10 +851,10 @@ const BulkLocalePublishAction = ({
617
851
  {
618
852
  model,
619
853
  documentIds: [documentId],
620
- locale: localesToPublish
854
+ locale: localesForAction
621
855
  },
622
856
  {
623
- skip: !documentId || localesToPublish.length === 0
857
+ skip: !enableDraftRelationsCount
624
858
  }
625
859
  );
626
860
  React.useEffect(() => {
@@ -646,23 +880,32 @@ const BulkLocalePublishAction = ({
646
880
  documentIds: [documentId],
647
881
  params: {
648
882
  ...params,
649
- 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
650
895
  }
651
896
  });
652
897
  setSelectedRows([]);
653
898
  };
654
899
  const handleAction = async () => {
655
900
  if (draftRelationsCount > 0) {
656
- setIsConfirmationOpen(true);
657
- } else {
901
+ setIsDraftRelationConfirmationOpen(true);
902
+ } else if (isBulkPublish) {
658
903
  await publish();
904
+ } else {
905
+ await unpublish();
659
906
  }
660
907
  };
661
- const isUnpublish = document?.status === "published";
662
- if (isUnpublish) {
663
- console.warn(["I18N"], "Bulk locale unpublish modal not implemented");
664
- }
665
- if (isConfirmationOpen) {
908
+ if (isDraftRelationConfirmationOpen) {
666
909
  return {
667
910
  label: formatMessage({
668
911
  id: "app.components.ConfirmDialog.title",
@@ -671,11 +914,11 @@ const BulkLocalePublishAction = ({
671
914
  variant: "danger",
672
915
  dialog: {
673
916
  onCancel: () => {
674
- setIsConfirmationOpen(false);
917
+ setIsDraftRelationConfirmationOpen(false);
675
918
  },
676
919
  onConfirm: async () => {
677
920
  await publish();
678
- setIsConfirmationOpen(false);
921
+ setIsDraftRelationConfirmationOpen(false);
679
922
  },
680
923
  type: "dialog",
681
924
  title: formatMessage({
@@ -685,27 +928,32 @@ const BulkLocalePublishAction = ({
685
928
  content: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "center", gap: 2, children: [
686
929
  /* @__PURE__ */ jsx(WarningCircle, { width: "2.4rem", height: "2.4rem", fill: "danger600" }),
687
930
  /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
688
- id: "content-manager.actions.discard.dialog.body",
689
- defaultMessage: "Are you sure you want to discard the changes? This action is irreversible."
931
+ id: getTranslation("CMEditViewBulkLocale.draft-relation-warning"),
932
+ defaultMessage: "Some locales are related to draft entries. Publishing them could leave broken links in your app."
933
+ }) }),
934
+ /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
935
+ id: getTranslation("CMEditViewBulkLocale.continue-confirmation"),
936
+ defaultMessage: "Are you sure you want to continue?"
690
937
  }) })
691
938
  ] })
692
939
  }
693
940
  };
694
941
  }
942
+ const hasPermission = selectedRows.map(({ locale }) => locale).every((locale) => canPublish.includes(locale));
695
943
  return {
696
944
  label: formatMessage({
697
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
698
- defaultMessage: "Publish Multiple Locales"
945
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
946
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
699
947
  }),
700
- icon: /* @__PURE__ */ jsx(ListPlus, {}),
701
- disabled: isPublishedTab || !canPublish,
948
+ variant: isBulkPublish ? "secondary" : "danger",
949
+ icon: isBulkPublish ? /* @__PURE__ */ jsx(ListPlus, {}) : /* @__PURE__ */ jsx(Cross, {}),
950
+ disabled: isOnPublishedTab || canPublish.length === 0,
702
951
  position: ["panel"],
703
- variant: "secondary",
704
952
  dialog: {
705
953
  type: "modal",
706
954
  title: formatMessage({
707
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
708
- defaultMessage: "Publish Multiple Locales"
955
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
956
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
709
957
  }),
710
958
  content: () => {
711
959
  return /* @__PURE__ */ jsx(
@@ -724,28 +972,35 @@ const BulkLocalePublishAction = ({
724
972
  validationErrors,
725
973
  headers,
726
974
  rows,
727
- localesMetadata
975
+ localesMetadata,
976
+ action: action ?? "bulk-publish"
728
977
  }
729
978
  )
730
979
  }
731
980
  );
732
981
  },
733
- footer: () => /* @__PURE__ */ jsx(Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
734
- Button$1,
982
+ footer: () => /* @__PURE__ */ jsx(Modal.Footer, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
983
+ Button,
735
984
  {
736
985
  loading: isDraftRelationsLoading,
737
- disabled: localesToPublish.length === 0,
986
+ disabled: !hasPermission || localesForAction.length === 0,
738
987
  variant: "default",
739
988
  onClick: handleAction,
740
989
  children: formatMessage({
741
- id: "app.utils.publish",
742
- defaultMessage: "Publish"
990
+ id: isBulkPublish ? "app.utils.publish" : "app.utils.unpublish",
991
+ defaultMessage: isBulkPublish ? "Publish" : "Unpublish"
743
992
  })
744
993
  }
745
994
  ) })
746
995
  }
747
996
  };
748
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
+ };
749
1004
  const StyledTrash = styled(Trash)`
750
1005
  path {
751
1006
  fill: currentColor;
@@ -802,13 +1057,6 @@ const UnpublishModalAdditionalInfo = () => {
802
1057
  }
803
1058
  ) });
804
1059
  };
805
- const Initializer = ({ setPlugin }) => {
806
- const setPluginRef = React.useRef(setPlugin);
807
- React.useEffect(() => {
808
- setPluginRef.current(pluginId);
809
- }, []);
810
- return null;
811
- };
812
1060
  const LocalePicker = () => {
813
1061
  const { formatMessage } = useIntl();
814
1062
  const [{ query }, setQuery] = useQueryParams();
@@ -972,54 +1220,13 @@ const LocaleListCell = ({
972
1220
  return locale.name;
973
1221
  }).toSorted((a, b) => formatter.compare(a, b));
974
1222
  return /* @__PURE__ */ jsxs(Popover.Root, { children: [
975
- /* @__PURE__ */ jsx(Popover.Trigger, { children: /* @__PURE__ */ jsx(Button, { type: "button", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs(
976
- ActionWrapper,
977
- {
978
- minWidth: "100%",
979
- alignItems: "center",
980
- justifyContent: "center",
981
- height: "3.2rem",
982
- width: "3.2rem",
983
- children: [
984
- /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", ellipsis: true, children: localesForDocument.join(", ") }),
985
- /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(CaretDown, {}) })
986
- ]
987
- }
988
- ) }) }),
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
+ ] }) }) }),
989
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)) }) })
990
1228
  ] });
991
1229
  };
992
- const Button = styled.button`
993
- width: 100%;
994
-
995
- svg {
996
- > g,
997
- path {
998
- fill: ${({ theme }) => theme.colors.neutral500};
999
- }
1000
- }
1001
- &:hover {
1002
- svg {
1003
- > g,
1004
- path {
1005
- fill: ${({ theme }) => theme.colors.neutral600};
1006
- }
1007
- }
1008
- }
1009
- &:active {
1010
- svg {
1011
- > g,
1012
- path {
1013
- fill: ${({ theme }) => theme.colors.neutral400};
1014
- }
1015
- }
1016
- }
1017
- `;
1018
- const ActionWrapper = styled(Flex)`
1019
- svg {
1020
- height: 0.4rem;
1021
- }
1022
- `;
1023
1230
  const addColumnToTableHook = ({ displayedHeaders, layout }) => {
1024
1231
  const { options } = layout;
1025
1232
  const isFieldLocalized = doesPluginOptionsHaveI18nLocalized(options) ? options.i18n.localized : false;
@@ -1048,18 +1255,11 @@ const addColumnToTableHook = ({ displayedHeaders, layout }) => {
1048
1255
  const addLocaleToReleasesHook = ({ displayedHeaders = [] }) => {
1049
1256
  return {
1050
1257
  displayedHeaders: [
1051
- // TODO: Fix when migrating to v5
1052
- // ...displayedHeaders,
1258
+ ...displayedHeaders,
1053
1259
  {
1054
- key: "__locale__",
1055
- fieldSchema: { type: "string" },
1056
- metadatas: {
1057
- label: {
1058
- id: "content-releases.page.ReleaseDetails.table.header.label.locale",
1059
- defaultMessage: "locale"
1060
- },
1061
- searchable: false,
1062
- sortable: false
1260
+ label: {
1261
+ id: "content-releases.page.ReleaseDetails.table.header.label.locale",
1262
+ defaultMessage: "locale"
1063
1263
  },
1064
1264
  name: "locale"
1065
1265
  }
@@ -1207,8 +1407,6 @@ const index = {
1207
1407
  app.addRBACMiddleware([localeMiddleware]);
1208
1408
  app.registerPlugin({
1209
1409
  id: pluginId,
1210
- initializer: Initializer,
1211
- isReady: false,
1212
1410
  name: pluginId
1213
1411
  });
1214
1412
  },
@@ -1226,11 +1424,11 @@ const index = {
1226
1424
  },
1227
1425
  id: "internationalization",
1228
1426
  to: "internationalization",
1229
- Component: () => import("./SettingsPage-Dsi2qGtq.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1427
+ Component: () => import("./SettingsPage-BIZrSFGY.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1230
1428
  permissions: PERMISSIONS.accessMain
1231
1429
  });
1232
1430
  const contentManager = app.getPlugin("content-manager");
1233
- contentManager.apis.addDocumentHeaderAction([LocalePickerAction]);
1431
+ contentManager.apis.addDocumentHeaderAction([LocalePickerAction, FillFromAnotherLocaleAction]);
1234
1432
  contentManager.apis.addDocumentAction((actions) => {
1235
1433
  const indexOfDeleteAction = actions.findIndex((action) => action.type === "delete");
1236
1434
  actions.splice(indexOfDeleteAction, 0, DeleteLocaleAction);
@@ -1238,6 +1436,7 @@ const index = {
1238
1436
  });
1239
1437
  contentManager.apis.addDocumentAction((actions) => {
1240
1438
  actions.splice(2, 0, BulkLocalePublishAction);
1439
+ actions.splice(5, 0, BulkLocaleUnpublishAction);
1241
1440
  return actions;
1242
1441
  });
1243
1442
  contentManager.injectComponent("listView", "actions", {
@@ -1343,7 +1542,7 @@ const index = {
1343
1542
  async registerTrads({ locales }) {
1344
1543
  const importedTrads = await Promise.all(
1345
1544
  locales.map((locale) => {
1346
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/de.json": () => import("./de-9eCAqqrB.mjs"), "./translations/dk.json": () => import("./dk-2qBjxt-P.mjs"), "./translations/en.json": () => import("./en-18tWw4P6.mjs"), "./translations/es.json": () => import("./es-DlmMVaBG.mjs"), "./translations/fr.json": () => import("./fr-3S6ke71d.mjs"), "./translations/ko.json": () => import("./ko-qTjQ8IMw.mjs"), "./translations/pl.json": () => import("./pl-B67TSHqT.mjs"), "./translations/ru.json": () => import("./ru-hagMa57T.mjs"), "./translations/tr.json": () => import("./tr-Dw_jmkG-.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-Dyc-aR-h.mjs"), "./translations/zh.json": () => import("./zh-57YM4amO.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
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 }) => {
1347
1546
  return {
1348
1547
  data: prefixPluginTranslations(data, pluginId),
1349
1548
  locale
@@ -1369,4 +1568,4 @@ export {
1369
1568
  index as i,
1370
1569
  useCreateLocaleMutation as u
1371
1570
  };
1372
- //# sourceMappingURL=index-DhtjJYrx.mjs.map
1571
+ //# sourceMappingURL=index-DXrgAtCA.mjs.map