@strapi/i18n 0.0.0-experimental.5b211b38912691ee2eab22d47b5095ea2fcfec76 → 0.0.0-experimental.5d4d8671a4141a722aa70f22022a255988f07137

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 (54) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/{SettingsPage-B5sTcP82.mjs → SettingsPage-BHvunuIF.mjs} +8 -8
  3. package/dist/_chunks/SettingsPage-BHvunuIF.mjs.map +1 -0
  4. package/dist/_chunks/{SettingsPage-DT1sxWa2.js → SettingsPage-Bcj7380u.js} +8 -8
  5. package/dist/_chunks/SettingsPage-Bcj7380u.js.map +1 -0
  6. package/dist/_chunks/{en-BsOU9o5z.js → en-BKBz3tro.js} +10 -3
  7. package/dist/_chunks/en-BKBz3tro.js.map +1 -0
  8. package/dist/_chunks/{en-CM6Pjfyv.mjs → en-DlXfy6Gy.mjs} +10 -3
  9. package/dist/_chunks/en-DlXfy6Gy.mjs.map +1 -0
  10. package/dist/_chunks/{index-jMrzaEb9.js → index-BKZbxhpm.js} +416 -170
  11. package/dist/_chunks/index-BKZbxhpm.js.map +1 -0
  12. package/dist/_chunks/{index-DsNqyQKx.mjs → index-DUdrr5PR.mjs} +412 -166
  13. package/dist/_chunks/index-DUdrr5PR.mjs.map +1 -0
  14. package/dist/admin/index.js +1 -1
  15. package/dist/admin/index.mjs +1 -1
  16. package/dist/admin/src/components/BulkLocaleActionModal.d.ts +2 -1
  17. package/dist/admin/src/components/CMHeaderActions.d.ts +29 -3
  18. package/dist/admin/src/components/CreateLocale.d.ts +6 -6
  19. package/dist/admin/src/utils/clean.d.ts +4 -0
  20. package/dist/admin/src/utils/schemas.d.ts +1 -0
  21. package/dist/server/index.js +413 -480
  22. package/dist/server/index.js.map +1 -1
  23. package/dist/server/index.mjs +415 -482
  24. package/dist/server/index.mjs.map +1 -1
  25. package/dist/server/src/bootstrap.d.ts +1 -4
  26. package/dist/server/src/bootstrap.d.ts.map +1 -1
  27. package/dist/server/src/index.d.ts +21 -13
  28. package/dist/server/src/index.d.ts.map +1 -1
  29. package/dist/server/src/register.d.ts.map +1 -1
  30. package/dist/server/src/services/index.d.ts +20 -10
  31. package/dist/server/src/services/index.d.ts.map +1 -1
  32. package/dist/server/src/services/permissions/actions.d.ts +14 -2
  33. package/dist/server/src/services/permissions/actions.d.ts.map +1 -1
  34. package/dist/server/src/services/permissions.d.ts +14 -2
  35. package/dist/server/src/services/permissions.d.ts.map +1 -1
  36. package/dist/server/src/services/sanitize/index.d.ts +11 -0
  37. package/dist/server/src/services/sanitize/index.d.ts.map +1 -0
  38. package/dist/server/src/utils/index.d.ts +2 -2
  39. package/dist/server/src/utils/index.d.ts.map +1 -1
  40. package/package.json +10 -10
  41. package/dist/_chunks/SettingsPage-B5sTcP82.mjs.map +0 -1
  42. package/dist/_chunks/SettingsPage-DT1sxWa2.js.map +0 -1
  43. package/dist/_chunks/en-BsOU9o5z.js.map +0 -1
  44. package/dist/_chunks/en-CM6Pjfyv.mjs.map +0 -1
  45. package/dist/_chunks/index-DsNqyQKx.mjs.map +0 -1
  46. package/dist/_chunks/index-jMrzaEb9.js.map +0 -1
  47. package/dist/admin/src/components/Initializer.d.ts +0 -5
  48. package/dist/server/src/migrations/content-type/disable/index.d.ts +0 -3
  49. package/dist/server/src/migrations/content-type/disable/index.d.ts.map +0 -1
  50. package/dist/server/src/migrations/content-type/enable/index.d.ts +0 -3
  51. package/dist/server/src/migrations/content-type/enable/index.d.ts.map +0 -1
  52. package/dist/server/src/services/entity-service-decorator.d.ts +0 -29
  53. package/dist/server/src/services/entity-service-decorator.d.ts.map +0 -1
  54. package/strapi-server.js +0 -3
@@ -1,12 +1,13 @@
1
1
  import get from "lodash/get";
2
2
  import * as yup from "yup";
3
- import { jsxs, jsx } from "react/jsx-runtime";
3
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
4
4
  import * as React from "react";
