@strapi/i18n 0.0.0-experimental.e14656d3b8681880212c13260b9a2b340c182f2d → 0.0.0-experimental.e350eaa6073e65190102b4b798c32c287053cc02

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/dist/_chunks/{SettingsPage-CbEn7zF5.mjs → SettingsPage-BAx9nmep.mjs} +4 -4
  2. package/dist/_chunks/SettingsPage-BAx9nmep.mjs.map +1 -0
  3. package/dist/_chunks/{SettingsPage-C0l7c5fn.js → SettingsPage-BTgjb2KS.js} +5 -6
  4. package/dist/_chunks/SettingsPage-BTgjb2KS.js.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-C0i3v7QQ.js → index-3yyF237r.js} +400 -145
  10. package/dist/_chunks/index-3yyF237r.js.map +1 -0
  11. package/dist/_chunks/{index-CkrCiZyq.mjs → index-B0NijiBB.mjs} +406 -150
  12. package/dist/_chunks/index-B0NijiBB.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 +398 -487
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +399 -487
  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 +7 -11
  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 +6 -8
  29. package/dist/server/src/services/index.d.ts.map +1 -1
  30. package/dist/server/src/services/sanitize/index.d.ts +11 -0
  31. package/dist/server/src/services/sanitize/index.d.ts.map +1 -0
  32. package/dist/server/src/utils/index.d.ts +2 -2
  33. package/dist/server/src/utils/index.d.ts.map +1 -1
  34. package/package.json +13 -13
  35. package/dist/_chunks/SettingsPage-C0l7c5fn.js.map +0 -1
  36. package/dist/_chunks/SettingsPage-CbEn7zF5.mjs.map +0 -1
  37. package/dist/_chunks/en-BsOU9o5z.js.map +0 -1
  38. package/dist/_chunks/en-CM6Pjfyv.mjs.map +0 -1
  39. package/dist/_chunks/index-C0i3v7QQ.js.map +0 -1
  40. package/dist/_chunks/index-CkrCiZyq.mjs.map +0 -1
  41. package/dist/server/src/migrations/content-type/disable/index.d.ts +0 -3
  42. package/dist/server/src/migrations/content-type/disable/index.d.ts.map +0 -1
  43. package/dist/server/src/migrations/content-type/enable/index.d.ts +0 -3
  44. package/dist/server/src/migrations/content-type/enable/index.d.ts.map +0 -1
  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,25 +1,32 @@
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";
14
14
  import { stringify } from "qs";
15
15
  import omit from "lodash/omit";
16
- const __variableDynamicImportRuntimeHelper = (glob, path) => {
16
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
17
17
  const v = glob[path];
18
18
  if (v) {
19
19
  return typeof v === "function" ? v() : Promise.resolve(v);
20
20
  }
21
21
  return new Promise((_, reject) => {
22
- (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(reject.bind(null, new Error("Unknown variable dynamic import: " + path)));
22
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
23
+ reject.bind(
24
+ null,
25
+ new Error(
26
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
27
+ )
28
+ )
29
+ );
23
30
  });
24
31
  };
25
32
  const pluginId = "i18n";
@@ -78,11 +85,11 @@ const CheckboxConfirmation = ({
78
85
  }) }) })
79
86
  ] }) }),
80
87
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
81
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button$1, { variant: "tertiary", children: formatMessage({
88
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
82
89
  id: "components.popUpWarning.button.cancel",
83
90
  defaultMessage: "No, cancel"
84
91
  }) }) }),
