@dainamite/cpq 0.5.0 → 0.7.0

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 (368) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/MIGRATION.md +74 -0
  3. package/dist/modules/cpq/acl.js +1 -0
  4. package/dist/modules/cpq/acl.js.map +2 -2
  5. package/dist/modules/cpq/api/_helpers/adjustmentCommandRoute.js +45 -0
  6. package/dist/modules/cpq/api/_helpers/adjustmentCommandRoute.js.map +7 -0
  7. package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js +23 -0
  8. package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js.map +7 -0
  9. package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js +19 -0
  10. package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js.map +7 -0
  11. package/dist/modules/cpq/api/orders/[orderId]/route.js +17 -1
  12. package/dist/modules/cpq/api/orders/[orderId]/route.js.map +2 -2
  13. package/dist/modules/cpq/api/orders/route.js +47 -2
  14. package/dist/modules/cpq/api/orders/route.js.map +2 -2
  15. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js +54 -0
  16. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js.map +7 -0
  17. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js +42 -2
  18. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js.map +2 -2
  19. package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js +27 -2
  20. package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js.map +2 -2
  21. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js +47 -0
  22. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js.map +7 -0
  23. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js +30 -0
  24. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js.map +7 -0
  25. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js +47 -0
  26. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js.map +7 -0
  27. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js +32 -2
  28. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js.map +2 -2
  29. package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js +33 -2
  30. package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js.map +2 -2
  31. package/dist/modules/cpq/api/quotes/price/route.js +43 -3
  32. package/dist/modules/cpq/api/quotes/price/route.js.map +2 -2
  33. package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js +3 -9
  34. package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +2 -2
  35. package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js +21 -25
  36. package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +2 -2
  37. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js +48 -47
  38. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js.map +2 -2
  39. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js +1 -1
  40. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js.map +2 -2
  41. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +3 -3
  42. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +2 -2
  43. package/dist/modules/cpq/backend/cpq/inventory/shared.js +37 -2
  44. package/dist/modules/cpq/backend/cpq/inventory/shared.js.map +2 -2
  45. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +174 -184
  46. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +2 -2
  47. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js +1 -1
  48. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js.map +2 -2
  49. package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js +168 -143
  50. package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js.map +2 -2
  51. package/dist/modules/cpq/backend/cpq/offerings/page.js +36 -34
  52. package/dist/modules/cpq/backend/cpq/offerings/page.js.map +2 -2
  53. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +140 -280
  54. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +2 -2
  55. package/dist/modules/cpq/backend/cpq/orders/page.js +6 -6
  56. package/dist/modules/cpq/backend/cpq/orders/page.js.map +2 -2
  57. package/dist/modules/cpq/backend/cpq/orders/page.meta.js +1 -1
  58. package/dist/modules/cpq/backend/cpq/orders/page.meta.js.map +1 -1
  59. package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js +41 -36
  60. package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js.map +2 -2
  61. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js +76 -72
  62. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js.map +2 -2
  63. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js +1 -1
  64. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js.map +2 -2
  65. package/dist/modules/cpq/backend/cpq/price-rules/page.js +18 -15
  66. package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +2 -2
  67. package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js +23 -21
  68. package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js.map +2 -2
  69. package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js +22 -50
  70. package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js.map +2 -2
  71. package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js +74 -52
  72. package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js.map +2 -2
  73. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js +20 -17
  74. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js.map +2 -2
  75. package/dist/modules/cpq/backend/cpq/pricing/page.js +1 -1
  76. package/dist/modules/cpq/backend/cpq/pricing/page.js.map +2 -2
  77. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js +115 -123
  78. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js.map +2 -2
  79. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js +208 -0
  80. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js.map +7 -0
  81. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js +79 -0
  82. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js.map +7 -0
  83. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js +53 -0
  84. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js.map +7 -0
  85. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js +107 -0
  86. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js.map +7 -0
  87. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js +130 -0
  88. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js.map +7 -0
  89. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +269 -311
  90. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +3 -3
  91. package/dist/modules/cpq/backend/cpq/quotes/page.js +23 -19
  92. package/dist/modules/cpq/backend/cpq/quotes/page.js.map +2 -2
  93. package/dist/modules/cpq/backend/cpq/quotes/page.meta.js +1 -1
  94. package/dist/modules/cpq/backend/cpq/quotes/page.meta.js.map +1 -1
  95. package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js +19 -15
  96. package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js.map +2 -2
  97. package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js +171 -178
  98. package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js.map +2 -2
  99. package/dist/modules/cpq/backend/cpq/specifications/page.js +2 -2
  100. package/dist/modules/cpq/backend/cpq/specifications/page.js.map +2 -2
  101. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js +60 -50
  102. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js.map +2 -2
  103. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js +22 -18
  104. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js.map +2 -2
  105. package/dist/modules/cpq/backend/cpq/wizards/page.js +1 -1
  106. package/dist/modules/cpq/backend/cpq/wizards/page.js.map +2 -2
  107. package/dist/modules/cpq/ce.js +8 -0
  108. package/dist/modules/cpq/ce.js.map +2 -2
  109. package/dist/modules/cpq/cli.js +34 -1
  110. package/dist/modules/cpq/cli.js.map +2 -2
  111. package/dist/modules/cpq/commands/price-adjustments.js +107 -0
  112. package/dist/modules/cpq/commands/price-adjustments.js.map +7 -0
  113. package/dist/modules/cpq/components/ArcLineDiff.js +53 -0
  114. package/dist/modules/cpq/components/ArcLineDiff.js.map +7 -0
  115. package/dist/modules/cpq/components/ChargeBreakdownTable.js +148 -0
  116. package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +7 -0
  117. package/dist/modules/cpq/components/PriceWaterfall.js +68 -0
  118. package/dist/modules/cpq/components/PriceWaterfall.js.map +7 -0
  119. package/dist/modules/cpq/components/PricingSummaryCard.js +112 -0
  120. package/dist/modules/cpq/components/PricingSummaryCard.js.map +7 -0
  121. package/dist/modules/cpq/components/StatusPath.js +5 -3
  122. package/dist/modules/cpq/components/StatusPath.js.map +2 -2
  123. package/dist/modules/cpq/components/useCpqRowActions.js +10 -22
  124. package/dist/modules/cpq/components/useCpqRowActions.js.map +2 -2
  125. package/dist/modules/cpq/components/useGrantedFeatures.js +27 -0
  126. package/dist/modules/cpq/components/useGrantedFeatures.js.map +7 -0
  127. package/dist/modules/cpq/data/entities.js +97 -0
  128. package/dist/modules/cpq/data/entities.js.map +2 -2
  129. package/dist/modules/cpq/data/price-adjustments.js +70 -0
  130. package/dist/modules/cpq/data/price-adjustments.js.map +7 -0
  131. package/dist/modules/cpq/data/validators.js +4 -0
  132. package/dist/modules/cpq/data/validators.js.map +2 -2
  133. package/dist/modules/cpq/di.js +6 -0
  134. package/dist/modules/cpq/di.js.map +2 -2
  135. package/dist/modules/cpq/events.js +47 -0
  136. package/dist/modules/cpq/events.js.map +2 -2
  137. package/dist/modules/cpq/i18n/de.json +1220 -0
  138. package/dist/modules/cpq/i18n/en.json +1220 -0
  139. package/dist/modules/cpq/i18n/es.json +1220 -0
  140. package/dist/modules/cpq/i18n/pl.json +1220 -0
  141. package/dist/modules/cpq/index.js +1 -1
  142. package/dist/modules/cpq/index.js.map +2 -2
  143. package/dist/modules/cpq/lib/auditActor.js +10 -0
  144. package/dist/modules/cpq/lib/auditActor.js.map +7 -0
  145. package/dist/modules/cpq/lib/formatMoney.js +7 -0
  146. package/dist/modules/cpq/lib/formatMoney.js.map +7 -0
  147. package/dist/modules/cpq/lib/priceAdjustmentDefaults.js +60 -0
  148. package/dist/modules/cpq/lib/priceAdjustmentDefaults.js.map +7 -0
  149. package/dist/modules/cpq/lib/priceAdjustmentView.js +169 -0
  150. package/dist/modules/cpq/lib/priceAdjustmentView.js.map +7 -0
  151. package/dist/modules/cpq/lib/quotePermissions.js +13 -0
  152. package/dist/modules/cpq/lib/quotePermissions.js.map +7 -0
  153. package/dist/modules/cpq/lib/quoteSummaryView.js +28 -0
  154. package/dist/modules/cpq/lib/quoteSummaryView.js.map +7 -0
  155. package/dist/modules/cpq/migrations/Migration20261110213052.js +22 -0
  156. package/dist/modules/cpq/migrations/Migration20261110213052.js.map +7 -0
  157. package/dist/modules/cpq/services/arcLineRemoval.js +53 -0
  158. package/dist/modules/cpq/services/arcLineRemoval.js.map +7 -0
  159. package/dist/modules/cpq/services/arcPriceFreeze.js +79 -0
  160. package/dist/modules/cpq/services/arcPriceFreeze.js.map +7 -0
  161. package/dist/modules/cpq/services/cpqOrderService.js +25 -28
  162. package/dist/modules/cpq/services/cpqOrderService.js.map +2 -2
  163. package/dist/modules/cpq/services/cpqPricingService.js +7 -0
  164. package/dist/modules/cpq/services/cpqPricingService.js.map +2 -2
  165. package/dist/modules/cpq/services/cpqQuotingService.js +330 -109
  166. package/dist/modules/cpq/services/cpqQuotingService.js.map +2 -2
  167. package/dist/modules/cpq/services/orderPricingSummary.js +26 -0
  168. package/dist/modules/cpq/services/orderPricingSummary.js.map +7 -0
  169. package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js +264 -0
  170. package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js.map +7 -0
  171. package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js +25 -0
  172. package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js.map +7 -0
  173. package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js +38 -0
  174. package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js.map +7 -0
  175. package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js +104 -0
  176. package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js.map +7 -0
  177. package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js +102 -0
  178. package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js.map +7 -0
  179. package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js +52 -0
  180. package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js.map +7 -0
  181. package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js +37 -0
  182. package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js.map +7 -0
  183. package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js +107 -0
  184. package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js.map +7 -0
  185. package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js +30 -0
  186. package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js.map +7 -0
  187. package/dist/modules/cpq/services/types.js.map +2 -2
  188. package/dist/modules/cpq/setup.js +6 -0
  189. package/dist/modules/cpq/setup.js.map +2 -2
  190. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js +39 -31
  191. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js.map +2 -2
  192. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js +13 -5
  193. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js.map +2 -2
  194. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js +41 -21
  195. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js.map +2 -2
  196. package/dist/modules/cpq/widgets/injection-table.js +2 -2
  197. package/dist/modules/cpq/widgets/injection-table.js.map +2 -2
  198. package/dist/modules/cpq/workflows/WizardRunner.js +10 -8
  199. package/dist/modules/cpq/workflows/WizardRunner.js.map +2 -2
  200. package/dist/modules/cpq/workflows/WizardTrigger.js +15 -9
  201. package/dist/modules/cpq/workflows/WizardTrigger.js.map +2 -2
  202. package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js +9 -7
  203. package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js.map +2 -2
  204. package/dist/modules/cpq/workflows/steps/InventorySelectStep.js +9 -4
  205. package/dist/modules/cpq/workflows/steps/InventorySelectStep.js.map +2 -2
  206. package/dist/modules/cpq/workflows/steps/ItemListStep.js +16 -19
  207. package/dist/modules/cpq/workflows/steps/ItemListStep.js.map +2 -2
  208. package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js +19 -32
  209. package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js.map +2 -2
  210. package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js +8 -6
  211. package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js.map +2 -2
  212. package/dist/modules/cpq/workflows/steps/ReviewStep.js +32 -30
  213. package/dist/modules/cpq/workflows/steps/ReviewStep.js.map +2 -2
  214. package/dist/modules/cpq/workflows/steps/index.js +12 -0
  215. package/dist/modules/cpq/workflows/steps/index.js.map +2 -2
  216. package/package.json +1 -1
  217. package/src/modules/cpq/__integration__/TC-CPQ-ARC-012.spec.ts +134 -0
  218. package/src/modules/cpq/__tests__/i18n/dictionaries.test.ts +62 -0
  219. package/src/modules/cpq/__tests__/i18n/hardcodedStrings.test.ts +35 -0
  220. package/src/modules/cpq/__tests__/i18n/sourceScan.ts +109 -0
  221. package/src/modules/cpq/acl.ts +1 -0
  222. package/src/modules/cpq/api/__tests__/adjustmentRoutes.test.ts +79 -0
  223. package/src/modules/cpq/api/__tests__/quoteWriteGuards.test.ts +100 -0
  224. package/src/modules/cpq/api/_helpers/__tests__/quoteMutationGuard.test.ts +48 -0
  225. package/src/modules/cpq/api/_helpers/adjustmentCommandRoute.ts +45 -0
  226. package/src/modules/cpq/api/_helpers/quoteMutationGuard.ts +38 -0
  227. package/src/modules/cpq/api/_helpers/quoteRouteOpenApi.ts +19 -0
  228. package/src/modules/cpq/api/orders/[orderId]/route.ts +18 -0
  229. package/src/modules/cpq/api/orders/route.ts +51 -1
  230. package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.ts +56 -0
  231. package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.ts +48 -1
  232. package/src/modules/cpq/api/quotes/[quoteId]/items/route.ts +31 -1
  233. package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.ts +39 -0
  234. package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.ts +24 -0
  235. package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.ts +40 -0
  236. package/src/modules/cpq/api/quotes/[quoteId]/recalculate/__tests__/route.test.ts +95 -0
  237. package/src/modules/cpq/api/quotes/[quoteId]/recalculate/route.ts +40 -1
  238. package/src/modules/cpq/api/quotes/[quoteId]/status/__tests__/route.test.ts +74 -0
  239. package/src/modules/cpq/api/quotes/[quoteId]/status/route.ts +37 -1
  240. package/src/modules/cpq/api/quotes/price/__tests__/route.test.ts +37 -0
  241. package/src/modules/cpq/api/quotes/price/route.ts +28 -2
  242. package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +3 -9
  243. package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +22 -26
  244. package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.ts +1 -1
  245. package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.tsx +43 -46
  246. package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +3 -3
  247. package/src/modules/cpq/backend/cpq/inventory/shared.tsx +37 -1
  248. package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.ts +1 -1
  249. package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +164 -120
  250. package/src/modules/cpq/backend/cpq/offerings/[id]/page.tsx +169 -130
  251. package/src/modules/cpq/backend/cpq/offerings/page.tsx +34 -24
  252. package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +122 -308
  253. package/src/modules/cpq/backend/cpq/orders/page.meta.ts +1 -1
  254. package/src/modules/cpq/backend/cpq/orders/page.tsx +6 -6
  255. package/src/modules/cpq/backend/cpq/price-rules/PriceRuleForm.tsx +48 -34
  256. package/src/modules/cpq/backend/cpq/price-rules/[id]/page.meta.ts +1 -1
  257. package/src/modules/cpq/backend/cpq/price-rules/[id]/page.tsx +76 -70
  258. package/src/modules/cpq/backend/cpq/price-rules/page.tsx +25 -16
  259. package/src/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.tsx +24 -18
  260. package/src/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.tsx +29 -19
  261. package/src/modules/cpq/backend/cpq/pricing/[id]/page.tsx +95 -47
  262. package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.ts +23 -15
  263. package/src/modules/cpq/backend/cpq/pricing/page.tsx +1 -1
  264. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.tsx +111 -88
  265. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.tsx +260 -0
  266. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.tsx +124 -0
  267. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/__tests__/PriceAdjustmentsPanel.test.tsx +76 -0
  268. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.tsx +76 -0
  269. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.tsx +133 -0
  270. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.tsx +165 -0
  271. package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +304 -348
  272. package/src/modules/cpq/backend/cpq/quotes/page.meta.ts +1 -1
  273. package/src/modules/cpq/backend/cpq/quotes/page.tsx +29 -21
  274. package/src/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.tsx +19 -15
  275. package/src/modules/cpq/backend/cpq/specifications/[id]/page.tsx +171 -171
  276. package/src/modules/cpq/backend/cpq/specifications/page.tsx +2 -2
  277. package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.tsx +51 -41
  278. package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/page.tsx +17 -13
  279. package/src/modules/cpq/backend/cpq/wizards/page.tsx +1 -1
  280. package/src/modules/cpq/ce.ts +8 -0
  281. package/src/modules/cpq/cli.ts +48 -1
  282. package/src/modules/cpq/commands/__tests__/price-adjustments.test.ts +54 -0
  283. package/src/modules/cpq/commands/price-adjustments.ts +90 -0
  284. package/src/modules/cpq/components/ArcLineDiff.tsx +87 -0
  285. package/src/modules/cpq/components/ChargeBreakdownTable.tsx +203 -0
  286. package/src/modules/cpq/components/PriceWaterfall.tsx +96 -0
  287. package/src/modules/cpq/components/PricingSummaryCard.tsx +128 -0
  288. package/src/modules/cpq/components/StatusPath.tsx +7 -4
  289. package/src/modules/cpq/components/__tests__/PriceWaterfall.test.tsx +38 -0
  290. package/src/modules/cpq/components/useCpqRowActions.tsx +17 -25
  291. package/src/modules/cpq/components/useGrantedFeatures.ts +35 -0
  292. package/src/modules/cpq/data/__tests__/price-adjustment-migration.test.ts +11 -0
  293. package/src/modules/cpq/data/__tests__/price-adjustments.test.ts +26 -0
  294. package/src/modules/cpq/data/entities.ts +83 -0
  295. package/src/modules/cpq/data/price-adjustments.ts +67 -0
  296. package/src/modules/cpq/data/validators.ts +3 -0
  297. package/src/modules/cpq/di.ts +6 -0
  298. package/src/modules/cpq/events.ts +53 -2
  299. package/src/modules/cpq/i18n/de.json +1220 -0
  300. package/src/modules/cpq/i18n/en.json +1220 -0
  301. package/src/modules/cpq/i18n/es.json +1220 -0
  302. package/src/modules/cpq/i18n/pl.json +1220 -0
  303. package/src/modules/cpq/index.ts +1 -1
  304. package/src/modules/cpq/lib/__tests__/auditActor.test.ts +12 -0
  305. package/src/modules/cpq/lib/__tests__/priceAdjustmentDefaults.test.ts +37 -0
  306. package/src/modules/cpq/lib/__tests__/priceAdjustmentView.test.ts +152 -0
  307. package/src/modules/cpq/lib/__tests__/quotePermissions.test.ts +30 -0
  308. package/src/modules/cpq/lib/__tests__/quoteSummaryView.test.ts +56 -0
  309. package/src/modules/cpq/lib/auditActor.ts +13 -0
  310. package/src/modules/cpq/lib/formatMoney.ts +4 -0
  311. package/src/modules/cpq/lib/priceAdjustmentDefaults.ts +36 -0
  312. package/src/modules/cpq/lib/priceAdjustmentView.ts +259 -0
  313. package/src/modules/cpq/lib/quotePermissions.ts +22 -0
  314. package/src/modules/cpq/lib/quoteSummaryView.ts +51 -0
  315. package/src/modules/cpq/migrations/.snapshot-open-mercato.json +3298 -2423
  316. package/src/modules/cpq/migrations/Migration20261110213052.ts +22 -0
  317. package/src/modules/cpq/services/__tests__/_helpers/mockEm.ts +9 -2
  318. package/src/modules/cpq/services/__tests__/adjustmentService.test.ts +183 -0
  319. package/src/modules/cpq/services/__tests__/arcLineRemoval.test.ts +109 -0
  320. package/src/modules/cpq/services/__tests__/arcPriceFreeze.test.ts +141 -0
  321. package/src/modules/cpq/services/__tests__/chargeWaterfall.test.ts +28 -0
  322. package/src/modules/cpq/services/__tests__/cpqOrderService.test.ts +35 -2
  323. package/src/modules/cpq/services/__tests__/cpqPricingService.test.ts +57 -0
  324. package/src/modules/cpq/services/__tests__/cpqQuotingService.arc.test.ts +237 -0
  325. package/src/modules/cpq/services/__tests__/cpqQuotingService.test.ts +58 -3
  326. package/src/modules/cpq/services/__tests__/orderPricingSummary.test.ts +50 -0
  327. package/src/modules/cpq/services/__tests__/pricingSnapshot.test.ts +62 -0
  328. package/src/modules/cpq/services/__tests__/quoteAdjustmentLifecycle.test.ts +35 -0
  329. package/src/modules/cpq/services/__tests__/quoteAdjustmentPricing.test.ts +65 -0
  330. package/src/modules/cpq/services/__tests__/quoteCloneAdjustments.test.ts +45 -0
  331. package/src/modules/cpq/services/__tests__/quoteDetachTargetAdjustments.test.ts +54 -0
  332. package/src/modules/cpq/services/__tests__/quoteEventsService.test.ts +72 -0
  333. package/src/modules/cpq/services/__tests__/quotePricingEvents.test.ts +126 -0
  334. package/src/modules/cpq/services/__tests__/quotePricingIsolation.test.ts +52 -0
  335. package/src/modules/cpq/services/__tests__/quotePricingWrite.test.ts +74 -0
  336. package/src/modules/cpq/services/__tests__/resolveManualAdjustments.test.ts +90 -0
  337. package/src/modules/cpq/services/__tests__/storedAdjustmentFailure.test.ts +9 -0
  338. package/src/modules/cpq/services/arcLineRemoval.ts +87 -0
  339. package/src/modules/cpq/services/arcPriceFreeze.ts +165 -0
  340. package/src/modules/cpq/services/cpqOrderService.ts +22 -28
  341. package/src/modules/cpq/services/cpqPricingService.ts +8 -0
  342. package/src/modules/cpq/services/cpqQuotingService.ts +374 -125
  343. package/src/modules/cpq/services/orderPricingSummary.ts +47 -0
  344. package/src/modules/cpq/services/priceAdjustments/adjustmentService.ts +249 -0
  345. package/src/modules/cpq/services/priceAdjustments/chargeWaterfall.ts +28 -0
  346. package/src/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.ts +25 -0
  347. package/src/modules/cpq/services/priceAdjustments/pricingSnapshot.ts +86 -0
  348. package/src/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.ts +68 -0
  349. package/src/modules/cpq/services/priceAdjustments/quotePricingEvents.ts +94 -0
  350. package/src/modules/cpq/services/priceAdjustments/quotePricingWrite.ts +51 -0
  351. package/src/modules/cpq/services/priceAdjustments/resolveManualAdjustments.ts +116 -0
  352. package/src/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.ts +31 -0
  353. package/src/modules/cpq/services/types.ts +34 -1
  354. package/src/modules/cpq/setup.ts +7 -1
  355. package/src/modules/cpq/widgets/injection/customer-inventory/widget.client.tsx +38 -31
  356. package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.client.tsx +7 -5
  357. package/src/modules/cpq/widgets/injection/quote-configurator/widget.client.tsx +36 -21
  358. package/src/modules/cpq/widgets/injection-table.ts +8 -2
  359. package/src/modules/cpq/workflows/WizardRunner.tsx +8 -5
  360. package/src/modules/cpq/workflows/WizardTrigger.tsx +15 -9
  361. package/src/modules/cpq/workflows/steps/CustomerSelectStep.tsx +11 -7
  362. package/src/modules/cpq/workflows/steps/InventorySelectStep.tsx +9 -5
  363. package/src/modules/cpq/workflows/steps/ItemListStep.tsx +18 -12
  364. package/src/modules/cpq/workflows/steps/OfferingSelectStep.tsx +20 -16
  365. package/src/modules/cpq/workflows/steps/ProductConfigureStep.tsx +8 -6
  366. package/src/modules/cpq/workflows/steps/ReviewStep.tsx +33 -18
  367. package/src/modules/cpq/workflows/steps/index.ts +12 -0
  368. package/src/modules/cpq/workflows/types.ts +4 -0
