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