85
- /* @__PURE__ */ jsx(Dialog.Action, { children: /* @__PURE__ */ jsx(Button$1, { variant: "danger-light", onClick: handleConfirm, children: formatMessage({
92
+ /* @__PURE__ */ jsx(Dialog.Action, { children: /* @__PURE__ */ jsx(Button, { variant: "danger-light", onClick: handleConfirm, children: formatMessage({
86
93
  id: getTranslation("CheckboxConfirmation.Modal.button-confirm"),
87
94
  defaultMessage: "Yes, disable"
88
95
  }) }) })
@@ -217,10 +224,94 @@ const relationsApi = i18nApi.injectEndpoints({
217
224
  })
218
225
  });
219
226
  const { useGetManyDraftRelationCountQuery } = relationsApi;
227
+ const cleanData = (data, schema, components) => {
228
+ const cleanedData = removeFields(data, [
229
+ "createdAt",
230
+ "createdBy",
231
+ "updatedAt",
232
+ "updatedBy",
233
+ "id",
234
+ "documentId",
235
+ "publishedAt",
236
+ "strapi_stage",
237
+ "strapi_assignee",
238
+ "locale",
239
+ "status"
240
+ ]);
241
+ const cleanedDataWithoutPasswordAndRelation = recursiveRemoveFieldTypes(
242
+ cleanedData,
243
+ schema,
244
+ components,
245
+ ["relation", "password"]
246
+ );
247
+ return cleanedDataWithoutPasswordAndRelation;
248
+ };
249
+ const removeFields = (data, fields) => {
250
+ return Object.keys(data).reduce((acc, current) => {
251
+ if (fields.includes(current)) {
252
+ return acc;
253
+ }
254
+ acc[current] = data[current];
255
+ return acc;
256
+ }, {});
257
+ };
258
+ const recursiveRemoveFieldTypes = (data, schema, components, fields) => {
259
+ return Object.keys(data).reduce((acc, current) => {
260
+ const attribute = schema.attributes[current] ?? { type: void 0 };
261
+ if (fields.includes(attribute.type)) {
262
+ return acc;
263
+ }
264
+ if (attribute.type === "dynamiczone") {
265
+ acc[current] = data[current].map((componentValue, index2) => {
266
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(
267
+ componentValue,
268
+ components[componentValue.__component],
269
+ components,
270
+ fields
271
+ );
272
+ return {
273
+ ...rest,
274
+ __temp_key__: index2 + 1
275
+ };
276
+ });
277
+ } else if (attribute.type === "component") {
278
+ const { repeatable, component } = attribute;
279
+ if (repeatable) {
280
+ acc[current] = (data[current] ?? []).map((compoData, index2) => {
281
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(
282
+ compoData,
283
+ components[component],
284
+ components,
285
+ fields
286
+ );
287
+ return {
288
+ ...rest,
289
+ __temp_key__: index2 + 1
290
+ };
291
+ });
292
+ } else {
293
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(
294
+ data[current] ?? {},
295
+ components[component],
296
+ components,
297
+ fields
298
+ );
299
+ acc[current] = rest;
300
+ }
301
+ } else {
302
+ acc[current] = data[current];
303
+ }
304
+ return acc;
305
+ }, {});
306
+ };
220
307
  const isErrorMessageDescriptor = (object) => {
221
308
  return typeof object === "object" && object !== null && "id" in object && "defaultMessage" in object;
222
309
  };
223
- const EntryValidationText = ({ status = "draft", validationErrors }) => {
310
+ const EntryValidationText = ({
311
+ status = "draft",
312
+ validationErrors,
313
+ action
314
+ }) => {
224
315
  const { formatMessage } = useIntl();
225
316
  const getErrorStr = (key, value) => {
226
317
  if (typeof value === "string") {
@@ -254,30 +345,63 @@ const EntryValidationText = ({ status = "draft", validationErrors }) => {
254
345
  ) })
255
346
  ] });
256
347
  }
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
- }
348
+ const getStatusMessage = () => {
349
+ if (action === "bulk-publish") {
350
+ if (status === "published") {
351
+ return {
352
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
353
+ text: formatMessage({
354
+ id: "content-manager.bulk-publish.already-published",
355
+ defaultMessage: "Already Published"
356
+ }),
357
+ textColor: "success600",
358
+ fontWeight: "bold"
359
+ };
360
+ } else if (status === "modified") {
361
+ return {
362
+ icon: /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
363
+ text: formatMessage({
364
+ id: "app.utils.ready-to-publish-changes",
365
+ defaultMessage: "Ready to publish changes"
366
+ })
367
+ };
368
+ } else {
369
+ return {
370
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
371
+ text: formatMessage({
372
+ id: "app.utils.ready-to-publish",
373
+ defaultMessage: "Ready to publish"
374
+ })
375
+ };
376
+ }
377
+ } else {
378
+ if (status === "draft") {
379
+ return {
380
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
381
+ text: formatMessage({
382
+ id: "content-manager.bulk-unpublish.already-unpublished",
383
+ defaultMessage: "Already Unpublished"
384
+ }),
385
+ textColor: "success600",
386
+ fontWeight: "bold"
387
+ };
388
+ } else {
389
+ return {
390
+ icon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
391
+ text: formatMessage({
392
+ id: "app.utils.ready-to-unpublish-changes",
393
+ defaultMessage: "Ready to unpublish"
394
+ }),
395
+ textColor: "success600",
396
+ fontWeight: "bold"
397
+ };
398
+ }
399
+ }
400
+ };
401
+ const { icon, text, textColor = "success600", fontWeight = "normal" } = getStatusMessage();
275
402
  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
- }) })
403
+ icon,
404
+ /* @__PURE__ */ jsx(Typography, { textColor, fontWeight, children: text })
281
405
  ] });
