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

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 (33) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/{SettingsPage-CeqfDjsb.mjs → SettingsPage-CsGvujny.mjs} +115 -126
  3. package/dist/_chunks/SettingsPage-CsGvujny.mjs.map +1 -0
  4. package/dist/_chunks/{SettingsPage-Djqsdrzs.js → SettingsPage-DA9haizZ.js} +115 -126
  5. package/dist/_chunks/SettingsPage-DA9haizZ.js.map +1 -0
  6. package/dist/_chunks/{en-BuBc6LKZ.js → en-BsOU9o5z.js} +6 -1
  7. package/dist/_chunks/en-BsOU9o5z.js.map +1 -0
  8. package/dist/_chunks/{en-CnrTsjWS.mjs → en-CM6Pjfyv.mjs} +6 -1
  9. package/dist/_chunks/en-CM6Pjfyv.mjs.map +1 -0
  10. package/dist/_chunks/{index-BDU1w_fd.mjs → index-CCZJF_EJ.mjs} +461 -152
  11. package/dist/_chunks/index-CCZJF_EJ.mjs.map +1 -0
  12. package/dist/_chunks/{index-DMXJeGjN.js → index-DIzVXZoE.js} +467 -160
  13. package/dist/_chunks/index-DIzVXZoE.js.map +1 -0
  14. package/dist/admin/index.js +1 -1
  15. package/dist/admin/index.mjs +2 -2
  16. package/dist/admin/src/components/BulkLocaleActionModal.d.ts +15 -0
  17. package/dist/admin/src/components/CMHeaderActions.d.ts +7 -1
  18. package/dist/admin/src/components/EditLocale.d.ts +5 -4
  19. package/dist/admin/src/contentReleasesHooks/releaseDetailsView.d.ts +9 -5
  20. package/dist/admin/src/services/api.d.ts +2 -3
  21. package/dist/admin/src/services/locales.d.ts +1 -1
  22. package/dist/admin/src/services/relations.d.ts +7 -0
  23. package/dist/admin/src/utils/baseQuery.d.ts +4 -19
  24. package/dist/admin/src/utils/schemas.d.ts +1 -0
  25. package/dist/shared/contracts/content-manager.d.ts +20 -1
  26. package/package.json +15 -16
  27. package/dist/_chunks/SettingsPage-CeqfDjsb.mjs.map +0 -1
  28. package/dist/_chunks/SettingsPage-Djqsdrzs.js.map +0 -1
  29. package/dist/_chunks/en-BuBc6LKZ.js.map +0 -1
  30. package/dist/_chunks/en-CnrTsjWS.mjs.map +0 -1
  31. package/dist/_chunks/index-BDU1w_fd.mjs.map +0 -1
  32. package/dist/_chunks/index-DMXJeGjN.js.map +0 -1
  33. package/dist/admin/src/components/Initializer.d.ts +0 -5
@@ -1,17 +1,17 @@
1
1
  import get from "lodash/get";
2
2
  import * as yup from "yup";
3
- import { jsxs, Fragment, jsx } from "react/jsx-runtime";
3
+ import { jsxs, jsx } from "react/jsx-runtime";
4
4
  import * as React from "react";
5
- import { Typography, Checkbox, Dialog, DialogBody, Flex, DialogFooter, Button as Button$1, Status, SingleSelect, SingleSelectOption, VisuallyHidden, useCollator, Popover, Box } from "@strapi/design-system";
6
- import { WarningCircle, Trash, Earth, EarthStriked, CaretDown } from "@strapi/icons";
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";
7
7
  import { useIntl } from "react-intl";
8
- import styled from "styled-components";
9
- import { useAuth, getFetchClient, useQueryParams, useNotification } from "@strapi/admin/strapi-admin";
10
- import { unstable_useDocument, unstable_useDocumentActions } from "@strapi/content-manager/strapi-admin";
11
- import { useParams, useNavigate, matchPath } from "react-router-dom";
12
- import { createApi } from "@reduxjs/toolkit/query/react";
13
- import { isAxiosError } from "axios";
8
+ import { styled } from "styled-components";
9
+ import { skipToken } from "@reduxjs/toolkit/query";
10
+ import { useAuth, adminApi, useTable, Table, useQueryParams, useNotification, useAPIErrorHandler } from "@strapi/admin/strapi-admin";
11
+ import { unstable_useDocument, unstable_useDocumentActions, buildValidParams } from "@strapi/content-manager/strapi-admin";
12
+ import { useParams, Link, useNavigate, matchPath } from "react-router-dom";
14
13
  import * as qs from "qs";
