@medusajs/dashboard 2.12.3-snapshot-20251216185234 → 2.12.3-snapshot-20251217081413

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 (117) hide show
  1. package/dist/{api-key-management-detail-6RCDH73M.mjs → api-key-management-detail-FRUN2KFK.mjs} +1 -1
  2. package/dist/app.css +9 -0
  3. package/dist/app.js +330 -324
  4. package/dist/app.mjs +2 -2
  5. package/dist/{campaign-detail-5Q4BYCPX.mjs → campaign-detail-HM3GQJLQ.mjs} +1 -1
  6. package/dist/{categories-metadata-J7M3XWI7.mjs → categories-metadata-WKL3MGD7.mjs} +1 -1
  7. package/dist/{category-detail-S5IPXMHX.mjs → category-detail-UTWWDKFP.mjs} +2 -2
  8. package/dist/{category-products-KPW6BA5J.mjs → category-products-XXBTCXFF.mjs} +2 -2
  9. package/dist/{chunk-OL24RDYM.mjs → chunk-5F427YCP.mjs} +2 -2
  10. package/dist/{chunk-MJDHVDOW.mjs → chunk-5ISRTMYH.mjs} +1 -1
  11. package/dist/{chunk-ST4P6BQN.mjs → chunk-DQUXK4WW.mjs} +1 -1
  12. package/dist/{chunk-YYOPBKME.mjs → chunk-DTCIBQO2.mjs} +1 -1
  13. package/dist/{chunk-OL6MEUKW.mjs → chunk-FJR3D6EM.mjs} +102 -102
  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/{chunk-DBXWB3RF.mjs → chunk-KSDXSKJ7.mjs} +1 -1
  18. package/dist/{chunk-PHLCT2HA.mjs → chunk-OK6NZN2A.mjs} +1 -1
  19. package/dist/{chunk-ZMG5B4FG.mjs → chunk-SG2JZPTG.mjs} +1 -1
  20. package/dist/{chunk-CVHJAKLQ.mjs → chunk-UMCJYHAD.mjs} +1 -1
  21. package/dist/{collection-add-products-FU2BS3D3.mjs → collection-add-products-42F7H77E.mjs} +2 -2
  22. package/dist/{collection-detail-VJE7XHLV.mjs → collection-detail-PXIS3G64.mjs} +2 -2
  23. package/dist/{collection-list-IGA6SCNF.mjs → collection-list-O74CGY24.mjs} +2 -2
  24. package/dist/{collection-metadata-QK7MI3D2.mjs → collection-metadata-U6FMA4IC.mjs} +1 -1
  25. package/dist/{customer-detail-MOV2T3LF.mjs → customer-detail-OMTFJ6CE.mjs} +1 -1
  26. package/dist/{customer-group-detail-6T7OXGQD.mjs → customer-group-detail-ADK3M5LG.mjs} +1 -1
  27. package/dist/{customer-group-list-AJEAF5D2.mjs → customer-group-list-7ZRQ2HWU.mjs} +1 -1
  28. package/dist/{customers-add-customer-group-QVTVSQYM.mjs → customers-add-customer-group-5U27WHJB.mjs} +1 -1
  29. package/dist/{edit-rules-SMVRTCUP.mjs → edit-rules-BM2ERGVJ.mjs} +1 -1
  30. package/dist/{inventory-create-BK52VALF.mjs → inventory-create-7MA7B5N2.mjs} +2 -2
  31. package/dist/{inventory-detail-ZPSEMYI2.mjs → inventory-detail-B4PRHZK3.mjs} +1 -1
  32. package/dist/{inventory-metadata-FNEJ3RAT.mjs → inventory-metadata-C7MJ3GY5.mjs} +1 -1
  33. package/dist/{inventory-stock-6WYWLWJ7.mjs → inventory-stock-WVTYPJTX.mjs} +3 -3
  34. package/dist/{location-detail-N3GUZSY7.mjs → location-detail-KO6EBDK5.mjs} +1 -1
  35. package/dist/{location-fulfillment-providers-7ZUJAGNY.mjs → location-fulfillment-providers-IORBE3E3.mjs} +2 -2
  36. package/dist/{location-service-zone-shipping-option-create-CNRWYZQC.mjs → location-service-zone-shipping-option-create-2R3ZFLVK.mjs} +3 -3
  37. package/dist/{location-service-zone-shipping-option-pricing-OGWI7VPT.mjs → location-service-zone-shipping-option-pricing-5HN2Z5RB.mjs} +2 -2
  38. package/dist/{login-VNOLI5YG.mjs → login-XKB6OR7I.mjs} +1 -1
  39. package/dist/{order-create-claim-SCDJGM46.mjs → order-create-claim-NKCOGF4A.mjs} +1 -1
  40. package/dist/{order-create-edit-2WALBPXS.mjs → order-create-edit-UNQYXGLL.mjs} +1 -1
  41. package/dist/{order-create-exchange-LQU4YN7F.mjs → order-create-exchange-WI7OA2WO.mjs} +1 -1
  42. package/dist/{order-create-fulfillment-OWUVTZXW.mjs → order-create-fulfillment-2LJTEWDY.mjs} +1 -1
  43. package/dist/{order-create-refund-Q6HQY42R.mjs → order-create-refund-7K6UJXGP.mjs} +1 -1
  44. package/dist/{order-create-shipment-WAGGEPRW.mjs → order-create-shipment-ZTDLLUBY.mjs} +1 -1
  45. package/dist/{order-detail-PVPGEWGY.mjs → order-detail-JTRUMRLO.mjs} +1 -1
  46. package/dist/{order-edit-billing-address-UM76J4KX.mjs → order-edit-billing-address-YHYNVLOE.mjs} +1 -1
  47. package/dist/{order-edit-email-CL3KNOCM.mjs → order-edit-email-TCQPEVZY.mjs} +1 -1
  48. package/dist/{order-edit-shipping-address-PIESTGVL.mjs → order-edit-shipping-address-CFSYQLKD.mjs} +1 -1
  49. package/dist/{order-export-LE363ZLB.mjs → order-export-G4SBNEJ7.mjs} +1 -1
  50. package/dist/{order-metadata-FHBB7MTG.mjs → order-metadata-KGPB37VL.mjs} +1 -1
  51. package/dist/{order-receive-return-PRVKP6J2.mjs → order-receive-return-JER24SEV.mjs} +1 -1
  52. package/dist/{order-request-transfer-XSAGRUMT.mjs → order-request-transfer-3FBUYZNT.mjs} +1 -1
  53. package/dist/{price-list-create-K5JEZT57.mjs → price-list-create-CXZCFFTP.mjs} +4 -4
  54. package/dist/{price-list-detail-Q5VG5VGW.mjs → price-list-detail-XOMU6U5J.mjs} +2 -2
  55. package/dist/{price-list-prices-add-2MQ226U4.mjs → price-list-prices-add-SDX5CQME.mjs} +4 -4
  56. package/dist/{price-list-prices-edit-OJZLV7OS.mjs → price-list-prices-edit-EKB6NI5D.mjs} +2 -2
  57. package/dist/{product-attributes-YF4TZOIO.mjs → product-attributes-S535VXSC.mjs} +2 -2
  58. package/dist/{product-create-KJML2332.mjs → product-create-DKBLOS3M.mjs} +3 -3
  59. package/dist/{product-create-variant-5EBCLM54.mjs → product-create-variant-OTJKT6WI.mjs} +2 -2
  60. package/dist/{product-detail-QG72542C.mjs → product-detail-D2PJ64Q3.mjs} +2 -2
  61. package/dist/{product-edit-DZZR775Q.mjs → product-edit-K6CAQD5H.mjs} +2 -2
  62. package/dist/{product-export-5AD7NELI.mjs → product-export-57UUAGXF.mjs} +2 -2
  63. package/dist/{product-image-variants-edit-M6QF2RLE.mjs → product-image-variants-edit-2BW5BJON.mjs} +1 -1
  64. package/dist/{product-import-V3KQN4TV.mjs → product-import-6EM4VUXP.mjs} +1 -1
  65. package/dist/{product-list-EUWZIFTM.mjs → product-list-5V5GEH5K.mjs} +2 -2
  66. package/dist/{product-metadata-GL2MVPDI.mjs → product-metadata-JZLHBLZQ.mjs} +1 -1
  67. package/dist/{product-organization-O7RHELMQ.mjs → product-organization-IQFN54D2.mjs} +2 -2
  68. package/dist/{product-prices-YWV6MSM6.mjs → product-prices-5ZL2RP7A.mjs} +1 -1
  69. package/dist/{product-stock-AKEFMK5O.mjs → product-stock-SJJABF6I.mjs} +3 -3
  70. package/dist/{product-tag-create-PQMDDKWH.mjs → product-tag-create-XXO4AQEC.mjs} +1 -1
  71. package/dist/{product-tag-detail-I3MBZX7U.mjs → product-tag-detail-BSK64HXL.mjs} +3 -3
  72. package/dist/{product-tag-edit-K3BBQLJR.mjs → product-tag-edit-ENCGDT7E.mjs} +1 -1
  73. package/dist/{product-tag-list-JUWSOMB7.mjs → product-tag-list-SLQGCNDZ.mjs} +3 -3
  74. package/dist/{product-tag-metadata-MJH5LH7E.mjs → product-tag-metadata-EPXHMU2K.mjs} +1 -1
  75. package/dist/{product-type-detail-RKHT5NBL.mjs → product-type-detail-4CRRU7YK.mjs} +2 -2
  76. package/dist/{product-type-metadata-CDJDFFGQ.mjs → product-type-metadata-73OKOGPP.mjs} +1 -1
  77. package/dist/{product-variant-detail-XAYG5CKE.mjs → product-variant-detail-RPHLG4HU.mjs} +1 -1
  78. package/dist/{product-variant-edit-DEZEY2H2.mjs → product-variant-edit-JF7NN64Y.mjs} +1 -1
  79. package/dist/{product-variant-metadata-VTZDNWUT.mjs → product-variant-metadata-HU2CXGPO.mjs} +1 -1
  80. package/dist/{promotion-create-HWFNUQXG.mjs → promotion-create-BHA3FQG2.mjs} +1 -1
  81. package/dist/{promotion-detail-QC36KXB3.mjs → promotion-detail-F3QSR52W.mjs} +1 -1
  82. package/dist/{refund-reason-create-YHCDEHGQ.mjs → refund-reason-create-ZA5TKW2Z.mjs} +1 -1
  83. package/dist/{refund-reason-edit-CZ5QZ2SZ.mjs → refund-reason-edit-N2CRCLKZ.mjs} +1 -1
  84. package/dist/{refund-reason-list-OJYYEYJE.mjs → refund-reason-list-SE4TMGMT.mjs} +1 -1
  85. package/dist/{region-metadata-H6XXUQ4S.mjs → region-metadata-O5NZBWXP.mjs} +1 -1
  86. package/dist/{reservation-detail-LZAQL4XA.mjs → reservation-detail-UFK6XIXE.mjs} +1 -1
  87. package/dist/{reservation-metadata-5HZSDDOK.mjs → reservation-metadata-AEJEKGLV.mjs} +1 -1
  88. package/dist/{sales-channel-add-products-F7YV4MO5.mjs → sales-channel-add-products-2LMB7EF5.mjs} +2 -2
  89. package/dist/{sales-channel-detail-MXIPZCGA.mjs → sales-channel-detail-EUQ4STQI.mjs} +2 -2
  90. package/dist/{sales-channel-list-RLGL7FM3.mjs → sales-channel-list-JXKGHX4G.mjs} +1 -1
  91. package/dist/{sales-channel-metadata-M364R4RJ.mjs → sales-channel-metadata-AJMQ5SQ2.mjs} +1 -1
  92. package/dist/{shipping-option-type-create-C5WUWON7.mjs → shipping-option-type-create-YVVIA2XC.mjs} +1 -1
  93. package/dist/{shipping-option-type-detail-PENS2K73.mjs → shipping-option-type-detail-ZZW36XLK.mjs} +2 -2
  94. package/dist/{shipping-option-type-edit-CIU5EHRP.mjs → shipping-option-type-edit-O6F74T3A.mjs} +1 -1
  95. package/dist/{shipping-option-type-list-DIOX7VG7.mjs → shipping-option-type-list-SPTE7MT6.mjs} +2 -2
  96. package/dist/{shipping-profile-metadata-75G2NNMA.mjs → shipping-profile-metadata-7WFE55VG.mjs} +1 -1
  97. package/dist/{store-add-locales-VJ4RJ7UI.mjs → store-add-locales-I4GX3KAY.mjs} +1 -1
  98. package/dist/{store-detail-JSNPOB2F.mjs → store-detail-YLJLBBZE.mjs} +1 -1
  99. package/dist/{store-metadata-CYXTVJUE.mjs → store-metadata-BZ57I2E6.mjs} +1 -1
  100. package/dist/{tax-region-create-DWGL4EUT.mjs → tax-region-create-FGTV7VJL.mjs} +1 -1
  101. package/dist/{tax-region-detail-2AE2EFI3.mjs → tax-region-detail-PPIMD7OX.mjs} +5 -5
  102. package/dist/{tax-region-edit-EEVEEU2Q.mjs → tax-region-edit-ELZKA7YH.mjs} +1 -1
  103. package/dist/{tax-region-province-detail-4ERSEQFF.mjs → tax-region-province-detail-FV2NDT3E.mjs} +5 -5
  104. package/dist/{tax-region-tax-override-create-PHCGEF7V.mjs → tax-region-tax-override-create-N572MQPZ.mjs} +3 -3
  105. package/dist/{tax-region-tax-override-edit-SMRPSILC.mjs → tax-region-tax-override-edit-5DCSJW6D.mjs} +4 -4
  106. package/dist/{translation-list-UF7FLXOW.mjs → translation-list-KSM4QA3K.mjs} +63 -50
  107. package/dist/{translations-edit-USQJNMAY.mjs → translations-edit-VRXZI5KW.mjs} +224 -253
  108. package/dist/{user-metadata-2WPJOEJA.mjs → user-metadata-GRJZZ524.mjs} +1 -1
  109. package/dist/{workflow-execution-detail-H2AKEZJX.mjs → workflow-execution-detail-HXTFWGKG.mjs} +1 -1
  110. package/package.json +9 -9
  111. package/src/components/data-grid/hooks/use-data-grid-cell.tsx +1 -0
  112. package/src/components/data-grid/hooks/use-data-grid-form-handlers.tsx +1 -0
  113. package/src/components/data-grid/hooks/use-data-grid-keydown-event.tsx +22 -4
  114. package/src/hooks/api/translations.tsx +26 -17
  115. package/src/routes/translations/translation-list/components/active-locales-section/active-locales-section.tsx +1 -1
  116. package/src/routes/translations/translation-list/components/translations-completion-section/translations-completion-section.tsx +66 -46
  117. package/src/routes/translations/translations-edit/components/translations-edit-form/translations-edit-form.tsx +285 -330
