@medusajs/dashboard 3.0.0-snapshot-20251216103925 → 3.0.0-snapshot-20251216135612

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 (125) hide show
  1. package/dist/{add-campaign-promotions-PHAHMGXP.mjs → add-campaign-promotions-OYPGISTF.mjs} +4 -4
  2. package/dist/{api-key-management-detail-NRGL7HHX.mjs → api-key-management-detail-6RCDH73M.mjs} +5 -5
  3. package/dist/{api-key-management-list-LJSWRAGE.mjs → api-key-management-list-KC5GOWAU.mjs} +5 -5
  4. package/dist/{api-key-management-sales-channels-3GRNDBZ4.mjs → api-key-management-sales-channels-LUB5G6RC.mjs} +5 -5
  5. package/dist/app.js +3752 -133
  6. package/dist/app.mjs +2 -2
  7. package/dist/{campaign-detail-22Q6XWGL.mjs → campaign-detail-5Q4BYCPX.mjs} +5 -5
  8. package/dist/{campaign-list-OH566KWW.mjs → campaign-list-PEOTTWBA.mjs} +6 -6
  9. package/dist/{category-detail-RRKJPMEG.mjs → category-detail-S5IPXMHX.mjs} +4 -4
  10. package/dist/{category-list-4HZP4FRE.mjs → category-list-QBYJ4T3R.mjs} +4 -4
  11. package/dist/{category-products-EFZRTCSF.mjs → category-products-KPW6BA5J.mjs} +4 -4
  12. package/dist/{chunk-LCEKY54O.mjs → chunk-27MGH3HR.mjs} +1 -1
  13. package/dist/{chunk-KQ4LC2YV.mjs → chunk-2DULKOPN.mjs} +1 -1
  14. package/dist/{chunk-SUYYSKCB.mjs → chunk-535OVBXR.mjs} +8 -1
  15. package/dist/{chunk-32FPYJ3S.mjs → chunk-AHZLMCZF.mjs} +1 -1
  16. package/dist/{chunk-5IFK2ZTA.mjs → chunk-APGDAT7X.mjs} +2 -2
  17. package/dist/{chunk-2X25XWOW.mjs → chunk-BTYBTKWK.mjs} +1 -1
  18. package/dist/{chunk-DNUVCBN7.mjs → chunk-CCQD65EY.mjs} +160 -58
  19. package/dist/{chunk-RL5EYTP6.mjs → chunk-CVHJAKLQ.mjs} +1 -1
  20. package/dist/{chunk-DZWH2RV6.mjs → chunk-DFFLVEZ5.mjs} +1 -1
  21. package/dist/{chunk-YTN73JYH.mjs → chunk-DTZXEQXZ.mjs} +1 -1
  22. package/dist/{chunk-7R743WZ6.mjs → chunk-EHU67PIM.mjs} +1 -1
  23. package/dist/{chunk-VX2HE7O5.mjs → chunk-FBYTX6K7.mjs} +1 -1
  24. package/dist/{chunk-RMDYKRWW.mjs → chunk-KFYQTOGB.mjs} +1 -1
  25. package/dist/{chunk-GA3UMQMZ.mjs → chunk-O7JNIATG.mjs} +1 -1
  26. package/dist/{chunk-OHY2N2Q4.mjs → chunk-OL24RDYM.mjs} +3 -3
  27. package/dist/{chunk-M4Q5Q4I3.mjs → chunk-PA3T6IWL.mjs} +2 -2
  28. package/dist/{chunk-NL376WAO.mjs → chunk-QISH26J3.mjs} +1 -1
  29. package/dist/{chunk-PVW6M64S.mjs → chunk-QOCJPBRB.mjs} +1 -1
  30. package/dist/{chunk-2GWGNMAA.mjs → chunk-R4ZOO4ON.mjs} +1 -1
  31. package/dist/{chunk-43X7ZR3P.mjs → chunk-RS7DWLEP.mjs} +1 -1
  32. package/dist/{chunk-ROKB75YP.mjs → chunk-VT2JJ5C2.mjs} +1 -1
  33. package/dist/{chunk-DK7IWUMK.mjs → chunk-WYATCUOM.mjs} +3 -3
  34. package/dist/{chunk-CV65NY6Y.mjs → chunk-YFIYCS7F.mjs} +1 -1
  35. package/dist/{chunk-O7WJSSQR.mjs → chunk-YKYVCQRS.mjs} +3346 -93
  36. package/dist/{chunk-GU5PJRPM.mjs → chunk-ZQJPHZKI.mjs} +1 -1
  37. package/dist/{collection-add-products-XUMV6XR7.mjs → collection-add-products-FU2BS3D3.mjs} +4 -4
  38. package/dist/{collection-detail-ONRBKJLN.mjs → collection-detail-VJE7XHLV.mjs} +4 -4
  39. package/dist/{collection-list-XCC4SIPJ.mjs → collection-list-IGA6SCNF.mjs} +4 -4
  40. package/dist/{customer-detail-FR6J37ZC.mjs → customer-detail-MOV2T3LF.mjs} +6 -6
  41. package/dist/{customer-group-add-customers-DM4VLTNB.mjs → customer-group-add-customers-XMR2WBXX.mjs} +6 -6
  42. package/dist/{customer-group-detail-YSKSNETG.mjs → customer-group-detail-6T7OXGQD.mjs} +6 -6
  43. package/dist/{customer-group-list-XBCD4FZH.mjs → customer-group-list-AJEAF5D2.mjs} +3 -3
  44. package/dist/{customer-list-TG4D4QOT.mjs → customer-list-UI5EQDII.mjs} +6 -6
  45. package/dist/{customers-add-customer-group-Q7FMR2Y5.mjs → customers-add-customer-group-QVTVSQYM.mjs} +4 -4
  46. package/dist/{inventory-create-3XONKYMZ.mjs → inventory-create-ANYUM4P5.mjs} +1 -1
  47. package/dist/{inventory-detail-6A6GOLB6.mjs → inventory-detail-ZPSEMYI2.mjs} +4 -4
  48. package/dist/{inventory-list-2CJLAK3X.mjs → inventory-list-RXJPSVZE.mjs} +4 -4
  49. package/dist/{inventory-stock-S3ZYYCMZ.mjs → inventory-stock-FD4ZM4BB.mjs} +2 -2
  50. package/dist/{location-fulfillment-providers-WM6DT252.mjs → location-fulfillment-providers-7ZUJAGNY.mjs} +4 -4
  51. package/dist/{location-sales-channels-WLVTMU4Z.mjs → location-sales-channels-P3QJTFDT.mjs} +5 -5
  52. package/dist/{location-service-zone-create-3FWF3DG5.mjs → location-service-zone-create-J43WN6G4.mjs} +5 -5
  53. package/dist/{location-service-zone-manage-areas-YFEAZUUN.mjs → location-service-zone-manage-areas-6ZPMKMSX.mjs} +5 -5
  54. package/dist/{location-service-zone-shipping-option-create-MJPH3WKX.mjs → location-service-zone-shipping-option-create-ZJ4GIBTJ.mjs} +2 -2
  55. package/dist/{location-service-zone-shipping-option-pricing-6IRNPWJY.mjs → location-service-zone-shipping-option-pricing-CR4BVYG3.mjs} +2 -2
  56. package/dist/{order-create-claim-GUYTLVPB.mjs → order-create-claim-SCDJGM46.mjs} +4 -4
  57. package/dist/{order-create-edit-ODIN6GRW.mjs → order-create-edit-JIE3HDHP.mjs} +4 -4
  58. package/dist/{order-create-exchange-ZT5RBRKL.mjs → order-create-exchange-LQU4YN7F.mjs} +4 -4
  59. package/dist/{order-create-return-E2KILJX2.mjs → order-create-return-52GHGW5Z.mjs} +4 -4
  60. package/dist/{order-detail-HFJONELJ.mjs → order-detail-PVPGEWGY.mjs} +2 -2
  61. package/dist/{order-export-4MZUPMGD.mjs → order-export-LE363ZLB.mjs} +3 -3
  62. package/dist/{order-list-ACSFGIPD.mjs → order-list-GRLQWN4L.mjs} +7 -7
  63. package/dist/{price-list-configuration-IHPSUNZJ.mjs → price-list-configuration-6S3MLNXQ.mjs} +5 -5
  64. package/dist/{price-list-create-YHXRQSC3.mjs → price-list-create-MFRUQADC.mjs} +7 -7
  65. package/dist/{price-list-detail-FR3FQR3H.mjs → price-list-detail-Q5VG5VGW.mjs} +5 -5
  66. package/dist/{price-list-list-YSEM6IAI.mjs → price-list-list-DG5YEZ44.mjs} +4 -4
  67. package/dist/{price-list-prices-add-GJVI47OY.mjs → price-list-prices-add-SDU5YZAT.mjs} +6 -6
  68. package/dist/{price-list-prices-edit-E4Q5TQPM.mjs → price-list-prices-edit-5USQR4D4.mjs} +2 -2
  69. package/dist/{product-attributes-QD3BWV5V.mjs → product-attributes-EFIRUBRO.mjs} +2 -2
  70. package/dist/{product-create-E2GZYQX4.mjs → product-create-K6EWZHIT.mjs} +7 -7
  71. package/dist/{product-create-variant-KEBN5OR7.mjs → product-create-variant-ERKHTEJZ.mjs} +1 -1
  72. package/dist/{product-detail-QBGGKRZ2.mjs → product-detail-DKPZDEIY.mjs} +4 -4
  73. package/dist/{product-edit-YP4KOQ4T.mjs → product-edit-55YXTIGO.mjs} +2 -2
  74. package/dist/{product-export-WUZYHPS5.mjs → product-export-5AD7NELI.mjs} +3 -3
  75. package/dist/{product-image-variants-edit-Y363J5NG.mjs → product-image-variants-edit-M6QF2RLE.mjs} +4 -4
  76. package/dist/{product-list-DNTS7WUN.mjs → product-list-EUWZIFTM.mjs} +7 -7
  77. package/dist/{product-organization-H557PLLB.mjs → product-organization-N3VBRXF4.mjs} +2 -2
  78. package/dist/{product-prices-JOG6IIQ7.mjs → product-prices-4C36AG4R.mjs} +1 -1
  79. package/dist/{product-sales-channels-ANCFZZ5S.mjs → product-sales-channels-PPXUG4KT.mjs} +5 -5
  80. package/dist/{product-stock-NYUFMEVG.mjs → product-stock-VEGE6SUZ.mjs} +2 -2
  81. package/dist/{product-tag-detail-EHBB3WUB.mjs → product-tag-detail-I3MBZX7U.mjs} +10 -10
  82. package/dist/{product-tag-list-LSW5FFVN.mjs → product-tag-list-JUWSOMB7.mjs} +10 -10
  83. package/dist/{product-type-detail-3VB6AWUW.mjs → product-type-detail-RKHT5NBL.mjs} +4 -4
  84. package/dist/{product-type-list-FD3TGPNP.mjs → product-type-list-QQKAHBJ3.mjs} +6 -6
  85. package/dist/{product-variant-detail-43T33AQP.mjs → product-variant-detail-XAYG5CKE.mjs} +4 -4
  86. package/dist/{profile-detail-BMC7IZBY.mjs → profile-detail-FRZ74HAF.mjs} +1 -1
  87. package/dist/{profile-edit-YZCUGEXF.mjs → profile-edit-ZNXO6WME.mjs} +1 -1
  88. package/dist/{promotion-detail-VJB55PJK.mjs → promotion-detail-QC36KXB3.mjs} +3 -3
  89. package/dist/{promotion-list-TMWKPLMJ.mjs → promotion-list-L22GJE3P.mjs} +4 -4
  90. package/dist/{refund-reason-list-URYYYEK6.mjs → refund-reason-list-OJYYEYJE.mjs} +8 -8
  91. package/dist/{region-add-countries-7U4J5RW6.mjs → region-add-countries-2VAVXMJQ.mjs} +4 -4
  92. package/dist/{region-create-IUGX33M5.mjs → region-create-NA7Y2LN4.mjs} +4 -4
  93. package/dist/{region-detail-D3JBW34A.mjs → region-detail-3BARMXUE.mjs} +4 -4
  94. package/dist/{region-list-JAQXIBYD.mjs → region-list-V4R2REMH.mjs} +4 -4
  95. package/dist/{reservation-list-2DN3YHIJ.mjs → reservation-list-B47DXTA7.mjs} +5 -5
  96. package/dist/{return-reason-list-IFFIDA5O.mjs → return-reason-list-SCBGTOEI.mjs} +10 -10
  97. package/dist/{sales-channel-add-products-VH5T3GDA.mjs → sales-channel-add-products-F7YV4MO5.mjs} +4 -4
  98. package/dist/{sales-channel-detail-I2ZHVXMG.mjs → sales-channel-detail-MXIPZCGA.mjs} +4 -4
  99. package/dist/{sales-channel-list-3FV4S2NN.mjs → sales-channel-list-RLGL7FM3.mjs} +5 -5
  100. package/dist/{shipping-option-type-list-ZMZMXFME.mjs → shipping-option-type-list-DIOX7VG7.mjs} +5 -5
  101. package/dist/{shipping-profiles-list-H3CBZKRH.mjs → shipping-profiles-list-WRPIJBZZ.mjs} +4 -4
  102. package/dist/{store-add-currencies-ZFS3WZHG.mjs → store-add-currencies-OX2WXFMS.mjs} +4 -4
  103. package/dist/{store-add-locales-IZOZP5C6.mjs → store-add-locales-VJ4RJ7UI.mjs} +4 -4
  104. package/dist/{store-detail-4IBAEVSD.mjs → store-detail-JSNPOB2F.mjs} +4 -4
  105. package/dist/{tax-region-detail-O2T7BI3V.mjs → tax-region-detail-2AE2EFI3.mjs} +13 -13
  106. package/dist/{tax-region-province-detail-2W7RXAM5.mjs → tax-region-province-detail-4ERSEQFF.mjs} +13 -13
  107. package/dist/{tax-region-tax-override-create-7IM4ZVPH.mjs → tax-region-tax-override-create-PHCGEF7V.mjs} +11 -11
  108. package/dist/{tax-region-tax-override-edit-3ZT5IZYR.mjs → tax-region-tax-override-edit-SMRPSILC.mjs} +12 -12
  109. package/dist/{translation-list-IAKEB7MY.mjs → translation-list-S5Z6PG2R.mjs} +8 -5
  110. package/dist/translations-edit-HUNKY7CO.mjs +708 -0
  111. package/dist/{user-invite-XB635N26.mjs → user-invite-GAGIM5DO.mjs} +4 -4
  112. package/dist/{user-list-YYUOQKQY.mjs → user-list-YTZQNYSO.mjs} +4 -4
  113. package/dist/{workflow-execution-list-IZVF2XMJ.mjs → workflow-execution-list-C3EJMVSZ.mjs} +4 -4
  114. package/package.json +9 -9
  115. package/src/components/data-grid/components/data-grid-cell-container.tsx +16 -4
  116. package/src/components/data-grid/components/data-grid-readonly-cell.tsx +16 -3
  117. package/src/components/data-grid/components/data-grid-root.tsx +19 -4
  118. package/src/components/data-grid/components/data-grid-text-cell.tsx +79 -9
  119. package/src/i18n/languages.ts +7 -0
  120. package/src/i18n/translations/index.ts +4 -0
  121. package/src/i18n/translations/zhTW.json +3249 -0
  122. package/src/routes/translations/translation-list/translation-list.tsx +9 -8
  123. package/src/routes/translations/translations-edit/components/translations-edit-form/translations-edit-form.tsx +388 -90
  124. package/src/routes/translations/translations-edit/translations-edit.tsx +0 -1
  125. package/dist/translations-edit-QKLE4L5B.mjs +0 -458
