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

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 (291) hide show
  1. package/dist/add-locales-2OZXZCT5.mjs +81 -0
  2. package/dist/{adjust-inventory-26YXAXQL.mjs → adjust-inventory-47KGDWME.mjs} +2 -2
  3. package/dist/{api-key-management-create-EMP32G2D.mjs → api-key-management-create-EFPPZPZ7.mjs} +4 -4
  4. package/dist/{api-key-management-detail-6RCDH73M.mjs → api-key-management-detail-4IFED7SF.mjs} +15 -15
  5. package/dist/{api-key-management-edit-IBM3ZXHK.mjs → api-key-management-edit-QGVBNLUL.mjs} +4 -4
  6. package/dist/{api-key-management-list-KC5GOWAU.mjs → api-key-management-list-ROIJIIPQ.mjs} +4 -4
  7. package/dist/{api-key-management-sales-channels-LUB5G6RC.mjs → api-key-management-sales-channels-7P5L45AT.mjs} +6 -6
  8. package/dist/app.css +39 -3
  9. package/dist/app.js +18590 -16625
  10. package/dist/app.mjs +13 -13
  11. package/dist/{campaign-create-FJOECKGT.mjs → campaign-create-EZGZ4SBQ.mjs} +3 -3
  12. package/dist/{campaign-detail-5Q4BYCPX.mjs → campaign-detail-NFKLFPY4.mjs} +12 -12
  13. package/dist/{categories-metadata-J7M3XWI7.mjs → categories-metadata-G7BACZ26.mjs} +12 -12
  14. package/dist/{category-create-KHJZSC7G.mjs → category-create-7MV25HYX.mjs} +6 -6
  15. package/dist/{category-detail-S5IPXMHX.mjs → category-detail-XJQQ7UU4.mjs} +14 -14
  16. package/dist/{category-edit-CTA2EPDG.mjs → category-edit-LV7EARAB.mjs} +3 -3
  17. package/dist/{category-list-QBYJ4T3R.mjs → category-list-NIVLV5S3.mjs} +6 -6
  18. package/dist/{category-organize-SXP33XET.mjs → category-organize-SCRGWKDG.mjs} +3 -3
  19. package/dist/{category-products-KPW6BA5J.mjs → category-products-FXFSMCR6.mjs} +13 -13
  20. package/dist/{chunk-OXPE5TAY.mjs → chunk-24C2YVFU.mjs} +1 -1
  21. package/dist/{chunk-4JQR6QNW.mjs → chunk-333HK6Y6.mjs} +2 -2
  22. package/dist/{chunk-DODQ3KJT.mjs → chunk-3RRA56WC.mjs} +8 -3
  23. package/dist/{chunk-5LGRZSEH.mjs → chunk-3YPZ3R4U.mjs} +1 -1
  24. package/dist/{chunk-2DULKOPN.mjs → chunk-5ISRTMYH.mjs} +1 -1
  25. package/dist/{chunk-PHLCT2HA.mjs → chunk-5ZPKY5XP.mjs} +1 -1
  26. package/dist/{chunk-U6G4M5LP.mjs → chunk-6EXKKRZY.mjs} +1 -1
  27. package/dist/{chunk-GRZSG4EP.mjs → chunk-74WPAWMW.mjs} +127 -226
  28. package/dist/chunk-7AXHHXCX.mjs +4 -0
  29. package/dist/{chunk-YKYVCQRS.mjs → chunk-BJ44US4R.mjs} +382 -242
  30. package/dist/{chunk-BMS2QLJY.mjs → chunk-BMJ5RYIO.mjs} +1 -1
  31. package/dist/{chunk-Y2YVTIJI.mjs → chunk-CW5PQUJ6.mjs} +1 -1
  32. package/dist/{chunk-IAV7IKJ6.mjs → chunk-DN5BRKVN.mjs} +1 -1
  33. package/dist/{chunk-RS7DWLEP.mjs → chunk-DQUXK4WW.mjs} +1 -1
  34. package/dist/{chunk-2XTBDCGE.mjs → chunk-DWH2OQFK.mjs} +1 -1
  35. package/dist/{chunk-CCQD65EY.mjs → chunk-FKNW5MLZ.mjs} +360 -288
  36. package/dist/{chunk-HTCYX4VD.mjs → chunk-G35UUG2P.mjs} +1 -1
  37. package/dist/{chunk-QKALAT7P.mjs → chunk-GGONMC2N.mjs} +1 -1
  38. package/dist/{chunk-FYWHE3W5.mjs → chunk-GJN5SXGZ.mjs} +1 -1
  39. package/dist/{chunk-CVHJAKLQ.mjs → chunk-HDO2UCKF.mjs} +1 -1
  40. package/dist/{chunk-3C6WQ7NH.mjs → chunk-IWNNQ6HA.mjs} +37 -3
  41. package/dist/{chunk-OL24RDYM.mjs → chunk-KHJL6MUQ.mjs} +8 -8
  42. package/dist/{chunk-6P4Q4AAP.mjs → chunk-KPI4WFJU.mjs} +3 -3
  43. package/dist/{chunk-AHZLMCZF.mjs → chunk-KSDXSKJ7.mjs} +1 -1
  44. package/dist/{chunk-BZKI5J2M.mjs → chunk-L7A2JIQY.mjs} +3 -3
  45. package/dist/{chunk-XY7A7GZJ.mjs → chunk-LEAMWI5H.mjs} +1 -1
  46. package/dist/{chunk-A4XYK3MY.mjs → chunk-LG5B2BVB.mjs} +2 -2
  47. package/dist/{chunk-23GTCEOV.mjs → chunk-ML7BA2NY.mjs} +1 -1
  48. package/dist/{chunk-OFN7DIZA.mjs → chunk-OYHFHBNL.mjs} +94 -7
  49. package/dist/{chunk-OSHH5GAS.mjs → chunk-Q34TRFIW.mjs} +1 -13
  50. package/dist/{chunk-UJ2TMPV4.mjs → chunk-QBIQEVTU.mjs} +0 -12
  51. package/dist/{chunk-YYOPBKME.mjs → chunk-QHRAWZGA.mjs} +3 -3
  52. package/dist/{chunk-A2WBKOXJ.mjs → chunk-SO2DE6MM.mjs} +2 -2
  53. package/dist/chunk-T6DDBLGY.mjs +13 -0
  54. package/dist/{chunk-EHU67PIM.mjs → chunk-TOCMU7UV.mjs} +7 -3
  55. package/dist/{chunk-YIOBBZUB.mjs → chunk-U7KANQ5A.mjs} +2 -2
  56. package/dist/{chunk-Z6BFNHEO.mjs → chunk-UCYUWNJL.mjs} +109 -62
  57. package/dist/{chunk-WYATCUOM.mjs → chunk-V2RGYIAG.mjs} +10 -10
  58. package/dist/{chunk-UWY5ZV66.mjs → chunk-VQ73B7ZZ.mjs} +1 -13
  59. package/dist/chunk-XD72PXRS.mjs +329 -0
  60. package/dist/{store-add-locales-VJ4RJ7UI.mjs → chunk-YFXI6CWD.mjs} +2 -67
  61. package/dist/{chunk-AWRCV3ME.mjs → chunk-YM4XGCHB.mjs} +1 -1
  62. package/dist/{chunk-ZMG5B4FG.mjs → chunk-ZGV5NOTE.mjs} +1 -1
  63. package/dist/{collection-add-products-FU2BS3D3.mjs → collection-add-products-YDMBU2JY.mjs} +13 -13
  64. package/dist/{collection-create-GWKWVT7B.mjs → collection-create-RXHEXU6T.mjs} +3 -3
  65. package/dist/{collection-detail-VJE7XHLV.mjs → collection-detail-75XPGVOX.mjs} +13 -13
  66. package/dist/{collection-edit-EZIO2BR5.mjs → collection-edit-CGF47VIN.mjs} +3 -3
  67. package/dist/{collection-list-IGA6SCNF.mjs → collection-list-75N5NYXQ.mjs} +18 -17
  68. package/dist/{collection-metadata-QK7MI3D2.mjs → collection-metadata-TGGC6HM2.mjs} +12 -12
  69. package/dist/{customer-detail-MOV2T3LF.mjs → customer-detail-KDLZ23B3.mjs} +17 -16
  70. package/dist/{customer-group-detail-6T7OXGQD.mjs → customer-group-detail-ZIAVMOYS.mjs} +12 -12
  71. package/dist/{customer-group-list-AJEAF5D2.mjs → customer-group-list-BDDC5VVZ.mjs} +15 -15
  72. package/dist/{customers-add-customer-group-QVTVSQYM.mjs → customers-add-customer-group-B3ASQGP6.mjs} +12 -12
  73. package/dist/{edit-inventory-item-H7DAZWIT.mjs → edit-inventory-item-OKAXQRJ5.mjs} +2 -2
  74. package/dist/{edit-inventory-item-attributes-7HTXXPGZ.mjs → edit-inventory-item-attributes-FVWDV6O5.mjs} +2 -2
  75. package/dist/{edit-reservation-OVTRZHJR.mjs → edit-reservation-3PU6YSNJ.mjs} +3 -3
  76. package/dist/{edit-rules-SMVRTCUP.mjs → edit-rules-LOJ3P4YB.mjs} +14 -14
  77. package/dist/en.json +85 -5
  78. package/dist/{inventory-create-ANYUM4P5.mjs → inventory-create-C2WWS7QR.mjs} +13 -13
  79. package/dist/{inventory-detail-ZPSEMYI2.mjs → inventory-detail-AKOZ4T3Z.mjs} +12 -12
  80. package/dist/{inventory-list-RXJPSVZE.mjs → inventory-list-DRFJIU2O.mjs} +2 -2
  81. package/dist/{inventory-metadata-FNEJ3RAT.mjs → inventory-metadata-N6CJZEWD.mjs} +12 -12
  82. package/dist/{inventory-stock-FD4ZM4BB.mjs → inventory-stock-IJ2N7LKC.mjs} +14 -14
  83. package/dist/{location-detail-N3GUZSY7.mjs → location-detail-PL4Y43RS.mjs} +16 -16
  84. package/dist/{location-fulfillment-providers-7ZUJAGNY.mjs → location-fulfillment-providers-2PLOVSXC.mjs} +17 -16
  85. package/dist/{location-sales-channels-P3QJTFDT.mjs → location-sales-channels-SZARSLWT.mjs} +5 -5
  86. package/dist/{location-service-zone-shipping-option-create-ZJ4GIBTJ.mjs → location-service-zone-shipping-option-create-CUNSAP24.mjs} +15 -15
  87. package/dist/{location-service-zone-shipping-option-edit-4CGPQ3VT.mjs → location-service-zone-shipping-option-edit-OJ462SVM.mjs} +1 -1
  88. package/dist/{location-service-zone-shipping-option-pricing-CR4BVYG3.mjs → location-service-zone-shipping-option-pricing-5HN2Z5RB.mjs} +2 -2
  89. package/dist/login-VBUNWZHI.mjs +301 -0
  90. package/dist/{manage-locations-7HH6R4UP.mjs → manage-locations-JPCSBA5D.mjs} +2 -2
  91. package/dist/{order-allocate-items-HZGGYJ42.mjs → order-allocate-items-6QHQASQJ.mjs} +4 -4
  92. package/dist/{order-create-claim-SCDJGM46.mjs → order-create-claim-ECVSH5GC.mjs} +22 -22
  93. package/dist/{order-create-edit-JIE3HDHP.mjs → order-create-edit-EVIOJIXE.mjs} +14 -13
  94. package/dist/{order-create-exchange-LQU4YN7F.mjs → order-create-exchange-ZVYYOGI3.mjs} +22 -22
  95. package/dist/{order-create-fulfillment-OWUVTZXW.mjs → order-create-fulfillment-FDIFEV6X.mjs} +16 -16
  96. package/dist/{order-create-refund-Q6HQY42R.mjs → order-create-refund-TNPGKR5P.mjs} +17 -17
  97. package/dist/{order-create-return-52GHGW5Z.mjs → order-create-return-NR6XQLDD.mjs} +9 -9
  98. package/dist/{order-create-shipment-WAGGEPRW.mjs → order-create-shipment-UP3WGRWB.mjs} +12 -12
  99. package/dist/{order-detail-PVPGEWGY.mjs → order-detail-F6D5DV74.mjs} +28 -28
  100. package/dist/{order-edit-billing-address-UM76J4KX.mjs → order-edit-billing-address-D4AJ6Z5Q.mjs} +44 -12
  101. package/dist/{order-edit-email-CL3KNOCM.mjs → order-edit-email-MXYA5BII.mjs} +12 -12
  102. package/dist/{order-edit-shipping-address-PIESTGVL.mjs → order-edit-shipping-address-GKW2ZPGR.mjs} +42 -12
  103. package/dist/{order-export-LE363ZLB.mjs → order-export-KQUI2TK7.mjs} +14 -14
  104. package/dist/{order-list-GRLQWN4L.mjs → order-list-7A5UKGXR.mjs} +11 -11
  105. package/dist/{order-metadata-FHBB7MTG.mjs → order-metadata-2IXELA7C.mjs} +12 -12
  106. package/dist/{order-receive-return-PRVKP6J2.mjs → order-receive-return-XXHOXGDC.mjs} +13 -13
  107. package/dist/{order-request-transfer-XSAGRUMT.mjs → order-request-transfer-2ENR64VR.mjs} +13 -13
  108. package/dist/{price-list-configuration-6S3MLNXQ.mjs → price-list-configuration-BZ3ZH5FY.mjs} +3 -3
  109. package/dist/{price-list-create-MFRUQADC.mjs → price-list-create-GFGNP6OS.mjs} +16 -16
  110. package/dist/{price-list-detail-Q5VG5VGW.mjs → price-list-detail-SNNOR5N7.mjs} +17 -17
  111. package/dist/{price-list-edit-53UW35L3.mjs → price-list-edit-YW5BXBF2.mjs} +3 -3
  112. package/dist/{price-list-list-DG5YEZ44.mjs → price-list-list-OF562HME.mjs} +4 -4
  113. package/dist/{price-list-prices-add-SDU5YZAT.mjs → price-list-prices-add-F7A2EV7L.mjs} +15 -15
  114. package/dist/{price-list-prices-edit-5USQR4D4.mjs → price-list-prices-edit-PQ26MOJE.mjs} +5 -5
  115. package/dist/{product-attributes-EFIRUBRO.mjs → product-attributes-JTPGHUXZ.mjs} +13 -13
  116. package/dist/{product-create-K6EWZHIT.mjs → product-create-VFLLQWVX.mjs} +485 -561
  117. package/dist/{product-create-variant-ERKHTEJZ.mjs → product-create-variant-PLZ5Q2R6.mjs} +14 -14
  118. package/dist/{product-detail-DKPZDEIY.mjs → product-detail-ISHFZV7X.mjs} +45 -66
  119. package/dist/{product-edit-55YXTIGO.mjs → product-edit-6EDDNIM6.mjs} +13 -13
  120. package/dist/{product-export-5AD7NELI.mjs → product-export-UMMROQ7T.mjs} +18 -17
  121. package/dist/{product-image-variants-edit-M6QF2RLE.mjs → product-image-variants-edit-VRG3AJID.mjs} +12 -12
  122. package/dist/{product-import-V3KQN4TV.mjs → product-import-EPS4KDID.mjs} +12 -12
  123. package/dist/{product-list-EUWZIFTM.mjs → product-list-37O7VB2L.mjs} +20 -20
  124. package/dist/{product-media-3VJ7KENL.mjs → product-media-IV5NUSLW.mjs} +3 -3
  125. package/dist/{product-metadata-GL2MVPDI.mjs → product-metadata-KTVRI7B3.mjs} +12 -12
  126. package/dist/product-option-create-JUGLFR2K.mjs +336 -0
  127. package/dist/product-option-detail-VVK3YBCE.mjs +331 -0
  128. package/dist/product-option-edit-7FQSKI2R.mjs +325 -0
  129. package/dist/product-option-list-YHGRI7OJ.mjs +268 -0
  130. package/dist/product-option-metadata-P2DHDGYR.mjs +73 -0
  131. package/dist/product-options-manage-4SZDF5H5.mjs +325 -0
  132. package/dist/{product-organization-N3VBRXF4.mjs → product-organization-OEUSIWNQ.mjs} +15 -15
  133. package/dist/{product-prices-4C36AG4R.mjs → product-prices-GUO3KM2U.mjs} +3 -3
  134. package/dist/{product-sales-channels-PPXUG4KT.mjs → product-sales-channels-BOZD3AC3.mjs} +5 -5
  135. package/dist/{product-shipping-profile-ETQFZ7DC.mjs → product-shipping-profile-FNFRDY47.mjs} +3 -3
  136. package/dist/{product-stock-VEGE6SUZ.mjs → product-stock-2DNYG73L.mjs} +14 -14
  137. package/dist/{product-tag-create-PQMDDKWH.mjs → product-tag-create-CW7GUCZW.mjs} +12 -12
  138. package/dist/{product-tag-detail-I3MBZX7U.mjs → product-tag-detail-IB5VIZ3K.mjs} +26 -25
  139. package/dist/{product-tag-edit-K3BBQLJR.mjs → product-tag-edit-ZSXD3DOC.mjs} +12 -12
  140. package/dist/{product-tag-list-JUWSOMB7.mjs → product-tag-list-MJO5PURP.mjs} +24 -23
  141. package/dist/{product-tag-metadata-MJH5LH7E.mjs → product-tag-metadata-DAKCXWZF.mjs} +12 -12
  142. package/dist/{product-type-create-DRFXTL5O.mjs → product-type-create-NRLNMABQ.mjs} +2 -2
  143. package/dist/{product-type-detail-RKHT5NBL.mjs → product-type-detail-J2IMAZW6.mjs} +14 -14
  144. package/dist/{product-type-edit-SRHCZDK7.mjs → product-type-edit-BHTPMMVI.mjs} +2 -2
  145. package/dist/{product-type-list-QQKAHBJ3.mjs → product-type-list-MQ2PW2IE.mjs} +3 -3
  146. package/dist/{product-type-metadata-CDJDFFGQ.mjs → product-type-metadata-BCZMUG3K.mjs} +12 -12
  147. package/dist/{product-variant-detail-XAYG5CKE.mjs → product-variant-detail-4L6DHT3K.mjs} +12 -12
  148. package/dist/{product-variant-edit-DEZEY2H2.mjs → product-variant-edit-PZU4P65H.mjs} +13 -13
  149. package/dist/{product-variant-manage-inventory-items-Y2VEOHP7.mjs → product-variant-manage-inventory-items-E3GPBPEZ.mjs} +3 -3
  150. package/dist/{product-variant-media-2WLVNGI4.mjs → product-variant-media-BSNCO642.mjs} +2 -2
  151. package/dist/{product-variant-metadata-VTZDNWUT.mjs → product-variant-metadata-FHGGZGPV.mjs} +12 -12
  152. package/dist/{promotion-add-campaign-DO67QK6M.mjs → promotion-add-campaign-NQSIFI33.mjs} +3 -3
  153. package/dist/{promotion-create-HWFNUQXG.mjs → promotion-create-GBZWJVAI.mjs} +24 -21
  154. package/dist/{promotion-detail-QC36KXB3.mjs → promotion-detail-W2OGUESO.mjs} +12 -12
  155. package/dist/{refund-reason-create-YHCDEHGQ.mjs → refund-reason-create-DLVRT5OU.mjs} +12 -12
  156. package/dist/{refund-reason-edit-CZ5QZ2SZ.mjs → refund-reason-edit-XCSU7RMH.mjs} +12 -12
  157. package/dist/{refund-reason-list-OJYYEYJE.mjs → refund-reason-list-G6UNVGPG.mjs} +18 -18
  158. package/dist/{region-create-NA7Y2LN4.mjs → region-create-2CRTF7HJ.mjs} +1 -1
  159. package/dist/{region-edit-WAU347DP.mjs → region-edit-IB3PJE7V.mjs} +1 -1
  160. package/dist/{region-metadata-H6XXUQ4S.mjs → region-metadata-WBEUKOI6.mjs} +12 -12
  161. package/dist/{reservation-create-ZCIYM6JI.mjs → reservation-create-ERFJUBME.mjs} +4 -4
  162. package/dist/{reservation-detail-LZAQL4XA.mjs → reservation-detail-Y6UZRZXY.mjs} +12 -12
  163. package/dist/{reservation-list-B47DXTA7.mjs → reservation-list-LEDZB7KL.mjs} +3 -3
  164. package/dist/{reservation-metadata-5HZSDDOK.mjs → reservation-metadata-7KH4EXMN.mjs} +12 -12
  165. package/dist/{return-reason-list-SCBGTOEI.mjs → return-reason-list-UUQAL464.mjs} +6 -6
  166. package/dist/{sales-channel-add-products-F7YV4MO5.mjs → sales-channel-add-products-GSPG7OTK.mjs} +14 -14
  167. package/dist/{sales-channel-create-MI7HHZYE.mjs → sales-channel-create-MM237EBQ.mjs} +3 -3
  168. package/dist/{sales-channel-detail-MXIPZCGA.mjs → sales-channel-detail-65ZFV2OI.mjs} +14 -14
  169. package/dist/{sales-channel-edit-VSHOIR37.mjs → sales-channel-edit-AC2J4N3M.mjs} +3 -3
  170. package/dist/{sales-channel-list-RLGL7FM3.mjs → sales-channel-list-LXLW7X3U.mjs} +14 -14
  171. package/dist/{sales-channel-metadata-M364R4RJ.mjs → sales-channel-metadata-QUAL2RH6.mjs} +13 -13
  172. package/dist/{shipping-option-type-create-C5WUWON7.mjs → shipping-option-type-create-EU75JC2Q.mjs} +12 -12
  173. package/dist/{shipping-option-type-detail-PENS2K73.mjs → shipping-option-type-detail-5GYEWATD.mjs} +13 -13
  174. package/dist/{shipping-option-type-edit-CIU5EHRP.mjs → shipping-option-type-edit-YR4JXT5M.mjs} +12 -12
  175. package/dist/{shipping-option-type-list-DIOX7VG7.mjs → shipping-option-type-list-BLC4Z5QW.mjs} +13 -13
  176. package/dist/{shipping-profile-metadata-75G2NNMA.mjs → shipping-profile-metadata-64YRWPDA.mjs} +12 -12
  177. package/dist/store-add-locales-TSMLNFZP.mjs +81 -0
  178. package/dist/{store-detail-JSNPOB2F.mjs → store-detail-3SS53LZR.mjs} +13 -13
  179. package/dist/{store-edit-5ZS562ZO.mjs → store-edit-SMEWFETF.mjs} +1 -1
  180. package/dist/{store-metadata-CYXTVJUE.mjs → store-metadata-IJGVDDA6.mjs} +12 -12
  181. package/dist/{tax-region-create-DWGL4EUT.mjs → tax-region-create-XLYIS5XX.mjs} +13 -13
  182. package/dist/{tax-region-detail-2AE2EFI3.mjs → tax-region-detail-66FKEGFB.mjs} +29 -28
  183. package/dist/{tax-region-edit-EEVEEU2Q.mjs → tax-region-edit-GBR4EDW7.mjs} +13 -13
  184. package/dist/{tax-region-province-detail-4ERSEQFF.mjs → tax-region-province-detail-OBA7Q7MQ.mjs} +29 -28
  185. package/dist/{tax-region-tax-override-create-PHCGEF7V.mjs → tax-region-tax-override-create-34WYD2QZ.mjs} +28 -27
  186. package/dist/{tax-region-tax-override-edit-SMRPSILC.mjs → tax-region-tax-override-edit-6WJVOGWA.mjs} +27 -26
  187. package/dist/translation-list-J65VNNAX.mjs +527 -0
  188. package/dist/{translations-edit-HUNKY7CO.mjs → translations-edit-6P3KC4MO.mjs} +285 -320
  189. package/dist/{user-detail-BJUXLZZQ.mjs → user-detail-VMOJFHIJ.mjs} +2 -2
  190. package/dist/{user-metadata-2WPJOEJA.mjs → user-metadata-UOPVFX2L.mjs} +12 -12
  191. package/dist/{workflow-execution-detail-H2AKEZJX.mjs → workflow-execution-detail-WAQ7IG4T.mjs} +12 -12
  192. package/package.json +9 -9
  193. package/src/components/data-grid/components/data-grid-cell-container.tsx +7 -10
  194. package/src/components/data-grid/components/data-grid-multiline-cell.tsx +103 -0
  195. package/src/components/data-grid/components/data-grid-readonly-cell.tsx +1 -4
  196. package/src/components/data-grid/components/data-grid-root.tsx +68 -51
  197. package/src/components/data-grid/components/data-grid-text-cell.tsx +6 -76
  198. package/src/components/data-grid/components/index.ts +1 -0
  199. package/src/components/data-grid/data-grid.tsx +2 -0
  200. package/src/components/data-grid/hooks/use-data-grid-cell.tsx +12 -0
  201. package/src/components/data-grid/hooks/use-data-grid-form-handlers.tsx +1 -0
  202. package/src/components/data-grid/hooks/use-data-grid-keydown-event.tsx +65 -7
  203. package/src/components/data-grid/types.ts +1 -0
  204. package/src/components/inputs/combobox/combobox.tsx +185 -95
  205. package/src/components/layout/main-layout/main-layout.tsx +7 -4
  206. package/src/components/table/table-cells/order/country-cell/country-cell.tsx +5 -1
  207. package/src/dashboard-app/routes/get-route.map.tsx +68 -12
  208. package/src/hooks/api/cloud.tsx +41 -0
  209. package/src/hooks/api/index.ts +3 -1
  210. package/src/hooks/api/product-options.tsx +151 -0
  211. package/src/hooks/api/products.tsx +26 -66
  212. package/src/hooks/api/translations.tsx +103 -191
  213. package/src/hooks/table/columns/use-order-table-columns.tsx +5 -3
  214. package/src/hooks/table/columns/use-product-option-table-columns.tsx +44 -0
  215. package/src/hooks/table/filters/index.ts +1 -0
  216. package/src/hooks/table/filters/use-product-option-table-filters.tsx +33 -0
  217. package/src/hooks/table/query/use-product-option-table-query.tsx +34 -0
  218. package/src/i18n/translations/$schema.json +304 -12
  219. package/src/i18n/translations/en.json +85 -5
  220. package/src/i18n/translations/es.json +21 -1
  221. package/src/routes/customers/customer-detail/components/customer-order-section/customer-order-section.tsx +1 -1
  222. package/src/routes/login/components/cloud-auth-login.tsx +114 -0
  223. package/src/routes/login/login.tsx +9 -1
  224. package/src/routes/orders/order-create-edit/components/order-edit-create-form/order-edit-item.tsx +1 -0
  225. package/src/routes/orders/order-edit-billing-address/components/edit-order-billing-address-form/edit-order-billing-address-form.tsx +34 -0
  226. package/src/routes/orders/order-edit-shipping-address/components/edit-order-shipping-address-form/edit-order-shipping-address-form.tsx +32 -0
  227. package/src/routes/product-options/common/hooks/use-delete-product-option-action.tsx +41 -0
  228. package/src/routes/product-options/product-option-create/components/create-product-option-form/create-product-option-details.tsx +71 -0
  229. package/src/routes/product-options/product-option-create/components/create-product-option-form/create-product-option-form.tsx +192 -0
  230. package/src/routes/product-options/product-option-create/components/create-product-option-form/create-product-option-organize.tsx +77 -0
  231. package/src/routes/product-options/product-option-create/components/create-product-option-form/index.ts +1 -0
  232. package/src/routes/product-options/product-option-create/components/create-product-option-form/schema.ts +15 -0
  233. package/src/routes/product-options/product-option-create/index.ts +1 -0
  234. package/src/routes/product-options/product-option-create/product-option-create.tsx +10 -0
  235. package/src/routes/product-options/product-option-detail/breadcrumb.tsx +14 -0
  236. package/src/routes/product-options/product-option-detail/components/product-option-general-section/index.ts +1 -0
  237. package/src/routes/product-options/product-option-detail/components/product-option-general-section/product-option-general-section.tsx +84 -0
  238. package/src/routes/product-options/product-option-detail/components/product-option-product-section/index.ts +1 -0
  239. package/src/routes/product-options/product-option-detail/components/product-option-product-section/product-option-product-section.tsx +82 -0
  240. package/src/routes/product-options/product-option-detail/index.ts +3 -0
  241. package/src/routes/product-options/product-option-detail/loader.ts +18 -0
  242. package/src/routes/product-options/product-option-detail/product-option-detail.tsx +50 -0
  243. package/src/routes/product-options/product-option-edit/components/edit-product-option-form/edit-product-option-details.tsx +94 -0
  244. package/src/routes/product-options/product-option-edit/components/edit-product-option-form/edit-product-option-form.tsx +116 -0
  245. package/src/routes/product-options/product-option-edit/components/edit-product-option-form/edit-product-option-organize.tsx +77 -0
  246. package/src/routes/product-options/product-option-edit/components/edit-product-option-form/index.ts +1 -0
  247. package/src/routes/product-options/product-option-edit/components/edit-product-option-form/schema.ts +13 -0
  248. package/src/routes/product-options/product-option-edit/index.ts +1 -0
  249. package/src/routes/product-options/product-option-edit/product-option-edit.tsx +34 -0
  250. package/src/routes/product-options/product-option-list/components/product-option-list-table/index.ts +1 -0
  251. package/src/routes/product-options/product-option-list/components/product-option-list-table/product-option-list-table.tsx +140 -0
  252. package/src/routes/product-options/product-option-list/index.ts +1 -0
  253. package/src/routes/product-options/product-option-list/product-option-list.tsx +19 -0
  254. package/src/routes/product-options/product-option-metadata/index.ts +1 -0
  255. package/src/routes/product-options/product-option-metadata/product-option-metadata.tsx +27 -0
  256. package/src/routes/products/product-create/components/product-create-details-form/components/product-create-details-general-section/product-create-general-section.tsx +16 -4
  257. package/src/routes/products/product-create/components/product-create-details-form/components/product-create-details-variant-section/product-create-details-variant-section.tsx +327 -194
  258. package/src/routes/products/product-create/components/product-create-form/product-create-form.tsx +4 -7
  259. package/src/routes/products/product-create/constants.ts +2 -0
  260. package/src/routes/products/product-create/utils.ts +6 -1
  261. package/src/routes/products/product-detail/components/product-option-section/product-option-section.tsx +33 -51
  262. package/src/routes/products/product-options-manage/components/product-options-manage-form/index.ts +1 -0
  263. package/src/routes/products/product-options-manage/components/product-options-manage-form/product-options-manage-form.tsx +296 -0
  264. package/src/routes/products/product-options-manage/index.ts +1 -0
  265. package/src/routes/products/product-options-manage/product-options-manage.tsx +35 -0
  266. package/src/routes/promotions/promotion-create/components/create-promotion-form/create-promotion-form.tsx +6 -0
  267. package/src/routes/translations/add-locales/add-locales.tsx +29 -0
  268. package/src/routes/translations/add-locales/index.tsx +1 -0
  269. package/src/routes/translations/translation-list/components/active-locales-section/active-locales-section.tsx +42 -17
  270. package/src/routes/translations/translation-list/components/translation-list-section/translation-list-section.tsx +5 -1
  271. package/src/routes/translations/translation-list/components/translations-completion-section/translations-completion-section.tsx +253 -41
  272. package/src/routes/translations/translation-list/translation-list.tsx +8 -2
  273. package/src/routes/translations/translations-edit/components/translations-edit-form/translations-edit-form.tsx +337 -380
  274. package/src/routes/translations/translations-edit/translations-edit.tsx +8 -13
  275. package/dist/chunk-HGRIOEAR.mjs +0 -32
  276. package/dist/chunk-XDJ7OMBR.mjs +0 -160
  277. package/dist/login-VNOLI5YG.mjs +0 -201
  278. package/dist/product-create-option-7AOXAA4S.mjs +0 -151
  279. package/dist/product-edit-option-LWJT3CYJ.mjs +0 -156
  280. package/dist/translation-list-S5Z6PG2R.mjs +0 -295
  281. package/src/routes/products/product-create-option/components/create-product-option-form/create-product-option-form.tsx +0 -122
  282. package/src/routes/products/product-create-option/components/create-product-option-form/index.ts +0 -1
  283. package/src/routes/products/product-create-option/index.ts +0 -1
  284. package/src/routes/products/product-create-option/product-create-option.tsx +0 -30
  285. package/src/routes/products/product-edit-option/components/edit-product-option-form/edit-product-option-form.tsx +0 -123
  286. package/src/routes/products/product-edit-option/components/edit-product-option-form/index.ts +0 -1
  287. package/src/routes/products/product-edit-option/index.ts +0 -1
  288. package/src/routes/products/product-edit-option/product-edit-option.tsx +0 -35
  289. package/dist/{chunk-FBYTX6K7.mjs → chunk-ORWRXJJT.mjs} +4 -4
  290. package/dist/{location-list-KVBA6J47.mjs → location-list-3WP65J3E.mjs} +1 -1
  291. package/dist/{user-list-YTZQNYSO.mjs → user-list-3EAKK4XC.mjs} +3 -3
