@medusajs/dashboard 3.0.0-snapshot-20251216145629 → 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 (290) 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 +26 -3
  9. package/dist/app.js +18504 -16698
  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-UULAOAFM.mjs → chunk-BJ44US4R.mjs} +358 -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 +73 -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-K4FGFORS.mjs → product-attributes-JTPGHUXZ.mjs} +13 -13
  116. package/dist/{product-create-7YTRHZQQ.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-32ZXNWOP.mjs → product-detail-ISHFZV7X.mjs} +45 -66
  119. package/dist/{product-edit-FBAKONMR.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-IUSVX22S.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-KECSHYZY.mjs → translations-edit-6P3KC4MO.mjs} +280 -304
  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 +257 -10
  219. package/src/i18n/translations/en.json +73 -5
  220. package/src/i18n/translations/es.json +9 -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 +182 -121
  272. package/src/routes/translations/translations-edit/components/translations-edit-form/translations-edit-form.tsx +330 -361
  273. package/src/routes/translations/translations-edit/translations-edit.tsx +8 -13
  274. package/dist/chunk-HGRIOEAR.mjs +0 -32
  275. package/dist/chunk-XDJ7OMBR.mjs +0 -160
  276. package/dist/login-VNOLI5YG.mjs +0 -201
  277. package/dist/product-create-option-7AOXAA4S.mjs +0 -151
  278. package/dist/product-edit-option-LWJT3CYJ.mjs +0 -156
  279. package/dist/translation-list-UF7FLXOW.mjs +0 -441
  280. package/src/routes/products/product-create-option/components/create-product-option-form/create-product-option-form.tsx +0 -122
  281. package/src/routes/products/product-create-option/components/create-product-option-form/index.ts +0 -1
  282. package/src/routes/products/product-create-option/index.ts +0 -1
  283. package/src/routes/products/product-create-option/product-create-option.tsx +0 -30
  284. package/src/routes/products/product-edit-option/components/edit-product-option-form/edit-product-option-form.tsx +0 -123
  285. package/src/routes/products/product-edit-option/components/edit-product-option-form/index.ts +0 -1
  286. package/src/routes/products/product-edit-option/index.ts +0 -1
  287. package/src/routes/products/product-edit-option/product-edit-option.tsx +0 -35
  288. package/dist/{chunk-FBYTX6K7.mjs → chunk-ORWRXJJT.mjs} +4 -4
  289. package/dist/{location-list-KVBA6J47.mjs → location-list-3WP65J3E.mjs} +1 -1
  290. 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
- }
184
-
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]
103
+ const extendedEntities = { ...snapshot.entities }
193
104
 
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 }
211
123
  }
212
124
 