@@ -0,0 +1,708 @@
1
+ import {
2
+ DataGrid,
3
+ createDataGridHelper
4
+ } from "./chunk-CCQD65EY.mjs";
5
+ import "./chunk-DK4WIVY6.mjs";
6
+ import "./chunk-IUCDCPJU.mjs";
7
+ import {
8
+ KeyboundForm
9
+ } from "./chunk-6HTZNHPT.mjs";
10
+ import "./chunk-WVA4O7QS.mjs";
11
+ import {
12
+ RouteFocusModal,
13
+ useRouteModal
14
+ } from "./chunk-D6UW7URG.mjs";
15
+ import {
16
+ useFeatureFlag
17
+ } from "./chunk-G4BWCU5P.mjs";
18
+ import "./chunk-LPEUYMRK.mjs";
19
+ import "./chunk-OC7BQLYI.mjs";
20
+ import "./chunk-S4DMV3ZT.mjs";
21
+ import "./chunk-OBQI23QM.mjs";
22
+ import {
23
+ useBatchTranslations,
24
+ useReferenceTranslations,
25
+ useTranslationSettings
26
+ } from "./chunk-GRZSG4EP.mjs";
27
+ import "./chunk-I4OBEAOJ.mjs";
28
+ import "./chunk-HI6URQ7H.mjs";
29
+ import "./chunk-6CLQKVAU.mjs";
30
+ import "./chunk-KI7TOXBR.mjs";
31
+ import "./chunk-UJ2TMPV4.mjs";
32
+ import "./chunk-23GTCEOV.mjs";
33
+ import "./chunk-WAXMT4IY.mjs";
34
+ import "./chunk-DYDGGABK.mjs";
35
+ import "./chunk-LGNTHZ5Y.mjs";
36
+ import "./chunk-CN7JXSGW.mjs";
37
+ import "./chunk-5BQQRHQS.mjs";
38
+ import "./chunk-HTCYX4VD.mjs";
39
+ import "./chunk-5AFMB7XQ.mjs";
40
+ import "./chunk-UWY5ZV66.mjs";
41
+ import "./chunk-A63RZVX6.mjs";
42
+ import "./chunk-HBXV7ENS.mjs";
43
+ import "./chunk-CDORR33H.mjs";
44
+ import "./chunk-4JQR6QNW.mjs";
45
+ import "./chunk-FYWHE3W5.mjs";
46
+ import "./chunk-OSHH5GAS.mjs";
47
+ import "./chunk-2XTBDCGE.mjs";
48
+ import "./chunk-3BF5SC66.mjs";
49
+ import {
50
+ useStore
51
+ } from "./chunk-VCSSQVPD.mjs";
52
+ import "./chunk-HP2JH45P.mjs";
53
+ import "./chunk-SQDIZZDW.mjs";
54
+ import "./chunk-Z6BFNHEO.mjs";
55
+ import "./chunk-HGRIOEAR.mjs";
56
+ import "./chunk-FXYH54JP.mjs";
57
+ import "./chunk-774WSTCC.mjs";
58
+ import "./chunk-NFEK63OE.mjs";
59
+ import "./chunk-QZ7TP4HQ.mjs";
60
+
61
+ // src/routes/translations/translations-edit/translations-edit.tsx
62
+ import { useNavigate, useSearchParams } from "react-router-dom";
63
+
64
+ // src/routes/translations/translations-edit/components/translations-edit-form/translations-edit-form.tsx
65
+ import { zodResolver } from "@hookform/resolvers/zod";
66
+ import { Button, Prompt, Select, toast } from "@medusajs/ui";
67
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
68
+ import { useForm } from "react-hook-form";
69
+ import { useTranslation } from "react-i18next";
70
+ import { z } from "zod";
71
+ import { jsx, jsxs } from "react/jsx-runtime";
72
+ var LocaleTranslationSchema = z.object({
73
+ id: z.string().nullish(),
74
+ locale_code: z.string(),
75
+ fields: z.record(z.string().optional())
76
+ });
77
+ var EntityTranslationsSchema = z.object({
78
+ locales: z.record(LocaleTranslationSchema)
79
+ });
80
+ var TranslationsFormSchema = z.object({
81
+ entities: z.record(EntityTranslationsSchema)
82
+ });
83
+ function isEntityRow(row) {
84
+ return row._type === "entity";
85
+ }
86
+ function initTranslationsFormState(translations, references, availableLocales, translatableFields) {
87
+ const existingMap = /* @__PURE__ */ new Map();
88
+ for (const t of translations) {
89
+ existingMap.set(`${t.reference_id}:${t.locale_code}`, t);
90
+ }
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);
97
+ const fields = {};
98
+ for (const fieldName of translatableFields) {
99
+ const fieldValue = existing?.translations?.[fieldName] ?? "";
100
+ fields[fieldName] = fieldValue;
101
+ }
102
+ locales[locale.locale_code] = {
103
+ id: existing?.id ?? null,
104
+ locale_code: locale.locale_code,
105
+ fields
106
+ };
107
+ }
108
+ entitiesTranslationState[reference.id] = { locales };
109
+ }
110
+ return {
111
+ entities: entitiesTranslationState
112
+ };
113
+ }
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
+ }));
124
+ }
125
+ function transformToBatchPayload(currentState, initialState, entityType) {
126
+ const payload = {
127
+ create: [],
128
+ update: [],
129
+ delete: []
130
+ };
131
+ 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) {
169
+ continue;
170
+ }
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(
194
+ (v) => v !== void 0 && v.trim() !== ""
195
+ );
196
+ const hadContent = initial && Object.values(initial.fields).some(
197
+ (v) => v !== void 0 && v.trim() !== ""
198
+ );
199
+ if (!localeTranslations.id && hasContent) {
200
+ payload.create.push({
201
+ reference_id: entityId,
202
+ reference: entityType,
203
+ locale_code: localeTranslations.locale_code,
204
+ translations: localeTranslations.fields
205
+ });
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);
216
+ }
217
+ }
218
+ return payload;
219
+ }
220
+ var columnHelper = createDataGridHelper();
221
+ var FIELD_COLUMN_WIDTH = 150;
222
+ function useTranslationsGridColumns({
223
+ entities,
224
+ translatableFields,
225
+ availableLocales,
226
+ selectedLocale,
227
+ dynamicColumnWidth
228
+ }) {
229
+ const { t } = useTranslation();
230
+ const columns = useMemo(() => {
231
+ const selectedLocaleData = availableLocales.find(
232
+ (l) => l.locale_code === selectedLocale
233
+ );
234
+ const baseColumns = [
235
+ columnHelper.column({
236
+ id: "field",
237
+ name: "field",
238
+ size: FIELD_COLUMN_WIDTH,
239
+ header: void 0,
240
+ cell: (context) => {
241
+ const row = context.row.original;
242
+ if (isEntityRow(row)) {
243
+ return /* @__PURE__ */ jsx(DataGrid.ReadonlyCell, { context });
244
+ }
245
+ return /* @__PURE__ */ jsx(DataGrid.ReadonlyCell, { context, color: "normal", children: /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center gap-x-2 overflow-hidden", children: /* @__PURE__ */ jsx("span", { className: "truncate", children: t(`fields.${row.field_name}`, {
246
+ defaultValue: row.field_name
247
+ }) }) }) });
248
+ },
249
+ disableHiding: true
250
+ }),
251
+ columnHelper.column({
252
+ id: "original",
253
+ name: "original",
254
+ size: dynamicColumnWidth,
255
+ header: t("general.original"),
256
+ disableHiding: true,
257
+ cell: (context) => {
258
+ const row = context.row.original;
259
+ if (isEntityRow(row)) {
260
+ return /* @__PURE__ */ jsx(DataGrid.ReadonlyCell, { context });
261
+ }
262
+ const entity = entities.find(
263
+ (entity2) => entity2.id === row.reference_id
264
+ );
265
+ if (!entity) {
266
+ return null;
267
+ }
268
+ return /* @__PURE__ */ jsx(DataGrid.ReadonlyCell, { context, isMultiLine: true, children: entity[row.field_name] });
269
+ }
270
+ })
271
+ ];
272
+ if (selectedLocaleData) {
273
+ baseColumns.push(
274
+ columnHelper.column({
275
+ id: selectedLocaleData.locale_code,
276
+ name: selectedLocaleData.locale.name,
277
+ size: dynamicColumnWidth,
278
+ header: () => selectedLocaleData.locale.name,
279
+ cell: (context) => {
280
+ const row = context.row.original;
281
+ if (isEntityRow(row)) {
282
+ return /* @__PURE__ */ jsx(DataGrid.ReadonlyCell, { context, isMultiLine: true });
283
+ }
284
+ return /* @__PURE__ */ jsx(DataGrid.TextCell, { context, isMultiLine: true });
285
+ },
286
+ field: (context) => {
287
+ const row = context.row.original;
288
+ if (isEntityRow(row)) {
289
+ return null;
290
+ }
291
+ return `entities.${row.reference_id}.locales.${selectedLocaleData.locale_code}.fields.${row.field_name}`;
292
+ },
293
+ type: "text"
294
+ })
295
+ );
296
+ }
297
+ return baseColumns;
298
+ }, [
299
+ t,
300
+ translatableFields,
301
+ availableLocales,
302
+ selectedLocale,
303
+ entities,
304
+ dynamicColumnWidth
305
+ ]);
306
+ return columns;
307
+ }
308
+ var TranslationsEditForm = ({
309
+ translations,
310
+ references,
311
+ entityType,
312
+ availableLocales,
313
+ translatableFields,
314
+ fetchNextPage,
315
+ hasNextPage,
316
+ isFetchingNextPage,
317
+ referenceCount
318
+ }) => {
319
+ const { t } = useTranslation();
320
+ const { handleSuccess, setCloseOnEscape } = useRouteModal();
321
+ const containerRef = useRef(null);
322
+ const [dynamicColumnWidth, setDynamicColumnWidth] = useState(400);
323
+ useEffect(() => {
324
+ const calculateColumnWidth = () => {
325
+ if (containerRef.current) {
326
+ const containerWidth = containerRef.current.offsetWidth;
327
+ const availableWidth = containerWidth - FIELD_COLUMN_WIDTH - 12;
328
+ const columnWidth = Math.max(300, Math.floor(availableWidth / 2));
329
+ setDynamicColumnWidth(columnWidth);
330
+ }
331
+ };
332
+ calculateColumnWidth();
333
+ const resizeObserver = new ResizeObserver(calculateColumnWidth);
334
+ if (containerRef.current) {
335
+ resizeObserver.observe(containerRef.current);
336
+ }
337
+ return () => resizeObserver.disconnect();
338
+ }, []);
339
+ const [selectedLocale, setSelectedLocale] = useState(
340
+ availableLocales[0]?.locale_code ?? ""
341
+ );
342
+ const [showUnsavedPrompt, setShowUnsavedPrompt] = useState(false);
343
+ const [pendingLocale, setPendingLocale] = useState(null);
344
+ const entities = useMemo(() => references, [references]);
345
+ const totalCount = useMemo(
346
+ () => referenceCount * (translatableFields.length + 1),
347
+ [referenceCount, translatableFields]
348
+ );
349
+ const initialState = useRef(
350
+ initTranslationsFormState(
351
+ translations,
352
+ entities,
353
+ availableLocales,
354
+ translatableFields
355
+ )
356
+ );
357
+ const form = useForm({
358
+ resolver: zodResolver(TranslationsFormSchema),
359
+ defaultValues: initialState.current
360
+ });
361
+ const rows = useMemo(
362
+ () => buildTranslationRows(entities, translatableFields),
363
+ [entities, translatableFields]
364
+ );
365
+ const { mutateAsync, isPending } = useBatchTranslations(entityType);
366
+ const handleLocaleChange = useCallback(
367
+ (newLocale) => {
368
+ if (newLocale === selectedLocale) {
369
+ return;
370
+ }
371
+ const currentValues = form.getValues();
372
+ const hasChanges = hasLocaleChanges(
373
+ currentValues,
374
+ initialState.current,
375
+ selectedLocale
376
+ );
377
+ if (hasChanges) {
378
+ setPendingLocale(newLocale);
379
+ setShowUnsavedPrompt(true);
380
+ } else {
381
+ setSelectedLocale(newLocale);
382
+ }
383
+ },
384
+ [selectedLocale, form]
385
+ );
386
+ const saveCurrentLocale = useCallback(async () => {
387
+ const currentValues = form.getValues();
388
+ const payload = transformSingleLocaleToBatchPayload(
389
+ currentValues,
390
+ initialState.current,
391
+ entityType,
392
+ selectedLocale
393
+ );
394
+ if (payload.create.length === 0 && payload.update.length === 0 && payload.delete.length === 0) {
395
+ return true;
396
+ }
397
+ try {
398
+ const BATCH_SIZE = 150;
399
+ const totalItems = payload.create.length + payload.update.length + payload.delete.length;
400
+ const batchCount = Math.ceil(totalItems / BATCH_SIZE);
401
+ for (let i = 0; i < batchCount; i++) {
402
+ let currentBatchAvailable = BATCH_SIZE;
403
+ const currentBatch = {
404
+ create: [],
405
+ update: [],
406
+ delete: []
407
+ };
408
+ if (payload.create.length > 0) {
409
+ currentBatch.create = payload.create.splice(0, currentBatchAvailable);
410
+ currentBatchAvailable -= currentBatch.create.length;
411
+ }
412
+ if (payload.update.length > 0) {
413
+ currentBatch.update = payload.update.splice(0, currentBatchAvailable);
414
+ currentBatchAvailable -= currentBatch.update.length;
415
+ }
416
+ if (payload.delete.length > 0) {
417
+ currentBatch.delete = payload.delete.splice(0, currentBatchAvailable);
418
+ currentBatchAvailable -= currentBatch.delete.length;
419
+ }
420
+ await mutateAsync(currentBatch);
421
+ }
422
+ const updatedInitialState = { ...initialState.current };
423
+ for (const entityId of Object.keys(currentValues.entities)) {
424
+ if (updatedInitialState.entities[entityId]?.locales[selectedLocale]) {
425
+ updatedInitialState.entities[entityId].locales[selectedLocale] = {
426
+ ...currentValues.entities[entityId].locales[selectedLocale]
427
+ };
428
+ }
429
+ }
430
+ initialState.current = updatedInitialState;
431
+ form.reset(currentValues);
432
+ return true;
433
+ } catch (error) {
434
+ toast.error(
435
+ error instanceof Error ? error.message : "Failed to save translations"
436
+ );
437
+ return false;
438
+ }
439
+ }, [form, entityType, selectedLocale, mutateAsync]);
440
+ const handleSaveAndSwitch = useCallback(async () => {
441
+ const success = await saveCurrentLocale();
442
+ if (success && pendingLocale) {
443
+ toast.success(
444
+ t("translations.edit.localeChangesSaved", {
445
+ defaultValue: "Changes saved successfully"
446
+ })
447
+ );
448
+ setSelectedLocale(pendingLocale);
449
+ }
450
+ setShowUnsavedPrompt(false);
451
+ setPendingLocale(null);
452
+ }, [saveCurrentLocale, pendingLocale, t]);
453
+ const handleCancelSwitch = useCallback(() => {
454
+ setShowUnsavedPrompt(false);
455
+ setPendingLocale(null);
456
+ }, []);
457
+ const handleSave = useCallback(
458
+ async (closeOnSuccess = false) => {
459
+ const success = await saveCurrentLocale();
460
+ if (success) {
461
+ toast.success(
462
+ t("translations.edit.successToast", {
463
+ defaultValue: "Translations updated successfully"
464
+ })
465
+ );
466
+ if (closeOnSuccess) {
467
+ handleSuccess();
468
+ }
469
+ }
470
+ },
471
+ [saveCurrentLocale, t, handleSuccess]
472
+ );
473
+ const handleSubmit = form.handleSubmit(async (values) => {
474
+ const payload = transformToBatchPayload(
475
+ values,
476
+ initialState.current,
477
+ entityType
478
+ );
479
+ if (payload.create.length === 0 && payload.update.length === 0 && payload.delete.length === 0) {
480
+ toast.info(
481
+ t("translations.noChanges", { defaultValue: "No changes to save" })
482
+ );
483
+ return;
484
+ }
485
+ const BATCH_SIZE = 150;
486
+ const totalItems = payload.create.length + payload.update.length + payload.delete.length;
487
+ const batchCount = Math.ceil(totalItems / BATCH_SIZE);
488
+ for (let i = 0; i < batchCount; i++) {
489
+ let currentBatchAvailable = BATCH_SIZE;
490
+ const currentBatch = {
491
+ create: [],
492
+ update: [],
493
+ delete: []
494
+ };
495
+ if (payload.create.length > 0) {
496
+ currentBatch.create = payload.create.splice(0, currentBatchAvailable);
497
+ currentBatchAvailable -= currentBatch.create.length;
498
+ }
499
+ if (payload.update.length > 0) {
500
+ currentBatch.update = payload.update.splice(0, currentBatchAvailable);
501
+ currentBatchAvailable -= currentBatch.update.length;
502
+ }
503
+ if (payload.delete.length > 0) {
504
+ currentBatch.delete = payload.delete.splice(0, currentBatchAvailable);
505
+ currentBatchAvailable -= currentBatch.delete.length;
506
+ }
507
+ await mutateAsync(currentBatch, {
508
+ onSuccess: () => {
509
+ if (i === batchCount - 1) {
510
+ toast.success(
511
+ t("translations.edit.successToast", {
512
+ defaultValue: "Translations updated successfully"
513
+ })
514
+ );
515
+ handleSuccess();
516
+ }
517
+ },
518
+ onError: (error) => {
519
+ toast.error(error.message);
520
+ }
521
+ });
522
+ }
523
+ });
524
+ const columns = useTranslationsGridColumns({
525
+ entities,
526
+ translatableFields,
527
+ availableLocales,
528
+ selectedLocale,
529
+ dynamicColumnWidth
530
+ });
531
+ const selectedLocaleDisplay = availableLocales.find(
532
+ (l) => l.locale_code === selectedLocale
533
+ )?.locale.name;
534
+ return /* @__PURE__ */ jsxs(RouteFocusModal.Form, { form, children: [
535
+ /* @__PURE__ */ jsxs(
536
+ KeyboundForm,
537
+ {
538
+ onSubmit: handleSubmit,
539
+ className: "flex h-full flex-col overflow-hidden",
540
+ children: [
541
+ /* @__PURE__ */ jsx(RouteFocusModal.Header, { children: /* @__PURE__ */ jsx("div", { className: "-my-2 flex w-full items-center justify-between border-l px-4", children: /* @__PURE__ */ jsxs(
542
+ Select,
543
+ {
544
+ value: selectedLocale,
545
+ onValueChange: handleLocaleChange,
546
+ size: "small",
547
+ children: [
548
+ /* @__PURE__ */ jsx(Select.Trigger, { className: "bg-ui-bg-base w-[200px]", children: /* @__PURE__ */ jsx(Select.Value, { children: selectedLocaleDisplay }) }),
549
+ /* @__PURE__ */ jsx(Select.Content, { children: availableLocales.map((locale) => /* @__PURE__ */ jsx(
550
+ Select.Item,
551
+ {
552
+ value: locale.locale_code,
553
+ children: locale.locale.name
554
+ },
555
+ locale.locale_code
556
+ )) })
557
+ ]
558
+ }
559
+ ) }) }),
560
+ /* @__PURE__ */ jsx(RouteFocusModal.Body, { className: "size-full overflow-hidden", children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "size-full", children: /* @__PURE__ */ jsx(
561
+ DataGrid,
562
+ {
563
+ columns,
564
+ data: rows,
565
+ getSubRows: (row) => {
566
+ if (isEntityRow(row)) {
567
+ return row.subRows;
568
+ }
569
+ },
570
+ state: form,
571
+ onEditingChange: (editing) => setCloseOnEscape(!editing),
572
+ totalRowCount: totalCount,
573
+ onFetchMore: fetchNextPage,
574
+ isFetchingMore: isFetchingNextPage,
575
+ hasNextPage
576
+ }
577
+ ) }) }),
578
+ /* @__PURE__ */ jsx(RouteFocusModal.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
579
+ /* @__PURE__ */ jsx(RouteFocusModal.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", children: t("actions.cancel") }) }),
580
+ /* @__PURE__ */ jsx(
581
+ Button,
582
+ {
583
+ size: "small",
584
+ type: "button",
585
+ variant: "secondary",
586
+ onClick: () => handleSave(false),
587
+ isLoading: isPending,
588
+ children: t("actions.saveChanges", { defaultValue: "Save changes" })
589
+ }
590
+ ),
591
+ /* @__PURE__ */ jsx(
592
+ Button,
593
+ {
594
+ size: "small",
595
+ type: "button",
596
+ onClick: () => handleSave(true),
597
+ isLoading: isPending,
598
+ children: t("actions.saveAndClose", { defaultValue: "Save and close" })
599
+ }
600
+ )
601
+ ] }) })
602
+ ]
603
+ }
604
+ ),
605
+ /* @__PURE__ */ jsx(Prompt, { open: showUnsavedPrompt, variant: "confirmation", children: /* @__PURE__ */ jsxs(Prompt.Content, { children: [
606
+ /* @__PURE__ */ jsxs(Prompt.Header, { children: [
607
+ /* @__PURE__ */ jsx(Prompt.Title, { children: t("translations.unsavedChanges.title", {
608
+ defaultValue: "Unsaved changes"
609
+ }) }),
610
+ /* @__PURE__ */ jsx(Prompt.Description, { children: t("translations.unsavedChanges.description", {
611
+ defaultValue: "You have unsaved changes for this locale. Save them before switching?"
612
+ }) })
613
+ ] }),
614
+ /* @__PURE__ */ jsxs(Prompt.Footer, { children: [
615
+ /* @__PURE__ */ jsx(
616
+ Button,
617
+ {
618
+ size: "small",
619
+ variant: "secondary",
620
+ onClick: handleCancelSwitch,
621
+ type: "button",
622
+ children: t("actions.close")
623
+ }
624
+ ),
625
+ /* @__PURE__ */ jsx(
626
+ Button,
627
+ {
628
+ size: "small",
629
+ onClick: handleSaveAndSwitch,
630
+ type: "button",
631
+ isLoading: isPending,
632
+ children: t("actions.saveChanges", { defaultValue: "Save changes" })
633
+ }
634
+ )
635
+ ] })
636
+ ] }) })
637
+ ] });
638
+ };
639
+
640
+ // src/routes/translations/translations-edit/translations-edit.tsx
641
+ import { useEffect as useEffect2 } from "react";
642
+ import { keepPreviousData } from "@tanstack/react-query";
643
+ import { jsx as jsx2 } from "react/jsx-runtime";
644
+ var TranslationsEdit = () => {
645
+ const isTranslationsEnabled = useFeatureFlag("translation");
646
+ const navigate = useNavigate();
647
+ const [searchParams] = useSearchParams();
648
+ const reference = searchParams.get("reference");
649
+ const referenceIdParam = searchParams.getAll("reference_id");
650
+ useEffect2(() => {
651
+ if (!reference || !isTranslationsEnabled) {
652
+ navigate(-1);
653
+ return;
654
+ }
655
+ }, [reference, navigate, isTranslationsEnabled]);
656
+ const {
657
+ translatable_fields,
658
+ isPending: isTranslationSettingsPending,
659
+ isError: isTranslationSettingsError,
660
+ error: translationSettingsError
661
+ } = useTranslationSettings({ entity_type: reference });
662
+ const {
663
+ translations,
664
+ references,
665
+ fetchNextPage,
666
+ count,
667
+ isFetchingNextPage,
668
+ hasNextPage,
669
+ isPending,
670
+ isError,
671
+ error
672
+ } = useReferenceTranslations(
673
+ reference,
674
+ translatable_fields?.[reference] ?? [],
675
+ referenceIdParam,
676
+ {
677
+ enabled: !!translatable_fields && !!reference,
678
+ placeholderData: keepPreviousData
679
+ }
680
+ );
681
+ const {
682
+ store,
683
+ isPending: isStorePending,
684
+ isError: isStoreError,
685
+ error: storeError
686
+ } = useStore();
687
+ const ready = !isPending && !!translations && !!translatable_fields && !isTranslationSettingsPending && !!references && !isStorePending && !!store;
688
+ if (isError || isStoreError || isTranslationSettingsError) {
689
+ throw error || storeError || translationSettingsError;
690
+ }
691
+ return /* @__PURE__ */ jsx2(RouteFocusModal, { prev: referenceIdParam.length ? -1 : "..", children: ready && /* @__PURE__ */ jsx2(
692
+ TranslationsEditForm,
693
+ {
694
+ translations,
695
+ references,
696
+ entityType: reference,
697
+ availableLocales: store?.supported_locales ?? [],
698
+ translatableFields: translatable_fields[reference],
699
+ fetchNextPage,
700
+ hasNextPage,
701
+ isFetchingNextPage,
702
+ referenceCount: count
703
+ }
704
+ ) });
705
+ };
706
+ export {
707
+ TranslationsEdit as Component
708
+ };