14
+ import { stringify } from "qs";
15
15
  import omit from "lodash/omit";
16
16
  const __variableDynamicImportRuntimeHelper = (glob, path) => {
17
17
  const v = glob[path];
@@ -48,9 +48,7 @@ const CheckboxConfirmation = ({
48
48
  };
49
49
  const handleConfirm = () => {
50
50
  onChange({ target: { name, value: false, type: "checkbox" } });
51
- setIsOpen(false);
52
51
  };
53
- const handleToggle = () => setIsOpen((prev) => !prev);
54
52
  const label = intlLabel.id ? formatMessage(
55
53
  { id: intlLabel.id, defaultMessage: intlLabel.defaultMessage },
56
54
  { ...intlLabel.values }
@@ -59,43 +57,36 @@ const CheckboxConfirmation = ({
59
57
  { id: description.id, defaultMessage: description.defaultMessage },
60
58
  { ...description.values }
61
59
  ) : "";
62
- return /* @__PURE__ */ jsxs(Fragment, { children: [
63
- /* @__PURE__ */ jsx(
64
- Checkbox,
65
- {
66
- hint,
67
- id: name,
68
- name,
69
- onValueChange: handleChange,
70
- value,
71
- type: "checkbox",
72
- children: label
73
- }
74
- ),
75
- isOpen && /* @__PURE__ */ jsxs(Dialog, { onClose: handleToggle, title: "Confirmation", isOpen, children: [
76
- /* @__PURE__ */ jsx(DialogBody, { icon: /* @__PURE__ */ jsx(WarningCircle, {}), children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
77
- /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(TextAlignTypography, { id: "confirm-description", children: formatMessage({
60
+ return /* @__PURE__ */ jsxs(Dialog.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
61
+ /* @__PURE__ */ jsxs(Field.Root, { hint, name, children: [
62
+ /* @__PURE__ */ jsx(Checkbox, { onCheckedChange: handleChange, checked: value, children: label }),
63
+ /* @__PURE__ */ jsx(Field.Hint, {})
64
+ ] }),
65
+ /* @__PURE__ */ jsxs(Dialog.Content, { children: [
66
+ /* @__PURE__ */ jsx(Dialog.Header, { children: formatMessage({
67
+ id: getTranslation("CheckboxConfirmation.Modal.title"),
68
+ defaultMessage: "Disable localization"
69
+ }) }),
70
+ /* @__PURE__ */ jsx(Dialog.Body, { icon: /* @__PURE__ */ jsx(WarningCircle, {}), children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
71
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(TextAlignTypography, { children: formatMessage({
78
72
  id: getTranslation("CheckboxConfirmation.Modal.content"),
79
73
  defaultMessage: "Disabling localization will engender the deletion of all your content but the one associated to your default locale (if existing)."
80
74
  }) }) }),
81
- /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", id: "confirm-description", children: formatMessage({
75
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "center", children: /* @__PURE__ */ jsx(Typography, { fontWeight: "semiBold", children: formatMessage({
82
76
  id: getTranslation("CheckboxConfirmation.Modal.body"),
83
77
  defaultMessage: "Do you want to disable it?"
84
78
  }) }) })
85
79
  ] }) }),
86
- /* @__PURE__ */ jsx(
87
- DialogFooter,
88
- {
89
- startAction: /* @__PURE__ */ jsx(Button$1, { onClick: handleToggle, variant: "tertiary", children: formatMessage({
90
- id: "components.popUpWarning.button.cancel",
91
- defaultMessage: "No, cancel"
92
- }) }),
93
- endAction: /* @__PURE__ */ jsx(Button$1, { variant: "danger-light", onClick: handleConfirm, children: formatMessage({
94
- id: getTranslation("CheckboxConfirmation.Modal.button-confirm"),
95
- defaultMessage: "Yes, disable"
96
- }) })
97
- }
98
- )
80
+ /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
81
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button$1, { variant: "tertiary", children: formatMessage({
82
+ id: "components.popUpWarning.button.cancel",
83
+ defaultMessage: "No, cancel"
84
+ }) }) }),
85
+ /* @__PURE__ */ jsx(Dialog.Action, { children: /* @__PURE__ */ jsx(Button$1, { variant: "danger-light", onClick: handleConfirm, children: formatMessage({
86
+ id: getTranslation("CheckboxConfirmation.Modal.button-confirm"),
87
+ defaultMessage: "Yes, disable"
88
+ }) }) })
89
+ ] })
99
90
  ] })
100
91
  ] });
101
92
  };
