@strapi/i18n 0.0.0-experimental.edc24aaa3bb5a90fa5fd4fee208167dd4e2e38d4 → 0.0.0-experimental.f0a0bc26f5ef0693aaea2a616bc6b816cfee54b6

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 (51) hide show
  1. package/dist/_chunks/{SettingsPage-DA9haizZ.js → SettingsPage-JyA1zd8f.js} +8 -8
  2. package/dist/_chunks/SettingsPage-JyA1zd8f.js.map +1 -0
  3. package/dist/_chunks/{SettingsPage-CsGvujny.mjs → SettingsPage-giNp3Ftr.mjs} +8 -8
  4. package/dist/_chunks/SettingsPage-giNp3Ftr.mjs.map +1 -0
  5. package/dist/_chunks/{en-BsOU9o5z.js → en-BKBz3tro.js} +10 -3
  6. package/dist/_chunks/en-BKBz3tro.js.map +1 -0
  7. package/dist/_chunks/{en-CM6Pjfyv.mjs → en-DlXfy6Gy.mjs} +10 -3
  8. package/dist/_chunks/en-DlXfy6Gy.mjs.map +1 -0
  9. package/dist/_chunks/{index-CCZJF_EJ.mjs → index-Bt5ork2g.mjs} +398 -149
  10. package/dist/_chunks/index-Bt5ork2g.mjs.map +1 -0
  11. package/dist/_chunks/{index-DIzVXZoE.js → index-BwAeKsym.js} +391 -142
  12. package/dist/_chunks/index-BwAeKsym.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/components/CreateLocale.d.ts +6 -6
  18. package/dist/admin/src/utils/clean.d.ts +4 -0
  19. package/dist/server/index.js +413 -480
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +415 -482
  22. package/dist/server/index.mjs.map +1 -1
  23. package/dist/server/src/bootstrap.d.ts +1 -4
  24. package/dist/server/src/bootstrap.d.ts.map +1 -1
  25. package/dist/server/src/index.d.ts +21 -13
  26. package/dist/server/src/index.d.ts.map +1 -1
  27. package/dist/server/src/register.d.ts.map +1 -1
  28. package/dist/server/src/services/index.d.ts +20 -10
  29. package/dist/server/src/services/index.d.ts.map +1 -1
  30. package/dist/server/src/services/permissions/actions.d.ts +14 -2
  31. package/dist/server/src/services/permissions/actions.d.ts.map +1 -1
  32. package/dist/server/src/services/permissions.d.ts +14 -2
  33. package/dist/server/src/services/permissions.d.ts.map +1 -1
  34. package/dist/server/src/services/sanitize/index.d.ts +11 -0
  35. package/dist/server/src/services/sanitize/index.d.ts.map +1 -0
  36. package/dist/server/src/utils/index.d.ts +2 -2
  37. package/dist/server/src/utils/index.d.ts.map +1 -1
  38. package/package.json +12 -12
  39. package/dist/_chunks/SettingsPage-CsGvujny.mjs.map +0 -1
  40. package/dist/_chunks/SettingsPage-DA9haizZ.js.map +0 -1
  41. package/dist/_chunks/en-BsOU9o5z.js.map +0 -1
  42. package/dist/_chunks/en-CM6Pjfyv.mjs.map +0 -1
  43. package/dist/_chunks/index-CCZJF_EJ.mjs.map +0 -1
  44. package/dist/_chunks/index-DIzVXZoE.js.map +0 -1
  45. package/dist/server/src/migrations/content-type/disable/index.d.ts +0 -3
  46. package/dist/server/src/migrations/content-type/disable/index.d.ts.map +0 -1
  47. package/dist/server/src/migrations/content-type/enable/index.d.ts +0 -3
  48. package/dist/server/src/migrations/content-type/enable/index.d.ts.map +0 -1
  49. package/dist/server/src/services/entity-service-decorator.d.ts +0 -29
  50. package/dist/server/src/services/entity-service-decorator.d.ts.map +0 -1
  51. 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
  );
