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

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-D0hqaut-.js} +8 -8
  2. package/dist/_chunks/SettingsPage-D0hqaut-.js.map +1 -0
  3. package/dist/_chunks/{SettingsPage-CsGvujny.mjs → SettingsPage-D67CaQAB.mjs} +8 -8
  4. package/dist/_chunks/SettingsPage-D67CaQAB.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-DIzVXZoE.js → index-46DNtLCn.js} +387 -141
  10. package/dist/_chunks/index-46DNtLCn.js.map +1 -0
  11. package/dist/_chunks/{index-CCZJF_EJ.mjs → index-CNR8i3KM.mjs} +394 -148
  12. package/dist/_chunks/index-CNR8i3KM.mjs.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,47 @@ 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
+ }
418
583
  return {
419
584
  label: formatMessage({
420
585
  id: getTranslation("Settings.locales.modal.locales.label"),
421
586
  defaultMessage: "Locales"
422
587
  }),
423
588
  options: locales.map((locale) => {
589
+ const entryWithLocaleExists = allCurrentLocales.some((doc) => doc.locale === locale.code);
424
590
  const currentLocaleDoc = allCurrentLocales.find(
425
591
  (doc) => "locale" in doc ? doc.locale === locale.code : false
426
592
  );
427
- const status = currentLocaleDoc?.status ?? "draft";
428
- const permissionsToCheck = currentLocaleDoc ? canCreate : canRead;
429
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
593
+ const permissionsToCheck = currentLocaleDoc ? canRead : canCreate;
430
594
  return {
431
595
  disabled: !permissionsToCheck.includes(locale.code),
432
596
  value: locale.code,
433
- label: locale.name,
434
- startIcon: schema?.options?.draftAndPublish ? /* @__PURE__ */ jsx(
435
- Status,
597
+ label: /* @__PURE__ */ jsx(
598
+ LocaleOption,
436
599
  {
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) })
600
+ isDraftAndPublishEnabled: !!schema?.options?.draftAndPublish,
601
+ locale,
602
+ status: currentLocaleDoc?.status,
603
+ entryExists: entryWithLocaleExists
446
604
  }
447
- ) : null
605
+ ),
606
+ startIcon: !entryWithLocaleExists ? /* @__PURE__ */ jsx(Plus, {}) : null
448
607
  };
449
608
  }),
609
+ customizeContent: () => currentLocale?.name,
450
610
  onSelect: handleSelect,
451
611
  value: currentLocale
452
612
  };
@@ -462,6 +622,99 @@ const getDocumentStatus = (document, meta) => {
462
622
  }
463
623
  return docStatus;
464
624
  };