@@ -146,7 +137,7 @@ const useI18n = () => {
146
137
  model: params.slug
147
138
  },
148
139
  {
149
- skip: !params.slug || !params.collectionType
140
+ skip: true
150
141
  }
151
142
  );
152
143
  if (doesPluginOptionsHaveI18nLocalized(schema?.pluginOptions)) {
@@ -160,64 +151,8 @@ const useI18n = () => {
160
151
  ...actions
161
152
  };
162
153
  };
163
- const axiosBaseQuery = () => async (query, { signal }) => {
164
- try {
165
- const { get: get2, post, del, put } = getFetchClient();
166
- if (typeof query === "string") {
167
- const result = await get2(query, { signal });
168
- return { data: result.data };
169
- } else {
170
- const { url, method = "GET", data, config } = query;
171
- if (method === "POST") {
172
- const result2 = await post(url, data, { ...config, signal });
173
- return { data: result2.data };
174
- }
175
- if (method === "DELETE") {
176
- const result2 = await del(url, { ...config, signal });
177
- return { data: result2.data };
178
- }
179
- if (method === "PUT") {
180
- const result2 = await put(url, data, { ...config, signal });
181
- return { data: result2.data };
182
- }
183
- const result = await get2(url, { ...config, signal });
184
- return { data: result.data };
185
- }
186
- } catch (err) {
187
- if (isAxiosError(err)) {
188
- if (typeof err.response?.data === "object" && err.response?.data !== null && "error" in err.response?.data) {
189
- return { data: void 0, error: err.response?.data.error };
190
- } else {
191
- return {
192
- data: void 0,
193
- error: {
194
- name: "UnknownError",
195
- message: "There was an unknown error response from the API",
196
- details: err.response?.data,
197
- status: err.response?.status
198
- }
199
- };
200
- }
201
- }
202
- const error = err;
203
- return {
204
- data: void 0,
205
- error: {
206
- name: error.name,
207
- message: error.message,
208
- stack: error.stack
209
- }
210
- };
211
- }
212
- };
213
- const isBaseQueryError = (error) => {
214
- return error.name !== void 0;
215
- };
216
- const i18nApi = createApi({
217
- reducerPath: "i18nApi",
218
- baseQuery: axiosBaseQuery(),
219
- tagTypes: ["Locale"],
220
- endpoints: () => ({})
154
+ const i18nApi = adminApi.enhanceEndpoints({
155
+ addTagTypes: ["Locale"]
221
156
  });
222
157
  const localesApi = i18nApi.injectEndpoints({
223
158
  endpoints: (builder) => ({
@@ -266,6 +201,176 @@ const {
266
201
  useGetDefaultLocalesQuery,
267
202
  useUpdateLocaleMutation
268
203
  } = localesApi;
204
+ const relationsApi = i18nApi.injectEndpoints({
205
+ overrideExisting: true,
206
+ endpoints: (builder) => ({
207
+ getManyDraftRelationCount: builder.query({
208
+ query: ({ model, ...params }) => ({
209
+ url: `/content-manager/collection-types/${model}/actions/countManyEntriesDraftRelations`,
210
+ method: "GET",
211
+ config: {
212
+ params
213
+ }
214
+ }),
215
+ transformResponse: (response) => response.data
216
+ })
217
+ })
218
+ });
219
+ const { useGetManyDraftRelationCountQuery } = relationsApi;
220
+ const isErrorMessageDescriptor = (object) => {
221
+ return typeof object === "object" && object !== null && "id" in object && "defaultMessage" in object;
222
+ };
223
+ const EntryValidationText = ({ status = "draft", validationErrors }) => {
224
+ const { formatMessage } = useIntl();
225
+ const getErrorStr = (key, value) => {
226
+ if (typeof value === "string") {
227
+ return `${key}: ${value}`;
228
+ } else if (isErrorMessageDescriptor(value)) {
229
+ return `${key}: ${formatMessage(value)}`;
230
+ } else if (Array.isArray(value)) {
231
+ return value.map((v) => getErrorStr(key, v)).join(" ");
232
+ } else if (typeof value === "object" && !Array.isArray(value)) {
233
+ return Object.entries(value).map(([k, v]) => getErrorStr(k, v)).join(" ");
234
+ } else {
235
+ return "";
236
+ }
237
+ };
238
+ if (validationErrors) {
239
+ const validationErrorsMessages = Object.entries(validationErrors).map(([key, value]) => {
240
+ return getErrorStr(key, value);
241
+ }).join(" ");
242
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
243
+ /* @__PURE__ */ jsx(CrossCircle, { fill: "danger600" }),
244
+ /* @__PURE__ */ jsx(Tooltip, { label: validationErrorsMessages, children: /* @__PURE__ */ jsx(
245
+ Typography,
246
+ {
247
+ maxWidth: "30rem",
248
+ textColor: "danger600",
249
+ variant: "omega",
250
+ fontWeight: "semiBold",
251
+ ellipsis: true,
252
+ children: validationErrorsMessages
253
+ }
254
+ ) })
255
+ ] });
256
+ }
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
+ }
275
+ 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
+ }) })
281
+ ] });
282
+ };
283
+ const BoldChunk = (chunks) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: chunks });
284
+ const BulkLocaleActionModal = ({
285
+ headers,
286
+ rows,
287
+ localesMetadata,
288
+ validationErrors = {}
289
+ }) => {
290
+ const { formatMessage } = useIntl();
291
+ const selectedRows = useTable(
292
+ "BulkLocaleActionModal",
293
+ (state) => state.selectedRows
294
+ );
295
+ const getFormattedCountMessage = () => {
296
+ const currentStatusByLocale = rows.reduce((acc, { locale, status }) => {
297
+ acc[locale] = status;
298
+ return acc;
299
+ }, {});
300
+ const localesWithErrors = Object.keys(validationErrors);
301
+ const alreadyPublishedCount = selectedRows.filter(
302
+ ({ locale }) => currentStatusByLocale[locale] === "published"
303
+ ).length;
304
+ const readyToPublishCount = selectedRows.filter(
305
+ ({ locale }) => (currentStatusByLocale[locale] === "draft" || currentStatusByLocale[locale] === "modified") && !localesWithErrors.includes(locale)
306
+ ).length;
307
+ const withErrorsCount = localesWithErrors.length;
308
+ return formatMessage(
309
+ {
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."
312
+ },
313
+ {
314
+ withErrorsCount,
315
+ readyToPublishCount,
316
+ alreadyPublishedCount,
317
+ b: BoldChunk
318
+ }
319
+ );
320
+ };
321
+ return /* @__PURE__ */ jsxs(Modal.Body, { children: [
322
+ /* @__PURE__ */ jsx(Typography, { children: getFormattedCountMessage() }),
323
+ /* @__PURE__ */ jsx(Box, { marginTop: 5, children: /* @__PURE__ */ jsxs(Table.Content, { children: [
324
+ /* @__PURE__ */ jsxs(Table.Head, { children: [
325
+ /* @__PURE__ */ jsx(Table.HeaderCheckboxCell, {}),
326
+ headers.map((head) => /* @__PURE__ */ jsx(Table.HeaderCell, { ...head }, head.name))
327
+ ] }),
328
+ /* @__PURE__ */ jsx(Table.Body, { children: rows.map(({ locale, status }, index2) => {
329
+ const error = validationErrors?.[locale] ?? null;
330
+ const statusVariant = status === "draft" ? "primary" : status === "published" ? "success" : "alternative";
331
+ return /* @__PURE__ */ jsxs(Table.Row, { children: [
332
+ /* @__PURE__ */ jsx(Table.CheckboxCell, { id: locale, "aria-label": `Select ${locale}` }),
333
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: Array.isArray(localesMetadata) ? localesMetadata.find((localeEntry) => localeEntry.code === locale)?.name : locale }) }),
334
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Box, { display: "flex", children: /* @__PURE__ */ jsx(
335
+ Status,
336
+ {
337
+ display: "flex",
338
+ paddingLeft: "6px",
339
+ paddingRight: "6px",
340
+ paddingTop: "2px",
341
+ paddingBottom: "2px",
342
+ showBullet: false,
343
+ size: "S",
344
+ variant: statusVariant,
345
+ children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
346
+ }
347
+ ) }) }),
348
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(EntryValidationText, { validationErrors: error, status }) }),
349
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
350
+ IconButton,
351
+ {
352
+ tag: Link,
353
+ to: {
354
+ search: stringify({ plugins: { i18n: { locale } } })
355
+ },
356
+ label: formatMessage(
357
+ {
358
+ id: getTranslation("Settings.list.actions.edit"),
359
+ defaultMessage: "Edit {name} locale"
360
+ },
361
+ {
362
+ name: locale
363
+ }
364
+ ),
365
+ borderWidth: 0,
366
+ children: /* @__PURE__ */ jsx(Pencil, {})
367
+ }
368
+ ) })
369
+ ] }, index2);
370
+ }) })
371
+ ] }) })
372
+ ] });
373
+ };
269
374
  const LocalePickerAction = ({
270
375
  document,
271
376
  meta,
@@ -337,7 +442,7 @@ const LocalePickerAction = ({
337
442
  showBullet: false,
338
443
  size: "S",
339
444
  variant: statusVariant,
340
- children: /* @__PURE__ */ jsx(Typography, { as: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
445
+ children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "pi", fontWeight: "bold", children: capitalize(status) })
341
446
  }
342
447
  ) : null
343
448
  };
@@ -388,7 +493,7 @@ const DeleteLocaleAction = ({
388
493
  }),
389
494
  content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
390
495
  /* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
391
- /* @__PURE__ */ jsx(Typography, { as: "p", variant: "omega", textAlign: "center", children: formatMessage({
496
+ /* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
392
497
  id: getTranslation("actions.delete.dialog.body"),
393
498
  defaultMessage: "Are you sure?"
394
499
  }) })
@@ -420,6 +525,238 @@ const DeleteLocaleAction = ({
420
525
  }
421
526
  };
422
527
  };
528
+ const BulkLocalePublishAction = ({
529
+ document: baseDocument,
530
+ documentId,
531
+ model,
532
+ collectionType
533
+ }) => {
534
+ const baseLocale = baseDocument?.locale ?? null;
535
+ const [{ query }] = useQueryParams();
536
+ const params = React.useMemo(() => buildValidParams(query), [query]);
537
+ const isPublishedTab = query.status === "published";
538
+ const { formatMessage } = useIntl();
539
+ const { hasI18n, canPublish } = useI18n();
540
+ const { toggleNotification } = useNotification();
541
+ const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
542
+ const [selectedRows, setSelectedRows] = React.useState([]);
543
+ const [isDraftRelationConfirmationOpen, setIsDraftRelationConfirmationOpen] = React.useState(false);
544
+ const { publishMany: publishManyAction } = unstable_useDocumentActions();
545
+ const {
546
+ document,
547
+ meta: documentMeta,
548
+ schema,
549
+ validate
550
+ } = unstable_useDocument(
551
+ {
552
+ model,
553
+ collectionType,
554
+ documentId,
555
+ params: {
556
+ locale: baseLocale
557
+ }
558
+ },
559
+ {
560
+ skip: !hasI18n
561
+ }
562
+ );
563
+ const { data: localesMetadata = [] } = useGetLocalesQuery(hasI18n ? void 0 : skipToken);
564
+ const headers = [
565
+ {
566
+ label: formatMessage({
567
+ id: "global.name",
568
+ defaultMessage: "Name"
569
+ }),
570
+ name: "name"
571
+ },
572
+ {
573
+ label: formatMessage({
574
+ id: getTranslation("CMEditViewBulkLocale.status"),
575
+ defaultMessage: "Status"
576
+ }),
577
+ name: "status"
578
+ },
579
+ {
580
+ label: formatMessage({
581
+ id: getTranslation("CMEditViewBulkLocale.publication-status"),
582
+ defaultMessage: "Publication Status"
583
+ }),
584
+ name: "publication-status"
585
+ }
586
+ ];
587
+ const [rows, validationErrors] = React.useMemo(() => {
588
+ if (!document || !documentMeta?.availableLocales) {
589
+ return [[], {}];
590
+ }
591
+ const rowsFromMeta = documentMeta?.availableLocales.map((doc) => {
592
+ const { locale, status } = doc;
593
+ return { locale, status };
594
+ });
595
+ rowsFromMeta.unshift({
596
+ locale: document.locale,
597
+ status: document.status
598
+ });
599
+ const allDocuments = [document, ...documentMeta?.availableLocales ?? []];
600
+ const errors = allDocuments.reduce((errs, document2) => {
601
+ if (!document2) {
602
+ return errs;
603
+ }
604
+ const validation = validate(document2);
605
+ if (validation !== null) {
606
+ errs[document2.locale] = validation;
607
+ }
608
+ return errs;
609
+ }, {});
610
+ return [rowsFromMeta, errors];
611
+ }, [document, documentMeta?.availableLocales, validate]);
612
+ const localesToPublish = selectedRows.reduce((acc, selectedRow) => {
613
+ if (selectedRow.status !== "published" && !Object.keys(validationErrors).includes(selectedRow.locale)) {
614
+ acc.push(selectedRow.locale);
615
+ }
616
+ return acc;
617
+ }, []);
618
+ const {
619
+ data: draftRelationsCount = 0,
620
+ isLoading: isDraftRelationsLoading,
621
+ error: isDraftRelationsError
622
+ } = useGetManyDraftRelationCountQuery(
623
+ {
624
+ model,
625
+ documentIds: [documentId],
626
+ locale: localesToPublish
627
+ },
628
+ {
629
+ skip: !documentId || localesToPublish.length === 0
630
+ }
631
+ );
632
+ React.useEffect(() => {
633
+ if (isDraftRelationsError) {
634
+ toggleNotification({
635
+ type: "danger",
636
+ message: formatAPIError(isDraftRelationsError)
637
+ });
638
+ }
639
+ }, [isDraftRelationsError, toggleNotification, formatAPIError]);
640
+ if (!schema?.options?.draftAndPublish) {
641
+ return null;
642
+ }
643
+ if (!hasI18n) {
644
+ return null;
645
+ }
646
+ if (!documentId) {
647
+ return null;
648
+ }
649
+ const publish = async () => {
650
+ await publishManyAction({
651
+ model,
652
+ documentIds: [documentId],
653
+ params: {
654
+ ...params,
655
+ locale: localesToPublish
656
+ }
657
+ });
658
+ setSelectedRows([]);
659
+ };
660
+ const handleAction = async () => {
661
+ if (draftRelationsCount > 0) {
662
+ setIsDraftRelationConfirmationOpen(true);
663
+ } else {
664
+ await publish();
665
+ }
666
+ };
667
+ const isUnpublish = document?.status === "published";
668
+ if (isUnpublish) {
669
+ console.warn(["I18N"], "Bulk locale unpublish modal not implemented");
670
+ }
671
+ if (isDraftRelationConfirmationOpen) {
672
+ return {
673
+ label: formatMessage({
674
+ id: "app.components.ConfirmDialog.title",
675
+ defaultMessage: "Confirmation"
676
+ }),
677
+ variant: "danger",
678
+ dialog: {
679
+ onCancel: () => {
680
+ setIsDraftRelationConfirmationOpen(false);
681
+ },
682
+ onConfirm: async () => {
683
+ await publish();
684
+ setIsDraftRelationConfirmationOpen(false);
685
+ },
686
+ type: "dialog",
687
+ title: formatMessage({
688
+ id: getTranslation("actions.publish.dialog.title"),
689
+ defaultMessage: "Confirmation"
690
+ }),
691
+ content: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "center", gap: 2, children: [
692
+ /* @__PURE__ */ jsx(WarningCircle, { width: "2.4rem", height: "2.4rem", fill: "danger600" }),
693
+ /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
694
+ id: getTranslation("CMEditViewBulkLocale.draft-relation-warning"),
695
+ defaultMessage: "Some locales are related to draft entries. Publishing them could leave broken links in your app."
696
+ }) }),
697
+ /* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage({
698
+ id: getTranslation("CMEditViewBulkLocale.continue-confirmation"),
699
+ defaultMessage: "Are you sure you want to continue?"
700
+ }) })
701
+ ] })
702
+ }
703
+ };
704
+ }
705
+ const hasPermission = selectedRows.map(({ locale }) => locale).every((locale) => canPublish.includes(locale));
706
+ return {
707
+ label: formatMessage({
708
+ id: getTranslation("CMEditViewBulkLocale.publish-title"),
709
+ defaultMessage: "Publish Multiple Locales"
710
+ }),
711
+ icon: /* @__PURE__ */ jsx(ListPlus, {}),
712
+ disabled: isPublishedTab || canPublish.length === 0,
713
+ position: ["panel"],
714
+ variant: "secondary",
715
+ dialog: {
716
+ type: "modal",
717
+ title: formatMessage({
718
+ id: getTranslation("CMEditViewBulkLocale.publish-title"),
719
+ defaultMessage: "Publish Multiple Locales"
720
+ }),
721
+ content: () => {
722
+ return /* @__PURE__ */ jsx(
723
+ Table.Root,
724
+ {
725
+ headers,
726
+ rows: rows.map((row) => ({
727
+ ...row,
728
+ id: row.locale
729
+ })),
730
+ selectedRows,
731
+ onSelectedRowsChange: (tableSelectedRows) => setSelectedRows(tableSelectedRows),
732
+ children: /* @__PURE__ */ jsx(
733
+ BulkLocaleActionModal,
734
+ {
735
+ validationErrors,
736
+ headers,
737
+ rows,
738
+ localesMetadata
739
+ }
740
+ )
741
+ }
742
+ );
743
+ },
744
+ footer: () => /* @__PURE__ */ jsx(Modal.Footer, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
745
+ Button$1,
746
+ {
747
+ loading: isDraftRelationsLoading,
748
+ disabled: !hasPermission || localesToPublish.length === 0,
749
+ variant: "default",
750
+ onClick: handleAction,
751
+ children: formatMessage({
752
+ id: "app.utils.publish",
753
+ defaultMessage: "Publish"
754
+ })
755
+ }
756
+ ) })
757
+ }
758
+ };
759
+ };
423
760
  const StyledTrash = styled(Trash)`
424
761
  path {
425
762
  fill: currentColor;
@@ -476,13 +813,6 @@ const UnpublishModalAdditionalInfo = () => {
476
813
  }
477
814
  ) });
478
815
  };
479
- const Initializer = ({ setPlugin }) => {
480
- const setPluginRef = React.useRef(setPlugin);
481
- React.useEffect(() => {
482
- setPluginRef.current(pluginId);
483
- }, []);
484
- return null;
485
- };
486
816
  const LocalePicker = () => {
487
817
  const { formatMessage } = useIntl();
488
818
  const [{ query }, setQuery] = useQueryParams();
@@ -542,7 +872,7 @@ const PERMISSIONS = {
542
872
  read: [{ action: "plugin::i18n.locale.read", subject: null }]
543
873
  };
544
874
  const mutateEditViewHook = ({ layout }) => {
545
- if ("i18n" in layout.options && typeof layout.options.i18n === "object" && layout.options.i18n !== null && "localized" in layout.options.i18n && !layout.options.i18n.localized) {
875
+ if (!("i18n" in layout.options) || typeof layout.options.i18n === "object" && layout.options.i18n !== null && "localized" in layout.options.i18n && !layout.options.i18n.localized) {
546
876
  return { layout };
547
877
  }
548
878
  const components = Object.entries(layout.components).reduce(
@@ -587,8 +917,8 @@ const doesFieldHaveI18nPluginOpt = (pluginOpts) => {
587
917
  };
588
918
  const LabelAction = ({ title, icon }) => {
589
919
  const { formatMessage } = useIntl();
590
- return /* @__PURE__ */ jsxs(Span, { as: "span", children: [
591
- /* @__PURE__ */ jsx(VisuallyHidden, { as: "span", children: `(${formatMessage(title)})` }),
920
+ return /* @__PURE__ */ jsxs(Span, { tag: "span", children: [
921
+ /* @__PURE__ */ jsx(VisuallyHidden, { tag: "span", children: formatMessage(title) }),
592
922
  React.cloneElement(icon, {
593
923
  "aria-hidden": true,
594
924
  focusable: false
@@ -623,13 +953,7 @@ const LocaleListCell = ({
623
953
  }
624
954
  });
625
955
  const { locale: language } = useIntl();
626
- const [visible, setVisible] = React.useState(false);
627
- const buttonRef = React.useRef(null);
628
956
  const { data: locales = [] } = useGetLocalesQuery();
629
- const handleTogglePopover = (e) => {
630
- e.stopPropagation();
631
- setVisible((prev) => !prev);
632
- };
633
957
  const formatter = useCollator(language, {
634
958
  sensitivity: "base"
635
959
  });
@@ -651,8 +975,8 @@ const LocaleListCell = ({
651
975
  }
652
976
  return locale.name;
653
977
  }).toSorted((a, b) => formatter.compare(a, b));
654
- return /* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleTogglePopover, ref: buttonRef, children: [
655
- /* @__PURE__ */ jsxs(
978
+ return /* @__PURE__ */ jsxs(Popover.Root, { children: [
979
+ /* @__PURE__ */ jsx(Popover.Trigger, { children: /* @__PURE__ */ jsx(Button, { type: "button", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs(
656
980
  ActionWrapper,
657
981
  {
658
982
  minWidth: "100%",
@@ -665,17 +989,8 @@ const LocaleListCell = ({
665
989
  /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(CaretDown, {}) })
666
990
  ]
667
991
  }
668
- ),
669
- visible && /* @__PURE__ */ jsx(
670
- Popover,
671
- {
672
- onDismiss: () => setVisible(false),
673
- source: buttonRef,
674
- spacing: 16,
675
- centered: true,
676
- children: /* @__PURE__ */ jsx("ul", { children: localesForDocument.map((name) => /* @__PURE__ */ jsx(Box, { padding: 3, as: "li", children: /* @__PURE__ */ jsx(Typography, { children: name }) }, name)) })
677
- }
678
- )
992
+ ) }) }),
993
+ /* @__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)) }) })
679
994
  ] });
680
995
  };
681
996
  const Button = styled.button`