@@ -1,6 +1,6 @@
1
1
  import { zodResolver } from "@hookform/resolvers/zod"
2
2
  import { AdminStoreLocale, HttpTypes } from "@medusajs/types"
3
- import { Button, Prompt, Select, toast } from "@medusajs/ui"
3
+ import { Button, Prompt, Select, toast, Text } from "@medusajs/ui"
4
4
  import { ColumnDef } from "@tanstack/react-table"
5
5
  import { useCallback, useEffect, useMemo, useRef, useState } from "react"
6
6
  import { useForm } from "react-hook-form"
@@ -18,38 +18,17 @@ import {
18
18
  import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
19
19
  import { useBatchTranslations } from "../../../../../hooks/api/translations"
20
20
 
21
- /**
22
- * Schema for a single locale translation.
23
- */
24
- const LocaleTranslationSchema = z.object({
21
+ const EntityTranslationsSchema = z.object({
25
22
  id: z.string().nullish(),
26
- locale_code: z.string(),
27
23
  fields: z.record(z.string().optional()),
28
24
  })
29
- export type LocaleTranslationSchema = z.infer<typeof LocaleTranslationSchema>
30
-
31
- /**
32
- * Schema for an entity's translations (parent row in DataGrid).
33
- * Contains all locale translations for that entity.
34
- */
35
- const EntityTranslationsSchema = z.object({
36
- locales: z.record(LocaleTranslationSchema),
37
- })
38
25
  export type EntityTranslationsSchema = z.infer<typeof EntityTranslationsSchema>
39
26
 
40
- /**
41
- * Form schema
42
- * Maps each reference_id to their corresponding translations for each locale
43
- */
44
27
  export const TranslationsFormSchema = z.object({
45
28
  entities: z.record(EntityTranslationsSchema),
46
29
  })
47
30
  export type TranslationsFormSchema = z.infer<typeof TranslationsFormSchema>
48
31
 
49
- /**
50
- * Row types for the DataGrid.
51
- * Parent rows are entities, subrows are translatable fields.
52
- */
53
32
  export type TranslationRow = EntityRow | FieldRow
54
33
 
55
34
  export type EntityRow = {
@@ -72,150 +51,94 @@ export function isFieldRow(row: TranslationRow): row is FieldRow {
72
51
  return row._type === "field"
73
52
  }
74
53
 
75
- function initTranslationsFormState(
54
+ type LocaleSnapshot = {
55
+ localeCode: string
56
+ entities: Record<string, EntityTranslationsSchema>
57
+ }
58
+
59
+ function buildLocaleSnapshot(
76
60
  translations: HttpTypes.AdminTranslation[],
77
61
  references: { id: string; [key: string]: string }[],
78
- availableLocales: AdminStoreLocale[],
62
+ localeCode: string,
79
63
  translatableFields: string[]
80
- ): TranslationsFormSchema {
81
- const existingMap = new Map<string, HttpTypes.AdminTranslation>()
64
+ ): LocaleSnapshot {
65
+ const referenceTranslations = new Map<string, HttpTypes.AdminTranslation>()
82
66
  for (const t of translations) {
83
- existingMap.set(`${t.reference_id}:${t.locale_code}`, t)
67
+ if (t.locale_code === localeCode) {
68
+ referenceTranslations.set(t.reference_id, t)
69
+ }
84
70
  }
85
71
 
86
- const entitiesTranslationState: Record<string, EntityTranslationsSchema> = {}
87
-
88
- for (const reference of references) {
89
- const locales: Record<string, LocaleTranslationSchema> = {}
90
-
91
- for (const locale of availableLocales) {
92
- const key = `${reference.id}:${locale.locale_code}`
93
- const existing = existingMap.get(key)
94
-
95
- const fields: Record<string, string> = {}
96
- for (const fieldName of translatableFields) {
97
- const fieldValue = (existing?.translations?.[fieldName] as string) ?? ""
98
- fields[fieldName] = fieldValue
99
- }
72
+ const entities: Record<string, EntityTranslationsSchema> = {}
73
+ for (const ref of references) {
74
+ const existing = referenceTranslations.get(ref.id)
75
+ const fields: Record<string, string> = {}
100
76
 
101
- locales[locale.locale_code] = {
102
- id: existing?.id ?? null,
103
- locale_code: locale.locale_code,
104
- fields,
105
- }
77
+ for (const fieldName of translatableFields) {
78
+ fields[fieldName] = (existing?.translations?.[fieldName] as string) ?? ""
106
79
  }
107
80
 
108
- entitiesTranslationState[reference.id] = { locales }
81
+ entities[ref.id] = {
82
+ id: existing?.id ?? null,
83
+ fields,
84
+ }
109
85
  }
110
86
 
111
- return {
112
- entities: entitiesTranslationState,
113
- }
87
+ return { localeCode, entities }
114
88
  }
115
89
 
116
- function buildTranslationRows(
117
- references: { id: string; [key: string]: string }[],
90
+ function extendSnapshot(
91
+ snapshot: LocaleSnapshot,
92
+ translations: HttpTypes.AdminTranslation[],
93
+ newReferences: { id: string; [key: string]: string }[],
118
94
  translatableFields: string[]
119
- ): TranslationRow[] {
120
- return references.map((reference) => ({
121
- _type: "entity" as const,
122
- reference_id: reference.id,
123
- subRows: translatableFields.map((fieldName) => ({
124
- _type: "field" as const,
125
- reference_id: reference.id,
126
- field_name: fieldName,
127
- })),
128
- }))
129
- }
130
-
131
- function transformToBatchPayload(
132
- currentState: TranslationsFormSchema,
133
- initialState: TranslationsFormSchema,
134
- entityType: string
135
- ): Required<HttpTypes.AdminBatchTranslations> {
136
- const payload: Required<HttpTypes.AdminBatchTranslations> = {
137
- create: [],
138
- update: [],
139
- delete: [],
140
- }
141
-
142
- for (const [entityId, entityData] of Object.entries(currentState.entities)) {
143
- for (const [localeCode, localeTranslations] of Object.entries(
144
- entityData.locales
145
- )) {
146
- const initial = initialState.entities[entityId]?.locales[localeCode]
147
- const hasContent = Object.values(localeTranslations.fields).some(
148
- (v) => v !== undefined && v.trim() !== ""
149
- )
150
- const hadContent =
151
- initial &&
152
- Object.values(initial.fields).some(
153
- (v) => v !== undefined && v.trim() !== ""
154
- )
155
-
156
- if (!localeTranslations.id && hasContent) {
157
- payload.create.push({
158
- reference_id: entityId,
159
- reference: entityType,
160
- locale_code: localeTranslations.locale_code,
161
- translations: localeTranslations.fields,
162
- })
163
- } else if (localeTranslations.id && hasContent) {
164
- // UPDATE: Has ID and has content - check if changed
165
- const hasChanged =
166
- !initial ||
167
- JSON.stringify(localeTranslations.fields) !==
168
- JSON.stringify(initial.fields)
169
-
170
- if (hasChanged) {
171
- payload.update.push({
172
- id: localeTranslations.id,
173
- translations: localeTranslations.fields,
174
- })
175
- }
176
- } else if (localeTranslations.id && !hasContent && hadContent) {
177
- payload.delete.push(localeTranslations.id)
178
- }
95
+ ): LocaleSnapshot {
96
+ const referenceTranslations = new Map<string, HttpTypes.AdminTranslation>()
97
+ for (const t of translations) {
98
+ if (t.locale_code === snapshot.localeCode) {
99
+ referenceTranslations.set(t.reference_id, t)
179
100
  }
180
101
  }
181
102
 
182
- return payload
183
- }
103
+ const extendedEntities = { ...snapshot.entities }
184
104
 
185
- function hasLocaleChanges(
186
- currentState: TranslationsFormSchema,
187
- initialState: TranslationsFormSchema,
188
- localeCode: string
189
- ): boolean {
190
- for (const [entityId, entityData] of Object.entries(currentState.entities)) {
191
- const currentLocale = entityData.locales[localeCode]
192
- const initialLocale = initialState.entities[entityId]?.locales[localeCode]
193
-
194
- if (!currentLocale || !initialLocale) {
195
- continue
196
- }
105
+ for (const ref of newReferences) {
106
+ if (!extendedEntities[ref.id]) {
107
+ const existing = referenceTranslations.get(ref.id)
108
+ const fields: Record<string, string> = {}
197
109
 
198
- for (const [fieldName, fieldValue] of Object.entries(
199
- currentLocale.fields
200
- )) {
201
- const initialValue = initialLocale.fields[fieldName] ?? ""
202
- const currentValue = fieldValue ?? ""
110
+ for (const fieldName of translatableFields) {
111
+ fields[fieldName] =
112
+ (existing?.translations?.[fieldName] as string) ?? ""
113
+ }
203
114
 
204
- if (currentValue !== initialValue) {
205
- return true
115
+ extendedEntities[ref.id] = {
116
+ id: existing?.id ?? null,
117
+ fields,
206
118
  }
207
119
  }
208
120
  }
209
121
 
210
- return false
122
+ return { ...snapshot, entities: extendedEntities }
123
+ }
124
+
125
+ function snapshotToFormValues(
126
+ snapshot: LocaleSnapshot
127
+ ): TranslationsFormSchema {
128
+ return { entities: snapshot.entities }
129
+ }
130
+
131
+ type ChangeDetectionResult = {
132
+ hasChanges: boolean
133
+ payload: Required<HttpTypes.AdminBatchTranslations>
211
134
  }
212
135
 
213
- function transformSingleLocaleToBatchPayload(
136
+ function computeChanges(
214
137
  currentState: TranslationsFormSchema,
215
- initialState: TranslationsFormSchema,
138
+ snapshot: LocaleSnapshot,
216
139
  entityType: string,
217
140
  localeCode: string
218
- ): Required<HttpTypes.AdminBatchTranslations> {
141
+ ): ChangeDetectionResult {
219
142
  const payload: Required<HttpTypes.AdminBatchTranslations> = {
220
143
  create: [],
221
144
  update: [],
@@ -223,44 +146,43 @@ function transformSingleLocaleToBatchPayload(
223
146
  }
224
147
 
225
148
  for (const [entityId, entityData] of Object.entries(currentState.entities)) {
226
- const localeTranslations = entityData.locales[localeCode]
227
- if (!localeTranslations) continue
149
+ const baseline = snapshot.entities[entityId]
150
+ if (!baseline) {
151
+ continue
152
+ }
228
153
 
229
- const initial = initialState.entities[entityId]?.locales[localeCode]
230
- const hasContent = Object.values(localeTranslations.fields).some(
154
+ const hasContent = Object.values(entityData.fields).some(
231
155
  (v) => v !== undefined && v.trim() !== ""
232
156
  )
233
- const hadContent =
234
- initial &&
235
- Object.values(initial.fields).some(
236
- (v) => v !== undefined && v.trim() !== ""
237
- )
157
+ const hadContent = Object.values(baseline.fields).some(
158
+ (v) => v !== undefined && v.trim() !== ""
159
+ )
160
+ const hasChanged =
161
+ JSON.stringify(entityData.fields) !== JSON.stringify(baseline.fields)
238
162
 
239
- if (!localeTranslations.id && hasContent) {
163
+ if (!entityData.id && hasContent) {
240
164
  payload.create.push({
241
165
  reference_id: entityId,
242
166
  reference: entityType,
243
- locale_code: localeTranslations.locale_code,
244
- translations: localeTranslations.fields,
167
+ locale_code: localeCode,
168
+ translations: entityData.fields,
245
169
  })
246
- } else if (localeTranslations.id && hasContent) {
247
- const hasChanged =
248
- !initial ||
249
- JSON.stringify(localeTranslations.fields) !==
250
- JSON.stringify(initial.fields)
251
-
252
- if (hasChanged) {
253
- payload.update.push({
254
- id: localeTranslations.id,
255
- translations: localeTranslations.fields,
256
- })
257
- }
258
- } else if (localeTranslations.id && !hasContent && hadContent) {
259
- payload.delete.push(localeTranslations.id)
170
+ } else if (entityData.id && hasContent && hasChanged) {
171
+ payload.update.push({
172
+ id: entityData.id,
173
+ translations: entityData.fields,
174
+ })
175
+ } else if (entityData.id && !hasContent && hadContent) {
176
+ payload.delete.push(entityData.id)
260
177
  }
261
178
  }
262
179
 
263
- return payload
180
+ const hasChanges =
181
+ payload.create.length > 0 ||
182
+ payload.update.length > 0 ||
183
+ payload.delete.length > 0
184
+
185
+ return { hasChanges, payload }
264
186
  }
265
187
 
266
188
  const columnHelper = createDataGridHelper<
@@ -268,29 +190,42 @@ const columnHelper = createDataGridHelper<
268
190
  TranslationsFormSchema
269
191
  >()
270
192
 
271
- const FIELD_COLUMN_WIDTH = 150
193
+ const FIELD_COLUMN_WIDTH = 350
194
+
195
+ function buildTranslationRows(
196
+ references: { id: string; [key: string]: string }[],
197
+ translatableFields: string[]
198
+ ): TranslationRow[] {
199
+ return references.map((reference) => ({
200
+ _type: "entity" as const,
201
+ reference_id: reference.id,
202
+ subRows: translatableFields.map((fieldName) => ({
203
+ _type: "field" as const,
204
+ reference_id: reference.id,
205
+ field_name: fieldName,
206
+ })),
207
+ }))
208
+ }
272
209
 
273
210
  function useTranslationsGridColumns({
274
211
  entities,
275
- translatableFields,
276
212
  availableLocales,
277
213
  selectedLocale,
278
214
  dynamicColumnWidth,
279
215
  }: {
280
216
  entities: { id: string; [key: string]: string }[]
281
- translatableFields: string[]
282
217
  availableLocales: AdminStoreLocale[]
283
218
  selectedLocale: string
284
219
  dynamicColumnWidth: number
285
220
  }) {
286
221
  const { t } = useTranslation()
287
222
 
288
- const columns: ColumnDef<TranslationRow>[] = useMemo(() => {
223
+ return useMemo(() => {
289
224
  const selectedLocaleData = availableLocales.find(
290
225
  (l) => l.locale_code === selectedLocale
291
226
  )
292
227
 
293
- const baseColumns = [
228
+ const columns: ColumnDef<TranslationRow>[] = [
294
229
  columnHelper.column({
295
230
  id: "field",
296
231
  name: "field",
@@ -300,19 +235,21 @@ function useTranslationsGridColumns({
300
235
  const row = context.row.original
301
236
 
302
237
  if (isEntityRow(row)) {
303
- return (
304
- <DataGrid.ReadonlyCell context={context}></DataGrid.ReadonlyCell>
305
- )
238
+ return <DataGrid.ReadonlyCell context={context} />
306
239
  }
307
240
 
308
241
  return (
309
242
  <DataGrid.ReadonlyCell context={context} color="normal">
310
243
  <div className="flex h-full w-full items-center gap-x-2 overflow-hidden">
311
- <span className="truncate">
244
+ <Text
245
+ className="text-ui-fg-subtle truncate"
246
+ weight="plus"
247
+ size="small"
248
+ >
312
249
  {t(`fields.${row.field_name}`, {
313
250
  defaultValue: row.field_name,
314
251
  })}
315
- </span>
252
+ </Text>
316
253
  </div>
317
254
  </DataGrid.ReadonlyCell>
318
255
  )
@@ -323,27 +260,29 @@ function useTranslationsGridColumns({
323
260
  id: "original",
324
261
  name: "original",
325
262
  size: dynamicColumnWidth,
326
- header: t("general.original"),
263
+ header: () => (
264
+ <Text className="text-ui-fg-base" weight="plus" size="small">
265
+ {t("general.original")}
266
+ </Text>
267
+ ),
327
268
  disableHiding: true,
328
269
  cell: (context) => {
329
270
  const row = context.row.original
330
271
 
331
272
  if (isEntityRow(row)) {
332
- return (
333
- <DataGrid.ReadonlyCell context={context}></DataGrid.ReadonlyCell>
334
- )
273
+ return <DataGrid.ReadonlyCell context={context} />
335
274
  }
336
275
 
337
- const entity = entities.find(
338
- (entity) => entity.id === row.reference_id
339
- )
276
+ const entity = entities.find((e) => e.id === row.reference_id)
340
277
  if (!entity) {
341
278
  return null
342
279
  }
343
280
 
344
281
  return (
345
- <DataGrid.ReadonlyCell context={context} isMultiLine>
346
- {entity[row.field_name]}
282
+ <DataGrid.ReadonlyCell color="normal" context={context} isMultiLine>
283
+ <Text className="text-ui-fg-subtle" weight="plus" size="small">
284
+ {entity[row.field_name]}
285
+ </Text>
347
286
  </DataGrid.ReadonlyCell>
348
287
  )
349
288
  },
@@ -351,12 +290,16 @@ function useTranslationsGridColumns({
351
290
  ]
352
291
 
353
292
  if (selectedLocaleData) {
354
- baseColumns.push(
293
+ columns.push(
355
294
  columnHelper.column({
356
295
  id: selectedLocaleData.locale_code,
357
296
  name: selectedLocaleData.locale.name,
358
297
  size: dynamicColumnWidth,
359
- header: () => selectedLocaleData.locale.name,
298
+ header: () => (
299
+ <Text className="text-ui-fg-base" weight="plus" size="small">
300
+ {selectedLocaleData.locale.name}
301
+ </Text>
302
+ ),
360
303
  cell: (context) => {
361
304
  const row = context.row.original
362
305
 
@@ -364,7 +307,7 @@ function useTranslationsGridColumns({
364
307
  return <DataGrid.ReadonlyCell context={context} isMultiLine />
365
308
  }
366
309
 
367
- return <DataGrid.TextCell context={context} isMultiLine />
310
+ return <DataGrid.MultilineCell context={context} />
368
311
  },
369
312
  field: (context) => {
370
313
  const row = context.row.original
@@ -373,24 +316,15 @@ function useTranslationsGridColumns({
373
316
  return null
374
317
  }
375
318
 
376
- return `entities.${row.reference_id}.locales.${selectedLocaleData.locale_code}.fields.${row.field_name}`
319
+ return `entities.${row.reference_id}.fields.${row.field_name}`
377
320
  },
378
- type: "text",
321
+ type: "multiline-text",
379
322
  })
380
323
  )
381
324
  }
382
325
 
383
- return baseColumns
384
- }, [
385
- t,
386
- translatableFields,
387
- availableLocales,
388
- selectedLocale,
389
- entities,
390
- dynamicColumnWidth,
391
- ])
392
-
393
- return columns
326
+ return columns
327
+ }, [t, availableLocales, selectedLocale, entities, dynamicColumnWidth])
394
328
  }
395
329
 
396
330
  type TranslationsEditFormProps = {
@@ -426,7 +360,7 @@ export const TranslationsEditForm = ({
426
360
  const calculateColumnWidth = () => {
427
361
  if (containerRef.current) {
428
362
  const containerWidth = containerRef.current.offsetWidth
429
- const availableWidth = containerWidth - FIELD_COLUMN_WIDTH - 12
363
+ const availableWidth = containerWidth - FIELD_COLUMN_WIDTH - 16
430
364
  const columnWidth = Math.max(300, Math.floor(availableWidth / 2))
431
365
  setDynamicColumnWidth(columnWidth)
432
366
  }
@@ -445,74 +379,105 @@ export const TranslationsEditForm = ({
445
379
  const [selectedLocale, setSelectedLocale] = useState<string>(
446
380
  availableLocales[0]?.locale_code ?? ""
447
381
  )
448
-
449
382
  const [showUnsavedPrompt, setShowUnsavedPrompt] = useState(false)
450
383
  const [pendingLocale, setPendingLocale] = useState<string | null>(null)
384
+ const [isSwitchingLocale, setIsSwitchingLocale] = useState(false)
451
385
 
452
- const entities = useMemo(() => references, [references])
453
- const totalCount = useMemo(
454
- () => referenceCount * (translatableFields.length + 1),
455
- [referenceCount, translatableFields]
456
- )
457
-
458
- const initialState = useRef(
459
- initTranslationsFormState(
386
+ const snapshotRef = useRef<LocaleSnapshot>(
387
+ buildLocaleSnapshot(
460
388
  translations,
461
- entities,
462
- availableLocales,
389
+ references,
390
+ selectedLocale,
463
391
  translatableFields
464
392
  )
465
393
  )
466
394
 
395
+ const knownEntityIdsRef = useRef<Set<string>>(
396
+ new Set(references.map((r) => r.id))
397
+ )
398
+
399
+ const latestPropsRef = useRef({ translations, references })
400
+ useEffect(() => {
401
+ latestPropsRef.current = { translations, references }
402
+ }, [translations, references])
403
+
467
404
  const form = useForm<TranslationsFormSchema>({
468
405
  resolver: zodResolver(TranslationsFormSchema),
469
- defaultValues: initialState.current,
406
+ defaultValues: snapshotToFormValues(snapshotRef.current),
470
407
  })
471
408
 
472
- const rows = useMemo(
473
- () => buildTranslationRows(entities, translatableFields),
474
- [entities, translatableFields]
475
- )
409
+ useEffect(() => {
410
+ const currentIds = new Set(references.map((r) => r.id))
411
+ const newReferences = references.filter(
412
+ (r) => !knownEntityIdsRef.current.has(r.id)
413
+ )
476
414
 
477
- const { mutateAsync, isPending } = useBatchTranslations(entityType)
415
+ if (newReferences.length === 0) {
416
+ return
417
+ }
478
418
 
479
- const handleLocaleChange = useCallback(
480
- (newLocale: string) => {
481
- if (newLocale === selectedLocale) {
482
- return
483
- }
419
+ knownEntityIdsRef.current = currentIds
420
+ snapshotRef.current = extendSnapshot(
421
+ snapshotRef.current,
422
+ translations,
423
+ newReferences,
424
+ translatableFields
425
+ )
484
426
 
485
- const currentValues = form.getValues()
486
- const hasChanges = hasLocaleChanges(
487
- currentValues,
488
- initialState.current,
489
- selectedLocale
490
- )
427
+ const currentValues = form.getValues()
428
+ const newFormValues: TranslationsFormSchema = {
429
+ entities: { ...currentValues.entities },
430
+ }
491
431
 
492
- if (hasChanges) {
493
- setPendingLocale(newLocale)
494
- setShowUnsavedPrompt(true)
495
- } else {
496
- setSelectedLocale(newLocale)
432
+ for (const ref of newReferences) {
433
+ if (!newFormValues.entities[ref.id]) {
434
+ newFormValues.entities[ref.id] = snapshotRef.current.entities[ref.id]
497
435
  }
498
- },
499
- [selectedLocale, form]
436
+ }
437
+
438
+ form.reset(newFormValues, {
439
+ keepDirty: true,
440
+ keepDirtyValues: true,
441
+ })
442
+ }, [references, translations, translatableFields, form])
443
+
444
+ const rows = useMemo(
445
+ () => buildTranslationRows(references, translatableFields),
446
+ [references, translatableFields]
447
+ )
448
+
449
+ const totalRowCount = useMemo(
450
+ () => referenceCount * (translatableFields.length + 1),
451
+ [referenceCount, translatableFields]
452
+ )
453
+
454
+ const selectedLocaleDisplay = useMemo(
455
+ () =>
456
+ availableLocales.find((l) => l.locale_code === selectedLocale)?.locale
457
+ .name,
458
+ [availableLocales, selectedLocale]
500
459
  )
501
460
 
461
+ const columns = useTranslationsGridColumns({
462
+ entities: references,
463
+ availableLocales,
464
+ selectedLocale,
465
+ dynamicColumnWidth,
466
+ })
467
+
468
+ const { mutateAsync, isPending, invalidateQueries } =
469
+ useBatchTranslations(entityType)
470
+
502
471
  const saveCurrentLocale = useCallback(async () => {
503
472
  const currentValues = form.getValues()
504
- const payload = transformSingleLocaleToBatchPayload(
473
+ const { hasChanges, payload } = computeChanges(
505
474
  currentValues,
506
- initialState.current,
475
+ snapshotRef.current,
507
476
  entityType,
508
477
  selectedLocale
509
478
  )
510
479
 
511
- if (
512
- payload.create.length === 0 &&
513
- payload.update.length === 0 &&
514
- payload.delete.length === 0
515
- ) {
480
+ if (!hasChanges) {
516
481
  return true
517
482
  }
518
483
 
@@ -524,11 +489,13 @@ export const TranslationsEditForm = ({
524
489
 
525
490
  for (let i = 0; i < batchCount; i++) {
526
491
  let currentBatchAvailable = BATCH_SIZE
492
+
527
493
  const currentBatch: HttpTypes.AdminBatchTranslations = {
528
494
  create: [],
529
495
  update: [],
530
496
  delete: [],
531
497
  }
498
+
532
499
  if (payload.create.length > 0) {
533
500
  currentBatch.create = payload.create.splice(0, currentBatchAvailable)
534
501
  currentBatchAvailable -= currentBatch.create.length
@@ -539,22 +506,36 @@ export const TranslationsEditForm = ({
539
506
  }
540
507
  if (payload.delete.length > 0) {
541
508
  currentBatch.delete = payload.delete.splice(0, currentBatchAvailable)
542
- currentBatchAvailable -= currentBatch.delete.length
543
509
  }
544
510
 
545
- await mutateAsync(currentBatch)
511
+ const response = await mutateAsync(currentBatch, {
512
+ onError: (error) => {
513
+ toast.error(error.message)
514
+ },
515
+ })
516
+
517
+ if (response.created) {
518
+ for (const created of response.created) {
519
+ form.setValue(`entities.${created.reference_id}.id`, created.id, {
520
+ shouldDirty: false,
521
+ })
522
+ if (snapshotRef.current.entities[created.reference_id]) {
523
+ snapshotRef.current.entities[created.reference_id].id = created.id
524
+ }
525
+ }
526
+ }
546
527
  }
547
528
 
548
- const updatedInitialState = { ...initialState.current }
549
- for (const entityId of Object.keys(currentValues.entities)) {
550
- if (updatedInitialState.entities[entityId]?.locales[selectedLocale]) {
551
- updatedInitialState.entities[entityId].locales[selectedLocale] = {
552
- ...currentValues.entities[entityId].locales[selectedLocale],
529
+ const savedValues = form.getValues()
530
+ for (const entityId of Object.keys(savedValues.entities)) {
531
+ if (snapshotRef.current.entities[entityId]) {
532
+ snapshotRef.current.entities[entityId] = {
533
+ ...savedValues.entities[entityId],
553
534
  }
554
535
  }
555
536
  }
556
- initialState.current = updatedInitialState
557
- form.reset(currentValues)
537
+
538
+ form.reset(savedValues)
558
539
 
559
540
  return true
560
541
  } catch (error) {
@@ -565,19 +546,70 @@ export const TranslationsEditForm = ({
565
546
  }
566
547
  }, [form, entityType, selectedLocale, mutateAsync])
567
548
 
549
+ const switchToLocale = useCallback(
550
+ async (newLocale: string) => {
551
+ setIsSwitchingLocale(true)
552
+
553
+ try {
554
+ await invalidateQueries()
555
+
556
+ await new Promise((resolve) => requestAnimationFrame(resolve))
557
+
558
+ const { translations, references } = latestPropsRef.current
559
+
560
+ const newSnapshot = buildLocaleSnapshot(
561
+ translations,
562
+ references,
563
+ newLocale,
564
+ translatableFields
565
+ )
566
+
567
+ snapshotRef.current = newSnapshot
568
+ knownEntityIdsRef.current = new Set(references.map((r) => r.id))
569
+
570
+ form.reset(snapshotToFormValues(newSnapshot))
571
+
572
+ setSelectedLocale(newLocale)
573
+ } finally {
574
+ setIsSwitchingLocale(false)
575
+ }
576
+ },
577
+ [translatableFields, form, invalidateQueries]
578
+ )
579
+
580
+ const handleLocaleChange = useCallback(
581
+ (newLocale: string) => {
582
+ if (newLocale === selectedLocale) {
583
+ return
584
+ }
585
+
586
+ const currentValues = form.getValues()
587
+ const { hasChanges } = computeChanges(
588
+ currentValues,
589
+ snapshotRef.current,
590
+ entityType,
591
+ selectedLocale
592
+ )
593
+
594
+ if (hasChanges) {
595
+ setPendingLocale(newLocale)
596
+ setShowUnsavedPrompt(true)
597
+ } else {
598
+ switchToLocale(newLocale)
599
+ }
600
+ },
601
+ [selectedLocale, form, entityType, switchToLocale]
602
+ )
603
+
568
604
  const handleSaveAndSwitch = useCallback(async () => {
569
605
  const success = await saveCurrentLocale()
570
606
  if (success && pendingLocale) {
571
- toast.success(
572
- t("translations.edit.localeChangesSaved", {
573
- defaultValue: "Changes saved successfully",
574
- })
575
- )
576
- setSelectedLocale(pendingLocale)
607
+ toast.success(t("translations.edit.successToast"))
608
+ await switchToLocale(pendingLocale)
577
609
  }
578
610
  setShowUnsavedPrompt(false)
579
611
  setPendingLocale(null)
580
- }, [saveCurrentLocale, pendingLocale, t])
612
+ }, [saveCurrentLocale, pendingLocale, t, switchToLocale])
581
613
 
582
614
  const handleCancelSwitch = useCallback(() => {
583
615
  setShowUnsavedPrompt(false)
@@ -588,11 +620,7 @@ export const TranslationsEditForm = ({
588
620
  async (closeOnSuccess: boolean = false) => {
589
621
  const success = await saveCurrentLocale()
590
622
  if (success) {
591
- toast.success(
592
- t("translations.edit.successToast", {
593
- defaultValue: "Translations updated successfully",
594
- })
595
- )
623
+ toast.success(t("translations.edit.successToast"))
596
624
  if (closeOnSuccess) {
597
625
  handleSuccess()
598
626
  }
@@ -601,111 +629,23 @@ export const TranslationsEditForm = ({
601
629
  [saveCurrentLocale, t, handleSuccess]
602
630
  )
603
631
 
604
- const handleSubmit = form.handleSubmit(async (values) => {
605
- const payload = transformToBatchPayload(
606
- values,
607
- initialState.current,
608
- entityType
609
- )
610
-
611
- if (
612
- payload.create.length === 0 &&
613
- payload.update.length === 0 &&
614
- payload.delete.length === 0
615
- ) {
616
- toast.info(
617
- t("translations.noChanges", { defaultValue: "No changes to save" })
618
- )
619
- return
620
- }
621
-
622
- const BATCH_SIZE = 150
623
- const totalItems =
624
- payload.create.length + payload.update.length + payload.delete.length
625
- const batchCount = Math.ceil(totalItems / BATCH_SIZE)
626
-
627
- for (let i = 0; i < batchCount; i++) {
628
- let currentBatchAvailable = BATCH_SIZE
629
- const currentBatch: HttpTypes.AdminBatchTranslations = {
630
- create: [],
631
- update: [],
632
- delete: [],
633
- }
634
- if (payload.create.length > 0) {
635
- currentBatch.create = payload.create.splice(0, currentBatchAvailable)
636
- currentBatchAvailable -= currentBatch.create.length
637
- }
638
- if (payload.update.length > 0) {
639
- currentBatch.update = payload.update.splice(0, currentBatchAvailable)
640
- currentBatchAvailable -= currentBatch.update.length
641
- }
642
- if (payload.delete.length > 0) {
643
- currentBatch.delete = payload.delete.splice(0, currentBatchAvailable)
644
- currentBatchAvailable -= currentBatch.delete.length
645
- }
646
-
647
- await mutateAsync(currentBatch, {
648
- onSuccess: () => {
649
- if (i === batchCount - 1) {
650
- toast.success(
651
- t("translations.edit.successToast", {
652
- defaultValue: "Translations updated successfully",
653
- })
654
- )
655
- handleSuccess()
656
- }
657
- },
658
- onError: (error) => {
659
- toast.error(error.message)
660
- },
661
- })
662
- }
663
- })
664
-
665
- const columns = useTranslationsGridColumns({
666
- entities,
667
- translatableFields,
668
- availableLocales,
669
- selectedLocale,
670
- dynamicColumnWidth,
671
- })
632
+ const handleClose = useCallback(() => {
633
+ invalidateQueries()
634
+ }, [invalidateQueries])
672
635
 
673
- const selectedLocaleDisplay = availableLocales.find(
674
- (l) => l.locale_code === selectedLocale
675
- )?.locale.name
636
+ const isLoading = isPending || isSwitchingLocale
676
637
 
677
638
  return (
678
- <RouteFocusModal.Form form={form}>
639
+ <RouteFocusModal.Form form={form} onClose={handleClose}>
679
640
  <KeyboundForm
680
- onSubmit={handleSubmit}
641
+ onSubmit={() => handleSave(true)}
681
642
  className="flex h-full flex-col overflow-hidden"
682
643
  >
683
- <RouteFocusModal.Header>
684
- <div className="-my-2 flex w-full items-center justify-between border-l px-4">
685
- <Select
686
- value={selectedLocale}
687
- onValueChange={handleLocaleChange}
688
- size="small"
689
- >
690
- <Select.Trigger className="bg-ui-bg-base w-[200px]">
691
- <Select.Value>{selectedLocaleDisplay}</Select.Value>
692
- </Select.Trigger>
693
- <Select.Content>
694
- {availableLocales.map((locale) => (
695
- <Select.Item
696
- key={locale.locale_code}
697
- value={locale.locale_code}
698
- >
699
- {locale.locale.name}
700
- </Select.Item>
701
- ))}
702
- </Select.Content>
703
- </Select>
704
- </div>
705
- </RouteFocusModal.Header>
644
+ <RouteFocusModal.Header />
706
645
  <RouteFocusModal.Body className="size-full overflow-hidden">
707
646
  <div ref={containerRef} className="size-full">
708
647
  <DataGrid
648
+ showColumnsDropdown={false}
709
649
  columns={columns}
710
650
  data={rows}
711
651
  getSubRows={(row) => {
@@ -715,17 +655,44 @@ export const TranslationsEditForm = ({
715
655
  }}
716
656
  state={form}
717
657
  onEditingChange={(editing) => setCloseOnEscape(!editing)}
718
- totalRowCount={totalCount}
658
+ totalRowCount={totalRowCount}
719
659
  onFetchMore={fetchNextPage}
720
660
  isFetchingMore={isFetchingNextPage}
721
661
  hasNextPage={hasNextPage}
662
+ headerContent={
663
+ <Select
664
+ disabled={isLoading}
665
+ value={selectedLocale}
666
+ onValueChange={handleLocaleChange}
667
+ size="small"
668
+ >
669
+ <Select.Trigger className="bg-ui-bg-base w-[200px]">
670
+ <Select.Value>{selectedLocaleDisplay}</Select.Value>
671
+ </Select.Trigger>
672
+ <Select.Content>
673
+ {availableLocales.map((locale) => (
674
+ <Select.Item
675
+ key={locale.locale_code}
676
+ value={locale.locale_code}
677
+ >
678
+ {locale.locale.name}
679
+ </Select.Item>
680
+ ))}
681
+ </Select.Content>
682
+ </Select>
683
+ }
722
684
  />
723
685
  </div>
724
686
  </RouteFocusModal.Body>
725
687
  <RouteFocusModal.Footer>
726
688
  <div className="flex items-center justify-end gap-x-2">
727
689
  <RouteFocusModal.Close asChild>
728
- <Button size="small" variant="secondary">
690
+ <Button
691
+ type="button"
692
+ size="small"
693
+ variant="secondary"
694
+ isLoading={isLoading}
695
+ >
729
696
  {t("actions.cancel")}
730
697
  </Button>
731
698
  </RouteFocusModal.Close>
@@ -734,17 +701,12 @@ export const TranslationsEditForm = ({
734
701
  type="button"
735
702
  variant="secondary"
736
703
  onClick={() => handleSave(false)}
737
- isLoading={isPending}
704
+ isLoading={isLoading}
738
705
  >
739
- {t("actions.saveChanges", { defaultValue: "Save changes" })}
706
+ {t("actions.saveChanges")}
740
707
  </Button>
741
- <Button
742
- size="small"
743
- type="button"
744
- onClick={() => handleSave(true)}
745
- isLoading={isPending}
746
- >
747
- {t("actions.saveAndClose", { defaultValue: "Save and close" })}
708
+ <Button size="small" type="submit" isLoading={isLoading}>
709
+ {t("actions.saveAndClose")}
748
710
  </Button>
749
711
  </div>
750
712
  </RouteFocusModal.Footer>
@@ -754,15 +716,10 @@ export const TranslationsEditForm = ({
754
716
  <Prompt.Content>
755
717
  <Prompt.Header>
756
718
  <Prompt.Title>
757
- {t("translations.unsavedChanges.title", {
758
- defaultValue: "Unsaved changes",
759
- })}
719
+ {t("translations.edit.unsavedChanges.title")}
760
720
  </Prompt.Title>
761
721
  <Prompt.Description>
762
- {t("translations.unsavedChanges.description", {
763
- defaultValue:
764
- "You have unsaved changes for this locale. Save them before switching?",
765
- })}
722
+ {t("translations.edit.unsavedChanges.description")}
766
723
  </Prompt.Description>
767
724
  </Prompt.Header>
768
725
  <Prompt.Footer>
@@ -778,9 +735,9 @@ export const TranslationsEditForm = ({
778
735
  size="small"
779
736
  onClick={handleSaveAndSwitch}
780
737
  type="button"
781
- isLoading={isPending}
738
+ isLoading={isLoading}
782
739
  >
783
- {t("actions.saveChanges", { defaultValue: "Save changes" })}
740
+ {t("actions.saveChanges")}
784
741
  </Button>
785
742
  </Prompt.Footer>
786
743
  </Prompt.Content>