5
- import { Typography, Dialog, Field, Checkbox, Flex, Button as Button$1, Box, Status, IconButton, Tooltip, SingleSelect, SingleSelectOption, VisuallyHidden, useCollator, Popover } from "@strapi/design-system";
6
- import { WarningCircle, Pencil, CrossCircle, CheckCircle, ArrowsCounterClockwise, Trash, ListPlus, Earth, EarthStriked, CaretDown } from "@strapi/icons";
5
+ import { Typography, Dialog, Field, Checkbox, Flex, Button, Modal, Box, Status, IconButton, Tooltip, SingleSelect, SingleSelectOption, VisuallyHidden, useCollator, Popover } from "@strapi/design-system";
6
+ import { WarningCircle, Pencil, CrossCircle, CheckCircle, ArrowsCounterClockwise, Trash, 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
- import { useAuth, adminApi, useTable, Table, useQueryParams, useNotification, useAPIErrorHandler } from "@strapi/admin/strapi-admin";
9
+ import { skipToken } from "@reduxjs/toolkit/query";
10
+ import { useAuth, adminApi, useTable, Table, useQueryParams, useForm, useNotification, useAPIErrorHandler } from "@strapi/admin/strapi-admin";
10
11
  import { unstable_useDocument, unstable_useDocumentActions, buildValidParams } from "@strapi/content-manager/strapi-admin";
11
12
  import { useParams, Link, useNavigate, matchPath } from "react-router-dom";
12
13
  import * as qs from "qs";
@@ -77,11 +78,11 @@ const CheckboxConfirmation = ({
77
78
  }) }) })
78
79
  ] }) }),
79
80
  /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
80
- /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button$1, { variant: "tertiary", children: formatMessage({
81
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
81
82
  id: "components.popUpWarning.button.cancel",
82
83
  defaultMessage: "No, cancel"
83
84
  }) }) }),
84
- /* @__PURE__ */ jsx(Dialog.Action, { children: /* @__PURE__ */ jsx(Button$1, { variant: "danger-light", onClick: handleConfirm, children: formatMessage({
85
+ /* @__PURE__ */ jsx(Dialog.Action, { children: /* @__PURE__ */ jsx(Button, { variant: "danger-light", onClick: handleConfirm, children: formatMessage({
85
86
  id: getTranslation("CheckboxConfirmation.Modal.button-confirm"),
86
87
  defaultMessage: "Yes, disable"
87
88
  }) }) })
@@ -136,7 +137,7 @@ const useI18n = () => {
136
137
  model: params.slug
137
138
  },
138
139
  {
139
- skip: !params.slug || !params.collectionType
140
+ skip: true
140
141
  }
141
142
  );
142
143
  if (doesPluginOptionsHaveI18nLocalized(schema?.pluginOptions)) {
@@ -216,10 +217,94 @@ const relationsApi = i18nApi.injectEndpoints({
216
217
  })
217
218
  });
218
219
  const { useGetManyDraftRelationCountQuery } = relationsApi;
220
+ const cleanData = (data, schema, components) => {
221
+ const cleanedData = removeFields(data, [
222
+ "createdAt",
223
+ "createdBy",
224
+ "updatedAt",
225
+ "updatedBy",
226
+ "id",
227
+ "documentId",
228
+ "publishedAt",
229
+ "strapi_stage",
230
+ "strapi_assignee",
231
+ "locale",
232
+ "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
+ };
219
300
  const isErrorMessageDescriptor = (object) => {
220
301
  return typeof object === "object" && object !== null && "id" in object && "defaultMessage" in object;
221
302
  };
222
- const EntryValidationText = ({ status = "draft", validationErrors }) => {
303
+ const EntryValidationText = ({
304
+ status = "draft",
305
+ validationErrors,
306
+ action
307
+ }) => {
223
308
  const { formatMessage } = useIntl();
224
309
  const getErrorStr = (key, value) => {
225
310
  if (typeof value === "string") {
@@ -253,30 +338,63 @@ const EntryValidationText = ({ status = "draft", validationErrors }) => {
253
338
  ) })
254
339
  ] });
255
340
  }
256
- if (status === "published") {
257
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
258
- /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
259
- /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
260
- id: "content-manager.bulk-publish.already-published",
261
- defaultMessage: "Already Published"
262
- }) })
263
- ] });
264
- }
265
- if (status === "modified") {
266
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
267
- /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
268
- /* @__PURE__ */ jsx(Typography, { children: formatMessage({
269
- id: "app.utils.ready-to-publish-changes",
270
- defaultMessage: "Ready to publish changes"
271
- }) })
272
- ] });
273
- }
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();
274
395
  return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
