@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.
Files changed (126) hide show
  1. package/dist/{api-key-management-detail-FRUN2KFK.mjs → api-key-management-detail-6RCDH73M.mjs} +1 -1
  2. package/dist/app.css +0 -19
  3. package/dist/app.js +679 -795
  4. package/dist/app.mjs +2 -2
  5. package/dist/{campaign-detail-HM3GQJLQ.mjs → campaign-detail-5Q4BYCPX.mjs} +1 -1
  6. package/dist/{categories-metadata-WKL3MGD7.mjs → categories-metadata-J7M3XWI7.mjs} +1 -1
  7. package/dist/{category-detail-UTWWDKFP.mjs → category-detail-S5IPXMHX.mjs} +2 -2
  8. package/dist/{category-products-XXBTCXFF.mjs → category-products-KPW6BA5J.mjs} +2 -2
  9. package/dist/{chunk-UMCJYHAD.mjs → chunk-CVHJAKLQ.mjs} +1 -1
  10. package/dist/{chunk-KSDXSKJ7.mjs → chunk-DBXWB3RF.mjs} +1 -1
  11. package/dist/{chunk-GLBHPDR4.mjs → chunk-GRZSG4EP.mjs} +16 -21
  12. package/dist/{chunk-FKNW5MLZ.mjs → chunk-LZFWCKOF.mjs} +4 -21
  13. package/dist/{chunk-5ISRTMYH.mjs → chunk-MJDHVDOW.mjs} +1 -1
  14. package/dist/{chunk-5F427YCP.mjs → chunk-OL24RDYM.mjs} +2 -2
  15. package/dist/{chunk-VFF5WB7C.mjs → chunk-OL6MEUKW.mjs} +104 -108
  16. package/dist/{chunk-OK6NZN2A.mjs → chunk-PHLCT2HA.mjs} +1 -1
  17. package/dist/{chunk-DQUXK4WW.mjs → chunk-ST4P6BQN.mjs} +1 -1
  18. package/dist/{chunk-HNJ65IND.mjs → chunk-WYATCUOM.mjs} +1 -1
  19. package/dist/{chunk-DTCIBQO2.mjs → chunk-YYOPBKME.mjs} +1 -1
  20. package/dist/{chunk-SG2JZPTG.mjs → chunk-ZMG5B4FG.mjs} +1 -1
  21. package/dist/{collection-add-products-42F7H77E.mjs → collection-add-products-FU2BS3D3.mjs} +2 -2
  22. package/dist/{collection-detail-PXIS3G64.mjs → collection-detail-VJE7XHLV.mjs} +2 -2
  23. package/dist/{collection-list-O74CGY24.mjs → collection-list-IGA6SCNF.mjs} +2 -2
  24. package/dist/{collection-metadata-U6FMA4IC.mjs → collection-metadata-QK7MI3D2.mjs} +1 -1
  25. package/dist/{customer-detail-OMTFJ6CE.mjs → customer-detail-MOV2T3LF.mjs} +1 -1
  26. package/dist/{customer-group-detail-ADK3M5LG.mjs → customer-group-detail-6T7OXGQD.mjs} +1 -1
  27. package/dist/{customer-group-list-7ZRQ2HWU.mjs → customer-group-list-AJEAF5D2.mjs} +1 -1
  28. package/dist/{customers-add-customer-group-5U27WHJB.mjs → customers-add-customer-group-QVTVSQYM.mjs} +1 -1
  29. package/dist/{edit-rules-BM2ERGVJ.mjs → edit-rules-SMVRTCUP.mjs} +1 -1
  30. package/dist/en.json +1 -1
  31. package/dist/{inventory-create-7MA7B5N2.mjs → inventory-create-BK52VALF.mjs} +2 -2
  32. package/dist/{inventory-detail-B4PRHZK3.mjs → inventory-detail-ZPSEMYI2.mjs} +1 -1
  33. package/dist/{inventory-metadata-C7MJ3GY5.mjs → inventory-metadata-FNEJ3RAT.mjs} +1 -1
  34. package/dist/{inventory-stock-WVTYPJTX.mjs → inventory-stock-6WYWLWJ7.mjs} +3 -3
  35. package/dist/{location-detail-KO6EBDK5.mjs → location-detail-N3GUZSY7.mjs} +1 -1
  36. package/dist/{location-fulfillment-providers-IORBE3E3.mjs → location-fulfillment-providers-7ZUJAGNY.mjs} +2 -2
  37. package/dist/{location-service-zone-shipping-option-create-2R3ZFLVK.mjs → location-service-zone-shipping-option-create-CNRWYZQC.mjs} +3 -3
  38. package/dist/{location-service-zone-shipping-option-pricing-5HN2Z5RB.mjs → location-service-zone-shipping-option-pricing-OGWI7VPT.mjs} +2 -2
  39. package/dist/{login-XKB6OR7I.mjs → login-VNOLI5YG.mjs} +1 -1
  40. package/dist/{order-create-claim-NKCOGF4A.mjs → order-create-claim-SCDJGM46.mjs} +1 -1
  41. package/dist/{order-create-edit-UNQYXGLL.mjs → order-create-edit-2WALBPXS.mjs} +1 -1
  42. package/dist/{order-create-exchange-WI7OA2WO.mjs → order-create-exchange-LQU4YN7F.mjs} +1 -1
  43. package/dist/{order-create-fulfillment-2LJTEWDY.mjs → order-create-fulfillment-OWUVTZXW.mjs} +1 -1
  44. package/dist/{order-create-refund-7K6UJXGP.mjs → order-create-refund-Q6HQY42R.mjs} +1 -1
  45. package/dist/{order-create-shipment-ZTDLLUBY.mjs → order-create-shipment-WAGGEPRW.mjs} +1 -1
  46. package/dist/{order-detail-JTRUMRLO.mjs → order-detail-PVPGEWGY.mjs} +1 -1
  47. package/dist/{order-edit-billing-address-YHYNVLOE.mjs → order-edit-billing-address-UM76J4KX.mjs} +1 -1
  48. package/dist/{order-edit-email-TCQPEVZY.mjs → order-edit-email-CL3KNOCM.mjs} +1 -1
  49. package/dist/{order-edit-shipping-address-CFSYQLKD.mjs → order-edit-shipping-address-PIESTGVL.mjs} +1 -1
  50. package/dist/{order-export-G4SBNEJ7.mjs → order-export-LE363ZLB.mjs} +1 -1
  51. package/dist/{order-metadata-KGPB37VL.mjs → order-metadata-FHBB7MTG.mjs} +1 -1
  52. package/dist/{order-receive-return-JER24SEV.mjs → order-receive-return-PRVKP6J2.mjs} +1 -1
  53. package/dist/{order-request-transfer-3FBUYZNT.mjs → order-request-transfer-XSAGRUMT.mjs} +1 -1
  54. package/dist/{price-list-create-CXZCFFTP.mjs → price-list-create-K5JEZT57.mjs} +4 -4
  55. package/dist/{price-list-detail-XOMU6U5J.mjs → price-list-detail-Q5VG5VGW.mjs} +2 -2
  56. package/dist/{price-list-prices-add-SDX5CQME.mjs → price-list-prices-add-2MQ226U4.mjs} +4 -4
  57. package/dist/{price-list-prices-edit-EKB6NI5D.mjs → price-list-prices-edit-OJZLV7OS.mjs} +2 -2
  58. package/dist/{product-attributes-MXDPSOWM.mjs → product-attributes-YF4TZOIO.mjs} +2 -2
  59. package/dist/{product-create-3O34JJLS.mjs → product-create-KJML2332.mjs} +3 -3
  60. package/dist/{product-create-variant-OTJKT6WI.mjs → product-create-variant-5EBCLM54.mjs} +2 -2
  61. package/dist/{product-detail-SYTLG5D3.mjs → product-detail-QG72542C.mjs} +2 -2
  62. package/dist/{product-edit-W72S22NM.mjs → product-edit-DZZR775Q.mjs} +2 -2
  63. package/dist/{product-export-57UUAGXF.mjs → product-export-5AD7NELI.mjs} +2 -2
  64. package/dist/{product-image-variants-edit-2BW5BJON.mjs → product-image-variants-edit-M6QF2RLE.mjs} +1 -1
  65. package/dist/{product-import-6EM4VUXP.mjs → product-import-V3KQN4TV.mjs} +1 -1
  66. package/dist/{product-list-5V5GEH5K.mjs → product-list-EUWZIFTM.mjs} +2 -2
  67. package/dist/{product-metadata-JZLHBLZQ.mjs → product-metadata-GL2MVPDI.mjs} +1 -1
  68. package/dist/{product-organization-SVXTCWIF.mjs → product-organization-O7RHELMQ.mjs} +2 -2
  69. package/dist/{product-prices-5ZL2RP7A.mjs → product-prices-YWV6MSM6.mjs} +1 -1
  70. package/dist/{product-stock-SJJABF6I.mjs → product-stock-AKEFMK5O.mjs} +3 -3
  71. package/dist/{product-tag-create-XXO4AQEC.mjs → product-tag-create-PQMDDKWH.mjs} +1 -1
  72. package/dist/{product-tag-detail-BSK64HXL.mjs → product-tag-detail-I3MBZX7U.mjs} +3 -3
  73. package/dist/{product-tag-edit-ENCGDT7E.mjs → product-tag-edit-K3BBQLJR.mjs} +1 -1
  74. package/dist/{product-tag-list-SLQGCNDZ.mjs → product-tag-list-JUWSOMB7.mjs} +3 -3
  75. package/dist/{product-tag-metadata-EPXHMU2K.mjs → product-tag-metadata-MJH5LH7E.mjs} +1 -1
  76. package/dist/{product-type-detail-4CRRU7YK.mjs → product-type-detail-RKHT5NBL.mjs} +2 -2
  77. package/dist/{product-type-metadata-73OKOGPP.mjs → product-type-metadata-CDJDFFGQ.mjs} +1 -1
  78. package/dist/{product-variant-detail-RPHLG4HU.mjs → product-variant-detail-XAYG5CKE.mjs} +1 -1
  79. package/dist/{product-variant-edit-JF7NN64Y.mjs → product-variant-edit-DEZEY2H2.mjs} +1 -1
  80. package/dist/{product-variant-metadata-HU2CXGPO.mjs → product-variant-metadata-VTZDNWUT.mjs} +1 -1
  81. package/dist/{promotion-create-BHA3FQG2.mjs → promotion-create-HWFNUQXG.mjs} +1 -1
  82. package/dist/{promotion-detail-F3QSR52W.mjs → promotion-detail-QC36KXB3.mjs} +1 -1
  83. package/dist/{refund-reason-create-ZA5TKW2Z.mjs → refund-reason-create-YHCDEHGQ.mjs} +1 -1
  84. package/dist/{refund-reason-edit-N2CRCLKZ.mjs → refund-reason-edit-CZ5QZ2SZ.mjs} +1 -1
  85. package/dist/{refund-reason-list-SE4TMGMT.mjs → refund-reason-list-OJYYEYJE.mjs} +1 -1
  86. package/dist/{region-metadata-O5NZBWXP.mjs → region-metadata-H6XXUQ4S.mjs} +1 -1
  87. package/dist/{reservation-detail-UFK6XIXE.mjs → reservation-detail-LZAQL4XA.mjs} +1 -1
  88. package/dist/{reservation-metadata-AEJEKGLV.mjs → reservation-metadata-5HZSDDOK.mjs} +1 -1
  89. package/dist/{sales-channel-add-products-2LMB7EF5.mjs → sales-channel-add-products-F7YV4MO5.mjs} +2 -2
  90. package/dist/{sales-channel-detail-EUQ4STQI.mjs → sales-channel-detail-MXIPZCGA.mjs} +2 -2
  91. package/dist/{sales-channel-list-JXKGHX4G.mjs → sales-channel-list-RLGL7FM3.mjs} +1 -1
  92. package/dist/{sales-channel-metadata-AJMQ5SQ2.mjs → sales-channel-metadata-M364R4RJ.mjs} +1 -1
  93. package/dist/{shipping-option-type-create-YVVIA2XC.mjs → shipping-option-type-create-C5WUWON7.mjs} +1 -1
  94. package/dist/{shipping-option-type-detail-ZZW36XLK.mjs → shipping-option-type-detail-PENS2K73.mjs} +2 -2
  95. package/dist/{shipping-option-type-edit-O6F74T3A.mjs → shipping-option-type-edit-CIU5EHRP.mjs} +1 -1
  96. package/dist/{shipping-option-type-list-SPTE7MT6.mjs → shipping-option-type-list-DIOX7VG7.mjs} +2 -2
  97. package/dist/{shipping-profile-metadata-7WFE55VG.mjs → shipping-profile-metadata-75G2NNMA.mjs} +1 -1
  98. package/dist/{chunk-IKTGFXWR.mjs → store-add-locales-VJ4RJ7UI.mjs} +67 -2
  99. package/dist/{store-detail-YLJLBBZE.mjs → store-detail-JSNPOB2F.mjs} +1 -1
  100. package/dist/{store-metadata-BZ57I2E6.mjs → store-metadata-CYXTVJUE.mjs} +1 -1
  101. package/dist/{tax-region-create-FGTV7VJL.mjs → tax-region-create-DWGL4EUT.mjs} +1 -1
  102. package/dist/{tax-region-detail-PPIMD7OX.mjs → tax-region-detail-2AE2EFI3.mjs} +5 -5
  103. package/dist/{tax-region-edit-ELZKA7YH.mjs → tax-region-edit-EEVEEU2Q.mjs} +1 -1
  104. package/dist/{tax-region-province-detail-FV2NDT3E.mjs → tax-region-province-detail-4ERSEQFF.mjs} +5 -5
  105. package/dist/{tax-region-tax-override-create-N572MQPZ.mjs → tax-region-tax-override-create-PHCGEF7V.mjs} +3 -3
  106. package/dist/{tax-region-tax-override-edit-5DCSJW6D.mjs → tax-region-tax-override-edit-SMRPSILC.mjs} +4 -4
  107. package/dist/{translation-list-FK7XYLHX.mjs → translation-list-UF7FLXOW.mjs} +141 -227
  108. package/dist/{translations-edit-VRXZI5KW.mjs → translations-edit-USQJNMAY.mjs} +253 -224
  109. package/dist/{user-metadata-GRJZZ524.mjs → user-metadata-2WPJOEJA.mjs} +1 -1
  110. package/dist/{workflow-execution-detail-HXTFWGKG.mjs → workflow-execution-detail-H2AKEZJX.mjs} +1 -1
  111. package/package.json +9 -9
  112. package/src/components/data-grid/hooks/use-data-grid-cell.tsx +0 -1
  113. package/src/components/data-grid/hooks/use-data-grid-form-handlers.tsx +0 -1
  114. package/src/components/data-grid/hooks/use-data-grid-keydown-event.tsx +4 -22
  115. package/src/dashboard-app/routes/get-route.map.tsx +0 -4
  116. package/src/hooks/api/translations.tsx +17 -26
  117. package/src/i18n/translations/en.json +1 -1
  118. package/src/i18n/translations/es.json +1 -1
  119. package/src/routes/translations/translation-list/components/active-locales-section/active-locales-section.tsx +17 -42
  120. package/src/routes/translations/translation-list/components/translation-list-section/translation-list-section.tsx +1 -5
  121. package/src/routes/translations/translation-list/components/translations-completion-section/translations-completion-section.tsx +121 -182
  122. package/src/routes/translations/translations-edit/components/translations-edit-form/translations-edit-form.tsx +330 -285
  123. package/dist/add-locales-GGNZCABB.mjs +0 -81
  124. package/dist/store-add-locales-GWCGIXHU.mjs +0 -81
  125. package/src/routes/translations/add-locales/add-locales.tsx +0 -29
  126. package/src/routes/translations/add-locales/index.tsx +0 -1
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  DataGrid,
3
3
  createDataGridHelper