@@ -2,23 +2,36 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { useRouter, useParams } from "next/navigation";
5
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
5
6
  import { Alert } from "@open-mercato/ui/primitives/alert";
6
7
  import { Button } from "@open-mercato/ui/primitives/button";
7
8
  import { Tag } from "@open-mercato/ui/primitives/tag";
8
9
  import { FormHeader } from "@open-mercato/ui/backend/forms";
9
10
  import {
10
- formatStatusLabel,
11
11
  subscriptionStatusMap
12
12
  } from "../../../../../components/statusMaps.js";
13
13
  import { StatusPath } from "../../../../../components/StatusPath.js";
14
- const CHANGE_TYPE_LABELS = {
15
- created: "Created",
16
- amend: "Amended",
17
- renew: "Renewed",
18
- cancel: "Cancelled",
19
- "merge-result": "Created from merging contracts",
20
- "merge-source": "Merged into a new contract"
21
- };
14
+ import { DepthBadge, PriceWaterfall } from "../../../../../components/PriceWaterfall.js";
15
+ import { parseStoredCharge } from "../../../../../lib/priceAdjustmentView.js";
16
+ import { buildInventoryStatusLabels, inventoryStatusLabel } from "../../shared.js";
17
+ function changeTypeLabel(t, changeType) {
18
+ switch (changeType) {
19
+ case "created":
20
+ return t("cpq.inventory.subscriptions.detail.changeType.created", "Created");
21
+ case "amend":
22
+ return t("cpq.inventory.subscriptions.detail.changeType.amend", "Amended");
23
+ case "renew":
24
+ return t("cpq.inventory.subscriptions.detail.changeType.renew", "Renewed");
25
+ case "cancel":
26
+ return t("cpq.inventory.subscriptions.detail.changeType.cancel", "Cancelled");
27
+ case "merge-result":
28
+ return t("cpq.inventory.subscriptions.detail.changeType.mergeResult", "Created from merging contracts");
29
+ case "merge-source":
30
+ return t("cpq.inventory.subscriptions.detail.changeType.mergeSource", "Merged into a new contract");
31
+ default:
32
+ return changeType;
33
+ }
34
+ }
22
35
  const CHANGE_TYPE_VARIANTS = {
23
36
  created: "neutral",
24
37
  amend: "info",
@@ -34,20 +47,16 @@ const ALLOWED_TRANSITIONS = {
34
47
  terminated: [],
35
48
  expired: []
36
49
  };
37
- const STATUS_LABELS = {
38
- pending: "Pending",
39
- active: "Active",
40
- suspended: "Suspended",
41
- terminated: "Terminated",
42
- expired: "Expired"
43
- };
50
+ const SUBSCRIPTION_STATUSES = ["pending", "active", "suspended", "terminated", "expired"];
44
51
  function fmt(amount, currency) {
45
52
  return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(Number(amount));
46
53
  }
47
54
  function StatusBadge({ status }) {
48
- return /* @__PURE__ */ jsx(Tag, { variant: subscriptionStatusMap[status] ?? "neutral", dot: true, children: formatStatusLabel(status) });
55
+ const t = useT();
56
+ return /* @__PURE__ */ jsx(Tag, { variant: subscriptionStatusMap[status] ?? "neutral", dot: true, children: inventoryStatusLabel(t, status) });
49
57
  }
50
58
  function SubscriptionDetailPage(props) {
59
+ const t = useT();
51
60
  const router = useRouter();
52
61
  const urlParams = useParams();
53
62
  const subId = props.params?.id ?? urlParams?.id;
@@ -82,16 +91,16 @@ function SubscriptionDetailPage(props) {
82
91
  const body = await res.json().catch(() => ({}));
83
92
  if (body.existingQuoteId) {
84
93
  const ok = window.confirm(
85
- `${body.error ?? "Subscription has another ARC quote in progress."}
86
-
87
- Open the existing quote now?`
94
+ t("cpq.inventory.subscriptions.detail.arc.existingQuoteConfirm", "{error}\n\nOpen the existing quote now?", {
95
+ error: body.error ?? t("cpq.inventory.subscriptions.detail.arc.existingQuoteInProgress", "Subscription has another ARC quote in progress.")
96
+ })
88
97
  );
89
98
  if (ok) {
90
99
  router.push(`/backend/cpq/quotes/${body.existingQuoteId}`);
91
100
  }
92
101
  return;
93
102
  }
94
- alert(`Failed to start ${type} quote: ${body.error ?? res.statusText}`);
103
+ alert(t("cpq.inventory.subscriptions.detail.arc.startFailed", "Failed to start {type} quote: {error}", { type, error: body.error ?? res.statusText }));
95
104
  return;
96
105
  }
97
106
  const data = await res.json();
@@ -100,7 +109,7 @@ Open the existing quote now?`
100
109
  setArcInFlight(null);
101
110
  }
102
111
  },
103
- [router, sub]
112
+ [router, sub, t]
104
113
  );
105
114
  const transitionStatus = async (targetStatus) => {
106
115
  if (!sub) return;
@@ -113,7 +122,7 @@ Open the existing quote now?`
113
122
  body: JSON.stringify({ id: sub.id, targetStatus })
114
123
  });