package/dist/app.js CHANGED
@@ -96929,7 +96929,7 @@ var init_tax_rates = __esm({
96929
96929
  });
96930
96930
 
96931
96931
  // src/hooks/api/translations.tsx
96932
- var import_react_query43, TRANSLATIONS_QUERY_KEY, translationsQueryKeys, TRANSLATION_SETTINGS_QUERY_KEY, translationSettingsQueryKeys, TRANSLATION_STATISTICS_QUERY_KEY, translationStatisticsQueryKeys, useReferenceTranslations, useBatchTranslations, useTranslationSettings, useTranslationStatistics;
96932
+ var import_react_query43, TRANSLATIONS_QUERY_KEY, translationsQueryKeys, TRANSLATION_SETTINGS_QUERY_KEY, translationSettingsQueryKeys, TRANSLATION_STATISTICS_QUERY_KEY, translationStatisticsQueryKeys, useReferenceTranslations, referenceInvalidationKeysMap, useBatchTranslations, useTranslationSettings, useTranslationStatistics;
96933
96933
  var init_translations2 = __esm({
96934
96934
  "src/hooks/api/translations.tsx"() {
96935
96935
  "use strict";
@@ -97085,28 +97085,33 @@ var init_translations2 = __esm({
97085
97085
  const { data, ...rest } = referenceHook();
97086
97086
  return { ...data, ...rest };
97087
97087
  };
97088
+ referenceInvalidationKeysMap = /* @__PURE__ */ new Map([
97089
+ ["product", productsQueryKeys.lists()],
97090
+ ["product_variant", productVariantQueryKeys.lists()],
97091
+ ["product_category", categoriesQueryKeys.lists()],
97092
+ ["product_collection", collectionsQueryKeys.lists()],
97093
+ ["product_type", productTypesQueryKeys.lists()],
97094
+ ["product_tag", productTagsQueryKeys.lists()]
97095
+ ]);
97088
97096
  useBatchTranslations = (reference, options) => {
97089
- const referenceInvalidationKeysMap = /* @__PURE__ */ new Map([
97090
- ["product", productsQueryKeys.lists()],
97091
- ["product_variant", productVariantQueryKeys.lists()],
97092
- ["product_category", categoriesQueryKeys.lists()],
97093
- ["product_collection", collectionsQueryKeys.lists()],
97094
- ["product_type", productTypesQueryKeys.lists()],
97095
- ["product_tag", productTagsQueryKeys.lists()]
97096
- ]);
97097
- return (0, import_react_query43.useMutation)({
97097
+ const mutation = (0, import_react_query43.useMutation)({
97098
97098
  mutationFn: (payload) => sdk.admin.translation.batch(payload),
97099
- onSuccess: (data, variables, context) => {
97099
+ ...options
97100
+ });
97101
+ const invalidateQueries = async () => {
97102
+ await Promise.all([
97100
97103
  queryClient.invalidateQueries({
97101
97104
  queryKey: referenceInvalidationKeysMap.get(reference)
97102
- });
97105
+ }),
97103
97106
  queryClient.invalidateQueries({
97104
97107
  queryKey: translationStatisticsQueryKeys.lists()
97105
- });
97106
- options?.onSuccess?.(data, variables, context);
97107
- },
97108
- ...options
97109
- });
97108
+ })
97109
+ ]);
97110
+ };
97111
+ return {
97112
+ ...mutation,
97113
+ invalidateQueries
97114
+ };
97110
97115
  };
