@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,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/cpq/backend/cpq/price-rules/%5Bid%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter, useParams } from 'next/navigation'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { NumberInput } from '../../../../components/NumberInput'\n\nconst RULE_TYPE_LABELS: Record<string, string> = {\n discount_percent: 'Discount %',\n discount_absolute: 'Discount $',\n surcharge_percent: 'Surcharge %',\n surcharge_absolute: 'Surcharge $',\n price_override: 'Override',\n}\n\ntype PriceRule = {\n id: string\n code: string\n name: string\n description: string | null\n productOfferingId: string | null\n ruleType: string\n value: number\n chargeCodeFilter: string | null\n chargeTypeFilter: string | null\n applicabilityCondition: Record<string, unknown> | null\n sortOrder: number\n isActive: boolean\n createdAt: string\n updatedAt: string\n}\n\ntype ProductOffering = { id: string; code: string; name: string }\n\ntype FormData = {\n name: string\n description: string\n productOfferingId: string\n ruleType: string\n value: string\n chargeCodeFilter: string\n chargeTypeFilter: string\n conditionAttribute: string\n conditionOperator: string\n conditionValue: string\n sortOrder: string\n isActive: boolean\n}\n\nfunction ruleToForm(rule: PriceRule): FormData {\n const cond = rule.applicabilityCondition as { attribute?: string; operator?: string; value?: string } | null\n return {\n name: rule.name,\n description: rule.description ?? '',\n productOfferingId: rule.productOfferingId ?? '',\n ruleType: rule.ruleType,\n value: String(rule.value),\n chargeCodeFilter: rule.chargeCodeFilter ?? '',\n chargeTypeFilter: rule.chargeTypeFilter ?? '',\n conditionAttribute: cond?.attribute ?? '',\n conditionOperator: cond?.operator ?? 'eq',\n conditionValue: cond?.value ?? '',\n sortOrder: String(rule.sortOrder),\n isActive: rule.isActive,\n }\n}\n\nexport default function PriceRuleDetailPage(props: { params?: { id?: string } }) {\n const router = useRouter()\n const urlParams = useParams()\n const ruleId = (props.params?.id ?? urlParams?.id) as string\n\n const [rule, setRule] = React.useState<PriceRule | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n\n const [editing, setEditing] = React.useState(false)\n const [form, setForm] = React.useState<FormData | null>(null)\n const [saving, setSaving] = React.useState(false)\n const [saveError, setSaveError] = React.useState<string | null>(null)\n\n const [deleting, setDeleting] = React.useState(false)\n const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false)\n\n const [offerings, setOfferings] = React.useState<ProductOffering[]>([])\n\n React.useEffect(() => {\n fetch('/api/cpq/product-offerings?pageSize=100')\n .then((r) => r.json())\n .then((data) => setOfferings(data.items ?? []))\n .catch(() => {})\n }, [])\n\n const offeringLookup = React.useMemo(() => {\n const map = new Map<string, ProductOffering>()\n for (const o of offerings) map.set(o.id, o)\n return map\n }, [offerings])\n\n const loadRule = React.useCallback(() => {\n setLoading(true)\n setError(null)\n fetch(`/api/cpq/price-rules?id=${ruleId}`)\n .then(async (r) => {\n if (!r.ok) throw new Error('Not found')\n return r.json()\n })\n .then((data: PriceRule) => {\n setRule(data)\n })\n .catch((err) => setError(err.message))\n .finally(() => setLoading(false))\n }, [ruleId])\n\n React.useEffect(() => { loadRule() }, [loadRule])\n\n function startEdit() {\n if (!rule) return\n setForm(ruleToForm(rule))\n setSaveError(null)\n setEditing(true)\n }\n\n function cancelEdit() {\n setEditing(false)\n setForm(null)\n setSaveError(null)\n }\n\n async function handleSave() {\n if (!form || !rule) return\n setSaving(true)\n setSaveError(null)\n try {\n const payload: Record<string, unknown> = {\n id: rule.id,\n name: form.name,\n ruleType: form.ruleType,\n value: Number(form.value),\n sortOrder: Number(form.sortOrder),\n isActive: form.isActive,\n productOfferingId: form.productOfferingId || null,\n }\n if (form.description) payload.description = form.description\n else payload.description = null\n if (form.chargeCodeFilter) payload.chargeCodeFilter = form.chargeCodeFilter\n else payload.chargeCodeFilter = null\n if (form.chargeTypeFilter) payload.chargeTypeFilter = form.chargeTypeFilter\n else payload.chargeTypeFilter = null\n if (form.conditionAttribute) {\n payload.applicabilityCondition = {\n attribute: form.conditionAttribute,\n operator: form.conditionOperator || 'eq',\n value: form.conditionValue,\n }\n } else {\n payload.applicabilityCondition = null\n }\n\n const res = await fetch('/api/cpq/price-rules', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!res.ok) {\n const err = await res.json()\n throw new Error(err.error ?? 'Failed to update')\n }\n const updated = await res.json()\n setRule(updated)\n setEditing(false)\n setForm(null)\n } catch (err: unknown) {\n setSaveError(err instanceof Error ? err.message : 'Unknown error')\n } finally {\n setSaving(false)\n }\n }\n\n async function handleDelete() {\n setDeleting(true)\n try {\n await fetch('/api/cpq/price-rules', {\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id: ruleId }),\n })\n router.push('/backend/cpq/price-rules')\n } catch {\n setDeleting(false)\n }\n }\n\n async function handleToggleActive() {\n if (!rule) return\n const res = await fetch('/api/cpq/price-rules', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id: rule.id, isActive: !rule.isActive }),\n })\n if (res.ok) {\n const updated = await res.json()\n setRule(updated)\n }\n }\n\n if (loading) {\n return <div className=\"text-sm text-muted-foreground py-12 text-center\">Loading...</div>\n }\n\n if (error || !rule) {\n return (\n <div className=\"space-y-4 py-12 text-center\">\n <div className=\"text-sm text-destructive\">{error ?? 'Rule not found'}</div>\n <button\n onClick={() => router.push('/backend/cpq/price-rules')}\n className=\"text-sm text-primary hover:underline\"\n >\n &larr; Back to Price Rules\n </button>\n </div>\n )\n }\n\n const cond = rule.applicabilityCondition as { attribute?: string; operator?: string; value?: string } | null\n const offering = rule.productOfferingId ? offeringLookup.get(rule.productOfferingId) : null\n\n return (\n <div className=\"space-y-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n onClick={() => router.push('/backend/cpq/price-rules')}\n className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\"\n >\n &larr; Back\n </button>\n <h1 className=\"text-2xl font-bold\">{rule.name}</h1>\n <Tag variant={rule.isActive ? 'success' : 'neutral'} dot>\n {rule.isActive ? 'Active' : 'Inactive'}\n </Tag>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" onClick={handleToggleActive}>\n {rule.isActive ? 'Deactivate' : 'Activate'}\n </Button>\n {!editing && (\n <button\n onClick={startEdit}\n className=\"rounded-md bg-primary px-4 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90\"\n >\n Edit\n </button>\n )}\n <button\n onClick={() => setShowDeleteConfirm(true)}\n className=\"rounded-md border border-destructive/30 px-3 py-1.5 text-sm font-medium text-destructive hover:bg-destructive/10 transition-colors\"\n >\n Delete\n </button>\n </div>\n </div>\n\n {/* Delete confirmation */}\n {showDeleteConfirm && (\n <div className=\"rounded-lg border border-destructive/30 bg-destructive/5 p-4 flex items-center justify-between\">\n <span className=\"text-sm\">Are you sure you want to delete <strong>{rule.name}</strong>?</span>\n <div className=\"flex gap-2\">\n <button\n onClick={handleDelete}\n disabled={deleting}\n className=\"rounded-md bg-destructive px-3 py-1.5 text-sm font-medium text-destructive-foreground hover:bg-destructive/90 disabled:opacity-50\"\n >\n {deleting ? 'Deleting...' : 'Yes, Delete'}\n </button>\n <button\n onClick={() => setShowDeleteConfirm(false)}\n className=\"rounded-md border px-3 py-1.5 text-sm font-medium hover:bg-muted/50\"\n >\n Cancel\n </button>\n </div>\n </div>\n )}\n\n {/* Edit mode */}\n {editing && form ? (\n <div className=\"rounded-lg border bg-card p-6 space-y-4\">\n <h2 className=\"text-lg font-semibold\">Edit Rule</h2>\n {saveError && <div className=\"rounded-md bg-destructive/10 p-3 text-sm text-destructive\">{saveError}</div>}\n <div className=\"grid grid-cols-2 gap-4\">\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">Code</span>\n <input className=\"w-full rounded-md border bg-muted/30 px-3 py-2 text-sm\" value={rule.code} disabled />\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">Name</span>\n <input className=\"w-full rounded-md border px-3 py-2 text-sm\" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">Rule Type</span>\n <select className=\"w-full rounded-md border px-3 py-2 text-sm\" value={form.ruleType} onChange={(e) => setForm({ ...form, ruleType: e.target.value })}>\n {Object.entries(RULE_TYPE_LABELS).map(([k, v]) => <option key={k} value={k}>{v}</option>)}\n </select>\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">Value</span>\n <NumberInput\n value={form.value === '' ? null : Number(form.value)}\n onChange={(n) => setForm({ ...form, value: n == null ? '' : String(n) })}\n />\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">Sort Order</span>\n <NumberInput\n integer\n value={form.sortOrder === '' ? null : Number(form.sortOrder)}\n onChange={(n) => setForm({ ...form, sortOrder: n == null ? '' : String(n) })}\n />\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">Description</span>\n <input className=\"w-full rounded-md border px-3 py-2 text-sm\" placeholder=\"Optional\" value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} />\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">Charge Type Filter</span>\n <select className=\"w-full rounded-md border px-3 py-2 text-sm\" value={form.chargeTypeFilter} onChange={(e) => setForm({ ...form, chargeTypeFilter: e.target.value })}>\n <option value=\"\">All charge types</option>\n <option value=\"nrc\">NRC only</option>\n <option value=\"mrc\">MRC only</option>\n <option value=\"usage\">Usage only</option>\n </select>\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">Charge Code Filter</span>\n <input className=\"w-full rounded-md border px-3 py-2 text-sm\" placeholder=\"e.g. setup_fee (leave empty for all)\" value={form.chargeCodeFilter} onChange={(e) => setForm({ ...form, chargeCodeFilter: e.target.value })} />\n </label>\n <label className=\"space-y-1 col-span-2\">\n <span className=\"text-sm font-medium\">Product Offering</span>\n <select className=\"w-full rounded-md border px-3 py-2 text-sm\" value={form.productOfferingId} onChange={(e) => setForm({ ...form, productOfferingId: e.target.value })}>\n <option value=\"\">Global (all products)</option>\n {offerings.map((o) => <option key={o.id} value={o.id}>{o.name} ({o.code})</option>)}\n </select>\n </label>\n <div className=\"col-span-2 space-y-1\">\n <span className=\"text-sm font-medium\">Applicability Condition</span>\n <span className=\"ml-2 text-xs text-muted-foreground\">(optional)</span>\n <div className=\"grid grid-cols-3 gap-2\">\n <input className=\"w-full rounded-md border px-3 py-2 text-sm\" placeholder=\"Attribute (e.g. port_size)\" value={form.conditionAttribute} onChange={(e) => setForm({ ...form, conditionAttribute: e.target.value })} />\n <select className=\"w-full rounded-md border px-3 py-2 text-sm\" value={form.conditionOperator} onChange={(e) => setForm({ ...form, conditionOperator: e.target.value })}>\n <option value=\"eq\">equals (=)</option>\n <option value=\"neq\">not equals (&#8800;)</option>\n </select>\n <input className=\"w-full rounded-md border px-3 py-2 text-sm\" placeholder=\"Value\" value={form.conditionValue} onChange={(e) => setForm({ ...form, conditionValue: e.target.value })} />\n </div>\n </div>\n </div>\n <label className=\"flex items-center gap-2 text-sm\">\n <input type=\"checkbox\" checked={form.isActive} onChange={(e) => setForm({ ...form, isActive: e.target.checked })} />\n Active\n </label>\n <div className=\"flex gap-2\">\n <button onClick={handleSave} disabled={saving} className=\"rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50\">\n {saving ? 'Saving...' : 'Save Changes'}\n </button>\n <button onClick={cancelEdit} className=\"rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted/50\">\n Cancel\n </button>\n </div>\n </div>\n ) : (\n /* Read-only view */\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Main info */}\n <div className=\"rounded-lg border bg-card p-6 space-y-4\">\n <h2 className=\"text-sm font-semibold text-muted-foreground uppercase tracking-wider\">Rule Configuration</h2>\n <dl className=\"space-y-3\">\n <div className=\"flex justify-between\">\n <dt className=\"text-sm text-muted-foreground\">Code</dt>\n <dd className=\"text-sm font-mono\">{rule.code}</dd>\n </div>\n <div className=\"flex justify-between border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground\">Rule Type</dt>\n <dd className=\"text-sm font-medium\">{RULE_TYPE_LABELS[rule.ruleType] ?? rule.ruleType}</dd>\n </div>\n <div className=\"flex justify-between border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground\">Value</dt>\n <dd className=\"text-sm font-mono font-medium\">\n {rule.ruleType.includes('percent') ? `${rule.value}%` : rule.ruleType === 'price_override' ? `= $${rule.value}` : `$${rule.value}`}\n </dd>\n </div>\n <div className=\"flex justify-between border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground\">Sort Order</dt>\n <dd className=\"text-sm font-mono\">{rule.sortOrder}</dd>\n </div>\n {rule.description && (\n <div className=\"border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground mb-1\">Description</dt>\n <dd className=\"text-sm\">{rule.description}</dd>\n </div>\n )}\n </dl>\n </div>\n\n {/* Scope & Filters */}\n <div className=\"rounded-lg border bg-card p-6 space-y-4\">\n <h2 className=\"text-sm font-semibold text-muted-foreground uppercase tracking-wider\">Scope &amp; Filters</h2>\n <dl className=\"space-y-3\">\n <div className=\"flex justify-between\">\n <dt className=\"text-sm text-muted-foreground\">Scope</dt>\n <dd className=\"text-sm\">\n {offering ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <span className=\"inline-block w-2 h-2 rounded-full bg-status-info-icon\" />\n {offering.name}\n <span className=\"text-muted-foreground font-mono text-xs\">({offering.code})</span>\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1.5\">\n <span className=\"inline-block w-2 h-2 rounded-full bg-muted-foreground\" />\n Global (all offerings)\n </span>\n )}\n </dd>\n </div>\n <div className=\"flex justify-between border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground\">Charge Type</dt>\n <dd className=\"text-sm\">{rule.chargeTypeFilter ? rule.chargeTypeFilter.toUpperCase() : 'All types'}</dd>\n </div>\n <div className=\"flex justify-between border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground\">Charge Code</dt>\n <dd className=\"text-sm font-mono\">{rule.chargeCodeFilter ?? 'All charges'}</dd>\n </div>\n <div className=\"border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground mb-1\">Applicability Condition</dt>\n <dd className=\"text-sm\">\n {cond?.attribute ? (\n <code className=\"rounded bg-muted px-2 py-0.5 text-xs\">\n {cond.attribute} {cond.operator === 'neq' ? '\u2260' : '='} {cond.value ?? '\"\"'}\n </code>\n ) : (\n <span className=\"text-muted-foreground\">No condition (always applies)</span>\n )}\n </dd>\n </div>\n </dl>\n </div>\n\n {/* Metadata */}\n <div className=\"rounded-lg border bg-card p-6 space-y-4 lg:col-span-2\">\n <h2 className=\"text-sm font-semibold text-muted-foreground uppercase tracking-wider\">Metadata</h2>\n <dl className=\"flex gap-8 text-sm\">\n <div>\n <dt className=\"text-muted-foreground\">Created</dt>\n <dd>{new Date(rule.createdAt).toLocaleString()}</dd>\n </div>\n <div>\n <dt className=\"text-muted-foreground\">Updated</dt>\n <dd>{new Date(rule.updatedAt).toLocaleString()}</dd>\n </div>\n <div>\n <dt className=\"text-muted-foreground\">ID</dt>\n <dd className=\"font-mono text-xs\">{rule.id}</dd>\n </div>\n </dl>\n </div>\n </div>\n )}\n </div>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter, useParams } from 'next/navigation'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { NumberInput } from '../../../../components/NumberInput'\nimport { buildRuleTypeLabels } from '../PriceRuleForm'\n\ntype PriceRule = {\n id: string\n code: string\n name: string\n description: string | null\n productOfferingId: string | null\n ruleType: string\n value: number\n chargeCodeFilter: string | null\n chargeTypeFilter: string | null\n applicabilityCondition: Record<string, unknown> | null\n sortOrder: number\n isActive: boolean\n createdAt: string\n updatedAt: string\n}\n\ntype ProductOffering = { id: string; code: string; name: string }\n\ntype FormData = {\n name: string\n description: string\n productOfferingId: string\n ruleType: string\n value: string\n chargeCodeFilter: string\n chargeTypeFilter: string\n conditionAttribute: string\n conditionOperator: string\n conditionValue: string\n sortOrder: string\n isActive: boolean\n}\n\nfunction ruleToForm(rule: PriceRule): FormData {\n const cond = rule.applicabilityCondition as { attribute?: string; operator?: string; value?: string } | null\n return {\n name: rule.name,\n description: rule.description ?? '',\n productOfferingId: rule.productOfferingId ?? '',\n ruleType: rule.ruleType,\n value: String(rule.value),\n chargeCodeFilter: rule.chargeCodeFilter ?? '',\n chargeTypeFilter: rule.chargeTypeFilter ?? '',\n conditionAttribute: cond?.attribute ?? '',\n conditionOperator: cond?.operator ?? 'eq',\n conditionValue: cond?.value ?? '',\n sortOrder: String(rule.sortOrder),\n isActive: rule.isActive,\n }\n}\n\nexport default function PriceRuleDetailPage(props: { params?: { id?: string } }) {\n const t = useT()\n const ruleTypeLabels = React.useMemo(() => buildRuleTypeLabels(t), [t])\n const router = useRouter()\n const urlParams = useParams()\n const ruleId = (props.params?.id ?? urlParams?.id) as string\n\n const [rule, setRule] = React.useState<PriceRule | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n\n const [editing, setEditing] = React.useState(false)\n const [form, setForm] = React.useState<FormData | null>(null)\n const [saving, setSaving] = React.useState(false)\n const [saveError, setSaveError] = React.useState<string | null>(null)\n\n const [deleting, setDeleting] = React.useState(false)\n const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false)\n\n const [offerings, setOfferings] = React.useState<ProductOffering[]>([])\n\n React.useEffect(() => {\n fetch('/api/cpq/product-offerings?pageSize=100')\n .then((r) => r.json())\n .then((data) => setOfferings(data.items ?? []))\n .catch(() => {})\n }, [])\n\n const offeringLookup = React.useMemo(() => {\n const map = new Map<string, ProductOffering>()\n for (const o of offerings) map.set(o.id, o)\n return map\n }, [offerings])\n\n const loadRule = React.useCallback(() => {\n setLoading(true)\n setError(null)\n fetch(`/api/cpq/price-rules?id=${ruleId}`)\n .then(async (r) => {\n if (!r.ok) throw new Error(t('cpq.priceRules.detail.notFound', 'Not found'))\n return r.json()\n })\n .then((data: PriceRule) => {\n setRule(data)\n })\n .catch((err) => setError(err.message))\n .finally(() => setLoading(false))\n }, [ruleId, t])\n\n React.useEffect(() => { loadRule() }, [loadRule])\n\n function startEdit() {\n if (!rule) return\n setForm(ruleToForm(rule))\n setSaveError(null)\n setEditing(true)\n }\n\n function cancelEdit() {\n setEditing(false)\n setForm(null)\n setSaveError(null)\n }\n\n async function handleSave() {\n if (!form || !rule) return\n setSaving(true)\n setSaveError(null)\n try {\n const payload: Record<string, unknown> = {\n id: rule.id,\n name: form.name,\n ruleType: form.ruleType,\n value: Number(form.value),\n sortOrder: Number(form.sortOrder),\n isActive: form.isActive,\n productOfferingId: form.productOfferingId || null,\n }\n if (form.description) payload.description = form.description\n else payload.description = null\n if (form.chargeCodeFilter) payload.chargeCodeFilter = form.chargeCodeFilter\n else payload.chargeCodeFilter = null\n if (form.chargeTypeFilter) payload.chargeTypeFilter = form.chargeTypeFilter\n else payload.chargeTypeFilter = null\n if (form.conditionAttribute) {\n payload.applicabilityCondition = {\n attribute: form.conditionAttribute,\n operator: form.conditionOperator || 'eq',\n value: form.conditionValue,\n }\n } else {\n payload.applicabilityCondition = null\n }\n\n const res = await fetch('/api/cpq/price-rules', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!res.ok) {\n const err = await res.json()\n throw new Error(err.error ?? t('cpq.priceRules.errors.updateFailed', 'Failed to update'))\n }\n const updated = await res.json()\n setRule(updated)\n setEditing(false)\n setForm(null)\n } catch (err: unknown) {\n setSaveError(err instanceof Error ? err.message : t('cpq.priceRules.errors.unknown', 'Unknown error'))\n } finally {\n setSaving(false)\n }\n }\n\n async function handleDelete() {\n setDeleting(true)\n try {\n await fetch('/api/cpq/price-rules', {\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id: ruleId }),\n })\n router.push('/backend/cpq/price-rules')\n } catch {\n setDeleting(false)\n }\n }\n\n async function handleToggleActive() {\n if (!rule) return\n const res = await fetch('/api/cpq/price-rules', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id: rule.id, isActive: !rule.isActive }),\n })\n if (res.ok) {\n const updated = await res.json()\n setRule(updated)\n }\n }\n\n if (loading) {\n return <div className=\"text-sm text-muted-foreground py-12 text-center\">{t('cpq.common.loading', 'Loading...')}</div>\n }\n\n if (error || !rule) {\n return (\n <div className=\"space-y-4 py-12 text-center\">\n <div className=\"text-sm text-destructive\">{error ?? t('cpq.priceRules.detail.ruleNotFound', 'Rule not found')}</div>\n <button\n onClick={() => router.push('/backend/cpq/price-rules')}\n className=\"text-sm text-primary hover:underline\"\n >\n \u2190 {t('cpq.priceRules.detail.backToList', 'Back to Price Rules')}\n </button>\n </div>\n )\n }\n\n const cond = rule.applicabilityCondition as { attribute?: string; operator?: string; value?: string } | null\n const offering = rule.productOfferingId ? offeringLookup.get(rule.productOfferingId) : null\n // `{name}` is left unresolved so the rule name can be rendered in <strong>.\n const deleteConfirmParts = t('cpq.priceRules.detail.deleteConfirm', 'Are you sure you want to delete {name}?').split('{name}')\n\n return (\n <div className=\"space-y-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n onClick={() => router.push('/backend/cpq/price-rules')}\n className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\"\n >\n \u2190 {t('cpq.common.back', 'Back')}\n </button>\n <h1 className=\"text-2xl font-bold\">{rule.name}</h1>\n <Tag variant={rule.isActive ? 'success' : 'neutral'} dot>\n {rule.isActive ? t('cpq.common.active', 'Active') : t('cpq.common.inactive', 'Inactive')}\n </Tag>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" onClick={handleToggleActive}>\n {rule.isActive\n ? t('cpq.priceRules.detail.deactivate', 'Deactivate')\n : t('cpq.priceRules.detail.activate', 'Activate')}\n </Button>\n {!editing && (\n <button\n onClick={startEdit}\n className=\"rounded-md bg-primary px-4 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90\"\n >\n {t('cpq.common.edit', 'Edit')}\n </button>\n )}\n <button\n onClick={() => setShowDeleteConfirm(true)}\n className=\"rounded-md border border-destructive/30 px-3 py-1.5 text-sm font-medium text-destructive hover:bg-destructive/10 transition-colors\"\n >\n {t('cpq.common.delete', 'Delete')}\n </button>\n </div>\n </div>\n\n {/* Delete confirmation */}\n {showDeleteConfirm && (\n <div className=\"rounded-lg border border-destructive/30 bg-destructive/5 p-4 flex items-center justify-between\">\n <span className=\"text-sm\">\n {deleteConfirmParts[0]}\n <strong>{rule.name}</strong>\n {deleteConfirmParts[1]}\n </span>\n <div className=\"flex gap-2\">\n <button\n onClick={handleDelete}\n disabled={deleting}\n className=\"rounded-md bg-destructive px-3 py-1.5 text-sm font-medium text-destructive-foreground hover:bg-destructive/90 disabled:opacity-50\"\n >\n {deleting\n ? t('cpq.priceRules.detail.deleting', 'Deleting...')\n : t('cpq.priceRules.detail.confirmDelete', 'Yes, Delete')}\n </button>\n <button\n onClick={() => setShowDeleteConfirm(false)}\n className=\"rounded-md border px-3 py-1.5 text-sm font-medium hover:bg-muted/50\"\n >\n {t('cpq.common.cancel', 'Cancel')}\n </button>\n </div>\n </div>\n )}\n\n {/* Edit mode */}\n {editing && form ? (\n <div className=\"rounded-lg border bg-card p-6 space-y-4\">\n <h2 className=\"text-lg font-semibold\">{t('cpq.priceRules.form.editTitle', 'Edit Rule')}</h2>\n {saveError && <div className=\"rounded-md bg-destructive/10 p-3 text-sm text-destructive\">{saveError}</div>}\n <div className=\"grid grid-cols-2 gap-4\">\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">{t('cpq.priceRules.form.code', 'Code')}</span>\n <input className=\"w-full rounded-md border bg-muted/30 px-3 py-2 text-sm\" value={rule.code} disabled />\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">{t('cpq.priceRules.form.name', 'Name')}</span>\n <input className=\"w-full rounded-md border px-3 py-2 text-sm\" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">{t('cpq.priceRules.form.ruleType', 'Rule Type')}</span>\n <select className=\"w-full rounded-md border px-3 py-2 text-sm\" value={form.ruleType} onChange={(e) => setForm({ ...form, ruleType: e.target.value })}>\n {Object.entries(ruleTypeLabels).map(([k, v]) => <option key={k} value={k}>{v}</option>)}\n </select>\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">{t('cpq.priceRules.form.value', 'Value')}</span>\n <NumberInput\n value={form.value === '' ? null : Number(form.value)}\n onChange={(n) => setForm({ ...form, value: n == null ? '' : String(n) })}\n />\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">{t('cpq.priceRules.form.sortOrder', 'Sort Order')}</span>\n <NumberInput\n integer\n value={form.sortOrder === '' ? null : Number(form.sortOrder)}\n onChange={(n) => setForm({ ...form, sortOrder: n == null ? '' : String(n) })}\n />\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">{t('cpq.priceRules.form.description', 'Description')}</span>\n <input className=\"w-full rounded-md border px-3 py-2 text-sm\" placeholder={t('cpq.priceRules.form.descriptionPlaceholder', 'Optional')} value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} />\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">{t('cpq.priceRules.form.chargeTypeFilter', 'Charge Type Filter')}</span>\n <select className=\"w-full rounded-md border px-3 py-2 text-sm\" value={form.chargeTypeFilter} onChange={(e) => setForm({ ...form, chargeTypeFilter: e.target.value })}>\n <option value=\"\">{t('cpq.priceRules.form.allChargeTypes', 'All charge types')}</option>\n <option value=\"nrc\">{t('cpq.priceRules.form.nrcOnly', 'NRC only')}</option>\n <option value=\"mrc\">{t('cpq.priceRules.form.mrcOnly', 'MRC only')}</option>\n <option value=\"usage\">{t('cpq.priceRules.form.usageOnly', 'Usage only')}</option>\n </select>\n </label>\n <label className=\"space-y-1\">\n <span className=\"text-sm font-medium\">{t('cpq.priceRules.form.chargeCodeFilter', 'Charge Code Filter')}</span>\n <input className=\"w-full rounded-md border px-3 py-2 text-sm\" placeholder={t('cpq.priceRules.form.chargeCodeFilterPlaceholder', 'e.g. setup_fee (leave empty for all)')} value={form.chargeCodeFilter} onChange={(e) => setForm({ ...form, chargeCodeFilter: e.target.value })} />\n </label>\n <label className=\"space-y-1 col-span-2\">\n <span className=\"text-sm font-medium\">{t('cpq.priceRules.form.productOffering', 'Product Offering')}</span>\n <select className=\"w-full rounded-md border px-3 py-2 text-sm\" value={form.productOfferingId} onChange={(e) => setForm({ ...form, productOfferingId: e.target.value })}>\n <option value=\"\">{t('cpq.priceRules.form.globalAllProducts', 'Global (all products)')}</option>\n {offerings.map((o) => <option key={o.id} value={o.id}>{o.name} ({o.code})</option>)}\n </select>\n </label>\n <div className=\"col-span-2 space-y-1\">\n <span className=\"text-sm font-medium\">{t('cpq.priceRules.form.applicabilityCondition', 'Applicability Condition')}</span>\n <span className=\"ml-2 text-xs text-muted-foreground\">{t('cpq.priceRules.form.optionalHint', '(optional)')}</span>\n <div className=\"grid grid-cols-3 gap-2\">\n <input className=\"w-full rounded-md border px-3 py-2 text-sm\" placeholder={t('cpq.priceRules.form.conditionAttributePlaceholder', 'Attribute (e.g. port_size)')} value={form.conditionAttribute} onChange={(e) => setForm({ ...form, conditionAttribute: e.target.value })} />\n <select className=\"w-full rounded-md border px-3 py-2 text-sm\" value={form.conditionOperator} onChange={(e) => setForm({ ...form, conditionOperator: e.target.value })}>\n <option value=\"eq\">{t('cpq.priceRules.form.operatorEq', 'equals (=)')}</option>\n <option value=\"neq\">{t('cpq.priceRules.form.operatorNeq', 'not equals (\u2260)')}</option>\n </select>\n <input className=\"w-full rounded-md border px-3 py-2 text-sm\" placeholder={t('cpq.priceRules.form.conditionValuePlaceholderShort', 'Value')} value={form.conditionValue} onChange={(e) => setForm({ ...form, conditionValue: e.target.value })} />\n </div>\n </div>\n </div>\n <label className=\"flex items-center gap-2 text-sm\">\n <input type=\"checkbox\" checked={form.isActive} onChange={(e) => setForm({ ...form, isActive: e.target.checked })} />\n {t('cpq.common.active', 'Active')}\n </label>\n <div className=\"flex gap-2\">\n <button onClick={handleSave} disabled={saving} className=\"rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50\">\n {saving ? t('cpq.common.saving', 'Saving...') : t('cpq.priceRules.detail.saveChanges', 'Save Changes')}\n </button>\n <button onClick={cancelEdit} className=\"rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted/50\">\n {t('cpq.common.cancel', 'Cancel')}\n </button>\n </div>\n </div>\n ) : (\n /* Read-only view */\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Main info */}\n <div className=\"rounded-lg border bg-card p-6 space-y-4\">\n <h2 className=\"text-sm font-semibold text-muted-foreground uppercase tracking-wider\">{t('cpq.priceRules.detail.ruleConfiguration', 'Rule Configuration')}</h2>\n <dl className=\"space-y-3\">\n <div className=\"flex justify-between\">\n <dt className=\"text-sm text-muted-foreground\">{t('cpq.priceRules.detail.code', 'Code')}</dt>\n <dd className=\"text-sm font-mono\">{rule.code}</dd>\n </div>\n <div className=\"flex justify-between border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground\">{t('cpq.priceRules.detail.ruleType', 'Rule Type')}</dt>\n <dd className=\"text-sm font-medium\">{ruleTypeLabels[rule.ruleType] ?? rule.ruleType}</dd>\n </div>\n <div className=\"flex justify-between border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground\">{t('cpq.priceRules.detail.value', 'Value')}</dt>\n <dd className=\"text-sm font-mono font-medium\">\n {rule.ruleType.includes('percent') ? `${rule.value}%` : rule.ruleType === 'price_override' ? `= $${rule.value}` : `$${rule.value}`}\n </dd>\n </div>\n <div className=\"flex justify-between border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground\">{t('cpq.priceRules.detail.sortOrder', 'Sort Order')}</dt>\n <dd className=\"text-sm font-mono\">{rule.sortOrder}</dd>\n </div>\n {rule.description && (\n <div className=\"border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground mb-1\">{t('cpq.priceRules.detail.description', 'Description')}</dt>\n <dd className=\"text-sm\">{rule.description}</dd>\n </div>\n )}\n </dl>\n </div>\n\n {/* Scope & Filters */}\n <div className=\"rounded-lg border bg-card p-6 space-y-4\">\n <h2 className=\"text-sm font-semibold text-muted-foreground uppercase tracking-wider\">{t('cpq.priceRules.detail.scopeAndFilters', 'Scope & Filters')}</h2>\n <dl className=\"space-y-3\">\n <div className=\"flex justify-between\">\n <dt className=\"text-sm text-muted-foreground\">{t('cpq.priceRules.detail.scope', 'Scope')}</dt>\n <dd className=\"text-sm\">\n {offering ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <span className=\"inline-block w-2 h-2 rounded-full bg-status-info-icon\" />\n {offering.name}\n <span className=\"text-muted-foreground font-mono text-xs\">({offering.code})</span>\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1.5\">\n <span className=\"inline-block w-2 h-2 rounded-full bg-muted-foreground\" />\n {t('cpq.priceRules.detail.globalAllOfferings', 'Global (all offerings)')}\n </span>\n )}\n </dd>\n </div>\n <div className=\"flex justify-between border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground\">{t('cpq.priceRules.detail.chargeType', 'Charge Type')}</dt>\n <dd className=\"text-sm\">{rule.chargeTypeFilter ? rule.chargeTypeFilter.toUpperCase() : t('cpq.priceRules.detail.allTypes', 'All types')}</dd>\n </div>\n <div className=\"flex justify-between border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground\">{t('cpq.priceRules.detail.chargeCode', 'Charge Code')}</dt>\n <dd className=\"text-sm font-mono\">{rule.chargeCodeFilter ?? t('cpq.priceRules.detail.allCharges', 'All charges')}</dd>\n </div>\n <div className=\"border-t pt-3\">\n <dt className=\"text-sm text-muted-foreground mb-1\">{t('cpq.priceRules.detail.applicabilityCondition', 'Applicability Condition')}</dt>\n <dd className=\"text-sm\">\n {cond?.attribute ? (\n <code className=\"rounded bg-muted px-2 py-0.5 text-xs\">\n {cond.attribute} {cond.operator === 'neq' ? '\u2260' : '='} {cond.value ?? '\"\"'}\n </code>\n ) : (\n <span className=\"text-muted-foreground\">{t('cpq.priceRules.detail.noCondition', 'No condition (always applies)')}</span>\n )}\n </dd>\n </div>\n </dl>\n </div>\n\n {/* Metadata */}\n <div className=\"rounded-lg border bg-card p-6 space-y-4 lg:col-span-2\">\n <h2 className=\"text-sm font-semibold text-muted-foreground uppercase tracking-wider\">{t('cpq.priceRules.detail.metadata', 'Metadata')}</h2>\n <dl className=\"flex gap-8 text-sm\">\n <div>\n <dt className=\"text-muted-foreground\">{t('cpq.priceRules.detail.created', 'Created')}</dt>\n <dd>{new Date(rule.createdAt).toLocaleString()}</dd>\n </div>\n <div>\n <dt className=\"text-muted-foreground\">{t('cpq.priceRules.detail.updated', 'Updated')}</dt>\n <dd>{new Date(rule.updatedAt).toLocaleString()}</dd>\n </div>\n <div>\n <dt className=\"text-muted-foreground\">{t('cpq.priceRules.detail.id', 'ID')}</dt>\n <dd className=\"font-mono text-xs\">{rule.id}</dd>\n </div>\n </dl>\n </div>\n </div>\n )}\n </div>\n )\n}\n"],
5
+ "mappings": ";AA2MW,cAOH,YAPG;AA1MX,YAAY,WAAW;AACvB,SAAS,WAAW,iBAAiB;AACrC,SAAS,cAAc;AACvB,SAAS,WAAW;AACpB,SAAS,YAAY;AACrB,SAAS,mBAAmB;AAC5B,SAAS,2BAA2B;AAoCpC,SAAS,WAAW,MAA2B;AAC7C,QAAM,OAAO,KAAK;AAClB,SAAO;AAAA,IACL,MAAM,KAAK;AAAA,IACX,aAAa,KAAK,eAAe;AAAA,IACjC,mBAAmB,KAAK,qBAAqB;AAAA,IAC7C,UAAU,KAAK;AAAA,IACf,OAAO,OAAO,KAAK,KAAK;AAAA,IACxB,kBAAkB,KAAK,oBAAoB;AAAA,IAC3C,kBAAkB,KAAK,oBAAoB;AAAA,IAC3C,oBAAoB,MAAM,aAAa;AAAA,IACvC,mBAAmB,MAAM,YAAY;AAAA,IACrC,gBAAgB,MAAM,SAAS;AAAA,IAC/B,WAAW,OAAO,KAAK,SAAS;AAAA,IAChC,UAAU,KAAK;AAAA,EACjB;AACF;AAEe,SAAR,oBAAqC,OAAqC;AAC/E,QAAM,IAAI,KAAK;AACf,QAAM,iBAAiB,MAAM,QAAQ,MAAM,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC;AACtE,QAAM,SAAS,UAAU;AACzB,QAAM,YAAY,UAAU;AAC5B,QAAM,SAAU,MAAM,QAAQ,MAAM,WAAW;AAE/C,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAA2B,IAAI;AAC7D,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,IAAI;AACjD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAE5D,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAA0B,IAAI;AAC5D,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAChD,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAwB,IAAI;AAEpE,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,KAAK;AACpD,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAAS,KAAK;AAEtE,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAA4B,CAAC,CAAC;AAEtE,QAAM,UAAU,MAAM;AACpB,UAAM,yCAAyC,EAC5C,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,EACpB,KAAK,CAAC,SAAS,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAC7C,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACnB,GAAG,CAAC,CAAC;AAEL,QAAM,iBAAiB,MAAM,QAAQ,MAAM;AACzC,UAAM,MAAM,oBAAI,IAA6B;AAC7C,eAAW,KAAK,UAAW,KAAI,IAAI,EAAE,IAAI,CAAC;AAC1C,WAAO;AAAA,EACT,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,WAAW,MAAM,YAAY,MAAM;AACvC,eAAW,IAAI;AACf,aAAS,IAAI;AACb,UAAM,2BAA2B,MAAM,EAAE,EACtC,KAAK,OAAO,MAAM;AACjB,UAAI,CAAC,EAAE,GAAI,OAAM,IAAI,MAAM,EAAE,kCAAkC,WAAW,CAAC;AAC3E,aAAO,EAAE,KAAK;AAAA,IAChB,CAAC,EACA,KAAK,CAAC,SAAoB;AACzB,cAAQ,IAAI;AAAA,IACd,CAAC,EACA,MAAM,CAAC,QAAQ,SAAS,IAAI,OAAO,CAAC,EACpC,QAAQ,MAAM,WAAW,KAAK,CAAC;AAAA,EACpC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAEd,QAAM,UAAU,MAAM;AAAE,aAAS;AAAA,EAAE,GAAG,CAAC,QAAQ,CAAC;AAEhD,WAAS,YAAY;AACnB,QAAI,CAAC,KAAM;AACX,YAAQ,WAAW,IAAI,CAAC;AACxB,iBAAa,IAAI;AACjB,eAAW,IAAI;AAAA,EACjB;AAEA,WAAS,aAAa;AACpB,eAAW,KAAK;AAChB,YAAQ,IAAI;AACZ,iBAAa,IAAI;AAAA,EACnB;AAEA,iBAAe,aAAa;AAC1B,QAAI,CAAC,QAAQ,CAAC,KAAM;AACpB,cAAU,IAAI;AACd,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,UAAmC;AAAA,QACvC,IAAI,KAAK;AAAA,QACT,MAAM,KAAK;AAAA,QACX,UAAU,KAAK;AAAA,QACf,OAAO,OAAO,KAAK,KAAK;AAAA,QACxB,WAAW,OAAO,KAAK,SAAS;AAAA,QAChC,UAAU,KAAK;AAAA,QACf,mBAAmB,KAAK,qBAAqB;AAAA,MAC/C;AACA,UAAI,KAAK,YAAa,SAAQ,cAAc,KAAK;AAAA,UAC5C,SAAQ,cAAc;AAC3B,UAAI,KAAK,iBAAkB,SAAQ,mBAAmB,KAAK;AAAA,UACtD,SAAQ,mBAAmB;AAChC,UAAI,KAAK,iBAAkB,SAAQ,mBAAmB,KAAK;AAAA,UACtD,SAAQ,mBAAmB;AAChC,UAAI,KAAK,oBAAoB;AAC3B,gBAAQ,yBAAyB;AAAA,UAC/B,WAAW,KAAK;AAAA,UAChB,UAAU,KAAK,qBAAqB;AAAA,UACpC,OAAO,KAAK;AAAA,QACd;AAAA,MACF,OAAO;AACL,gBAAQ,yBAAyB;AAAA,MACnC;AAEA,YAAM,MAAM,MAAM,MAAM,wBAAwB;AAAA,QAC9C,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,MAC9B,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,cAAM,IAAI,MAAM,IAAI,SAAS,EAAE,sCAAsC,kBAAkB,CAAC;AAAA,MAC1F;AACA,YAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,cAAQ,OAAO;AACf,iBAAW,KAAK;AAChB,cAAQ,IAAI;AAAA,IACd,SAAS,KAAc;AACrB,mBAAa,eAAe,QAAQ,IAAI,UAAU,EAAE,iCAAiC,eAAe,CAAC;AAAA,IACvG,UAAE;AACA,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF;AAEA,iBAAe,eAAe;AAC5B,gBAAY,IAAI;AAChB,QAAI;AACF,YAAM,MAAM,wBAAwB;AAAA,QAClC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,IAAI,OAAO,CAAC;AAAA,MACrC,CAAC;AACD,aAAO,KAAK,0BAA0B;AAAA,IACxC,QAAQ;AACN,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF;AAEA,iBAAe,qBAAqB;AAClC,QAAI,CAAC,KAAM;AACX,UAAM,MAAM,MAAM,MAAM,wBAAwB;AAAA,MAC9C,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,IAAI,KAAK,IAAI,UAAU,CAAC,KAAK,SAAS,CAAC;AAAA,IAChE,CAAC;AACD,QAAI,IAAI,IAAI;AACV,YAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,cAAQ,OAAO;AAAA,IACjB;AAAA,EACF;AAEA,MAAI,SAAS;AACX,WAAO,oBAAC,SAAI,WAAU,mDAAmD,YAAE,sBAAsB,YAAY,GAAE;AAAA,EACjH;AAEA,MAAI,SAAS,CAAC,MAAM;AAClB,WACE,qBAAC,SAAI,WAAU,+BACb;AAAA,0BAAC,SAAI,WAAU,4BAA4B,mBAAS,EAAE,sCAAsC,gBAAgB,GAAE;AAAA,MAC9G;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM,OAAO,KAAK,0BAA0B;AAAA,UACrD,WAAU;AAAA,UACX;AAAA;AAAA,YACI,EAAE,oCAAoC,qBAAqB;AAAA;AAAA;AAAA,MAChE;AAAA,OACF;AAAA,EAEJ;AAEA,QAAM,OAAO,KAAK;AAClB,QAAM,WAAW,KAAK,oBAAoB,eAAe,IAAI,KAAK,iBAAiB,IAAI;AAEvF,QAAM,qBAAqB,EAAE,uCAAuC,yCAAyC,EAAE,MAAM,QAAQ;AAE7H,SACE,qBAAC,SAAI,WAAU,aAEb;AAAA,yBAAC,SAAI,WAAU,qCACb;AAAA,2BAAC,SAAI,WAAU,2BACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,OAAO,KAAK,0BAA0B;AAAA,YACrD,WAAU;AAAA,YACX;AAAA;AAAA,cACI,EAAE,mBAAmB,MAAM;AAAA;AAAA;AAAA,QAChC;AAAA,QACA,oBAAC,QAAG,WAAU,sBAAsB,eAAK,MAAK;AAAA,QAC9C,oBAAC,OAAI,SAAS,KAAK,WAAW,YAAY,WAAW,KAAG,MACrD,eAAK,WAAW,EAAE,qBAAqB,QAAQ,IAAI,EAAE,uBAAuB,UAAU,GACzF;AAAA,SACF;AAAA,MACA,qBAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,SAAS,oBAC9C,eAAK,WACF,EAAE,oCAAoC,YAAY,IAClD,EAAE,kCAAkC,UAAU,GACpD;AAAA,QACC,CAAC,WACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS;AAAA,YACT,WAAU;AAAA,YAET,YAAE,mBAAmB,MAAM;AAAA;AAAA,QAC9B;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,qBAAqB,IAAI;AAAA,YACxC,WAAU;AAAA,YAET,YAAE,qBAAqB,QAAQ;AAAA;AAAA,QAClC;AAAA,SACF;AAAA,OACF;AAAA,IAGC,qBACC,qBAAC,SAAI,WAAU,kGACb;AAAA,2BAAC,UAAK,WAAU,WACb;AAAA,2BAAmB,CAAC;AAAA,QACrB,oBAAC,YAAQ,eAAK,MAAK;AAAA,QAClB,mBAAmB,CAAC;AAAA,SACvB;AAAA,MACA,qBAAC,SAAI,WAAU,cACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS;AAAA,YACT,UAAU;AAAA,YACV,WAAU;AAAA,YAET,qBACG,EAAE,kCAAkC,aAAa,IACjD,EAAE,uCAAuC,aAAa;AAAA;AAAA,QAC5D;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,qBAAqB,KAAK;AAAA,YACzC,WAAU;AAAA,YAET,YAAE,qBAAqB,QAAQ;AAAA;AAAA,QAClC;AAAA,SACF;AAAA,OACF;AAAA,IAID,WAAW,OACV,qBAAC,SAAI,WAAU,2CACb;AAAA,0BAAC,QAAG,WAAU,yBAAyB,YAAE,iCAAiC,WAAW,GAAE;AAAA,MACtF,aAAa,oBAAC,SAAI,WAAU,6DAA6D,qBAAU;AAAA,MACpG,qBAAC,SAAI,WAAU,0BACb;AAAA,6BAAC,WAAM,WAAU,aACf;AAAA,8BAAC,UAAK,WAAU,uBAAuB,YAAE,4BAA4B,MAAM,GAAE;AAAA,UAC7E,oBAAC,WAAM,WAAU,0DAAyD,OAAO,KAAK,MAAM,UAAQ,MAAC;AAAA,WACvG;AAAA,QACA,qBAAC,WAAM,WAAU,aACf;AAAA,8BAAC,UAAK,WAAU,uBAAuB,YAAE,4BAA4B,MAAM,GAAE;AAAA,UAC7E,oBAAC,WAAM,WAAU,8CAA6C,OAAO,KAAK,MAAM,UAAU,CAAC,MAAM,QAAQ,EAAE,GAAG,MAAM,MAAM,EAAE,OAAO,MAAM,CAAC,GAAG;AAAA,WAC/I;AAAA,QACA,qBAAC,WAAM,WAAU,aACf;AAAA,8BAAC,UAAK,WAAU,uBAAuB,YAAE,gCAAgC,WAAW,GAAE;AAAA,UACtF,oBAAC,YAAO,WAAU,8CAA6C,OAAO,KAAK,UAAU,UAAU,CAAC,MAAM,QAAQ,EAAE,GAAG,MAAM,UAAU,EAAE,OAAO,MAAM,CAAC,GAChJ,iBAAO,QAAQ,cAAc,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,oBAAC,YAAe,OAAO,GAAI,eAAd,CAAgB,CAAS,GACxF;AAAA,WACF;AAAA,QACA,qBAAC,WAAM,WAAU,aACf;AAAA,8BAAC,UAAK,WAAU,uBAAuB,YAAE,6BAA6B,OAAO,GAAE;AAAA,UAC/E;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,KAAK,UAAU,KAAK,OAAO,OAAO,KAAK,KAAK;AAAA,cACnD,UAAU,CAAC,MAAM,QAAQ,EAAE,GAAG,MAAM,OAAO,KAAK,OAAO,KAAK,OAAO,CAAC,EAAE,CAAC;AAAA;AAAA,UACzE;AAAA,WACF;AAAA,QACA,qBAAC,WAAM,WAAU,aACf;AAAA,8BAAC,UAAK,WAAU,uBAAuB,YAAE,iCAAiC,YAAY,GAAE;AAAA,UACxF;AAAA,YAAC;AAAA;AAAA,cACC,SAAO;AAAA,cACP,OAAO,KAAK,cAAc,KAAK,OAAO,OAAO,KAAK,SAAS;AAAA,cAC3D,UAAU,CAAC,MAAM,QAAQ,EAAE,GAAG,MAAM,WAAW,KAAK,OAAO,KAAK,OAAO,CAAC,EAAE,CAAC;AAAA;AAAA,UAC7E;AAAA,WACF;AAAA,QACA,qBAAC,WAAM,WAAU,aACf;AAAA,8BAAC,UAAK,WAAU,uBAAuB,YAAE,mCAAmC,aAAa,GAAE;AAAA,UAC3F,oBAAC,WAAM,WAAU,8CAA6C,aAAa,EAAE,8CAA8C,UAAU,GAAG,OAAO,KAAK,aAAa,UAAU,CAAC,MAAM,QAAQ,EAAE,GAAG,MAAM,aAAa,EAAE,OAAO,MAAM,CAAC,GAAG;AAAA,WACvO;AAAA,QACA,qBAAC,WAAM,WAAU,aACf;AAAA,8BAAC,UAAK,WAAU,uBAAuB,YAAE,wCAAwC,oBAAoB,GAAE;AAAA,UACvG,qBAAC,YAAO,WAAU,8CAA6C,OAAO,KAAK,kBAAkB,UAAU,CAAC,MAAM,QAAQ,EAAE,GAAG,MAAM,kBAAkB,EAAE,OAAO,MAAM,CAAC,GACjK;AAAA,gCAAC,YAAO,OAAM,IAAI,YAAE,sCAAsC,kBAAkB,GAAE;AAAA,YAC9E,oBAAC,YAAO,OAAM,OAAO,YAAE,+BAA+B,UAAU,GAAE;AAAA,YAClE,oBAAC,YAAO,OAAM,OAAO,YAAE,+BAA+B,UAAU,GAAE;AAAA,YAClE,oBAAC,YAAO,OAAM,SAAS,YAAE,iCAAiC,YAAY,GAAE;AAAA,aAC1E;AAAA,WACF;AAAA,QACA,qBAAC,WAAM,WAAU,aACf;AAAA,8BAAC,UAAK,WAAU,uBAAuB,YAAE,wCAAwC,oBAAoB,GAAE;AAAA,UACvG,oBAAC,WAAM,WAAU,8CAA6C,aAAa,EAAE,mDAAmD,sCAAsC,GAAG,OAAO,KAAK,kBAAkB,UAAU,CAAC,MAAM,QAAQ,EAAE,GAAG,MAAM,kBAAkB,EAAE,OAAO,MAAM,CAAC,GAAG;AAAA,WAClR;AAAA,QACA,qBAAC,WAAM,WAAU,wBACf;AAAA,8BAAC,UAAK,WAAU,uBAAuB,YAAE,uCAAuC,kBAAkB,GAAE;AAAA,UACpG,qBAAC,YAAO,WAAU,8CAA6C,OAAO,KAAK,mBAAmB,UAAU,CAAC,MAAM,QAAQ,EAAE,GAAG,MAAM,mBAAmB,EAAE,OAAO,MAAM,CAAC,GACnK;AAAA,gCAAC,YAAO,OAAM,IAAI,YAAE,yCAAyC,uBAAuB,GAAE;AAAA,YACrF,UAAU,IAAI,CAAC,MAAM,qBAAC,YAAkB,OAAO,EAAE,IAAK;AAAA,gBAAE;AAAA,cAAK;AAAA,cAAG,EAAE;AAAA,cAAK;AAAA,iBAArC,EAAE,EAAoC,CAAS;AAAA,aACpF;AAAA,WACF;AAAA,QACA,qBAAC,SAAI,WAAU,wBACb;AAAA,8BAAC,UAAK,WAAU,uBAAuB,YAAE,8CAA8C,yBAAyB,GAAE;AAAA,UAClH,oBAAC,UAAK,WAAU,sCAAsC,YAAE,oCAAoC,YAAY,GAAE;AAAA,UAC1G,qBAAC,SAAI,WAAU,0BACb;AAAA,gCAAC,WAAM,WAAU,8CAA6C,aAAa,EAAE,qDAAqD,4BAA4B,GAAG,OAAO,KAAK,oBAAoB,UAAU,CAAC,MAAM,QAAQ,EAAE,GAAG,MAAM,oBAAoB,EAAE,OAAO,MAAM,CAAC,GAAG;AAAA,YAC5Q,qBAAC,YAAO,WAAU,8CAA6C,OAAO,KAAK,mBAAmB,UAAU,CAAC,MAAM,QAAQ,EAAE,GAAG,MAAM,mBAAmB,EAAE,OAAO,MAAM,CAAC,GACnK;AAAA,kCAAC,YAAO,OAAM,MAAM,YAAE,kCAAkC,YAAY,GAAE;AAAA,cACtE,oBAAC,YAAO,OAAM,OAAO,YAAE,mCAAmC,qBAAgB,GAAE;AAAA,eAC9E;AAAA,YACA,oBAAC,WAAM,WAAU,8CAA6C,aAAa,EAAE,sDAAsD,OAAO,GAAG,OAAO,KAAK,gBAAgB,UAAU,CAAC,MAAM,QAAQ,EAAE,GAAG,MAAM,gBAAgB,EAAE,OAAO,MAAM,CAAC,GAAG;AAAA,aAClP;AAAA,WACF;AAAA,SACF;AAAA,MACA,qBAAC,WAAM,WAAU,mCACf;AAAA,4BAAC,WAAM,MAAK,YAAW,SAAS,KAAK,UAAU,UAAU,CAAC,MAAM,QAAQ,EAAE,GAAG,MAAM,UAAU,EAAE,OAAO,QAAQ,CAAC,GAAG;AAAA,QACjH,EAAE,qBAAqB,QAAQ;AAAA,SAClC;AAAA,MACA,qBAAC,SAAI,WAAU,cACb;AAAA,4BAAC,YAAO,SAAS,YAAY,UAAU,QAAQ,WAAU,uHACtD,mBAAS,EAAE,qBAAqB,WAAW,IAAI,EAAE,qCAAqC,cAAc,GACvG;AAAA,QACA,oBAAC,YAAO,SAAS,YAAY,WAAU,qEACpC,YAAE,qBAAqB,QAAQ,GAClC;AAAA,SACF;AAAA,OACF;AAAA;AAAA,MAGA,qBAAC,SAAI,WAAU,yCAEb;AAAA,6BAAC,SAAI,WAAU,2CACb;AAAA,8BAAC,QAAG,WAAU,wEAAwE,YAAE,2CAA2C,oBAAoB,GAAE;AAAA,UACzJ,qBAAC,QAAG,WAAU,aACZ;AAAA,iCAAC,SAAI,WAAU,wBACb;AAAA,kCAAC,QAAG,WAAU,iCAAiC,YAAE,8BAA8B,MAAM,GAAE;AAAA,cACvF,oBAAC,QAAG,WAAU,qBAAqB,eAAK,MAAK;AAAA,eAC/C;AAAA,YACA,qBAAC,SAAI,WAAU,sCACb;AAAA,kCAAC,QAAG,WAAU,iCAAiC,YAAE,kCAAkC,WAAW,GAAE;AAAA,cAChG,oBAAC,QAAG,WAAU,uBAAuB,yBAAe,KAAK,QAAQ,KAAK,KAAK,UAAS;AAAA,eACtF;AAAA,YACA,qBAAC,SAAI,WAAU,sCACb;AAAA,kCAAC,QAAG,WAAU,iCAAiC,YAAE,+BAA+B,OAAO,GAAE;AAAA,cACzF,oBAAC,QAAG,WAAU,iCACX,eAAK,SAAS,SAAS,SAAS,IAAI,GAAG,KAAK,KAAK,MAAM,KAAK,aAAa,mBAAmB,MAAM,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,IAClI;AAAA,eACF;AAAA,YACA,qBAAC,SAAI,WAAU,sCACb;AAAA,kCAAC,QAAG,WAAU,iCAAiC,YAAE,mCAAmC,YAAY,GAAE;AAAA,cAClG,oBAAC,QAAG,WAAU,qBAAqB,eAAK,WAAU;AAAA,eACpD;AAAA,YACC,KAAK,eACJ,qBAAC,SAAI,WAAU,iBACb;AAAA,kCAAC,QAAG,WAAU,sCAAsC,YAAE,qCAAqC,aAAa,GAAE;AAAA,cAC1G,oBAAC,QAAG,WAAU,WAAW,eAAK,aAAY;AAAA,eAC5C;AAAA,aAEJ;AAAA,WACF;AAAA,QAGA,qBAAC,SAAI,WAAU,2CACb;AAAA,8BAAC,QAAG,WAAU,wEAAwE,YAAE,yCAAyC,iBAAiB,GAAE;AAAA,UACpJ,qBAAC,QAAG,WAAU,aACZ;AAAA,iCAAC,SAAI,WAAU,wBACb;AAAA,kCAAC,QAAG,WAAU,iCAAiC,YAAE,+BAA+B,OAAO,GAAE;AAAA,cACzF,oBAAC,QAAG,WAAU,WACX,qBACC,qBAAC,UAAK,WAAU,oCACd;AAAA,oCAAC,UAAK,WAAU,yDAAwD;AAAA,gBACvE,SAAS;AAAA,gBACV,qBAAC,UAAK,WAAU,2CAA0C;AAAA;AAAA,kBAAE,SAAS;AAAA,kBAAK;AAAA,mBAAC;AAAA,iBAC7E,IAEA,qBAAC,UAAK,WAAU,oCACd;AAAA,oCAAC,UAAK,WAAU,yDAAwD;AAAA,gBACvE,EAAE,4CAA4C,wBAAwB;AAAA,iBACzE,GAEJ;AAAA,eACF;AAAA,YACA,qBAAC,SAAI,WAAU,sCACb;AAAA,kCAAC,QAAG,WAAU,iCAAiC,YAAE,oCAAoC,aAAa,GAAE;AAAA,cACpG,oBAAC,QAAG,WAAU,WAAW,eAAK,mBAAmB,KAAK,iBAAiB,YAAY,IAAI,EAAE,kCAAkC,WAAW,GAAE;AAAA,eAC1I;AAAA,YACA,qBAAC,SAAI,WAAU,sCACb;AAAA,kCAAC,QAAG,WAAU,iCAAiC,YAAE,oCAAoC,aAAa,GAAE;AAAA,cACpG,oBAAC,QAAG,WAAU,qBAAqB,eAAK,oBAAoB,EAAE,oCAAoC,aAAa,GAAE;AAAA,eACnH;AAAA,YACA,qBAAC,SAAI,WAAU,iBACb;AAAA,kCAAC,QAAG,WAAU,sCAAsC,YAAE,gDAAgD,yBAAyB,GAAE;AAAA,cACjI,oBAAC,QAAG,WAAU,WACX,gBAAM,YACL,qBAAC,UAAK,WAAU,wCACb;AAAA,qBAAK;AAAA,gBAAU;AAAA,gBAAE,KAAK,aAAa,QAAQ,WAAM;AAAA,gBAAI;AAAA,gBAAE,KAAK,SAAS;AAAA,iBACxE,IAEA,oBAAC,UAAK,WAAU,yBAAyB,YAAE,qCAAqC,+BAA+B,GAAE,GAErH;AAAA,eACF;AAAA,aACF;AAAA,WACF;AAAA,QAGA,qBAAC,SAAI,WAAU,yDACb;AAAA,8BAAC,QAAG,WAAU,wEAAwE,YAAE,kCAAkC,UAAU,GAAE;AAAA,UACtI,qBAAC,QAAG,WAAU,sBACZ;AAAA,iCAAC,SACC;AAAA,kCAAC,QAAG,WAAU,yBAAyB,YAAE,iCAAiC,SAAS,GAAE;AAAA,cACrF,oBAAC,QAAI,cAAI,KAAK,KAAK,SAAS,EAAE,eAAe,GAAE;AAAA,eACjD;AAAA,YACA,qBAAC,SACC;AAAA,kCAAC,QAAG,WAAU,yBAAyB,YAAE,iCAAiC,SAAS,GAAE;AAAA,cACrF,oBAAC,QAAI,cAAI,KAAK,KAAK,SAAS,EAAE,eAAe,GAAE;AAAA,eACjD;AAAA,YACA,qBAAC,SACC;AAAA,kCAAC,QAAG,WAAU,yBAAyB,YAAE,4BAA4B,IAAI,GAAE;AAAA,cAC3E,oBAAC,QAAG,WAAU,qBAAqB,eAAK,IAAG;AAAA,eAC7C;AAAA,aACF;AAAA,WACF;AAAA,SACF;AAAA;AAAA,KAEJ;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -4,7 +4,7 @@ const metadata = {
4
4
  navHidden: true,
5
5
  breadcrumb: [
6
6
  { label: "Price Rules", labelKey: "cpq.priceRules.list.title", href: "/backend/cpq/price-rules" },
7
- { label: "Rule Detail" }
7
+ { label: "Rule Detail", labelKey: "cpq.priceRules.detail.breadcrumb" }
8
8
  ]
9
9
  };
10
10
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/cpq/backend/cpq/price-rules/%5Bid%5D/page.meta.ts"],
4
- "sourcesContent": ["export const metadata = {\n requireAuth: true,\n requireFeatures: ['cpq.pricing.view'],\n navHidden: true,\n breadcrumb: [\n { label: 'Price Rules', labelKey: 'cpq.priceRules.list.title', href: '/backend/cpq/price-rules' },\n { label: 'Rule Detail' },\n ],\n} as const\n"],
5
- "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,kBAAkB;AAAA,EACpC,WAAW;AAAA,EACX,YAAY;AAAA,IACV,EAAE,OAAO,eAAe,UAAU,6BAA6B,MAAM,2BAA2B;AAAA,IAChG,EAAE,OAAO,cAAc;AAAA,EACzB;AACF;",
4
+ "sourcesContent": ["export const metadata = {\n requireAuth: true,\n requireFeatures: ['cpq.pricing.view'],\n navHidden: true,\n breadcrumb: [\n { label: 'Price Rules', labelKey: 'cpq.priceRules.list.title', href: '/backend/cpq/price-rules' },\n { label: 'Rule Detail', labelKey: 'cpq.priceRules.detail.breadcrumb' },\n ],\n} as const\n"],
5
+ "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,kBAAkB;AAAA,EACpC,WAAW;AAAA,EACX,YAAY;AAAA,IACV,EAAE,OAAO,eAAe,UAAU,6BAA6B,MAAM,2BAA2B;AAAA,IAChG,EAAE,OAAO,eAAe,UAAU,mCAAmC;AAAA,EACvE;AACF;",
6
6
  "names": []
7
7
  }
@@ -8,7 +8,7 @@ import { Tag } from "@open-mercato/ui/primitives/tag";
8
8
  import { CpqListView, useCpqListData } from "../../../components/CpqListView.js";
9
9
  import {
10
10
  PriceRuleForm,
11
- RULE_TYPE_LABELS,
11
+ buildRuleTypeLabels,
12
12
  emptyPriceRuleForm,
13
13
  priceRuleFormToPayload
14
14
  } from "./PriceRuleForm.js";
@@ -48,17 +48,18 @@ function PriceRulesPage() {
48
48
  fetch("/api/cpq/product-offerings?pageSize=100").then((r) => r.json()).then((d) => setOfferings(d.items ?? [])).catch(() => {
49
49
  });
50
50
  }, []);
51
+ const ruleTypeLabels = React.useMemo(() => buildRuleTypeLabels(t), [t]);
51
52
  const ruleTypeOptions = React.useMemo(
52
- () => Object.entries(RULE_TYPE_LABELS).map(([value, label]) => ({ value, label })),
53
- []
53
+ () => Object.entries(ruleTypeLabels).map(([value, label]) => ({ value, label })),
54
+ [ruleTypeLabels]
54
55
  );
55
56
  const chargeTypeOptions = React.useMemo(
56
57
  () => [
57
- { value: "nrc", label: "NRC" },
58
- { value: "mrc", label: "MRC" },
59
- { value: "usage", label: "Usage" }
58
+ { value: "nrc", label: t("cpq.priceRules.chargeTypes.nrc", "NRC") },
59
+ { value: "mrc", label: t("cpq.priceRules.chargeTypes.mrc", "MRC") },
60
+ { value: "usage", label: t("cpq.priceRules.chargeTypes.usage", "Usage") }
60
61
  ],
61
- []
62
+ [t]
62
63
  );
63
64
  const filters = React.useMemo(
64
65
  () => [
@@ -104,16 +105,18 @@ function PriceRulesPage() {
104
105
  });
105
106
  if (!res.ok) {
106
107
  const err = await res.json();
107
- throw new Error(err.details ? JSON.stringify(err.details) : err.error ?? "Failed to create");
108
+ throw new Error(
109
+ err.details ? JSON.stringify(err.details) : err.error ?? t("cpq.priceRules.errors.createFailed", "Failed to create")
110
+ );
108
111
  }
109
112
  setShowForm(false);
110
113
  data.reload();
111
114
  } catch (err) {
112
- setError(err instanceof Error ? err.message : "Unknown error");
115
+ setError(err instanceof Error ? err.message : t("cpq.priceRules.errors.unknown", "Unknown error"));
113
116
  } finally {
114
117
  setSaving(false);
115
118
  }
116
- }, [data, form]);
119
+ }, [data, form, t]);
117
120
  const columns = React.useMemo(
118
121
  () => [
119
122
  {
@@ -134,7 +137,7 @@ function PriceRulesPage() {
134
137
  {
135
138
  accessorKey: "ruleType",
136
139
  header: t("cpq.priceRules.table.type", "Type"),
137
- cell: ({ row }) => /* @__PURE__ */ jsx("span", { children: RULE_TYPE_LABELS[row.original.ruleType] ?? row.original.ruleType })
140
+ cell: ({ row }) => /* @__PURE__ */ jsx("span", { children: ruleTypeLabels[row.original.ruleType] ?? row.original.ruleType })
138
141
  },
139
142
  {
140
143
  id: "value",
@@ -144,7 +147,7 @@ function PriceRulesPage() {
144
147
  {
145
148
  id: "scope",
146
149
  header: t("cpq.priceRules.table.scope", "Scope"),
147
- cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "text-xs", children: row.original.productOfferingId ? row.original.productOfferingName ?? "Product-scoped" : "Global" })
150
+ cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "text-xs", children: row.original.productOfferingId ? row.original.productOfferingName ?? t("cpq.priceRules.scope.productScoped", "Product-scoped") : t("cpq.priceRules.scope.global", "Global") })
148
151
  },
149
152
  {
150
153
  id: "filter",
@@ -166,10 +169,10 @@ function PriceRulesPage() {
166
169
  {
167
170
  accessorKey: "isActive",
168
171
  header: t("cpq.priceRules.table.status", "Status"),
169
- cell: ({ row }) => /* @__PURE__ */ jsx(Tag, { variant: row.original.isActive ? "success" : "neutral", dot: true, children: row.original.isActive ? "Active" : "Inactive" })
172
+ cell: ({ row }) => /* @__PURE__ */ jsx(Tag, { variant: row.original.isActive ? "success" : "neutral", dot: true, children: row.original.isActive ? t("cpq.common.active", "Active") : t("cpq.common.inactive", "Inactive") })
170
173
  }
171
174
  ],
172
- [t]
175
+ [ruleTypeLabels, t]
173
176
  );
174
177
  return /* @__PURE__ */ jsx(
175
178
  CpqListView,
@@ -201,7 +204,7 @@ function PriceRulesPage() {
201
204
  onCancel: () => setShowForm(false)
202
205
  }
203
206
  ) : null,
204
- emptyState: /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: 'No price rules found. Click "Create Rule" to add one.' })
207
+ emptyState: /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: t("cpq.priceRules.empty", 'No price rules found. Click "Create Rule" to add one.') })
205
208
  }
