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