@@ -339,13 +459,12 @@ const BulkLocaleActionModal = ({
339
459
  paddingRight: "6px",
340
460
  paddingTop: "2px",
341
461
  paddingBottom: "2px",
342
- showBullet: false,
343
462
  size: "S",
344
463
  variant: statusVariant,
345
464
  children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
346
465
  }
347
466
  ) }) }),
348
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status }) }),
467
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status, action }) }),
349
468
  /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
350
469
  IconButton,
351
470
  {
@@ -362,7 +481,7 @@ const BulkLocaleActionModal = ({
362
481
  name: locale
363
482
  }
364
483
  ),
365
- borderWidth: 0,
484
+ variant: "ghost",
366
485
  children: /* @__PURE__ */ jsx(Pencil, {})
367
486
  }
368
487
  ) })
@@ -371,6 +490,47 @@ const BulkLocaleActionModal = ({
371
490
  ] }) })
372
491
  ] });
373
492
  };
493
+ const statusVariants = {
494
+ draft: "secondary",
495
+ published: "success",
496
+ modified: "alternative"
497
+ };
498
+ const LocaleOption = ({
499
+ isDraftAndPublishEnabled,
500
+ locale,
501
+ status,
502
+ entryExists
503
+ }) => {
504
+ const { formatMessage } = useIntl();
505
+ if (!entryExists) {
506
+ return formatMessage(
507
+ {
508
+ id: getTranslation("CMEditViewLocalePicker.locale.create"),
509
+ defaultMessage: "Create <bold>{locale}</bold> locale"
510
+ },
511
+ {
512
+ bold: (locale2) => /* @__PURE__ */ jsx("b", { children: locale2 }),
513
+ locale: locale.name
514
+ }
515
+ );
516
+ }
517
+ return /* @__PURE__ */ jsxs(Flex, { width: "100%", gap: 1, justifyContent: "space-between", children: [
518
+ /* @__PURE__ */ jsx(Typography, { children: locale.name }),
519
+ isDraftAndPublishEnabled ? /* @__PURE__ */ jsx(
520
+ Status,
521
+ {
522
+ display: "flex",
523
+ paddingLeft: "6px",
524
+ paddingRight: "6px",
525
+ paddingTop: "2px",
526
+ paddingBottom: "2px",
527
+ size: "S",
528
+ variant: statusVariants[status],
529
+ children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
530
+ }
531
+ ) : null
532
+ ] });
533
+ };
374
534
  const LocalePickerAction = ({
375
535
  document,
376
536
  meta,
@@ -382,7 +542,13 @@ const LocalePickerAction = ({
382
542
  const [{ query }, setQuery] = useQueryParams();
383
543
  const { hasI18n, canCreate, canRead } = useI18n();
384
544
  const { data: locales = [] } = useGetLocalesQuery();
385
- const { schema } = unstable_useDocument({ model, collectionType, documentId });
545
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
546
+ const { schema } = unstable_useDocument({
547
+ model,
548
+ collectionType,
549
+ documentId,
550
+ params: { locale: currentDesiredLocale }
551
+ });
386
552
  const handleSelect = React.useCallback(
387
553
  (value) => {
388
554
  setQuery({
@@ -400,53 +566,50 @@ const LocalePickerAction = ({
400
566
  if (!Array.isArray(locales) || !hasI18n) {
401
567
  return;
402
568
  }
403
- const currentDesiredLocale = query.plugins?.i18n?.locale;
404
569
  const doesLocaleExist = locales.find((loc) => loc.code === currentDesiredLocale);
405
570
  const defaultLocale = locales.find((locale) => locale.isDefault);
406
571
  if (!doesLocaleExist && defaultLocale?.code) {
407
572
  handleSelect(defaultLocale.code);
408
573
  }
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;
574
+ }, [handleSelect, hasI18n, locales, currentDesiredLocale]);
575
+ const currentLocale = Array.isArray(locales) ? locales.find((locale) => locale.code === currentDesiredLocale) : void 0;
414
576
  const allCurrentLocales = [
415
- { status: getDocumentStatus(document, meta), locale: currentLocale },
577
+ { status: getDocumentStatus(document, meta), locale: currentLocale?.code },
416
578
  ...meta?.availableLocales ?? []
417
579
  ];
580
+ if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
581
+ return null;
582
+ }
583
+ const displayedLocales = locales.filter((locale) => {
584
+ return canRead.includes(locale.code);
585
+ });
418
586
  return {
419
587
  label: formatMessage({
420
588
  id: getTranslation("Settings.locales.modal.locales.label"),
421
589
  defaultMessage: "Locales"
422
590
  }),
423
- options: locales.map((locale) => {
591
+ options: displayedLocales.map((locale) => {
592
+ const entryWithLocaleExists = allCurrentLocales.some((doc) => doc.locale === locale.code);
424
593
  const currentLocaleDoc = allCurrentLocales.find(
425
594
  (doc) => "locale" in doc ? doc.locale === locale.code : false
426
595
  );
427
- const status = currentLocaleDoc?.status ?? "draft";
428
- const permissionsToCheck = currentLocaleDoc ? canCreate : canRead;
429
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
596
+ const permissionsToCheck = currentLocaleDoc ? canRead : canCreate;
430
597
  return {
431
598
  disabled: !permissionsToCheck.includes(locale.code),
432
599
  value: locale.code,
433
- label: locale.name,
434
- startIcon: schema?.options?.draftAndPublish ? /* @__PURE__ */ jsx(
435
- Status,
600
+ label: /* @__PURE__ */ jsx(
601
+ LocaleOption,
436
602
  {
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) })
603
+ isDraftAndPublishEnabled: !!schema?.options?.draftAndPublish,
604
+ locale,
605
+ status: currentLocaleDoc?.status,
606
+ entryExists: entryWithLocaleExists
446
607
  }
447
- ) : null
608
+ ),
609
+ startIcon: !entryWithLocaleExists ? /* @__PURE__ */ jsx(Plus, {}) : null
448
610
  };
449
611
  }),
612
+ customizeContent: () => currentLocale?.name,
450
613
  onSelect: handleSelect,
451
614
  value: currentLocale
452
615
  };
@@ -462,6 +625,99 @@ const getDocumentStatus = (document, meta) => {
462
625
  }
463
626
  return docStatus;
464
627
  };
628
+ const FillFromAnotherLocaleAction = ({
629
+ documentId,
630
+ meta,
631
+ model,
632
+ collectionType
633
+ }) => {
634
+ const { formatMessage } = useIntl();
635
+ const [{ query }] = useQueryParams();
636
+ const { hasI18n } = useI18n();
637
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
638
+ const [localeSelected, setLocaleSelected] = React.useState(null);
639
+ const setValues = useForm("FillFromAnotherLocale", (state) => state.setValues);
640
+ const { getDocument } = unstable_useDocumentActions();
641
+ const { schema, components } = unstable_useDocument({
642
+ model,
643
+ documentId,
644
+ collectionType,
645
+ params: { locale: currentDesiredLocale }
646
+ });
647
+ const { data: locales = [] } = useGetLocalesQuery();
648
+ const availableLocales = Array.isArray(locales) ? locales.filter((locale) => meta?.availableLocales.some((l) => l.locale === locale.code)) : [];
649
+ const fillFromLocale = (onClose) => async () => {
650
+ const response = await getDocument({
651
+ collectionType,
652
+ model,
653
+ documentId,
654
+ params: { locale: localeSelected }
655
+ });
656
+ if (!response || !schema) {
657
+ return;
658
+ }
659
+ const { data } = response;
660
+ const cleanedData = cleanData(data, schema, components);
661
+ setValues(cleanedData);
662
+ onClose();
663
+ };
664
+ if (!hasI18n) {
665
+ return null;
666
+ }
667
+ return {
668
+ type: "icon",
669
+ icon: /* @__PURE__ */ jsx(Download, {}),
670
+ disabled: availableLocales.length === 0,
671
+ label: formatMessage({
672
+ id: getTranslation("CMEditViewCopyLocale.copy-text"),
673
+ defaultMessage: "Fill in from another locale"
674
+ }),
675
+ dialog: {
676
+ type: "dialog",
677
+ title: formatMessage({
678
+ id: getTranslation("CMEditViewCopyLocale.dialog.title"),
679
+ defaultMessage: "Confirmation"
680
+ }),
681
+ content: ({ onClose }) => /* @__PURE__ */ jsxs(Fragment, { children: [
682
+ /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
683
+ /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
684
+ /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
685
+ id: getTranslation("CMEditViewCopyLocale.dialog.body"),
686
+ defaultMessage: "Your current content will be erased and filled by the content of the selected locale:"
687
+ }) }),
688
+ /* @__PURE__ */ jsxs(Field.Root, { width: "100%", children: [
689
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
690
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.label"),
691
+ defaultMessage: "Locale"
692
+ }) }),
693
+ /* @__PURE__ */ jsx(
694
+ SingleSelect,
695
+ {
696
+ value: localeSelected,
697
+ placeholder: formatMessage({
698
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.placeholder"),
699
+ defaultMessage: "Select one locale..."
700
+ }),
701
+ onChange: (value) => setLocaleSelected(value),
702
+ children: availableLocales.map((locale) => /* @__PURE__ */ jsx(SingleSelectOption, { value: locale.code, children: locale.name }, locale.code))
703
+ }
704
+ )
705
+ ] })
706
+ ] }) }),
707
+ /* @__PURE__ */ jsx(Dialog.Footer, { children: /* @__PURE__ */ jsxs(Flex, { gap: 2, width: "100%", children: [
708
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "tertiary", onClick: onClose, children: formatMessage({
709
+ id: getTranslation("CMEditViewCopyLocale.cancel-text"),
710
+ defaultMessage: "No, cancel"
711
+ }) }),
712
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "success", onClick: fillFromLocale(onClose), children: formatMessage({
713
+ id: getTranslation("CMEditViewCopyLocale.submit-text"),
714
+ defaultMessage: "Yes, fill in"
715
+ }) })
716
+ ] }) })
717
+ ] })
718
+ }
719
+ };
720
+ };
465
721
  const DeleteLocaleAction = ({
466
722
  document,
467
723
  documentId,
@@ -473,16 +729,23 @@ const DeleteLocaleAction = ({
473
729
  const { toggleNotification } = useNotification();
474
730
  const { delete: deleteAction } = unstable_useDocumentActions();
475
731
  const { hasI18n, canDelete } = useI18n();
732
+ const [{ query }] = useQueryParams();
733
+ const { data: locales = [] } = useGetLocalesQuery();
734
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
735
+ const locale = !("error" in locales) && locales.find((loc) => loc.code === currentDesiredLocale);
476
736
  if (!hasI18n) {
477
737
  return null;
478
738
  }
479
739
  return {
480
740
  disabled: document?.locale && !canDelete.includes(document.locale) || !document || !document.id,
481
741
  position: ["header", "table-row"],
482
- label: formatMessage({
483
- id: getTranslation("actions.delete.label"),
484
- defaultMessage: "Delete locale"
485
- }),
742
+ label: formatMessage(
743
+ {
744
+ id: getTranslation("actions.delete.label"),
745
+ defaultMessage: "Delete entry ({locale})"
746
+ },
747
+ { locale: locale && locale.name }
748
+ ),
486
749
  icon: /* @__PURE__ */ jsx(StyledTrash, {}),
487
750
  variant: "danger",
488
751
  dialog: {
@@ -499,7 +762,12 @@ const DeleteLocaleAction = ({
499
762
  }) })
500
763
  ] }),
501
764
  onConfirm: async () => {
502
- if (!documentId || !document?.locale) {
765
+ const unableToDelete = (
766
+ // We are unable to delete a collection type without a document ID
767
+ // & unable to delete generally if there is no document locale
768
+ collectionType !== "single-types" && !documentId || !document?.locale
769
+ );
770
+ if (unableToDelete) {
503
771
  console.error(
504
772
  "You're trying to delete a document without an id or locale, this is likely a bug with Strapi. Please open an issue."
505
773
  );
@@ -525,23 +793,24 @@ const DeleteLocaleAction = ({
525
793
  }
526
794
  };
527
795
  };
528
- const BulkLocalePublishAction = ({
796
+ const BulkLocaleAction = ({
529
797
  document: baseDocument,
530
798
  documentId,
531
799
  model,
532
- collectionType
800
+ collectionType,
801
+ action
533
802
  }) => {
534
803
  const baseLocale = baseDocument?.locale ?? null;
535
804
  const [{ query }] = useQueryParams();
536
805
  const params = React.useMemo(() => buildValidParams(query), [query]);
537
- const isPublishedTab = query.status === "published";
806
+ const isOnPublishedTab = query.status === "published";
538
807
  const { formatMessage } = useIntl();
539
808
  const { hasI18n, canPublish } = useI18n();
540
809
  const { toggleNotification } = useNotification();
541
810
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
542
811
  const [selectedRows, setSelectedRows] = React.useState([]);
543
812
  const [isDraftRelationConfirmationOpen, setIsDraftRelationConfirmationOpen] = React.useState(false);
544
- const { publishMany: publishManyAction } = unstable_useDocumentActions();
813
+ const { publishMany: publishManyAction, unpublishMany: unpublishManyAction } = unstable_useDocumentActions();
545
814
  const {
546
815
  document,
547
816
  meta: documentMeta,
@@ -557,7 +826,7 @@ const BulkLocalePublishAction = ({
557
826
  }
558
827
  },
559
828
  {
560
- skip: !hasI18n
829
+ skip: !hasI18n || !baseLocale
561
830
  }
562
831
  );
563
832
  const { data: localesMetadata = [] } = useGetLocalesQuery(hasI18n ? void 0 : skipToken);
@@ -609,12 +878,19 @@ const BulkLocalePublishAction = ({
609
878
  }, {});
610
879
  return [rowsFromMeta, errors];
611
880
  }, [document, documentMeta?.availableLocales, validate]);
612
- const localesToPublish = selectedRows.reduce((acc, selectedRow) => {
613
- if (selectedRow.status !== "published" && !Object.keys(validationErrors).includes(selectedRow.locale)) {
881
+ const isBulkPublish = action === "bulk-publish";
882
+ const localesForAction = selectedRows.reduce((acc, selectedRow) => {
883
+ const isValidLocale = (
884
+ // Validation errors are irrelevant if we are trying to unpublish
885
+ !isBulkPublish || !Object.keys(validationErrors).includes(selectedRow.locale)
886
+ );
887
+ const shouldAddLocale = isBulkPublish ? selectedRow.status !== "published" && isValidLocale : selectedRow.status !== "draft" && isValidLocale;
888
+ if (shouldAddLocale) {
614
889
  acc.push(selectedRow.locale);
615
890
  }
616
891
  return acc;
617
892
  }, []);
893
+ const enableDraftRelationsCount = false;
618
894
  const {
619
895
  data: draftRelationsCount = 0,
620
896
  isLoading: isDraftRelationsLoading,
@@ -623,10 +899,10 @@ const BulkLocalePublishAction = ({
623
899
  {
624
900
  model,
625
901
  documentIds: [documentId],
626
- locale: localesToPublish
902
+ locale: localesForAction
627
903
  },
628
904
  {
629
- skip: !documentId || localesToPublish.length === 0
905
+ skip: !enableDraftRelationsCount
630
906
  }
631
907
  );
632
908
  React.useEffect(() => {
@@ -652,7 +928,18 @@ const BulkLocalePublishAction = ({
652
928
  documentIds: [documentId],
653
929
  params: {
654
930
  ...params,
655
- locale: localesToPublish
931
+ locale: localesForAction
932
+ }
933
+ });
934
+ setSelectedRows([]);
935
+ };
936
+ const unpublish = async () => {
937
+ await unpublishManyAction({
938
+ model,
939
+ documentIds: [documentId],
940
+ params: {
941
+ ...params,
942
+ locale: localesForAction
656
943
  }
657
944
  });
658
945
  setSelectedRows([]);
@@ -660,14 +947,12 @@ const BulkLocalePublishAction = ({
660
947
  const handleAction = async () => {
661
948
  if (draftRelationsCount > 0) {
662
949
  setIsDraftRelationConfirmationOpen(true);
663
- } else {
950
+ } else if (isBulkPublish) {
664
951
  await publish();
952
+ } else {
953
+ await unpublish();
665
954
  }
666
955
  };
667
- const isUnpublish = document?.status === "published";
668
- if (isUnpublish) {
669
- console.warn(["I18N"], "Bulk locale unpublish modal not implemented");
670
- }
671
956
  if (isDraftRelationConfirmationOpen) {
672
957
  return {
673
958
  label: formatMessage({
@@ -705,18 +990,18 @@ const BulkLocalePublishAction = ({
705
990
  const hasPermission = selectedRows.map(({ locale }) => locale).every((locale) => canPublish.includes(locale));
706
991
  return {
707
992
  label: formatMessage({
708
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
709
- defaultMessage: "Publish Multiple Locales"
993
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
994
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
710
995
  }),
711
- icon: /* @__PURE__ */ jsx(ListPlus, {}),
712
- disabled: isPublishedTab || canPublish.length === 0,
996
+ variant: isBulkPublish ? "secondary" : "danger",
997
+ icon: isBulkPublish ? /* @__PURE__ */ jsx(ListPlus, {}) : /* @__PURE__ */ jsx(Cross, {}),
998
+ disabled: isOnPublishedTab || canPublish.length === 0,
713
999
  position: ["panel"],
714
- variant: "secondary",
715
1000
  dialog: {
716
1001
  type: "modal",
717
1002
  title: formatMessage({
718
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
719
- defaultMessage: "Publish Multiple Locales"
1003
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
1004
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
720
1005
  }),
721
1006
  content: () => {
722
1007
  return /* @__PURE__ */ jsx(
@@ -735,28 +1020,35 @@ const BulkLocalePublishAction = ({
735
1020
  validationErrors,
736
1021
  headers,
737
1022
  rows,
738
- localesMetadata
1023
+ localesMetadata,
1024
+ action: action ?? "bulk-publish"
739
1025
  }
740
1026
  )
741
1027
  }
742
1028
  );
743
1029
  },
744
1030
  footer: () => /* @__PURE__ */ jsx(Modal.Footer, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
745
- Button$1,
1031
+ Button,
746
1032
  {
747
1033
  loading: isDraftRelationsLoading,
748
- disabled: !hasPermission || localesToPublish.length === 0,
1034
+ disabled: !hasPermission || localesForAction.length === 0,
749
1035
  variant: "default",
750
1036
  onClick: handleAction,
751
1037
  children: formatMessage({
752
- id: "app.utils.publish",
753
- defaultMessage: "Publish"
1038
+ id: isBulkPublish ? "app.utils.publish" : "app.utils.unpublish",
1039
+ defaultMessage: isBulkPublish ? "Publish" : "Unpublish"
754
1040
  })
755
1041
  }
756
1042
  ) })
757
1043
  }
758
1044
  };
759
1045
  };
1046
+ const BulkLocalePublishAction = (props) => {
1047
+ return BulkLocaleAction({ action: "bulk-publish", ...props });
1048
+ };
1049
+ const BulkLocaleUnpublishAction = (props) => {
1050
+ return BulkLocaleAction({ action: "bulk-unpublish", ...props });
1051
+ };
760
1052
  const StyledTrash = styled(Trash)`
761
1053
  path {
762
1054
  fill: currentColor;
@@ -976,54 +1268,13 @@ const LocaleListCell = ({
976
1268
  return locale.name;
977
1269
  }).toSorted((a, b) => formatter.compare(a, b));
978
1270
  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
- ) }) }),
1271
+ /* @__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: [
1272
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", ellipsis: true, marginRight: 2, children: localesForDocument.join(", ") }),
1273
+ /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(CaretDown, { width: "1.2rem", height: "1.2rem" }) })
1274
+ ] }) }) }),
993
1275
  /* @__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
1276
  ] });
995
1277
  };
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
1278
  const addColumnToTableHook = ({ displayedHeaders, layout }) => {
1028
1279
  const { options } = layout;
1029
1280
  const isFieldLocalized = doesPluginOptionsHaveI18nLocalized(options) ? options.i18n.localized : false;
@@ -1149,9 +1400,6 @@ const localeMiddleware = (ctx) => (next) => (permissions) => {
1149
1400
  return next(revisedPermissions);
1150
1401
  };
1151
1402
  const prefixPluginTranslations = (trad, pluginId2) => {
1152
- if (!pluginId2) {
1153
- throw new TypeError("pluginId can't be empty");
1154
- }
1155
1403
  return Object.keys(trad).reduce((acc, current) => {
1156
1404
  acc[`${pluginId2}.${current}`] = trad[current];
1157
1405
  return acc;
@@ -1221,11 +1469,11 @@ const index = {
1221
1469
  },
1222
1470
  id: "internationalization",
1223
1471
  to: "internationalization",
1224
- Component: () => import("./SettingsPage-CsGvujny.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1472
+ Component: () => import("./SettingsPage-giNp3Ftr.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1225
1473
  permissions: PERMISSIONS.accessMain
1226
1474
  });
1227
1475
  const contentManager = app.getPlugin("content-manager");
1228
- contentManager.apis.addDocumentHeaderAction([LocalePickerAction]);
1476
+ contentManager.apis.addDocumentHeaderAction([LocalePickerAction, FillFromAnotherLocaleAction]);
1229
1477
  contentManager.apis.addDocumentAction((actions) => {
1230
1478
  const indexOfDeleteAction = actions.findIndex((action) => action.type === "delete");
1231
1479
  actions.splice(indexOfDeleteAction, 0, DeleteLocaleAction);
@@ -1233,6 +1481,7 @@ const index = {
1233
1481
  });
1234
1482
  contentManager.apis.addDocumentAction((actions) => {
1235
1483
  actions.splice(2, 0, BulkLocalePublishAction);
1484
+ actions.splice(5, 0, BulkLocaleUnpublishAction);
1236
1485
  return actions;
1237
1486
  });
1238
1487
  contentManager.injectComponent("listView", "actions", {
@@ -1338,7 +1587,7 @@ const index = {
1338
1587
  async registerTrads({ locales }) {
1339
1588
  const importedTrads = await Promise.all(
1340
1589
  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 }) => {
1590
+ 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-DlXfy6Gy.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
1591
  return {
1343
1592
  data: prefixPluginTranslations(data, pluginId),
1344
1593
  locale
@@ -1364,4 +1613,4 @@ export {
1364
1613
  index as i,
1365
1614
  useCreateLocaleMutation as u
1366
1615
  };
1367
- //# sourceMappingURL=index-CCZJF_EJ.mjs.map
1616
+ //# sourceMappingURL=index-Bt5ork2g.mjs.map