115
124
  const data = await res.json();
116
- if (!res.ok) throw new Error(data?.error ?? "Status transition failed");
125
+ if (!res.ok) throw new Error(data?.error ?? t("cpq.inventory.subscriptions.detail.error.transitionFailed", "Status transition failed"));
117
126
  const [freshSub, freshAssets] = await Promise.all([
118
127
  fetch(`/api/cpq/inventory/subscriptions?id=${sub.id}`).then((r) => r.ok ? r.json() : null),
119
128
  fetch(`/api/cpq/inventory/assets?subscriptionId=${sub.id}&pageSize=50`).then((r) => r.ok ? r.json() : null).then((r) => r?.items ?? [])
@@ -173,7 +182,7 @@ Open the existing quote now?`
173
182
  async function load() {
174
183
  try {
175
184
  const res = await fetch(`/api/cpq/inventory/subscriptions?id=${subId}`);
176
- if (!res.ok) throw new Error("Subscription not found");
185
+ if (!res.ok) throw new Error(t("cpq.inventory.subscriptions.detail.error.notFound", "Subscription not found"));
177
186
  const data = await res.json();
178
187
  if (!cancelled) {
179
188
  setSub(data);
@@ -238,7 +247,7 @@ Open the existing quote now?`
238
247
  return () => {
239
248
  cancelled = true;
240
249
  };
241
- }, [subId]);
250
+ }, [subId, t]);
242
251
  const toggleItem = (id) => {
243
252
  setExpandedItems((prev) => {
244
253
  const n = new Set(prev);
@@ -249,31 +258,36 @@ Open the existing quote now?`
249
258
  if (loading) {
250
259
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 py-12 justify-center text-sm text-muted-foreground", children: [
251
260
  /* @__PURE__ */ jsx(Spinner, {}),
252
- " Loading\u2026"
261
+ " ",
262
+ t("cpq.inventory.subscriptions.detail.loading", "Loading\u2026")
253
263
  ] });
254
264
  }
255
265
  if (!sub) {
256
266
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
257
- /* @__PURE__ */ jsx("button", { onClick: () => router.push("/backend/cpq/inventory"), className: "text-sm text-muted-foreground hover:text-foreground", children: "\u2190 Back" }),
267
+ /* @__PURE__ */ jsxs("button", { onClick: () => router.push("/backend/cpq/inventory"), className: "text-sm text-muted-foreground hover:text-foreground", children: [
268
+ "\u2190 ",
269
+ t("cpq.common.back", "Back")
270
+ ] }),
258
271
  error && /* @__PURE__ */ jsx(ErrorBanner, { message: error }),
259
- /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: "Subscription not found." })
272
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.notFound", "Subscription not found.") })
260
273
  ] });
261
274
  }
262
275
  const currency = sub.currencyCode || "USD";
263
276
  const flatItems = flattenItems(sub.items);
264
277
  const canArc = sub.status === "active" || sub.status === "suspended";
265
278
  const statusTransitions = ALLOWED_TRANSITIONS[sub.status] ?? [];
279
+ const statusLabels = buildInventoryStatusLabels(t, SUBSCRIPTION_STATUSES);
266
280
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
267
281
  /* @__PURE__ */ jsx(
268
282
  FormHeader,
269
283
  {
270
284
  mode: "detail",
271
285
  backHref: "/backend/cpq/inventory",
272
- backLabel: "Back to Customer Inventory",
273
- entityTypeLabel: "Subscription",
286
+ backLabel: t("cpq.inventory.detail.backToInventory", "Back to Customer Inventory"),
287
+ entityTypeLabel: t("cpq.inventory.subscriptions.detail.entityType", "Subscription"),
274
288
  title: sub.name,
275
289
  statusBadge: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 pt-1", children: [
276
- /* @__PURE__ */ jsx(Tag, { variant: subscriptionStatusMap[sub.status] ?? "neutral", dot: true, children: STATUS_LABELS[sub.status] ?? formatStatusLabel(sub.status) }),
290
+ /* @__PURE__ */ jsx(Tag, { variant: subscriptionStatusMap[sub.status] ?? "neutral", dot: true, children: inventoryStatusLabel(t, sub.status) }),
277
291
  /* @__PURE__ */ jsx(Tag, { variant: "neutral", className: "capitalize", children: sub.billingCycle }),
278
292
  transitioning && /* @__PURE__ */ jsx(Spinner, {})
279
293
  ] }),
@@ -285,7 +299,7 @@ Open the existing quote now?`
285
299
  variant: "outline",
286
300
  disabled: arcInFlight !== null,
287
301
  onClick: () => startArcQuote("amend"),
288
- children: "Amend"
302
+ children: t("cpq.inventory.subscriptions.detail.arc.amend", "Amend")
289
303
  }
290
304
  ),
291
305
  /* @__PURE__ */ jsx(
@@ -295,7 +309,7 @@ Open the existing quote now?`
295
309
  variant: "outline",
296
310
  disabled: arcInFlight !== null,
297
311
  onClick: () => startArcQuote("renew"),
298
- children: "Renew"
312
+ children: t("cpq.inventory.subscriptions.detail.arc.renew", "Renew")
299
313
  }
300
314
  ),
301
315
  /* @__PURE__ */ jsx(
@@ -305,7 +319,7 @@ Open the existing quote now?`
305
319
  variant: "destructive-outline",
306
320
  disabled: arcInFlight !== null,
307
321
  onClick: () => startArcQuote("cancel"),
308
- children: "Cancel"
322
+ children: t("cpq.inventory.subscriptions.detail.arc.cancel", "Cancel")
309
323
  }
310
324
  )