275
- /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
276
- /* @__PURE__ */ jsx(Typography, { children: formatMessage({
277
- id: "app.utils.ready-to-publish",
278
- defaultMessage: "Ready to publish"
279
- }) })
396
+ icon,
397
+ /* @__PURE__ */ jsx(Typography, { textColor, fontWeight, children: text })
280
398
  ] });
281
399
  };
282
400
  const BoldChunk = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
@@ -284,7 +402,8 @@ const BulkLocaleActionModal = ({
284
402
  headers,
285
403
  rows,
286
404
  localesMetadata,
287
- validationErrors = {}
405
+ validationErrors = {},
406
+ action
288
407
  }) => {
289
408
  const { formatMessage } = useIntl();
290
409
  const selectedRows = useTable(
@@ -297,27 +416,29 @@ const BulkLocaleActionModal = ({
297
416
  return acc;
298
417
  }, {});
299
418
  const localesWithErrors = Object.keys(validationErrors);
300
- const alreadyPublishedCount = selectedRows.filter(
419
+ const publishedCount = selectedRows.filter(
301
420
  ({ locale }) => currentStatusByLocale[locale] === "published"
302
421
  ).length;
303
- const readyToPublishCount = selectedRows.filter(
422
+ const draftCount = selectedRows.filter(
304
423
  ({ locale }) => (currentStatusByLocale[locale] === "draft" || currentStatusByLocale[locale] === "modified") && !localesWithErrors.includes(locale)
305
424
  ).length;
306
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.";
307
428
  return formatMessage(
308
429
  {
309
- id: "content-manager.containers.list.selectedEntriesModal.selectedCount",
310
- defaultMessage: "<b>{alreadyPublishedCount}</b> {alreadyPublishedCount, plural, =0 {entries} one {entry} other {entries}} already published. <b>{readyToPublishCount}</b> {readyToPublishCount, plural, =0 {entries} one {entry} other {entries}} ready to publish. <b>{withErrorsCount}</b> {withErrorsCount, plural, =0 {entries} one {entry} other {entries}} waiting for action."
430
+ id: messageId,
431
+ defaultMessage
311
432
  },
312
433
  {
313
434
  withErrorsCount,
314
- readyToPublishCount,
315
- alreadyPublishedCount,
435
+ draftCount,
436
+ publishedCount,
316
437
  b: BoldChunk
317
438
  }
318
439
  );
319
440
  };
320
- return /* @__PURE__ */ jsxs(React.Fragment, { children: [
441
+ return /* @__PURE__ */ jsxs(Modal.Body, { children: [
321
442
  /* @__PURE__ */ jsx(Typography, { children: getFormattedCountMessage() }),
322
443
  /* @__PURE__ */ jsx(Box, { marginTop: 5, children: /* @__PURE__ */ jsxs(Table.Content, { children: [
323
444
  /* @__PURE__ */ jsxs(Table.Head, { children: [
@@ -344,7 +465,7 @@ const BulkLocaleActionModal = ({
344
465
  children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
345
466
  }
346
467
  ) }) }),
347
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status }) }),
468
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status, action }) }),
348
469
  /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
349
470
  IconButton,
350
471
  {
@@ -361,7 +482,7 @@ const BulkLocaleActionModal = ({
361
482
  name: locale
362
483
  }
363
484
  ),
364
- borderWidth: 0,
485
+ variant: "ghost",
365
486
  children: /* @__PURE__ */ jsx(Pencil, {})
366
487
  }
367
488
  ) })
@@ -370,6 +491,48 @@ const BulkLocaleActionModal = ({
370
491
  ] }) })
371
492
  ] });
372
493
  };