97111
97116
  useTranslationSettings = (query, options) => {
97112
97117
  const { data, ...rest } = (0, import_react_query43.useQuery)({
@@ -114120,6 +114125,7 @@ var init_use_data_grid_cell = __esm({
114120
114125
  case "number":
114121
114126
  return numberCharacterRegex.test(key);
114122
114127
  case "text":
114128
+ case "multiline-text":
114123
114129
  return textCharacterRegex.test(key);
114124
114130
  default:
114125
114131
  return false;
@@ -115270,6 +115276,7 @@ function convertArrayToPrimitive(values, type) {
115270
115276
  case "boolean":
115271
115277
  return values.map(convertToBoolean);
115272
115278
  case "text":
115279
+ case "multiline-text":
115273
115280
  return values.map(covertToString);
115274
115281
  default:
115275
115282
  throw new Error(`Unsupported target type "${type}".`);
@@ -115470,7 +115477,19 @@ var init_use_data_grid_keydown_event = __esm({
115470
115477
  },
115471
115478
  [rangeEnd, matrix, getSelectionValues, setSelectionValues, execute]
115472
115479
  );
115473
- const handleSpaceKeyTextOrNumber = (0, import_react99.useCallback)(
115480
+ const handleSpaceKeyText = (0, import_react99.useCallback)(
115481
+ (anchor2) => {
115482
+ const field = matrix.getCellField(anchor2);
115483
+ const input = queryTool?.getInput(anchor2);
115484
+ if (!field || !input) {
115485
+ return;
115486
+ }
115487
+ createSnapshot(anchor2);
115488
+ input.focus();
115489
+ },
115490
+ [matrix, queryTool, createSnapshot]
115491
+ );
115492
+ const handleSpaceKeyNumber = (0, import_react99.useCallback)(
115474
115493
  (anchor2) => {
115475
115494
  const field = matrix.getCellField(anchor2);
115476
115495
  const input = queryTool?.getInput(anchor2);
@@ -115541,10 +115560,12 @@ var init_use_data_grid_keydown_event = __esm({
115541
115560
  case "togglable-number":
115542
115561
  handleSpaceKeyTogglableNumber(anchor);
115543
115562
  break;
115544
- case "number":
115545
115563
  case "text":
115546
115564
  case "multiline-text":
115547
- handleSpaceKeyTextOrNumber(anchor);
115565
+ handleSpaceKeyText(anchor);
115566
+ break;
115567
+ case "number":
115568
+ handleSpaceKeyNumber(anchor);
115548
115569
  break;
115549
115570
  }
115550
115571
  },
@@ -115553,7 +115574,8 @@ var init_use_data_grid_keydown_event = __esm({
115553
115574
  isEditing,
115554
115575
  matrix,
115555
115576
  handleSpaceKeyBoolean,
115556
- handleSpaceKeyTextOrNumber,
115577
+ handleSpaceKeyText,
115578
+ handleSpaceKeyNumber,
115557
115579
  handleSpaceKeyTogglableNumber
115558
115580
  ]
115559
115581
  );
@@ -190739,7 +190761,7 @@ var init_active_locales_section = __esm({
190739
190761
  const hasLocales = locales.length > 0;
190740
190762
  return /* @__PURE__ */ (0, import_jsx_runtime686.jsxs)(import_ui450.Container, { className: "flex flex-col p-0", children: [
190741
190763
  /* @__PURE__ */ (0, import_jsx_runtime686.jsxs)("div", { className: "flex items-center justify-between px-6 py-4", children: [
190742
- /* @__PURE__ */ (0, import_jsx_runtime686.jsx)(import_ui450.Heading, { children: t5("translations.activeLocales.heading") }),
190764
+ /* @__PURE__ */ (0, import_jsx_runtime686.jsx)(import_ui450.Heading, { level: "h2", children: t5("translations.activeLocales.heading") }),
190743
190765
  /* @__PURE__ */ (0, import_jsx_runtime686.jsx)(import_ui450.IconButton, { variant: "transparent", onClick: handleManageLocales, children: /* @__PURE__ */ (0, import_jsx_runtime686.jsx)(import_icons174.PencilSquare, {}) })
190744
190766
  ] }),
190745
190767
  /* @__PURE__ */ (0, import_jsx_runtime686.jsx)("div", { className: "px-1 pb-1", children: hasLocales ? /* @__PURE__ */ (0, import_jsx_runtime686.jsxs)(import_ui450.Container, { className: "bg-ui-bg-component flex items-center gap-x-4 px-6 py-2", children: [
@@ -190850,60 +190872,63 @@ var init_translations_completion_section = __esm({
190850
190872
  () => Math.max(...localeStats.map((s) => s.total), 1),
190851
190873
  [localeStats]
190852
190874
  );
190853
- return /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)(import_ui452.Container, { className: "flex flex-col gap-y-3 px-6 py-4", children: [
190854
- /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)("div", { className: "flex items-center justify-between", children: [
190855
- /* @__PURE__ */ (0, import_jsx_runtime688.jsx)(import_ui452.Heading, { children: t5("translations.completion.heading") }),
190856
- /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)(import_ui452.Text, { size: "small", weight: "plus", className: "text-ui-fg-subtle", children: [
190857
- translatedCount.toLocaleString(),
190858
- " ",
190859
- t5("general.of"),
190860
- " ",
190861
- totalCount.toLocaleString()
190862
- ] })
190863
- ] }),
190864
- /* @__PURE__ */ (0, import_jsx_runtime688.jsx)("div", { className: "flex h-3 w-full overflow-hidden", children: percentage > 0 ? /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)(import_jsx_runtime688.Fragment, { children: [
190865
- /* @__PURE__ */ (0, import_jsx_runtime688.jsx)(
190866
- "div",
190867
- {
190868
- className: "mr-0.5 h-full rounded-sm transition-all",
190869
- style: {
190870
- width: `${percentage}%`,
190871
- backgroundColor: "var(--bg-interactive)"
190875
+ const localeStatsCount = (0, import_react305.useMemo)(() => localeStats.length, [localeStats]);
190876
+ return /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)(import_ui452.Container, { className: "p-0", children: [
190877
+ /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)("div", { className: "flex flex-col gap-y-4 px-6 py-4", children: [
190878
+ /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)("div", { className: "flex items-center justify-between", children: [
190879
+ /* @__PURE__ */ (0, import_jsx_runtime688.jsx)(import_ui452.Heading, { level: "h2", children: t5("translations.completion.heading") }),
190880
+ /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)(import_ui452.Text, { size: "small", weight: "plus", className: "text-ui-fg-subtle", children: [
190881
+ translatedCount.toLocaleString(),
190882
+ " ",
190883
+ t5("general.of"),
190884
+ " ",
190885
+ totalCount.toLocaleString()
190886
+ ] })
190887
+ ] }),
190888
+ /* @__PURE__ */ (0, import_jsx_runtime688.jsx)("div", { className: "flex h-3 w-full overflow-hidden", children: percentage > 0 ? /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)(import_jsx_runtime688.Fragment, { children: [
190889
+ /* @__PURE__ */ (0, import_jsx_runtime688.jsx)(
190890
+ "div",
190891
+ {
190892
+ className: "mr-0.5 h-full rounded-sm transition-all",
190893
+ style: {
190894
+ width: `${percentage}%`,
190895
+ backgroundColor: "var(--bg-interactive)"
190896
+ }
190872
190897
  }
190873
- }
190874
- ),
190875
- /* @__PURE__ */ (0, import_jsx_runtime688.jsx)(
190898
+ ),
190899
+ /* @__PURE__ */ (0, import_jsx_runtime688.jsx)(
190900
+ "div",
190901
+ {
190902
+ className: "h-full flex-1 rounded-sm",
190903
+ style: {
190904
+ backgroundColor: "var(--bg-interactive)",
190905
+ opacity: 0.3
190906
+ }
190907
+ }
190908
+ )
190909
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime688.jsx)(
190876
190910
  "div",
190877
190911
  {
190878
- className: "h-full flex-1 rounded-sm",
190912
+ className: "h-full w-full rounded-sm",
190879
190913
  style: {
190880
190914
  backgroundColor: "var(--bg-interactive)",
190881
190915
  opacity: 0.3
190882
190916
  }
190883
190917
  }
190884
- )
190885
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime688.jsx)(
190886
- "div",
190887
- {
190888
- className: "h-full w-full rounded-full",
190889
- style: {
190890
- backgroundColor: "var(--bg-interactive)",
190891
- opacity: 0.3
190892
- }
190893
- }
190894
- ) }),
190895
- /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)("div", { className: "flex items-center justify-between", children: [
190896
- /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)(import_ui452.Text, { size: "small", weight: "plus", className: "text-ui-fg-subtle", children: [
190897
- percentage.toFixed(1),
190898
- "%"
190899
- ] }),
190900
- /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)(import_ui452.Text, { size: "small", weight: "plus", className: "text-ui-fg-subtle", children: [
190901
- remaining.toLocaleString(),
190902
- " ",
190903
- t5("general.remaining").toLowerCase()
190918
+ ) }),
190919
+ /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)("div", { className: "flex items-center justify-between", children: [
190920
+ /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)(import_ui452.Text, { size: "small", weight: "plus", className: "text-ui-fg-subtle", children: [
190921
+ percentage.toFixed(1),
190922
+ "%"
190923
+ ] }),
190924
+ /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)(import_ui452.Text, { size: "small", weight: "plus", className: "text-ui-fg-subtle", children: [
190925
+ remaining.toLocaleString(),
190926
+ " ",
190927
+ t5("general.remaining").toLowerCase()
190928
+ ] })
190904
190929
  ] })
190905
190930
  ] }),
190906
- localeStats.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)("div", { className: "mt-4 flex flex-col gap-y-2", children: [
190931
+ localeStats.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)("div", { className: "border-ui-border-strong flex flex-col gap-y-3 border-t border-dashed px-6 pb-6 pt-4", children: [
190907
190932
  /* @__PURE__ */ (0, import_jsx_runtime688.jsx)("div", { className: "flex h-32 w-full items-end gap-1", children: localeStats.map((locale) => {
190908
190933
  const heightPercent = locale.total / maxTotal * 100;
190909
190934
  const translatedPercent = locale.total > 0 ? locale.translated / locale.total * 100 : 0;
@@ -190960,7 +190985,7 @@ var init_translations_completion_section = __esm({
190960
190985
  children: /* @__PURE__ */ (0, import_jsx_runtime688.jsxs)(
190961
190986
  "div",
190962
190987
  {
190963
- className: "flex min-w-2 flex-1 cursor-pointer flex-col justify-end overflow-hidden rounded-t-sm transition-opacity",
190988
+ className: "flex min-w-2 flex-1 flex-col justify-end overflow-hidden rounded-t-sm transition-opacity",
190964
190989
  style: { height: `${heightPercent}%` },
190965
190990
  onMouseEnter: () => setHoveredLocale(locale.code),
190966
190991
  onMouseLeave: () => setHoveredLocale(null),
@@ -190995,12 +191020,22 @@ var init_translations_completion_section = __esm({
190995
191020
  locale.code
190996
191021
  );
190997
191022
  }) }),
190998
- /* @__PURE__ */ (0, import_jsx_runtime688.jsx)(
191023
+ localeStatsCount < 9 && /* @__PURE__ */ (0, import_jsx_runtime688.jsx)("div", { className: "flex w-full gap-1", children: localeStats.map((locale) => /* @__PURE__ */ (0, import_jsx_runtime688.jsx)(
191024
+ import_ui452.Text,
191025
+ {
191026
+ size: "xsmall",
191027
+ weight: "plus",
191028
+ className: "text-ui-fg-subtle min-w-2 flex-1 whitespace-normal break-words text-center leading-tight",
191029
+ children: localeStatsCount < 6 ? locale.name : locale.code
191030
+ },
191031
+ locale.code
191032
+ )) }),
191033
+ localeStatsCount > 9 && /* @__PURE__ */ (0, import_jsx_runtime688.jsx)(
190999
191034
  import_ui452.Text,
191000
191035
  {
191001
- size: "small",
191002
191036
  weight: "plus",
191003
- className: "text-ui-fg-muted text-center",
191037
+ size: "xsmall",
191038
+ className: "text-ui-fg-subtle text-center",
191004
191039
  children: t5("translations.completion.footer")
191005
191040
  }
191006
191041
  )
@@ -191146,153 +191181,113 @@ var init_translation_list2 = __esm({
191146
191181
  function isEntityRow(row) {
191147
191182
  return row._type === "entity";
191148
191183
  }
191149
- function initTranslationsFormState(translations, references, availableLocales, translatableFields) {
191150
- const existingMap = /* @__PURE__ */ new Map();
191184
+ function buildLocaleSnapshot(translations, references, localeCode, translatableFields) {
191185
+ const referenceTranslations = /* @__PURE__ */ new Map();
191186
+ for (const t5 of translations) {
191187
+ if (t5.locale_code === localeCode) {
191188
+ referenceTranslations.set(t5.reference_id, t5);
191189
+ }
191190
+ }
191191
+ const entities = {};
191192
+ for (const ref of references) {
191193
+ const existing = referenceTranslations.get(ref.id);
191194
+ const fields = {};
191195
+ for (const fieldName of translatableFields) {
191196
+ fields[fieldName] = existing?.translations?.[fieldName] ?? "";
191197
+ }
191198
+ entities[ref.id] = {
191199
+ id: existing?.id ?? null,
191200
+ fields
191201
+ };
191202
+ }
191203
+ return { localeCode, entities };
191204
+ }
191205
+ function extendSnapshot(snapshot, translations, newReferences, translatableFields) {
191206
+ const referenceTranslations = /* @__PURE__ */ new Map();
191151
191207
  for (const t5 of translations) {
191152
- existingMap.set(`${t5.reference_id}:${t5.locale_code}`, t5);
191153
- }
191154
- const entitiesTranslationState = {};
191155
- for (const reference of references) {
191156
- const locales = {};
191157
- for (const locale of availableLocales) {
191158
- const key = `${reference.id}:${locale.locale_code}`;
191159
- const existing = existingMap.get(key);
191208
+ if (t5.locale_code === snapshot.localeCode) {
191209
+ referenceTranslations.set(t5.reference_id, t5);
191210
+ }
191211
+ }
191212
+ const extendedEntities = { ...snapshot.entities };
191213
+ for (const ref of newReferences) {
191214
+ if (!extendedEntities[ref.id]) {
191215
+ const existing = referenceTranslations.get(ref.id);
191160
191216
  const fields = {};
191161
191217
  for (const fieldName of translatableFields) {
191162
- const fieldValue = existing?.translations?.[fieldName] ?? "";
191163
- fields[fieldName] = fieldValue;
191218
+ fields[fieldName] = existing?.translations?.[fieldName] ?? "";
191164
191219
  }
191165
- locales[locale.locale_code] = {
191220
+ extendedEntities[ref.id] = {
191166
191221
  id: existing?.id ?? null,
191167
- locale_code: locale.locale_code,
191168
191222
  fields
191169
191223
  };
191170
191224
  }
191171
- entitiesTranslationState[reference.id] = { locales };
191172
191225
  }
191173
- return {
191174
- entities: entitiesTranslationState
191175
- };
191226
+ return { ...snapshot, entities: extendedEntities };
191176
191227
  }
191177
- function buildTranslationRows(references, translatableFields) {
191178
- return references.map((reference) => ({
191179
- _type: "entity",
191180
- reference_id: reference.id,
191181
- subRows: translatableFields.map((fieldName) => ({
191182
- _type: "field",
191183
- reference_id: reference.id,
191184
- field_name: fieldName
191185
- }))
191186
- }));
191228
+ function snapshotToFormValues(snapshot) {
191229
+ return { entities: snapshot.entities };
191187
191230
  }
191188
- function transformToBatchPayload(currentState, initialState, entityType) {
191231
+ function computeChanges(currentState, snapshot, entityType, localeCode) {
191189
191232
  const payload = {
191190
191233
  create: [],
191191
191234
  update: [],
191192
191235
  delete: []
191193
191236
  };
191194
191237
  for (const [entityId, entityData] of Object.entries(currentState.entities)) {
191195
- for (const [localeCode, localeTranslations] of Object.entries(
191196
- entityData.locales
191197
- )) {
191198
- const initial = initialState.entities[entityId]?.locales[localeCode];
191199
- const hasContent = Object.values(localeTranslations.fields).some(
191200
- (v) => v !== void 0 && v.trim() !== ""
191201
- );
191202
- const hadContent = initial && Object.values(initial.fields).some(
191203
- (v) => v !== void 0 && v.trim() !== ""
191204
- );
191205
- if (!localeTranslations.id && hasContent) {
191206
- payload.create.push({
191207
- reference_id: entityId,
191208
- reference: entityType,
191209
- locale_code: localeTranslations.locale_code,
191210
- translations: localeTranslations.fields
191211
- });
191212
- } else if (localeTranslations.id && hasContent) {
191213
- const hasChanged = !initial || JSON.stringify(localeTranslations.fields) !== JSON.stringify(initial.fields);
191214
- if (hasChanged) {
191215
- payload.update.push({
191216
- id: localeTranslations.id,
191217
- translations: localeTranslations.fields
191218
- });
191219
- }
191220
- } else if (localeTranslations.id && !hasContent && hadContent) {
191221
- payload.delete.push(localeTranslations.id);
191222
- }
191223
- }
191224
- }
191225
- return payload;
191226
- }
191227
- function hasLocaleChanges(currentState, initialState, localeCode) {
191228
- for (const [entityId, entityData] of Object.entries(currentState.entities)) {
191229
- const currentLocale = entityData.locales[localeCode];
191230
- const initialLocale = initialState.entities[entityId]?.locales[localeCode];
191231
- if (!currentLocale || !initialLocale) {
191238
+ const baseline = snapshot.entities[entityId];
191239
+ if (!baseline) {
191232
191240
  continue;
191233
191241
  }
191234
- for (const [fieldName, fieldValue] of Object.entries(
191235
- currentLocale.fields
191236
- )) {
191237
- const initialValue = initialLocale.fields[fieldName] ?? "";
191238
- const currentValue = fieldValue ?? "";
191239
- if (currentValue !== initialValue) {
191240
- return true;
191241
- }
191242
- }
191243
- }
191244
- return false;
191245
- }
191246
- function transformSingleLocaleToBatchPayload(currentState, initialState, entityType, localeCode) {
191247
- const payload = {
191248
- create: [],
191249
- update: [],
191250
- delete: []
191251
- };
191252
- for (const [entityId, entityData] of Object.entries(currentState.entities)) {
191253
- const localeTranslations = entityData.locales[localeCode];
191254
- if (!localeTranslations) continue;
191255
- const initial = initialState.entities[entityId]?.locales[localeCode];
191256
- const hasContent = Object.values(localeTranslations.fields).some(
191242
+ const hasContent = Object.values(entityData.fields).some(
191257
191243
  (v) => v !== void 0 && v.trim() !== ""
191258
191244
  );
191259
- const hadContent = initial && Object.values(initial.fields).some(
191245
+ const hadContent = Object.values(baseline.fields).some(
191260
191246
  (v) => v !== void 0 && v.trim() !== ""
191261
191247
  );
191262
- if (!localeTranslations.id && hasContent) {
191248
+ const hasChanged = JSON.stringify(entityData.fields) !== JSON.stringify(baseline.fields);
191249
+ if (!entityData.id && hasContent) {
191263
191250
  payload.create.push({
191264
191251
  reference_id: entityId,
191265
191252
  reference: entityType,
191266
- locale_code: localeTranslations.locale_code,
191267
- translations: localeTranslations.fields
191268
- });
191269
- } else if (localeTranslations.id && hasContent) {
191270
- const hasChanged = !initial || JSON.stringify(localeTranslations.fields) !== JSON.stringify(initial.fields);
191271
- if (hasChanged) {
191272
- payload.update.push({
191273
- id: localeTranslations.id,
191274
- translations: localeTranslations.fields
191275
- });
191276
- }
191277
- } else if (localeTranslations.id && !hasContent && hadContent) {
191278
- payload.delete.push(localeTranslations.id);
191253
+ locale_code: localeCode,
191254
+ translations: entityData.fields
191255
+ });
191256
+ } else if (entityData.id && hasContent && hasChanged) {
191257
+ payload.update.push({
191258
+ id: entityData.id,
191259
+ translations: entityData.fields
191260
+ });
191261
+ } else if (entityData.id && !hasContent && hadContent) {
191262
+ payload.delete.push(entityData.id);
191279
191263
  }
191280
191264
  }
191281
- return payload;
191265
+ const hasChanges = payload.create.length > 0 || payload.update.length > 0 || payload.delete.length > 0;
191266
+ return { hasChanges, payload };
191267
+ }
191268
+ function buildTranslationRows(references, translatableFields) {
191269
+ return references.map((reference) => ({
191270
+ _type: "entity",
191271
+ reference_id: reference.id,
191272
+ subRows: translatableFields.map((fieldName) => ({
191273
+ _type: "field",
191274
+ reference_id: reference.id,
191275
+ field_name: fieldName
191276
+ }))
191277
+ }));
191282
191278
  }
191283
191279
  function useTranslationsGridColumns({
191284
191280
  entities,
191285
- translatableFields,
191286
191281
  availableLocales,
191287
191282
  selectedLocale,
191288
191283
  dynamicColumnWidth
191289
191284
  }) {
191290
191285
  const { t: t5 } = (0, import_react_i18next487.useTranslation)();
191291
- const columns = (0, import_react307.useMemo)(() => {
191286
+ return (0, import_react307.useMemo)(() => {
191292
191287
  const selectedLocaleData = availableLocales.find(
191293
191288
  (l) => l.locale_code === selectedLocale
191294
191289
  );
191295
- const baseColumns = [
191290
+ const columns = [
191296
191291
  columnHelper93.column({
191297
191292
  id: "field",
191298
191293
  name: "field",
@@ -191328,9 +191323,7 @@ function useTranslationsGridColumns({
191328
191323
  if (isEntityRow(row)) {
191329
191324
  return /* @__PURE__ */ (0, import_jsx_runtime690.jsx)(DataGrid.ReadonlyCell, { context });
191330
191325
  }
191331
- const entity = entities.find(
191332
- (entity2) => entity2.id === row.reference_id
191333
- );
191326
+ const entity = entities.find((e) => e.id === row.reference_id);
191334
191327
  if (!entity) {
191335
191328
  return null;
191336
191329
  }
@@ -191339,7 +191332,7 @@ function useTranslationsGridColumns({
191339
191332
  })
191340
191333
  ];
191341
191334
  if (selectedLocaleData) {
191342
- baseColumns.push(
191335
+ columns.push(
191343
191336
  columnHelper93.column({
191344
191337
  id: selectedLocaleData.locale_code,
191345
191338
  name: selectedLocaleData.locale.name,
@@ -191357,24 +191350,16 @@ function useTranslationsGridColumns({
191357
191350
  if (isEntityRow(row)) {
191358
191351
  return null;
191359
191352
  }
191360
- return `entities.${row.reference_id}.locales.${selectedLocaleData.locale_code}.fields.${row.field_name}`;
191353
+ return `entities.${row.reference_id}.fields.${row.field_name}`;
191361
191354
  },
191362
191355
  type: "multiline-text"
191363
191356
  })
191364
191357
  );
191365
191358
  }
191366
- return baseColumns;
191367
- }, [
191368
- t5,
191369
- translatableFields,
191370
- availableLocales,
191371
- selectedLocale,
191372
- entities,
191373
- dynamicColumnWidth
191374
- ]);
191375
- return columns;
191359
+ return columns;
191360
+ }, [t5, availableLocales, selectedLocale, entities, dynamicColumnWidth]);
191376
191361
  }
191377
- var import_zod166, import_ui454, import_react307, import_react_hook_form145, import_react_i18next487, import_zod167, import_jsx_runtime690, LocaleTranslationSchema, EntityTranslationsSchema, TranslationsFormSchema, columnHelper93, FIELD_COLUMN_WIDTH, TranslationsEditForm;
191362
+ var import_zod166, import_ui454, import_react307, import_react_hook_form145, import_react_i18next487, import_zod167, import_jsx_runtime690, EntityTranslationsSchema, TranslationsFormSchema, columnHelper93, FIELD_COLUMN_WIDTH, TranslationsEditForm;
191378
191363
  var init_translations_edit_form = __esm({
191379
191364
  "src/routes/translations/translations-edit/components/translations-edit-form/translations-edit-form.tsx"() {
191380
191365
  "use strict";
@@ -191389,19 +191374,15 @@ var init_translations_edit_form = __esm({
191389
191374
  init_keybound_form2();
191390
191375
  init_translations2();
191391
191376
  import_jsx_runtime690 = require("react/jsx-runtime");
191392
- LocaleTranslationSchema = import_zod167.z.object({
191377
+ EntityTranslationsSchema = import_zod167.z.object({
191393
191378
  id: import_zod167.z.string().nullish(),
191394
- locale_code: import_zod167.z.string(),
191395
191379
  fields: import_zod167.z.record(import_zod167.z.string().optional())
191396
191380
  });
191397
- EntityTranslationsSchema = import_zod167.z.object({
191398
- locales: import_zod167.z.record(LocaleTranslationSchema)
191399
- });
191400
191381
  TranslationsFormSchema = import_zod167.z.object({
191401
191382
  entities: import_zod167.z.record(EntityTranslationsSchema)
191402
191383
  });
191403
191384
  columnHelper93 = createDataGridHelper();
191404
- FIELD_COLUMN_WIDTH = 150;
191385
+ FIELD_COLUMN_WIDTH = 350;
191405
191386
  TranslationsEditForm = ({
191406
191387
  translations,
191407
191388
  references,
@@ -191438,57 +191419,83 @@ var init_translations_edit_form = __esm({
191438
191419
  );
191439
191420
  const [showUnsavedPrompt, setShowUnsavedPrompt] = (0, import_react307.useState)(false);
191440
191421
  const [pendingLocale, setPendingLocale] = (0, import_react307.useState)(null);
191441
- const entities = (0, import_react307.useMemo)(() => references, [references]);
191442
- const totalCount = (0, import_react307.useMemo)(
191443
- () => referenceCount * (translatableFields.length + 1),
191444
- [referenceCount, translatableFields]
191445
- );
191446
- const initialState = (0, import_react307.useRef)(
191447
- initTranslationsFormState(
191422
+ const [isSwitchingLocale, setIsSwitchingLocale] = (0, import_react307.useState)(false);
191423
+ const snapshotRef = (0, import_react307.useRef)(
191424
+ buildLocaleSnapshot(
191448
191425
  translations,
191449
- entities,
191450
- availableLocales,
191426
+ references,
191427
+ selectedLocale,
191451
191428
  translatableFields
191452
191429
  )
191453
191430
  );
191431
+ const knownEntityIdsRef = (0, import_react307.useRef)(
191432
+ new Set(references.map((r) => r.id))
191433
+ );
191434
+ const latestPropsRef = (0, import_react307.useRef)({ translations, references });
191435
+ (0, import_react307.useEffect)(() => {
191436
+ latestPropsRef.current = { translations, references };
191437
+ }, [translations, references]);
191454
191438
  const form = (0, import_react_hook_form145.useForm)({
191455
191439
  resolver: (0, import_zod166.zodResolver)(TranslationsFormSchema),
191456
- defaultValues: initialState.current
191440
+ defaultValues: snapshotToFormValues(snapshotRef.current)
191457
191441
  });
191442
+ (0, import_react307.useEffect)(() => {
191443
+ const currentIds = new Set(references.map((r) => r.id));
191444
+ const newReferences = references.filter(
191445
+ (r) => !knownEntityIdsRef.current.has(r.id)
191446
+ );
191447
+ if (newReferences.length === 0) {
191448
+ return;
191449
+ }
191450
+ knownEntityIdsRef.current = currentIds;
191451
+ snapshotRef.current = extendSnapshot(
191452
+ snapshotRef.current,
191453
+ translations,
191454
+ newReferences,
191455
+ translatableFields
191456
+ );
191457
+ const currentValues = form.getValues();
191458
+ const newFormValues = {
191459
+ entities: { ...currentValues.entities }
191460
+ };
191461
+ for (const ref of newReferences) {
191462
+ if (!newFormValues.entities[ref.id]) {
191463
+ newFormValues.entities[ref.id] = snapshotRef.current.entities[ref.id];
191464
+ }
191465
+ }
191466
+ form.reset(newFormValues, {
191467
+ keepDirty: true,
191468
+ keepDirtyValues: true
191469
+ });
191470
+ }, [references, translations, translatableFields, form]);
191458
191471
  const rows = (0, import_react307.useMemo)(
191459
- () => buildTranslationRows(entities, translatableFields),
191460
- [entities, translatableFields]
191472
+ () => buildTranslationRows(references, translatableFields),
191473
+ [references, translatableFields]
191461
191474
  );
191462
- const { mutateAsync, isPending } = useBatchTranslations(entityType);
191463
- const handleLocaleChange = (0, import_react307.useCallback)(
191464
- (newLocale) => {
191465
- if (newLocale === selectedLocale) {
191466
- return;
191467
- }
191468
- const currentValues = form.getValues();
191469
- const hasChanges = hasLocaleChanges(
191470
- currentValues,
191471
- initialState.current,
191472
- selectedLocale
191473
- );
191474
- if (hasChanges) {
191475
- setPendingLocale(newLocale);
191476
- setShowUnsavedPrompt(true);
191477
- } else {
191478
- setSelectedLocale(newLocale);
191479
- }
191480
- },
191481
- [selectedLocale, form]
191475
+ const totalRowCount = (0, import_react307.useMemo)(
191476
+ () => referenceCount * (translatableFields.length + 1),
191477
+ [referenceCount, translatableFields]
191478
+ );
191479
+ const selectedLocaleDisplay = (0, import_react307.useMemo)(
191480
+ () => availableLocales.find((l) => l.locale_code === selectedLocale)?.locale.name,
191481
+ [availableLocales, selectedLocale]
191482
191482
  );
191483
+ const columns = useTranslationsGridColumns({
191484
+ entities: references,
191485
+ availableLocales,
191486
+ selectedLocale,
191487
+ dynamicColumnWidth
191488
+ });
191489
+ const { mutateAsync, isPending, invalidateQueries } = useBatchTranslations(entityType);
191483
191490
  const saveCurrentLocale = (0, import_react307.useCallback)(async () => {
191484
191491
  const currentValues = form.getValues();
191485
- const payload = transformSingleLocaleToBatchPayload(
191492
+ const { hasChanges, payload } = computeChanges(
191486
191493
  currentValues,
191487
- initialState.current,
191494
+ snapshotRef.current,
191488
191495
  entityType,
191489
191496
  selectedLocale
191490
191497
  );
191491
- if (payload.create.length === 0 && payload.update.length === 0 && payload.delete.length === 0) {
191498
+ if (!hasChanges) {
191492
191499
  return true;
191493
191500
  }
191494
191501
  try {
@@ -191512,20 +191519,32 @@ var init_translations_edit_form = __esm({
191512
191519
  }
191513
191520
  if (payload.delete.length > 0) {
191514
191521
  currentBatch.delete = payload.delete.splice(0, currentBatchAvailable);
191515
- currentBatchAvailable -= currentBatch.delete.length;
191516
191522
  }
191517
- await mutateAsync(currentBatch);
191523
+ const response = await mutateAsync(currentBatch, {
191524
+ onError: (error) => {
191525
+ import_ui454.toast.error(error.message);
191526
+ }
191527
+ });
191528
+ if (response.created) {
191529
+ for (const created of response.created) {
191530
+ form.setValue(`entities.${created.reference_id}.id`, created.id, {
191531
+ shouldDirty: false
191532
+ });
191533
+ if (snapshotRef.current.entities[created.reference_id]) {
191534
+ snapshotRef.current.entities[created.reference_id].id = created.id;
191535
+ }
191536
+ }
191537
+ }
191518
191538
  }
191519
- const updatedInitialState = { ...initialState.current };
191520
- for (const entityId of Object.keys(currentValues.entities)) {
191521
- if (updatedInitialState.entities[entityId]?.locales[selectedLocale]) {
191522
- updatedInitialState.entities[entityId].locales[selectedLocale] = {
191523
- ...currentValues.entities[entityId].locales[selectedLocale]
191539
+ const savedValues = form.getValues();
191540
+ for (const entityId of Object.keys(savedValues.entities)) {
191541
+ if (snapshotRef.current.entities[entityId]) {
191542
+ snapshotRef.current.entities[entityId] = {
191543
+ ...savedValues.entities[entityId]
191524
191544
  };
191525
191545
  }
191526
191546
  }
191527
- initialState.current = updatedInitialState;
191528
- form.reset(currentValues);
191547
+ form.reset(savedValues);
191529
191548
  return true;
191530
191549
  } catch (error) {
191531
191550
  import_ui454.toast.error(
@@ -191534,19 +191553,59 @@ var init_translations_edit_form = __esm({
191534
191553
  return false;
191535
191554
  }
191536
191555
  }, [form, entityType, selectedLocale, mutateAsync]);
191556
+ const switchToLocale = (0, import_react307.useCallback)(
191557
+ async (newLocale) => {
191558
+ setIsSwitchingLocale(true);
191559
+ try {
191560
+ await invalidateQueries();
191561
+ await new Promise((resolve) => requestAnimationFrame(resolve));
191562
+ const { translations: translations2, references: references2 } = latestPropsRef.current;
191563
+ const newSnapshot = buildLocaleSnapshot(
191564
+ translations2,
191565
+ references2,
191566
+ newLocale,
191567
+ translatableFields
191568
+ );
191569
+ snapshotRef.current = newSnapshot;
191570
+ knownEntityIdsRef.current = new Set(references2.map((r) => r.id));
191571
+ form.reset(snapshotToFormValues(newSnapshot));
191572
+ setSelectedLocale(newLocale);
191573
+ } finally {
191574
+ setIsSwitchingLocale(false);
191575
+ }
191576
+ },
191577
+ [translatableFields, form, invalidateQueries]
191578
+ );
191579
+ const handleLocaleChange = (0, import_react307.useCallback)(
191580
+ (newLocale) => {
191581
+ if (newLocale === selectedLocale) {
191582
+ return;
191583
+ }
191584
+ const currentValues = form.getValues();
191585
+ const { hasChanges } = computeChanges(
191586
+ currentValues,
191587
+ snapshotRef.current,
191588
+ entityType,
191589
+ selectedLocale
191590
+ );
191591
+ if (hasChanges) {
191592
+ setPendingLocale(newLocale);
191593
+ setShowUnsavedPrompt(true);
191594
+ } else {
191595
+ switchToLocale(newLocale);
191596
+ }
191597
+ },
191598
+ [selectedLocale, form, entityType, switchToLocale]
191599
+ );
191537
191600
  const handleSaveAndSwitch = (0, import_react307.useCallback)(async () => {
191538
191601
  const success = await saveCurrentLocale();
191539
191602
  if (success && pendingLocale) {
191540
- import_ui454.toast.success(
191541
- t5("translations.edit.localeChangesSaved", {
191542
- defaultValue: "Changes saved successfully"
191543
- })
191544
- );
191545
- setSelectedLocale(pendingLocale);
191603
+ import_ui454.toast.success(t5("translations.edit.successToast"));
191604
+ await switchToLocale(pendingLocale);
191546
191605
  }
191547
191606
  setShowUnsavedPrompt(false);
191548
191607
  setPendingLocale(null);
191549
- }, [saveCurrentLocale, pendingLocale, t5]);
191608
+ }, [saveCurrentLocale, pendingLocale, t5, switchToLocale]);
191550
191609
  const handleCancelSwitch = (0, import_react307.useCallback)(() => {
191551
191610
  setShowUnsavedPrompt(false);
191552
191611
  setPendingLocale(null);
@@ -191563,69 +191622,15 @@ var init_translations_edit_form = __esm({
191563
191622
  },
191564
191623
  [saveCurrentLocale, t5, handleSuccess]
191565
191624
  );
191566
- const handleSubmit = form.handleSubmit(async (values) => {
191567
- const payload = transformToBatchPayload(
191568
- values,
191569
- initialState.current,
191570
- entityType
191571
- );
191572
- if (payload.create.length === 0 && payload.update.length === 0 && payload.delete.length === 0) {
191573
- return;
191574
- }
191575
- const BATCH_SIZE = 150;
191576
- const totalItems = payload.create.length + payload.update.length + payload.delete.length;
191577
- const batchCount = Math.ceil(totalItems / BATCH_SIZE);
191578
- for (let i = 0; i < batchCount; i++) {
191579
- let currentBatchAvailable = BATCH_SIZE;
191580
- const currentBatch = {
191581
- create: [],
191582
- update: [],
191583
- delete: []
191584
- };
191585
- if (payload.create.length > 0) {
191586
- currentBatch.create = payload.create.splice(0, currentBatchAvailable);
191587
- currentBatchAvailable -= currentBatch.create.length;
191588
- }
191589
- if (payload.update.length > 0) {
191590
- currentBatch.update = payload.update.splice(0, currentBatchAvailable);
191591
- currentBatchAvailable -= currentBatch.update.length;
191592
- }
191593
- if (payload.delete.length > 0) {
191594
- currentBatch.delete = payload.delete.splice(0, currentBatchAvailable);
191595
- currentBatchAvailable -= currentBatch.delete.length;
191596
- }
191597
- await mutateAsync(currentBatch, {
191598
- onSuccess: () => {
191599
- if (i === batchCount - 1) {
191600
- import_ui454.toast.success(
191601
- t5("translations.edit.successToast", {
191602
- defaultValue: "Translations updated successfully"
191603
- })
191604
- );
191605
- handleSuccess();
191606
- }
191607
- },
191608
- onError: (error) => {
191609
- import_ui454.toast.error(error.message);
191610
- }
191611
- });
191612
- }
191613
- });
191614
- const columns = useTranslationsGridColumns({
191615
- entities,
191616
- translatableFields,
191617
- availableLocales,
191618
- selectedLocale,
191619
- dynamicColumnWidth
191620
- });
191621
- const selectedLocaleDisplay = availableLocales.find(
191622
- (l) => l.locale_code === selectedLocale
191623
- )?.locale.name;
191624
- return /* @__PURE__ */ (0, import_jsx_runtime690.jsxs)(RouteFocusModal.Form, { form, children: [
191625
+ const handleClose = (0, import_react307.useCallback)(() => {
191626
+ invalidateQueries();
191627
+ }, [invalidateQueries]);
191628
+ const isLoading = isPending || isSwitchingLocale;
191629
+ return /* @__PURE__ */ (0, import_jsx_runtime690.jsxs)(RouteFocusModal.Form, { form, onClose: handleClose, children: [
191625
191630
  /* @__PURE__ */ (0, import_jsx_runtime690.jsxs)(
191626
191631
  KeyboundForm,
191627
191632
  {
191628
- onSubmit: handleSubmit,
191633
+ onSubmit: () => handleSave(true),
191629
191634
  className: "flex h-full flex-col overflow-hidden",
191630
191635
  children: [
191631
191636
  /* @__PURE__ */ (0, import_jsx_runtime690.jsx)(RouteFocusModal.Header, {}),
@@ -191642,13 +191647,14 @@ var init_translations_edit_form = __esm({
191642
191647
  },
191643
191648
  state: form,
191644
191649
  onEditingChange: (editing) => setCloseOnEscape(!editing),
191645
- totalRowCount: totalCount,
191650
+ totalRowCount,
191646
191651
  onFetchMore: fetchNextPage,
191647
191652
  isFetchingMore: isFetchingNextPage,
191648
191653
  hasNextPage,
191649
191654
  headerContent: /* @__PURE__ */ (0, import_jsx_runtime690.jsxs)(
191650
191655
  import_ui454.Select,
191651
191656
  {
191657
+ disabled: isLoading,
191652
191658
  value: selectedLocale,
191653
191659
  onValueChange: handleLocaleChange,
191654
191660
  size: "small",
@@ -191668,28 +191674,28 @@ var init_translations_edit_form = __esm({
191668
191674
  }
191669
191675
  ) }) }),
191670
191676
  /* @__PURE__ */ (0, import_jsx_runtime690.jsx)(RouteFocusModal.Footer, { children: /* @__PURE__ */ (0, import_jsx_runtime690.jsxs)("div", { className: "flex items-center justify-end gap-x-2", children: [
191671
- /* @__PURE__ */ (0, import_jsx_runtime690.jsx)(RouteFocusModal.Close, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime690.jsx)(import_ui454.Button, { size: "small", variant: "secondary", children: t5("actions.cancel") }) }),
191672
- /* @__PURE__ */ (0, import_jsx_runtime690.jsx)(
191677
+ /* @__PURE__ */ (0, import_jsx_runtime690.jsx)(RouteFocusModal.Close, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime690.jsx)(
191673
191678
  import_ui454.Button,
191674
191679
  {
191675
- size: "small",
191676
191680
  type: "button",
191681
+ size: "small",
191677
191682
  variant: "secondary",
191678
- onClick: () => handleSave(false),
191679
- isLoading: isPending,
191680
- children: t5("actions.saveChanges")
191683
+ isLoading,
191684
+ children: t5("actions.cancel")
191681
191685
  }
191682
- ),
191686
+ ) }),
191683
191687
  /* @__PURE__ */ (0, import_jsx_runtime690.jsx)(
191684
191688
  import_ui454.Button,
191685
191689
  {
191686
191690
  size: "small",
191687
191691
  type: "button",
191688
- onClick: () => handleSave(true),
191689
- isLoading: isPending,
191690
- children: t5("actions.saveAndClose")
191692
+ variant: "secondary",
191693
+ onClick: () => handleSave(false),
191694
+ isLoading,
191695
+ children: t5("actions.saveChanges")
191691
191696
  }
191692
- )
191697
+ ),
191698
+ /* @__PURE__ */ (0, import_jsx_runtime690.jsx)(import_ui454.Button, { size: "small", type: "submit", isLoading, children: t5("actions.saveAndClose") })
191693
191699
  ] }) })
191694
191700
  ]
191695
191701
  }
@@ -191716,7 +191722,7 @@ var init_translations_edit_form = __esm({
191716
191722
  size: "small",
191717
191723
  onClick: handleSaveAndSwitch,
191718
191724
  type: "button",
191719
- isLoading: isPending,
191725
+ isLoading,
191720
191726
  children: t5("actions.saveChanges")
191721
191727
  }
191722
191728
  )