311
325
  ] }) : null
@@ -318,52 +332,52 @@ Open the existing quote now?`
318
332
  path: ["pending", "active"],
319
333
  terminals: ["suspended", "terminated", "expired"],
320
334
  statusMap: subscriptionStatusMap,
321
- labels: STATUS_LABELS,
335
+ labels: statusLabels,
322
336
  onTransition: transitionStatus,
323
337
  allowedTransitions: statusTransitions,
324
338
  disabled: transitioning,
325
- ariaLabel: "Subscription status path"
339
+ ariaLabel: t("cpq.inventory.subscriptions.detail.statusPath", "Subscription status path")
326
340
  }
327
341
  ) }),
328
342
  error && /* @__PURE__ */ jsx(ErrorBanner, { message: error }),
329
343
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [
330
344
  /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4 space-y-3", children: [
331
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: "Subscription Details" }),
345
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: t("cpq.inventory.subscriptions.detail.sections.details", "Subscription Details") }),
332
346
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-4 gap-y-2", children: [
333
- /* @__PURE__ */ jsx(Field, { label: "Code", value: sub.code, mono: true }),
334
- /* @__PURE__ */ jsx(Field, { label: "Status", value: sub.status }),
335
- /* @__PURE__ */ jsx(Field, { label: "Billing Cycle", value: sub.billingCycle }),
336
- /* @__PURE__ */ jsx(Field, { label: "Currency", value: currency }),
337
- /* @__PURE__ */ jsx(Field, { label: "Auto-Renew", value: sub.autoRenew ? "Yes" : "No" }),
338
- sub.termMonths != null && /* @__PURE__ */ jsx(Field, { label: "Term", value: `${sub.termMonths} months` }),
339
- sub.startDate && /* @__PURE__ */ jsx(Field, { label: "Start Date", value: new Date(sub.startDate).toLocaleDateString() }),
340
- sub.currentTermEnd && /* @__PURE__ */ jsx(Field, { label: "Term End", value: new Date(sub.currentTermEnd).toLocaleDateString() }),
341
- sub.activatedAt && /* @__PURE__ */ jsx(Field, { label: "Activated", value: new Date(sub.activatedAt).toLocaleString() }),
342
- sub.suspendedAt && /* @__PURE__ */ jsx(Field, { label: "Suspended", value: new Date(sub.suspendedAt).toLocaleString() }),
343
- sub.terminatedAt && /* @__PURE__ */ jsx(Field, { label: "Terminated", value: new Date(sub.terminatedAt).toLocaleString() })
347
+ /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.code", "Code"), value: sub.code, mono: true }),
348
+ /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.status", "Status"), value: sub.status }),
349
+ /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.billingCycle", "Billing Cycle"), value: sub.billingCycle }),
350
+ /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.currency", "Currency"), value: currency }),
351
+ /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.autoRenew", "Auto-Renew"), value: sub.autoRenew ? t("cpq.common.yes", "Yes") : t("cpq.common.no", "No") }),
352
+ sub.termMonths != null && /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.term", "Term"), value: t("cpq.inventory.subscriptions.detail.fields.termMonths", "{count} months", { count: sub.termMonths }) }),
353
+ sub.startDate && /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.startDate", "Start Date"), value: new Date(sub.startDate).toLocaleDateString() }),
354
+ sub.currentTermEnd && /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.termEnd", "Term End"), value: new Date(sub.currentTermEnd).toLocaleDateString() }),
355
+ sub.activatedAt && /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.activated", "Activated"), value: new Date(sub.activatedAt).toLocaleString() }),
356
+ sub.suspendedAt && /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.suspended", "Suspended"), value: new Date(sub.suspendedAt).toLocaleString() }),
357
+ sub.terminatedAt && /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.terminated", "Terminated"), value: new Date(sub.terminatedAt).toLocaleString() })
344
358
  ] }),
345
359
  sub.description && /* @__PURE__ */ jsxs("div", { className: "pt-1", children: [
346
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Description" }),
360
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.fields.description", "Description") }),
347
361
  /* @__PURE__ */ jsx("p", { className: "text-sm", children: sub.description })
348
362
  ] })
349
363
  ] }),
350
364
  /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4 space-y-3", children: [
351
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: "Pricing & References" }),
365
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: t("cpq.inventory.subscriptions.detail.sections.pricing", "Pricing & References") }),
352
366
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
353
367
  /* @__PURE__ */ jsxs("div", { children: [
354
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Monthly Recurring (MRC)" }),
368
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.fields.mrc", "Monthly Recurring (MRC)") }),
355
369
  /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold font-mono", children: fmt(sub.mrcAmount, currency) })
356
370
  ] }),
357
371
  /* @__PURE__ */ jsxs("div", { children: [
358
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Non-Recurring (NRC)" }),
372
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.fields.nrc", "Non-Recurring (NRC)") }),
359
373
  /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold font-mono", children: fmt(sub.nrcAmount, currency) })
360
374
  ] })
361
375
  ] }),
362
376
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-4 gap-y-2 pt-1", children: [
363
- /* @__PURE__ */ jsx(Field, { label: "Customer", value: customerName ?? sub.customerId.slice(0, 12) + "\u2026" }),
364
- /* @__PURE__ */ jsx(Field, { label: "Created", value: new Date(sub.createdAt).toLocaleString() }),
377
+ /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.customer", "Customer"), value: customerName ?? sub.customerId.slice(0, 12) + "\u2026" }),
378
+ /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.created", "Created"), value: new Date(sub.createdAt).toLocaleString() }),
365
379
  sub.sourceQuoteId && /* @__PURE__ */ jsxs("div", { children: [
366
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Source Quote" }),
380
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.fields.sourceQuote", "Source Quote") }),
367
381
  /* @__PURE__ */ jsx(
368
382
  "button",
369
383
  {
@@ -374,7 +388,7 @@ Open the existing quote now?`
374
388
  )