494
+ const statusVariants = {
495
+ draft: "secondary",
496
+ published: "success",
497
+ modified: "alternative"
498
+ };
499
+ const LocaleOption = ({
500
+ isDraftAndPublishEnabled,
501
+ locale,
502
+ status,
503
+ entryExists
504
+ }) => {
505
+ const { formatMessage } = useIntl();
506
+ if (!entryExists) {
507
+ return formatMessage(
508
+ {
509
+ id: getTranslation("CMEditViewLocalePicker.locale.create"),
510
+ defaultMessage: "Create <bold>{locale}</bold> locale"
511
+ },
512
+ {
513
+ bold: (locale2) => /* @__PURE__ */ jsx("b", { children: locale2 }),
514
+ locale: locale.name
515
+ }
516
+ );
517
+ }
518
+ return /* @__PURE__ */ jsxs(Flex, { width: "100%", gap: 1, justifyContent: "space-between", children: [
519
+ /* @__PURE__ */ jsx(Typography, { children: locale.name }),
520
+ isDraftAndPublishEnabled ? /* @__PURE__ */ jsx(
521
+ Status,
522
+ {
523
+ display: "flex",
524
+ paddingLeft: "6px",
525
+ paddingRight: "6px",
526
+ paddingTop: "2px",
527
+ paddingBottom: "2px",
528
+ showBullet: false,
529
+ size: "S",
530
+ variant: statusVariants[status],
531
+ children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
532
+ }
533
+ ) : null
534
+ ] });
535
+ };
373
536
  const LocalePickerAction = ({
374
537
  document,
375
538
  meta,
@@ -381,7 +544,13 @@ const LocalePickerAction = ({
381
544
  const [{ query }, setQuery] = useQueryParams();
382
545
  const { hasI18n, canCreate, canRead } = useI18n();
383
546
  const { data: locales = [] } = useGetLocalesQuery();
384
- const { schema } = unstable_useDocument({ model, collectionType, documentId });
547
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
548
+ const { schema } = unstable_useDocument({
549
+ model,
550
+ collectionType,
551
+ documentId,
552
+ params: { locale: currentDesiredLocale }
553
+ });
385
554
  const handleSelect = React.useCallback(
386
555
  (value) => {
387
556
  setQuery({
@@ -399,53 +568,47 @@ const LocalePickerAction = ({
399
568
  if (!Array.isArray(locales) || !hasI18n) {
400
569
  return;
401
570
  }
402
- const currentDesiredLocale = query.plugins?.i18n?.locale;
403
571
  const doesLocaleExist = locales.find((loc) => loc.code === currentDesiredLocale);
404
572
  const defaultLocale = locales.find((locale) => locale.isDefault);
405
573
  if (!doesLocaleExist && defaultLocale?.code) {
406
574
  handleSelect(defaultLocale.code);
407
575
  }
408
- }, [handleSelect, hasI18n, locales, query.plugins?.i18n?.locale]);
409
- if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
410
- return null;
411
- }
412
- const currentLocale = query.plugins?.i18n?.locale || locales.find((loc) => loc.isDefault)?.code;
576
+ }, [handleSelect, hasI18n, locales, currentDesiredLocale]);
577
+ const currentLocale = Array.isArray(locales) ? locales.find((locale) => locale.code === currentDesiredLocale) : void 0;
413
578
  const allCurrentLocales = [
414
- { status: getDocumentStatus(document, meta), locale: currentLocale },
579
+ { status: getDocumentStatus(document, meta), locale: currentLocale?.code },
415
580
  ...meta?.availableLocales ?? []
416
581
  ];
582
+ if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
583
+ return null;
584
+ }
417
585
  return {
418
586
  label: formatMessage({
419
587
  id: getTranslation("Settings.locales.modal.locales.label"),
420
588
  defaultMessage: "Locales"
421
589
  }),
422
590
  options: locales.map((locale) => {
591
+ const entryWithLocaleExists = allCurrentLocales.some((doc) => doc.locale === locale.code);
423
592
  const currentLocaleDoc = allCurrentLocales.find(
424
593
  (doc) => "locale" in doc ? doc.locale === locale.code : false
425
594
  );
426
- const status = currentLocaleDoc?.status ?? "draft";
427
- const permissionsToCheck = currentLocaleDoc ? canCreate : canRead;
428
- const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
595
+ const permissionsToCheck = currentLocaleDoc ? canRead : canCreate;
429
596
  return {
430
597
  disabled: !permissionsToCheck.includes(locale.code),
431
598
  value: locale.code,
432
- label: locale.name,
433
- startIcon: schema?.options?.draftAndPublish ? /* @__PURE__ */ jsx(
434
- Status,
599
+ label: /* @__PURE__ */ jsx(
600
+ LocaleOption,
435
601
  {
436
- display: "flex",
437
- paddingLeft: "6px",
438
- paddingRight: "6px",
439
- paddingTop: "2px",
440
- paddingBottom: "2px",
441
- showBullet: false,
442
- size: "S",
443
- variant: statusVariant,
444
- children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
602
+ isDraftAndPublishEnabled: !!schema?.options?.draftAndPublish,
603
+ locale,
604
+ status: currentLocaleDoc?.status,
605
+ entryExists: entryWithLocaleExists
445
606
  }
446
- ) : null
607
+ ),
608
+ startIcon: !entryWithLocaleExists ? /* @__PURE__ */ jsx(Plus, {}) : null
447
609
  };
448
610
  }),
611
+ customizeContent: () => currentLocale?.name,
449
612
  onSelect: handleSelect,
450
613
  value: currentLocale
451
614
  };
@@ -461,6 +624,99 @@ const getDocumentStatus = (document, meta) => {
461
624
  }
462
625
  return docStatus;
463
626
  };
627
+ const FillFromAnotherLocaleAction = ({
628
+ documentId,
629
+ meta,
630
+ model,
631
+ collectionType
632
+ }) => {
633
+ const { formatMessage } = useIntl();
634
+ const [{ query }] = useQueryParams();
635
+ const { hasI18n } = useI18n();
636
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
637
+ const [localeSelected, setLocaleSelected] = React.useState(null);
638
+ const setValues = useForm("FillFromAnotherLocale", (state) => state.setValues);
639
+ const { getDocument } = unstable_useDocumentActions();
640
+ const { schema, components } = unstable_useDocument({
641
+ model,
642
+ documentId,
643
+ collectionType,
644
+ params: { locale: currentDesiredLocale }
645
+ });
646
+ const { data: locales = [] } = useGetLocalesQuery();
647
+ const availableLocales = Array.isArray(locales) ? locales.filter((locale) => meta?.availableLocales.some((l) => l.locale === locale.code)) : [];
648
+ const fillFromLocale = (onClose) => async () => {
649
+ const response = await getDocument({
650
+ collectionType,
651
+ model,
652
+ documentId,
653
+ params: { locale: localeSelected }
654
+ });
655
+ if (!response || !schema) {
656
+ return;
657
+ }
658
+ const { data } = response;
659
+ const cleanedData = cleanData(data, schema, components);
660
+ setValues(cleanedData);
661
+ onClose();
662
+ };
663
+ if (!hasI18n) {
664
+ return null;
665
+ }
666
+ return {
667
+ type: "icon",
668
+ icon: /* @__PURE__ */ jsx(Download, {}),
669
+ disabled: availableLocales.length === 0,
670
+ label: formatMessage({
671
+ id: getTranslation("CMEditViewCopyLocale.copy-text"),
672
+ defaultMessage: "Fill in from another locale"
673
+ }),
674
+ dialog: {
675
+ type: "dialog",
676
+ title: formatMessage({
677
+ id: getTranslation("CMEditViewCopyLocale.dialog.title"),
678
+ defaultMessage: "Confirmation"
679
+ }),
680
+ content: ({ onClose }) => /* @__PURE__ */ jsxs(Fragment, { children: [
681
+ /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
682
+ /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
683
+ /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
684
+ id: getTranslation("CMEditViewCopyLocale.dialog.body"),
685
+ defaultMessage: "Your current content will be erased and filled by the content of the selected locale:"
686
+ }) }),
687
+ /* @__PURE__ */ jsxs(Field.Root, { width: "100%", children: [
688
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
689
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.label"),
690
+ defaultMessage: "Locale"
691
+ }) }),
692
+ /* @__PURE__ */ jsx(
693
+ SingleSelect,
694
+ {
695
+ value: localeSelected,
696
+ placeholder: formatMessage({
697
+ id: getTranslation("CMEditViewCopyLocale.dialog.field.placeholder"),
698
+ defaultMessage: "Select one locale..."
699
+ }),
700
+ onChange: (value) => setLocaleSelected(value),
701
+ children: availableLocales.map((locale) => /* @__PURE__ */ jsx(SingleSelectOption, { value: locale.code, children: locale.name }, locale.code))
702
+ }
703
+ )
704
+ ] })
705
+ ] }) }),
706
+ /* @__PURE__ */ jsx(Dialog.Footer, { children: /* @__PURE__ */ jsxs(Flex, { gap: 2, width: "100%", children: [
707
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "tertiary", onClick: onClose, children: formatMessage({
708
+ id: getTranslation("CMEditViewCopyLocale.cancel-text"),
709
+ defaultMessage: "No, cancel"
710
+ }) }),
711
+ /* @__PURE__ */ jsx(Button, { flex: "auto", variant: "success", onClick: fillFromLocale(onClose), children: formatMessage({
712
+ id: getTranslation("CMEditViewCopyLocale.submit-text"),
713
+ defaultMessage: "Yes, fill in"
714
+ }) })
715
+ ] }) })
716
+ ] })
717
+ }
718
+ };
719
+ };
464
720
  const DeleteLocaleAction = ({
465
721
  document,
466
722
  documentId,
@@ -472,16 +728,23 @@ const DeleteLocaleAction = ({
472
728
  const { toggleNotification } = useNotification();
473
729
  const { delete: deleteAction } = unstable_useDocumentActions();
474
730
  const { hasI18n, canDelete } = useI18n();
731
+ const [{ query }] = useQueryParams();
732
+ const { data: locales = [] } = useGetLocalesQuery();
733
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
734
+ const locale = !("error" in locales) && locales.find((loc) => loc.code === currentDesiredLocale);
475
735
  if (!hasI18n) {
476
736
  return null;
477
737
  }
478
738
  return {
479
739
  disabled: document?.locale && !canDelete.includes(document.locale) || !document || !document.id,
480
740
  position: ["header", "table-row"],
481
- label: formatMessage({
482
- id: getTranslation("actions.delete.label"),
483
- defaultMessage: "Delete locale"
484
- }),
741
+ label: formatMessage(
742
+ {
743
+ id: getTranslation("actions.delete.label"),
744
+ defaultMessage: "Delete entry ({locale})"
745
+ },
746
+ { locale: locale && locale.name }
747
+ ),
485
748
  icon: /* @__PURE__ */ jsx(StyledTrash, {}),
486
749
  variant: "danger",
487
750
  dialog: {
@@ -498,7 +761,12 @@ const DeleteLocaleAction = ({
498
761
  }) })
499
762
  ] }),
500
763
  onConfirm: async () => {
501
- if (!documentId || !document?.locale) {
764
+ const unableToDelete = (
765
+ // We are unable to delete a collection type without a document ID
766
+ // & unable to delete generally if there is no document locale
767
+ collectionType !== "single-types" && !documentId || !document?.locale
768
+ );
769
+ if (unableToDelete) {
502
770
  console.error(
503
771
  "You're trying to delete a document without an id or locale, this is likely a bug with Strapi. Please open an issue."
504
772
  );
@@ -524,37 +792,43 @@ const DeleteLocaleAction = ({
524
792
  }
525
793
  };
526
794
  };
527
- const BulkLocalePublishAction = ({
795
+ const BulkLocaleAction = ({
528
796
  document: baseDocument,
529
797
  documentId,
530
798
  model,
531
- collectionType
799
+ collectionType,
800
+ action
532
801
  }) => {
533
802
  const baseLocale = baseDocument?.locale ?? null;
534
803
  const [{ query }] = useQueryParams();
535
804
  const params = React.useMemo(() => buildValidParams(query), [query]);
536
- const isPublishedTab = query.status === "published";
805
+ const isOnPublishedTab = query.status === "published";
537
806
  const { formatMessage } = useIntl();
538
807
  const { hasI18n, canPublish } = useI18n();
539
808
  const { toggleNotification } = useNotification();
540
809
  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
541
810
  const [selectedRows, setSelectedRows] = React.useState([]);
542
811
  const [isDraftRelationConfirmationOpen, setIsDraftRelationConfirmationOpen] = React.useState(false);
543
- const { publishMany: publishManyAction } = unstable_useDocumentActions();
812
+ const { publishMany: publishManyAction, unpublishMany: unpublishManyAction } = unstable_useDocumentActions();
544
813
  const {
545
814
  document,
546
815
  meta: documentMeta,
547
816
  schema,
548
817
  validate
549
- } = unstable_useDocument({
550
- model,
551
- collectionType,
552
- documentId,
553
- params: {
554
- locale: baseLocale
818
+ } = unstable_useDocument(
819
+ {
820
+ model,
821
+ collectionType,
822
+ documentId,
823
+ params: {
824
+ locale: baseLocale
825
+ }
826
+ },
827
+ {
828
+ skip: !hasI18n || !baseLocale
555
829
  }
556
- });
557
- const { data: localesMetadata = [] } = useGetLocalesQuery();
830
+ );
831
+ const { data: localesMetadata = [] } = useGetLocalesQuery(hasI18n ? void 0 : skipToken);
558
832
  const headers = [
559
833
  {
560
834
  label: formatMessage({
@@ -603,12 +877,19 @@ const BulkLocalePublishAction = ({
603
877
  }, {});
604
878
  return [rowsFromMeta, errors];
605
879
  }, [document, documentMeta?.availableLocales, validate]);
606
- const localesToPublish = selectedRows.reduce((acc, selectedRow) => {
607
- if (selectedRow.status !== "published" && !Object.keys(validationErrors).includes(selectedRow.locale)) {
880
+ const isBulkPublish = action === "bulk-publish";
881
+ const localesForAction = selectedRows.reduce((acc, selectedRow) => {
882
+ const isValidLocale = (
883
+ // Validation errors are irrelevant if we are trying to unpublish
884
+ !isBulkPublish || !Object.keys(validationErrors).includes(selectedRow.locale)
885
+ );
886
+ const shouldAddLocale = isBulkPublish ? selectedRow.status !== "published" && isValidLocale : selectedRow.status !== "draft" && isValidLocale;
887
+ if (shouldAddLocale) {
608
888
  acc.push(selectedRow.locale);
609
889
  }
610
890
  return acc;
611
891
  }, []);
892
+ const enableDraftRelationsCount = false;
612
893
  const {
613
894
  data: draftRelationsCount = 0,
614
895
  isLoading: isDraftRelationsLoading,
@@ -617,10 +898,10 @@ const BulkLocalePublishAction = ({
617
898
  {
618
899
  model,
619
900
  documentIds: [documentId],
620
- locale: localesToPublish
901
+ locale: localesForAction
621
902
  },
622
903
  {
623
- skip: !documentId || localesToPublish.length === 0
904
+ skip: !enableDraftRelationsCount
624
905
  }
625
906
  );
626
907
  React.useEffect(() => {
@@ -646,7 +927,18 @@ const BulkLocalePublishAction = ({
646
927
  documentIds: [documentId],
647
928
  params: {
648
929
  ...params,
649
- locale: localesToPublish
930
+ locale: localesForAction
931
+ }
932
+ });
933
+ setSelectedRows([]);
934
+ };
935
+ const unpublish = async () => {
936
+ await unpublishManyAction({
937
+ model,
938
+ documentIds: [documentId],
939
+ params: {
940
+ ...params,
941
+ locale: localesForAction
650
942
  }
651
943
  });
652
944
  setSelectedRows([]);
@@ -654,14 +946,12 @@ const BulkLocalePublishAction = ({
654
946
  const handleAction = async () => {
655
947
  if (draftRelationsCount > 0) {
656
948
  setIsDraftRelationConfirmationOpen(true);
657
- } else {
949
+ } else if (isBulkPublish) {
658
950
  await publish();
951
+ } else {
952
+ await unpublish();
659
953
  }
660
954
  };
661
- const isUnpublish = document?.status === "published";
662
- if (isUnpublish) {
663
- console.warn(["I18N"], "Bulk locale unpublish modal not implemented");
664
- }
665
955
  if (isDraftRelationConfirmationOpen) {
666
956
  return {
667
957
  label: formatMessage({
@@ -696,20 +986,21 @@ const BulkLocalePublishAction = ({
696
986
  }
697
987
  };
698
988
  }
989
+ const hasPermission = selectedRows.map(({ locale }) => locale).every((locale) => canPublish.includes(locale));
699
990
  return {
700
991
  label: formatMessage({
701
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
702
- defaultMessage: "Publish Multiple Locales"
992
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
993
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
703
994
  }),
704
- icon: /* @__PURE__ */ jsx(ListPlus, {}),
705
- disabled: isPublishedTab || !canPublish,
995
+ variant: isBulkPublish ? "secondary" : "danger",
996
+ icon: isBulkPublish ? /* @__PURE__ */ jsx(ListPlus, {}) : /* @__PURE__ */ jsx(Cross, {}),
997
+ disabled: isOnPublishedTab || canPublish.length === 0,
706
998
  position: ["panel"],
707
- variant: "secondary",
708
999
  dialog: {
709
1000
  type: "modal",
710
1001
  title: formatMessage({
711
- id: getTranslation("CMEditViewBulkLocale.publish-title"),
712
- defaultMessage: "Publish Multiple Locales"
1002
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? "publish" : "unpublish"}-title`),
1003
+ defaultMessage: `${isBulkPublish ? "Publish" : "Unpublish"} Multiple Locales`
713
1004
  }),
714
1005
  content: () => {
715
1006
  return /* @__PURE__ */ jsx(
@@ -728,28 +1019,35 @@ const BulkLocalePublishAction = ({
728
1019
  validationErrors,
729
1020
  headers,
730
1021
  rows,
731
- localesMetadata
1022
+ localesMetadata,
1023
+ action: action ?? "bulk-publish"
732
1024
  }
733
1025
  )
734
1026
  }
735
1027
  );
736
1028
  },
737
- footer: () => /* @__PURE__ */ jsx(Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
738
- Button$1,
1029
+ footer: () => /* @__PURE__ */ jsx(Modal.Footer, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
1030
+ Button,
739
1031
  {
740
1032
  loading: isDraftRelationsLoading,
741
- disabled: localesToPublish.length === 0,
1033
+ disabled: !hasPermission || localesForAction.length === 0,
742
1034
  variant: "default",
743
1035
  onClick: handleAction,
744
1036
  children: formatMessage({
745
- id: "app.utils.publish",
746
- defaultMessage: "Publish"
1037
+ id: isBulkPublish ? "app.utils.publish" : "app.utils.unpublish",
1038
+ defaultMessage: isBulkPublish ? "Publish" : "Unpublish"
747
1039
  })
748
1040
  }
749
1041
  ) })
750
1042
  }
751
1043
  };
752
1044
  };
1045
+ const BulkLocalePublishAction = (props) => {
1046
+ return BulkLocaleAction({ action: "bulk-publish", ...props });
1047
+ };
1048
+ const BulkLocaleUnpublishAction = (props) => {
1049
+ return BulkLocaleAction({ action: "bulk-unpublish", ...props });
1050
+ };
753
1051
  const StyledTrash = styled(Trash)`
754
1052
  path {
755
1053
  fill: currentColor;
@@ -806,13 +1104,6 @@ const UnpublishModalAdditionalInfo = () => {
806
1104
  }
807
1105
  ) });
808
1106
  };
809
- const Initializer = ({ setPlugin }) => {
810
- const setPluginRef = React.useRef(setPlugin);
811
- React.useEffect(() => {
812
- setPluginRef.current(pluginId);
813
- }, []);
814
- return null;
815
- };
816
1107
  const LocalePicker = () => {
817
1108
  const { formatMessage } = useIntl();
818
1109
  const [{ query }, setQuery] = useQueryParams();
@@ -976,54 +1267,13 @@ const LocaleListCell = ({
976
1267
  return locale.name;
977
1268
  }).toSorted((a, b) => formatter.compare(a, b));
978
1269
  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
- ) }) }),
1270
+ /* @__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: [
1271
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", ellipsis: true, marginRight: 2, children: localesForDocument.join(", ") }),
1272
+ /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(CaretDown, { width: "1.2rem", height: "1.2rem" }) })
1273
+ ] }) }) }),
993
1274
  /* @__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
1275
  ] });
995
1276
  };
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
1277
  const addColumnToTableHook = ({ displayedHeaders, layout }) => {
1028
1278
  const { options } = layout;
1029
1279
  const isFieldLocalized = doesPluginOptionsHaveI18nLocalized(options) ? options.i18n.localized : false;
@@ -1149,9 +1399,6 @@ const localeMiddleware = (ctx) => (next) => (permissions) => {
1149
1399
  return next(revisedPermissions);
1150
1400
  };
1151
1401
  const prefixPluginTranslations = (trad, pluginId2) => {
1152
- if (!pluginId2) {
1153
- throw new TypeError("pluginId can't be empty");
1154
- }
1155
1402
  return Object.keys(trad).reduce((acc, current) => {
1156
1403
  acc[`${pluginId2}.${current}`] = trad[current];
1157
1404
  return acc;
@@ -1204,8 +1451,6 @@ const index = {
1204
1451
  app.addRBACMiddleware([localeMiddleware]);
1205
1452
  app.registerPlugin({
1206
1453
  id: pluginId,
1207
- initializer: Initializer,
1208
- isReady: false,
1209
1454
  name: pluginId
1210
1455
  });
1211
1456
  },
@@ -1223,11 +1468,11 @@ const index = {
1223
1468
  },
1224
1469
  id: "internationalization",
1225
1470
  to: "internationalization",
1226
- Component: () => import("./SettingsPage-B5sTcP82.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1471
+ Component: () => import("./SettingsPage-BHvunuIF.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1227
1472
  permissions: PERMISSIONS.accessMain
1228
1473
  });
1229
1474
  const contentManager = app.getPlugin("content-manager");
1230
- contentManager.apis.addDocumentHeaderAction([LocalePickerAction]);
1475
+ contentManager.apis.addDocumentHeaderAction([LocalePickerAction, FillFromAnotherLocaleAction]);
1231
1476
  contentManager.apis.addDocumentAction((actions) => {
1232
1477
  const indexOfDeleteAction = actions.findIndex((action) => action.type === "delete");
1233
1478
  actions.splice(indexOfDeleteAction, 0, DeleteLocaleAction);
@@ -1235,6 +1480,7 @@ const index = {
1235
1480
  });
1236
1481
  contentManager.apis.addDocumentAction((actions) => {
1237
1482
  actions.splice(2, 0, BulkLocalePublishAction);
1483
+ actions.splice(5, 0, BulkLocaleUnpublishAction);
1238
1484
  return actions;
1239
1485
  });
1240
1486
  contentManager.injectComponent("listView", "actions", {
@@ -1340,7 +1586,7 @@ const index = {
1340
1586
  async registerTrads({ locales }) {
1341
1587
  const importedTrads = await Promise.all(
1342
1588
  locales.map((locale) => {
1343
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/de.json": () => import("./de-9eCAqqrB.mjs"), "./translations/dk.json": () => import("./dk-2qBjxt-P.mjs"), "./translations/en.json": () => import("./en-CM6Pjfyv.mjs"), "./translations/es.json": () => import("./es-DlmMVaBG.mjs"), "./translations/fr.json": () => import("./fr-3S6ke71d.mjs"), "./translations/ko.json": () => import("./ko-qTjQ8IMw.mjs"), "./translations/pl.json": () => import("./pl-B67TSHqT.mjs"), "./translations/ru.json": () => import("./ru-hagMa57T.mjs"), "./translations/tr.json": () => import("./tr-Dw_jmkG-.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-Dyc-aR-h.mjs"), "./translations/zh.json": () => import("./zh-57YM4amO.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
1589
+ 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 }) => {
1344
1590
  return {
1345
1591
  data: prefixPluginTranslations(data, pluginId),
1346
1592
  locale
@@ -1366,4 +1612,4 @@ export {
1366
1612
  index as i,
1367
1613
  useCreateLocaleMutation as u
1368
1614
  };
1369
- //# sourceMappingURL=index-DsNqyQKx.mjs.map
1615
+ //# sourceMappingURL=index-DUdrr5PR.mjs.map