@@ -737,18 +1052,11 @@ const addColumnToTableHook = ({ displayedHeaders, layout }) => {
737
1052
  const addLocaleToReleasesHook = ({ displayedHeaders = [] }) => {
738
1053
  return {
739
1054
  displayedHeaders: [
740
- // TODO: Fix when migrating to v5
741
- // ...displayedHeaders,
1055
+ ...displayedHeaders,
742
1056
  {
743
- key: "__locale__",
744
- fieldSchema: { type: "string" },
745
- metadatas: {
746
- label: {
747
- id: "content-releases.page.ReleaseDetails.table.header.label.locale",
748
- defaultMessage: "locale"
749
- },
750
- searchable: false,
751
- sortable: false
1057
+ label: {
1058
+ id: "content-releases.page.ReleaseDetails.table.header.label.locale",
1059
+ defaultMessage: "locale"
752
1060
  },
753
1061
  name: "locale"
754
1062
  }
@@ -896,8 +1204,6 @@ const index = {
896
1204
  app.addRBACMiddleware([localeMiddleware]);
897
1205
  app.registerPlugin({
898
1206
  id: pluginId,
899
- initializer: Initializer,
900
- isReady: false,
901
1207
  name: pluginId
902
1208
  });
903
1209
  },
@@ -915,7 +1221,7 @@ const index = {
915
1221
  },
916
1222
  id: "internationalization",
917
1223
  to: "internationalization",
918
- Component: () => import("./SettingsPage-CeqfDjsb.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
1224
+ Component: () => import("./SettingsPage-CsGvujny.mjs").then((mod) => ({ default: mod.ProtectedSettingsPage })),
919
1225
  permissions: PERMISSIONS.accessMain
920
1226
  });
921
1227
  const contentManager = app.getPlugin("content-manager");
@@ -925,6 +1231,10 @@ const index = {
925
1231
  actions.splice(indexOfDeleteAction, 0, DeleteLocaleAction);
926
1232
  return actions;
927
1233
  });
1234
+ contentManager.apis.addDocumentAction((actions) => {
1235
+ actions.splice(2, 0, BulkLocalePublishAction);
1236
+ return actions;
1237
+ });
928
1238
  contentManager.injectComponent("listView", "actions", {
929
1239
  name: "i18n-locale-filter",
930
1240
  Component: LocalePicker
@@ -1028,7 +1338,7 @@ const index = {
1028
1338
  async registerTrads({ locales }) {
1029
1339
  const importedTrads = await Promise.all(
1030
1340
  locales.map((locale) => {
1031
- 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-CnrTsjWS.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 }) => {
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 }) => {
1032
1342
  return {
1033
1343
  data: prefixPluginTranslations(data, pluginId),
1034
1344
  locale
@@ -1047,12 +1357,11 @@ const index = {
1047
1357
  export {
1048
1358
  PERMISSIONS as P,
1049
1359
  useGetDefaultLocalesQuery as a,
1050
- useGetLocalesQuery as b,
1051
- useDeleteLocaleMutation as c,
1052
- useUpdateLocaleMutation as d,
1053
- index as e,
1360
+ useDeleteLocaleMutation as b,
1361
+ useUpdateLocaleMutation as c,
1362
+ useGetLocalesQuery as d,
1054
1363
  getTranslation as g,
1055
- isBaseQueryError as i,
1364
+ index as i,
1056
1365
  useCreateLocaleMutation as u
1057
1366
  };
1058
- //# sourceMappingURL=index-BDU1w_fd.mjs.map
1367
+ //# sourceMappingURL=index-CCZJF_EJ.mjs.map