375
389
  ] }),
376
390
  sub.sourceOrderId && /* @__PURE__ */ jsxs("div", { children: [
377
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Source Order" }),
391
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.fields.sourceOrder", "Source Order") }),
378
392
  /* @__PURE__ */ jsx(
379
393
  "button",
380
394
  {
@@ -388,7 +402,7 @@ Open the existing quote now?`
388
402
  ] })
389
403
  ] }),
390
404
  sub.metadata && Object.keys(sub.metadata).length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4", children: [
391
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2", children: "Metadata" }),
405
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2", children: t("cpq.inventory.subscriptions.detail.sections.metadata", "Metadata") }),
392
406
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1", children: Object.entries(sub.metadata).map(([key, value]) => /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
393
407
  /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
394
408
  key,
@@ -398,12 +412,8 @@ Open the existing quote now?`
398
412
  ] }, key)) })
399
413
  ] }),
400
414
  /* @__PURE__ */ jsxs("div", { children: [
401
- /* @__PURE__ */ jsxs("h3", { className: "text-sm font-medium mb-2 px-1", children: [
402
- "Subscription Items (",
403
- flatItems.length,
404
- ")"
405
- ] }),
406
- flatItems.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card py-8 text-center text-sm text-muted-foreground", children: "No items." }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card divide-y", children: flatItems.map((item) => {
415
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2 px-1", children: t("cpq.inventory.subscriptions.detail.items.title", "Subscription Items ({count})", { count: flatItems.length }) }),
416
+ flatItems.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card py-8 text-center text-sm text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.items.empty", "No items.") }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card divide-y", children: flatItems.map((item) => {
407
417
  const isExpanded = expandedItems.has(item.id);
408
418
  const indent = item._depth ?? 0;
409
419
  return /* @__PURE__ */ jsxs("div", { children: [
@@ -425,11 +435,17 @@ Open the existing quote now?`
425
435
  ] }) }),
426
436
  /* @__PURE__ */ jsxs("div", { className: "shrink-0 text-right text-xs space-y-0.5", children: [
427
437
  Number(item.mrcAmount) > 0 && /* @__PURE__ */ jsxs("div", { children: [
428
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "MRC " }),
438
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
439
+ t("cpq.inventory.subscriptions.detail.items.mrc", "MRC"),
440
+ " "
441
+ ] }),
429
442
  /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: fmt(item.mrcAmount, item.currencyCode || currency) })
430
443
  ] }),
431
444
  Number(item.nrcAmount) > 0 && /* @__PURE__ */ jsxs("div", { children: [
432
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "NRC " }),
445
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
446
+ t("cpq.inventory.subscriptions.detail.items.nrc", "NRC"),
447
+ " "
448
+ ] }),
433
449
  /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: fmt(item.nrcAmount, item.currencyCode || currency) })
434
450
  ] })
435
451
  ] })
@@ -441,7 +457,7 @@ Open the existing quote now?`
441
457
  item.offeringId && /* @__PURE__ */ jsx(
442
458
  RefField,
443
459
  {
444
- label: "Offering",
460
+ label: t("cpq.inventory.subscriptions.detail.fields.offering", "Offering"),
445
461
  name: offeringNames[item.offeringId],
446
462
  fallback: item.offeringId,
447
463
  onClick: () => router.push(`/backend/cpq/offerings/${item.offeringId}`)
@@ -450,7 +466,7 @@ Open the existing quote now?`
450
466
  item.specId && /* @__PURE__ */ jsx(
451
467
  RefField,
452
468
  {
453
- label: "Specification",
469
+ label: t("cpq.inventory.subscriptions.detail.fields.specification", "Specification"),
454
470
  name: specNames[item.specId],
455
471
  fallback: item.specId,
456
472
  onClick: () => router.push(`/backend/cpq/specifications/${item.specId}`)
@@ -459,18 +475,18 @@ Open the existing quote now?`
459
475
  item.productId && /* @__PURE__ */ jsx(
460
476
  RefField,
461
477
  {
462
- label: "Product",
478
+ label: t("cpq.inventory.subscriptions.detail.fields.product", "Product"),
463
479
  name: productNames[item.productId],
464
480
  fallback: item.productId,
465
481
  onClick: () => router.push(`/backend/catalog/products/${item.productId}`)
466
482
  }
467
483
  ),
468
- /* @__PURE__ */ jsx(Field, { label: "Currency", value: item.currencyCode || currency }),
469
- /* @__PURE__ */ jsx(Field, { label: "Quantity", value: String(item.quantity) }),
470
- item.capacityTotal && /* @__PURE__ */ jsx(Field, { label: "Capacity", value: `${item.capacityUsed ?? "0"} / ${item.capacityTotal} ${item.capacityUnit ?? ""}` })
484
+ /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.currency", "Currency"), value: item.currencyCode || currency }),
485
+ /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.quantity", "Quantity"), value: String(item.quantity) }),
486
+ item.capacityTotal && /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.subscriptions.detail.fields.capacity", "Capacity"), value: `${item.capacityUsed ?? "0"} / ${item.capacityTotal} ${item.capacityUnit ?? ""}` })
471
487
  ] }),
472
488
  item.configuration && Object.keys(item.configuration).length > 0 && /* @__PURE__ */ jsxs("div", { children: [
473
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Configuration" }),
489
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: t("cpq.inventory.subscriptions.detail.items.configuration", "Configuration") }),
474
490
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1", children: Object.entries(item.configuration).map(([key, value]) => /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
475
491
  /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
476
492
  key,
@@ -480,26 +496,38 @@ Open the existing quote now?`
480
496
  ] }, key)) })
481
497
  ] }),
482
498
  item.charges && item.charges.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
483
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Charges" }),
499
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: t("cpq.inventory.subscriptions.detail.items.charges", "Charges") }),
484
500
  /* @__PURE__ */ jsxs("table", { className: "w-full text-xs", children: [
485
501
  /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "text-muted-foreground", children: [
486
- /* @__PURE__ */ jsx("th", { className: "text-left py-1 font-medium", children: "Charge" }),
487
- /* @__PURE__ */ jsx("th", { className: "text-left py-1 font-medium", children: "Type" }),
488
- /* @__PURE__ */ jsx("th", { className: "text-right py-1 font-medium", children: "Unit Price" }),
489
- /* @__PURE__ */ jsx("th", { className: "text-right py-1 font-medium", children: "Qty" }),
490
- /* @__PURE__ */ jsx("th", { className: "text-right py-1 font-medium", children: "Total" })
502
+ /* @__PURE__ */ jsx("th", { className: "text-left py-1 font-medium", children: t("cpq.inventory.subscriptions.detail.charges.charge", "Charge") }),
503
+ /* @__PURE__ */ jsx("th", { className: "text-left py-1 font-medium", children: t("cpq.inventory.subscriptions.detail.charges.type", "Type") }),
504
+ /* @__PURE__ */ jsx("th", { className: "text-right py-1 font-medium", children: t("cpq.inventory.subscriptions.detail.charges.list", "List") }),
505
+ /* @__PURE__ */ jsx("th", { className: "text-right py-1 font-medium", children: t("cpq.inventory.subscriptions.detail.charges.offer", "Offer") }),
506
+ /* @__PURE__ */ jsx("th", { className: "text-right py-1 font-medium", children: t("cpq.inventory.subscriptions.detail.charges.qty", "Qty") }),
507
+ /* @__PURE__ */ jsx("th", { className: "text-right py-1 font-medium", children: t("cpq.inventory.subscriptions.detail.charges.total", "Total") })
491
508
  ] }) }),
492
- /* @__PURE__ */ jsx("tbody", { children: item.charges.map((c, i) => /* @__PURE__ */ jsxs("tr", { className: "border-t border-dashed border-border/50", children: [
493
- /* @__PURE__ */ jsx("td", { className: "py-1", children: c.chargeName ?? "\u2014" }),
494
- /* @__PURE__ */ jsx("td", { className: "py-1 uppercase", children: c.chargeType ?? "\u2014" }),
495
- /* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: c.unitPrice != null ? fmt(c.unitPrice, currency) : "\u2014" }),
496
- /* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: c.quantity ?? "\u2014" }),
497
- /* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: c.totalPrice != null ? fmt(c.totalPrice, currency) : "\u2014" })
498
- ] }, i)) })
509
+ /* @__PURE__ */ jsx("tbody", { children: item.charges.map((raw, i) => {
510
+ const c = parseStoredCharge(raw);
511
+ if (!c) return null;
512
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
513
+ /* @__PURE__ */ jsxs("tr", { className: "border-t border-dashed border-border/50", children: [
514
+ /* @__PURE__ */ jsx("td", { className: "py-1", children: c.chargeName }),
515
+ /* @__PURE__ */ jsx("td", { className: "py-1 uppercase", children: c.chargeType }),
516
+ /* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono text-muted-foreground", children: fmt(c.listUnitPrice ?? c.unitPrice, currency) }),
517
+ /* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
518
+ /* @__PURE__ */ jsx(DepthBadge, { before: c.listUnitPrice ?? c.unitPrice, after: c.unitPrice }),
519
+ fmt(c.unitPrice, currency)
520
+ ] }) }),
521
+ /* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: c.quantity ?? "\u2014" }),
522
+ /* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: c.totalPrice != null ? fmt(c.totalPrice, currency) : "\u2014" })
523
+ ] }),
524
+ (c.adjustments?.length ?? 0) > 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 6, className: "pb-2 pl-4", children: /* @__PURE__ */ jsx(PriceWaterfall, { charge: c, currency }) }) })
525
+ ] }, i);
526
+ }) })
499
527
  ] })
500
528
  ] }),
501
529
  item.metadata && Object.keys(item.metadata).length > 0 && /* @__PURE__ */ jsxs("div", { children: [
502
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Metadata" }),
530
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: t("cpq.inventory.subscriptions.detail.items.metadata", "Metadata") }),
503
531
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1", children: Object.entries(item.metadata).map(([key, value]) => /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
504
532
  /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
505
533
  key,
@@ -509,107 +537,68 @@ Open the existing quote now?`
509
537
  ] }, key)) })
510
538
  ] }),
511
539
  /* @__PURE__ */ jsxs("div", { className: "flex gap-4 text-xs text-muted-foreground", children: [
512
- /* @__PURE__ */ jsxs("span", { children: [
513
- "Created: ",
514
- new Date(item.createdAt).toLocaleString()
515
- ] }),
516
- /* @__PURE__ */ jsxs("span", { children: [
517
- "Updated: ",
518
- new Date(item.updatedAt).toLocaleString()
519
- ] })
540
+ /* @__PURE__ */ jsx("span", { children: t("cpq.inventory.subscriptions.detail.items.createdAt", "Created: {date}", { date: new Date(item.createdAt).toLocaleString() }) }),
541
+ /* @__PURE__ */ jsx("span", { children: t("cpq.inventory.subscriptions.detail.items.updatedAt", "Updated: {date}", { date: new Date(item.updatedAt).toLocaleString() }) })
520
542
  ] })
521
543
  ] })