4
- } from "./chunk-FKNW5MLZ.mjs";
4
+ } from "./chunk-LZFWCKOF.mjs";
5
5
  import "./chunk-DK4WIVY6.mjs";
6
6
  import "./chunk-IUCDCPJU.mjs";
7
7
  import {
@@ -23,7 +23,7 @@ import {
23
23
  useBatchTranslations,
24
24
  useReferenceTranslations,
25
25
  useTranslationSettings
26
- } from "./chunk-GLBHPDR4.mjs";
26
+ } from "./chunk-GRZSG4EP.mjs";
27
27
  import "./chunk-I4OBEAOJ.mjs";
28
28
  import "./chunk-HI6URQ7H.mjs";
29
29
  import "./chunk-6CLQKVAU.mjs";
@@ -69,125 +69,169 @@ import { useForm } from "react-hook-form";
69
69
  import { useTranslation } from "react-i18next";
70
70
  import { z } from "zod";
71
71
  import { jsx, jsxs } from "react/jsx-runtime";
72
- var EntityTranslationsSchema = z.object({
72
+ var LocaleTranslationSchema = z.object({
73
73
  id: z.string().nullish(),
74
+ locale_code: z.string(),
74
75
  fields: z.record(z.string().optional())
75
76
  });
77
+ var EntityTranslationsSchema = z.object({
78
+ locales: z.record(LocaleTranslationSchema)
79
+ });
76
80
  var TranslationsFormSchema = z.object({
77
81
  entities: z.record(EntityTranslationsSchema)
78
82
  });
79
83
  function isEntityRow(row) {
80
84
  return row._type === "entity";
81
85
  }
82
- function buildLocaleSnapshot(translations, references, localeCode, translatableFields) {
83
- const referenceTranslations = /* @__PURE__ */ new Map();
84
- for (const t of translations) {
85
- if (t.locale_code === localeCode) {
86
- referenceTranslations.set(t.reference_id, t);
87
- }
88
- }
89
- const entities = {};
90
- for (const ref of references) {
91
- const existing = referenceTranslations.get(ref.id);
92
- const fields = {};
93
- for (const fieldName of translatableFields) {
94
- fields[fieldName] = existing?.translations?.[fieldName] ?? "";
95
- }
96
- entities[ref.id] = {
97
- id: existing?.id ?? null,
98
- fields
99
- };
100
- }
101
- return { localeCode, entities };
102
- }
103
- function extendSnapshot(snapshot, translations, newReferences, translatableFields) {
104
- const referenceTranslations = /* @__PURE__ */ new Map();
86
+ function initTranslationsFormState(translations, references, availableLocales, translatableFields) {
87
+ const existingMap = /* @__PURE__ */ new Map();
105
88
  for (const t of translations) {
106
- if (t.locale_code === snapshot.localeCode) {
107
- referenceTranslations.set(t.reference_id, t);
108
- }
89
+ existingMap.set(`${t.reference_id}:${t.locale_code}`, t);
109
90
  }
110
- const extendedEntities = { ...snapshot.entities };
111
- for (const ref of newReferences) {
112
- if (!extendedEntities[ref.id]) {
113
- const existing = referenceTranslations.get(ref.id);
91
+ const entitiesTranslationState = {};
92
+ for (const reference of references) {
93
+ const locales = {};
94
+ for (const locale of availableLocales) {
95
+ const key = `${reference.id}:${locale.locale_code}`;
96
+ const existing = existingMap.get(key);
114
97
  const fields = {};
115
98
  for (const fieldName of translatableFields) {
116
- fields[fieldName] = existing?.translations?.[fieldName] ?? "";
99
+ const fieldValue = existing?.translations?.[fieldName] ?? "";
100
+ fields[fieldName] = fieldValue;
117
101
  }
118
- extendedEntities[ref.id] = {
102
+ locales[locale.locale_code] = {
119
103
  id: existing?.id ?? null,
104
+ locale_code: locale.locale_code,
120
105
  fields
121
106
  };
122
107
  }
108
+ entitiesTranslationState[reference.id] = { locales };
123
109
  }
124
- return { ...snapshot, entities: extendedEntities };
110
+ return {
111
+ entities: entitiesTranslationState
112
+ };
125
113
  }
126
- function snapshotToFormValues(snapshot) {
127
- return { entities: snapshot.entities };
114
+ function buildTranslationRows(references, translatableFields) {
115
+ return references.map((reference) => ({
116
+ _type: "entity",
117
+ reference_id: reference.id,
118
+ subRows: translatableFields.map((fieldName) => ({
119
+ _type: "field",
120
+ reference_id: reference.id,
121
+ field_name: fieldName
122
+ }))
123
+ }));
128
124
  }
129
- function computeChanges(currentState, snapshot, entityType, localeCode) {
125
+ function transformToBatchPayload(currentState, initialState, entityType) {
130
126
  const payload = {
131
127
  create: [],
132
128
  update: [],
133
129
  delete: []
134
130
  };
135
131
  for (const [entityId, entityData] of Object.entries(currentState.entities)) {
136
- const baseline = snapshot.entities[entityId];
137
- if (!baseline) {
132
+ for (const [localeCode, localeTranslations] of Object.entries(
133
+ entityData.locales
134
+ )) {
135
+ const initial = initialState.entities[entityId]?.locales[localeCode];
136
+ const hasContent = Object.values(localeTranslations.fields).some(
137
+ (v) => v !== void 0 && v.trim() !== ""
138
+ );
139
+ const hadContent = initial && Object.values(initial.fields).some(
140
+ (v) => v !== void 0 && v.trim() !== ""
141
+ );
142
+ if (!localeTranslations.id && hasContent) {
143
+ payload.create.push({
144
+ reference_id: entityId,
145
+ reference: entityType,
146
+ locale_code: localeTranslations.locale_code,
147
+ translations: localeTranslations.fields
148
+ });
149
+ } else if (localeTranslations.id && hasContent) {
150
+ const hasChanged = !initial || JSON.stringify(localeTranslations.fields) !== JSON.stringify(initial.fields);
151
+ if (hasChanged) {
152
+ payload.update.push({
153
+ id: localeTranslations.id,
154
+ translations: localeTranslations.fields
155
+ });
156
+ }
157
+ } else if (localeTranslations.id && !hasContent && hadContent) {
158
+ payload.delete.push(localeTranslations.id);
159
+ }
160
+ }
161
+ }
162
+ return payload;
163
+ }
164
+ function hasLocaleChanges(currentState, initialState, localeCode) {
165
+ for (const [entityId, entityData] of Object.entries(currentState.entities)) {
166
+ const currentLocale = entityData.locales[localeCode];
167
+ const initialLocale = initialState.entities[entityId]?.locales[localeCode];
168
+ if (!currentLocale || !initialLocale) {
138
169
  continue;
139
170
  }
140
- const hasContent = Object.values(entityData.fields).some(
171
+ for (const [fieldName, fieldValue] of Object.entries(
172
+ currentLocale.fields
173
+ )) {
174
+ const initialValue = initialLocale.fields[fieldName] ?? "";
175
+ const currentValue = fieldValue ?? "";
176
+ if (currentValue !== initialValue) {
177
+ return true;
178
+ }
179
+ }
180
+ }
181
+ return false;
182
+ }
183
+ function transformSingleLocaleToBatchPayload(currentState, initialState, entityType, localeCode) {
184
+ const payload = {
185
+ create: [],
186
+ update: [],
187
+ delete: []
188
+ };
189
+ for (const [entityId, entityData] of Object.entries(currentState.entities)) {
190
+ const localeTranslations = entityData.locales[localeCode];
191
+ if (!localeTranslations) continue;
192
+ const initial = initialState.entities[entityId]?.locales[localeCode];
193
+ const hasContent = Object.values(localeTranslations.fields).some(
141
194
  (v) => v !== void 0 && v.trim() !== ""
142
195
  );
143
- const hadContent = Object.values(baseline.fields).some(
196
+ const hadContent = initial && Object.values(initial.fields).some(
144
197
  (v) => v !== void 0 && v.trim() !== ""
145
198
  );
146
- const hasChanged = JSON.stringify(entityData.fields) !== JSON.stringify(baseline.fields);
147
- if (!entityData.id && hasContent) {
199
+ if (!localeTranslations.id && hasContent) {
148
200
  payload.create.push({
149
201
  reference_id: entityId,
150
202
  reference: entityType,
151
- locale_code: localeCode,
152
- translations: entityData.fields
153
- });
154
- } else if (entityData.id && hasContent && hasChanged) {
155
- payload.update.push({
156
- id: entityData.id,
157
- translations: entityData.fields
203
+ locale_code: localeTranslations.locale_code,
204
+ translations: localeTranslations.fields
158
205
  });
159
- } else if (entityData.id && !hasContent && hadContent) {
160
- payload.delete.push(entityData.id);
206
+ } else if (localeTranslations.id && hasContent) {
207
+ const hasChanged = !initial || JSON.stringify(localeTranslations.fields) !== JSON.stringify(initial.fields);
208
+ if (hasChanged) {
209
+ payload.update.push({
210
+ id: localeTranslations.id,
211
+ translations: localeTranslations.fields
212
+ });
213
+ }
214
+ } else if (localeTranslations.id && !hasContent && hadContent) {
215
+ payload.delete.push(localeTranslations.id);
161
216
  }
162
217
  }
163
- const hasChanges = payload.create.length > 0 || payload.update.length > 0 || payload.delete.length > 0;
164
- return { hasChanges, payload };
218
+ return payload;
165
219
  }
166
220
  var columnHelper = createDataGridHelper();
167
- var FIELD_COLUMN_WIDTH = 350;
168
- function buildTranslationRows(references, translatableFields) {
169
- return references.map((reference) => ({
170
- _type: "entity",
171
- reference_id: reference.id,
172
- subRows: translatableFields.map((fieldName) => ({
173
- _type: "field",
174
- reference_id: reference.id,
175
- field_name: fieldName
176
- }))
177
- }));
178
- }
221
+ var FIELD_COLUMN_WIDTH = 150;
179
222
  function useTranslationsGridColumns({
180
223
  entities,
224
+ translatableFields,
181
225
  availableLocales,
182
226
  selectedLocale,
183
227
  dynamicColumnWidth
184
228
  }) {
185
229
  const { t } = useTranslation();
186
- return useMemo(() => {
230
+ const columns = useMemo(() => {
187
231
  const selectedLocaleData = availableLocales.find(
188
232
  (l) => l.locale_code === selectedLocale
189
233
  );
190
- const columns = [
234
+ const baseColumns = [
191
235
  columnHelper.column({
192
236
  id: "field",
193
237
  name: "field",
@@ -223,7 +267,9 @@ function useTranslationsGridColumns({
223
267
  if (isEntityRow(row)) {
224
268
  return /* @__PURE__ */ jsx(DataGrid.ReadonlyCell, { context });
225
269
  }
226
- const entity = entities.find((e) => e.id === row.reference_id);
270
+ const entity = entities.find(
271
+ (entity2) => entity2.id === row.reference_id
272
+ );
227
273
  if (!entity) {
228
274
  return null;
229
275
  }
@@ -232,7 +278,7 @@ function useTranslationsGridColumns({
232
278
  })
233
279
  ];
234
280
  if (selectedLocaleData) {
235
- columns.push(
281
+ baseColumns.push(
236
282
  columnHelper.column({
237
283
  id: selectedLocaleData.locale_code,
238
284
  name: selectedLocaleData.locale.name,
@@ -250,14 +296,22 @@ function useTranslationsGridColumns({
250
296
  if (isEntityRow(row)) {
251
297
  return null;
252
298
  }
253
- return `entities.${row.reference_id}.fields.${row.field_name}`;
299
+ return `entities.${row.reference_id}.locales.${selectedLocaleData.locale_code}.fields.${row.field_name}`;
254
300
  },
255
301
  type: "multiline-text"
256
302
  })
257
303
  );
258
304
  }
259
- return columns;
260
- }, [t, availableLocales, selectedLocale, entities, dynamicColumnWidth]);
305
+ return baseColumns;
306
+ }, [
307
+ t,
308
+ translatableFields,
309
+ availableLocales,
310
+ selectedLocale,
311
+ entities,
312
+ dynamicColumnWidth
313
+ ]);
314
+ return columns;
261
315
  }
262
316
  var TranslationsEditForm = ({
263
317
  translations,
@@ -295,83 +349,57 @@ var TranslationsEditForm = ({
295
349
  );
296
350
  const [showUnsavedPrompt, setShowUnsavedPrompt] = useState(false);
297
351
  const [pendingLocale, setPendingLocale] = useState(null);
298
- const [isSwitchingLocale, setIsSwitchingLocale] = useState(false);
299
- const snapshotRef = useRef(
300
- buildLocaleSnapshot(
352
+ const entities = useMemo(() => references, [references]);
353
+ const totalCount = useMemo(
354
+ () => referenceCount * (translatableFields.length + 1),
355
+ [referenceCount, translatableFields]
356
+ );
357
+ const initialState = useRef(
358
+ initTranslationsFormState(
301
359
  translations,
302
- references,
303
- selectedLocale,
360
+ entities,
361
+ availableLocales,
304
362
  translatableFields
305
363
  )
306
364
  );
307
- const knownEntityIdsRef = useRef(
308
- new Set(references.map((r) => r.id))
309
- );
310
- const latestPropsRef = useRef({ translations, references });
311
- useEffect(() => {
312
- latestPropsRef.current = { translations, references };
313
- }, [translations, references]);
314
365
  const form = useForm({
315
366
  resolver: zodResolver(TranslationsFormSchema),
316
- defaultValues: snapshotToFormValues(snapshotRef.current)
367
+ defaultValues: initialState.current
317
368
  });
318
- useEffect(() => {
319
- const currentIds = new Set(references.map((r) => r.id));
320
- const newReferences = references.filter(
321
- (r) => !knownEntityIdsRef.current.has(r.id)
322
- );
323
- if (newReferences.length === 0) {
324
- return;
325
- }
326
- knownEntityIdsRef.current = currentIds;
327
- snapshotRef.current = extendSnapshot(
328
- snapshotRef.current,
329
- translations,
330
- newReferences,
331
- translatableFields
332
- );
333
- const currentValues = form.getValues();
334
- const newFormValues = {
335
- entities: { ...currentValues.entities }
336
- };
337
- for (const ref of newReferences) {
338
- if (!newFormValues.entities[ref.id]) {
339
- newFormValues.entities[ref.id] = snapshotRef.current.entities[ref.id];
340
- }
341
- }
342
- form.reset(newFormValues, {
343
- keepDirty: true,
344
- keepDirtyValues: true
345
- });
346
- }, [references, translations, translatableFields, form]);
347
369
  const rows = useMemo(
348
- () => buildTranslationRows(references, translatableFields),
349
- [references, translatableFields]
350
- );
351
- const totalRowCount = useMemo(
352
- () => referenceCount * (translatableFields.length + 1),
353
- [referenceCount, translatableFields]
370
+ () => buildTranslationRows(entities, translatableFields),
371
+ [entities, translatableFields]
354
372
  );
355
- const selectedLocaleDisplay = useMemo(
356
- () => availableLocales.find((l) => l.locale_code === selectedLocale)?.locale.name,
357
- [availableLocales, selectedLocale]
373
+ const { mutateAsync, isPending } = useBatchTranslations(entityType);
374
+ const handleLocaleChange = useCallback(
375
+ (newLocale) => {
376
+ if (newLocale === selectedLocale) {
377
+ return;
378
+ }
379
+ const currentValues = form.getValues();
380
+ const hasChanges = hasLocaleChanges(
381
+ currentValues,
382
+ initialState.current,
383
+ selectedLocale
384
+ );
385
+ if (hasChanges) {
386
+ setPendingLocale(newLocale);
387
+ setShowUnsavedPrompt(true);
388
+ } else {
389
+ setSelectedLocale(newLocale);
390
+ }
391
+ },
392
+ [selectedLocale, form]
358
393
  );
359
- const columns = useTranslationsGridColumns({
360
- entities: references,
361
- availableLocales,
362
- selectedLocale,
363
- dynamicColumnWidth
364
- });
365
- const { mutateAsync, isPending, invalidateQueries } = useBatchTranslations(entityType);
366
394
  const saveCurrentLocale = useCallback(async () => {
367
395
  const currentValues = form.getValues();
368
- const { hasChanges, payload } = computeChanges(
396
+ const payload = transformSingleLocaleToBatchPayload(
369
397
  currentValues,
370
- snapshotRef.current,
398
+ initialState.current,
371
399
  entityType,
372
400
  selectedLocale
373
401
  );
374
- if (!hasChanges) {
402
+ if (payload.create.length === 0 && payload.update.length === 0 && payload.delete.length === 0) {
375
403
  return true;
376
404
  }
377
405
  try {
@@ -395,32 +423,20 @@ var TranslationsEditForm = ({
395
423
  }
396
424
  if (payload.delete.length > 0) {
397
425
  currentBatch.delete = payload.delete.splice(0, currentBatchAvailable);
426
+ currentBatchAvailable -= currentBatch.delete.length;
398
427
  }
399
- const response = await mutateAsync(currentBatch, {
400
- onError: (error) => {
401
- toast.error(error.message);
402
- }
403
- });
404
- if (response.created) {
405
- for (const created of response.created) {
406
- form.setValue(`entities.${created.reference_id}.id`, created.id, {
407
- shouldDirty: false
408
- });
409
- if (snapshotRef.current.entities[created.reference_id]) {
410
- snapshotRef.current.entities[created.reference_id].id = created.id;
411
- }
412
- }
413
- }
428
+ await mutateAsync(currentBatch);
414
429
  }
415
- const savedValues = form.getValues();
416
- for (const entityId of Object.keys(savedValues.entities)) {
417
- if (snapshotRef.current.entities[entityId]) {
418
- snapshotRef.current.entities[entityId] = {
419
- ...savedValues.entities[entityId]
430
+ const updatedInitialState = { ...initialState.current };
431
+ for (const entityId of Object.keys(currentValues.entities)) {
432
+ if (updatedInitialState.entities[entityId]?.locales[selectedLocale]) {
433
+ updatedInitialState.entities[entityId].locales[selectedLocale] = {
434
+ ...currentValues.entities[entityId].locales[selectedLocale]
420
435
  };
421
436
  }
422
437
  }
423
- form.reset(savedValues);
438
+ initialState.current = updatedInitialState;
439
+ form.reset(currentValues);
424
440
  return true;
425
441
  } catch (error) {
426
442
  toast.error(
@@ -429,59 +445,19 @@ var TranslationsEditForm = ({
429
445
  return false;
430
446
  }
431
447
  }, [form, entityType, selectedLocale, mutateAsync]);
432
- const switchToLocale = useCallback(
433
- async (newLocale) => {
434
- setIsSwitchingLocale(true);
435
- try {
436
- await invalidateQueries();
437
- await new Promise((resolve) => requestAnimationFrame(resolve));
438
- const { translations: translations2, references: references2 } = latestPropsRef.current;
439
- const newSnapshot = buildLocaleSnapshot(
440
- translations2,
441
- references2,
442
- newLocale,
443
- translatableFields
444
- );
445
- snapshotRef.current = newSnapshot;
446
- knownEntityIdsRef.current = new Set(references2.map((r) => r.id));
447
- form.reset(snapshotToFormValues(newSnapshot));
448
- setSelectedLocale(newLocale);
449
- } finally {
450
- setIsSwitchingLocale(false);
451
- }
452
- },
453
- [translatableFields, form, invalidateQueries]
454
- );
455
- const handleLocaleChange = useCallback(
456
- (newLocale) => {
457
- if (newLocale === selectedLocale) {
458
- return;
459
- }
460
- const currentValues = form.getValues();
461
- const { hasChanges } = computeChanges(
462
- currentValues,
463
- snapshotRef.current,
464
- entityType,
465
- selectedLocale
466
- );
467
- if (hasChanges) {
468
- setPendingLocale(newLocale);
469
- setShowUnsavedPrompt(true);
470
- } else {
471
- switchToLocale(newLocale);
472
- }
473
- },
474
- [selectedLocale, form, entityType, switchToLocale]
475
- );
476
448
  const handleSaveAndSwitch = useCallback(async () => {
477
449
  const success = await saveCurrentLocale();
478
450
  if (success && pendingLocale) {
479
- toast.success(t("translations.edit.successToast"));
480
- await switchToLocale(pendingLocale);
451
+ toast.success(
452
+ t("translations.edit.localeChangesSaved", {
453
+ defaultValue: "Changes saved successfully"
454
+ })
455
+ );
456
+ setSelectedLocale(pendingLocale);
481
457
  }
482
458
  setShowUnsavedPrompt(false);
483
459
  setPendingLocale(null);
484
- }, [saveCurrentLocale, pendingLocale, t, switchToLocale]);
460
+ }, [saveCurrentLocale, pendingLocale, t]);
485
461
  const handleCancelSwitch = useCallback(() => {
486
462
  setShowUnsavedPrompt(false);
487
463
  setPendingLocale(null);
@@ -498,15 +474,69 @@ var TranslationsEditForm = ({
498
474
  },
499
475
  [saveCurrentLocale, t, handleSuccess]
500
476
  );
501
- const handleClose = useCallback(() => {
502
- invalidateQueries();
503
- }, [invalidateQueries]);
504
- const isLoading = isPending || isSwitchingLocale;
505
- return /* @__PURE__ */ jsxs(RouteFocusModal.Form, { form, onClose: handleClose, children: [
477
+ const handleSubmit = form.handleSubmit(async (values) => {
478
+ const payload = transformToBatchPayload(
479
+ values,
480
+ initialState.current,
481
+ entityType
482
+ );
483
+ if (payload.create.length === 0 && payload.update.length === 0 && payload.delete.length === 0) {
484
+ return;
485
+ }
486
+ const BATCH_SIZE = 150;
487
+ const totalItems = payload.create.length + payload.update.length + payload.delete.length;
488
+ const batchCount = Math.ceil(totalItems / BATCH_SIZE);
489
+ for (let i = 0; i < batchCount; i++) {
490
+ let currentBatchAvailable = BATCH_SIZE;
491
+ const currentBatch = {
492
+ create: [],
493
+ update: [],
494
+ delete: []
495
+ };
496
+ if (payload.create.length > 0) {
497
+ currentBatch.create = payload.create.splice(0, currentBatchAvailable);
498
+ currentBatchAvailable -= currentBatch.create.length;
499
+ }
500
+ if (payload.update.length > 0) {
501
+ currentBatch.update = payload.update.splice(0, currentBatchAvailable);
502
+ currentBatchAvailable -= currentBatch.update.length;
503
+ }
504
+ if (payload.delete.length > 0) {
505
+ currentBatch.delete = payload.delete.splice(0, currentBatchAvailable);
506
+ currentBatchAvailable -= currentBatch.delete.length;
507
+ }
508
+ await mutateAsync(currentBatch, {
509
+ onSuccess: () => {
510
+ if (i === batchCount - 1) {
511
+ toast.success(
512
+ t("translations.edit.successToast", {
513
+ defaultValue: "Translations updated successfully"
514
+ })
515
+ );
516
+ handleSuccess();
517
+ }
518
+ },
519
+ onError: (error) => {
520
+ toast.error(error.message);
521
+ }
522
+ });
523
+ }
524
+ });
525
+ const columns = useTranslationsGridColumns({
526
+ entities,
527
+ translatableFields,
528
+ availableLocales,
529
+ selectedLocale,
530
+ dynamicColumnWidth
531
+ });
532
+ const selectedLocaleDisplay = availableLocales.find(
533
+ (l) => l.locale_code === selectedLocale
534
+ )?.locale.name;
535
+ return /* @__PURE__ */ jsxs(RouteFocusModal.Form, { form, children: [
506
536
  /* @__PURE__ */ jsxs(
507
537
  KeyboundForm,
508
538
  {
509
- onSubmit: () => handleSave(true),
539
+ onSubmit: handleSubmit,
510
540
  className: "flex h-full flex-col overflow-hidden",
511
541
  children: [
512
542
  /* @__PURE__ */ jsx(RouteFocusModal.Header, {}),
@@ -523,14 +553,13 @@ var TranslationsEditForm = ({
523
553
  },
524
554
  state: form,
525
555
  onEditingChange: (editing) => setCloseOnEscape(!editing),
526
- totalRowCount,
556
+ totalRowCount: totalCount,
527
557
  onFetchMore: fetchNextPage,
528
558
  isFetchingMore: isFetchingNextPage,
529
559
  hasNextPage,
530
560
  headerContent: /* @__PURE__ */ jsxs(
531
561
  Select,
532
562
  {
533
- disabled: isLoading,
534
563
  value: selectedLocale,
535
564
  onValueChange: handleLocaleChange,
536
565
  size: "small",
@@ -550,28 +579,28 @@ var TranslationsEditForm = ({
550
579
  }
551
580
  ) }) }),
552
581
  /* @__PURE__ */ jsx(RouteFocusModal.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
553
- /* @__PURE__ */ jsx(RouteFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsx(
582
+ /* @__PURE__ */ jsx(RouteFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: t("actions.cancel") }) }),
583
+ /* @__PURE__ */ jsx(
554
584
  Button,
555
585
  {
556
- type: "button",
557
586
  size: "small",
587
+ type: "button",
558
588
  variant: "secondary",
559
- isLoading,
560
- children: t("actions.cancel")
589
+ onClick: () => handleSave(false),
590
+ isLoading: isPending,
591
+ children: t("actions.saveChanges")
561
592
  }
562
- ) }),
593
+ ),
563
594
  /* @__PURE__ */ jsx(
564
595
  Button,
565
596
  {
566
597
  size: "small",
567
598
  type: "button",
568
- variant: "secondary",
569
- onClick: () => handleSave(false),
570
- isLoading,
571
- children: t("actions.saveChanges")
599
+ onClick: () => handleSave(true),
600
+ isLoading: isPending,
601
+ children: t("actions.saveAndClose")
572
602
  }
573
- ),
574
- /* @__PURE__ */ jsx(Button, { size: "small", type: "submit", isLoading, children: t("actions.saveAndClose") })
603
+ )
575
604
  ] }) })
576
605
  ]
577
606
  }
@@ -598,7 +627,7 @@ var TranslationsEditForm = ({
598
627
  size: "small",
599
628
  onClick: handleSaveAndSwitch,
600
629
  type: "button",
601
- isLoading,
630
+ isLoading: isPending,
602
631
  children: t("actions.saveChanges")
603
632
  }
604
633
  )
@@ -12,7 +12,7 @@ import "./chunk-LPEUYMRK.mjs";
12
12
  import "./chunk-OC7BQLYI.mjs";
13
13
  import "./chunk-S4DMV3ZT.mjs";
14
14
  import "./chunk-OBQI23QM.mjs";
15
- import "./chunk-GLBHPDR4.mjs";
15
+ import "./chunk-GRZSG4EP.mjs";
16
16
  import "./chunk-I4OBEAOJ.mjs";
17
17
  import "./chunk-HI6URQ7H.mjs";
18
18
  import "./chunk-6CLQKVAU.mjs";
@@ -20,7 +20,7 @@ import {
20
20
  import {
21
21
  useDocumentDirection
22
22
  } from "./chunk-S4DMV3ZT.mjs";
23
- import "./chunk-GLBHPDR4.mjs";
23
+ import "./chunk-GRZSG4EP.mjs";
24
24
  import "./chunk-I4OBEAOJ.mjs";
25
25
  import "./chunk-HI6URQ7H.mjs";
26
26
  import "./chunk-6CLQKVAU.mjs";