282
406
  };
283
407
  const BoldChunk = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
@@ -285,7 +409,8 @@ const BulkLocaleActionModal = ({
285
409
  headers,
286
410
  rows,
287
411
  localesMetadata,
288
- validationErrors = {}
412
+ validationErrors = {},
413
+ action
289
414
  }) => {
290
415
  const { formatMessage } = useIntl();
291
416
  const selectedRows = useTable(
@@ -298,22 +423,24 @@ const BulkLocaleActionModal = ({
298
423
  return acc;
299
424
  }, {});
300
425
  const localesWithErrors = Object.keys(validationErrors);
301
- const alreadyPublishedCount = selectedRows.filter(
426
+ const publishedCount = selectedRows.filter(
302
427
  ({ locale }) => currentStatusByLocale[locale] === "published"
303
428
  ).length;
304
- const readyToPublishCount = selectedRows.filter(
429
+ const draftCount = selectedRows.filter(
305
430
  ({ locale }) => (currentStatusByLocale[locale] === "draft" || currentStatusByLocale[locale] === "modified") && !localesWithErrors.includes(locale)
306
431
  ).length;
307
432
  const withErrorsCount = localesWithErrors.length;
433
+ const messageId = action === "bulk-publish" ? "content-manager.containers.list.selectedEntriesModal.selectedCount.publish" : "content-manager.containers.list.selectedEntriesModal.selectedCount.unpublish";
434
+ 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
435
  return formatMessage(
309
436
  {
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."
437
+ id: messageId,
438
+ defaultMessage
312
439
  },
313
440
  {
314
441
  withErrorsCount,
315
- readyToPublishCount,
316
- alreadyPublishedCount,
442
+ draftCount,
443
+ publishedCount,
317
444
  b: BoldChunk
318
445
  }
319
446
  );
@@ -339,13 +466,12 @@ const BulkLocaleActionModal = ({
339
466
  paddingRight: "6px",
340
467
  paddingTop: "2px",
341
468
  paddingBottom: "2px",
342
- showBullet: false,
343
469
  size: "S",
344
470
  variant: statusVariant,
345
471
  children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
346
472
  }
347
473
  ) }) }),
348
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status }) }),
474
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status, action }) }),
349
475
  /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
350
476
  IconButton,
351
477
  {
@@ -371,6 +497,47 @@ const BulkLocaleActionModal = ({
371
497
  ] }) })
372
498
  ] });
373
499
  };