522
544
  ] }, item.id);
523
545
  }) })
524
546
  ] }),
525
547
  /* @__PURE__ */ jsxs("div", { children: [
526
- /* @__PURE__ */ jsxs("h3", { className: "text-sm font-medium mb-2 px-1", children: [
527
- "Change History (",
528
- changeLog.length,
529
- ")"
530
- ] }),
531
- changeLogLoading ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card py-6 text-center text-sm text-muted-foreground", children: "Loading\u2026" }) : changeLog.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card py-6 text-center text-sm text-muted-foreground", children: "No ARC changes yet." }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card divide-y", children: changeLog.map((row) => /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 space-y-1", children: [
548
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2 px-1", children: t("cpq.inventory.subscriptions.detail.changeLog.title", "Change History ({count})", { count: changeLog.length }) }),
549
+ changeLogLoading ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card py-6 text-center text-sm text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.loading", "Loading\u2026") }) : changeLog.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card py-6 text-center text-sm text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.changeLog.empty", "No ARC changes yet.") }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card divide-y", children: changeLog.map((row) => /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 space-y-1", children: [
532
550
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
533
- /* @__PURE__ */ jsx(Tag, { variant: CHANGE_TYPE_VARIANTS[row.changeType] ?? "neutral", children: CHANGE_TYPE_LABELS[row.changeType] ?? row.changeType }),
551
+ /* @__PURE__ */ jsx(Tag, { variant: CHANGE_TYPE_VARIANTS[row.changeType] ?? "neutral", children: changeTypeLabel(t, row.changeType) }),
534
552
  /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: new Date(row.effectiveAt).toLocaleString() }),
