@strapi/i18n 0.0.0-next.fc1775f7731f8999840e56e298a216b9a6c5c4ad → 0.0.0-next.fdac61dd05ca665168f51f655f1d165b55ec4231

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 (126) hide show
  1. package/dist/admin/chunks/SettingsPage-CcMc5fyu.js +820 -0
  2. package/dist/admin/chunks/SettingsPage-CcMc5fyu.js.map +1 -0
  3. package/dist/admin/chunks/SettingsPage-g9SsggAu.mjs +797 -0
  4. package/dist/admin/chunks/SettingsPage-g9SsggAu.mjs.map +1 -0
  5. package/dist/admin/chunks/de-Cm8mYdaO.mjs +64 -0
  6. package/dist/admin/chunks/de-Cm8mYdaO.mjs.map +1 -0
  7. package/dist/admin/chunks/de-nEMWvIiY.js +66 -0
  8. package/dist/admin/chunks/de-nEMWvIiY.js.map +1 -0
  9. package/dist/admin/chunks/dk-BeUFOegB.mjs +64 -0
  10. package/dist/admin/chunks/dk-BeUFOegB.mjs.map +1 -0
  11. package/dist/admin/chunks/dk-CYATLPVe.js +66 -0
  12. package/dist/admin/chunks/dk-CYATLPVe.js.map +1 -0
  13. package/dist/admin/chunks/en-CG5cUCbD.js +81 -0
  14. package/dist/admin/chunks/en-CG5cUCbD.js.map +1 -0
  15. package/dist/admin/chunks/en-eWSaCeOb.mjs +79 -0
  16. package/dist/admin/chunks/en-eWSaCeOb.mjs.map +1 -0
  17. package/dist/admin/chunks/es-CWsogTGm.js +66 -0
  18. package/dist/admin/chunks/es-CWsogTGm.js.map +1 -0
  19. package/dist/admin/chunks/es-DqF_IdAc.mjs +64 -0
  20. package/dist/admin/chunks/es-DqF_IdAc.mjs.map +1 -0
  21. package/dist/admin/chunks/fr-CC7UFcYd.js +66 -0
  22. package/dist/admin/chunks/fr-CC7UFcYd.js.map +1 -0
  23. package/dist/admin/chunks/fr-CyARbZ3c.mjs +64 -0
  24. package/dist/admin/chunks/fr-CyARbZ3c.mjs.map +1 -0
  25. package/dist/admin/chunks/index-BWv_l535.mjs +2007 -0
  26. package/dist/admin/chunks/index-BWv_l535.mjs.map +1 -0
  27. package/dist/admin/chunks/index-Cek49tl0.js +2036 -0
  28. package/dist/admin/chunks/index-Cek49tl0.js.map +1 -0
  29. package/dist/admin/chunks/ko-Ax4NSedM.mjs +63 -0
  30. package/dist/admin/chunks/ko-Ax4NSedM.mjs.map +1 -0
  31. package/dist/admin/chunks/ko-XwGmfhoq.js +65 -0
  32. package/dist/admin/chunks/ko-XwGmfhoq.js.map +1 -0
  33. package/dist/admin/chunks/pl-B-aqvMqL.mjs +64 -0
  34. package/dist/admin/chunks/pl-B-aqvMqL.mjs.map +1 -0
  35. package/dist/admin/chunks/pl-B_vzY_ZB.js +66 -0
  36. package/dist/admin/chunks/pl-B_vzY_ZB.js.map +1 -0
  37. package/dist/admin/chunks/ru-VkPjQ-Sk.mjs +66 -0
  38. package/dist/admin/chunks/ru-VkPjQ-Sk.mjs.map +1 -0
  39. package/dist/admin/chunks/ru-WzHcJV1f.js +68 -0
  40. package/dist/admin/chunks/ru-WzHcJV1f.js.map +1 -0
  41. package/dist/admin/chunks/tr-CcWp6u3w.js +66 -0
  42. package/dist/admin/chunks/tr-CcWp6u3w.js.map +1 -0
  43. package/dist/admin/chunks/tr-DcTR88c9.mjs +64 -0
  44. package/dist/admin/chunks/tr-DcTR88c9.mjs.map +1 -0
  45. package/dist/admin/chunks/zh-C9So4SGq.js +66 -0
  46. package/dist/admin/chunks/zh-C9So4SGq.js.map +1 -0
  47. package/dist/admin/chunks/zh-Hans-DnU2bhri.js +57 -0
  48. package/dist/admin/chunks/zh-Hans-DnU2bhri.js.map +1 -0
  49. package/dist/admin/chunks/zh-Hans-L3wsRegj.mjs +55 -0
  50. package/dist/admin/chunks/zh-Hans-L3wsRegj.mjs.map +1 -0
  51. package/dist/admin/chunks/zh-RZyMiPIs.mjs +64 -0
  52. package/dist/admin/chunks/zh-RZyMiPIs.mjs.map +1 -0
  53. package/dist/admin/index.js +20 -4
  54. package/dist/admin/index.js.map +1 -1
  55. package/dist/admin/index.mjs +15 -6
  56. package/dist/admin/index.mjs.map +1 -1
  57. package/dist/admin/src/components/CMHeaderActions.d.ts +1 -1
  58. package/dist/admin/src/components/tests/CreateLocale.test.d.ts +1 -0
  59. package/dist/admin/src/components/tests/DeleteLocale.test.d.ts +1 -0
  60. package/dist/admin/src/components/tests/EditLocale.test.d.ts +1 -0
  61. package/dist/admin/src/components/tests/LocaleListCell.test.d.ts +1 -0
  62. package/dist/admin/src/contentReleasesHooks/releaseDetailsView.d.ts +1 -1
  63. package/dist/admin/src/pages/tests/SettingsPage.test.d.ts +1 -0
  64. package/dist/admin/tests/server.d.ts +1 -0
  65. package/dist/admin/tests/utils.d.ts +6 -0
  66. package/dist/server/index.js +3650 -3390
  67. package/dist/server/index.js.map +1 -1
  68. package/dist/server/index.mjs +3632 -3374
  69. package/dist/server/index.mjs.map +1 -1
  70. package/dist/server/src/services/localizations.d.ts +1 -1
  71. package/dist/server/src/services/localizations.d.ts.map +1 -1
  72. package/dist/shared/contracts/content-manager.d.ts +1 -1
  73. package/dist/shared/contracts/shared.d.ts +1 -1
  74. package/package.json +14 -11
  75. package/dist/_chunks/SettingsPage-B6QDUmu9.mjs +0 -554
  76. package/dist/_chunks/SettingsPage-B6QDUmu9.mjs.map +0 -1
  77. package/dist/_chunks/SettingsPage-BsHtr3lV.js +0 -573
  78. package/dist/_chunks/SettingsPage-BsHtr3lV.js.map +0 -1
  79. package/dist/_chunks/de-9eCAqqrB.mjs +0 -66
  80. package/dist/_chunks/de-9eCAqqrB.mjs.map +0 -1
  81. package/dist/_chunks/de-DtWiGdHl.js +0 -66
  82. package/dist/_chunks/de-DtWiGdHl.js.map +0 -1
  83. package/dist/_chunks/dk-2qBjxt-P.mjs +0 -66
  84. package/dist/_chunks/dk-2qBjxt-P.mjs.map +0 -1
  85. package/dist/_chunks/dk-D8C-casx.js +0 -66
  86. package/dist/_chunks/dk-D8C-casx.js.map +0 -1
  87. package/dist/_chunks/en-BKBz3tro.js +0 -81
  88. package/dist/_chunks/en-BKBz3tro.js.map +0 -1
  89. package/dist/_chunks/en-DlXfy6Gy.mjs +0 -81
  90. package/dist/_chunks/en-DlXfy6Gy.mjs.map +0 -1
  91. package/dist/_chunks/es-DS-XFGSw.js +0 -66
  92. package/dist/_chunks/es-DS-XFGSw.js.map +0 -1
  93. package/dist/_chunks/es-DlmMVaBG.mjs +0 -66
  94. package/dist/_chunks/es-DlmMVaBG.mjs.map +0 -1
  95. package/dist/_chunks/fr-3S6ke71d.mjs +0 -66
  96. package/dist/_chunks/fr-3S6ke71d.mjs.map +0 -1
  97. package/dist/_chunks/fr-BTjekDpq.js +0 -66
  98. package/dist/_chunks/fr-BTjekDpq.js.map +0 -1
  99. package/dist/_chunks/index-3XgwXL6T.js +0 -1628
  100. package/dist/_chunks/index-3XgwXL6T.js.map +0 -1
  101. package/dist/_chunks/index-iEQ79W05.mjs +0 -1607
  102. package/dist/_chunks/index-iEQ79W05.mjs.map +0 -1
  103. package/dist/_chunks/ko-DmcGUBQ3.js +0 -65
  104. package/dist/_chunks/ko-DmcGUBQ3.js.map +0 -1
  105. package/dist/_chunks/ko-qTjQ8IMw.mjs +0 -65
  106. package/dist/_chunks/ko-qTjQ8IMw.mjs.map +0 -1
  107. package/dist/_chunks/pl-B67TSHqT.mjs +0 -66
  108. package/dist/_chunks/pl-B67TSHqT.mjs.map +0 -1
  109. package/dist/_chunks/pl-Cn5RYonZ.js +0 -66
  110. package/dist/_chunks/pl-Cn5RYonZ.js.map +0 -1
  111. package/dist/_chunks/ru-BMBgVL3s.js +0 -68
  112. package/dist/_chunks/ru-BMBgVL3s.js.map +0 -1
  113. package/dist/_chunks/ru-hagMa57T.mjs +0 -68
  114. package/dist/_chunks/ru-hagMa57T.mjs.map +0 -1
  115. package/dist/_chunks/tr-CarUU76c.js +0 -66
  116. package/dist/_chunks/tr-CarUU76c.js.map +0 -1
  117. package/dist/_chunks/tr-Dw_jmkG-.mjs +0 -66
  118. package/dist/_chunks/tr-Dw_jmkG-.mjs.map +0 -1
  119. package/dist/_chunks/zh-57YM4amO.mjs +0 -66
  120. package/dist/_chunks/zh-57YM4amO.mjs.map +0 -1
  121. package/dist/_chunks/zh-CukOviB0.js +0 -66
  122. package/dist/_chunks/zh-CukOviB0.js.map +0 -1
  123. package/dist/_chunks/zh-Hans-DSHIXAa3.js +0 -57
  124. package/dist/_chunks/zh-Hans-DSHIXAa3.js.map +0 -1
  125. package/dist/_chunks/zh-Hans-Dyc-aR-h.mjs +0 -57
  126. package/dist/_chunks/zh-Hans-Dyc-aR-h.mjs.map +0 -1
