@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
@@ -1,6 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import React, { useState, useEffect, useCallback } from 'react'
4
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
4
5
  import type { WizardStepProps } from '../types'
5
6
  import { NumberInput } from '../../components/NumberInput'
6
7
 
@@ -43,6 +44,7 @@ function ConfigPanel({
43
44
  onChange: (code: string, value: unknown) => void
44
45
  onRemove: () => void
45
46
  }) {
47
+ const t = useT()
46
48
  const [attributes, setAttributes] = useState<ConstrainedAttribute[]>([])
47
49
  const [loading, setLoading] = useState(true)
48
50
  const [configVersion, setConfigVersion] = useState(0)
@@ -89,7 +91,7 @@ function ConfigPanel({
89
91
  if (attr.attributeType === 'select' || attr.attributeType === 'enum') {
90
92
  return (
91
93
  <select value={String(value)} onChange={(e) => handleChange(attr.code, e.target.value)} className="w-full rounded-md border px-3 py-2 text-sm">
92
- <option value="">Select...</option>
94
+ <option value="">{t('cpq.wizard.selectPlaceholder', 'Select...')}</option>
93
95
  {attr.options?.map((opt) => <option key={opt.value} value={opt.value}>{opt.label}</option>)}
94
96
  </select>
95
97
  )
@@ -117,12 +119,12 @@ function ConfigPanel({
117
119
  <div className="rounded-lg border p-4 space-y-3">
118
120
  <div className="flex items-center justify-between border-b pb-2">
119
121
  <h4 className="text-sm font-medium">{offering.name}</h4>
120
- <button type="button" onClick={onRemove} className="text-xs text-muted-foreground hover:text-foreground">Remove</button>
122
+ <button type="button" onClick={onRemove} className="text-xs text-muted-foreground hover:text-foreground">{t('cpq.wizard.remove', 'Remove')}</button>
121
123
  </div>
122
124
  {loading ? (
123
- <div className="text-sm text-muted-foreground">Loading attributes...</div>
125
+ <div className="text-sm text-muted-foreground">{t('cpq.wizard.loadingAttributes', 'Loading attributes...')}</div>
124
126
  ) : attributes.length === 0 ? (
125
- <div className="text-sm text-muted-foreground">No configurable attributes.</div>
127
+ <div className="text-sm text-muted-foreground">{t('cpq.wizard.steps.offeringSelect.noAttributes', 'No configurable attributes.')}</div>
126
128
  ) : (
127
129
  <div className="space-y-3">
128
130
  {attributes.map((attr) => (
@@ -150,6 +152,7 @@ function BandwidthIndicator({
150
152
  portCapacityMb: number
151
153
  items: SelectedItem[]
152
154
  }) {
155
+ const t = useT()
153
156
  const totalBandwidth = items.reduce((sum, item) => {
154
157
  const bw = Number(item.configuration.bandwidth_mb) || 0
155
158
  return sum + bw
@@ -161,9 +164,9 @@ function BandwidthIndicator({
161
164
  return (
162
165
  <div className="rounded-lg border p-3 space-y-2">
163
166
  <div className="flex items-center justify-between text-xs">
164
- <span className="font-medium">Bandwidth Utilization</span>
167
+ <span className="font-medium">{t('cpq.wizard.steps.offeringSelect.bandwidth.title', 'Bandwidth Utilization')}</span>
165
168
  <span className={overCapacity ? 'text-destructive font-medium' : 'text-muted-foreground'}>
166
- {totalBandwidth} / {portCapacityMb} MB
169
+ {t('cpq.wizard.steps.offeringSelect.bandwidth.usage', '{used} / {capacity} MB', { used: totalBandwidth, capacity: portCapacityMb })}
167
170
  </span>
168
171
  </div>
169
172
  <div className="h-2 rounded-full bg-muted overflow-hidden">
@@ -174,7 +177,7 @@ function BandwidthIndicator({
174
177
  </div>
175
178
  {overCapacity && (
176
179
  <p className="text-xs text-destructive">
177
- Total bandwidth exceeds port capacity by {totalBandwidth - portCapacityMb} MB
180
+ {t('cpq.wizard.steps.offeringSelect.bandwidth.exceeded', 'Total bandwidth exceeds port capacity by {excess} MB', { excess: totalBandwidth - portCapacityMb })}
178
181
  </p>
179
182
  )}
180
183
  </div>
@@ -186,6 +189,7 @@ function BandwidthIndicator({
186
189
  const PORT_SIZE_MB: Record<string, number> = { '1G': 1000, '10G': 10000, '100G': 100000 }
187
190
 
188
191
  export function OfferingSelectStep({ config, onComplete, onBack, stepData, currentStepId, completedSteps, quoteId, customerId }: WizardStepProps) {
192
+ const t = useT()
189
193
  const [offerings, setOfferings] = useState<Offering[]>([])
190
194
  const [selectedItems, setSelectedItems] = useState<SelectedItem[]>([])
191
195
  const [loading, setLoading] = useState(true)
@@ -336,7 +340,7 @@ export function OfferingSelectStep({ config, onComplete, onBack, stepData, curre
336
340
  }, [selectedItems, enableConfiguration, quoteId, resolvedCustomerId, onComplete])
337
341
 
338
342
  if (loading) {
339
- return <div className="text-sm text-muted-foreground">Loading offerings...</div>
343
+ return <div className="text-sm text-muted-foreground">{t('cpq.wizard.steps.offeringSelect.loading', 'Loading offerings...')}</div>
340
344
  }
341
345
 
342
346
  // Offerings not yet added
@@ -368,18 +372,18 @@ export function OfferingSelectStep({ config, onComplete, onBack, stepData, curre
368
372
  >
369
373
  <div className="flex items-center justify-between">
370
374
  <h4 className="text-sm font-medium">{o.name}</h4>
371
- {isAdded && <span className="text-xs text-primary font-medium">Added</span>}
375
+ {isAdded && <span className="text-xs text-primary font-medium">{t('cpq.wizard.steps.offeringSelect.added', 'Added')}</span>}
372
376
  </div>
373
377
  <p className="text-xs text-muted-foreground mt-1">{o.description || o.code}</p>
374
378
  {!isAdded && (
375
- <span className="text-xs text-primary mt-2 inline-block">+ Add</span>
379
+ <span className="text-xs text-primary mt-2 inline-block">{t('cpq.wizard.steps.offeringSelect.add', '+ Add')}</span>
376
380
  )}
377
381
  </div>
378
382
  )
379
383
  })}
380
384
  {offerings.length === 0 && (
381
385
  <div className="rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground">
382
- No offerings available.
386
+ {t('cpq.wizard.steps.offeringSelect.empty', 'No offerings available.')}
383
387
  </div>
384
388
  )}
385
389
  </div>
@@ -392,7 +396,7 @@ export function OfferingSelectStep({ config, onComplete, onBack, stepData, curre
392
396
  {/* Configuration panels for selected items */}
393
397
  {enableConfiguration && selectedItems.length > 0 && (
394
398
  <div className="space-y-3">
395
- <h4 className="text-sm font-medium">Selected Services ({selectedItems.length})</h4>
399
+ <h4 className="text-sm font-medium">{t('cpq.wizard.steps.offeringSelect.selectedServices', 'Selected Services ({count})', { count: selectedItems.length })}</h4>
396
400
  {selectedItems.map((item, index) => (
397
401
  <ConfigPanel
398
402
  key={`${item.offering.id}-${index}`}
@@ -408,11 +412,11 @@ export function OfferingSelectStep({ config, onComplete, onBack, stepData, curre
408
412
  {/* No configuration — just show selected list */}
409
413
  {!enableConfiguration && selectedItems.length > 0 && (
410
414
  <div className="space-y-2">
411
- <h4 className="text-sm font-medium">Selected ({selectedItems.length})</h4>
415
+ <h4 className="text-sm font-medium">{t('cpq.wizard.steps.offeringSelect.selected', 'Selected ({count})', { count: selectedItems.length })}</h4>
412
416
  {selectedItems.map((item, index) => (
413
417
  <div key={`${item.offering.id}-${index}`} className="flex items-center justify-between rounded-md border p-3">
414
418
  <span className="text-sm font-medium">{item.offering.name}</span>
415
- <button type="button" onClick={() => removeItem(index)} className="text-xs text-muted-foreground hover:text-foreground">Remove</button>
419
+ <button type="button" onClick={() => removeItem(index)} className="text-xs text-muted-foreground hover:text-foreground">{t('cpq.wizard.remove', 'Remove')}</button>
416
420
  </div>
417
421
  ))}
418
422
  </div>
@@ -421,7 +425,7 @@ export function OfferingSelectStep({ config, onComplete, onBack, stepData, curre
421
425
  <div className="flex gap-2 justify-end pt-2">
422
426
  {completedSteps.length > 0 && (
423
427
  <button type="button" onClick={onBack} className="inline-flex items-center justify-center rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted">
424
- Back
428
+ {t('cpq.common.back', 'Back')}
425
429
  </button>
426
430
  )}
427
431
  <button
@@ -430,7 +434,7 @@ export function OfferingSelectStep({ config, onComplete, onBack, stepData, curre
430
434
  disabled={selectedItems.length === 0 || saving}
431
435
  className="inline-flex items-center justify-center rounded-md bg-primary px-5 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed"
432
436
  >
433
- {saving ? 'Saving...' : 'Next'}
437
+ {saving ? t('cpq.common.saving', 'Saving...') : t('cpq.wizard.next', 'Next')}
434
438
  </button>
435
439
  </div>
436
440
  </div>
@@ -1,6 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import React, { useState, useEffect, useCallback } from 'react'
4
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
4
5
  import type { WizardStepProps } from '../types'
5
6
  import { NumberInput } from '../../components/NumberInput'
6
7
 
@@ -34,6 +35,7 @@ function resolveOfferingId(config: Record<string, unknown>, stepData: Record<str
34
35
  }
35
36
 
36
37
  export function ProductConfigureStep({ config, onComplete, onBack, stepData, currentStepId, completedSteps, quoteId, customerId }: WizardStepProps) {
38
+ const t = useT()
37
39
  const [attributes, setAttributes] = useState<ConstrainedAttribute[]>([])
38
40
  const [values, setValues] = useState<Record<string, unknown>>({})
39
41
  const [loading, setLoading] = useState(true)
@@ -181,7 +183,7 @@ export function ProductConfigureStep({ config, onComplete, onBack, stepData, cur
181
183
  disabled={isReadonly}
182
184
  className="w-full rounded-md border px-3 py-2 text-sm disabled:opacity-50"
183
185
  >
184
- <option value="">Select...</option>
186
+ <option value="">{t('cpq.wizard.selectPlaceholder', 'Select...')}</option>
185
187
  {attr.options?.map((opt) => (
186
188
  <option key={opt.value} value={opt.value}>{opt.label}</option>
187
189
  ))}
@@ -226,13 +228,13 @@ export function ProductConfigureStep({ config, onComplete, onBack, stepData, cur
226
228
  }
227
229
 
228
230
  if (loading) {
229
- return <div className="text-sm text-muted-foreground">Loading attributes...</div>
231
+ return <div className="text-sm text-muted-foreground">{t('cpq.wizard.loadingAttributes', 'Loading attributes...')}</div>
230
232
  }
231
233
 
232
234
  if (!offeringId) {
233
235
  return (
234
236
  <div className="rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground">
235
- No offering selected. Please go back and select an offering.
237
+ {t('cpq.wizard.steps.productConfigure.noOffering', 'No offering selected. Please go back and select an offering.')}
236
238
  </div>
237
239
  )
238
240
  }
@@ -264,13 +266,13 @@ export function ProductConfigureStep({ config, onComplete, onBack, stepData, cur
264
266
  return renderGroup(group.label, groupAttrs)
265
267
  })
266
268
  ) : (
267
- renderGroup('Configuration', attributes)
269
+ renderGroup(t('cpq.lines.configuration', 'Configuration'), attributes)
268
270
  )}
269
271
 
270
272
  <div className="flex gap-2 justify-end pt-2">
271
273
  {completedSteps.length > 0 && (
272
274
  <button type="button" onClick={onBack} className="inline-flex items-center justify-center rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted">
273
- Back
275
+ {t('cpq.common.back', 'Back')}
274
276
  </button>
275
277
  )}
276
278
  <button
@@ -279,7 +281,7 @@ export function ProductConfigureStep({ config, onComplete, onBack, stepData, cur
279
281
  disabled={saving}
280
282
  className="inline-flex items-center justify-center rounded-md bg-primary px-5 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
281
283
  >
282
- {saving ? 'Saving...' : 'Next'}
284
+ {saving ? t('cpq.common.saving', 'Saving...') : t('cpq.wizard.next', 'Next')}
283
285
  </button>
284
286
  </div>
285
287
  </div>
@@ -1,6 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import React, { useState, useEffect, useCallback } from 'react'
4
+ import { useT, type TranslateFn } from '@open-mercato/shared/lib/i18n/context'
4
5
  import type { WizardStepProps } from '../types'
5
6
 
6
7
  type ResolvedCharge = {
@@ -47,7 +48,21 @@ function fmt(n: number, currency = 'USD') {
47
48
  return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(n)
48
49
  }
49
50
 
51
+ function chargeTypeLabel(t: TranslateFn, chargeType: string): string {
52
+ switch (chargeType) {
53
+ case 'nrc':
54
+ return t('cpq.lines.chargeType.nrc', 'nrc')
55
+ case 'mrc':
56
+ return t('cpq.lines.chargeType.mrc', 'mrc')
57
+ case 'usage':
58
+ return t('cpq.lines.chargeType.usage', 'usage')
59
+ default:
60
+ return chargeType
61
+ }
62
+ }
63
+
50
64
  export function ReviewStep({ config, onComplete, onBack, completedSteps, quoteId }: WizardStepProps) {
65
+ const t = useT()
51
66
  const [quote, setQuote] = useState<QuoteData | null>(null)
52
67
  const [loading, setLoading] = useState(true)
53
68
  const [submitting, setSubmitting] = useState(false)
@@ -55,7 +70,7 @@ export function ReviewStep({ config, onComplete, onBack, completedSteps, quoteId
55
70
  const showChargeBreakdown = config.showChargeBreakdown !== false
56
71
  const showAdjustments = config.showAdjustments !== false
57
72
  const submitAction = (config.submitAction as string) ?? 'save'
58
- const submitLabel = (config.submitLabel as string) ?? 'Complete'
73
+ const submitLabel = (config.submitLabel as string) ?? t('cpq.wizard.steps.review.complete', 'Complete')
59
74
 
60
75
  const loadQuote = useCallback(async () => {
61
76
  if (!quoteId) { setLoading(false); return }
@@ -102,13 +117,13 @@ export function ReviewStep({ config, onComplete, onBack, completedSteps, quoteId
102
117
  }, [quoteId, submitAction, quote, onComplete])
103
118
 
104
119
  if (loading) {
105
- return <div className="text-sm text-muted-foreground">Loading quote summary...</div>
120
+ return <div className="text-sm text-muted-foreground">{t('cpq.wizard.steps.review.loading', 'Loading quote summary...')}</div>
106
121
  }
107
122
 
108
123
  if (!quote) {
109
124
  return (
110
125
  <div className="rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground">
111
- No quote data available. Please go back and configure items.
126
+ {t('cpq.wizard.steps.review.noData', 'No quote data available. Please go back and configure items.')}
112
127
  </div>
113
128
  )
114
129
  }
@@ -119,25 +134,25 @@ export function ReviewStep({ config, onComplete, onBack, completedSteps, quoteId
119
134
  <div className="space-y-5">
120
135
  {/* Quote summary */}
121
136
  <div className="rounded-lg border bg-muted/30 p-4 space-y-3">
122
- <h4 className="text-sm font-medium">Quote Summary</h4>
137
+ <h4 className="text-sm font-medium">{t('cpq.wizard.steps.review.summaryTitle', 'Quote Summary')}</h4>
123
138
  <div className="grid grid-cols-2 gap-4">
124
139
  <div>
125
- <span className="text-xs text-muted-foreground">NRC Total</span>
140
+ <span className="text-xs text-muted-foreground">{t('cpq.wizard.steps.review.nrcTotal', 'NRC Total')}</span>
126
141
  <p className="text-lg font-semibold">{fmt(pricingSummary.nrcTotal, pricingSummary.currencyCode)}</p>
127
142
  </div>
128
143
  <div>
129
- <span className="text-xs text-muted-foreground">MRC Total</span>
144
+ <span className="text-xs text-muted-foreground">{t('cpq.wizard.steps.review.mrcTotal', 'MRC Total')}</span>
130
145
  <p className="text-lg font-semibold">{fmt(pricingSummary.mrcTotal, pricingSummary.currencyCode)}</p>
131
146
  </div>
132
147
  {pricingSummary.discountTotal > 0 && (
133
148
  <div>
134
- <span className="text-xs text-muted-foreground">Discounts</span>
149
+ <span className="text-xs text-muted-foreground">{t('cpq.wizard.steps.review.discounts', 'Discounts')}</span>
135
150
  <p className="text-sm">-{fmt(pricingSummary.discountTotal, pricingSummary.currencyCode)}</p>
136
151
  </div>
137
152
  )}
138
153
  {pricingSummary.surchargeTotal > 0 && (
139
154
  <div>
140
- <span className="text-xs text-muted-foreground">Surcharges</span>
155
+ <span className="text-xs text-muted-foreground">{t('cpq.wizard.steps.review.surcharges', 'Surcharges')}</span>
141
156
  <p className="text-sm">+{fmt(pricingSummary.surchargeTotal, pricingSummary.currencyCode)}</p>
142
157
  </div>
143
158
  )}
@@ -146,18 +161,18 @@ export function ReviewStep({ config, onComplete, onBack, completedSteps, quoteId
146
161
 
147
162
  {/* Line items */}
148
163
  <div className="space-y-3">
149
- <h4 className="text-sm font-medium">Items ({lines.length})</h4>
164
+ <h4 className="text-sm font-medium">{t('cpq.wizard.steps.review.items', 'Items ({count})', { count: lines.length })}</h4>
150
165
  <div className="space-y-2">
151
166
  {lines.map((line) => (
152
167
  <div key={line.lineId} className="rounded-lg border p-3 space-y-2">
153
168
  <div className="flex items-center justify-between">
154
169
  <strong className="text-sm">{line.offeringName}</strong>
155
170
  <span className="text-sm">
156
- {fmt(line.nrcTotal, pricingSummary.currencyCode)} NRC / {fmt(line.mrcTotal, pricingSummary.currencyCode)} MRC
171
+ {t('cpq.wizard.linePricing', '{nrc} NRC / {mrc} MRC', { nrc: fmt(line.nrcTotal, pricingSummary.currencyCode), mrc: fmt(line.mrcTotal, pricingSummary.currencyCode) })}
157
172
  </span>
158
173
  </div>
159
174
  {line.quantity > 1 && (
160
- <span className="text-xs text-muted-foreground">Quantity: {line.quantity}</span>
175
+ <span className="text-xs text-muted-foreground">{t('cpq.wizard.steps.review.quantity', 'Quantity: {quantity}', { quantity: line.quantity })}</span>
161
176
  )}
162
177
 
163
178
  {showChargeBreakdown && line.charges?.length > 0 && (
@@ -165,9 +180,9 @@ export function ReviewStep({ config, onComplete, onBack, completedSteps, quoteId
165
180
  <table className="w-full text-xs">
166
181
  <thead>
167
182
  <tr className="border-b bg-muted/30">
168
- <th className="px-3 py-1.5 text-left font-medium">Charge</th>
169
- <th className="px-3 py-1.5 text-left font-medium">Type</th>
170
- <th className="px-3 py-1.5 text-right font-medium">Price</th>
183
+ <th className="px-3 py-1.5 text-left font-medium">{t('cpq.wizard.steps.review.charges.charge', 'Charge')}</th>
184
+ <th className="px-3 py-1.5 text-left font-medium">{t('cpq.wizard.steps.review.charges.type', 'Type')}</th>
185
+ <th className="px-3 py-1.5 text-right font-medium">{t('cpq.wizard.steps.review.charges.price', 'Price')}</th>
171
186
  </tr>
172
187
  </thead>
173
188
  <tbody>
@@ -175,9 +190,9 @@ export function ReviewStep({ config, onComplete, onBack, completedSteps, quoteId
175
190
  <React.Fragment key={charge.chargeCode}>
176
191
  <tr className="border-b last:border-0">
177
192
  <td className="px-3 py-1.5">{charge.chargeName}</td>
178
- <td className="px-3 py-1.5 uppercase">{charge.chargeType}</td>
193
+ <td className="px-3 py-1.5 uppercase">{chargeTypeLabel(t, charge.chargeType)}</td>
179
194
  <td className="px-3 py-1.5 text-right">
180
- {charge.totalPrice != null ? fmt(charge.totalPrice, charge.currencyCode) : `${fmt(charge.unitPrice, charge.currencyCode)}/unit`}
195
+ {charge.totalPrice != null ? fmt(charge.totalPrice, charge.currencyCode) : t('cpq.lines.perUnit', '{price}/unit', { price: fmt(charge.unitPrice, charge.currencyCode) })}
181
196
  </td>
182
197
  </tr>
183
198
  {showAdjustments && charge.adjustments?.map((adj) => (
@@ -202,7 +217,7 @@ export function ReviewStep({ config, onComplete, onBack, completedSteps, quoteId
202
217
  <div className="flex gap-2 justify-end pt-2">
203
218
  {completedSteps.length > 0 && (
204
219
  <button type="button" onClick={onBack} className="inline-flex items-center justify-center rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted">
205
- Back
220
+ {t('cpq.common.back', 'Back')}
206
221
  </button>
207
222
  )}
208
223
  <button
@@ -211,7 +226,7 @@ export function ReviewStep({ config, onComplete, onBack, completedSteps, quoteId
211
226
  disabled={submitting}
212
227
  className="inline-flex items-center justify-center rounded-md bg-primary px-5 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
213
228
  >
214
- {submitting ? 'Submitting...' : submitLabel}
229
+ {submitting ? t('cpq.wizard.steps.review.submitting', 'Submitting...') : submitLabel}
215
230
  </button>
216
231
  </div>
217
232
  </div>
@@ -9,6 +9,8 @@ import { InventorySelectStep } from './InventorySelectStep'
9
9
  export function registerBuiltInStepTypes() {
10
10
  registerStepType({
11
11
  type: 'customer_select',
12
+ labelKey: 'cpq.wizard.steps.customerSelect.label',
13
+ descriptionKey: 'cpq.wizard.steps.customerSelect.description',
12
14
  label: 'Customer Selection',
13
15
  description: 'Search and select a customer for the quote.',
14
16
  component: CustomerSelectStep,
@@ -16,6 +18,8 @@ export function registerBuiltInStepTypes() {
16
18
 
17
19
  registerStepType({
18
20
  type: 'offering_select',
21
+ labelKey: 'cpq.wizard.steps.offeringSelect.label',
22
+ descriptionKey: 'cpq.wizard.steps.offeringSelect.description',
19
23
  label: 'Offering Selection',
20
24
  description: 'Browse and select a product offering from the catalog.',
21
25
  component: OfferingSelectStep,
@@ -23,6 +27,8 @@ export function registerBuiltInStepTypes() {
23
27
 
24
28
  registerStepType({
25
29
  type: 'product_configure',
30
+ labelKey: 'cpq.wizard.steps.productConfigure.label',
31
+ descriptionKey: 'cpq.wizard.steps.productConfigure.description',
26
32
  label: 'Product Configuration',
27
33
  description: 'Configure run-time attributes for the selected offering.',
28
34
  component: ProductConfigureStep,
@@ -30,6 +36,8 @@ export function registerBuiltInStepTypes() {
30
36
 
31
37
  registerStepType({
32
38
  type: 'item_list',
39
+ labelKey: 'cpq.wizard.steps.itemList.label',
40
+ descriptionKey: 'cpq.wizard.steps.itemList.description',
33
41
  label: 'Item List',
34
42
  description: 'View and manage child items on the quote.',
35
43
  component: ItemListStep,
@@ -37,6 +45,8 @@ export function registerBuiltInStepTypes() {
37
45
 
38
46
  registerStepType({
39
47
  type: 'review',
48
+ labelKey: 'cpq.wizard.steps.review.label',
49
+ descriptionKey: 'cpq.wizard.steps.review.description',
40
50
  label: 'Review & Submit',
41
51
  description: 'Review the quote summary and submit.',
42
52
  component: ReviewStep,
@@ -44,6 +54,8 @@ export function registerBuiltInStepTypes() {
44
54
 
45
55
  registerStepType({
46
56
  type: 'inventory_select',
57
+ labelKey: 'cpq.wizard.steps.inventorySelect.label',
58
+ descriptionKey: 'cpq.wizard.steps.inventorySelect.description',
47
59
  label: 'Inventory Selection',
48
60
  description: 'Select existing customer inventory items (stub — pending inventory domain).',
49
61
  component: InventorySelectStep,
@@ -97,6 +97,10 @@ export interface WizardStepProps {
97
97
  export interface WizardStepType {
98
98
  type: string
99
99
  label: string
100
+ /** i18n key for `label`; the English `label` is the fallback. */
101
+ labelKey?: string
100
102
  description: string
103
+ /** i18n key for `description`; the English `description` is the fallback. */
104
+ descriptionKey?: string
101
105
  component: React.ComponentType<WizardStepProps>
102
106
  }