535
- row.sourceQuoteId && /* @__PURE__ */ jsxs(
553
+ row.sourceQuoteId && /* @__PURE__ */ jsx(
536
554
  "button",
537
555
  {
538
556
  onClick: () => router.push(`/backend/cpq/quotes/${row.sourceQuoteId}`),
539
557
  className: "text-xs text-primary hover:underline",
540
- children: [
541
- "Quote ",
542
- logQuoteNumbers[row.sourceQuoteId] ?? `${row.sourceQuoteId.slice(0, 8)}\u2026`
543
- ]
558
+ children: t("cpq.inventory.subscriptions.detail.changeLog.quote", "Quote {number}", { number: logQuoteNumbers[row.sourceQuoteId] ?? `${row.sourceQuoteId.slice(0, 8)}\u2026` })
544
559
  }
545
560
  ),
546
- row.sourceOrderId && /* @__PURE__ */ jsxs(
561
+ row.sourceOrderId && /* @__PURE__ */ jsx(
547
562
  "button",
548
563
  {
549
564
  onClick: () => router.push(`/backend/cpq/orders/${row.sourceOrderId}`),
550
565
  className: "text-xs text-primary hover:underline",
551
- children: [
552
- "Order ",
553
- logOrderNumbers[row.sourceOrderId] ?? `${row.sourceOrderId.slice(0, 8)}\u2026`
554
- ]
566
+ children: t("cpq.inventory.subscriptions.detail.changeLog.order", "Order {number}", { number: logOrderNumbers[row.sourceOrderId] ?? `${row.sourceOrderId.slice(0, 8)}\u2026` })
555
567
  }
556
568
  )
557
569
  ] }),
558
570
  /* @__PURE__ */ jsx(ChangeLogLineDetails, { row, currency }),
559
- row.termChange && /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
560
- "Term: ",
561
- String(row.termChange.oldTermEnd ?? "\u2014"),
562
- " \u2192 ",
563
- String(row.termChange.newTermEnd ?? "\u2014")
564
- ] }),
565
- row.reasonCode && /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
566
- "Reason: ",
567
- row.reasonCode,
568
- row.reasonText ? ` \u2014 ${row.reasonText}` : ""
569
- ] }),
570
- row.etfAmount && /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
571
- "ETF: ",
572
- row.etfAmount,
573
- " ",
574
- row.etfCurrency ?? ""
575
- ] }),
571
+ row.termChange && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.changeLog.term", "Term: {from} \u2192 {to}", {
572
+ from: String(row.termChange.oldTermEnd ?? "\u2014"),
573
+ to: String(row.termChange.newTermEnd ?? "\u2014")
574
+ }) }),
575
+ row.reasonCode && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: row.reasonText ? t("cpq.inventory.subscriptions.detail.changeLog.reasonWithText", "Reason: {code} \u2014 {text}", { code: row.reasonCode, text: row.reasonText }) : t("cpq.inventory.subscriptions.detail.changeLog.reason", "Reason: {code}", { code: row.reasonCode }) }),
576
+ row.etfAmount && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.changeLog.etf", "ETF: {amount} {currency}", { amount: row.etfAmount, currency: row.etfCurrency ?? "" }) }),
576
577
  row.mergedIntoSubscriptionId && /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
577
- "Merged into",
578
+ t("cpq.inventory.subscriptions.detail.changeLog.mergedInto", "Merged into"),
578
579
  " ",
579
- /* @__PURE__ */ jsxs(
580
+ /* @__PURE__ */ jsx(
580
581
  "button",
581
582
  {
582
583
  onClick: () => router.push(`/backend/cpq/inventory/subscriptions/${row.mergedIntoSubscriptionId}`),
583
584
  className: "text-primary hover:underline",
584
- children: [
585
- "subscription ",
586
- row.mergedIntoSubscriptionId.slice(0, 8),
587
- "\u2026"
588
- ]
585
+ children: t("cpq.inventory.subscriptions.detail.changeLog.mergedIntoSubscription", "subscription {id}\u2026", { id: row.mergedIntoSubscriptionId.slice(0, 8) })
589
586
  }
590
587
  )
591
588
  ] }),
592
- row.mergedFromSubscriptionIds && row.mergedFromSubscriptionIds.length > 0 && /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
593
- "Merged from ",
594
- row.mergedFromSubscriptionIds.length,
595
- " subscriptions"
596
- ] })
589
+ row.mergedFromSubscriptionIds && row.mergedFromSubscriptionIds.length > 0 && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.changeLog.mergedFrom", "Merged from {count} subscriptions", { count: row.mergedFromSubscriptionIds.length }) })
597
590
  ] }, row.id)) })
598
591
  ] }),
599
592
  /* @__PURE__ */ jsxs("div", { children: [
600
- /* @__PURE__ */ jsxs("h3", { className: "text-sm font-medium mb-2 px-1", children: [
601
- "Linked Assets (",
602
- assets.length,
603
- ")"
604
- ] }),
605
- assets.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card py-8 text-center text-sm text-muted-foreground", children: "No assets linked to this subscription." }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
593
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2 px-1", children: t("cpq.inventory.subscriptions.detail.assets.title", "Linked Assets ({count})", { count: assets.length }) }),
594
+ assets.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card py-8 text-center text-sm text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.assets.empty", "No assets linked to this subscription.") }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
606
595
  /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/50", children: [
607
- /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Code" }),
608
- /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Name" }),
609
- /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Type" }),
610
- /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Status" }),
611
- /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right font-medium", children: "Price" }),
612
- /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right font-medium", children: "Qty" })
596
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.inventory.subscriptions.detail.assets.table.code", "Code") }),
597
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.inventory.subscriptions.detail.assets.table.name", "Name") }),
598
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.inventory.subscriptions.detail.assets.table.type", "Type") }),
599
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.inventory.subscriptions.detail.assets.table.status", "Status") }),
600
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right font-medium", children: t("cpq.inventory.subscriptions.detail.assets.table.price", "Price") }),
601
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right font-medium", children: t("cpq.inventory.subscriptions.detail.assets.table.qty", "Qty") })
613
602
  ] }) }),
