@medusajs/dashboard 2.12.3-snapshot-20251216185234 → 2.12.3
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.
- package/dist/add-locales-GGNZCABB.mjs +81 -0
- package/dist/{api-key-management-detail-6RCDH73M.mjs → api-key-management-detail-FRUN2KFK.mjs} +1 -1
- package/dist/app.css +19 -0
- package/dist/app.js +795 -680
- package/dist/app.mjs +2 -2
- package/dist/{campaign-detail-5Q4BYCPX.mjs → campaign-detail-HM3GQJLQ.mjs} +1 -1
- package/dist/{categories-metadata-J7M3XWI7.mjs → categories-metadata-WKL3MGD7.mjs} +1 -1
- package/dist/{category-detail-S5IPXMHX.mjs → category-detail-UTWWDKFP.mjs} +2 -2
- package/dist/{category-products-KPW6BA5J.mjs → category-products-XXBTCXFF.mjs} +2 -2
- package/dist/{chunk-OL24RDYM.mjs → chunk-5F427YCP.mjs} +2 -2
- package/dist/{chunk-MJDHVDOW.mjs → chunk-5ISRTMYH.mjs} +1 -1
- package/dist/{chunk-ST4P6BQN.mjs → chunk-DQUXK4WW.mjs} +1 -1
- package/dist/{chunk-YYOPBKME.mjs → chunk-DTCIBQO2.mjs} +1 -1
- package/dist/{chunk-LZFWCKOF.mjs → chunk-FKNW5MLZ.mjs} +21 -4
- package/dist/{chunk-GRZSG4EP.mjs → chunk-GLBHPDR4.mjs} +21 -16
- package/dist/{chunk-WYATCUOM.mjs → chunk-HNJ65IND.mjs} +1 -1
- package/dist/{store-add-locales-VJ4RJ7UI.mjs → chunk-IKTGFXWR.mjs} +2 -67
- package/dist/{chunk-DBXWB3RF.mjs → chunk-KSDXSKJ7.mjs} +1 -1
- package/dist/{chunk-OL6MEUKW.mjs → chunk-LWYKUORZ.mjs} +108 -105
- package/dist/{chunk-PHLCT2HA.mjs → chunk-OK6NZN2A.mjs} +1 -1
- package/dist/{chunk-ZMG5B4FG.mjs → chunk-SG2JZPTG.mjs} +1 -1
- package/dist/{chunk-CVHJAKLQ.mjs → chunk-UMCJYHAD.mjs} +1 -1
- package/dist/{collection-add-products-FU2BS3D3.mjs → collection-add-products-42F7H77E.mjs} +2 -2
- package/dist/{collection-detail-VJE7XHLV.mjs → collection-detail-PXIS3G64.mjs} +2 -2
- package/dist/{collection-list-IGA6SCNF.mjs → collection-list-O74CGY24.mjs} +2 -2
- package/dist/{collection-metadata-QK7MI3D2.mjs → collection-metadata-U6FMA4IC.mjs} +1 -1
- package/dist/{customer-detail-MOV2T3LF.mjs → customer-detail-OMTFJ6CE.mjs} +1 -1
- package/dist/{customer-group-detail-6T7OXGQD.mjs → customer-group-detail-ADK3M5LG.mjs} +1 -1
- package/dist/{customer-group-list-AJEAF5D2.mjs → customer-group-list-7ZRQ2HWU.mjs} +1 -1
- package/dist/{customers-add-customer-group-QVTVSQYM.mjs → customers-add-customer-group-5U27WHJB.mjs} +1 -1
- package/dist/{edit-rules-SMVRTCUP.mjs → edit-rules-BM2ERGVJ.mjs} +1 -1
- package/dist/en.json +1 -2
- package/dist/{inventory-create-BK52VALF.mjs → inventory-create-7MA7B5N2.mjs} +2 -2
- package/dist/{inventory-detail-ZPSEMYI2.mjs → inventory-detail-B4PRHZK3.mjs} +1 -1
- package/dist/{inventory-metadata-FNEJ3RAT.mjs → inventory-metadata-C7MJ3GY5.mjs} +1 -1
- package/dist/{inventory-stock-6WYWLWJ7.mjs → inventory-stock-WVTYPJTX.mjs} +3 -3
- package/dist/{location-detail-N3GUZSY7.mjs → location-detail-KO6EBDK5.mjs} +1 -1
- package/dist/{location-fulfillment-providers-7ZUJAGNY.mjs → location-fulfillment-providers-IORBE3E3.mjs} +2 -2
- package/dist/{location-service-zone-shipping-option-create-CNRWYZQC.mjs → location-service-zone-shipping-option-create-2R3ZFLVK.mjs} +3 -3
- package/dist/{location-service-zone-shipping-option-pricing-OGWI7VPT.mjs → location-service-zone-shipping-option-pricing-5HN2Z5RB.mjs} +2 -2
- package/dist/{login-VNOLI5YG.mjs → login-XKB6OR7I.mjs} +1 -1
- package/dist/{order-create-claim-SCDJGM46.mjs → order-create-claim-NKCOGF4A.mjs} +1 -1
- package/dist/{order-create-edit-2WALBPXS.mjs → order-create-edit-UNQYXGLL.mjs} +1 -1
- package/dist/{order-create-exchange-LQU4YN7F.mjs → order-create-exchange-WI7OA2WO.mjs} +1 -1
- package/dist/{order-create-fulfillment-OWUVTZXW.mjs → order-create-fulfillment-2LJTEWDY.mjs} +1 -1
- package/dist/{order-create-refund-Q6HQY42R.mjs → order-create-refund-7K6UJXGP.mjs} +1 -1
- package/dist/{order-create-shipment-WAGGEPRW.mjs → order-create-shipment-ZTDLLUBY.mjs} +1 -1
- package/dist/{order-detail-PVPGEWGY.mjs → order-detail-JTRUMRLO.mjs} +1 -1
- package/dist/{order-edit-billing-address-UM76J4KX.mjs → order-edit-billing-address-YHYNVLOE.mjs} +1 -1
- package/dist/{order-edit-email-CL3KNOCM.mjs → order-edit-email-TCQPEVZY.mjs} +1 -1
- package/dist/{order-edit-shipping-address-PIESTGVL.mjs → order-edit-shipping-address-CFSYQLKD.mjs} +1 -1
- package/dist/{order-export-LE363ZLB.mjs → order-export-G4SBNEJ7.mjs} +1 -1
- package/dist/{order-metadata-FHBB7MTG.mjs → order-metadata-KGPB37VL.mjs} +1 -1
- package/dist/{order-receive-return-PRVKP6J2.mjs → order-receive-return-JER24SEV.mjs} +1 -1
- package/dist/{order-request-transfer-XSAGRUMT.mjs → order-request-transfer-3FBUYZNT.mjs} +1 -1
- package/dist/{price-list-create-K5JEZT57.mjs → price-list-create-CXZCFFTP.mjs} +4 -4
- package/dist/{price-list-detail-Q5VG5VGW.mjs → price-list-detail-XOMU6U5J.mjs} +2 -2
- package/dist/{price-list-prices-add-2MQ226U4.mjs → price-list-prices-add-SDX5CQME.mjs} +4 -4
- package/dist/{price-list-prices-edit-OJZLV7OS.mjs → price-list-prices-edit-EKB6NI5D.mjs} +2 -2
- package/dist/{product-attributes-YF4TZOIO.mjs → product-attributes-STD47BGC.mjs} +2 -2
- package/dist/{product-create-KJML2332.mjs → product-create-LVGWVQAT.mjs} +3 -3
- package/dist/{product-create-variant-5EBCLM54.mjs → product-create-variant-OTJKT6WI.mjs} +2 -2
- package/dist/{product-detail-QG72542C.mjs → product-detail-OYVHJH3D.mjs} +2 -2
- package/dist/{product-edit-DZZR775Q.mjs → product-edit-3SIUUIW4.mjs} +2 -2
- package/dist/{product-export-5AD7NELI.mjs → product-export-57UUAGXF.mjs} +2 -2
- package/dist/{product-image-variants-edit-M6QF2RLE.mjs → product-image-variants-edit-2BW5BJON.mjs} +1 -1
- package/dist/{product-import-V3KQN4TV.mjs → product-import-6EM4VUXP.mjs} +1 -1
- package/dist/{product-list-EUWZIFTM.mjs → product-list-5V5GEH5K.mjs} +2 -2
- package/dist/{product-metadata-GL2MVPDI.mjs → product-metadata-JZLHBLZQ.mjs} +1 -1
- package/dist/{product-organization-O7RHELMQ.mjs → product-organization-3PQ45C4B.mjs} +2 -2
- package/dist/{product-prices-YWV6MSM6.mjs → product-prices-5ZL2RP7A.mjs} +1 -1
- package/dist/{product-stock-AKEFMK5O.mjs → product-stock-SJJABF6I.mjs} +3 -3
- package/dist/{product-tag-create-PQMDDKWH.mjs → product-tag-create-XXO4AQEC.mjs} +1 -1
- package/dist/{product-tag-detail-I3MBZX7U.mjs → product-tag-detail-BSK64HXL.mjs} +3 -3
- package/dist/{product-tag-edit-K3BBQLJR.mjs → product-tag-edit-ENCGDT7E.mjs} +1 -1
- package/dist/{product-tag-list-JUWSOMB7.mjs → product-tag-list-SLQGCNDZ.mjs} +3 -3
- package/dist/{product-tag-metadata-MJH5LH7E.mjs → product-tag-metadata-EPXHMU2K.mjs} +1 -1
- package/dist/{product-type-detail-RKHT5NBL.mjs → product-type-detail-4CRRU7YK.mjs} +2 -2
- package/dist/{product-type-metadata-CDJDFFGQ.mjs → product-type-metadata-73OKOGPP.mjs} +1 -1
- package/dist/{product-variant-detail-XAYG5CKE.mjs → product-variant-detail-RPHLG4HU.mjs} +1 -1
- package/dist/{product-variant-edit-DEZEY2H2.mjs → product-variant-edit-JF7NN64Y.mjs} +1 -1
- package/dist/{product-variant-metadata-VTZDNWUT.mjs → product-variant-metadata-HU2CXGPO.mjs} +1 -1
- package/dist/{promotion-create-HWFNUQXG.mjs → promotion-create-BHA3FQG2.mjs} +1 -1
- package/dist/{promotion-detail-QC36KXB3.mjs → promotion-detail-F3QSR52W.mjs} +1 -1
- package/dist/{refund-reason-create-YHCDEHGQ.mjs → refund-reason-create-ZA5TKW2Z.mjs} +1 -1
- package/dist/{refund-reason-edit-CZ5QZ2SZ.mjs → refund-reason-edit-N2CRCLKZ.mjs} +1 -1
- package/dist/{refund-reason-list-OJYYEYJE.mjs → refund-reason-list-SE4TMGMT.mjs} +1 -1
- package/dist/{region-metadata-H6XXUQ4S.mjs → region-metadata-O5NZBWXP.mjs} +1 -1
- package/dist/{reservation-detail-LZAQL4XA.mjs → reservation-detail-UFK6XIXE.mjs} +1 -1
- package/dist/{reservation-metadata-5HZSDDOK.mjs → reservation-metadata-AEJEKGLV.mjs} +1 -1
- package/dist/{sales-channel-add-products-F7YV4MO5.mjs → sales-channel-add-products-2LMB7EF5.mjs} +2 -2
- package/dist/{sales-channel-detail-MXIPZCGA.mjs → sales-channel-detail-EUQ4STQI.mjs} +2 -2
- package/dist/{sales-channel-list-RLGL7FM3.mjs → sales-channel-list-JXKGHX4G.mjs} +1 -1
- package/dist/{sales-channel-metadata-M364R4RJ.mjs → sales-channel-metadata-AJMQ5SQ2.mjs} +1 -1
- package/dist/{shipping-option-type-create-C5WUWON7.mjs → shipping-option-type-create-YVVIA2XC.mjs} +1 -1
- package/dist/{shipping-option-type-detail-PENS2K73.mjs → shipping-option-type-detail-ZZW36XLK.mjs} +2 -2
- package/dist/{shipping-option-type-edit-CIU5EHRP.mjs → shipping-option-type-edit-O6F74T3A.mjs} +1 -1
- package/dist/{shipping-option-type-list-DIOX7VG7.mjs → shipping-option-type-list-SPTE7MT6.mjs} +2 -2
- package/dist/{shipping-profile-metadata-75G2NNMA.mjs → shipping-profile-metadata-7WFE55VG.mjs} +1 -1
- package/dist/store-add-locales-GWCGIXHU.mjs +81 -0
- package/dist/{store-detail-JSNPOB2F.mjs → store-detail-YLJLBBZE.mjs} +1 -1
- package/dist/{store-metadata-CYXTVJUE.mjs → store-metadata-BZ57I2E6.mjs} +1 -1
- package/dist/{tax-region-create-DWGL4EUT.mjs → tax-region-create-FGTV7VJL.mjs} +1 -1
- package/dist/{tax-region-detail-2AE2EFI3.mjs → tax-region-detail-PPIMD7OX.mjs} +5 -5
- package/dist/{tax-region-edit-EEVEEU2Q.mjs → tax-region-edit-ELZKA7YH.mjs} +1 -1
- package/dist/{tax-region-province-detail-4ERSEQFF.mjs → tax-region-province-detail-FV2NDT3E.mjs} +5 -5
- package/dist/{tax-region-tax-override-create-PHCGEF7V.mjs → tax-region-tax-override-create-N572MQPZ.mjs} +3 -3
- package/dist/{tax-region-tax-override-edit-SMRPSILC.mjs → tax-region-tax-override-edit-5DCSJW6D.mjs} +4 -4
- package/dist/{translation-list-UF7FLXOW.mjs → translation-list-FK7XYLHX.mjs} +227 -141
- package/dist/{translations-edit-USQJNMAY.mjs → translations-edit-VRXZI5KW.mjs} +224 -253
- package/dist/{user-metadata-2WPJOEJA.mjs → user-metadata-GRJZZ524.mjs} +1 -1
- package/dist/{workflow-execution-detail-H2AKEZJX.mjs → workflow-execution-detail-HXTFWGKG.mjs} +1 -1
- package/package.json +9 -9
- package/src/components/data-grid/hooks/use-data-grid-cell.tsx +1 -0
- package/src/components/data-grid/hooks/use-data-grid-form-handlers.tsx +1 -0
- package/src/components/data-grid/hooks/use-data-grid-keydown-event.tsx +22 -4
- package/src/dashboard-app/routes/get-route.map.tsx +4 -0
- package/src/hooks/api/translations.tsx +26 -17
- package/src/i18n/translations/en.json +1 -2
- package/src/i18n/translations/es.json +1 -1
- package/src/routes/translations/add-locales/add-locales.tsx +29 -0
- package/src/routes/translations/add-locales/index.tsx +1 -0
- package/src/routes/translations/translation-list/components/active-locales-section/active-locales-section.tsx +42 -17
- package/src/routes/translations/translation-list/components/translation-list-section/translation-list-section.tsx +5 -1
- package/src/routes/translations/translation-list/components/translations-completion-section/translations-completion-section.tsx +182 -121
- package/src/routes/translations/translations-edit/components/translations-edit-form/translations-edit-form.tsx +285 -330
|
@@ -18,38 +18,17 @@ import {
|
|
|
18
18
|
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
|
|
19
19
|
import { useBatchTranslations } from "../../../../../hooks/api/translations"
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
* Schema for a single locale translation.
|
|
23
|
-
*/
|
|
24
|
-
const LocaleTranslationSchema = z.object({
|
|
21
|
+
const EntityTranslationsSchema = z.object({
|
|
25
22
|
id: z.string().nullish(),
|
|
26
|
-
locale_code: z.string(),
|
|
27
23
|
fields: z.record(z.string().optional()),
|
|
28
24
|
})
|
|
29
|
-
export type LocaleTranslationSchema = z.infer<typeof LocaleTranslationSchema>
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Schema for an entity's translations (parent row in DataGrid).
|
|
33
|
-
* Contains all locale translations for that entity.
|
|
34
|
-
*/
|
|
35
|
-
const EntityTranslationsSchema = z.object({
|
|
36
|
-
locales: z.record(LocaleTranslationSchema),
|
|
37
|
-
})
|
|
38
25
|
export type EntityTranslationsSchema = z.infer<typeof EntityTranslationsSchema>
|
|
39
26
|
|
|
40
|
-
/**
|
|
41
|
-
* Form schema
|
|
42
|
-
* Maps each reference_id to their corresponding translations for each locale
|
|
43
|
-
*/
|
|
44
27
|
export const TranslationsFormSchema = z.object({
|
|
45
28
|
entities: z.record(EntityTranslationsSchema),
|
|
46
29
|
})
|
|
47
30
|
export type TranslationsFormSchema = z.infer<typeof TranslationsFormSchema>
|
|
48
31
|
|
|
49
|
-
/**
|
|
50
|
-
* Row types for the DataGrid.
|
|
51
|
-
* Parent rows are entities, subrows are translatable fields.
|
|
52
|
-
*/
|
|
53
32
|
export type TranslationRow = EntityRow | FieldRow
|
|
54
33
|
|
|
55
34
|
export type EntityRow = {
|
|
@@ -72,150 +51,94 @@ export function isFieldRow(row: TranslationRow): row is FieldRow {
|
|
|
72
51
|
return row._type === "field"
|
|
73
52
|
}
|
|
74
53
|
|
|
75
|
-
|
|
54
|
+
type LocaleSnapshot = {
|
|
55
|
+
localeCode: string
|
|
56
|
+
entities: Record<string, EntityTranslationsSchema>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function buildLocaleSnapshot(
|
|
76
60
|
translations: HttpTypes.AdminTranslation[],
|
|
77
61
|
references: { id: string; [key: string]: string }[],
|
|
78
|
-
|
|
62
|
+
localeCode: string,
|
|
79
63
|
translatableFields: string[]
|
|
80
|
-
):
|
|
81
|
-
const
|
|
64
|
+
): LocaleSnapshot {
|
|
65
|
+
const referenceTranslations = new Map<string, HttpTypes.AdminTranslation>()
|
|
82
66
|
for (const t of translations) {
|
|
83
|
-
|
|
67
|
+
if (t.locale_code === localeCode) {
|
|
68
|
+
referenceTranslations.set(t.reference_id, t)
|
|
69
|
+
}
|
|
84
70
|
}
|
|
85
71
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
for (const locale of availableLocales) {
|
|
92
|
-
const key = `${reference.id}:${locale.locale_code}`
|
|
93
|
-
const existing = existingMap.get(key)
|
|
94
|
-
|
|
95
|
-
const fields: Record<string, string> = {}
|
|
96
|
-
for (const fieldName of translatableFields) {
|
|
97
|
-
const fieldValue = (existing?.translations?.[fieldName] as string) ?? ""
|
|
98
|
-
fields[fieldName] = fieldValue
|
|
99
|
-
}
|
|
72
|
+
const entities: Record<string, EntityTranslationsSchema> = {}
|
|
73
|
+
for (const ref of references) {
|
|
74
|
+
const existing = referenceTranslations.get(ref.id)
|
|
75
|
+
const fields: Record<string, string> = {}
|
|
100
76
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
locale_code: locale.locale_code,
|
|
104
|
-
fields,
|
|
105
|
-
}
|
|
77
|
+
for (const fieldName of translatableFields) {
|
|
78
|
+
fields[fieldName] = (existing?.translations?.[fieldName] as string) ?? ""
|
|
106
79
|
}
|
|
107
80
|
|
|
108
|
-
|
|
81
|
+
entities[ref.id] = {
|
|
82
|
+
id: existing?.id ?? null,
|
|
83
|
+
fields,
|
|
84
|
+
}
|
|
109
85
|
}
|
|
110
86
|
|
|
111
|
-
return {
|
|
112
|
-
entities: entitiesTranslationState,
|
|
113
|
-
}
|
|
87
|
+
return { localeCode, entities }
|
|
114
88
|
}
|
|
115
89
|
|
|
116
|
-
function
|
|
117
|
-
|
|
90
|
+
function extendSnapshot(
|
|
91
|
+
snapshot: LocaleSnapshot,
|
|
92
|
+
translations: HttpTypes.AdminTranslation[],
|
|
93
|
+
newReferences: { id: string; [key: string]: string }[],
|
|
118
94
|
translatableFields: string[]
|
|
119
|
-
):
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
_type: "field" as const,
|
|
125
|
-
reference_id: reference.id,
|
|
126
|
-
field_name: fieldName,
|
|
127
|
-
})),
|
|
128
|
-
}))
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function transformToBatchPayload(
|
|
132
|
-
currentState: TranslationsFormSchema,
|
|
133
|
-
initialState: TranslationsFormSchema,
|
|
134
|
-
entityType: string
|
|
135
|
-
): Required<HttpTypes.AdminBatchTranslations> {
|
|
136
|
-
const payload: Required<HttpTypes.AdminBatchTranslations> = {
|
|
137
|
-
create: [],
|
|
138
|
-
update: [],
|
|
139
|
-
delete: [],
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
for (const [entityId, entityData] of Object.entries(currentState.entities)) {
|
|
143
|
-
for (const [localeCode, localeTranslations] of Object.entries(
|
|
144
|
-
entityData.locales
|
|
145
|
-
)) {
|
|
146
|
-
const initial = initialState.entities[entityId]?.locales[localeCode]
|
|
147
|
-
const hasContent = Object.values(localeTranslations.fields).some(
|
|
148
|
-
(v) => v !== undefined && v.trim() !== ""
|
|
149
|
-
)
|
|
150
|
-
const hadContent =
|
|
151
|
-
initial &&
|
|
152
|
-
Object.values(initial.fields).some(
|
|
153
|
-
(v) => v !== undefined && v.trim() !== ""
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
if (!localeTranslations.id && hasContent) {
|
|
157
|
-
payload.create.push({
|
|
158
|
-
reference_id: entityId,
|
|
159
|
-
reference: entityType,
|
|
160
|
-
locale_code: localeTranslations.locale_code,
|
|
161
|
-
translations: localeTranslations.fields,
|
|
162
|
-
})
|
|
163
|
-
} else if (localeTranslations.id && hasContent) {
|
|
164
|
-
// UPDATE: Has ID and has content - check if changed
|
|
165
|
-
const hasChanged =
|
|
166
|
-
!initial ||
|
|
167
|
-
JSON.stringify(localeTranslations.fields) !==
|
|
168
|
-
JSON.stringify(initial.fields)
|
|
169
|
-
|
|
170
|
-
if (hasChanged) {
|
|
171
|
-
payload.update.push({
|
|
172
|
-
id: localeTranslations.id,
|
|
173
|
-
translations: localeTranslations.fields,
|
|
174
|
-
})
|
|
175
|
-
}
|
|
176
|
-
} else if (localeTranslations.id && !hasContent && hadContent) {
|
|
177
|
-
payload.delete.push(localeTranslations.id)
|
|
178
|
-
}
|
|
95
|
+
): LocaleSnapshot {
|
|
96
|
+
const referenceTranslations = new Map<string, HttpTypes.AdminTranslation>()
|
|
97
|
+
for (const t of translations) {
|
|
98
|
+
if (t.locale_code === snapshot.localeCode) {
|
|
99
|
+
referenceTranslations.set(t.reference_id, t)
|
|
179
100
|
}
|
|
180
101
|
}
|
|
181
102
|
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function hasLocaleChanges(
|
|
186
|
-
currentState: TranslationsFormSchema,
|
|
187
|
-
initialState: TranslationsFormSchema,
|
|
188
|
-
localeCode: string
|
|
189
|
-
): boolean {
|
|
190
|
-
for (const [entityId, entityData] of Object.entries(currentState.entities)) {
|
|
191
|
-
const currentLocale = entityData.locales[localeCode]
|
|
192
|
-
const initialLocale = initialState.entities[entityId]?.locales[localeCode]
|
|
103
|
+
const extendedEntities = { ...snapshot.entities }
|
|
193
104
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
105
|
+
for (const ref of newReferences) {
|
|
106
|
+
if (!extendedEntities[ref.id]) {
|
|
107
|
+
const existing = referenceTranslations.get(ref.id)
|
|
108
|
+
const fields: Record<string, string> = {}
|
|
197
109
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const currentValue = fieldValue ?? ""
|
|
110
|
+
for (const fieldName of translatableFields) {
|
|
111
|
+
fields[fieldName] =
|
|
112
|
+
(existing?.translations?.[fieldName] as string) ?? ""
|
|
113
|
+
}
|
|
203
114
|
|
|
204
|
-
|
|
205
|
-
|
|
115
|
+
extendedEntities[ref.id] = {
|
|
116
|
+
id: existing?.id ?? null,
|
|
117
|
+
fields,
|
|
206
118
|
}
|
|
207
119
|
}
|
|
208
120
|
}
|
|
209
121
|
|
|
210
|
-
return
|
|
122
|
+
return { ...snapshot, entities: extendedEntities }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function snapshotToFormValues(
|
|
126
|
+
snapshot: LocaleSnapshot
|
|
127
|
+
): TranslationsFormSchema {
|
|
128
|
+
return { entities: snapshot.entities }
|
|
211
129
|
}
|
|
212
130
|
|
|
213
|
-
|
|
131
|
+
type ChangeDetectionResult = {
|
|
132
|
+
hasChanges: boolean
|
|
133
|
+
payload: Required<HttpTypes.AdminBatchTranslations>
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function computeChanges(
|
|
214
137
|
currentState: TranslationsFormSchema,
|
|
215
|
-
|
|
138
|
+
snapshot: LocaleSnapshot,
|
|
216
139
|
entityType: string,
|
|
217
140
|
localeCode: string
|
|
218
|
-
):
|
|
141
|
+
): ChangeDetectionResult {
|
|
219
142
|
const payload: Required<HttpTypes.AdminBatchTranslations> = {
|
|
220
143
|
create: [],
|
|
221
144
|
update: [],
|
|
@@ -223,44 +146,43 @@ function transformSingleLocaleToBatchPayload(
|
|
|
223
146
|
}
|
|
224
147
|
|
|
225
148
|
for (const [entityId, entityData] of Object.entries(currentState.entities)) {
|
|
226
|
-
const
|
|
227
|
-
if (!
|
|
149
|
+
const baseline = snapshot.entities[entityId]
|
|
150
|
+
if (!baseline) {
|
|
151
|
+
continue
|
|
152
|
+
}
|
|
228
153
|
|
|
229
|
-
const
|
|
230
|
-
const hasContent = Object.values(localeTranslations.fields).some(
|
|
154
|
+
const hasContent = Object.values(entityData.fields).some(
|
|
231
155
|
(v) => v !== undefined && v.trim() !== ""
|
|
232
156
|
)
|
|
233
|
-
const hadContent =
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
)
|
|
157
|
+
const hadContent = Object.values(baseline.fields).some(
|
|
158
|
+
(v) => v !== undefined && v.trim() !== ""
|
|
159
|
+
)
|
|
160
|
+
const hasChanged =
|
|
161
|
+
JSON.stringify(entityData.fields) !== JSON.stringify(baseline.fields)
|
|
238
162
|
|
|
239
|
-
if (!
|
|
163
|
+
if (!entityData.id && hasContent) {
|
|
240
164
|
payload.create.push({
|
|
241
165
|
reference_id: entityId,
|
|
242
166
|
reference: entityType,
|
|
243
|
-
locale_code:
|
|
244
|
-
translations:
|
|
167
|
+
locale_code: localeCode,
|
|
168
|
+
translations: entityData.fields,
|
|
245
169
|
})
|
|
246
|
-
} else if (
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
payload.update.push({
|
|
254
|
-
id: localeTranslations.id,
|
|
255
|
-
translations: localeTranslations.fields,
|
|
256
|
-
})
|
|
257
|
-
}
|
|
258
|
-
} else if (localeTranslations.id && !hasContent && hadContent) {
|
|
259
|
-
payload.delete.push(localeTranslations.id)
|
|
170
|
+
} else if (entityData.id && hasContent && hasChanged) {
|
|
171
|
+
payload.update.push({
|
|
172
|
+
id: entityData.id,
|
|
173
|
+
translations: entityData.fields,
|
|
174
|
+
})
|
|
175
|
+
} else if (entityData.id && !hasContent && hadContent) {
|
|
176
|
+
payload.delete.push(entityData.id)
|
|
260
177
|
}
|
|
261
178
|
}
|
|
262
179
|
|
|
263
|
-
|
|
180
|
+
const hasChanges =
|
|
181
|
+
payload.create.length > 0 ||
|
|
182
|
+
payload.update.length > 0 ||
|
|
183
|
+
payload.delete.length > 0
|
|
184
|
+
|
|
185
|
+
return { hasChanges, payload }
|
|
264
186
|
}
|
|
265
187
|
|
|
266
188
|
const columnHelper = createDataGridHelper<
|
|
@@ -268,29 +190,42 @@ const columnHelper = createDataGridHelper<
|
|
|
268
190
|
TranslationsFormSchema
|
|
269
191
|
>()
|
|
270
192
|
|
|
271
|
-
const FIELD_COLUMN_WIDTH =
|
|
193
|
+
const FIELD_COLUMN_WIDTH = 350
|
|
194
|
+
|
|
195
|
+
function buildTranslationRows(
|
|
196
|
+
references: { id: string; [key: string]: string }[],
|
|
197
|
+
translatableFields: string[]
|
|
198
|
+
): TranslationRow[] {
|
|
199
|
+
return references.map((reference) => ({
|
|
200
|
+
_type: "entity" as const,
|
|
201
|
+
reference_id: reference.id,
|
|
202
|
+
subRows: translatableFields.map((fieldName) => ({
|
|
203
|
+
_type: "field" as const,
|
|
204
|
+
reference_id: reference.id,
|
|
205
|
+
field_name: fieldName,
|
|
206
|
+
})),
|
|
207
|
+
}))
|
|
208
|
+
}
|
|
272
209
|
|
|
273
210
|
function useTranslationsGridColumns({
|
|
274
211
|
entities,
|
|
275
|
-
translatableFields,
|
|
276
212
|
availableLocales,
|
|
277
213
|
selectedLocale,
|
|
278
214
|
dynamicColumnWidth,
|
|
279
215
|
}: {
|
|
280
216
|
entities: { id: string; [key: string]: string }[]
|
|
281
|
-
translatableFields: string[]
|
|
282
217
|
availableLocales: AdminStoreLocale[]
|
|
283
218
|
selectedLocale: string
|
|
284
219
|
dynamicColumnWidth: number
|
|
285
220
|
}) {
|
|
286
221
|
const { t } = useTranslation()
|
|
287
222
|
|
|
288
|
-
|
|
223
|
+
return useMemo(() => {
|
|
289
224
|
const selectedLocaleData = availableLocales.find(
|
|
290
225
|
(l) => l.locale_code === selectedLocale
|
|
291
226
|
)
|
|
292
227
|
|
|
293
|
-
const
|
|
228
|
+
const columns: ColumnDef<TranslationRow>[] = [
|
|
294
229
|
columnHelper.column({
|
|
295
230
|
id: "field",
|
|
296
231
|
name: "field",
|
|
@@ -300,9 +235,7 @@ function useTranslationsGridColumns({
|
|
|
300
235
|
const row = context.row.original
|
|
301
236
|
|
|
302
237
|
if (isEntityRow(row)) {
|
|
303
|
-
return
|
|
304
|
-
<DataGrid.ReadonlyCell context={context}></DataGrid.ReadonlyCell>
|
|
305
|
-
)
|
|
238
|
+
return <DataGrid.ReadonlyCell context={context} />
|
|
306
239
|
}
|
|
307
240
|
|
|
308
241
|
return (
|
|
@@ -337,14 +270,10 @@ function useTranslationsGridColumns({
|
|
|
337
270
|
const row = context.row.original
|
|
338
271
|
|
|
339
272
|
if (isEntityRow(row)) {
|
|
340
|
-
return
|
|
341
|
-
<DataGrid.ReadonlyCell context={context}></DataGrid.ReadonlyCell>
|
|
342
|
-
)
|
|
273
|
+
return <DataGrid.ReadonlyCell context={context} />
|
|
343
274
|
}
|
|
344
275
|
|
|
345
|
-
const entity = entities.find(
|
|
346
|
-
(entity) => entity.id === row.reference_id
|
|
347
|
-
)
|
|
276
|
+
const entity = entities.find((e) => e.id === row.reference_id)
|
|
348
277
|
if (!entity) {
|
|
349
278
|
return null
|
|
350
279
|
}
|
|
@@ -361,7 +290,7 @@ function useTranslationsGridColumns({
|
|
|
361
290
|
]
|
|
362
291
|
|
|
363
292
|
if (selectedLocaleData) {
|
|
364
|
-
|
|
293
|
+
columns.push(
|
|
365
294
|
columnHelper.column({
|
|
366
295
|
id: selectedLocaleData.locale_code,
|
|
367
296
|
name: selectedLocaleData.locale.name,
|
|
@@ -387,24 +316,15 @@ function useTranslationsGridColumns({
|
|
|
387
316
|
return null
|
|
388
317
|
}
|
|
389
318
|
|
|
390
|
-
return `entities.${row.reference_id}.
|
|
319
|
+
return `entities.${row.reference_id}.fields.${row.field_name}`
|
|
391
320
|
},
|
|
392
321
|
type: "multiline-text",
|
|
393
322
|
})
|
|
394
323
|
)
|
|
395
324
|
}
|
|
396
325
|
|
|
397
|
-
return
|
|
398
|
-
}, [
|
|
399
|
-
t,
|
|
400
|
-
translatableFields,
|
|
401
|
-
availableLocales,
|
|
402
|
-
selectedLocale,
|
|
403
|
-
entities,
|
|
404
|
-
dynamicColumnWidth,
|
|
405
|
-
])
|
|
406
|
-
|
|
407
|
-
return columns
|
|
326
|
+
return columns
|
|
327
|
+
}, [t, availableLocales, selectedLocale, entities, dynamicColumnWidth])
|
|
408
328
|
}
|
|
409
329
|
|
|
410
330
|
type TranslationsEditFormProps = {
|
|
@@ -459,74 +379,105 @@ export const TranslationsEditForm = ({
|
|
|
459
379
|
const [selectedLocale, setSelectedLocale] = useState<string>(
|
|
460
380
|
availableLocales[0]?.locale_code ?? ""
|
|
461
381
|
)
|
|
462
|
-
|
|
463
382
|
const [showUnsavedPrompt, setShowUnsavedPrompt] = useState(false)
|
|
464
383
|
const [pendingLocale, setPendingLocale] = useState<string | null>(null)
|
|
384
|
+
const [isSwitchingLocale, setIsSwitchingLocale] = useState(false)
|
|
465
385
|
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
() => referenceCount * (translatableFields.length + 1),
|
|
469
|
-
[referenceCount, translatableFields]
|
|
470
|
-
)
|
|
471
|
-
|
|
472
|
-
const initialState = useRef(
|
|
473
|
-
initTranslationsFormState(
|
|
386
|
+
const snapshotRef = useRef<LocaleSnapshot>(
|
|
387
|
+
buildLocaleSnapshot(
|
|
474
388
|
translations,
|
|
475
|
-
|
|
476
|
-
|
|
389
|
+
references,
|
|
390
|
+
selectedLocale,
|
|
477
391
|
translatableFields
|
|
478
392
|
)
|
|
479
393
|
)
|
|
480
394
|
|
|
395
|
+
const knownEntityIdsRef = useRef<Set<string>>(
|
|
396
|
+
new Set(references.map((r) => r.id))
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
const latestPropsRef = useRef({ translations, references })
|
|
400
|
+
useEffect(() => {
|
|
401
|
+
latestPropsRef.current = { translations, references }
|
|
402
|
+
}, [translations, references])
|
|
403
|
+
|
|
481
404
|
const form = useForm<TranslationsFormSchema>({
|
|
482
405
|
resolver: zodResolver(TranslationsFormSchema),
|
|
483
|
-
defaultValues:
|
|
406
|
+
defaultValues: snapshotToFormValues(snapshotRef.current),
|
|
484
407
|
})
|
|
485
408
|
|
|
486
|
-
|
|
487
|
-
() =>
|
|
488
|
-
|
|
489
|
-
|
|
409
|
+
useEffect(() => {
|
|
410
|
+
const currentIds = new Set(references.map((r) => r.id))
|
|
411
|
+
const newReferences = references.filter(
|
|
412
|
+
(r) => !knownEntityIdsRef.current.has(r.id)
|
|
413
|
+
)
|
|
490
414
|
|
|
491
|
-
|
|
415
|
+
if (newReferences.length === 0) {
|
|
416
|
+
return
|
|
417
|
+
}
|
|
492
418
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
419
|
+
knownEntityIdsRef.current = currentIds
|
|
420
|
+
snapshotRef.current = extendSnapshot(
|
|
421
|
+
snapshotRef.current,
|
|
422
|
+
translations,
|
|
423
|
+
newReferences,
|
|
424
|
+
translatableFields
|
|
425
|
+
)
|
|
498
426
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
selectedLocale
|
|
504
|
-
)
|
|
427
|
+
const currentValues = form.getValues()
|
|
428
|
+
const newFormValues: TranslationsFormSchema = {
|
|
429
|
+
entities: { ...currentValues.entities },
|
|
430
|
+
}
|
|
505
431
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
} else {
|
|
510
|
-
setSelectedLocale(newLocale)
|
|
432
|
+
for (const ref of newReferences) {
|
|
433
|
+
if (!newFormValues.entities[ref.id]) {
|
|
434
|
+
newFormValues.entities[ref.id] = snapshotRef.current.entities[ref.id]
|
|
511
435
|
}
|
|
512
|
-
}
|
|
513
|
-
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
form.reset(newFormValues, {
|
|
439
|
+
keepDirty: true,
|
|
440
|
+
keepDirtyValues: true,
|
|
441
|
+
})
|
|
442
|
+
}, [references, translations, translatableFields, form])
|
|
443
|
+
|
|
444
|
+
const rows = useMemo(
|
|
445
|
+
() => buildTranslationRows(references, translatableFields),
|
|
446
|
+
[references, translatableFields]
|
|
514
447
|
)
|
|
515
448
|
|
|
449
|
+
const totalRowCount = useMemo(
|
|
450
|
+
() => referenceCount * (translatableFields.length + 1),
|
|
451
|
+
[referenceCount, translatableFields]
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
const selectedLocaleDisplay = useMemo(
|
|
455
|
+
() =>
|
|
456
|
+
availableLocales.find((l) => l.locale_code === selectedLocale)?.locale
|
|
457
|
+
.name,
|
|
458
|
+
[availableLocales, selectedLocale]
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
const columns = useTranslationsGridColumns({
|
|
462
|
+
entities: references,
|
|
463
|
+
availableLocales,
|
|
464
|
+
selectedLocale,
|
|
465
|
+
dynamicColumnWidth,
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
const { mutateAsync, isPending, invalidateQueries } =
|
|
469
|
+
useBatchTranslations(entityType)
|
|
470
|
+
|
|
516
471
|
const saveCurrentLocale = useCallback(async () => {
|
|
517
472
|
const currentValues = form.getValues()
|
|
518
|
-
const payload =
|
|
473
|
+
const { hasChanges, payload } = computeChanges(
|
|
519
474
|
currentValues,
|
|
520
|
-
|
|
475
|
+
snapshotRef.current,
|
|
521
476
|
entityType,
|
|
522
477
|
selectedLocale
|
|
523
478
|
)
|
|
524
479
|
|
|
525
|
-
if (
|
|
526
|
-
payload.create.length === 0 &&
|
|
527
|
-
payload.update.length === 0 &&
|
|
528
|
-
payload.delete.length === 0
|
|
529
|
-
) {
|
|
480
|
+
if (!hasChanges) {
|
|
530
481
|
return true
|
|
531
482
|
}
|
|
532
483
|
|
|
@@ -538,11 +489,13 @@ export const TranslationsEditForm = ({
|
|
|
538
489
|
|
|
539
490
|
for (let i = 0; i < batchCount; i++) {
|
|
540
491
|
let currentBatchAvailable = BATCH_SIZE
|
|
492
|
+
|
|
541
493
|
const currentBatch: HttpTypes.AdminBatchTranslations = {
|
|
542
494
|
create: [],
|
|
543
495
|
update: [],
|
|
544
496
|
delete: [],
|
|
545
497
|
}
|
|
498
|
+
|
|
546
499
|
if (payload.create.length > 0) {
|
|
547
500
|
currentBatch.create = payload.create.splice(0, currentBatchAvailable)
|
|
548
501
|
currentBatchAvailable -= currentBatch.create.length
|
|
@@ -553,22 +506,36 @@ export const TranslationsEditForm = ({
|
|
|
553
506
|
}
|
|
554
507
|
if (payload.delete.length > 0) {
|
|
555
508
|
currentBatch.delete = payload.delete.splice(0, currentBatchAvailable)
|
|
556
|
-
currentBatchAvailable -= currentBatch.delete.length
|
|
557
509
|
}
|
|
558
510
|
|
|
559
|
-
await mutateAsync(currentBatch
|
|
511
|
+
const response = await mutateAsync(currentBatch, {
|
|
512
|
+
onError: (error) => {
|
|
513
|
+
toast.error(error.message)
|
|
514
|
+
},
|
|
515
|
+
})
|
|
516
|
+
|
|
517
|
+
if (response.created) {
|
|
518
|
+
for (const created of response.created) {
|
|
519
|
+
form.setValue(`entities.${created.reference_id}.id`, created.id, {
|
|
520
|
+
shouldDirty: false,
|
|
521
|
+
})
|
|
522
|
+
if (snapshotRef.current.entities[created.reference_id]) {
|
|
523
|
+
snapshotRef.current.entities[created.reference_id].id = created.id
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
560
527
|
}
|
|
561
528
|
|
|
562
|
-
const
|
|
563
|
-
for (const entityId of Object.keys(
|
|
564
|
-
if (
|
|
565
|
-
|
|
566
|
-
...
|
|
529
|
+
const savedValues = form.getValues()
|
|
530
|
+
for (const entityId of Object.keys(savedValues.entities)) {
|
|
531
|
+
if (snapshotRef.current.entities[entityId]) {
|
|
532
|
+
snapshotRef.current.entities[entityId] = {
|
|
533
|
+
...savedValues.entities[entityId],
|
|
567
534
|
}
|
|
568
535
|
}
|
|
569
536
|
}
|
|
570
|
-
|
|
571
|
-
form.reset(
|
|
537
|
+
|
|
538
|
+
form.reset(savedValues)
|
|
572
539
|
|
|
573
540
|
return true
|
|
574
541
|
} catch (error) {
|
|
@@ -579,19 +546,70 @@ export const TranslationsEditForm = ({
|
|
|
579
546
|
}
|
|
580
547
|
}, [form, entityType, selectedLocale, mutateAsync])
|
|
581
548
|
|
|
549
|
+
const switchToLocale = useCallback(
|
|
550
|
+
async (newLocale: string) => {
|
|
551
|
+
setIsSwitchingLocale(true)
|
|
552
|
+
|
|
553
|
+
try {
|
|
554
|
+
await invalidateQueries()
|
|
555
|
+
|
|
556
|
+
await new Promise((resolve) => requestAnimationFrame(resolve))
|
|
557
|
+
|
|
558
|
+
const { translations, references } = latestPropsRef.current
|
|
559
|
+
|
|
560
|
+
const newSnapshot = buildLocaleSnapshot(
|
|
561
|
+
translations,
|
|
562
|
+
references,
|
|
563
|
+
newLocale,
|
|
564
|
+
translatableFields
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
snapshotRef.current = newSnapshot
|
|
568
|
+
knownEntityIdsRef.current = new Set(references.map((r) => r.id))
|
|
569
|
+
|
|
570
|
+
form.reset(snapshotToFormValues(newSnapshot))
|
|
571
|
+
|
|
572
|
+
setSelectedLocale(newLocale)
|
|
573
|
+
} finally {
|
|
574
|
+
setIsSwitchingLocale(false)
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
[translatableFields, form, invalidateQueries]
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
const handleLocaleChange = useCallback(
|
|
581
|
+
(newLocale: string) => {
|
|
582
|
+
if (newLocale === selectedLocale) {
|
|
583
|
+
return
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const currentValues = form.getValues()
|
|
587
|
+
const { hasChanges } = computeChanges(
|
|
588
|
+
currentValues,
|
|
589
|
+
snapshotRef.current,
|
|
590
|
+
entityType,
|
|
591
|
+
selectedLocale
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
if (hasChanges) {
|
|
595
|
+
setPendingLocale(newLocale)
|
|
596
|
+
setShowUnsavedPrompt(true)
|
|
597
|
+
} else {
|
|
598
|
+
switchToLocale(newLocale)
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
[selectedLocale, form, entityType, switchToLocale]
|
|
602
|
+
)
|
|
603
|
+
|
|
582
604
|
const handleSaveAndSwitch = useCallback(async () => {
|
|
583
605
|
const success = await saveCurrentLocale()
|
|
584
606
|
if (success && pendingLocale) {
|
|
585
|
-
toast.success(
|
|
586
|
-
|
|
587
|
-
defaultValue: "Changes saved successfully",
|
|
588
|
-
})
|
|
589
|
-
)
|
|
590
|
-
setSelectedLocale(pendingLocale)
|
|
607
|
+
toast.success(t("translations.edit.successToast"))
|
|
608
|
+
await switchToLocale(pendingLocale)
|
|
591
609
|
}
|
|
592
610
|
setShowUnsavedPrompt(false)
|
|
593
611
|
setPendingLocale(null)
|
|
594
|
-
}, [saveCurrentLocale, pendingLocale, t])
|
|
612
|
+
}, [saveCurrentLocale, pendingLocale, t, switchToLocale])
|
|
595
613
|
|
|
596
614
|
const handleCancelSwitch = useCallback(() => {
|
|
597
615
|
setShowUnsavedPrompt(false)
|
|
@@ -611,83 +629,19 @@ export const TranslationsEditForm = ({
|
|
|
611
629
|
[saveCurrentLocale, t, handleSuccess]
|
|
612
630
|
)
|
|
613
631
|
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
initialState.current,
|
|
618
|
-
entityType
|
|
619
|
-
)
|
|
632
|
+
const handleClose = useCallback(() => {
|
|
633
|
+
invalidateQueries()
|
|
634
|
+
}, [invalidateQueries])
|
|
620
635
|
|
|
621
|
-
|
|
622
|
-
payload.create.length === 0 &&
|
|
623
|
-
payload.update.length === 0 &&
|
|
624
|
-
payload.delete.length === 0
|
|
625
|
-
) {
|
|
626
|
-
return
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
const BATCH_SIZE = 150
|
|
630
|
-
const totalItems =
|
|
631
|
-
payload.create.length + payload.update.length + payload.delete.length
|
|
632
|
-
const batchCount = Math.ceil(totalItems / BATCH_SIZE)
|
|
633
|
-
|
|
634
|
-
for (let i = 0; i < batchCount; i++) {
|
|
635
|
-
let currentBatchAvailable = BATCH_SIZE
|
|
636
|
-
const currentBatch: HttpTypes.AdminBatchTranslations = {
|
|
637
|
-
create: [],
|
|
638
|
-
update: [],
|
|
639
|
-
delete: [],
|
|
640
|
-
}
|
|
641
|
-
if (payload.create.length > 0) {
|
|
642
|
-
currentBatch.create = payload.create.splice(0, currentBatchAvailable)
|
|
643
|
-
currentBatchAvailable -= currentBatch.create.length
|
|
644
|
-
}
|
|
645
|
-
if (payload.update.length > 0) {
|
|
646
|
-
currentBatch.update = payload.update.splice(0, currentBatchAvailable)
|
|
647
|
-
currentBatchAvailable -= currentBatch.update.length
|
|
648
|
-
}
|
|
649
|
-
if (payload.delete.length > 0) {
|
|
650
|
-
currentBatch.delete = payload.delete.splice(0, currentBatchAvailable)
|
|
651
|
-
currentBatchAvailable -= currentBatch.delete.length
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
await mutateAsync(currentBatch, {
|
|
655
|
-
onSuccess: () => {
|
|
656
|
-
if (i === batchCount - 1) {
|
|
657
|
-
toast.success(
|
|
658
|
-
t("translations.edit.successToast", {
|
|
659
|
-
defaultValue: "Translations updated successfully",
|
|
660
|
-
})
|
|
661
|
-
)
|
|
662
|
-
handleSuccess()
|
|
663
|
-
}
|
|
664
|
-
},
|
|
665
|
-
onError: (error) => {
|
|
666
|
-
toast.error(error.message)
|
|
667
|
-
},
|
|
668
|
-
})
|
|
669
|
-
}
|
|
670
|
-
})
|
|
671
|
-
|
|
672
|
-
const columns = useTranslationsGridColumns({
|
|
673
|
-
entities,
|
|
674
|
-
translatableFields,
|
|
675
|
-
availableLocales,
|
|
676
|
-
selectedLocale,
|
|
677
|
-
dynamicColumnWidth,
|
|
678
|
-
})
|
|
679
|
-
|
|
680
|
-
const selectedLocaleDisplay = availableLocales.find(
|
|
681
|
-
(l) => l.locale_code === selectedLocale
|
|
682
|
-
)?.locale.name
|
|
636
|
+
const isLoading = isPending || isSwitchingLocale
|
|
683
637
|
|
|
684
638
|
return (
|
|
685
|
-
<RouteFocusModal.Form form={form}>
|
|
639
|
+
<RouteFocusModal.Form form={form} onClose={handleClose}>
|
|
686
640
|
<KeyboundForm
|
|
687
|
-
onSubmit={
|
|
641
|
+
onSubmit={() => handleSave(true)}
|
|
688
642
|
className="flex h-full flex-col overflow-hidden"
|
|
689
643
|
>
|
|
690
|
-
<RouteFocusModal.Header
|
|
644
|
+
<RouteFocusModal.Header />
|
|
691
645
|
<RouteFocusModal.Body className="size-full overflow-hidden">
|
|
692
646
|
<div ref={containerRef} className="size-full">
|
|
693
647
|
<DataGrid
|
|
@@ -701,12 +655,13 @@ export const TranslationsEditForm = ({
|
|
|
701
655
|
}}
|
|
702
656
|
state={form}
|
|
703
657
|
onEditingChange={(editing) => setCloseOnEscape(!editing)}
|
|
704
|
-
totalRowCount={
|
|
658
|
+
totalRowCount={totalRowCount}
|
|
705
659
|
onFetchMore={fetchNextPage}
|
|
706
660
|
isFetchingMore={isFetchingNextPage}
|
|
707
661
|
hasNextPage={hasNextPage}
|
|
708
662
|
headerContent={
|
|
709
663
|
<Select
|
|
664
|
+
disabled={isLoading}
|
|
710
665
|
value={selectedLocale}
|
|
711
666
|
onValueChange={handleLocaleChange}
|
|
712
667
|
size="small"
|
|
@@ -732,7 +687,12 @@ export const TranslationsEditForm = ({
|
|
|
732
687
|
<RouteFocusModal.Footer>
|
|
733
688
|
<div className="flex items-center justify-end gap-x-2">
|
|
734
689
|
<RouteFocusModal.Close asChild>
|
|
735
|
-
<Button
|
|
690
|
+
<Button
|
|
691
|
+
type="button"
|
|
692
|
+
size="small"
|
|
693
|
+
variant="secondary"
|
|
694
|
+
isLoading={isLoading}
|
|
695
|
+
>
|
|
736
696
|
{t("actions.cancel")}
|
|
737
697
|
</Button>
|
|
738
698
|
</RouteFocusModal.Close>
|
|
@@ -741,16 +701,11 @@ export const TranslationsEditForm = ({
|
|
|
741
701
|
type="button"
|
|
742
702
|
variant="secondary"
|
|
743
703
|
onClick={() => handleSave(false)}
|
|
744
|
-
isLoading={
|
|
704
|
+
isLoading={isLoading}
|
|
745
705
|
>
|
|
746
706
|
{t("actions.saveChanges")}
|
|
747
707
|
</Button>
|
|
748
|
-
<Button
|
|
749
|
-
size="small"
|
|
750
|
-
type="button"
|
|
751
|
-
onClick={() => handleSave(true)}
|
|
752
|
-
isLoading={isPending}
|
|
753
|
-
>
|
|
708
|
+
<Button size="small" type="submit" isLoading={isLoading}>
|
|
754
709
|
{t("actions.saveAndClose")}
|
|
755
710
|
</Button>
|
|
756
711
|
</div>
|
|
@@ -780,7 +735,7 @@ export const TranslationsEditForm = ({
|
|
|
780
735
|
size="small"
|
|
781
736
|
onClick={handleSaveAndSwitch}
|
|
782
737
|
type="button"
|
|
783
|
-
isLoading={
|
|
738
|
+
isLoading={isLoading}
|
|
784
739
|
>
|
|
785
740
|
{t("actions.saveChanges")}
|
|
786
741
|
</Button>
|