625
+ const FillFromAnotherLocaleAction = ({
626
+ documentId,
627
+ meta,
628
+ model,
629
+ collectionType
630
+ }) => {
631
+ const { formatMessage } = useIntl();
632
+ const [{ query }] = useQueryParams();
633
+ const { hasI18n } = useI18n();
634
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
635
+ const [localeSelected, setLocaleSelected] = React.useState(null);
636
+ const setValues = useForm("FillFromAnotherLocale", (state) => state.setValues);
637
+ const { getDocument } = unstable_useDocumentActions();
638
+ const { schema, components } = unstable_useDocument({
639
+ model,
640
+ documentId,
641
+ collectionType,
642
+ params: { locale: currentDesiredLocale }
643
+ });
644
+ const { data: locales = [] } = useGetLocalesQuery();
645
+ const availableLocales = Array.isArray(locales) ? locales.filter((locale) => meta?.availableLocales.some((l) => l.locale === locale.code)) : [];
646
+ const fillFromLocale = (onClose) => async () => {
647
+ const response = await getDocument({
648
+ collectionType,
649
+ model,
650
+ documentId,
651
+ params: { locale: localeSelected }
652
+ });
653
+ if (!response || !schema) {
654
+ return;
655
+ }
656
+ const { data } = response;
657
+ const cleanedData = cleanData(data, schema, components);
658
+ setValues(cleanedData);
659
+ onClose();
660
+ };
661
+ if (!hasI18n) {
662
+ return null;
663
+ }
664
+ return {
665
+ type: "icon",
666
+ icon: /* @__PURE__ */ jsx(Download, {}),
667
+ disabled: availableLocales.length === 0,
668
+ label: formatMessage({
669
+ id: getTranslation("CMEditViewCopyLocale.copy-text"),
670
+ defaultMessage: "Fill in from another locale"
671
+ }),
672
+ dialog: {
673
+ type: "dialog",
674
+ title: formatMessage({
675
+ id: getTranslation("CMEditViewCopyLocale.dialog.title"),
676
+ defaultMessage: "Confirmation"
677
+ }),
678
+ content: ({ onClose }) => /* @__PURE__ */ jsxs(Fragment, { children: [
679
+ /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
680
+ /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
681
+ /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
682
+ id: getTranslation("CMEditViewCopyLocale.dialog.body"),
683
+ defaultMessage: "Your current content will be erased and filled by the content of the selected locale:"
684
+ }) }),
685
+ /* @__PURE__ */ jsxs(Field.Root, { width: "100%", children: [
686
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
687
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.label"),
688
+ defaultMessage: "Locale"
689
+ }) }),
690
+ /* @__PURE__ */ jsx(
691
+ SingleSelect,
692
+ {
693
+ value: localeSelected,
694
+ placeholder: formatMessage({
695
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.placeholder"),
696
+ defaultMessage: "Select one locale..."
697
+ }),
698
+ onChange: (value) => setLocaleSelected(value),
699
+ children: availableLocales.map((locale) => /* @__PURE__ */ jsx(SingleSelectOption, { value: locale.code, children: locale.name }, locale.code))
700
+ }
701
+ )
702
+ ] })
703
+ ] }) }),
704
+ /* @__PURE__ */ jsx(Dialog.Footer, { children: /* @__PURE__ */ jsxs(Flex, { gap: 2, width: "100%", children: [
705
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "tertiary", onClick: onClose, children: formatMessage({
706
+ id: getTranslation("CMEditViewCopyLocale.cancel-text"),
707
+ defaultMessage: "No, cancel"
708
+ }) }),
709
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "success", onClick: fillFromLocale(onClose), children: formatMessage({
710
+ id: getTranslation("CMEditViewCopyLocale.submit-text"),
711
+ defaultMessage: "Yes, fill in"
712
+ }) })
713
+ ] }) })
714
+ ] })
715
+ }
716
+ };
717
+ };
465
718
  const DeleteLocaleAction = ({
466
719
  document,
467
720
  documentId,
@@ -473,16 +726,23 @@ const DeleteLocaleAction = ({
473
726
  const { toggleNotification } = useNotification();
474
727
  const { delete: deleteAction } = unstable_useDocumentActions();
475
728
  const { hasI18n, canDelete } = useI18n();
729
+ const [{ query }] = useQueryParams();
730
+ const { data: locales = [] } = useGetLocalesQuery();
731
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
732
+ const locale = !("error" in locales) && locales.find((loc) => loc.code === currentDesiredLocale);
476
733
  if (!hasI18n) {
477
734
  return null;
478
735
  }
479
736
  return {
480
737
  disabled: document?.locale && !canDelete.includes(document.locale) || !document || !document.id,
481
738
  position: ["header", "table-row"],
482
- label: formatMessage({
483
- id: getTranslation("actions.delete.label"),
484
- defaultMessage: "Delete locale"
485
- }),
739
+ label: formatMessage(
740
+ {
741
+ id: getTranslation("actions.delete.label"),
742
+ defaultMessage: "Delete entry ({locale})"
743
+ },
744
+ { locale: locale && locale.name }
745
+ ),
486
746
  icon: /* @__PURE__ */ jsx(StyledTrash, {}),
487
747
  variant: "danger",
488
748
  dialog: {
@@ -499,7 +759,12 @@ const DeleteLocaleAction = ({
499
759
  }) })
500
760
  ] }),
501
761
  onConfirm: async () => {
502
- if (!documentId || !document?.locale) {
762
+ const unableToDelete = (
763
+ // We are unable to delete a collection type without a document ID
764
+ // & unable to delete generally if there is no document locale
765
+ collectionType !== "single-types" && !documentId || !document?.locale
766
+ );
767
+ if (unableToDelete) {
503
768
  console.error(
504
769
  "You're trying to delete a document without an id or locale, this is likely a bug with Strapi. Please open an issue."
505
770
  );
@@ -525,23 +790,24 @@ const DeleteLocaleAction = ({
525
790
  }
526
791
  };
527
792
  };
528
- const BulkLocalePublishAction = ({
793
+ const BulkLocaleAction = ({
529
794
  document: baseDocument,
530
795
  documentId,
531
796
  model,
532
- collectionType
797
+ collectionType,
798
+ action
533
799
  }) => {
534
800
  const baseLocale = baseDocument?.locale ?? null;
535
801
  const [{ query }] = useQueryParams();
536
802
  const params = React.useMemo(() => buildValidParams(query), [query]);
537
- const isPublishedTab = query.status === "published";
803
+ const isOnPublishedTab = query.status === "published";
538
804
  const { formatMessage } = useIntl();
539
805
  const { hasI18n, canPublish } = useI18n();
540
806
  const { toggleNotification } = useNotification();
541
807
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
542
808
  const [selectedRows, setSelectedRows] = React.useState([]);
543
809
  const [isDraftRelationConfirmationOpen, setIsDraftRelationConfirmationOpen] = React.useState(false);
544
- const { publishMany: publishManyAction } = unstable_useDocumentActions();
810
+ const { publishMany: publishManyAction, unpublishMany: unpublishManyAction } = unstable_useDocumentActions();
545
811
  const {
546
812
  document,
547
813
  meta: documentMeta,
@@ -557,7 +823,7 @@ const BulkLocalePublishAction = ({
557
823
  }
558
824
  },
559
825
  {
560
- skip: !hasI18n
826
+ skip: !hasI18n || !baseLocale
561
827
  }
562
828
  );
563
829
  const { data: localesMetadata = [] } = useGetLocalesQuery(hasI18n ? void 0 : skipToken);
@@ -609,12 +875,19 @@ const BulkLocalePublishAction = ({
609
875
  }, {});
610
876
  return [rowsFromMeta, errors];
611
877
  }, [document, documentMeta?.availableLocales, validate]);
612
- const localesToPublish = selectedRows.reduce((acc, selectedRow) => {
613
- if (selectedRow.status !== "published" && !Object.keys(validationErrors).includes(selectedRow.locale)) {
878
+ const isBulkPublish = action === "bulk-publish";
879
+ const localesForAction = selectedRows.reduce((acc, selectedRow) => {
880
+ const isValidLocale = (
881
+ // Validation errors are irrelevant if we are trying to unpublish
882
+ !isBulkPublish || !Object.keys(validationErrors).includes(selectedRow.locale)
883
+ );
884
+ const shouldAddLocale = isBulkPublish ? selectedRow.status !== "published" && isValidLocale : selectedRow.status !== "draft" && isValidLocale;
885
+ if (shouldAddLocale) {
614
886
  acc.push(selectedRow.locale);
615
887
  }
616
888
  return acc;
617
889
  }, []);
890
+ const enableDraftRelationsCount = false;
618
891
  const {
619
892
  data: draftRelationsCount = 0,
620
893
  isLoading: isDraftRelationsLoading,
@@ -623,10 +896,10 @@ const BulkLocalePublishAction = ({
623
896
  {
624
897
  model,
625
898
  documentIds: [documentId],
626
- locale: localesToPublish
899
+ locale: localesForAction
627
900
  },
628
901
  {
629
- skip: !documentId || localesToPublish.length === 0
902
+ skip: !enableDraftRelationsCount
630
903
  }
631
904
  );
632
905
  React.useEffect(() => {
@@ -652,7 +925,18 @@ const BulkLocalePublishAction = ({
652
925
  documentIds: [documentId],
653
926
  params: {
654
927
  ...params,
655
- locale: localesToPublish
928
+ locale: localesForAction
929
+ }
930
+ });
931
+ setSelectedRows([]);
932
+ };
933
+ const unpublish = async () => {
934
+ await unpublishManyAction({
935
+ model,
936
+ documentIds: [documentId],
937
+ params: {
938
+ ...params,
939
+ locale: localesForAction
656
940
  }
657
941
  });
658
942
  setSelectedRows([]);
@@ -660,14 +944,12 @@ const BulkLocalePublishAction = ({
660
944
  const handleAction = async () => {
661
945
  if (draftRelationsCount > 0) {
662
946
  setIsDraftRelationConfirmationOpen(true);
663
- } else {
947
+ } else if (isBulkPublish) {
664
948
  await publish();
949
+ } else {
950
+ await unpublish();
665
951
  }
666
952
  };
667
- const isUnpublish = document?.status === "published";
668
- if (isUnpublish) {
669
- console.warn(["I18N"], "Bulk locale unpublish modal not implemented");
670
- }
671
953
  if (isDraftRelationConfirmationOpen) {
672
954
  return {
673
955
  label: formatMessage({
@@ -705,18 +987,18 @@ const BulkLocalePublishAction = ({
705
987
  const hasPermission = selectedRows.map(({ locale }) => locale).every((locale) => canPublish.includes(locale));
706
988
  return {
707
989
  label: formatMessage({
708
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
709
- defaultMessage: "Publish Multiple Locales"
990
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
991
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
710
992
  }),
711
- icon: /* @__PURE__ */ jsx(ListPlus, {}),
712
- disabled: isPublishedTab || canPublish.length === 0,
993
+ variant: isBulkPublish ? "secondary" : "danger",
994
+ icon: isBulkPublish ? /* @__PURE__ */ jsx(ListPlus, {}) : /* @__PURE__ */ jsx(Cross, {}),
995
+ disabled: isOnPublishedTab || canPublish.length === 0,
713
996
  position: ["panel"],
714
- variant: "secondary",
715
997
  dialog: {
716
998
  type: "modal",
717
999
  title: formatMessage({
718
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
719
- defaultMessage: "Publish Multiple Locales"
1000
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
1001
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
720
1002
  }),
721
1003
  content: () => {
722
1004
  return /* @__PURE__ */ jsx(
@@ -735,28 +1017,35 @@ const BulkLocalePublishAction = ({
735
1017
  validationErrors,
736
1018
  headers,
737
1019
  rows,
738
- localesMetadata
1020
+ localesMetadata,
1021
+ action: action ?? "bulk-publish"
739
1022
  }
740
1023
  )
741
1024
  }
742
1025
  );
743
1026
  },
744
1027
  footer: () => /* @__PURE__ */ jsx(Modal.Footer, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
745
- Button$1,
1028
+ Button,
746
1029
  {
747
1030
  loading: isDraftRelationsLoading,
748
- disabled: !hasPermission || localesToPublish.length === 0,
1031
+ disabled: !hasPermission || localesForAction.length === 0,
749
1032
  variant: "default",
750
1033
  onClick: handleAction,
751
1034
  children: formatMessage({
752
- id: "app.utils.publish",
753
- defaultMessage: "Publish"
1035
+ id: isBulkPublish ? "app.utils.publish" : "app.utils.unpublish",
1036
+ defaultMessage: isBulkPublish ? "Publish" : "Unpublish"
754
1037
  })
755
1038
  }
756
1039
  ) })
757
1040
  }
758
1041
  };
759
1042
  };
1043
+ const BulkLocalePublishAction = (props) => {
1044
+ return BulkLocaleAction({ action: "bulk-publish", ...props });
1045
+ };
1046
+ const BulkLocaleUnpublishAction = (props) => {
1047
+ return BulkLocaleAction({ action: "bulk-unpublish", ...props });
1048
+ };
760
1049
  const StyledTrash = styled(Trash)`
761
1050
  path {
762
1051
  fill: currentColor;
@@ -976,54 +1265,13 @@ const LocaleListCell = ({
976
1265
  return locale.name;
977
1266
  }).toSorted((a, b) => formatter.compare(a, b));
978
1267
  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
- ) }) }),
1268
+ /* @__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: [
1269
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", ellipsis: true, marginRight: 2, children: localesForDocument.join(", ") }),
1270
+ /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(CaretDown, { width: "1.2rem", height: "1.2rem" }) })
1271
+ ] }) }) }),
993
1272
  /* @__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
1273
  ] });
995
1274
  };
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
1275
  const addColumnToTableHook = ({ displayedHeaders, layout }) => {
1028
1276
  const { options } = layout;
1029
1277
  const isFieldLocalized = doesPluginOptionsHaveI18nLocalized(options) ? options.i18n.localized : false;
@@ -1149,9 +1397,6 @@ const localeMiddleware = (ctx) => (next) => (permissions) => {
1149
1397
  return next(revisedPermissions);
1150
1398
  };
1151
1399
  const prefixPluginTranslations = (trad, pluginId2) => {
1152
- if (!pluginId2) {
1153
- throw new TypeError("pluginId can't be empty");
1154
- }
1155
1400
  return Object.keys(trad).reduce((acc, current) => {
1156
1401
  acc[`${pluginId2}.${current}`] = trad[current];
1157
1402
  return acc;
@@ -1221,11 +1466,11 @@ const index = {
1221
1466
  },
1222
1467
  id: "internationalization",
1223
1468
  to: "internationalization",
1224
- Component: () => import("./SettingsPage-CsGvujny.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1469
+ Component: () => import("./SettingsPage-D67CaQAB.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1225
1470
  permissions: PERMISSIONS.accessMain
1226
1471
  });
1227
1472
  const contentManager = app.getPlugin("content-manager");
1228
- contentManager.apis.addDocumentHeaderAction([LocalePickerAction]);
1473
+ contentManager.apis.addDocumentHeaderAction([LocalePickerAction, FillFromAnotherLocaleAction]);
1229
1474
  contentManager.apis.addDocumentAction((actions) => {
1230
1475
  const indexOfDeleteAction = actions.findIndex((action) => action.type === "delete");
1231
1476
  actions.splice(indexOfDeleteAction, 0, DeleteLocaleAction);
@@ -1233,6 +1478,7 @@ const index = {
1233
1478
  });
1234
1479
  contentManager.apis.addDocumentAction((actions) => {
1235
1480
  actions.splice(2, 0, BulkLocalePublishAction);
1481
+ actions.splice(5, 0, BulkLocaleUnpublishAction);
1236
1482
  return actions;
1237
1483
  });
1238
1484
  contentManager.injectComponent("listView", "actions", {
@@ -1338,7 +1584,7 @@ const index = {
1338
1584
  async registerTrads({ locales }) {
1339
1585
  const importedTrads = await Promise.all(
1340
1586
  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 }) => {
1587
+ 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
1588
  return {
1343
1589
  data: prefixPluginTranslations(data, pluginId),
1344
1590
  locale
@@ -1364,4 +1610,4 @@ export {
1364
1610
  index as i,
1365
1611
  useCreateLocaleMutation as u
1366
1612
  };
1367
- //# sourceMappingURL=index-CCZJF_EJ.mjs.map
1613
+ //# sourceMappingURL=index-CNR8i3KM.mjs.map