614
603
  /* @__PURE__ */ jsx("tbody", { children: assets.map((a) => /* @__PURE__ */ jsxs("tr", { onClick: () => router.push(`/backend/cpq/inventory/assets/${a.id}`), className: "border-b cursor-pointer hover:bg-muted/30 transition-colors", children: [
615
604
  /* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-muted-foreground", children: a.code }),
@@ -669,6 +658,8 @@ function ErrorBanner({ message }) {
669
658
  return /* @__PURE__ */ jsx(Alert, { variant: "destructive", children: message });
670
659
  }
671
660
  function ChangeLogLineDetails({ row, currency }) {
661
+ const t = useT();
662
+ const itemFallback = t("cpq.inventory.subscriptions.detail.changeLog.item", "Item");
672
663
  const beforeItems = new Map(
673
664
  (row.beforeSnapshot?.items ?? []).map((it) => [it.id, it])
674
665
  );
@@ -690,7 +681,7 @@ function ChangeLogLineDetails({ row, currency }) {
690
681
  derived.push({
691
682
  key: `${itemId}-${i}`,
692
683
  action: lc.action,
693
- name: item?.name ?? (lc.action === "add" ? "New item" : "Item"),
684
+ name: item?.name ?? (lc.action === "add" ? t("cpq.inventory.subscriptions.detail.changeLog.newItem", "New item") : itemFallback),
694
685
  beforeMrc,
695
686
  afterMrc: lc.action === "add" && afterMrc == null && lc.mrcAmount != null ? Number(lc.mrcAmount) : afterMrc,
696
687
  delta: explicitDelta ?? computedDelta
@@ -702,7 +693,7 @@ function ChangeLogLineDetails({ row, currency }) {
702
693
  derived.push({
703
694
  key: it.id,
704
695
  action: "cancel",
705
- name: it.name ?? "Item",
696
+ name: it.name ?? itemFallback,
706
697
  beforeMrc: mrc,
707
698
  afterMrc: 0,
708
699
  delta: -mrc
@@ -714,7 +705,7 @@ function ChangeLogLineDetails({ row, currency }) {
714
705
  derived.push({
715
706
  key: it.id,
716
707
  action: "add",
717
- name: it.name ?? "Item",
708
+ name: it.name ?? itemFallback,
718
709
  beforeMrc: null,
719
710
  afterMrc: mrc,
720
711
  delta: null
@@ -726,7 +717,7 @@ function ChangeLogLineDetails({ row, currency }) {
726
717
  derived.push({
727
718
  key: it.id,
728
719
  action: "cancel",
729
- name: it.name ?? "Item",
720
+ name: it.name ?? itemFallback,
730
721
  beforeMrc: mrc,
731
722
  afterMrc: 0,
732
723
  delta: -mrc
@@ -735,57 +726,56 @@ function ChangeLogLineDetails({ row, currency }) {
735
726
  }
736
727
  if (derived.length === 0) {
737
728
  if (row.changeType === "amend" || row.changeType === "renew") {
738
- return /* @__PURE__ */ jsx("div", { className: "mt-1.5 text-xs text-muted-foreground italic", children: "No item-level changes recorded." });
729
+ return /* @__PURE__ */ jsx("div", { className: "mt-1.5 text-xs text-muted-foreground italic", children: t("cpq.inventory.subscriptions.detail.changeLog.noItemChanges", "No item-level changes recorded.") });
739
730
  }
740
731
  return null;
741
732
  }
742
733
  return /* @__PURE__ */ jsx("div", { className: "mt-1.5 rounded border bg-muted/20 px-3 py-2 space-y-1", children: derived.map((d) => /* @__PURE__ */ jsx(ChangeLogLineRow, { change: d, currency }, d.key)) });
743
734
  }
744
735
  const ACTION_GLYPH = {
745
- add: { sign: "+", color: "text-status-success-text", label: "added" },
746
- modify: { sign: "~", color: "text-status-info-text", label: "modified" },
747
- cancel: { sign: "\u2212", color: "text-destructive", label: "removed" }
736
+ add: { sign: "+", color: "text-status-success-text" },
737
+ modify: { sign: "~", color: "text-status-info-text" },
738
+ cancel: { sign: "\u2212", color: "text-destructive" }
748
739
  };
740
+ function actionGlyphLabel(t, action) {
741
+ switch (action) {
742
+ case "add":
743
+ return t("cpq.inventory.subscriptions.detail.changeLog.action.add", "added");
744
+ case "modify":
745
+ return t("cpq.inventory.subscriptions.detail.changeLog.action.modify", "modified");
746
+ case "cancel":
747
+ return t("cpq.inventory.subscriptions.detail.changeLog.action.cancel", "removed");
748
+ }
749
+ }
749
750
  function ChangeLogLineRow({
750
751
  change,
751
752
  currency
752
753
  }) {
754
+ const t = useT();
753
755
  const glyph = ACTION_GLYPH[change.action];
754
756
  const showPriceChange = change.action === "modify" && change.beforeMrc != null && change.afterMrc != null && change.beforeMrc !== change.afterMrc;
755
757
  const showSinglePrice = !showPriceChange && (change.action === "add" && change.afterMrc != null && change.afterMrc > 0 || change.action === "cancel" && change.beforeMrc != null && change.beforeMrc > 0);
756
758
  const showDelta = change.delta != null && change.delta !== 0;
757
759
  const isNoopModify = change.action === "modify" && !showPriceChange && !showDelta;
758
760
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs", children: [
759
- /* @__PURE__ */ jsx("span", { className: `font-bold w-3 text-center ${glyph.color}`, title: glyph.label, children: glyph.sign }),
761
+ /* @__PURE__ */ jsx("span", { className: `font-bold w-3 text-center ${glyph.color}`, title: actionGlyphLabel(t, change.action), children: glyph.sign }),
760
762
  /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: change.name }),
761
- showPriceChange && /* @__PURE__ */ jsxs("span", { className: "font-mono text-muted-foreground", children: [
762
- fmt(change.beforeMrc, currency),
763
- " \u2192 ",
764
- fmt(change.afterMrc, currency),
765
- "/mo"
766
- ] }),
767
- showSinglePrice && change.action === "add" && /* @__PURE__ */ jsxs("span", { className: "font-mono text-muted-foreground", children: [
768
- fmt(change.afterMrc, currency),
769
- "/mo"
770
- ] }),
771
- showSinglePrice && change.action === "cancel" && /* @__PURE__ */ jsxs("span", { className: "font-mono text-muted-foreground", children: [
772
- "was ",
773
- fmt(change.beforeMrc, currency),
774
- "/mo"
775
- ] }),
776
- showDelta && /* @__PURE__ */ jsxs(
763
+ showPriceChange && /* @__PURE__ */ jsx("span", { className: "font-mono text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.changeLog.priceChangePerMonth", "{before} \u2192 {after}/mo", {
764
+ before: fmt(change.beforeMrc, currency),
765
+ after: fmt(change.afterMrc, currency)
766
+ }) }),
767
+ showSinglePrice && change.action === "add" && /* @__PURE__ */ jsx("span", { className: "font-mono text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.changeLog.pricePerMonth", "{price}/mo", { price: fmt(change.afterMrc, currency) }) }),
768
+ showSinglePrice && change.action === "cancel" && /* @__PURE__ */ jsx("span", { className: "font-mono text-muted-foreground", children: t("cpq.inventory.subscriptions.detail.changeLog.wasPricePerMonth", "was {price}/mo", { price: fmt(change.beforeMrc, currency) }) }),
769
+ showDelta && /* @__PURE__ */ jsx(
777
770
  "span",
778
771
  {
779
772
  className: `font-mono font-medium ${change.delta > 0 ? "text-status-success-text" : "text-destructive"}`,
780
- children: [
781
- "(",
782
- change.delta > 0 ? "+" : "",
783
- fmt(change.delta, currency),
784
- "/mo)"
785
- ]
773
+ children: t("cpq.inventory.subscriptions.detail.changeLog.deltaPerMonth", "({delta}/mo)", {
774
+ delta: `${change.delta > 0 ? "+" : ""}${fmt(change.delta, currency)}`
775
+ })
786
776
  }
787
777
  ),
788
- isNoopModify && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground italic", children: "no price change" })
778
+ isNoopModify && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground italic", children: t("cpq.inventory.subscriptions.detail.changeLog.noPriceChange", "no price change") })
789
779
  ] });
790
780
  }
791
781
  export {