@@ -0,0 +1,2036 @@
1
+ 'use strict';
2
+
3
+ var get = require('lodash/get');
4
+ var yup = require('yup');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var React = require('react');
7
+ var designSystem = require('@strapi/design-system');
8
+ var icons = require('@strapi/icons');
9
+ var reactIntl = require('react-intl');
10
+ var styledComponents = require('styled-components');
11
+ var query = require('@reduxjs/toolkit/query');
12
+ var strapiAdmin = require('@strapi/admin/strapi-admin');
13
+ var strapiAdmin$1 = require('@strapi/content-manager/strapi-admin');
14
+ var reactRouterDom = require('react-router-dom');
15
+ var qs = require('qs');
16
+ var omit = require('lodash/omit');
17
+
18
+ function _interopNamespaceDefault(e) {
19
+ var n = Object.create(null);
20
+ if (e) {
21
+ Object.keys(e).forEach(function (k) {
22
+ if (k !== 'default') {
23
+ var d = Object.getOwnPropertyDescriptor(e, k);
24
+ Object.defineProperty(n, k, d.get ? d : {
25
+ enumerable: true,
26
+ get: function () { return e[k]; }
27
+ });
28
+ }
29
+ });
30
+ }
31
+ n.default = e;
32
+ return Object.freeze(n);
33
+ }
34
+
35
+ var yup__namespace = /*#__PURE__*/_interopNamespaceDefault(yup);
36
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
37
+ var qs__namespace = /*#__PURE__*/_interopNamespaceDefault(qs);
38
+
39
+ const pluginId = 'i18n';
40
+
41
+ const getTranslation = (id)=>`${pluginId}.${id}`;
42
+
43
+ const TextAlignTypography = styledComponents.styled(designSystem.Typography)`
44
+ text-align: center;
45
+ `;
46
+ const CheckboxConfirmation = ({ description, isCreating = false, intlLabel, name, onChange, value })=>{
47
+ const { formatMessage } = reactIntl.useIntl();
48
+ const [isOpen, setIsOpen] = React__namespace.useState(false);
49
+ const handleChange = (value)=>{
50
+ if (isCreating || value) {
51
+ return onChange({
52
+ target: {
53
+ name,
54
+ value,
55
+ type: 'checkbox'
56
+ }
57
+ });
58
+ }
59
+ if (!value) {
60
+ return setIsOpen(true);
61
+ }
62
+ return null;
63
+ };
64
+ const handleConfirm = ()=>{
65
+ onChange({
66
+ target: {
67
+ name,
68
+ value: false,
69
+ type: 'checkbox'
70
+ }
71
+ });
72
+ };
73
+ const label = intlLabel.id ? formatMessage({
74
+ id: intlLabel.id,
75
+ defaultMessage: intlLabel.defaultMessage
76
+ }, {
77
+ ...intlLabel.values
78
+ }) : name;
79
+ const hint = description ? formatMessage({
80
+ id: description.id,
81
+ defaultMessage: description.defaultMessage
82
+ }, {
83
+ ...description.values
84
+ }) : '';
85
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Dialog.Root, {
86
+ open: isOpen,
87
+ onOpenChange: setIsOpen,
88
+ children: [
89
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
90
+ hint: hint,
91
+ name: name,
92
+ children: [
93
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Checkbox, {
94
+ onCheckedChange: handleChange,
95
+ checked: value,
96
+ children: label
97
+ }),
98
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Hint, {})
99
+ ]
100
+ }),
101
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Dialog.Content, {
102
+ children: [
103
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Header, {
104
+ children: formatMessage({
105
+ id: getTranslation('CheckboxConfirmation.Modal.title'),
106
+ defaultMessage: 'Disable localization'
107
+ })
108
+ }),
109
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Body, {
110
+ icon: /*#__PURE__*/ jsxRuntime.jsx(icons.WarningCircle, {}),
111
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
112
+ direction: "column",
113
+ alignItems: "stretch",
114
+ gap: 2,
115
+ children: [
116
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
117
+ justifyContent: "center",
118
+ children: /*#__PURE__*/ jsxRuntime.jsx(TextAlignTypography, {
119
+ children: formatMessage({
120
+ id: getTranslation('CheckboxConfirmation.Modal.content'),
121
+ defaultMessage: 'Disabling localization will engender the deletion of all your content but the one associated to your default locale (if existing).'
122
+ })
123
+ })
124
+ }),
125
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
126
+ justifyContent: "center",
127
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
128
+ fontWeight: "semiBold",
129
+ children: formatMessage({
130
+ id: getTranslation('CheckboxConfirmation.Modal.body'),
131
+ defaultMessage: 'Do you want to disable it?'
132
+ })
133
+ })
134
+ })
135
+ ]
136
+ })
137
+ }),
138
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Dialog.Footer, {
139
+ children: [
140
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Cancel, {
141
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
142
+ variant: "tertiary",
143
+ children: formatMessage({
144
+ id: 'components.popUpWarning.button.cancel',
145
+ defaultMessage: 'No, cancel'
146
+ })
147
+ })
148
+ }),
149
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Action, {
150
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
151
+ variant: "danger-light",
152
+ onClick: handleConfirm,
153
+ children: formatMessage({
154
+ id: getTranslation('CheckboxConfirmation.Modal.button-confirm'),
155
+ defaultMessage: 'Yes, disable'
156
+ })
157
+ })
158
+ })
159
+ ]
160
+ })
161
+ ]
162
+ })
163
+ ]
164
+ });
165
+ };
166
+
167
+ const LOCALIZED_FIELDS = [
168
+ 'biginteger',
169
+ 'boolean',
170
+ 'component',
171
+ 'date',
172
+ 'datetime',
173
+ 'decimal',
174
+ 'dynamiczone',
175
+ 'email',
176
+ 'enumeration',
177
+ 'float',
178
+ 'integer',
179
+ 'json',
180
+ 'media',
181
+ 'number',
182
+ 'password',
183
+ 'richtext',
184
+ 'blocks',
185
+ 'string',
186
+ 'text',
187
+ 'time'
188
+ ];
189
+ const doesPluginOptionsHaveI18nLocalized = (opts)=>typeof opts === 'object' && opts !== null && 'i18n' in opts && typeof opts.i18n === 'object' && opts.i18n !== null && 'localized' in opts.i18n && typeof opts.i18n.localized === 'boolean';
190
+
191
+ const capitalize = (str)=>str.charAt(0).toUpperCase() + str.slice(1);
192
+
193
+ /**
194
+ * @alpha
195
+ * @description This hook is used to get the i18n status of a content type.
196
+ * Also returns the CRUDP permission locale properties for the content type
197
+ * so we know which locales the user can perform actions on.
198
+ */ const useI18n = ()=>{
199
+ // Extract the params from the URL to pass to our useDocument hook
200
+ const params = reactRouterDom.useParams();
201
+ const userPermissions = strapiAdmin.useAuth('useI18n', (state)=>state.permissions);
202
+ const actions = React__namespace.useMemo(()=>{
203
+ const permissions = userPermissions.filter((permission)=>permission.subject === params.slug);
204
+ return permissions.reduce((acc, permission)=>{
205
+ const [actionShorthand] = permission.action.split('.').slice(-1);
206
+ return {
207
+ ...acc,
208
+ [`can${capitalize(actionShorthand)}`]: permission.properties?.locales ?? []
209
+ };
210
+ }, {
211
+ canCreate: [],
212
+ canRead: [],
213
+ canUpdate: [],
214
+ canDelete: [],
215
+ canPublish: []
216
+ });
217
+ }, [
218
+ params.slug,
219
+ userPermissions
220
+ ]);
221
+ // TODO: use specific hook to get schema only
222
+ const { schema } = strapiAdmin$1.unstable_useDocument({
223
+ // We can non-null assert these because below we skip the query if they are not present
224
+ collectionType: params.collectionType,
225
+ model: params.slug
226
+ }, {
227
+ skip: true
228
+ });
229
+ if (doesPluginOptionsHaveI18nLocalized(schema?.pluginOptions)) {
230
+ return {
231
+ hasI18n: schema.pluginOptions.i18n.localized,
232
+ ...actions
233
+ };
234
+ }
235
+ return {
236
+ hasI18n: false,
237
+ ...actions
238
+ };
239
+ };
240
+
241
+ const i18nApi = strapiAdmin.adminApi.enhanceEndpoints({
242
+ addTagTypes: [
243
+ 'Locale'
244
+ ]
245
+ });
246
+
247
+ const localesApi = i18nApi.injectEndpoints({
248
+ endpoints: (builder)=>({
249
+ createLocale: builder.mutation({
250
+ query: (data)=>({
251
+ url: '/i18n/locales',
252
+ method: 'POST',
253
+ data
254
+ }),
255
+ invalidatesTags: [
256
+ {
257
+ type: 'Locale',
258
+ id: 'LIST'
259
+ }
260
+ ]
261
+ }),
262
+ deleteLocale: builder.mutation({
263
+ query: (id)=>({
264
+ url: `/i18n/locales/${id}`,
265
+ method: 'DELETE'
266
+ }),
267
+ invalidatesTags: (result, error, id)=>[
268
+ {
269
+ type: 'Locale',
270
+ id
271
+ }
272
+ ]
273
+ }),
274
+ getLocales: builder.query({
275
+ query: ()=>'/i18n/locales',
276
+ providesTags: (res)=>[
277
+ {
278
+ type: 'Locale',
279
+ id: 'LIST'
280
+ },
281
+ ...Array.isArray(res) ? res.map((locale)=>({
282
+ type: 'Locale',
283
+ id: locale.id
284
+ })) : []
285
+ ]
286
+ }),
287
+ getDefaultLocales: builder.query({
288
+ query: ()=>'/i18n/iso-locales'
289
+ }),
290
+ updateLocale: builder.mutation({
291
+ query: ({ id, ...data })=>({
292
+ url: `/i18n/locales/${id}`,
293
+ method: 'PUT',
294
+ data
295
+ }),
296
+ invalidatesTags: (result, error, { id })=>[
297
+ {
298
+ type: 'Locale',
299
+ id
300
+ }
301
+ ]
302
+ })
303
+ })
304
+ });
305
+ const { useCreateLocaleMutation, useDeleteLocaleMutation, useGetLocalesQuery, useGetDefaultLocalesQuery, useUpdateLocaleMutation } = localesApi;
306
+
307
+ const relationsApi = i18nApi.injectEndpoints({
308
+ overrideExisting: true,
309
+ endpoints: (builder)=>({
310
+ getManyDraftRelationCount: builder.query({
311
+ query: ({ model, ...params })=>({
312
+ url: `/content-manager/collection-types/${model}/actions/countManyEntriesDraftRelations`,
313
+ method: 'GET',
314
+ config: {
315
+ params
316
+ }
317
+ }),
318
+ transformResponse: (response)=>response.data
319
+ })
320
+ })
321
+ });
322
+ const { useGetManyDraftRelationCountQuery } = relationsApi;
323
+
324
+ const cleanData = (data, schema, components)=>{
325
+ const cleanedData = removeFields(data, [
326
+ 'createdAt',
327
+ 'createdBy',
328
+ 'updatedAt',
329
+ 'updatedBy',
330
+ 'id',
331
+ 'documentId',
332
+ 'publishedAt',
333
+ 'strapi_stage',
334
+ 'strapi_assignee',
335
+ 'locale',
336
+ 'status'
337
+ ]);
338
+ const cleanedDataWithoutPasswordAndRelation = recursiveRemoveFieldTypes(cleanedData, schema, components, [
339
+ 'relation',
340
+ 'password'
341
+ ]);
342
+ return cleanedDataWithoutPasswordAndRelation;
343
+ };
344
+ const removeFields = (data, fields)=>{
345
+ return Object.keys(data).reduce((acc, current)=>{
346
+ if (fields.includes(current)) {
347
+ return acc;
348
+ }
349
+ acc[current] = data[current];
350
+ return acc;
351
+ }, {});
352
+ };
353
+ const recursiveRemoveFieldTypes = (data, schema, components, fields)=>{
354
+ return Object.keys(data).reduce((acc, current)=>{
355
+ const attribute = schema.attributes[current] ?? {
356
+ type: undefined
357
+ };
358
+ if (fields.includes(attribute.type)) {
359
+ return acc;
360
+ }
361
+ if (attribute.type === 'dynamiczone') {
362
+ acc[current] = data[current].map((componentValue, index)=>{
363
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(componentValue, components[componentValue.__component], components, fields);
364
+ return {
365
+ ...rest,
366
+ __temp_key__: index + 1
367
+ };
368
+ });
369
+ } else if (attribute.type === 'component') {
370
+ const { repeatable, component } = attribute;
371
+ if (repeatable) {
372
+ acc[current] = (data[current] ?? []).map((compoData, index)=>{
373
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(compoData, components[component], components, fields);
374
+ return {
375
+ ...rest,
376
+ __temp_key__: index + 1
377
+ };
378
+ });
379
+ } else {
380
+ const { id: _, ...rest } = recursiveRemoveFieldTypes(data[current] ?? {}, components[component], components, fields);
381
+ acc[current] = rest;
382
+ }
383
+ } else {
384
+ acc[current] = data[current];
385
+ }
386
+ return acc;
387
+ }, {});
388
+ };
389
+
390
+ const isErrorMessageDescriptor = (object)=>{
391
+ return typeof object === 'object' && object !== null && 'id' in object && 'defaultMessage' in object;
392
+ };
393
+ const EntryValidationText = ({ status = 'draft', validationErrors, action })=>{
394
+ const { formatMessage } = reactIntl.useIntl();
395
+ /**
396
+ * TODO: Should this be extracted an made into a factory to recursively get
397
+ * error messages??
398
+ */ const getErrorStr = (key, value)=>{
399
+ if (typeof value === 'string') {
400
+ return `${key}: ${value}`;
401
+ } else if (isErrorMessageDescriptor(value)) {
402
+ return `${key}: ${formatMessage(value)}`;
403
+ } else if (Array.isArray(value)) {
404
+ return value.map((v)=>getErrorStr(key, v)).join(' ');
405
+ } else if (typeof value === 'object' && !Array.isArray(value)) {
406
+ return Object.entries(value).map(([k, v])=>getErrorStr(k, v)).join(' ');
407
+ } else {
408
+ /**
409
+ * unlikely to happen, but we need to return something
410
+ */ return '';
411
+ }
412
+ };
413
+ if (validationErrors) {
414
+ const validationErrorsMessages = Object.entries(validationErrors).map(([key, value])=>{
415
+ return getErrorStr(key, value);
416
+ }).join(' ');
417
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
418
+ gap: 2,
419
+ children: [
420
+ /*#__PURE__*/ jsxRuntime.jsx(icons.CrossCircle, {
421
+ fill: "danger600"
422
+ }),
423
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Tooltip, {
424
+ label: validationErrorsMessages,
425
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
426
+ maxWidth: '30rem',
427
+ textColor: "danger600",
428
+ variant: "omega",
429
+ fontWeight: "semiBold",
430
+ ellipsis: true,
431
+ children: validationErrorsMessages
432
+ })
433
+ })
434
+ ]
435
+ });
436
+ }
437
+ const getStatusMessage = ()=>{
438
+ if (action === 'bulk-publish') {
439
+ if (status === 'published') {
440
+ return {
441
+ icon: /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
442
+ fill: "success600"
443
+ }),
444
+ text: formatMessage({
445
+ id: 'content-manager.bulk-publish.already-published',
446
+ defaultMessage: 'Already Published'
447
+ }),
448
+ textColor: 'success600',
449
+ fontWeight: 'bold'
450
+ };
451
+ } else if (status === 'modified') {
452
+ return {
453
+ icon: /*#__PURE__*/ jsxRuntime.jsx(icons.ArrowsCounterClockwise, {
454
+ fill: "alternative600"
455
+ }),
456
+ text: formatMessage({
457
+ id: 'app.utils.ready-to-publish-changes',
458
+ defaultMessage: 'Ready to publish changes'
459
+ })
460
+ };
461
+ } else {
462
+ return {
463
+ icon: /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
464
+ fill: "success600"
465
+ }),
466
+ text: formatMessage({
467
+ id: 'app.utils.ready-to-publish',
468
+ defaultMessage: 'Ready to publish'
469
+ })
470
+ };
471
+ }
472
+ } else {
473
+ if (status === 'draft') {
474
+ return {
475
+ icon: /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
476
+ fill: "success600"
477
+ }),
478
+ text: formatMessage({
479
+ id: 'content-manager.bulk-unpublish.already-unpublished',
480
+ defaultMessage: 'Already Unpublished'
481
+ }),
482
+ textColor: 'success600',
483
+ fontWeight: 'bold'
484
+ };
485
+ } else {
486
+ return {
487
+ icon: /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
488
+ fill: "success600"
489
+ }),
490
+ text: formatMessage({
491
+ id: 'app.utils.ready-to-unpublish-changes',
492
+ defaultMessage: 'Ready to unpublish'
493
+ }),
494
+ textColor: 'success600',
495
+ fontWeight: 'bold'
496
+ };
497
+ }
498
+ }
499
+ };
500
+ const { icon, text, textColor = 'success600', fontWeight = 'normal' } = getStatusMessage();
501
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
502
+ gap: 2,
503
+ children: [
504
+ icon,
505
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
506
+ textColor: textColor,
507
+ fontWeight: fontWeight,
508
+ children: text
509
+ })
510
+ ]
511
+ });
512
+ };
513
+ /* -------------------------------------------------------------------------------------------------
514
+ * BoldChunk
515
+ * -----------------------------------------------------------------------------------------------*/ const BoldChunk = (chunks)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
516
+ fontWeight: "bold",
517
+ children: chunks
518
+ });
519
+ const BulkLocaleActionModal = ({ headers, rows, localesMetadata, validationErrors = {}, action })=>{
520
+ const { formatMessage } = reactIntl.useIntl();
521
+ const selectedRows = strapiAdmin.useTable('BulkLocaleActionModal', (state)=>state.selectedRows);
522
+ const getFormattedCountMessage = ()=>{
523
+ const currentStatusByLocale = rows.reduce((acc, { locale, status })=>{
524
+ acc[locale] = status;
525
+ return acc;
526
+ }, {});
527
+ const localesWithErrors = Object.keys(validationErrors);
528
+ const publishedCount = selectedRows.filter(({ locale })=>currentStatusByLocale[locale] === 'published').length;
529
+ const draftCount = selectedRows.filter(({ locale })=>(currentStatusByLocale[locale] === 'draft' || currentStatusByLocale[locale] === 'modified') && !localesWithErrors.includes(locale)).length;
530
+ const withErrorsCount = localesWithErrors.length;
531
+ const messageId = action === 'bulk-publish' ? 'content-manager.containers.list.selectedEntriesModal.selectedCount.publish' : 'content-manager.containers.list.selectedEntriesModal.selectedCount.unpublish';
532
+ 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.';
533
+ return formatMessage({
534
+ id: messageId,
535
+ defaultMessage
536
+ }, {
537
+ withErrorsCount,
538
+ draftCount,
539
+ publishedCount,
540
+ b: BoldChunk
541
+ });
542
+ };
543
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Modal.Body, {
544
+ children: [
545
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
546
+ children: getFormattedCountMessage()
547
+ }),
548
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
549
+ marginTop: 5,
550
+ children: /*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin.Table.Content, {
551
+ children: [
552
+ /*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin.Table.Head, {
553
+ children: [
554
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.HeaderCheckboxCell, {}),
555
+ headers.map((head)=>/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.HeaderCell, {
556
+ ...head
557
+ }, head.name))
558
+ ]
559
+ }),
560
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.Body, {
561
+ children: rows.map(({ locale, status }, index)=>{
562
+ const error = validationErrors?.[locale] ?? null;
563
+ const statusVariant = status === 'draft' ? 'primary' : status === 'published' ? 'success' : 'alternative';
564
+ return /*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin.Table.Row, {
565
+ children: [
566
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.CheckboxCell, {
567
+ id: locale,
568
+ "aria-label": `Select ${locale}`
569
+ }),
570
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.Cell, {
571
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
572
+ variant: "sigma",
573
+ textColor: "neutral600",
574
+ children: Array.isArray(localesMetadata) ? localesMetadata.find((localeEntry)=>localeEntry.code === locale)?.name : locale
575
+ })
576
+ }),
577
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.Cell, {
578
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
579
+ display: "flex",
580
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Status, {
581
+ display: "flex",
582
+ paddingLeft: "6px",
583
+ paddingRight: "6px",
584
+ paddingTop: "2px",
585
+ paddingBottom: "2px",
586
+ size: 'S',
587
+ variant: statusVariant,
588
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
589
+ tag: "span",
590
+ variant: "pi",
591
+ fontWeight: "bold",
592
+ children: capitalize(status)
593
+ })
594
+ })
595
+ })
596
+ }),
597
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.Cell, {
598
+ children: /*#__PURE__*/ jsxRuntime.jsx(EntryValidationText, {
599
+ validationErrors: error,
600
+ status: status,
601
+ action: action
602
+ })
603
+ }),
604
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.Cell, {
605
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.IconButton, {
606
+ tag: reactRouterDom.Link,
607
+ to: {
608
+ search: qs.stringify({
609
+ plugins: {
610
+ i18n: {
611
+ locale
612
+ }
613
+ }
614
+ })
615
+ },
616
+ label: formatMessage({
617
+ id: getTranslation('Settings.list.actions.edit'),
618
+ defaultMessage: 'Edit {name} locale'
619
+ }, {
620
+ name: locale
621
+ }),
622
+ variant: "ghost",
623
+ children: /*#__PURE__*/ jsxRuntime.jsx(icons.Pencil, {})
624
+ })
625
+ })
626
+ ]
627
+ }, index);
628
+ })
629
+ })
630
+ ]
631
+ })
632
+ })
633
+ ]
634
+ });
635
+ };
636
+
637
+ const statusVariants = {
638
+ draft: 'secondary',
639
+ published: 'success',
640
+ modified: 'alternative'
641
+ };
642
+ const LocaleOption = ({ isDraftAndPublishEnabled, locale, status, entryExists })=>{
643
+ const { formatMessage } = reactIntl.useIntl();
644
+ if (!entryExists) {
645
+ return formatMessage({
646
+ id: getTranslation('CMEditViewLocalePicker.locale.create'),
647
+ defaultMessage: 'Create <bold>{locale}</bold> locale'
648
+ }, {
649
+ bold: (locale)=>/*#__PURE__*/ jsxRuntime.jsx("b", {
650
+ children: locale
651
+ }),
652
+ locale: locale.name
653
+ });
654
+ }
655
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
656
+ width: "100%",
657
+ gap: 1,
658
+ justifyContent: "space-between",
659
+ children: [
660
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
661
+ children: locale.name
662
+ }),
663
+ isDraftAndPublishEnabled ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Status, {
664
+ display: "flex",
665
+ paddingLeft: "6px",
666
+ paddingRight: "6px",
667
+ paddingTop: "2px",
668
+ paddingBottom: "2px",
669
+ size: "S",
670
+ variant: statusVariants[status],
671
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
672
+ tag: "span",
673
+ variant: "pi",
674
+ fontWeight: "bold",
675
+ children: capitalize(status)
676
+ })
677
+ }) : null
678
+ ]
679
+ });
680
+ };
681
+ const LocalePickerAction = ({ document, meta, model, collectionType, documentId })=>{
682
+ const { formatMessage } = reactIntl.useIntl();
683
+ const [{ query }, setQuery] = strapiAdmin.useQueryParams();
684
+ const { hasI18n, canCreate, canRead } = useI18n();
685
+ const { data: locales = [] } = useGetLocalesQuery();
686
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
687
+ const { schema } = strapiAdmin$1.unstable_useDocument({
688
+ model,
689
+ collectionType,
690
+ documentId,
691
+ params: {
692
+ locale: currentDesiredLocale
693
+ }
694
+ });
695
+ const handleSelect = React__namespace.useCallback((value)=>{
696
+ setQuery({
697
+ plugins: {
698
+ ...query.plugins,
699
+ i18n: {
700
+ locale: value
701
+ }
702
+ }
703
+ });
704
+ }, [
705
+ query.plugins,
706
+ setQuery
707
+ ]);
708
+ React__namespace.useEffect(()=>{
709
+ if (!Array.isArray(locales) || !hasI18n) {
710
+ return;
711
+ }
712
+ /**
713
+ * Handle the case where the current locale query param doesn't exist
714
+ * in the list of available locales, so we redirect to the default locale.
715
+ */ const doesLocaleExist = locales.find((loc)=>loc.code === currentDesiredLocale);
716
+ const defaultLocale = locales.find((locale)=>locale.isDefault);
717
+ if (!doesLocaleExist && defaultLocale?.code) {
718
+ handleSelect(defaultLocale.code);
719
+ }
720
+ }, [
721
+ handleSelect,
722
+ hasI18n,
723
+ locales,
724
+ currentDesiredLocale
725
+ ]);
726
+ const currentLocale = Array.isArray(locales) ? locales.find((locale)=>locale.code === currentDesiredLocale) : undefined;
727
+ const allCurrentLocales = [
728
+ {
729
+ status: getDocumentStatus(document, meta),
730
+ locale: currentLocale?.code
731
+ },
732
+ ...document?.localizations ?? []
733
+ ];
734
+ if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
735
+ return null;
736
+ }
737
+ const displayedLocales = locales.filter((locale)=>{
738
+ /**
739
+ * If you can read we allow you to see the locale exists
740
+ * otherwise the locale is hidden.
741
+ */ return canRead.includes(locale.code);
742
+ });
743
+ return {
744
+ label: formatMessage({
745
+ id: getTranslation('Settings.locales.modal.locales.label'),
746
+ defaultMessage: 'Locales'
747
+ }),
748
+ options: displayedLocales.map((locale)=>{
749
+ const entryWithLocaleExists = allCurrentLocales.some((doc)=>doc.locale === locale.code);
750
+ const currentLocaleDoc = allCurrentLocales.find((doc)=>'locale' in doc ? doc.locale === locale.code : false);
751
+ const permissionsToCheck = currentLocaleDoc ? canRead : canCreate;
752
+ return {
753
+ disabled: !permissionsToCheck.includes(locale.code),
754
+ value: locale.code,
755
+ label: /*#__PURE__*/ jsxRuntime.jsx(LocaleOption, {
756
+ isDraftAndPublishEnabled: !!schema?.options?.draftAndPublish,
757
+ locale: locale,
758
+ status: currentLocaleDoc?.status,
759
+ entryExists: entryWithLocaleExists
760
+ }),
761
+ startIcon: !entryWithLocaleExists ? /*#__PURE__*/ jsxRuntime.jsx(icons.Plus, {}) : null
762
+ };
763
+ }),
764
+ customizeContent: ()=>currentLocale?.name,
765
+ onSelect: handleSelect,
766
+ value: currentLocale
767
+ };
768
+ };
769
+ const getDocumentStatus = (document, meta)=>{
770
+ const docStatus = document?.status;
771
+ const statuses = meta?.availableStatus ?? [];
772
+ /**
773
+ * Creating an entry
774
+ */ if (!docStatus) {
775
+ return 'draft';
776
+ }
777
+ /**
778
+ * We're viewing a draft, but the document could have a published version
779
+ */ if (docStatus === 'draft' && statuses.find((doc)=>doc.publishedAt !== null)) {
780
+ return 'published';
781
+ }
782
+ return docStatus;
783
+ };
784
+ /* -------------------------------------------------------------------------------------------------
785
+ * FillFromAnotherLocaleAction
786
+ * -----------------------------------------------------------------------------------------------*/ const FillFromAnotherLocaleAction = ({ documentId, meta, model, collectionType })=>{
787
+ const { formatMessage } = reactIntl.useIntl();
788
+ const [{ query }] = strapiAdmin.useQueryParams();
789
+ const { hasI18n } = useI18n();
790
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
791
+ const [localeSelected, setLocaleSelected] = React__namespace.useState(null);
792
+ const setValues = strapiAdmin.useForm('FillFromAnotherLocale', (state)=>state.setValues);
793
+ const { getDocument } = strapiAdmin$1.unstable_useDocumentActions();
794
+ const { schema, components } = strapiAdmin$1.unstable_useDocument({
795
+ model,
796
+ documentId,
797
+ collectionType,
798
+ params: {
799
+ locale: currentDesiredLocale
800
+ }
801
+ });
802
+ const { data: locales = [] } = useGetLocalesQuery();
803
+ const availableLocales = Array.isArray(locales) ? locales.filter((locale)=>meta?.availableLocales.some((l)=>l.locale === locale.code)) : [];
804
+ const fillFromLocale = (onClose)=>async ()=>{
805
+ const response = await getDocument({
806
+ collectionType,
807
+ model,
808
+ documentId,
809
+ params: {
810
+ locale: localeSelected
811
+ }
812
+ });
813
+ if (!response || !schema) {
814
+ return;
815
+ }
816
+ const { data } = response;
817
+ const cleanedData = cleanData(data, schema, components);
818
+ setValues(cleanedData);
819
+ onClose();
820
+ };
821
+ if (!hasI18n) {
822
+ return null;
823
+ }
824
+ return {
825
+ type: 'icon',
826
+ icon: /*#__PURE__*/ jsxRuntime.jsx(icons.Earth, {}),
827
+ disabled: availableLocales.length === 0,
828
+ label: formatMessage({
829
+ id: getTranslation('CMEditViewCopyLocale.copy-text'),
830
+ defaultMessage: 'Fill in from another locale'
831
+ }),
832
+ dialog: {
833
+ type: 'dialog',
834
+ title: formatMessage({
835
+ id: getTranslation('CMEditViewCopyLocale.dialog.title'),
836
+ defaultMessage: 'Confirmation'
837
+ }),
838
+ content: ({ onClose })=>/*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
839
+ children: [
840
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Body, {
841
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
842
+ direction: "column",
843
+ gap: 3,
844
+ children: [
845
+ /*#__PURE__*/ jsxRuntime.jsx(icons.WarningCircle, {
846
+ width: "24px",
847
+ height: "24px",
848
+ fill: "danger600"
849
+ }),
850
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
851
+ textAlign: "center",
852
+ children: formatMessage({
853
+ id: getTranslation('CMEditViewCopyLocale.dialog.body'),
854
+ defaultMessage: 'Your current content will be erased and filled by the content of the selected locale:'
855
+ })
856
+ }),
857
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
858
+ width: "100%",
859
+ children: [
860
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
861
+ children: formatMessage({
862
+ id: getTranslation('CMEditViewCopyLocale.dialog.field.label'),
863
+ defaultMessage: 'Locale'
864
+ })
865
+ }),
866
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelect, {
867
+ value: localeSelected,
868
+ placeholder: formatMessage({
869
+ id: getTranslation('CMEditViewCopyLocale.dialog.field.placeholder'),
870
+ defaultMessage: 'Select one locale...'
871
+ }),
872
+ // @ts-expect-error – the DS will handle numbers, but we're not allowing the API.
873
+ onChange: (value)=>setLocaleSelected(value),
874
+ children: availableLocales.map((locale)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelectOption, {
875
+ value: locale.code,
876
+ children: locale.name
877
+ }, locale.code))
878
+ })
879
+ ]
880
+ })
881
+ ]
882
+ })
883
+ }),
884
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Footer, {
885
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
886
+ gap: 2,
887
+ width: "100%",
888
+ children: [
889
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
890
+ flex: "auto",
891
+ variant: "tertiary",
892
+ onClick: onClose,
893
+ children: formatMessage({
894
+ id: getTranslation('CMEditViewCopyLocale.cancel-text'),
895
+ defaultMessage: 'No, cancel'
896
+ })
897
+ }),
898
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
899
+ flex: "auto",
900
+ variant: "success",
901
+ onClick: fillFromLocale(onClose),
902
+ children: formatMessage({
903
+ id: getTranslation('CMEditViewCopyLocale.submit-text'),
904
+ defaultMessage: 'Yes, fill in'
905
+ })
906
+ })
907
+ ]
908
+ })
909
+ })
910
+ ]
911
+ })
912
+ }
913
+ };
914
+ };
915
+ /* -------------------------------------------------------------------------------------------------
916
+ * DeleteLocaleAction
917
+ * -----------------------------------------------------------------------------------------------*/ const DeleteLocaleAction = ({ document, documentId, model, collectionType })=>{
918
+ const { formatMessage } = reactIntl.useIntl();
919
+ const navigate = reactRouterDom.useNavigate();
920
+ const { toggleNotification } = strapiAdmin.useNotification();
921
+ const { delete: deleteAction, isLoading } = strapiAdmin$1.unstable_useDocumentActions();
922
+ const { hasI18n, canDelete } = useI18n();
923
+ // Get the current locale object, using the URL instead of document so it works while creating
924
+ const [{ query }] = strapiAdmin.useQueryParams();
925
+ const { data: locales = [] } = useGetLocalesQuery();
926
+ const currentDesiredLocale = query.plugins?.i18n?.locale;
927
+ const locale = !('error' in locales) && locales.find((loc)=>loc.code === currentDesiredLocale);
928
+ if (!hasI18n) {
929
+ return null;
930
+ }
931
+ return {
932
+ disabled: document?.locale && !canDelete.includes(document.locale) || !document || !document.id,
933
+ position: [
934
+ 'header',
935
+ 'table-row'
936
+ ],
937
+ label: formatMessage({
938
+ id: getTranslation('actions.delete.label'),
939
+ defaultMessage: 'Delete entry ({locale})'
940
+ }, {
941
+ locale: locale && locale.name
942
+ }),
943
+ icon: /*#__PURE__*/ jsxRuntime.jsx(StyledTrash, {}),
944
+ variant: 'danger',
945
+ dialog: {
946
+ type: 'dialog',
947
+ title: formatMessage({
948
+ id: getTranslation('actions.delete.dialog.title'),
949
+ defaultMessage: 'Confirmation'
950
+ }),
951
+ content: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
952
+ direction: "column",
953
+ gap: 2,
954
+ children: [
955
+ /*#__PURE__*/ jsxRuntime.jsx(icons.WarningCircle, {
956
+ width: "24px",
957
+ height: "24px",
958
+ fill: "danger600"
959
+ }),
960
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
961
+ tag: "p",
962
+ variant: "omega",
963
+ textAlign: "center",
964
+ children: formatMessage({
965
+ id: getTranslation('actions.delete.dialog.body'),
966
+ defaultMessage: 'Are you sure?'
967
+ })
968
+ })
969
+ ]
970
+ }),
971
+ loading: isLoading,
972
+ onConfirm: async ()=>{
973
+ const unableToDelete = // We are unable to delete a collection type without a document ID
974
+ // & unable to delete generally if there is no document locale
975
+ collectionType !== 'single-types' && !documentId || !document?.locale;
976
+ if (unableToDelete) {
977
+ console.error("You're trying to delete a document without an id or locale, this is likely a bug with Strapi. Please open an issue.");
978
+ toggleNotification({
979
+ message: formatMessage({
980
+ id: getTranslation('actions.delete.error'),
981
+ defaultMessage: 'An error occurred while trying to delete the document locale.'
982
+ }),
983
+ type: 'danger'
984
+ });
985
+ return;
986
+ }
987
+ const res = await deleteAction({
988
+ documentId,
989
+ model,
990
+ collectionType,
991
+ params: {
992
+ locale: document.locale
993
+ }
994
+ });
995
+ if (!('error' in res)) {
996
+ navigate({
997
+ pathname: `../${collectionType}/${model}`
998
+ }, {
999
+ replace: true
1000
+ });
1001
+ }
1002
+ }
1003
+ }
1004
+ };
1005
+ };
1006
+ /* -------------------------------------------------------------------------------------------------
1007
+ * BulkLocaleAction
1008
+ *
1009
+ * This component is used to handle bulk publish and unpublish actions on locales.
1010
+ * -----------------------------------------------------------------------------------------------*/ const BulkLocaleAction = ({ document, documentId, model, collectionType, action })=>{
1011
+ const locale = document?.locale ?? null;
1012
+ const [{ query: query$1 }] = strapiAdmin.useQueryParams();
1013
+ const params = React__namespace.useMemo(()=>strapiAdmin$1.buildValidParams(query$1), [
1014
+ query$1
1015
+ ]);
1016
+ const isOnPublishedTab = query$1.status === 'published';
1017
+ const { formatMessage } = reactIntl.useIntl();
1018
+ const { hasI18n, canPublish } = useI18n();
1019
+ const { toggleNotification } = strapiAdmin.useNotification();
1020
+ const { _unstableFormatAPIError: formatAPIError } = strapiAdmin.useAPIErrorHandler();
1021
+ const [selectedRows, setSelectedRows] = React__namespace.useState([]);
1022
+ const [isDraftRelationConfirmationOpen, setIsDraftRelationConfirmationOpen] = React__namespace.useState(false);
1023
+ const { publishMany: publishManyAction, unpublishMany: unpublishManyAction } = strapiAdmin$1.unstable_useDocumentActions();
1024
+ const { schema, validate } = strapiAdmin$1.unstable_useDocument({
1025
+ model,
1026
+ collectionType,
1027
+ documentId,
1028
+ params: {
1029
+ locale
1030
+ }
1031
+ }, {
1032
+ // No need to fetch the document, the data is already available in the `document` prop
1033
+ skip: true
1034
+ });
1035
+ const { data: localesMetadata = [] } = useGetLocalesQuery(hasI18n ? undefined : query.skipToken);
1036
+ const headers = [
1037
+ {
1038
+ label: formatMessage({
1039
+ id: 'global.name',
1040
+ defaultMessage: 'Name'
1041
+ }),
1042
+ name: 'name'
1043
+ },
1044
+ {
1045
+ label: formatMessage({
1046
+ id: getTranslation('CMEditViewBulkLocale.status'),
1047
+ defaultMessage: 'Status'
1048
+ }),
1049
+ name: 'status'
1050
+ },
1051
+ {
1052
+ label: formatMessage({
1053
+ id: getTranslation('CMEditViewBulkLocale.publication-status'),
1054
+ defaultMessage: 'Publication Status'
1055
+ }),
1056
+ name: 'publication-status'
1057
+ }
1058
+ ];
1059
+ // Extract the rows for the bulk locale publish modal and any validation
1060
+ // errors per locale
1061
+ const [rows, validationErrors] = React__namespace.useMemo(()=>{
1062
+ if (!document) {
1063
+ return [
1064
+ [],
1065
+ {}
1066
+ ];
1067
+ }
1068
+ const localizations = document.localizations ?? [];
1069
+ // Build the rows for the bulk locale publish modal by combining the current
1070
+ // document with all the available locales from the document meta
1071
+ const locales = localizations.map((doc)=>{
1072
+ const { locale, status } = doc;
1073
+ return {
1074
+ locale,
1075
+ status
1076
+ };
1077
+ });
1078
+ // Add the current document locale
1079
+ locales.unshift({
1080
+ locale: document.locale,
1081
+ status: document.status
1082
+ });
1083
+ // Build the validation errors for each locale.
1084
+ const allDocuments = [
1085
+ document,
1086
+ ...localizations
1087
+ ];
1088
+ const errors = allDocuments.reduce((errs, document)=>{
1089
+ if (!document) {
1090
+ return errs;
1091
+ }
1092
+ // Validate each locale entry via the useDocument validate function and store any errors in a dictionary
1093
+ const validation = validate(document);
1094
+ if (validation !== null) {
1095
+ errs[document.locale] = validation;
1096
+ }
1097
+ return errs;
1098
+ }, {});
1099
+ return [
1100
+ locales,
1101
+ errors
1102
+ ];
1103
+ }, [
1104
+ document,
1105
+ validate
1106
+ ]);
1107
+ const isBulkPublish = action === 'bulk-publish';
1108
+ const localesForAction = selectedRows.reduce((acc, selectedRow)=>{
1109
+ const isValidLocale = // Validation errors are irrelevant if we are trying to unpublish
1110
+ !isBulkPublish || !Object.keys(validationErrors).includes(selectedRow.locale);
1111
+ const shouldAddLocale = isBulkPublish ? selectedRow.status !== 'published' && isValidLocale : selectedRow.status !== 'draft' && isValidLocale;
1112
+ if (shouldAddLocale) {
1113
+ acc.push(selectedRow.locale);
1114
+ }
1115
+ return acc;
1116
+ }, []);
1117
+ // TODO skipping this for now as there is a bug with the draft relation count that will be worked on separately
1118
+ // see https://www.notion.so/strapi/Count-draft-relations-56901b492efb45ab90d42fe975b32bd8?pvs=4
1119
+ const enableDraftRelationsCount = false;
1120
+ const { data: draftRelationsCount = 0, isLoading: isDraftRelationsLoading, error: isDraftRelationsError } = useGetManyDraftRelationCountQuery({
1121
+ model,
1122
+ documentIds: [
1123
+ documentId
1124
+ ],
1125
+ locale: localesForAction
1126
+ }, {
1127
+ skip: !enableDraftRelationsCount
1128
+ });
1129
+ React__namespace.useEffect(()=>{
1130
+ if (isDraftRelationsError) {
1131
+ toggleNotification({
1132
+ type: 'danger',
1133
+ message: formatAPIError(isDraftRelationsError)
1134
+ });
1135
+ }
1136
+ }, [
1137
+ isDraftRelationsError,
1138
+ toggleNotification,
1139
+ formatAPIError
1140
+ ]);
1141
+ if (!schema?.options?.draftAndPublish) {
1142
+ return null;
1143
+ }
1144
+ if (!hasI18n) {
1145
+ return null;
1146
+ }
1147
+ if (!documentId) {
1148
+ return null;
1149
+ }
1150
+ // This document action can be enabled given that draft and publish and i18n are
1151
+ // enabled and we can publish the current locale.
1152
+ const publish = async ()=>{
1153
+ await publishManyAction({
1154
+ model,
1155
+ documentIds: [
1156
+ documentId
1157
+ ],
1158
+ params: {
1159
+ ...params,
1160
+ locale: localesForAction
1161
+ }
1162
+ });
1163
+ setSelectedRows([]);
1164
+ };
1165
+ const unpublish = async ()=>{
1166
+ await unpublishManyAction({
1167
+ model,
1168
+ documentIds: [
1169
+ documentId
1170
+ ],
1171
+ params: {
1172
+ ...params,
1173
+ locale: localesForAction
1174
+ }
1175
+ });
1176
+ setSelectedRows([]);
1177
+ };
1178
+ const handleAction = async ()=>{
1179
+ if (draftRelationsCount > 0) {
1180
+ setIsDraftRelationConfirmationOpen(true);
1181
+ } else if (isBulkPublish) {
1182
+ await publish();
1183
+ } else {
1184
+ await unpublish();
1185
+ }
1186
+ };
1187
+ if (isDraftRelationConfirmationOpen) {
1188
+ return {
1189
+ label: formatMessage({
1190
+ id: 'app.components.ConfirmDialog.title',
1191
+ defaultMessage: 'Confirmation'
1192
+ }),
1193
+ variant: 'danger',
1194
+ dialog: {
1195
+ onCancel: ()=>{
1196
+ setIsDraftRelationConfirmationOpen(false);
1197
+ },
1198
+ onConfirm: async ()=>{
1199
+ await publish();
1200
+ setIsDraftRelationConfirmationOpen(false);
1201
+ },
1202
+ type: 'dialog',
1203
+ title: formatMessage({
1204
+ id: getTranslation('actions.publish.dialog.title'),
1205
+ defaultMessage: 'Confirmation'
1206
+ }),
1207
+ content: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
1208
+ direction: "column",
1209
+ alignItems: "center",
1210
+ gap: 2,
1211
+ children: [
1212
+ /*#__PURE__*/ jsxRuntime.jsx(icons.WarningCircle, {
1213
+ width: "2.4rem",
1214
+ height: "2.4rem",
1215
+ fill: "danger600"
1216
+ }),
1217
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1218
+ textAlign: "center",
1219
+ children: formatMessage({
1220
+ id: getTranslation('CMEditViewBulkLocale.draft-relation-warning'),
1221
+ defaultMessage: 'Some locales are related to draft entries. Publishing them could leave broken links in your app.'
1222
+ })
1223
+ }),
1224
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1225
+ textAlign: "center",
1226
+ children: formatMessage({
1227
+ id: getTranslation('CMEditViewBulkLocale.continue-confirmation'),
1228
+ defaultMessage: 'Are you sure you want to continue?'
1229
+ })
1230
+ })
1231
+ ]
1232
+ })
1233
+ }
1234
+ };
1235
+ }
1236
+ const hasPermission = selectedRows.map(({ locale })=>locale).every((locale)=>canPublish.includes(locale));
1237
+ return {
1238
+ label: formatMessage({
1239
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? 'publish' : 'unpublish'}-title`),
1240
+ defaultMessage: `${isBulkPublish ? 'Publish' : 'Unpublish'} Multiple Locales`
1241
+ }),
1242
+ variant: isBulkPublish ? 'secondary' : 'danger',
1243
+ icon: isBulkPublish ? /*#__PURE__*/ jsxRuntime.jsx(icons.ListPlus, {}) : /*#__PURE__*/ jsxRuntime.jsx(icons.Cross, {}),
1244
+ disabled: isOnPublishedTab || canPublish.length === 0,
1245
+ position: [
1246
+ 'panel'
1247
+ ],
1248
+ dialog: {
1249
+ type: 'modal',
1250
+ title: formatMessage({
1251
+ id: getTranslation(`CMEditViewBulkLocale.${isBulkPublish ? 'publish' : 'unpublish'}-title`),
1252
+ defaultMessage: `${isBulkPublish ? 'Publish' : 'Unpublish'} Multiple Locales`
1253
+ }),
1254
+ content: ()=>{
1255
+ return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Table.Root, {
1256
+ headers: headers,
1257
+ rows: rows.map((row)=>({
1258
+ ...row,
1259
+ id: row.locale
1260
+ })),
1261
+ selectedRows: selectedRows,
1262
+ onSelectedRowsChange: (tableSelectedRows)=>setSelectedRows(tableSelectedRows),
1263
+ children: /*#__PURE__*/ jsxRuntime.jsx(BulkLocaleActionModal, {
1264
+ validationErrors: validationErrors,
1265
+ headers: headers,
1266
+ rows: rows,
1267
+ localesMetadata: localesMetadata,
1268
+ action: action ?? 'bulk-publish'
1269
+ })
1270
+ });
1271
+ },
1272
+ footer: ()=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Footer, {
1273
+ justifyContent: "flex-end",
1274
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
1275
+ loading: isDraftRelationsLoading,
1276
+ disabled: !hasPermission || localesForAction.length === 0,
1277
+ variant: "default",
1278
+ onClick: handleAction,
1279
+ children: formatMessage({
1280
+ id: isBulkPublish ? 'app.utils.publish' : 'app.utils.unpublish',
1281
+ defaultMessage: isBulkPublish ? 'Publish' : 'Unpublish'
1282
+ })
1283
+ })
1284
+ })
1285
+ }
1286
+ };
1287
+ };
1288
+ /* -------------------------------------------------------------------------------------------------
1289
+ * BulkLocalePublishAction
1290
+ * -----------------------------------------------------------------------------------------------*/ const BulkLocalePublishAction = (props)=>{
1291
+ return BulkLocaleAction({
1292
+ action: 'bulk-publish',
1293
+ ...props
1294
+ });
1295
+ };
1296
+ /* -------------------------------------------------------------------------------------------------
1297
+ * BulkLocaleUnpublishAction
1298
+ * -----------------------------------------------------------------------------------------------*/ const BulkLocaleUnpublishAction = (props)=>{
1299
+ return BulkLocaleAction({
1300
+ action: 'bulk-unpublish',
1301
+ ...props
1302
+ });
1303
+ };
1304
+ /**
1305
+ * Because the icon system is completely broken, we have to do
1306
+ * this to remove the fill from the cog.
1307
+ */ const StyledTrash = styledComponents.styled(icons.Trash)`
1308
+ path {
1309
+ fill: currentColor;
1310
+ }
1311
+ `;
1312
+
1313
+ const Emphasis = (chunks)=>{
1314
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1315
+ fontWeight: "semiBold",
1316
+ textColor: "danger500",
1317
+ children: chunks
1318
+ });
1319
+ };
1320
+ const DeleteModalAdditionalInfo = ()=>{
1321
+ const { hasI18n } = useI18n();
1322
+ const { formatMessage } = reactIntl.useIntl();
1323
+ if (!hasI18n) {
1324
+ return null;
1325
+ }
1326
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1327
+ textColor: "danger500",
1328
+ children: formatMessage({
1329
+ id: getTranslation('Settings.list.actions.deleteAdditionalInfos'),
1330
+ defaultMessage: 'This will delete the active locale versions <em>(from Internationalization)</em>'
1331
+ }, {
1332
+ em: Emphasis
1333
+ })
1334
+ });
1335
+ };
1336
+ const PublishModalAdditionalInfo = ()=>{
1337
+ const { hasI18n } = useI18n();
1338
+ const { formatMessage } = reactIntl.useIntl();
1339
+ if (!hasI18n) {
1340
+ return null;
1341
+ }
1342
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1343
+ textColor: "danger500",
1344
+ children: formatMessage({
1345
+ id: getTranslation('Settings.list.actions.publishAdditionalInfos'),
1346
+ defaultMessage: 'This will publish the active locale versions <em>(from Internationalization)</em>'
1347
+ }, {
1348
+ em: Emphasis
1349
+ })
1350
+ });
1351
+ };
1352
+ const UnpublishModalAdditionalInfo = ()=>{
1353
+ const { hasI18n } = useI18n();
1354
+ const { formatMessage } = reactIntl.useIntl();
1355
+ if (!hasI18n) {
1356
+ return null;
1357
+ }
1358
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1359
+ textColor: "danger500",
1360
+ children: formatMessage({
1361
+ id: getTranslation('Settings.list.actions.unpublishAdditionalInfos'),
1362
+ defaultMessage: 'This will unpublish the active locale versions <em>(from Internationalization)</em>'
1363
+ }, {
1364
+ em: Emphasis
1365
+ })
1366
+ });
1367
+ };
1368
+
1369
+ const LocalePicker = ()=>{
1370
+ const { formatMessage } = reactIntl.useIntl();
1371
+ const [{ query }, setQuery] = strapiAdmin.useQueryParams();
1372
+ const { hasI18n, canRead, canCreate } = useI18n();
1373
+ const { data: locales = [] } = useGetLocalesQuery(undefined, {
1374
+ skip: !hasI18n
1375
+ });
1376
+ const handleChange = React__namespace.useCallback((code, replace = false)=>{
1377
+ setQuery({
1378
+ page: 1,
1379
+ plugins: {
1380
+ ...query.plugins,
1381
+ i18n: {
1382
+ locale: code
1383
+ }
1384
+ }
1385
+ }, 'push', replace);
1386
+ }, [
1387
+ query.plugins,
1388
+ setQuery
1389
+ ]);
1390
+ React__namespace.useEffect(()=>{
1391
+ if (!Array.isArray(locales) || !hasI18n) {
1392
+ return;
1393
+ }
1394
+ /**
1395
+ * Handle the case where the current locale query param doesn't exist
1396
+ * in the list of available locales, so we redirect to the default locale.
1397
+ */ const currentDesiredLocale = query.plugins?.i18n?.locale;
1398
+ const doesLocaleExist = locales.find((loc)=>loc.code === currentDesiredLocale);
1399
+ const defaultLocale = locales.find((locale)=>locale.isDefault);
1400
+ if (!doesLocaleExist && defaultLocale?.code) {
1401
+ handleChange(defaultLocale.code, true);
1402
+ }
1403
+ }, [
1404
+ hasI18n,
1405
+ handleChange,
1406
+ locales,
1407
+ query.plugins?.i18n?.locale
1408
+ ]);
1409
+ if (!hasI18n || !Array.isArray(locales) || locales.length === 0) {
1410
+ return null;
1411
+ }
1412
+ const displayedLocales = locales.filter((locale)=>{
1413
+ /**
1414
+ * If you can create or read we allow you to see the locale exists
1415
+ * this is because in the ListView, you may be able to create a new entry
1416
+ * in a locale you can't read.
1417
+ */ return canCreate.includes(locale.code) || canRead.includes(locale.code);
1418
+ });
1419
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelect, {
1420
+ size: "S",
1421
+ "aria-label": formatMessage({
1422
+ id: getTranslation('actions.select-locale'),
1423
+ defaultMessage: 'Select locale'
1424
+ }),
1425
+ value: query.plugins?.i18n?.locale || locales.find((locale)=>locale.isDefault)?.code,
1426
+ // @ts-expect-error – This can be removed in V2 of the DS.
1427
+ onChange: handleChange,
1428
+ children: displayedLocales.map((locale)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelectOption, {
1429
+ value: locale.code,
1430
+ children: locale.name
1431
+ }, locale.id))
1432
+ });
1433
+ };
1434
+
1435
+ const PERMISSIONS = {
1436
+ accessMain: [
1437
+ {
1438
+ action: 'plugin::i18n.locale.read',
1439
+ subject: null
1440
+ }
1441
+ ],
1442
+ create: [
1443
+ {
1444
+ action: 'plugin::i18n.locale.create',
1445
+ subject: null
1446
+ }
1447
+ ],
1448
+ delete: [
1449
+ {
1450
+ action: 'plugin::i18n.locale.delete',
1451
+ subject: null
1452
+ }
1453
+ ],
1454
+ update: [
1455
+ {
1456
+ action: 'plugin::i18n.locale.update',
1457
+ subject: null
1458
+ }
1459
+ ],
1460
+ read: [
1461
+ {
1462
+ action: 'plugin::i18n.locale.read',
1463
+ subject: null
1464
+ }
1465
+ ]
1466
+ };
1467
+
1468
+ const mutateEditViewHook = ({ layout })=>{
1469
+ // If i18n isn't explicitly enabled on the content type, then no field can be localized
1470
+ if (!('i18n' in layout.options) || typeof layout.options.i18n === 'object' && layout.options.i18n !== null && 'localized' in layout.options.i18n && !layout.options.i18n.localized) {
1471
+ return {
1472
+ layout
1473
+ };
1474
+ }
1475
+ const components = Object.entries(layout.components).reduce((acc, [key, componentLayout])=>{
1476
+ return {
1477
+ ...acc,
1478
+ [key]: {
1479
+ ...componentLayout,
1480
+ layout: componentLayout.layout.map((row)=>row.map(addLabelActionToField))
1481
+ }
1482
+ };
1483
+ }, {});
1484
+ return {
1485
+ layout: {
1486
+ ...layout,
1487
+ components,
1488
+ layout: layout.layout.map((panel)=>panel.map((row)=>row.map(addLabelActionToField)))
1489
+ }
1490
+ };
1491
+ };
1492
+ const addLabelActionToField = (field)=>{
1493
+ const isFieldLocalized = doesFieldHaveI18nPluginOpt(field.attribute.pluginOptions) ? field.attribute.pluginOptions.i18n.localized : true;
1494
+ const labelActionProps = {
1495
+ title: {
1496
+ id: isFieldLocalized ? getTranslation('Field.localized') : getTranslation('Field.not-localized'),
1497
+ defaultMessage: isFieldLocalized ? 'This value is unique for the selected locale' : 'This value is the same across all locales'
1498
+ },
1499
+ icon: isFieldLocalized ? /*#__PURE__*/ jsxRuntime.jsx(icons.Earth, {}) : null
1500
+ };
1501
+ return {
1502
+ ...field,
1503
+ labelAction: isFieldLocalized ? /*#__PURE__*/ jsxRuntime.jsx(LabelAction, {
1504
+ ...labelActionProps
1505
+ }) : null
1506
+ };
1507
+ };
1508
+ const doesFieldHaveI18nPluginOpt = (pluginOpts)=>{
1509
+ if (!pluginOpts) {
1510
+ return false;
1511
+ }
1512
+ return 'i18n' in pluginOpts && typeof pluginOpts.i18n === 'object' && pluginOpts.i18n !== null && 'localized' in pluginOpts.i18n;
1513
+ };
1514
+ const LabelAction = ({ title, icon })=>{
1515
+ const { formatMessage } = reactIntl.useIntl();
1516
+ return /*#__PURE__*/ jsxRuntime.jsxs(Span, {
1517
+ tag: "span",
1518
+ children: [
1519
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.VisuallyHidden, {
1520
+ tag: "span",
1521
+ children: formatMessage(title)
1522
+ }),
1523
+ /*#__PURE__*/ React__namespace.cloneElement(icon, {
1524
+ 'aria-hidden': true,
1525
+ focusable: false
1526
+ })
1527
+ ]
1528
+ });
1529
+ };
1530
+ const Span = styledComponents.styled(designSystem.Flex)`
1531
+ svg {
1532
+ width: 12px;
1533
+ height: 12px;
1534
+
1535
+ fill: ${({ theme })=>theme.colors.neutral500};
1536
+
1537
+ path {
1538
+ fill: ${({ theme })=>theme.colors.neutral500};
1539
+ }
1540
+ }
1541
+ `;
1542
+
1543
+ const LocaleListCell = ({ locale: currentLocale, localizations })=>{
1544
+ const { locale: language } = reactIntl.useIntl();
1545
+ const { data: locales = [] } = useGetLocalesQuery();
1546
+ const formatter = designSystem.useCollator(language, {
1547
+ sensitivity: 'base'
1548
+ });
1549
+ if (!Array.isArray(locales) || !localizations) {
1550
+ return null;
1551
+ }
1552
+ const availableLocales = localizations.map((loc)=>loc.locale);
1553
+ const localesForDocument = locales.reduce((acc, locale)=>{
1554
+ const createdLocale = [
1555
+ currentLocale,
1556
+ ...availableLocales
1557
+ ].find((loc)=>{
1558
+ return loc === locale.code;
1559
+ });
1560
+ if (createdLocale) {
1561
+ acc.push(locale);
1562
+ }
1563
+ return acc;
1564
+ }, []).map((locale)=>{
1565
+ if (locale.isDefault) {
1566
+ return `${locale.name} (default)`;
1567
+ }
1568
+ return locale.name;
1569
+ }).toSorted((a, b)=>formatter.compare(a, b));
1570
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Popover.Root, {
1571
+ children: [
1572
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
1573
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
1574
+ variant: "ghost",
1575
+ type: "button",
1576
+ onClick: (e)=>e.stopPropagation(),
1577
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
1578
+ minWidth: "100%",
1579
+ alignItems: "center",
1580
+ justifyContent: "center",
1581
+ fontWeight: "regular",
1582
+ children: [
1583
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1584
+ textColor: "neutral800",
1585
+ ellipsis: true,
1586
+ marginRight: 2,
1587
+ children: localesForDocument.join(', ')
1588
+ }),
1589
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
1590
+ children: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {
1591
+ width: "1.2rem",
1592
+ height: "1.2rem"
1593
+ })
1594
+ })
1595
+ ]
1596
+ })
1597
+ })
1598
+ }),
1599
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Content, {
1600
+ sideOffset: 16,
1601
+ children: /*#__PURE__*/ jsxRuntime.jsx("ul", {
1602
+ children: localesForDocument.map((name)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
1603
+ padding: 3,
1604
+ tag: "li",
1605
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1606
+ children: name
1607
+ })
1608
+ }, name))
1609
+ })
1610
+ })
1611
+ ]
1612
+ });
1613
+ };
1614
+
1615
+ const addColumnToTableHook = ({ displayedHeaders, layout })=>{
1616
+ const { options } = layout;
1617
+ const isFieldLocalized = doesPluginOptionsHaveI18nLocalized(options) ? options.i18n.localized : false;
1618
+ if (!isFieldLocalized) {
1619
+ return {
1620
+ displayedHeaders,
1621
+ layout
1622
+ };
1623
+ }
1624
+ return {
1625
+ displayedHeaders: [
1626
+ ...displayedHeaders,
1627
+ {
1628
+ attribute: {
1629
+ type: 'string'
1630
+ },
1631
+ label: {
1632
+ id: getTranslation('list-view.table.header.label'),
1633
+ defaultMessage: 'Available in'
1634
+ },
1635
+ searchable: false,
1636
+ sortable: false,
1637
+ name: 'locales',
1638
+ // @ts-expect-error – ID is seen as number | string; this will change when we move the type over.
1639
+ cellFormatter: (props, _header, meta)=>/*#__PURE__*/ jsxRuntime.jsx(LocaleListCell, {
1640
+ ...props,
1641
+ ...meta
1642
+ })
1643
+ }
1644
+ ],
1645
+ layout
1646
+ };
1647
+ };
1648
+
1649
+ const addLocaleToReleasesHook = ({ displayedHeaders = [] })=>{
1650
+ return {
1651
+ displayedHeaders: [
1652
+ ...displayedHeaders,
1653
+ {
1654
+ label: {
1655
+ id: 'content-releases.page.ReleaseDetails.table.header.label.locale',
1656
+ defaultMessage: 'locale'
1657
+ },
1658
+ name: 'locale'
1659
+ }
1660
+ ],
1661
+ hasI18nEnabled: true
1662
+ };
1663
+ };
1664
+
1665
+ const extendCTBAttributeInitialDataMiddleware = ()=>{
1666
+ return ({ getState })=>(next)=>(action)=>{
1667
+ const enhanceAction = ()=>{
1668
+ // the block here is to catch the error when trying to access the state
1669
+ // of the ctb when the plugin is not mounted
1670
+ try {
1671
+ const store = getState();
1672
+ const hasi18nEnabled = get(store, [
1673
+ 'content-type-builder_dataManagerProvider',
1674
+ 'modifiedData',
1675
+ 'contentType',
1676
+ 'schema',
1677
+ 'pluginOptions',
1678
+ 'i18n',
1679
+ 'localized'
1680
+ ], false);
1681
+ if (hasi18nEnabled) {
1682
+ const pluginOptions = action.options ? {
1683
+ ...action.options.pluginOptions,
1684
+ i18n: {
1685
+ localized: true
1686
+ }
1687
+ } : {
1688
+ i18n: {
1689
+ localized: true
1690
+ }
1691
+ };
1692
+ return next({
1693
+ ...action,
1694
+ options: {
1695
+ pluginOptions
1696
+ }
1697
+ });
1698
+ }
1699
+ return next(action);
1700
+ } catch (err) {
1701
+ return next(action);
1702
+ }
1703
+ };
1704
+ if (action.type === 'ContentTypeBuilder/FormModal/SET_ATTRIBUTE_DATA_SCHEMA' && action.forTarget === 'contentType' && ![
1705
+ 'relation',
1706
+ 'component'
1707
+ ].includes(action.attributeType) && !action.isEditing) {
1708
+ return enhanceAction();
1709
+ }
1710
+ if (action.type === 'ContentTypeBuilder/FormModal/SET_CUSTOM_FIELD_DATA_SCHEMA' && action.forTarget === 'contentType' && !action.isEditing) {
1711
+ return enhanceAction();
1712
+ }
1713
+ if ((action.type === 'ContentTypeBuilder/FormModal/RESET_PROPS_AND_SET_FORM_FOR_ADDING_AN_EXISTING_COMPO' || action.type === 'ContentTypeBuilder/FormModal/RESET_PROPS_AND_SAVE_CURRENT_DATA') && action.forTarget === 'contentType') {
1714
+ return enhanceAction();
1715
+ }
1716
+ return next(action);
1717
+ };
1718
+ };
1719
+
1720
+ const extendCTBInitialDataMiddleware = ()=>{
1721
+ return ()=>(next)=>(action)=>{
1722
+ if (action.type === 'ContentTypeBuilder/FormModal/SET_DATA_TO_EDIT' && action.modalType === 'contentType') {
1723
+ const i18n = {
1724
+ localized: false
1725
+ };
1726
+ const pluginOptions = action.data.pluginOptions ? {
1727
+ ...action.data.pluginOptions,
1728
+ i18n
1729
+ } : {
1730
+ i18n
1731
+ };
1732
+ const data = {
1733
+ ...action.data,
1734
+ pluginOptions
1735
+ };
1736
+ if (action.actionType === 'create') {
1737
+ return next({
1738
+ ...action,
1739
+ data
1740
+ });
1741
+ }
1742
+ // Override the action if the pluginOption config does not contain i18n
1743
+ // In this case we need to set the proper initialData shape
1744
+ if (!action.data.pluginOptions?.i18n?.localized) {
1745
+ return next({
1746
+ ...action,
1747
+ data
1748
+ });
1749
+ }
1750
+ }
1751
+ // action is not the one we want to override
1752
+ return next(action);
1753
+ };
1754
+ };
1755
+
1756
+ const localeMiddleware = (ctx)=>(next)=>(permissions)=>{
1757
+ const match = reactRouterDom.matchPath('/content-manager/:collectionType/:model?/:id', ctx.pathname);
1758
+ if (!match) {
1759
+ return next(permissions);
1760
+ }
1761
+ const search = qs__namespace.parse(ctx.search);
1762
+ if (typeof search !== 'object') {
1763
+ return next(permissions);
1764
+ }
1765
+ if (!('plugins' in search && typeof search.plugins === 'object')) {
1766
+ return next(permissions);
1767
+ }
1768
+ if (!('i18n' in search.plugins && typeof search.plugins.i18n === 'object' && !Array.isArray(search.plugins.i18n))) {
1769
+ return next(permissions);
1770
+ }
1771
+ const { locale } = search.plugins.i18n;
1772
+ if (typeof locale !== 'string') {
1773
+ return next(permissions);
1774
+ }
1775
+ const revisedPermissions = permissions.filter((permission)=>!permission.properties?.locales || permission.properties.locales.includes(locale));
1776
+ return next(revisedPermissions);
1777
+ };
1778
+
1779
+ const prefixPluginTranslations = (trad, pluginId)=>{
1780
+ return Object.keys(trad).reduce((acc, current)=>{
1781
+ acc[`${pluginId}.${current}`] = trad[current];
1782
+ return acc;
1783
+ }, {});
1784
+ };
1785
+
1786
+ /* -------------------------------------------------------------------------------------------------
1787
+ * mutateCTBContentTypeSchema
1788
+ * -----------------------------------------------------------------------------------------------*/ const mutateCTBContentTypeSchema = (nextSchema, prevSchema)=>{
1789
+ // Don't perform mutations components
1790
+ if (!doesPluginOptionsHaveI18nLocalized(nextSchema.pluginOptions)) {
1791
+ return nextSchema;
1792
+ }
1793
+ const isNextSchemaLocalized = nextSchema.pluginOptions.i18n.localized;
1794
+ const isPrevSchemaLocalized = doesPluginOptionsHaveI18nLocalized(prevSchema?.schema?.pluginOptions) ? prevSchema?.schema?.pluginOptions.i18n.localized : false;
1795
+ // No need to perform modification on the schema, if the i18n feature was not changed
1796
+ // at the ct level
1797
+ if (isNextSchemaLocalized && isPrevSchemaLocalized) {
1798
+ return nextSchema;
1799
+ }
1800
+ if (isNextSchemaLocalized) {
1801
+ const attributes = addLocalisationToFields(nextSchema.attributes);
1802
+ return {
1803
+ ...nextSchema,
1804
+ attributes
1805
+ };
1806
+ }
1807
+ // Remove the i18n object from the pluginOptions
1808
+ if (!isNextSchemaLocalized) {
1809
+ const pluginOptions = omit(nextSchema.pluginOptions, 'i18n');
1810
+ const attributes = disableAttributesLocalisation(nextSchema.attributes);
1811
+ return {
1812
+ ...nextSchema,
1813
+ pluginOptions,
1814
+ attributes
1815
+ };
1816
+ }
1817
+ return nextSchema;
1818
+ };
1819
+ /* -------------------------------------------------------------------------------------------------
1820
+ * addLocalisationToFields
1821
+ * -----------------------------------------------------------------------------------------------*/ const addLocalisationToFields = (attributes)=>Object.keys(attributes).reduce((acc, current)=>{
1822
+ const currentAttribute = attributes[current];
1823
+ if (LOCALIZED_FIELDS.includes(currentAttribute.type)) {
1824
+ const i18n = {
1825
+ localized: true
1826
+ };
1827
+ const pluginOptions = currentAttribute.pluginOptions ? {
1828
+ ...currentAttribute.pluginOptions,
1829
+ i18n
1830
+ } : {
1831
+ i18n
1832
+ };
1833
+ acc[current] = {
1834
+ ...currentAttribute,
1835
+ pluginOptions
1836
+ };
1837
+ return acc;
1838
+ }
1839
+ acc[current] = currentAttribute;
1840
+ return acc;
1841
+ }, {});
1842
+ const disableAttributesLocalisation = (attributes)=>Object.keys(attributes).reduce((acc, current)=>{
1843
+ acc[current] = omit(attributes[current], 'pluginOptions.i18n');
1844
+ return acc;
1845
+ }, {});
1846
+
1847
+ function __variableDynamicImportRuntime1__(path) {
1848
+ switch (path) {
1849
+ case './translations/de.json': return Promise.resolve().then(function () { return require('./de-nEMWvIiY.js'); });
1850
+ case './translations/dk.json': return Promise.resolve().then(function () { return require('./dk-CYATLPVe.js'); });
1851
+ case './translations/en.json': return Promise.resolve().then(function () { return require('./en-CG5cUCbD.js'); });
1852
+ case './translations/es.json': return Promise.resolve().then(function () { return require('./es-CWsogTGm.js'); });
1853
+ case './translations/fr.json': return Promise.resolve().then(function () { return require('./fr-CC7UFcYd.js'); });
1854
+ case './translations/ko.json': return Promise.resolve().then(function () { return require('./ko-XwGmfhoq.js'); });
1855
+ case './translations/pl.json': return Promise.resolve().then(function () { return require('./pl-B_vzY_ZB.js'); });
1856
+ case './translations/ru.json': return Promise.resolve().then(function () { return require('./ru-WzHcJV1f.js'); });
1857
+ case './translations/tr.json': return Promise.resolve().then(function () { return require('./tr-CcWp6u3w.js'); });
1858
+ case './translations/zh-Hans.json': return Promise.resolve().then(function () { return require('./zh-Hans-DnU2bhri.js'); });
1859
+ case './translations/zh.json': return Promise.resolve().then(function () { return require('./zh-C9So4SGq.js'); });
1860
+ default: return new Promise(function(resolve, reject) {
1861
+ (typeof queueMicrotask === 'function' ? queueMicrotask : setTimeout)(
1862
+ reject.bind(null, new Error("Unknown variable dynamic import: " + path))
1863
+ );
1864
+ })
1865
+ }
1866
+ }
1867
+ // eslint-disable-next-line import/no-default-export
1868
+ var index = {
1869
+ register (app) {
1870
+ app.addMiddlewares([
1871
+ extendCTBAttributeInitialDataMiddleware,
1872
+ extendCTBInitialDataMiddleware
1873
+ ]);
1874
+ app.addMiddlewares([
1875
+ ()=>i18nApi.middleware
1876
+ ]);
1877
+ app.addReducers({
1878
+ [i18nApi.reducerPath]: i18nApi.reducer
1879
+ });
1880
+ app.addRBACMiddleware([
1881
+ localeMiddleware
1882
+ ]);
1883
+ app.registerPlugin({
1884
+ id: pluginId,
1885
+ name: pluginId
1886
+ });
1887
+ },
1888
+ bootstrap (app) {
1889
+ // // Hook that adds a column into the CM's LV table
1890
+ app.registerHook('Admin/CM/pages/ListView/inject-column-in-table', addColumnToTableHook);
1891
+ app.registerHook('Admin/CM/pages/EditView/mutate-edit-view-layout', mutateEditViewHook);
1892
+ // Hooks that checks if the locale is present in the release
1893
+ app.registerHook('ContentReleases/pages/ReleaseDetails/add-locale-in-releases', addLocaleToReleasesHook);
1894
+ // Add the settings link
1895
+ app.addSettingsLink('global', {
1896
+ intlLabel: {
1897
+ id: getTranslation('plugin.name'),
1898
+ defaultMessage: 'Internationalization'
1899
+ },
1900
+ id: 'internationalization',
1901
+ to: 'internationalization',
1902
+ Component: ()=>Promise.resolve().then(function () { return require('./SettingsPage-CcMc5fyu.js'); }).then((mod)=>({
1903
+ default: mod.ProtectedSettingsPage
1904
+ })),
1905
+ permissions: PERMISSIONS.accessMain
1906
+ });
1907
+ const contentManager = app.getPlugin('content-manager');
1908
+ contentManager.apis.addDocumentHeaderAction([
1909
+ LocalePickerAction,
1910
+ FillFromAnotherLocaleAction
1911
+ ]);
1912
+ contentManager.apis.addDocumentAction((actions)=>{
1913
+ const indexOfDeleteAction = actions.findIndex((action)=>action.type === 'delete');
1914
+ actions.splice(indexOfDeleteAction, 0, DeleteLocaleAction);
1915
+ return actions;
1916
+ });
1917
+ contentManager.apis.addDocumentAction((actions)=>{
1918
+ // When enabled the bulk locale publish action should be the first action
1919
+ // in 'More Document Actions' and therefore the third action in the array
1920
+ actions.splice(2, 0, BulkLocalePublishAction);
1921
+ actions.splice(5, 0, BulkLocaleUnpublishAction);
1922
+ return actions;
1923
+ });
1924
+ contentManager.injectComponent('listView', 'actions', {
1925
+ name: 'i18n-locale-filter',
1926
+ Component: LocalePicker
1927
+ });
1928
+ contentManager.injectComponent('listView', 'publishModalAdditionalInfos', {
1929
+ name: 'i18n-publish-bullets-in-modal',
1930
+ Component: PublishModalAdditionalInfo
1931
+ });
1932
+ contentManager.injectComponent('listView', 'unpublishModalAdditionalInfos', {
1933
+ name: 'i18n-unpublish-bullets-in-modal',
1934
+ Component: UnpublishModalAdditionalInfo
1935
+ });
1936
+ contentManager.injectComponent('listView', 'deleteModalAdditionalInfos', {
1937
+ name: 'i18n-delete-bullets-in-modal',
1938
+ Component: DeleteModalAdditionalInfo
1939
+ });
1940
+ const ctbPlugin = app.getPlugin('content-type-builder');
1941
+ if (ctbPlugin) {
1942
+ const ctbFormsAPI = ctbPlugin.apis.forms;
1943
+ ctbFormsAPI.addContentTypeSchemaMutation(mutateCTBContentTypeSchema);
1944
+ ctbFormsAPI.components.add({
1945
+ id: 'checkboxConfirmation',
1946
+ component: CheckboxConfirmation
1947
+ });
1948
+ ctbFormsAPI.extendContentType({
1949
+ validator: ()=>({
1950
+ i18n: yup__namespace.object().shape({
1951
+ localized: yup__namespace.bool()
1952
+ })
1953
+ }),
1954
+ form: {
1955
+ advanced () {
1956
+ return [
1957
+ {
1958
+ name: 'pluginOptions.i18n.localized',
1959
+ description: {
1960
+ id: getTranslation('plugin.schema.i18n.localized.description-content-type'),
1961
+ defaultMessage: 'Allows translating an entry into different languages'
1962
+ },
1963
+ type: 'checkboxConfirmation',
1964
+ intlLabel: {
1965
+ id: getTranslation('plugin.schema.i18n.localized.label-content-type'),
1966
+ defaultMessage: 'Localization'
1967
+ }
1968
+ }
1969
+ ];
1970
+ }
1971
+ }
1972
+ });
1973
+ ctbFormsAPI.extendFields(LOCALIZED_FIELDS, {
1974
+ form: {
1975
+ advanced ({ contentTypeSchema, forTarget, type, step }) {
1976
+ if (forTarget !== 'contentType') {
1977
+ return [];
1978
+ }
1979
+ const hasI18nEnabled = get(contentTypeSchema, [
1980
+ 'schema',
1981
+ 'pluginOptions',
1982
+ 'i18n',
1983
+ 'localized'
1984
+ ], false);
1985
+ if (!hasI18nEnabled) {
1986
+ return [];
1987
+ }
1988
+ if (type === 'component' && step === '1') {
1989
+ return [];
1990
+ }
1991
+ return [
1992
+ {
1993
+ name: 'pluginOptions.i18n.localized',
1994
+ description: {
1995
+ id: getTranslation('plugin.schema.i18n.localized.description-field'),
1996
+ defaultMessage: 'The field can have different values in each locale'
1997
+ },
1998
+ type: 'checkbox',
1999
+ intlLabel: {
2000
+ id: getTranslation('plugin.schema.i18n.localized.label-field'),
2001
+ defaultMessage: 'Enable localization for this field'
2002
+ }
2003
+ }
2004
+ ];
2005
+ }
2006
+ }
2007
+ });
2008
+ }
2009
+ },
2010
+ async registerTrads ({ locales }) {
2011
+ const importedTrads = await Promise.all(locales.map((locale)=>{
2012
+ return __variableDynamicImportRuntime1__(`./translations/${locale}.json`).then(({ default: data })=>{
2013
+ return {
2014
+ data: prefixPluginTranslations(data, pluginId),
2015
+ locale
2016
+ };
2017
+ }).catch(()=>{
2018
+ return {
2019
+ data: {},
2020
+ locale
2021
+ };
2022
+ });
2023
+ }));
2024
+ return Promise.resolve(importedTrads);
2025
+ }
2026
+ };
2027
+
2028
+ exports.PERMISSIONS = PERMISSIONS;
2029
+ exports.getTranslation = getTranslation;
2030
+ exports.index = index;
2031
+ exports.useCreateLocaleMutation = useCreateLocaleMutation;
2032
+ exports.useDeleteLocaleMutation = useDeleteLocaleMutation;
2033
+ exports.useGetDefaultLocalesQuery = useGetDefaultLocalesQuery;
2034
+ exports.useGetLocalesQuery = useGetLocalesQuery;
2035
+ exports.useUpdateLocaleMutation = useUpdateLocaleMutation;
2036
+ //# sourceMappingURL=index-Cek49tl0.js.map