500
+ const statusVariants = {
501
+ draft: "secondary",
502
+ published: "success",
503
+ modified: "alternative"
504
+ };
505
+ const LocaleOption = ({
506
+ isDraftAndPublishEnabled,
507
+ locale,
508
+ status,
509
+ entryExists
510
+ }) => {
511
+ const { formatMessage } = useIntl();
512
+ if (!entryExists) {
513
+ return formatMessage(
514
+ {
515
+ id: getTranslation("CMEditViewLocalePicker.locale.create"),
516
+ defaultMessage: "Create <bold>{locale}</bold> locale"
517
+ },
518
+ {
519
+ bold: (locale2) => /* @__PURE__ */ jsx("b", { children: locale2 }),
520
+ locale: locale.name
521
+ }
522
+ );
523
+ }
524
+ return /* @__PURE__ */ jsxs(Flex, { width: "100%", gap: 1, justifyContent: "space-between", children: [
525
+ /* @__PURE__ */ jsx(Typography, { children: locale.name }),
526
+ isDraftAndPublishEnabled ? /* @__PURE__ */ jsx(
527
+ Status,
528
+ {
529
+ display: "flex",
530
+ paddingLeft: "6px",
531
+ paddingRight: "6px",
532
+ paddingTop: "2px",
533
+ paddingBottom: "2px",
534
+ size: "S",
535
+ variant: statusVariants[status],
536
+ children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
537
+ }
538
+ ) : null
539
+ ] });
540
+ };
374
541
  const LocalePickerAction = ({
375
542
  document,
376
543
  meta,
@@ -382,7 +549,13 @@ const LocalePickerAction = ({
382
549
  const [{ query }, setQuery] = useQueryParams();
383
550
  const { hasI18n, canCreate, canRead } = useI18n();
384
551
  const { data: locales = [] } = useGetLocalesQuery();
385
- const { schema } = unstable_useDocument({ model, collectionType, documentId });
552
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
553
+ const { schema } = unstable_useDocument({
554
+ model,
555
+ collectionType,
556
+ documentId,
557
+ params: { locale: currentDesiredLocale }
558
+ });
386
559
  const handleSelect = React.useCallback(
387
560
  (value) => {
388
561
  setQuery({
@@ -400,53 +573,50 @@ const LocalePickerAction = ({
400
573
  if (!Array.isArray(locales) || !hasI18n) {
401
574
  return;
402
575
  }
403
- const currentDesiredLocale = query.plugins?.i18n?.locale;
404
576
  const doesLocaleExist = locales.find((loc) => loc.code === currentDesiredLocale);
405
577
  const defaultLocale = locales.find((locale) => locale.isDefault);
406
578
  if (!doesLocaleExist && defaultLocale?.code) {
407
579
  handleSelect(defaultLocale.code);
408
580
  }
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;
581
+ }, [handleSelect, hasI18n, locales, currentDesiredLocale]);
582
+ const currentLocale = Array.isArray(locales) ? locales.find((locale) => locale.code === currentDesiredLocale) : void 0;
414
583
  const allCurrentLocales = [
415
- { status: getDocumentStatus(document, meta), locale: currentLocale },
584
+ { status: getDocumentStatus(document, meta), locale: currentLocale?.code },
416
585
  ...meta?.availableLocales ?? []
417
586
  ];
587
+ if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
588
+ return null;
589
+ }
590
+ const displayedLocales = locales.filter((locale) => {
591
+ return canRead.includes(locale.code);
592
+ });
418
593
  return {
419
594
  label: formatMessage({
420
595
  id: getTranslation("Settings.locales.modal.locales.label"),
421
596
  defaultMessage: "Locales"
422
597
  }),
423
- options: locales.map((locale) => {
598
+ options: displayedLocales.map((locale) => {
599
+ const entryWithLocaleExists = allCurrentLocales.some((doc) => doc.locale === locale.code);
424
600
  const currentLocaleDoc = allCurrentLocales.find(
425
601
  (doc) => "locale" in doc ? doc.locale === locale.code : false
426
602
  );
427
- const status = currentLocaleDoc?.status ?? "draft";
428
- const permissionsToCheck = currentLocaleDoc ? canCreate : canRead;
429
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
603
+ const permissionsToCheck = currentLocaleDoc ? canRead : canCreate;
430
604
  return {
431
605
  disabled: !permissionsToCheck.includes(locale.code),
432
606
  value: locale.code,
433
- label: locale.name,
434
- startIcon: schema?.options?.draftAndPublish ? /* @__PURE__ */ jsx(
435
- Status,
607
+ label: /* @__PURE__ */ jsx(
608
+ LocaleOption,
436
609
  {
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) })
610
+ isDraftAndPublishEnabled: !!schema?.options?.draftAndPublish,
611
+ locale,
612
+ status: currentLocaleDoc?.status,
613
+ entryExists: entryWithLocaleExists
446
614
  }
447
- ) : null
615
+ ),
616
+ startIcon: !entryWithLocaleExists ? /* @__PURE__ */ jsx(Plus, {}) : null
448
617
  };
449
618
  }),
619
+ customizeContent: () => currentLocale?.name,
450
620
  onSelect: handleSelect,
451
621
  value: currentLocale
452
622
  };
@@ -462,6 +632,99 @@ const getDocumentStatus = (document, meta) => {
462
632
  }
463
633
  return docStatus;
464
634
  };
635
+ const FillFromAnotherLocaleAction = ({
636
+ documentId,
637
+ meta,
638
+ model,
639
+ collectionType
640
+ }) => {
641
+ const { formatMessage } = useIntl();
642
+ const [{ query }] = useQueryParams();
643
+ const { hasI18n } = useI18n();
644
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
645
+ const [localeSelected, setLocaleSelected] = React.useState(null);
646
+ const setValues = useForm("FillFromAnotherLocale", (state) => state.setValues);
647
+ const { getDocument } = unstable_useDocumentActions();
648
+ const { schema, components } = unstable_useDocument({
649
+ model,
650
+ documentId,
651
+ collectionType,
652
+ params: { locale: currentDesiredLocale }
653
+ });
654
+ const { data: locales = [] } = useGetLocalesQuery();
655
+ const availableLocales = Array.isArray(locales) ? locales.filter((locale) => meta?.availableLocales.some((l) => l.locale === locale.code)) : [];
656
+ const fillFromLocale = (onClose) => async () => {
657
+ const response = await getDocument({
658
+ collectionType,
659
+ model,
660
+ documentId,
661
+ params: { locale: localeSelected }
662
+ });
663
+ if (!response || !schema) {
664
+ return;
665
+ }
666
+ const { data } = response;
667
+ const cleanedData = cleanData(data, schema, components);
668
+ setValues(cleanedData);
669
+ onClose();
670
+ };
671
+ if (!hasI18n) {
672
+ return null;
673
+ }
674
+ return {
675
+ type: "icon",
676
+ icon: /* @__PURE__ */ jsx(Download, {}),
677
+ disabled: availableLocales.length === 0,
678
+ label: formatMessage({
679
+ id: getTranslation("CMEditViewCopyLocale.copy-text"),
680
+ defaultMessage: "Fill in from another locale"
681
+ }),
682
+ dialog: {
683
+ type: "dialog",
684
+ title: formatMessage({
685
+ id: getTranslation("CMEditViewCopyLocale.dialog.title"),
686
+ defaultMessage: "Confirmation"
687
+ }),
688
+ content: ({ onClose }) => /* @__PURE__ */ jsxs(Fragment, { children: [
689
+ /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
690
+ /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
691
+ /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
692
+ id: getTranslation("CMEditViewCopyLocale.dialog.body"),
693
+ defaultMessage: "Your current content will be erased and filled by the content of the selected locale:"
694
+ }) }),
695
+ /* @__PURE__ */ jsxs(Field.Root, { width: "100%", children: [
696
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
697
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.label"),
698
+ defaultMessage: "Locale"
699
+ }) }),
700
+ /* @__PURE__ */ jsx(
701
+ SingleSelect,
702
+ {
703
+ value: localeSelected,
704
+ placeholder: formatMessage({
705
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.placeholder"),
706
+ defaultMessage: "Select one locale..."
707
+ }),
708
+ onChange: (value) => setLocaleSelected(value),
709
+ children: availableLocales.map((locale) => /* @__PURE__ */ jsx(SingleSelectOption, { value: locale.code, children: locale.name }, locale.code))
710
+ }
711
+ )
712
+ ] })
713
+ ] }) }),
714
+ /* @__PURE__ */ jsx(Dialog.Footer, { children: /* @__PURE__ */ jsxs(Flex, { gap: 2, width: "100%", children: [
715
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "tertiary", onClick: onClose, children: formatMessage({
716
+ id: getTranslation("CMEditViewCopyLocale.cancel-text"),
717
+ defaultMessage: "No, cancel"
718
+ }) }),
719
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "success", onClick: fillFromLocale(onClose), children: formatMessage({
720
+ id: getTranslation("CMEditViewCopyLocale.submit-text"),
721
+ defaultMessage: "Yes, fill in"
722
+ }) })
723
+ ] }) })
724
+ ] })
725
+ }
726
+ };
727
+ };
465
728
  const DeleteLocaleAction = ({
466
729
  document,
467
730
  documentId,
@@ -473,16 +736,23 @@ const DeleteLocaleAction = ({
473
736
  const { toggleNotification } = useNotification();
474
737
  const { delete: deleteAction } = unstable_useDocumentActions();
475
738
  const { hasI18n, canDelete } = useI18n();
739
+ const [{ query }] = useQueryParams();
740
+ const { data: locales = [] } = useGetLocalesQuery();
741
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
742
+ const locale = !("error" in locales) && locales.find((loc) => loc.code === currentDesiredLocale);
476
743
  if (!hasI18n) {
477
744
  return null;
478
745
  }
479
746
  return {
480
747
  disabled: document?.locale && !canDelete.includes(document.locale) || !document || !document.id,
481
748
  position: ["header", "table-row"],
482
- label: formatMessage({
483
- id: getTranslation("actions.delete.label"),
484
- defaultMessage: "Delete locale"
485
- }),
749
+ label: formatMessage(
750
+ {
751
+ id: getTranslation("actions.delete.label"),
752
+ defaultMessage: "Delete entry ({locale})"
753
+ },
754
+ { locale: locale && locale.name }
755
+ ),
486
756
  icon: /* @__PURE__ */ jsx(StyledTrash, {}),
487
757
  variant: "danger",
488
758
  dialog: {
@@ -499,7 +769,12 @@ const DeleteLocaleAction = ({
499
769
  }) })
500
770
  ] }),
501
771
  onConfirm: async () => {
502
- if (!documentId || !document?.locale) {
772
+ const unableToDelete = (
773
+ // We are unable to delete a collection type without a document ID
774
+ // & unable to delete generally if there is no document locale
775
+ collectionType !== "single-types" && !documentId || !document?.locale
776
+ );
777
+ if (unableToDelete) {
503
778
  console.error(
504
779
  "You're trying to delete a document without an id or locale, this is likely a bug with Strapi. Please open an issue."
505
780
  );
@@ -525,23 +800,24 @@ const DeleteLocaleAction = ({
525
800
  }
526
801
  };
527
802
  };
528
- const BulkLocalePublishAction = ({
803
+ const BulkLocaleAction = ({
529
804
  document: baseDocument,
530
805
  documentId,
531
806
  model,
532
- collectionType
807
+ collectionType,
808
+ action
533
809
  }) => {
534
810
  const baseLocale = baseDocument?.locale ?? null;
535
811
  const [{ query }] = useQueryParams();
536
812
  const params = React.useMemo(() => buildValidParams(query), [query]);
537
- const isPublishedTab = query.status === "published";
813
+ const isOnPublishedTab = query.status === "published";
538
814
  const { formatMessage } = useIntl();
539
815
  const { hasI18n, canPublish } = useI18n();
540
816
  const { toggleNotification } = useNotification();
541
817
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
542
818
  const [selectedRows, setSelectedRows] = React.useState([]);
543
819
  const [isDraftRelationConfirmationOpen, setIsDraftRelationConfirmationOpen] = React.useState(false);
544
- const { publishMany: publishManyAction } = unstable_useDocumentActions();
820
+ const { publishMany: publishManyAction, unpublishMany: unpublishManyAction } = unstable_useDocumentActions();
545
821
  const {
546
822
  document,
547
823
  meta: documentMeta,
@@ -557,7 +833,7 @@ const BulkLocalePublishAction = ({
557
833
  }
558
834
  },
559
835
  {
560
- skip: !hasI18n
836
+ skip: !hasI18n || !baseLocale
561
837
  }
562
838
  );
563
839
  const { data: localesMetadata = [] } = useGetLocalesQuery(hasI18n ? void 0 : skipToken);
@@ -609,12 +885,19 @@ const BulkLocalePublishAction = ({
609
885
  }, {});
610
886
  return [rowsFromMeta, errors];
611
887
  }, [document, documentMeta?.availableLocales, validate]);
612
- const localesToPublish = selectedRows.reduce((acc, selectedRow) => {
613
- if (selectedRow.status !== "published" && !Object.keys(validationErrors).includes(selectedRow.locale)) {
888
+ const isBulkPublish = action === "bulk-publish";
889
+ const localesForAction = selectedRows.reduce((acc, selectedRow) => {
890
+ const isValidLocale = (
891
+ // Validation errors are irrelevant if we are trying to unpublish
892
+ !isBulkPublish || !Object.keys(validationErrors).includes(selectedRow.locale)
893
+ );
894
+ const shouldAddLocale = isBulkPublish ? selectedRow.status !== "published" && isValidLocale : selectedRow.status !== "draft" && isValidLocale;
895
+ if (shouldAddLocale) {
614
896
  acc.push(selectedRow.locale);
615
897
  }
616
898
  return acc;
617
899
  }, []);
900
+ const enableDraftRelationsCount = false;
618
901
  const {
619
902
  data: draftRelationsCount = 0,
620
903
  isLoading: isDraftRelationsLoading,
@@ -623,10 +906,10 @@ const BulkLocalePublishAction = ({
623
906
  {
624
907
  model,
625
908
  documentIds: [documentId],
626
- locale: localesToPublish
909
+ locale: localesForAction
627
910
  },
628
911
  {
629
- skip: !documentId || localesToPublish.length === 0
912
+ skip: !enableDraftRelationsCount
630
913
  }
631
914
  );
632
915
  React.useEffect(() => {
@@ -652,7 +935,18 @@ const BulkLocalePublishAction = ({
652
935
  documentIds: [documentId],
653
936
  params: {
654
937
  ...params,
655
- locale: localesToPublish
938
+ locale: localesForAction
939
+ }
940
+ });
941
+ setSelectedRows([]);
942
+ };
943
+ const unpublish = async () => {
944
+ await unpublishManyAction({
945
+ model,
946
+ documentIds: [documentId],
947
+ params: {
948
+ ...params,
949
+ locale: localesForAction
656
950
  }
657
951
  });
658
952
  setSelectedRows([]);
@@ -660,14 +954,12 @@ const BulkLocalePublishAction = ({
660
954
  const handleAction = async () => {
661
955
  if (draftRelationsCount > 0) {
662
956
  setIsDraftRelationConfirmationOpen(true);
663
- } else {
957
+ } else if (isBulkPublish) {
664
958
  await publish();
959
+ } else {
960
+ await unpublish();
665
961
  }
666
962
  };
667
- const isUnpublish = document?.status === "published";
668
- if (isUnpublish) {
669
- console.warn(["I18N"], "Bulk locale unpublish modal not implemented");
670
- }
671
963
  if (isDraftRelationConfirmationOpen) {
672
964
  return {
673
965
  label: formatMessage({
@@ -705,18 +997,18 @@ const BulkLocalePublishAction = ({
705
997
  const hasPermission = selectedRows.map(({ locale }) => locale).every((locale) => canPublish.includes(locale));
706
998
  return {
707
999
  label: formatMessage({
708
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
709
- defaultMessage: "Publish Multiple Locales"
1000
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
1001
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
710
1002
  }),
711
- icon: /* @__PURE__ */ jsx(ListPlus, {}),
712
- disabled: isPublishedTab || canPublish.length === 0,
1003
+ variant: isBulkPublish ? "secondary" : "danger",
1004
+ icon: isBulkPublish ? /* @__PURE__ */ jsx(ListPlus, {}) : /* @__PURE__ */ jsx(Cross, {}),
1005
+ disabled: isOnPublishedTab || canPublish.length === 0,
713
1006
  position: ["panel"],
714
- variant: "secondary",
715
1007
  dialog: {
716
1008
  type: "modal",
717
1009
  title: formatMessage({
718
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
719
- defaultMessage: "Publish Multiple Locales"
1010
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
1011
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
720
1012
  }),
721
1013
  content: () => {
722
1014
  return /* @__PURE__ */ jsx(
@@ -735,28 +1027,35 @@ const BulkLocalePublishAction = ({
735
1027
  validationErrors,
736
1028
  headers,
737
1029
  rows,
738
- localesMetadata
1030
+ localesMetadata,
1031
+ action: action ?? "bulk-publish"
739
1032
  }
740
1033
  )
741
1034
  }
742
1035
  );
743
1036
  },
744
1037
  footer: () => /* @__PURE__ */ jsx(Modal.Footer, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
745
- Button$1,
1038
+ Button,
746
1039
  {
747
1040
  loading: isDraftRelationsLoading,
748
- disabled: !hasPermission || localesToPublish.length === 0,
1041
+ disabled: !hasPermission || localesForAction.length === 0,
749
1042
  variant: "default",
750
1043
  onClick: handleAction,
751
1044
  children: formatMessage({
752
- id: "app.utils.publish",
753
- defaultMessage: "Publish"
1045
+ id: isBulkPublish ? "app.utils.publish" : "app.utils.unpublish",
1046
+ defaultMessage: isBulkPublish ? "Publish" : "Unpublish"
754
1047
  })
755
1048
  }
756
1049
  ) })
757
1050
  }
758
1051
  };
759
1052
  };
1053
+ const BulkLocalePublishAction = (props) => {
1054
+ return BulkLocaleAction({ action: "bulk-publish", ...props });
1055
+ };
1056
+ const BulkLocaleUnpublishAction = (props) => {
1057
+ return BulkLocaleAction({ action: "bulk-unpublish", ...props });
1058
+ };
760
1059
  const StyledTrash = styled(Trash)`
761
1060
  path {
762
1061
  fill: currentColor;
@@ -976,54 +1275,13 @@ const LocaleListCell = ({
976
1275
  return locale.name;
977
1276
  }).toSorted((a, b) => formatter.compare(a, b));
978
1277
  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
- ) }) }),
1278
+ /* @__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: [
1279
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", ellipsis: true, marginRight: 2, children: localesForDocument.join(", ") }),
1280
+ /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(CaretDown, { width: "1.2rem", height: "1.2rem" }) })
1281
+ ] }) }) }),
993
1282
  /* @__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
1283
  ] });
995
1284
  };
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
1285
  const addColumnToTableHook = ({ displayedHeaders, layout }) => {
1028
1286
  const { options } = layout;
1029
1287
  const isFieldLocalized = doesPluginOptionsHaveI18nLocalized(options) ? options.i18n.localized : false;
@@ -1149,9 +1407,6 @@ const localeMiddleware = (ctx) => (next) => (permissions) => {
1149
1407
  return next(revisedPermissions);
1150
1408
  };
1151
1409
  const prefixPluginTranslations = (trad, pluginId2) => {
1152
- if (!pluginId2) {
1153
- throw new TypeError("pluginId can't be empty");
1154
- }
1155
1410
  return Object.keys(trad).reduce((acc, current) => {
1156
1411
  acc[`${pluginId2}.${current}`] = trad[current];
1157
1412
  return acc;
@@ -1221,11 +1476,11 @@ const index = {
1221
1476
  },
1222
1477
  id: "internationalization",
1223
1478
  to: "internationalization",
1224
- Component: () => import("./SettingsPage-CbEn7zF5.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1479
+ Component: () => import("./SettingsPage-BAx9nmep.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1225
1480
  permissions: PERMISSIONS.accessMain
1226
1481
  });
1227
1482
  const contentManager = app.getPlugin("content-manager");
1228
- contentManager.apis.addDocumentHeaderAction([LocalePickerAction]);
1483
+ contentManager.apis.addDocumentHeaderAction([LocalePickerAction, FillFromAnotherLocaleAction]);
1229
1484
  contentManager.apis.addDocumentAction((actions) => {
1230
1485
  const indexOfDeleteAction = actions.findIndex((action) => action.type === "delete");
1231
1486
  actions.splice(indexOfDeleteAction, 0, DeleteLocaleAction);
@@ -1233,6 +1488,7 @@ const index = {
1233
1488
  });
1234
1489
  contentManager.apis.addDocumentAction((actions) => {
1235
1490
  actions.splice(2, 0, BulkLocalePublishAction);
1491
+ actions.splice(5, 0, BulkLocaleUnpublishAction);
1236
1492
  return actions;
1237
1493
  });
1238
1494
  contentManager.injectComponent("listView", "actions", {
@@ -1338,7 +1594,7 @@ const index = {
1338
1594
  async registerTrads({ locales }) {
1339
1595
  const importedTrads = await Promise.all(
1340
1596
  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 }) => {
1597
+ 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`, 3).then(({ default: data }) => {
1342
1598
  return {
1343
1599
  data: prefixPluginTranslations(data, pluginId),
1344
1600
  locale
@@ -1364,4 +1620,4 @@ export {
1364
1620
  index as i,
1365
1621
  useCreateLocaleMutation as u
1366
1622
  };
1367
- //# sourceMappingURL=index-CkrCiZyq.mjs.map
1623
+ //# sourceMappingURL=index-B0NijiBB.mjs.map