206
209
  );
207
210
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/cpq/backend/cpq/price-rules/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { CpqListView, useCpqListData } from '../../../components/CpqListView'\nimport {\n PriceRuleForm,\n RULE_TYPE_LABELS,\n emptyPriceRuleForm,\n priceRuleFormToPayload,\n type PriceRule,\n type PriceRuleFormData,\n type ProductOffering,\n} from './PriceRuleForm'\n\nconst PAGE_SIZE = 50\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.ruleType === 'string' && values.ruleType) {\n params.set('ruleType', values.ruleType)\n }\n if (typeof values.chargeTypeFilter === 'string' && values.chargeTypeFilter) {\n params.set('chargeTypeFilter', values.chargeTypeFilter)\n }\n if (values.globalOnly === true) params.set('globalOnly', 'true')\n if (values.isActive === true) params.set('isActive', 'true')\n if (values.isActive === false) params.set('isActive', 'false')\n}\n\nfunction formatValue(rule: PriceRule) {\n if (rule.ruleType.includes('percent')) return `${rule.value}%`\n if (rule.ruleType === 'price_override') return `= ${rule.value}`\n return `${rule.value}`\n}\n\nexport default function PriceRulesPage() {\n const t = useT()\n const router = useRouter()\n\n const data = useCpqListData<PriceRule>({\n endpoint: '/api/cpq/price-rules',\n pageSize: PAGE_SIZE,\n initialSorting: [{ id: 'sortOrder', desc: false }],\n buildFilterParams,\n loadErrorMessage: t('cpq.priceRules.list.error.load', 'Failed to load price rules'),\n })\n\n const [showForm, setShowForm] = React.useState(false)\n const [form, setForm] = React.useState<PriceRuleFormData>(emptyPriceRuleForm)\n const [saving, setSaving] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [offerings, setOfferings] = React.useState<ProductOffering[]>([])\n\n React.useEffect(() => {\n fetch('/api/cpq/product-offerings?pageSize=100')\n .then((r) => r.json())\n .then((d) => setOfferings(d.items ?? []))\n .catch(() => {})\n }, [])\n\n const ruleTypeOptions = React.useMemo(\n () => Object.entries(RULE_TYPE_LABELS).map(([value, label]) => ({ value, label })),\n [],\n )\n\n const chargeTypeOptions = React.useMemo(\n () => [\n { value: 'nrc', label: 'NRC' },\n { value: 'mrc', label: 'MRC' },\n { value: 'usage', label: 'Usage' },\n ],\n [],\n )\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'ruleType',\n label: t('cpq.priceRules.filters.ruleType', 'Rule Type'),\n type: 'select',\n options: ruleTypeOptions,\n },\n {\n id: 'chargeTypeFilter',\n label: t('cpq.priceRules.filters.chargeType', 'Charge Type'),\n type: 'select',\n options: chargeTypeOptions,\n },\n {\n id: 'globalOnly',\n label: t('cpq.priceRules.filters.globalOnly', 'Global only'),\n type: 'checkbox',\n },\n {\n id: 'isActive',\n label: t('cpq.priceRules.filters.isActive', 'Active'),\n type: 'checkbox',\n },\n ],\n [chargeTypeOptions, ruleTypeOptions, t],\n )\n\n const openCreate = React.useCallback(() => {\n setForm(emptyPriceRuleForm)\n setError(null)\n setShowForm(true)\n }, [])\n\n const handleSave = React.useCallback(async () => {\n setSaving(true)\n setError(null)\n try {\n const payload = priceRuleFormToPayload(form)\n const res = await fetch('/api/cpq/price-rules', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!res.ok) {\n const err = await res.json()\n throw new Error(err.details ? JSON.stringify(err.details) : err.error ?? 'Failed to create')\n }\n setShowForm(false)\n data.reload()\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : 'Unknown error')\n } finally {\n setSaving(false)\n }\n }, [data, form])\n\n const columns = React.useMemo<ColumnDef<PriceRule>[]>(\n () => [\n {\n accessorKey: 'sortOrder',\n header: t('cpq.priceRules.table.order', 'Order'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">{row.original.sortOrder}</span>,\n },\n {\n accessorKey: 'name',\n header: t('cpq.priceRules.table.name', 'Name'),\n cell: ({ row }) => <span className=\"font-medium\">{row.original.name}</span>,\n },\n {\n accessorKey: 'code',\n header: t('cpq.priceRules.table.code', 'Code'),\n cell: ({ row }) => <span className=\"font-mono text-xs\">{row.original.code}</span>,\n },\n {\n accessorKey: 'ruleType',\n header: t('cpq.priceRules.table.type', 'Type'),\n cell: ({ row }) => (\n <span>{RULE_TYPE_LABELS[row.original.ruleType] ?? row.original.ruleType}</span>\n ),\n },\n {\n id: 'value',\n header: t('cpq.priceRules.table.value', 'Value'),\n cell: ({ row }) => <span className=\"font-mono\">{formatValue(row.original)}</span>,\n },\n {\n id: 'scope',\n header: t('cpq.priceRules.table.scope', 'Scope'),\n cell: ({ row }) => (\n <span className=\"text-xs\">\n {row.original.productOfferingId\n ? row.original.productOfferingName ?? 'Product-scoped'\n : 'Global'}\n </span>\n ),\n },\n {\n id: 'filter',\n header: t('cpq.priceRules.table.filter', 'Filter'),\n cell: ({ row }) => {\n const rule = row.original\n const parts: string[] = []\n if (rule.chargeTypeFilter) parts.push(rule.chargeTypeFilter)\n if (rule.chargeCodeFilter) parts.push(rule.chargeCodeFilter)\n const cond = rule.applicabilityCondition as\n | { attribute?: string; operator?: string; value?: string }\n | null\n if (cond?.attribute) {\n const op = cond.operator === 'neq' ? '\u2260' : '='\n parts.push(`${cond.attribute} ${op} ${cond.value ?? '\"\"'}`)\n }\n return (\n <span className=\"text-xs text-muted-foreground\">\n {parts.length > 0 ? parts.join(' / ') : '\u2014'}\n </span>\n )\n },\n meta: { truncate: true, maxWidth: 240 },\n },\n {\n accessorKey: 'isActive',\n header: t('cpq.priceRules.table.status', 'Status'),\n cell: ({ row }) => (\n <Tag variant={row.original.isActive ? 'success' : 'neutral'} dot>\n {row.original.isActive ? 'Active' : 'Inactive'}\n </Tag>\n ),\n },\n ],\n [t],\n )\n\n return (\n <CpqListView<PriceRule>\n title={t('cpq.priceRules.list.title', 'Price Rules')}\n tableId=\"cpq.price-rules.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.priceRules.search.placeholder', 'Search price rules...')}\n actions={\n <Button type=\"button\" onClick={openCreate}>\n {t('cpq.priceRules.create', 'Create Rule')}\n </Button>\n }\n crud={{\n endpoint: '/api/cpq/price-rules',\n entityName: t('cpq.priceRules.entityName', 'price rule'),\n editHref: (row) => `/backend/cpq/price-rules/${row.id}`,\n }}\n onRowClick={(row) => router.push(`/backend/cpq/price-rules/${row.id}`)}\n toolbarContent={\n showForm ? (\n <PriceRuleForm\n editingId={null}\n form={form}\n onFormChange={setForm}\n offerings={offerings}\n saving={saving}\n error={error}\n onSave={handleSave}\n onCancel={() => setShowForm(false)}\n />\n ) : null\n }\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n No price rules found. Click &quot;Create Rule&quot; to add one.\n </div>\n }\n />\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { CpqListView, useCpqListData } from '../../../components/CpqListView'\nimport {\n PriceRuleForm,\n buildRuleTypeLabels,\n emptyPriceRuleForm,\n priceRuleFormToPayload,\n type PriceRule,\n type PriceRuleFormData,\n type ProductOffering,\n} from './PriceRuleForm'\n\nconst PAGE_SIZE = 50\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.ruleType === 'string' && values.ruleType) {\n params.set('ruleType', values.ruleType)\n }\n if (typeof values.chargeTypeFilter === 'string' && values.chargeTypeFilter) {\n params.set('chargeTypeFilter', values.chargeTypeFilter)\n }\n if (values.globalOnly === true) params.set('globalOnly', 'true')\n if (values.isActive === true) params.set('isActive', 'true')\n if (values.isActive === false) params.set('isActive', 'false')\n}\n\nfunction formatValue(rule: PriceRule) {\n if (rule.ruleType.includes('percent')) return `${rule.value}%`\n if (rule.ruleType === 'price_override') return `= ${rule.value}`\n return `${rule.value}`\n}\n\nexport default function PriceRulesPage() {\n const t = useT()\n const router = useRouter()\n\n const data = useCpqListData<PriceRule>({\n endpoint: '/api/cpq/price-rules',\n pageSize: PAGE_SIZE,\n initialSorting: [{ id: 'sortOrder', desc: false }],\n buildFilterParams,\n loadErrorMessage: t('cpq.priceRules.list.error.load', 'Failed to load price rules'),\n })\n\n const [showForm, setShowForm] = React.useState(false)\n const [form, setForm] = React.useState<PriceRuleFormData>(emptyPriceRuleForm)\n const [saving, setSaving] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [offerings, setOfferings] = React.useState<ProductOffering[]>([])\n\n React.useEffect(() => {\n fetch('/api/cpq/product-offerings?pageSize=100')\n .then((r) => r.json())\n .then((d) => setOfferings(d.items ?? []))\n .catch(() => {})\n }, [])\n\n const ruleTypeLabels = React.useMemo(() => buildRuleTypeLabels(t), [t])\n\n const ruleTypeOptions = React.useMemo(\n () => Object.entries(ruleTypeLabels).map(([value, label]) => ({ value, label })),\n [ruleTypeLabels],\n )\n\n const chargeTypeOptions = React.useMemo(\n () => [\n { value: 'nrc', label: t('cpq.priceRules.chargeTypes.nrc', 'NRC') },\n { value: 'mrc', label: t('cpq.priceRules.chargeTypes.mrc', 'MRC') },\n { value: 'usage', label: t('cpq.priceRules.chargeTypes.usage', 'Usage') },\n ],\n [t],\n )\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'ruleType',\n label: t('cpq.priceRules.filters.ruleType', 'Rule Type'),\n type: 'select',\n options: ruleTypeOptions,\n },\n {\n id: 'chargeTypeFilter',\n label: t('cpq.priceRules.filters.chargeType', 'Charge Type'),\n type: 'select',\n options: chargeTypeOptions,\n },\n {\n id: 'globalOnly',\n label: t('cpq.priceRules.filters.globalOnly', 'Global only'),\n type: 'checkbox',\n },\n {\n id: 'isActive',\n label: t('cpq.priceRules.filters.isActive', 'Active'),\n type: 'checkbox',\n },\n ],\n [chargeTypeOptions, ruleTypeOptions, t],\n )\n\n const openCreate = React.useCallback(() => {\n setForm(emptyPriceRuleForm)\n setError(null)\n setShowForm(true)\n }, [])\n\n const handleSave = React.useCallback(async () => {\n setSaving(true)\n setError(null)\n try {\n const payload = priceRuleFormToPayload(form)\n const res = await fetch('/api/cpq/price-rules', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!res.ok) {\n const err = await res.json()\n throw new Error(\n err.details\n ? JSON.stringify(err.details)\n : err.error ?? t('cpq.priceRules.errors.createFailed', 'Failed to create'),\n )\n }\n setShowForm(false)\n data.reload()\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : t('cpq.priceRules.errors.unknown', 'Unknown error'))\n } finally {\n setSaving(false)\n }\n }, [data, form, t])\n\n const columns = React.useMemo<ColumnDef<PriceRule>[]>(\n () => [\n {\n accessorKey: 'sortOrder',\n header: t('cpq.priceRules.table.order', 'Order'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">{row.original.sortOrder}</span>,\n },\n {\n accessorKey: 'name',\n header: t('cpq.priceRules.table.name', 'Name'),\n cell: ({ row }) => <span className=\"font-medium\">{row.original.name}</span>,\n },\n {\n accessorKey: 'code',\n header: t('cpq.priceRules.table.code', 'Code'),\n cell: ({ row }) => <span className=\"font-mono text-xs\">{row.original.code}</span>,\n },\n {\n accessorKey: 'ruleType',\n header: t('cpq.priceRules.table.type', 'Type'),\n cell: ({ row }) => (\n <span>{ruleTypeLabels[row.original.ruleType] ?? row.original.ruleType}</span>\n ),\n },\n {\n id: 'value',\n header: t('cpq.priceRules.table.value', 'Value'),\n cell: ({ row }) => <span className=\"font-mono\">{formatValue(row.original)}</span>,\n },\n {\n id: 'scope',\n header: t('cpq.priceRules.table.scope', 'Scope'),\n cell: ({ row }) => (\n <span className=\"text-xs\">\n {row.original.productOfferingId\n ? row.original.productOfferingName ??\n t('cpq.priceRules.scope.productScoped', 'Product-scoped')\n : t('cpq.priceRules.scope.global', 'Global')}\n </span>\n ),\n },\n {\n id: 'filter',\n header: t('cpq.priceRules.table.filter', 'Filter'),\n cell: ({ row }) => {\n const rule = row.original\n const parts: string[] = []\n if (rule.chargeTypeFilter) parts.push(rule.chargeTypeFilter)\n if (rule.chargeCodeFilter) parts.push(rule.chargeCodeFilter)\n const cond = rule.applicabilityCondition as\n | { attribute?: string; operator?: string; value?: string }\n | null\n if (cond?.attribute) {\n const op = cond.operator === 'neq' ? '\u2260' : '='\n parts.push(`${cond.attribute} ${op} ${cond.value ?? '\"\"'}`)\n }\n return (\n <span className=\"text-xs text-muted-foreground\">\n {parts.length > 0 ? parts.join(' / ') : '\u2014'}\n </span>\n )\n },\n meta: { truncate: true, maxWidth: 240 },\n },\n {\n accessorKey: 'isActive',\n header: t('cpq.priceRules.table.status', 'Status'),\n cell: ({ row }) => (\n <Tag variant={row.original.isActive ? 'success' : 'neutral'} dot>\n {row.original.isActive\n ? t('cpq.common.active', 'Active')\n : t('cpq.common.inactive', 'Inactive')}\n </Tag>\n ),\n },\n ],\n [ruleTypeLabels, t],\n )\n\n return (\n <CpqListView<PriceRule>\n title={t('cpq.priceRules.list.title', 'Price Rules')}\n tableId=\"cpq.price-rules.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.priceRules.search.placeholder', 'Search price rules...')}\n actions={\n <Button type=\"button\" onClick={openCreate}>\n {t('cpq.priceRules.create', 'Create Rule')}\n </Button>\n }\n crud={{\n endpoint: '/api/cpq/price-rules',\n entityName: t('cpq.priceRules.entityName', 'price rule'),\n editHref: (row) => `/backend/cpq/price-rules/${row.id}`,\n }}\n onRowClick={(row) => router.push(`/backend/cpq/price-rules/${row.id}`)}\n toolbarContent={\n showForm ? (\n <PriceRuleForm\n editingId={null}\n form={form}\n onFormChange={setForm}\n offerings={offerings}\n saving={saving}\n error={error}\n onSave={handleSave}\n onCancel={() => setShowForm(false)}\n />\n ) : null\n }\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t('cpq.priceRules.empty', 'No price rules found. Click \"Create Rule\" to add one.')}\n </div>\n }\n />\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -12,11 +12,13 @@ import {
12
12
  SelectValue
13
13
  } from "@open-mercato/ui/primitives/select";
14
14
  import { Trash2 } from "lucide-react";
15
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
15
16
  function EntriesEditor({
16
17
  table,
17
18
  initialEntries,
18
19
  onSaved
19
20
  }) {
21
+ const t = useT();
20
22
  const [drafts, setDrafts] = React.useState(
21
23
  () => initialEntries.map((e) => ({ ...e, _state: null, _localId: e.id }))
22
24
  );
@@ -87,7 +89,7 @@ function EntriesEditor({
87
89
  });
88
90
  if (!res.ok) {
89
91
  const body = await res.json().catch(() => ({}));
90
- return { ok: false, error: body.error ?? "Failed to create entries" };
92
+ return { ok: false, error: body.error ?? t("cpq.pricing.entries.errors.createFailed", "Failed to create entries") };
91
93
  }
92
94
  }
93
95
  const mods = drafts.filter((d) => d._state === "modified");
@@ -107,7 +109,7 @@ function EntriesEditor({
107
109
  });
108
110
  if (!res.ok) {
109
111
  const body = await res.json().catch(() => ({}));
110
- return { ok: false, error: body.error ?? `Failed to update entry ${d.id}` };
112
+ return { ok: false, error: body.error ?? t("cpq.pricing.entries.errors.updateFailed", "Failed to update entry {id}", { id: d.id }) };
111
113
  }
112
114
  }
113
115
  const dels = drafts.filter((d) => d._state === "deleted");
@@ -119,12 +121,12 @@ function EntriesEditor({
119
121
  });
120
122
  if (!res.ok) {
121
123
  const body = await res.json().catch(() => ({}));
122
- return { ok: false, error: body.error ?? `Failed to delete entry ${d.id}` };
124
+ return { ok: false, error: body.error ?? t("cpq.pricing.entries.errors.deleteFailed", "Failed to delete entry {id}", { id: d.id }) };
123
125
  }
124
126
  }
125
127
  return { ok: true };
126
128
  } catch (err) {
127
- return { ok: false, error: err instanceof Error ? err.message : "Failed to save entries" };
129
+ return { ok: false, error: err instanceof Error ? err.message : t("cpq.pricing.entries.errors.saveFailed", "Failed to save entries") };
128
130
  } finally {
129
131
  setSaving(false);
130
132
  }