213
- function transformSingleLocaleToBatchPayload(
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>
134
+ }
135
+
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 = {
@@ -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)
@@ -597,108 +629,23 @@ export const TranslationsEditForm = ({
597
629
  [saveCurrentLocale, t, handleSuccess]
598
630
  )
599
631
 
600
- const handleSubmit = form.handleSubmit(async (values) => {
601
- const payload = transformToBatchPayload(
602
- values,
603
- initialState.current,
604
- entityType
605
- )
632
+ const handleClose = useCallback(() => {
633
+ invalidateQueries()
634
+ }, [invalidateQueries])
606
635
 
607
- if (
608
- payload.create.length === 0 &&
609
- payload.update.length === 0 &&
610
- payload.delete.length === 0
611
- ) {
612
- return
613
- }
614
-
615
- const BATCH_SIZE = 150
616
- const totalItems =
617
- payload.create.length + payload.update.length + payload.delete.length
618
- const batchCount = Math.ceil(totalItems / BATCH_SIZE)
619
-
620
- for (let i = 0; i < batchCount; i++) {
621
- let currentBatchAvailable = BATCH_SIZE
622
- const currentBatch: HttpTypes.AdminBatchTranslations = {
623
- create: [],
624
- update: [],
625
- delete: [],
626
- }
627
- if (payload.create.length > 0) {
628
- currentBatch.create = payload.create.splice(0, currentBatchAvailable)
629
- currentBatchAvailable -= currentBatch.create.length
630
- }
631
- if (payload.update.length > 0) {
632
- currentBatch.update = payload.update.splice(0, currentBatchAvailable)
633
- currentBatchAvailable -= currentBatch.update.length
634
- }
635
- if (payload.delete.length > 0) {
636
- currentBatch.delete = payload.delete.splice(0, currentBatchAvailable)
637
- currentBatchAvailable -= currentBatch.delete.length
638
- }
639
-
640
- await mutateAsync(currentBatch, {
641
- onSuccess: () => {
642
- if (i === batchCount - 1) {
643
- toast.success(
644
- t("translations.edit.successToast", {
645
- defaultValue: "Translations updated successfully",
646
- })
647
- )
648
- handleSuccess()
649
- }
650
- },
651
- onError: (error) => {
652
- toast.error(error.message)
653
- },
654
- })
655
- }
656
- })
657
-
658
- const columns = useTranslationsGridColumns({
659
- entities,
660
- translatableFields,
661
- availableLocales,
662
- selectedLocale,
663
- dynamicColumnWidth,
664
- })
665
-
666
- const selectedLocaleDisplay = availableLocales.find(
667
- (l) => l.locale_code === selectedLocale
668
- )?.locale.name
636
+ const isLoading = isPending || isSwitchingLocale
669
637
 
670
638
  return (
671
- <RouteFocusModal.Form form={form}>
639
+ <RouteFocusModal.Form form={form} onClose={handleClose}>
672
640
  <KeyboundForm
673
- onSubmit={handleSubmit}
641
+ onSubmit={() => handleSave(true)}
674
642
  className="flex h-full flex-col overflow-hidden"
675
643
  >
676
- <RouteFocusModal.Header>
677
- <div className="-my-2 flex w-full items-center justify-between border-l px-4">
678
- <Select
679
- value={selectedLocale}
680
- onValueChange={handleLocaleChange}
681
- size="small"
682
- >
683
- <Select.Trigger className="bg-ui-bg-base w-[200px]">
684
- <Select.Value>{selectedLocaleDisplay}</Select.Value>
685
- </Select.Trigger>
686
- <Select.Content>
687
- {availableLocales.map((locale) => (
688
- <Select.Item
689
- key={locale.locale_code}
690
- value={locale.locale_code}
691
- >
692
- {locale.locale.name}
693
- </Select.Item>
694
- ))}
695
- </Select.Content>
696
- </Select>
697
- </div>
698
- </RouteFocusModal.Header>
644
+ <RouteFocusModal.Header />
699
645
  <RouteFocusModal.Body className="size-full overflow-hidden">
700
646
  <div ref={containerRef} className="size-full">
701
647
  <DataGrid
648
+ showColumnsDropdown={false}
702
649
  columns={columns}
703
650
  data={rows}
704
651
  getSubRows={(row) => {
@@ -708,17 +655,44 @@ export const TranslationsEditForm = ({
708
655
  }}
709
656
  state={form}
710
657
  onEditingChange={(editing) => setCloseOnEscape(!editing)}
711
- totalRowCount={totalCount}
658
+ totalRowCount={totalRowCount}
712
659
  onFetchMore={fetchNextPage}
713
660
  isFetchingMore={isFetchingNextPage}
714
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
+ }
715
684
  />
716
685
  </div>
717
686
  </RouteFocusModal.Body>
718
687
  <RouteFocusModal.Footer>
719
688
  <div className="flex items-center justify-end gap-x-2">
720
689
  <RouteFocusModal.Close asChild>
721
- <Button size="small" variant="secondary">
690
+ <Button
691
+ type="button"
692
+ size="small"
693
+ variant="secondary"
694
+ isLoading={isLoading}
695
+ >
722
696
  {t("actions.cancel")}
723
697
  </Button>
724
698
  </RouteFocusModal.Close>
@@ -727,16 +701,11 @@ export const TranslationsEditForm = ({
727
701
  type="button"
728
702
  variant="secondary"
729
703
  onClick={() => handleSave(false)}
730
- isLoading={isPending}
704
+ isLoading={isLoading}
731
705
  >
732
706
  {t("actions.saveChanges")}
733
707
  </Button>
734
- <Button
735
- size="small"
736
- type="button"
737
- onClick={() => handleSave(true)}
738
- isLoading={isPending}
739
- >
708
+ <Button size="small" type="submit" isLoading={isLoading}>
740
709
  {t("actions.saveAndClose")}
741
710
  </Button>
742
711
  </div>
@@ -766,7 +735,7 @@ export const TranslationsEditForm = ({
766
735
  size="small"
767
736
  onClick={handleSaveAndSwitch}
768
737
  type="button"
769
- isLoading={isPending}
738
+ isLoading={isLoading}
770
739
  >
771
740
  {t("actions.saveChanges")}
772
741
  </Button>