@@ -140,30 +142,29 @@ function EntriesEditor({
140
142
  };
141
143
  return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
142
144
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
143
- /* @__PURE__ */ jsxs("h2", { className: "text-lg font-semibold", children: [
144
- "Entries (",
145
- visibleDrafts.length,
146
- ")"
147
- ] }),
148
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: mode === "view" ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setMode("edit"), children: "Edit Entries" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
149
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => addRow(false), children: "+ Add Row" }),
150
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => addRow(true), children: "+ Add Tier" }),
151
- /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", onClick: handleSave, disabled: saving || !dirty, children: saving ? "Saving..." : "Save" }),
152
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: cancelEdit, disabled: saving, children: "Cancel" })
145
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: t("cpq.pricing.entries.title", "Entries ({count})", { count: visibleDrafts.length }) }),
146
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: mode === "view" ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setMode("edit"), children: t("cpq.pricing.entries.edit", "Edit Entries") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
147
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => addRow(false), children: t("cpq.pricing.entries.addRow", "+ Add Row") }),
148
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => addRow(true), children: t("cpq.pricing.entries.addTier", "+ Add Tier") }),
149
+ /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", onClick: handleSave, disabled: saving || !dirty, children: saving ? t("cpq.common.saving", "Saving...") : t("cpq.common.save", "Save") }),
150
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: cancelEdit, disabled: saving, children: t("cpq.common.cancel", "Cancel") })
153
151
  ] }) })
154
152
  ] }),
155
153
  error && /* @__PURE__ */ jsx(Alert, { variant: "destructive", children: error }),
156
- visibleDrafts.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: mode === "edit" ? 'No entries yet \u2014 use "+ Add Row" (dimensions lookup) or "+ Add Tier" (range lookup).' : "No entries in this pricing table." }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
154
+ visibleDrafts.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: mode === "edit" ? t(
155
+ "cpq.pricing.entries.emptyEditing",
156
+ 'No entries yet \u2014 use "+ Add Row" (dimensions lookup) or "+ Add Tier" (range lookup).'
157
+ ) : t("cpq.pricing.entries.empty", "No entries in this pricing table.") }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
157
158
  /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/50", children: [
158
159
  table.dimensions.map((dim) => /* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium whitespace-nowrap", children: dim.label }, dim.key)),
159
160
  hasTieredEntries && /* @__PURE__ */ jsxs(Fragment, { children: [
160
- /* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium whitespace-nowrap", children: "Tier #" }),
161
- /* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium whitespace-nowrap", children: "Range From" }),
162
- /* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium whitespace-nowrap", children: "Range To" })
161
+ /* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium whitespace-nowrap", children: t("cpq.pricing.entries.tierNumber", "Tier #") }),
162
+ /* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium whitespace-nowrap", children: t("cpq.pricing.entries.rangeFrom", "Range From") }),
163
+ /* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium whitespace-nowrap", children: t("cpq.pricing.entries.rangeTo", "Range To") })
163
164
  ] }),
164
165
  table.priceColumns.map((col) => /* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-right font-medium whitespace-nowrap", children: col.label }, col.key)),
165
- /* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium", children: "Currency" }),
166
- mode === "edit" && /* @__PURE__ */ jsx("th", { className: "px-3 py-3 w-8", "aria-label": "Actions" })
166
+ /* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium", children: t("cpq.pricing.entries.currency", "Currency") }),
167
+ mode === "edit" && /* @__PURE__ */ jsx("th", { className: "px-3 py-3 w-8", "aria-label": t("cpq.pricing.entries.actions", "Actions") })
167
168
  ] }) }),
168
169
  /* @__PURE__ */ jsx("tbody", { children: visibleDrafts.map((entry) => /* @__PURE__ */ jsx(
169
170
  EntryRow,
@@ -188,6 +189,7 @@ function EntryRow({
188
189
  onChange,
189
190
  onDelete
190
191
  }) {
192
+ const t = useT();
191
193
  const readOnly = mode === "view";
192
194
  const rowBg = entry._state === "new" ? "bg-success/5" : entry._state === "modified" ? "bg-warning/5" : "";
193
195
  return /* @__PURE__ */ jsxs("tr", { className: `border-b hover:bg-muted/30 ${rowBg}`, children: [
@@ -263,7 +265,7 @@ function EntryRow({
263
265
  type: "button",
264
266
  onClick: onDelete,
265
267
  className: "text-destructive hover:bg-destructive/10 rounded p-1",
266
- "aria-label": "Delete row",
268
+ "aria-label": t("cpq.pricing.entries.deleteRow", "Delete row"),
267
269
  children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
268
270
  }
269
271
  ) })