@dainamite/cpq 0.4.0 → 0.6.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 (273) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/MIGRATION.md +101 -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/handleArcError.js +2 -2
  8. package/dist/modules/cpq/api/_helpers/handleArcError.js.map +2 -2
  9. package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js +23 -0
  10. package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js.map +7 -0
  11. package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js +19 -0
  12. package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js.map +7 -0
  13. package/dist/modules/cpq/api/orders/[orderId]/activate/route.js +2 -2
  14. package/dist/modules/cpq/api/orders/[orderId]/activate/route.js.map +2 -2
  15. package/dist/modules/cpq/api/orders/[orderId]/route.js +19 -3
  16. package/dist/modules/cpq/api/orders/[orderId]/route.js.map +2 -2
  17. package/dist/modules/cpq/api/orders/[orderId]/status/route.js +2 -2
  18. package/dist/modules/cpq/api/orders/[orderId]/status/route.js.map +2 -2
  19. package/dist/modules/cpq/api/orders/route.js +49 -4
  20. package/dist/modules/cpq/api/orders/route.js.map +2 -2
  21. package/dist/modules/cpq/api/quotes/[quoteId]/clone/route.js +2 -2
  22. package/dist/modules/cpq/api/quotes/[quoteId]/clone/route.js.map +2 -2
  23. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js +54 -0
  24. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js.map +7 -0
  25. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js +45 -5
  26. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js.map +2 -2
  27. package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js +27 -2
  28. package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js.map +2 -2
  29. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js +47 -0
  30. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js.map +7 -0
  31. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js +30 -0
  32. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js.map +7 -0
  33. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js +47 -0
  34. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js.map +7 -0
  35. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js +34 -4
  36. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js.map +2 -2
  37. package/dist/modules/cpq/api/quotes/[quoteId]/route.js +2 -2
  38. package/dist/modules/cpq/api/quotes/[quoteId]/route.js.map +2 -2
  39. package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js +33 -2
  40. package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js.map +2 -2
  41. package/dist/modules/cpq/api/quotes/price/route.js +43 -3
  42. package/dist/modules/cpq/api/quotes/price/route.js.map +2 -2
  43. package/dist/modules/cpq/api/quotes/route.js +2 -2
  44. package/dist/modules/cpq/api/quotes/route.js.map +2 -2
  45. package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +1 -1
  46. package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +1 -1
  47. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +1 -1
  48. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +1 -1
  49. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +22 -8
  50. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +2 -2
  51. package/dist/modules/cpq/backend/cpq/offerings/page.js.map +1 -1
  52. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +89 -232
  53. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +2 -2
  54. package/dist/modules/cpq/backend/cpq/orders/page.js.map +1 -1
  55. package/dist/modules/cpq/backend/cpq/orders/page.meta.js +1 -1
  56. package/dist/modules/cpq/backend/cpq/orders/page.meta.js.map +1 -1
  57. package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +1 -1
  58. package/dist/modules/cpq/backend/cpq/pricing/page.js.map +1 -1
  59. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js +205 -0
  60. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js.map +7 -0
  61. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js +79 -0
  62. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js.map +7 -0
  63. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js +53 -0
  64. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js.map +7 -0
  65. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js +107 -0
  66. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js.map +7 -0
  67. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js +130 -0
  68. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js.map +7 -0
  69. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +144 -221
  70. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +3 -3
  71. package/dist/modules/cpq/backend/cpq/quotes/page.js.map +1 -1
  72. package/dist/modules/cpq/backend/cpq/quotes/page.meta.js +1 -1
  73. package/dist/modules/cpq/backend/cpq/quotes/page.meta.js.map +1 -1
  74. package/dist/modules/cpq/backend/cpq/specifications/page.js.map +1 -1
  75. package/dist/modules/cpq/backend/cpq/wizards/page.js.map +1 -1
  76. package/dist/modules/cpq/ce.js +8 -0
  77. package/dist/modules/cpq/ce.js.map +2 -2
  78. package/dist/modules/cpq/cli.js +34 -1
  79. package/dist/modules/cpq/cli.js.map +2 -2
  80. package/dist/modules/cpq/commands/price-adjustments.js +107 -0
  81. package/dist/modules/cpq/commands/price-adjustments.js.map +7 -0
  82. package/dist/modules/cpq/components/ArcLineDiff.js +53 -0
  83. package/dist/modules/cpq/components/ArcLineDiff.js.map +7 -0
  84. package/dist/modules/cpq/components/ChargeBreakdownTable.js +154 -0
  85. package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +7 -0
  86. package/dist/modules/cpq/components/CpqListView.js.map +2 -2
  87. package/dist/modules/cpq/components/PriceWaterfall.js +66 -0
  88. package/dist/modules/cpq/components/PriceWaterfall.js.map +7 -0
  89. package/dist/modules/cpq/components/PricingSummaryCard.js +117 -0
  90. package/dist/modules/cpq/components/PricingSummaryCard.js.map +7 -0
  91. package/dist/modules/cpq/components/useGrantedFeatures.js +27 -0
  92. package/dist/modules/cpq/components/useGrantedFeatures.js.map +7 -0
  93. package/dist/modules/cpq/data/entities.js +97 -0
  94. package/dist/modules/cpq/data/entities.js.map +2 -2
  95. package/dist/modules/cpq/data/price-adjustments.js +70 -0
  96. package/dist/modules/cpq/data/price-adjustments.js.map +7 -0
  97. package/dist/modules/cpq/data/validators.js +4 -0
  98. package/dist/modules/cpq/data/validators.js.map +2 -2
  99. package/dist/modules/cpq/di.js +6 -0
  100. package/dist/modules/cpq/di.js.map +2 -2
  101. package/dist/modules/cpq/events.js +47 -0
  102. package/dist/modules/cpq/events.js.map +2 -2
  103. package/dist/modules/cpq/index.js +1 -1
  104. package/dist/modules/cpq/index.js.map +2 -2
  105. package/dist/modules/cpq/lib/auditActor.js +10 -0
  106. package/dist/modules/cpq/lib/auditActor.js.map +7 -0
  107. package/dist/modules/cpq/lib/formatMoney.js +7 -0
  108. package/dist/modules/cpq/lib/formatMoney.js.map +7 -0
  109. package/dist/modules/cpq/lib/priceAdjustmentDefaults.js +60 -0
  110. package/dist/modules/cpq/lib/priceAdjustmentDefaults.js.map +7 -0
  111. package/dist/modules/cpq/lib/priceAdjustmentView.js +169 -0
  112. package/dist/modules/cpq/lib/priceAdjustmentView.js.map +7 -0
  113. package/dist/modules/cpq/lib/quotePermissions.js +13 -0
  114. package/dist/modules/cpq/lib/quotePermissions.js.map +7 -0
  115. package/dist/modules/cpq/lib/quoteSummaryView.js +28 -0
  116. package/dist/modules/cpq/lib/quoteSummaryView.js.map +7 -0
  117. package/dist/modules/cpq/migrations/Migration20261110213052.js +22 -0
  118. package/dist/modules/cpq/migrations/Migration20261110213052.js.map +7 -0
  119. package/dist/modules/cpq/services/arcLineRemoval.js +53 -0
  120. package/dist/modules/cpq/services/arcLineRemoval.js.map +7 -0
  121. package/dist/modules/cpq/services/arcPriceFreeze.js +79 -0
  122. package/dist/modules/cpq/services/arcPriceFreeze.js.map +7 -0
  123. package/dist/modules/cpq/services/cpqOrderService.js +34 -30
  124. package/dist/modules/cpq/services/cpqOrderService.js.map +2 -2
  125. package/dist/modules/cpq/services/cpqPricingService.js +7 -0
  126. package/dist/modules/cpq/services/cpqPricingService.js.map +2 -2
  127. package/dist/modules/cpq/services/cpqQuotingService.js +339 -111
  128. package/dist/modules/cpq/services/cpqQuotingService.js.map +2 -2
  129. package/dist/modules/cpq/services/orderPricingSummary.js +26 -0
  130. package/dist/modules/cpq/services/orderPricingSummary.js.map +7 -0
  131. package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js +264 -0
  132. package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js.map +7 -0
  133. package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js +25 -0
  134. package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js.map +7 -0
  135. package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js +38 -0
  136. package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js.map +7 -0
  137. package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js +104 -0
  138. package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js.map +7 -0
  139. package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js +102 -0
  140. package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js.map +7 -0
  141. package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js +52 -0
  142. package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js.map +7 -0
  143. package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js +37 -0
  144. package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js.map +7 -0
  145. package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js +107 -0
  146. package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js.map +7 -0
  147. package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js +30 -0
  148. package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js.map +7 -0
  149. package/dist/modules/cpq/services/types.js.map +2 -2
  150. package/dist/modules/cpq/setup.js +6 -0
  151. package/dist/modules/cpq/setup.js.map +2 -2
  152. package/package.json +9 -9
  153. package/src/modules/cpq/__integration__/TC-CPQ-002.spec.ts +8 -5
  154. package/src/modules/cpq/__integration__/TC-CPQ-ARC-012.spec.ts +134 -0
  155. package/src/modules/cpq/acl.ts +1 -0
  156. package/src/modules/cpq/api/__tests__/adjustmentRoutes.test.ts +79 -0
  157. package/src/modules/cpq/api/__tests__/quoteWriteGuards.test.ts +100 -0
  158. package/src/modules/cpq/api/_helpers/__tests__/quoteMutationGuard.test.ts +48 -0
  159. package/src/modules/cpq/api/_helpers/adjustmentCommandRoute.ts +45 -0
  160. package/src/modules/cpq/api/_helpers/handleArcError.ts +12 -12
  161. package/src/modules/cpq/api/_helpers/quoteMutationGuard.ts +38 -0
  162. package/src/modules/cpq/api/_helpers/quoteRouteOpenApi.ts +19 -0
  163. package/src/modules/cpq/api/orders/[orderId]/activate/route.ts +2 -2
  164. package/src/modules/cpq/api/orders/[orderId]/route.ts +20 -2
  165. package/src/modules/cpq/api/orders/[orderId]/status/route.ts +2 -2
  166. package/src/modules/cpq/api/orders/route.ts +53 -3
  167. package/src/modules/cpq/api/quotes/[quoteId]/clone/route.ts +2 -2
  168. package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.ts +56 -0
  169. package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.ts +51 -4
  170. package/src/modules/cpq/api/quotes/[quoteId]/items/route.ts +31 -1
  171. package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.ts +39 -0
  172. package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.ts +24 -0
  173. package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.ts +40 -0
  174. package/src/modules/cpq/api/quotes/[quoteId]/recalculate/__tests__/route.test.ts +95 -0
  175. package/src/modules/cpq/api/quotes/[quoteId]/recalculate/route.ts +42 -3
  176. package/src/modules/cpq/api/quotes/[quoteId]/route.ts +2 -2
  177. package/src/modules/cpq/api/quotes/[quoteId]/status/__tests__/route.test.ts +74 -0
  178. package/src/modules/cpq/api/quotes/[quoteId]/status/route.ts +37 -1
  179. package/src/modules/cpq/api/quotes/price/__tests__/route.test.ts +37 -0
  180. package/src/modules/cpq/api/quotes/price/route.ts +28 -2
  181. package/src/modules/cpq/api/quotes/route.ts +2 -2
  182. package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +1 -1
  183. package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +1 -1
  184. package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +1 -1
  185. package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +32 -10
  186. package/src/modules/cpq/backend/cpq/offerings/page.tsx +1 -1
  187. package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +60 -269
  188. package/src/modules/cpq/backend/cpq/orders/page.meta.ts +1 -1
  189. package/src/modules/cpq/backend/cpq/orders/page.tsx +1 -1
  190. package/src/modules/cpq/backend/cpq/price-rules/page.tsx +1 -1
  191. package/src/modules/cpq/backend/cpq/pricing/page.tsx +1 -1
  192. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.tsx +260 -0
  193. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.tsx +124 -0
  194. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/__tests__/PriceAdjustmentsPanel.test.tsx +76 -0
  195. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.tsx +76 -0
  196. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.tsx +133 -0
  197. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.tsx +165 -0
  198. package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +178 -266
  199. package/src/modules/cpq/backend/cpq/quotes/page.meta.ts +1 -1
  200. package/src/modules/cpq/backend/cpq/quotes/page.tsx +1 -1
  201. package/src/modules/cpq/backend/cpq/specifications/page.tsx +1 -1
  202. package/src/modules/cpq/backend/cpq/wizards/page.tsx +1 -1
  203. package/src/modules/cpq/ce.ts +8 -0
  204. package/src/modules/cpq/cli.ts +48 -1
  205. package/src/modules/cpq/commands/__tests__/price-adjustments.test.ts +54 -0
  206. package/src/modules/cpq/commands/price-adjustments.ts +90 -0
  207. package/src/modules/cpq/components/ArcLineDiff.tsx +87 -0
  208. package/src/modules/cpq/components/ChargeBreakdownTable.tsx +201 -0
  209. package/src/modules/cpq/components/CpqListView.tsx +2 -1
  210. package/src/modules/cpq/components/PriceWaterfall.tsx +94 -0
  211. package/src/modules/cpq/components/PricingSummaryCard.tsx +128 -0
  212. package/src/modules/cpq/components/__tests__/PriceWaterfall.test.tsx +38 -0
  213. package/src/modules/cpq/components/useGrantedFeatures.ts +35 -0
  214. package/src/modules/cpq/data/__tests__/price-adjustment-migration.test.ts +11 -0
  215. package/src/modules/cpq/data/__tests__/price-adjustments.test.ts +26 -0
  216. package/src/modules/cpq/data/entities.ts +83 -0
  217. package/src/modules/cpq/data/price-adjustments.ts +67 -0
  218. package/src/modules/cpq/data/validators.ts +3 -0
  219. package/src/modules/cpq/di.ts +6 -0
  220. package/src/modules/cpq/events.ts +53 -2
  221. package/src/modules/cpq/index.ts +1 -1
  222. package/src/modules/cpq/lib/__tests__/auditActor.test.ts +12 -0
  223. package/src/modules/cpq/lib/__tests__/priceAdjustmentDefaults.test.ts +37 -0
  224. package/src/modules/cpq/lib/__tests__/priceAdjustmentView.test.ts +152 -0
  225. package/src/modules/cpq/lib/__tests__/quotePermissions.test.ts +30 -0
  226. package/src/modules/cpq/lib/__tests__/quoteSummaryView.test.ts +56 -0
  227. package/src/modules/cpq/lib/auditActor.ts +13 -0
  228. package/src/modules/cpq/lib/formatMoney.ts +4 -0
  229. package/src/modules/cpq/lib/priceAdjustmentDefaults.ts +36 -0
  230. package/src/modules/cpq/lib/priceAdjustmentView.ts +259 -0
  231. package/src/modules/cpq/lib/quotePermissions.ts +22 -0
  232. package/src/modules/cpq/lib/quoteSummaryView.ts +51 -0
  233. package/src/modules/cpq/migrations/.snapshot-open-mercato.json +3298 -2423
  234. package/src/modules/cpq/migrations/Migration20261110213052.ts +22 -0
  235. package/src/modules/cpq/services/__tests__/_helpers/mockEm.ts +9 -2
  236. package/src/modules/cpq/services/__tests__/adjustmentService.test.ts +183 -0
  237. package/src/modules/cpq/services/__tests__/arcLineRemoval.test.ts +109 -0
  238. package/src/modules/cpq/services/__tests__/arcPriceFreeze.test.ts +141 -0
  239. package/src/modules/cpq/services/__tests__/chargeWaterfall.test.ts +28 -0
  240. package/src/modules/cpq/services/__tests__/cpqOrderService.test.ts +35 -2
  241. package/src/modules/cpq/services/__tests__/cpqPricingService.test.ts +57 -0
  242. package/src/modules/cpq/services/__tests__/cpqQuotingService.arc.test.ts +237 -0
  243. package/src/modules/cpq/services/__tests__/cpqQuotingService.test.ts +58 -3
  244. package/src/modules/cpq/services/__tests__/errorBrands.test.ts +72 -0
  245. package/src/modules/cpq/services/__tests__/orderPricingSummary.test.ts +50 -0
  246. package/src/modules/cpq/services/__tests__/pricingSnapshot.test.ts +62 -0
  247. package/src/modules/cpq/services/__tests__/quoteAdjustmentLifecycle.test.ts +35 -0
  248. package/src/modules/cpq/services/__tests__/quoteAdjustmentPricing.test.ts +65 -0
  249. package/src/modules/cpq/services/__tests__/quoteCloneAdjustments.test.ts +45 -0
  250. package/src/modules/cpq/services/__tests__/quoteDetachTargetAdjustments.test.ts +54 -0
  251. package/src/modules/cpq/services/__tests__/quoteEventsService.test.ts +72 -0
  252. package/src/modules/cpq/services/__tests__/quotePricingEvents.test.ts +126 -0
  253. package/src/modules/cpq/services/__tests__/quotePricingIsolation.test.ts +52 -0
  254. package/src/modules/cpq/services/__tests__/quotePricingWrite.test.ts +74 -0
  255. package/src/modules/cpq/services/__tests__/resolveManualAdjustments.test.ts +90 -0
  256. package/src/modules/cpq/services/__tests__/storedAdjustmentFailure.test.ts +9 -0
  257. package/src/modules/cpq/services/arcLineRemoval.ts +87 -0
  258. package/src/modules/cpq/services/arcPriceFreeze.ts +165 -0
  259. package/src/modules/cpq/services/cpqOrderService.ts +32 -28
  260. package/src/modules/cpq/services/cpqPricingService.ts +8 -0
  261. package/src/modules/cpq/services/cpqQuotingService.ts +388 -125
  262. package/src/modules/cpq/services/orderPricingSummary.ts +47 -0
  263. package/src/modules/cpq/services/priceAdjustments/adjustmentService.ts +249 -0
  264. package/src/modules/cpq/services/priceAdjustments/chargeWaterfall.ts +28 -0
  265. package/src/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.ts +25 -0
  266. package/src/modules/cpq/services/priceAdjustments/pricingSnapshot.ts +86 -0
  267. package/src/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.ts +68 -0
  268. package/src/modules/cpq/services/priceAdjustments/quotePricingEvents.ts +94 -0
  269. package/src/modules/cpq/services/priceAdjustments/quotePricingWrite.ts +51 -0
  270. package/src/modules/cpq/services/priceAdjustments/resolveManualAdjustments.ts +116 -0
  271. package/src/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.ts +31 -0
  272. package/src/modules/cpq/services/types.ts +34 -1
  273. package/src/modules/cpq/setup.ts +7 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/cpq/backend/cpq/orders/%5Bid%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter, useParams } from 'next/navigation'\nimport { ArrowRight, ExternalLink, Play, X } from 'lucide-react'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport {\n formatStatusLabel,\n orderCpqStatusMap,\n type OrderCpqStatus,\n} from '../../../../components/statusMaps'\nimport { StatusPath } from '../../../../components/StatusPath'\n\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype ArcLineSource = {\n subscriptionItemId: string\n name: string\n mrcAmount: number\n nrcAmount: number\n quantity: number\n}\n\ntype TierBreakdownEntry = { tier: number; from: number; to: number; pricePerUnit: number; quantity: number; subtotal: number }\ntype RuleAdjustment = { ruleCode: string; ruleName: string; ruleType: string; value: number; unitPriceBefore: number; unitPriceAfter: number; delta: number }\n\ntype ResolvedCharge = {\n chargeName?: string\n chargeType?: string\n pricingMethod?: 'flat' | 'tiered' | 'per_unit'\n unitPrice?: number\n quantity?: number | null\n totalPrice?: number | null\n note?: string | null\n breakdown?: { tiers: TierBreakdownEntry[] } | null\n adjustments?: RuleAdjustment[] | null\n}\n\ntype OrderLineResult = {\n lineId: string\n offeringId: string | null\n offeringName: string\n offeringType: string | null\n productId: string | null\n specId: string | null\n action: string\n parentLineId: string | null\n quantity: number\n configuration: Record<string, unknown>\n startDate: string | null\n termMonths: number | null\n endDate: string | null\n nrcTotal: number\n mrcTotal: number\n charges: ResolvedCharge[]\n sourceQuoteLineId: string | null\n arcSource: ArcLineSource | null\n}\n\ntype OrderResult = {\n id: string\n orderId: string\n orderNumber: string\n customerId: string\n cpqStatus: string\n sourceQuoteId: string | null\n quoteType: string\n currencyCode: string\n pricingSummary: {\n nrcTotal: number\n mrcTotal: number\n currencyCode: string\n }\n activatedAt: string | null\n createdAt: string\n lines: OrderLineResult[]\n}\n\nconst ARC_BADGE_VARIANTS: Record<string, 'brand' | 'success' | 'error'> = {\n amend: 'brand',\n renew: 'success',\n cancel: 'error',\n}\n\nconst RULE_TYPE_LABELS: Record<string, string> = {\n discount_percent: '% discount',\n discount_absolute: 'fixed discount',\n surcharge_percent: '% surcharge',\n surcharge_absolute: 'fixed surcharge',\n price_override: 'override',\n}\n\nfunction fmtRuleValue(adj: RuleAdjustment, currency?: string): string {\n if (adj.ruleType === 'discount_percent' || adj.ruleType === 'surcharge_percent') return `${adj.value}%`\n if (currency) return fmt(adj.value, currency)\n return String(adj.value)\n}\n\n// \u2500\u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction fmt(amount: number, currency: string): string {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)\n}\n\nasync function apiJson<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetch(url, init)\n const json = await res.json()\n if (!res.ok) throw new Error(json?.error ?? `Request failed (${res.status})`)\n return json as T\n}\n\n// \u2500\u2500\u2500 Main Page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport default function CpqOrderDetailPage(props: { params?: { id?: string } }) {\n const router = useRouter()\n const urlParams = useParams()\n const cpqOrderId = (props.params?.id ?? urlParams?.id) as string\n\n const [order, setOrder] = React.useState<OrderResult | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [activating, setActivating] = React.useState(false)\n const [expandedLines, setExpandedLines] = React.useState<Set<string>>(new Set())\n const [pricingDetail, setPricingDetail] = React.useState<{ lineIdx: number; chargeIdx: number } | null>(null)\n const [customerName, setCustomerName] = React.useState<string | null>(null)\n const [linkedSubscriptions, setLinkedSubscriptions] = React.useState<Array<{\n id: string\n name: string\n code: string\n status: string\n }>>([])\n\n // Find every subscription this order touches. NEW orders write\n // `sourceOrderId` on the freshly-created subscription, so a\n // `?sourceOrderId=\u2026` filter is enough there. ARC orders (amend /\n // renew / cancel) mutate existing subscriptions in place \u2014 the\n // subscription's `sourceOrderId` stays pinned to the original\n // order, so we also resolve targets via the source quote.\n const fetchLinkedSubscriptions = React.useCallback(\n async (orderConfigId: string, sourceQuoteId: string | null) => {\n const byOrderId = fetch(\n `/api/cpq/inventory/subscriptions?sourceOrderId=${orderConfigId}&pageSize=10`,\n )\n .then((r) => (r.ok ? r.json() : null))\n .then((d) => (d?.items ?? (Array.isArray(d) ? d : [])) as Array<{ id: string; name: string; code: string; status: string }>)\n .catch(() => [])\n\n const byTargets = sourceQuoteId\n ? fetch(`/api/cpq/quotes/${sourceQuoteId}/target-subscriptions`)\n .then((r) => (r.ok ? r.json() : null))\n .then(async (d) => {\n const targets: Array<{ subscriptionId: string }> = d?.items ?? []\n const fetched = await Promise.all(\n targets.map((t) =>\n fetch(`/api/cpq/inventory/subscriptions?id=${t.subscriptionId}`)\n .then((r) => (r.ok ? r.json() : null))\n .catch(() => null),\n ),\n )\n return fetched.filter(Boolean) as Array<{ id: string; name: string; code: string; status: string }>\n })\n .catch(() => [] as Array<{ id: string; name: string; code: string; status: string }>)\n : Promise.resolve([] as Array<{ id: string; name: string; code: string; status: string }>)\n\n const [a, b] = await Promise.all([byOrderId, byTargets])\n // Dedupe by id \u2014 a single subscription may appear in both lists\n // (e.g. a NEW order that was later amended into the same sub).\n const seen = new Set<string>()\n const merged: Array<{ id: string; name: string; code: string; status: string }> = []\n for (const sub of [...a, ...b]) {\n if (!sub || seen.has(sub.id)) continue\n seen.add(sub.id)\n merged.push({ id: sub.id, name: sub.name, code: sub.code, status: sub.status })\n }\n setLinkedSubscriptions(merged)\n },\n [],\n )\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n try {\n const result = await apiJson<OrderResult>(`/api/cpq/orders/${cpqOrderId}`)\n if (!cancelled) {\n setOrder(result)\n fetch(`/api/customers/companies?id=${result.customerId}&pageSize=1`)\n .then((r) => r.json())\n .then((data) => {\n const items = data.items ?? data ?? []\n const c = items[0]\n if (c && !cancelled) setCustomerName(c.display_name || c.displayName || c.name || null)\n })\n .catch(() => {})\n if (result.cpqStatus === 'active' || result.cpqStatus === 'fulfilled') {\n void fetchLinkedSubscriptions(result.id, result.sourceQuoteId)\n }\n }\n } catch (err) {\n if (!cancelled) setError((err as Error).message)\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [cpqOrderId, fetchLinkedSubscriptions])\n\n const activateOrder = async () => {\n if (!order) return\n try {\n setActivating(true)\n setError(null)\n const result = await apiJson<OrderResult>(`/api/cpq/orders/${order.id}/activate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({}),\n })\n setOrder(result)\n void fetchLinkedSubscriptions(result.id, result.sourceQuoteId)\n } catch (err) {\n setError((err as Error).message)\n } finally {\n setActivating(false)\n }\n }\n\n const cancelOrder = async () => {\n if (!order) return\n try {\n setError(null)\n const result = await apiJson<OrderResult>(`/api/cpq/orders/${order.id}/status`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ targetStatus: 'cancelled' }),\n })\n setOrder(result)\n } catch (err) {\n setError((err as Error).message)\n }\n }\n\n const toggleLine = (id: string) => {\n setExpandedLines((prev) => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n })\n }\n\n // \u2500\u2500\u2500 Render: loading / error \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 py-12 justify-center text-sm text-muted-foreground\">\n <Spinner /> Loading\u2026\n </div>\n )\n }\n\n if (!order) {\n return (\n <div className=\"space-y-4\">\n <button onClick={() => router.push('/backend/cpq/orders')} className=\"text-sm text-muted-foreground hover:text-foreground\">\u2190 Back to Orders</button>\n {error && <ErrorBanner message={error} />}\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">Order not found.</div>\n </div>\n )\n }\n\n // \u2500\u2500\u2500 Render: detail \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const canActivate = order.cpqStatus === 'draft' || order.cpqStatus === 'pending_activation'\n const canCancel = order.cpqStatus === 'draft' || order.cpqStatus === 'pending_activation' || order.cpqStatus === 'active'\n const currency = order.currencyCode\n\n const togglePricingDetail = (lineIdx: number, chargeIdx: number) => {\n setPricingDetail((prev) =>\n prev?.lineIdx === lineIdx && prev?.chargeIdx === chargeIdx ? null : { lineIdx, chargeIdx },\n )\n }\n\n return (\n <div className=\"space-y-6\">\n {/* Header \u2014 standard FormHeader detail mode, mirrors the CPQ quote\n layout so order \u2194 quote stays consistent. Title is just the order\n number; status / ARC / converted tags sit underneath; primary\n actions (Activate, then Cancel) live on the right next to the\n View Sales Order / Source Quote utility links. */}\n <FormHeader\n mode=\"detail\"\n backHref=\"/backend/cpq/orders\"\n backLabel=\"Back to CPQ Orders\"\n entityTypeLabel=\"CPQ Order\"\n title={order.orderNumber || order.orderId.slice(0, 8)}\n statusBadge={\n <div className=\"flex flex-wrap items-center gap-2 pt-1\">\n <Tag variant={orderCpqStatusMap[order.cpqStatus as OrderCpqStatus] ?? 'neutral'} dot>\n {formatStatusLabel(order.cpqStatus)}\n </Tag>\n {order.quoteType && order.quoteType !== 'new' && (\n <Tag variant={ARC_BADGE_VARIANTS[order.quoteType] ?? 'neutral'} className=\"uppercase\">\n {order.quoteType}\n </Tag>\n )}\n </div>\n }\n actionsContent={\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => router.push(`/backend/sales/orders/${order.orderId}`)}\n >\n <ExternalLink className=\"h-3.5 w-3.5\" />\n View Sales Order\n </Button>\n {order.sourceQuoteId && (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => router.push(`/backend/cpq/quotes/${order.sourceQuoteId}`)}\n >\n Source Quote\n </Button>\n )}\n {canActivate && (\n <Button type=\"button\" onClick={activateOrder} disabled={activating}>\n {activating ? <Spinner /> : <Play className=\"h-4 w-4\" />}\n Activate\n </Button>\n )}\n {canCancel && (\n <Button type=\"button\" variant=\"destructive-outline\" onClick={cancelOrder}>\n <X className=\"h-4 w-4\" />\n Cancel\n </Button>\n )}\n </>\n }\n />\n\n {/* Status path \u2014 visual progress only. Orders advance via the\n dedicated Activate / Cancel buttons because activation has\n backend side effects (subscription / asset creation). */}\n <div className=\"flex items-center gap-3 flex-wrap\">\n <StatusPath\n // Order path is read-only \u2014 activation has backend side effects\n // (subscription / asset creation) so transitions go through the\n // dedicated Activate / Cancel buttons.\n current={order.cpqStatus}\n path={['draft', 'pending_activation', 'active', 'fulfilled']}\n terminals={['cancelled']}\n statusMap={orderCpqStatusMap}\n ariaLabel=\"Order status path\"\n />\n </div>\n\n {error && <ErrorBanner message={error} onDismiss={() => setError(null)} />}\n\n {/* Linked subscriptions \u2014 one card per subscription this order\n touches. NEW orders write one fresh subscription; ARC orders\n (amend / renew / cancel) reach one or more existing\n subscriptions through the source quote's target list. */}\n {linkedSubscriptions.length > 0 && (\n <div className=\"space-y-2\">\n {linkedSubscriptions.map((sub) => (\n <div\n key={sub.id}\n className=\"rounded-lg border bg-card p-4 flex items-center gap-4\"\n >\n <div className=\"flex-1 min-w-0 space-y-1\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n Linked Subscription\n </span>\n <Tag\n variant={sub.status === 'active' ? 'success' : 'info'}\n dot\n className=\"text-[10px] px-1.5\"\n >\n {sub.status}\n </Tag>\n </div>\n <div className=\"text-sm font-medium\">\n {sub.name}{' '}\n <span className=\"text-muted-foreground font-mono text-xs\">({sub.code})</span>\n </div>\n {order.activatedAt && (\n <div className=\"text-xs text-muted-foreground\">\n Activated on {new Date(order.activatedAt).toLocaleString()}\n </div>\n )}\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => router.push(`/backend/cpq/inventory/subscriptions/${sub.id}`)}\n >\n <ArrowRight className=\"h-3.5 w-3.5\" />\n Open\n </Button>\n </div>\n ))}\n </div>\n )}\n\n {/* Meta cards \u2014 secondary reference info, matches the quote layout. */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n <MetaCard label=\"Customer\">\n {customerName ? (\n <span className=\"font-medium\">{customerName}</span>\n ) : (\n <span className=\"font-mono text-xs text-muted-foreground\">\n {order.customerId.slice(0, 8)}\u2026\n </span>\n )}\n </MetaCard>\n <MetaCard label=\"Created\">\n <span className=\"font-medium\">{new Date(order.createdAt).toLocaleDateString()}</span>\n </MetaCard>\n <MetaCard label=\"Currency\">\n <span className=\"font-medium\">{currency}</span>\n </MetaCard>\n </div>\n\n {/* Pricing Summary Card */}\n <div className=\"rounded-lg border bg-card p-4\">\n <h3 className=\"text-sm font-medium mb-3\">Pricing Summary</h3>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-xs text-muted-foreground\">Non-Recurring (NRC)</p>\n <p className=\"text-lg font-semibold font-mono\">{fmt(order.pricingSummary.nrcTotal, currency)}</p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground\">Monthly Recurring (MRC)</p>\n <p className=\"text-lg font-semibold font-mono\">{fmt(order.pricingSummary.mrcTotal, currency)}</p>\n </div>\n </div>\n </div>\n\n {/* Order Lines */}\n <OrderLineTree\n lines={order.lines}\n currency={currency}\n expandedLines={expandedLines}\n pricingDetail={pricingDetail}\n onToggleLine={toggleLine}\n onTogglePricingDetail={togglePricingDetail}\n />\n </div>\n )\n}\n\nfunction MetaCard({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {label}\n </div>\n <div className=\"mt-1 text-sm\">{children}</div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Sub-components \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction OrderLineTree({\n lines,\n currency,\n expandedLines,\n pricingDetail,\n onToggleLine,\n onTogglePricingDetail,\n}: {\n lines: OrderLineResult[]\n currency: string\n expandedLines: Set<string>\n pricingDetail: { lineIdx: number; chargeIdx: number } | null\n onToggleLine: (id: string) => void\n onTogglePricingDetail: (lineIdx: number, chargeIdx: number) => void\n}) {\n const rootLines = lines.filter((l) => !l.parentLineId)\n const childrenByParent = new Map<string, OrderLineResult[]>()\n for (const line of lines) {\n if (line.parentLineId) {\n const arr = childrenByParent.get(line.parentLineId) ?? []\n arr.push(line)\n childrenByParent.set(line.parentLineId, arr)\n }\n }\n\n // Stable index per visible line so the pricing-detail toggle can be\n // keyed by (lineIdx, chargeIdx) the same way the quote page does.\n const indexOf = new Map<string, number>()\n lines.forEach((l, i) => indexOf.set(l.lineId, i))\n\n return (\n <div>\n <h3 className=\"text-sm font-medium mb-2 px-1\">Line Items ({lines.length})</h3>\n {lines.length === 0 ? (\n <div className=\"rounded-lg border bg-card py-8 text-center text-sm text-muted-foreground\">\n No line items.\n </div>\n ) : (\n <div className=\"rounded-lg border bg-card divide-y\">\n {rootLines.map((line) => {\n const isBundle = line.offeringType === 'bundle'\n const children = childrenByParent.get(line.lineId) ?? []\n const isExpanded = expandedLines.has(line.lineId)\n const lineIdx = indexOf.get(line.lineId) ?? 0\n return (\n <div key={line.lineId}>\n <OrderLineRow\n line={line}\n lineIdx={lineIdx}\n currency={currency}\n isExpanded={isExpanded}\n isBundle={isBundle}\n childCount={children.length}\n indent={0}\n pricingDetail={pricingDetail}\n onToggle={() => onToggleLine(line.lineId)}\n onTogglePricingDetail={onTogglePricingDetail}\n />\n {isBundle && isExpanded && children.length > 0 && (\n <div className=\"border-t border-dashed border-brand-violet/30 bg-brand-violet/5\">\n <div className=\"px-6 py-1.5\">\n <span className=\"text-[10px] font-medium text-brand-violet uppercase tracking-wider\">Bundle Components ({children.length})</span>\n </div>\n {children.map((child) => {\n const childIdx = indexOf.get(child.lineId) ?? 0\n return (\n <OrderLineRow\n key={child.lineId}\n line={child}\n lineIdx={childIdx}\n currency={currency}\n isExpanded={expandedLines.has(child.lineId)}\n isBundle={false}\n childCount={0}\n indent={1}\n pricingDetail={pricingDetail}\n onToggle={() => onToggleLine(child.lineId)}\n onTogglePricingDetail={onTogglePricingDetail}\n />\n )\n })}\n </div>\n )}\n </div>\n )\n })}\n </div>\n )}\n </div>\n )\n}\n\nfunction OrderLineRow({ line, lineIdx, currency, isExpanded, isBundle, childCount, indent, pricingDetail, onToggle, onTogglePricingDetail }: {\n line: OrderLineResult; lineIdx: number; currency: string; isExpanded: boolean; isBundle: boolean; childCount: number; indent: number\n pricingDetail: { lineIdx: number; chargeIdx: number } | null\n onToggle: () => void\n onTogglePricingDetail: (lineIdx: number, chargeIdx: number) => void\n}) {\n const paddingLeft = indent > 0 ? `${indent * 2 + 1}rem` : undefined\n\n return (\n <div>\n <div\n className=\"flex items-center gap-3 px-4 py-3 hover:bg-muted/30 transition-colors cursor-pointer\"\n style={paddingLeft ? { paddingLeft } : undefined}\n onClick={onToggle}\n >\n <button className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:text-foreground\">\n <svg className={`h-4 w-4 transition-transform ${isExpanded ? 'rotate-90' : ''}`} fill=\"none\" viewBox=\"0 0 24 24\" strokeWidth=\"2\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" />\n </svg>\n </button>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n {indent > 0 && <span className=\"text-muted-foreground text-xs\">\u2514</span>}\n {isBundle && (\n <Tag variant=\"brand\" className=\"px-1.5 text-xs\">bundle</Tag>\n )}\n <span className=\"font-medium text-sm\">{line.offeringName}</span>\n {line.quantity > 1 && <span className=\"rounded bg-muted px-1.5 py-0.5 text-xs font-medium\">\u00D7{line.quantity}</span>}\n <Tag variant=\"info\" className=\"px-1.5 text-xs\">{line.action}</Tag>\n {isBundle && <span className=\"text-xs text-muted-foreground\">{childCount} component{childCount !== 1 ? 's' : ''}</span>}\n </div>\n </div>\n\n <div className=\"shrink-0 text-right text-xs space-y-0.5\">\n {line.nrcTotal > 0 && <div><span className=\"text-muted-foreground\">NRC </span><span className=\"font-mono font-medium\">{fmt(line.nrcTotal, currency)}</span></div>}\n {line.mrcTotal > 0 && <div><span className=\"text-muted-foreground\">MRC </span><span className=\"font-mono font-medium\">{fmt(line.mrcTotal, currency)}</span></div>}\n </div>\n </div>\n\n {isExpanded && (() => {\n const internalKeys = new Set(['_arcMirroredName', '_arcMirroredFromItemId', 'offeringName'])\n const visibleConfigEntries = Object.entries(line.configuration).filter(\n ([key]) => !internalKeys.has(key),\n )\n const hasConfig = visibleConfigEntries.length > 0\n const hasCharges = line.charges.length > 0\n const hasTerm = !!(line.startDate || line.termMonths || line.endDate)\n const hasArcDiff = !!line.arcSource\n const mirroredFromItemId = (line.configuration?._arcMirroredFromItemId as string | undefined) ?? null\n const isEmpty = !hasConfig && !hasCharges && !hasTerm && !hasArcDiff\n\n return (\n <div\n className=\"border-t bg-muted/10 px-12 py-3 space-y-3\"\n style={paddingLeft ? { paddingLeft: `calc(${paddingLeft} + 2rem)` } : undefined}\n >\n {hasArcDiff && (\n <ArcLineDiff line={line} currency={currency} />\n )}\n {isEmpty ? (\n <div className=\"text-xs text-muted-foreground space-y-1\">\n <div>\n No detailed breakdown for this line.\n {mirroredFromItemId && ' Mirrored from existing subscription item.'}\n </div>\n {(line.nrcTotal > 0 || line.mrcTotal > 0) && (\n <div className=\"flex gap-4\">\n {line.nrcTotal > 0 && (\n <span>NRC <span className=\"font-mono font-medium text-foreground\">{fmt(line.nrcTotal, currency)}</span></span>\n )}\n {line.mrcTotal > 0 && (\n <span>MRC <span className=\"font-mono font-medium text-foreground\">{fmt(line.mrcTotal, currency)}</span></span>\n )}\n </div>\n )}\n </div>\n ) : (\n <>\n {hasConfig && (\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-1\">Configuration</p>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1\">\n {visibleConfigEntries.map(([key, value]) => (\n <div key={key} className=\"text-xs\">\n <span className=\"text-muted-foreground\">{key}: </span>\n <span className=\"font-medium\">{String(value)}</span>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {hasCharges && (\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-1\">Charges</p>\n <ChargeBreakdown\n line={line}\n lineIdx={lineIdx}\n currency={currency}\n pricingDetail={pricingDetail}\n onTogglePricingDetail={onTogglePricingDetail}\n />\n </div>\n )}\n\n {hasTerm && (\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n {line.startDate && <span>Start: {new Date(line.startDate).toLocaleDateString()}</span>}\n {line.termMonths && <span>Term: {line.termMonths}mo</span>}\n {line.endDate && <span>End: {new Date(line.endDate).toLocaleDateString()}</span>}\n </div>\n )}\n </>\n )}\n </div>\n )\n })()}\n </div>\n )\n}\n\n// Charge breakdown table with the waterfall (\"show pricing details\")\n// toggle. Same shape as the quote page so a charge looks identical on\n// both surfaces, including tier-by-tier subtotals and any price-rule\n// adjustments that fired during calculation.\nfunction ChargeBreakdown({ line, lineIdx, currency, pricingDetail, onTogglePricingDetail }: {\n line: OrderLineResult; lineIdx: number; currency: string\n pricingDetail: { lineIdx: number; chargeIdx: number } | null\n onTogglePricingDetail: (lineIdx: number, chargeIdx: number) => void\n}) {\n return (\n <table className=\"w-full text-xs\">\n <thead>\n <tr className=\"text-muted-foreground\">\n <th className=\"text-left py-1 font-medium\">Charge</th>\n <th className=\"text-left py-1 font-medium\">Type</th>\n <th className=\"text-right py-1 font-medium\">Unit Price</th>\n <th className=\"text-right py-1 font-medium\">Qty</th>\n <th className=\"text-right py-1 font-medium\">Total</th>\n <th className=\"w-8\" />\n </tr>\n </thead>\n <tbody>\n {line.charges.map((c, i) => {\n const hasDetail = !!(c.breakdown?.tiers?.length || c.adjustments?.length)\n const isDetailOpen = pricingDetail?.lineIdx === lineIdx && pricingDetail?.chargeIdx === i\n return (\n <React.Fragment key={i}>\n <tr className=\"border-t border-dashed border-border/50\">\n <td className=\"py-1\">{c.chargeName ?? '\u2014'}</td>\n <td className=\"py-1 uppercase\">{c.chargeType ?? '\u2014'}</td>\n <td className=\"py-1 text-right font-mono\">{c.unitPrice != null ? fmt(c.unitPrice, currency) : '\u2014'}</td>\n <td className=\"py-1 text-right font-mono\">{c.quantity ?? '\u2014'}</td>\n <td className=\"py-1 text-right font-mono\">{c.totalPrice != null ? fmt(c.totalPrice, currency) : c.note ?? '\u2014'}</td>\n <td className=\"py-1 text-center\">\n {hasDetail && (\n <button\n onClick={() => onTogglePricingDetail(lineIdx, i)}\n className=\"inline-flex items-center justify-center rounded p-0.5 hover:bg-accent text-muted-foreground hover:text-foreground transition-colors\"\n title=\"Show pricing breakdown\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" strokeWidth={1.5} stroke=\"currentColor\" className=\"w-3.5 h-3.5\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z\" />\n </svg>\n </button>\n )}\n </td>\n </tr>\n {isDetailOpen && (\n <tr>\n <td colSpan={6} className=\"pb-3 pt-1\">\n <div className=\"rounded border bg-background p-3 space-y-3 text-xs\">\n <div className=\"flex items-center justify-between\">\n <span className=\"font-semibold text-sm\">Pricing Details \u2014 {c.chargeName}</span>\n <span className=\"text-muted-foreground\">{c.pricingMethod ?? 'flat'}</span>\n </div>\n\n {c.breakdown?.tiers && c.breakdown.tiers.length > 0 && (\n <div>\n <div className=\"font-medium text-muted-foreground mb-1\">Tier Breakdown</div>\n <table className=\"w-full\">\n <thead>\n <tr className=\"text-muted-foreground\">\n <th className=\"text-left py-0.5 font-medium\">Tier</th>\n <th className=\"text-right py-0.5 font-medium\">Range</th>\n <th className=\"text-right py-0.5 font-medium\">Price/Unit</th>\n <th className=\"text-right py-0.5 font-medium\">Qty</th>\n <th className=\"text-right py-0.5 font-medium\">Subtotal</th>\n </tr>\n </thead>\n <tbody>\n {c.breakdown.tiers.map((t) => (\n <tr key={t.tier} className=\"border-t border-border/30\">\n <td className=\"py-0.5\">{t.tier}</td>\n <td className=\"py-0.5 text-right font-mono\">{t.from}\u2013{t.to}</td>\n <td className=\"py-0.5 text-right font-mono\">{fmt(t.pricePerUnit, currency)}</td>\n <td className=\"py-0.5 text-right font-mono\">{t.quantity}</td>\n <td className=\"py-0.5 text-right font-mono\">{fmt(t.subtotal, currency)}</td>\n </tr>\n ))}\n <tr className=\"border-t font-medium\">\n <td colSpan={4} className=\"py-0.5 text-right\">Tiered Total</td>\n <td className=\"py-0.5 text-right font-mono\">\n {fmt(c.breakdown.tiers.reduce((s, t) => s + t.subtotal, 0), currency)}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n )}\n\n {c.adjustments && c.adjustments.length > 0 && (\n <div>\n <div className=\"font-medium text-muted-foreground mb-1\">Applied Price Rules</div>\n <table className=\"w-full\">\n <thead>\n <tr className=\"text-muted-foreground\">\n <th className=\"text-left py-0.5 font-medium\">Rule</th>\n <th className=\"text-left py-0.5 font-medium\">Type</th>\n <th className=\"text-right py-0.5 font-medium\">Value</th>\n <th className=\"text-right py-0.5 font-medium\">Before</th>\n <th className=\"text-right py-0.5 font-medium\">After</th>\n <th className=\"text-right py-0.5 font-medium\">Delta</th>\n </tr>\n </thead>\n <tbody>\n {c.adjustments.map((adj, ai) => (\n <tr key={ai} className=\"border-t border-border/30\">\n <td className=\"py-0.5\">{adj.ruleName}</td>\n <td className=\"py-0.5\">{RULE_TYPE_LABELS[adj.ruleType] ?? adj.ruleType}</td>\n <td className=\"py-0.5 text-right font-mono\">{fmtRuleValue(adj, currency)}</td>\n <td className=\"py-0.5 text-right font-mono\">{fmt(adj.unitPriceBefore, currency)}</td>\n <td className=\"py-0.5 text-right font-mono\">{fmt(adj.unitPriceAfter, currency)}</td>\n <td className={`py-0.5 text-right font-mono ${adj.delta < 0 ? 'text-destructive' : adj.delta > 0 ? 'text-status-success-text' : ''}`}>\n {adj.delta > 0 ? '+' : ''}{fmt(adj.delta, currency)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n\n <div className=\"flex items-center justify-between pt-1 border-t text-sm font-semibold\">\n <span>Final Total</span>\n <span className=\"font-mono\">{c.totalPrice != null ? fmt(c.totalPrice, currency) : '\u2014'}</span>\n </div>\n </div>\n </td>\n </tr>\n )}\n </React.Fragment>\n )\n })}\n </tbody>\n </table>\n )\n}\n\nfunction Spinner() {\n return (\n <svg className=\"h-4 w-4 animate-spin\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z\" />\n </svg>\n )\n}\n\nfunction ErrorBanner({ message, onDismiss }: { message: string; onDismiss?: () => void }) {\n return (\n <Alert variant=\"destructive\" className=\"flex items-start gap-2\">\n <span className=\"flex-1\">{message}</span>\n {onDismiss && (\n <button\n onClick={onDismiss}\n className=\"text-destructive/70 hover:text-destructive text-sm font-bold\"\n aria-label=\"Dismiss\"\n >\n \u00D7\n </button>\n )}\n </Alert>\n )\n}\n\n// XD-250 ARC: side-by-side \"Before / After\" snapshot for a quote / order line\n// that mirrors an existing subscription item. \"Before\" comes from the live\n// CpqInventorySubscriptionItem at render time; \"After\" is what the line will\n// apply on activation. For 'cancel' lines the after column is greyed out.\nfunction ArcLineDiff({ line, currency }: { line: OrderLineResult; currency: string }) {\n const src = line.arcSource\n if (!src) return null\n const isCancel = line.action === 'cancel'\n\n const beforeMrc = src.mrcAmount\n const beforeNrc = src.nrcAmount\n const beforeQty = src.quantity\n const afterMrc = isCancel ? 0 : line.mrcTotal\n const afterNrc = isCancel ? 0 : line.nrcTotal\n const afterQty = isCancel ? 0 : line.quantity\n const mrcDelta = afterMrc - beforeMrc\n const nrcDelta = afterNrc - beforeNrc\n const qtyDelta = afterQty - beforeQty\n\n return (\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-1.5\">Change Preview</p>\n <div className=\"rounded border bg-background overflow-hidden\">\n <table className=\"w-full text-xs\">\n <thead className=\"bg-muted/40\">\n <tr className=\"text-muted-foreground\">\n <th className=\"text-left px-3 py-1.5 font-medium\" />\n <th className=\"text-right px-3 py-1.5 font-medium\">Before</th>\n <th className=\"text-right px-3 py-1.5 font-medium\">After</th>\n <th className=\"text-right px-3 py-1.5 font-medium\">\u0394</th>\n </tr>\n </thead>\n <tbody>\n <ArcDiffRow label=\"Quantity\" before={beforeQty} after={afterQty} delta={qtyDelta} currency={null} dimAfter={isCancel} />\n <ArcDiffRow label=\"MRC\" before={beforeMrc} after={afterMrc} delta={mrcDelta} currency={currency} dimAfter={isCancel} />\n <ArcDiffRow label=\"NRC\" before={beforeNrc} after={afterNrc} delta={nrcDelta} currency={currency} dimAfter={isCancel} />\n </tbody>\n </table>\n </div>\n {isCancel && (\n <p className=\"text-xs text-destructive mt-1 italic\">\n Cancellation \u2014 item will be terminated on activation.\n </p>\n )}\n </div>\n )\n}\n\nfunction ArcDiffRow({\n label,\n before,\n after,\n delta,\n currency,\n dimAfter,\n}: {\n label: string\n before: number\n after: number\n delta: number\n currency: string | null\n dimAfter: boolean\n}) {\n const fmtVal = (v: number) => (currency ? fmt(v, currency) : String(v))\n const showDelta = delta !== 0\n return (\n <tr className=\"border-t border-dashed border-border/40\">\n <td className=\"px-3 py-1 text-muted-foreground\">{label}</td>\n <td className=\"px-3 py-1 text-right font-mono\">{fmtVal(before)}</td>\n <td className={`px-3 py-1 text-right font-mono ${dimAfter ? 'text-muted-foreground line-through' : ''}`}>\n {fmtVal(after)}\n </td>\n <td className={`px-3 py-1 text-right font-mono font-medium ${showDelta ? (delta > 0 ? 'text-status-success-text' : 'text-destructive') : 'text-muted-foreground'}`}>\n {showDelta ? `${delta > 0 ? '+' : ''}${fmtVal(delta)}` : '\u2014'}\n </td>\n </tr>\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 { ArrowRight, ExternalLink, Play, X } from 'lucide-react'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport {\n formatStatusLabel,\n orderCpqStatusMap,\n type OrderCpqStatus,\n} from '../../../../components/statusMaps'\nimport { StatusPath } from '../../../../components/StatusPath'\nimport { ChargeBreakdownTable, type ChargeRowView } from '../../../../components/ChargeBreakdownTable'\nimport { PricingSummaryCard, type PricingSummaryView } from '../../../../components/PricingSummaryCard'\nimport { ArcLineDiff, LineActionBadge } from '../../../../components/ArcLineDiff'\nimport { arcRecurringDelta } from '../../../../lib/quoteSummaryView'\nimport { isArcCancelledLine } from '../../../../services/arcLineRemoval'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype ArcLineSource = {\n subscriptionItemId: string\n name: string\n mrcAmount: number\n nrcAmount: number\n quantity: number\n}\n\ntype TierBreakdownEntry = { tier: number; from: number; to: number; pricePerUnit: number; quantity: number; subtotal: number }\ntype RuleAdjustment = { ruleCode: string; ruleName: string; ruleType: string; value: number; unitPriceBefore: number; unitPriceAfter: number; delta: number; origin?: 'rule' | 'manual'; adjustmentId?: string; reasonCode?: string }\n\ntype ResolvedCharge = {\n chargeName?: string\n chargeType?: string\n pricingMethod?: 'flat' | 'tiered' | 'per_unit'\n unitPrice?: number\n quantity?: number | null\n totalPrice?: number | null\n note?: string | null\n breakdown?: { tiers: TierBreakdownEntry[] } | null\n adjustments?: RuleAdjustment[] | null\n listUnitPrice?: number\n listTotalPrice?: number | null\n standardUnitPrice?: number\n standardTotalPrice?: number | null\n}\n\n/** Order lines carry the quote's stored waterfall verbatim (no recalculation at conversion). */\nfunction toChargeRow(c: ResolvedCharge): ChargeRowView {\n return { ...c, chargeName: c.chargeName ?? '\u2014', chargeType: (c.chargeType ?? 'mrc') as 'nrc' | 'mrc' | 'usage', unitPrice: c.unitPrice ?? 0, totalPrice: c.totalPrice ?? null }\n}\n\ntype OrderLineResult = {\n lineId: string\n offeringId: string | null\n offeringName: string\n offeringType: string | null\n productId: string | null\n specId: string | null\n action: string\n parentLineId: string | null\n quantity: number\n configuration: Record<string, unknown>\n startDate: string | null\n termMonths: number | null\n endDate: string | null\n nrcTotal: number\n mrcTotal: number\n charges: ResolvedCharge[]\n sourceQuoteLineId: string | null\n arcSource: ArcLineSource | null\n}\n\ntype OrderResult = {\n id: string\n orderId: string\n orderNumber: string\n customerId: string\n cpqStatus: string\n sourceQuoteId: string | null\n quoteType: string\n currencyCode: string\n pricingSummary: PricingSummaryView & { currencyCode: string }\n activatedAt: string | null\n createdAt: string\n lines: OrderLineResult[]\n}\n\nconst ARC_BADGE_VARIANTS: Record<string, 'brand' | 'success' | 'error'> = {\n amend: 'brand',\n renew: 'success',\n cancel: 'error',\n}\n\n\n// \u2500\u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction fmt(amount: number, currency: string): string {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)\n}\n\nasync function apiJson<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetch(url, init)\n const json = await res.json()\n if (!res.ok) throw new Error(json?.error ?? `Request failed (${res.status})`)\n return json as T\n}\n\n// \u2500\u2500\u2500 Main Page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport default function CpqOrderDetailPage(props: { params?: { id?: string } }) {\n const router = useRouter()\n const urlParams = useParams()\n const cpqOrderId = (props.params?.id ?? urlParams?.id) as string\n\n const [order, setOrder] = React.useState<OrderResult | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [activating, setActivating] = React.useState(false)\n const [expandedLines, setExpandedLines] = React.useState<Set<string>>(new Set())\n const [pricingDetail, setPricingDetail] = React.useState<{ lineIdx: number; chargeIdx: number } | null>(null)\n const [customerName, setCustomerName] = React.useState<string | null>(null)\n const [linkedSubscriptions, setLinkedSubscriptions] = React.useState<Array<{\n id: string\n name: string\n code: string\n status: string\n }>>([])\n\n // Find every subscription this order touches. NEW orders write\n // `sourceOrderId` on the freshly-created subscription, so a\n // `?sourceOrderId=\u2026` filter is enough there. ARC orders (amend /\n // renew / cancel) mutate existing subscriptions in place \u2014 the\n // subscription's `sourceOrderId` stays pinned to the original\n // order, so we also resolve targets via the source quote.\n const fetchLinkedSubscriptions = React.useCallback(\n async (orderConfigId: string, sourceQuoteId: string | null) => {\n const byOrderId = fetch(\n `/api/cpq/inventory/subscriptions?sourceOrderId=${orderConfigId}&pageSize=10`,\n )\n .then((r) => (r.ok ? r.json() : null))\n .then((d) => (d?.items ?? (Array.isArray(d) ? d : [])) as Array<{ id: string; name: string; code: string; status: string }>)\n .catch(() => [])\n\n const byTargets = sourceQuoteId\n ? fetch(`/api/cpq/quotes/${sourceQuoteId}/target-subscriptions`)\n .then((r) => (r.ok ? r.json() : null))\n .then(async (d) => {\n const targets: Array<{ subscriptionId: string }> = d?.items ?? []\n const fetched = await Promise.all(\n targets.map((t) =>\n fetch(`/api/cpq/inventory/subscriptions?id=${t.subscriptionId}`)\n .then((r) => (r.ok ? r.json() : null))\n .catch(() => null),\n ),\n )\n return fetched.filter(Boolean) as Array<{ id: string; name: string; code: string; status: string }>\n })\n .catch(() => [] as Array<{ id: string; name: string; code: string; status: string }>)\n : Promise.resolve([] as Array<{ id: string; name: string; code: string; status: string }>)\n\n const [a, b] = await Promise.all([byOrderId, byTargets])\n // Dedupe by id \u2014 a single subscription may appear in both lists\n // (e.g. a NEW order that was later amended into the same sub).\n const seen = new Set<string>()\n const merged: Array<{ id: string; name: string; code: string; status: string }> = []\n for (const sub of [...a, ...b]) {\n if (!sub || seen.has(sub.id)) continue\n seen.add(sub.id)\n merged.push({ id: sub.id, name: sub.name, code: sub.code, status: sub.status })\n }\n setLinkedSubscriptions(merged)\n },\n [],\n )\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n try {\n const result = await apiJson<OrderResult>(`/api/cpq/orders/${cpqOrderId}`)\n if (!cancelled) {\n setOrder(result)\n fetch(`/api/customers/companies?id=${result.customerId}&pageSize=1`)\n .then((r) => r.json())\n .then((data) => {\n const items = data.items ?? data ?? []\n const c = items[0]\n if (c && !cancelled) setCustomerName(c.display_name || c.displayName || c.name || null)\n })\n .catch(() => {})\n if (result.cpqStatus === 'active' || result.cpqStatus === 'fulfilled') {\n void fetchLinkedSubscriptions(result.id, result.sourceQuoteId)\n }\n }\n } catch (err) {\n if (!cancelled) setError((err as Error).message)\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [cpqOrderId, fetchLinkedSubscriptions])\n\n const activateOrder = async () => {\n if (!order) return\n try {\n setActivating(true)\n setError(null)\n const result = await apiJson<OrderResult>(`/api/cpq/orders/${order.id}/activate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({}),\n })\n setOrder(result)\n void fetchLinkedSubscriptions(result.id, result.sourceQuoteId)\n } catch (err) {\n setError((err as Error).message)\n } finally {\n setActivating(false)\n }\n }\n\n const cancelOrder = async () => {\n if (!order) return\n try {\n setError(null)\n const result = await apiJson<OrderResult>(`/api/cpq/orders/${order.id}/status`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ targetStatus: 'cancelled' }),\n })\n setOrder(result)\n } catch (err) {\n setError((err as Error).message)\n }\n }\n\n const toggleLine = (id: string) => {\n setExpandedLines((prev) => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n })\n }\n\n // \u2500\u2500\u2500 Render: loading / error \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 py-12 justify-center text-sm text-muted-foreground\">\n <Spinner /> Loading\u2026\n </div>\n )\n }\n\n if (!order) {\n return (\n <div className=\"space-y-4\">\n <button onClick={() => router.push('/backend/cpq/orders')} className=\"text-sm text-muted-foreground hover:text-foreground\">\u2190 Back to Orders</button>\n {error && <ErrorBanner message={error} />}\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">Order not found.</div>\n </div>\n )\n }\n\n // \u2500\u2500\u2500 Render: detail \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const canActivate = order.cpqStatus === 'draft' || order.cpqStatus === 'pending_activation'\n const canCancel = order.cpqStatus === 'draft' || order.cpqStatus === 'pending_activation' || order.cpqStatus === 'active'\n const currency = order.currencyCode\n\n const togglePricingDetail = (lineIdx: number, chargeIdx: number) => {\n setPricingDetail((prev) =>\n prev?.lineIdx === lineIdx && prev?.chargeIdx === chargeIdx ? null : { lineIdx, chargeIdx },\n )\n }\n\n return (\n <div className=\"space-y-6\">\n {/* Header \u2014 standard FormHeader detail mode, mirrors the CPQ quote\n layout so order \u2194 quote stays consistent. Title is just the order\n number; status / ARC / converted tags sit underneath; primary\n actions (Activate, then Cancel) live on the right next to the\n View Sales Order / Source Quote utility links. */}\n <FormHeader\n mode=\"detail\"\n backHref=\"/backend/cpq/orders\"\n backLabel=\"Back to CPQ Orders\"\n entityTypeLabel=\"CPQ Order\"\n title={order.orderNumber || order.orderId.slice(0, 8)}\n statusBadge={\n <div className=\"flex flex-wrap items-center gap-2 pt-1\">\n <Tag variant={orderCpqStatusMap[order.cpqStatus as OrderCpqStatus] ?? 'neutral'} dot>\n {formatStatusLabel(order.cpqStatus)}\n </Tag>\n {order.quoteType && order.quoteType !== 'new' && (\n <Tag variant={ARC_BADGE_VARIANTS[order.quoteType] ?? 'neutral'} className=\"uppercase\">\n {order.quoteType}\n </Tag>\n )}\n </div>\n }\n actionsContent={\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => router.push(`/backend/sales/orders/${order.orderId}`)}\n >\n <ExternalLink className=\"h-3.5 w-3.5\" />\n View Sales Order\n </Button>\n {order.sourceQuoteId && (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => router.push(`/backend/cpq/quotes/${order.sourceQuoteId}`)}\n >\n Source Quote\n </Button>\n )}\n {canActivate && (\n <Button type=\"button\" onClick={activateOrder} disabled={activating}>\n {activating ? <Spinner /> : <Play className=\"h-4 w-4\" />}\n Activate\n </Button>\n )}\n {canCancel && (\n <Button type=\"button\" variant=\"destructive-outline\" onClick={cancelOrder}>\n <X className=\"h-4 w-4\" />\n Cancel\n </Button>\n )}\n </>\n }\n />\n\n {/* Status path \u2014 visual progress only. Orders advance via the\n dedicated Activate / Cancel buttons because activation has\n backend side effects (subscription / asset creation). */}\n <div className=\"flex items-center gap-3 flex-wrap\">\n <StatusPath\n // Order path is read-only \u2014 activation has backend side effects\n // (subscription / asset creation) so transitions go through the\n // dedicated Activate / Cancel buttons.\n current={order.cpqStatus}\n path={['draft', 'pending_activation', 'active', 'fulfilled']}\n terminals={['cancelled']}\n statusMap={orderCpqStatusMap}\n ariaLabel=\"Order status path\"\n />\n </div>\n\n {error && <ErrorBanner message={error} onDismiss={() => setError(null)} />}\n\n {/* Linked subscriptions \u2014 one card per subscription this order\n touches. NEW orders write one fresh subscription; ARC orders\n (amend / renew / cancel) reach one or more existing\n subscriptions through the source quote's target list. */}\n {linkedSubscriptions.length > 0 && (\n <div className=\"space-y-2\">\n {linkedSubscriptions.map((sub) => (\n <div\n key={sub.id}\n className=\"rounded-lg border bg-card p-4 flex items-center gap-4\"\n >\n <div className=\"flex-1 min-w-0 space-y-1\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n Linked Subscription\n </span>\n <Tag\n variant={sub.status === 'active' ? 'success' : 'info'}\n dot\n className=\"text-[10px] px-1.5\"\n >\n {sub.status}\n </Tag>\n </div>\n <div className=\"text-sm font-medium\">\n {sub.name}{' '}\n <span className=\"text-muted-foreground font-mono text-xs\">({sub.code})</span>\n </div>\n {order.activatedAt && (\n <div className=\"text-xs text-muted-foreground\">\n Activated on {new Date(order.activatedAt).toLocaleString()}\n </div>\n )}\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => router.push(`/backend/cpq/inventory/subscriptions/${sub.id}`)}\n >\n <ArrowRight className=\"h-3.5 w-3.5\" />\n Open\n </Button>\n </div>\n ))}\n </div>\n )}\n\n {/* Meta cards \u2014 secondary reference info, matches the quote layout. */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n <MetaCard label=\"Customer\">\n {customerName ? (\n <span className=\"font-medium\">{customerName}</span>\n ) : (\n <span className=\"font-mono text-xs text-muted-foreground\">\n {order.customerId.slice(0, 8)}\u2026\n </span>\n )}\n </MetaCard>\n <MetaCard label=\"Created\">\n <span className=\"font-medium\">{new Date(order.createdAt).toLocaleDateString()}</span>\n </MetaCard>\n <MetaCard label=\"Currency\">\n <span className=\"font-medium\">{currency}</span>\n </MetaCard>\n </div>\n\n <PricingSummaryCard\n summary={order.pricingSummary}\n currency={currency}\n arcDelta={order.quoteType === 'amend' || order.quoteType === 'renew'\n ? arcRecurringDelta(order.lines.map((l) => ({ ...l, action: l.action as 'add' | 'modify' | 'cancel' })))\n : null}\n />\n\n {/* Order Lines */}\n <OrderLineTree\n lines={order.lines}\n quoteType={order.quoteType}\n currency={currency}\n expandedLines={expandedLines}\n pricingDetail={pricingDetail}\n onToggleLine={toggleLine}\n onTogglePricingDetail={togglePricingDetail}\n />\n </div>\n )\n}\n\nfunction MetaCard({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {label}\n </div>\n <div className=\"mt-1 text-sm\">{children}</div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Sub-components \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction OrderLineTree({\n lines,\n quoteType,\n currency,\n expandedLines,\n pricingDetail,\n onToggleLine,\n onTogglePricingDetail,\n}: {\n lines: OrderLineResult[]\n quoteType: string\n currency: string\n expandedLines: Set<string>\n pricingDetail: { lineIdx: number; chargeIdx: number } | null\n onToggleLine: (id: string) => void\n onTogglePricingDetail: (lineIdx: number, chargeIdx: number) => void\n}) {\n const rootLines = lines.filter((l) => !l.parentLineId)\n const childrenByParent = new Map<string, OrderLineResult[]>()\n for (const line of lines) {\n if (line.parentLineId) {\n const arr = childrenByParent.get(line.parentLineId) ?? []\n arr.push(line)\n childrenByParent.set(line.parentLineId, arr)\n }\n }\n\n // Stable index per visible line so the pricing-detail toggle can be\n // keyed by (lineIdx, chargeIdx) the same way the quote page does.\n const indexOf = new Map<string, number>()\n lines.forEach((l, i) => indexOf.set(l.lineId, i))\n\n return (\n <div>\n <h3 className=\"text-sm font-medium mb-2 px-1\">Line Items ({lines.length})</h3>\n {lines.length === 0 ? (\n <div className=\"rounded-lg border bg-card py-8 text-center text-sm text-muted-foreground\">\n No line items.\n </div>\n ) : (\n <div className=\"rounded-lg border bg-card divide-y\">\n {rootLines.map((line) => {\n const isBundle = line.offeringType === 'bundle'\n const children = childrenByParent.get(line.lineId) ?? []\n const isExpanded = expandedLines.has(line.lineId)\n const lineIdx = indexOf.get(line.lineId) ?? 0\n return (\n <div key={line.lineId}>\n <OrderLineRow\n line={line}\n lineIdx={lineIdx}\n quoteType={quoteType}\n currency={currency}\n isExpanded={isExpanded}\n isBundle={isBundle}\n childCount={children.length}\n indent={0}\n pricingDetail={pricingDetail}\n onToggle={() => onToggleLine(line.lineId)}\n onTogglePricingDetail={onTogglePricingDetail}\n />\n {isBundle && isExpanded && children.length > 0 && (\n <div className=\"border-t border-dashed border-brand-violet/30 bg-brand-violet/5\">\n <div className=\"px-6 py-1.5\">\n <span className=\"text-[10px] font-medium text-brand-violet uppercase tracking-wider\">Bundle Components ({children.length})</span>\n </div>\n {children.map((child) => {\n const childIdx = indexOf.get(child.lineId) ?? 0\n return (\n <OrderLineRow\n key={child.lineId}\n line={child}\n lineIdx={childIdx}\n quoteType={quoteType}\n currency={currency}\n isExpanded={expandedLines.has(child.lineId)}\n isBundle={false}\n childCount={0}\n indent={1}\n pricingDetail={pricingDetail}\n onToggle={() => onToggleLine(child.lineId)}\n onTogglePricingDetail={onTogglePricingDetail}\n />\n )\n })}\n </div>\n )}\n </div>\n )\n })}\n </div>\n )}\n </div>\n )\n}\n\nfunction OrderLineRow({ line, lineIdx, quoteType, currency, isExpanded, isBundle, childCount, indent, pricingDetail, onToggle, onTogglePricingDetail }: {\n line: OrderLineResult; lineIdx: number; quoteType: string; currency: string; isExpanded: boolean; isBundle: boolean; childCount: number; indent: number\n pricingDetail: { lineIdx: number; chargeIdx: number } | null\n onToggle: () => void\n onTogglePricingDetail: (lineIdx: number, chargeIdx: number) => void\n}) {\n const t = useT()\n const paddingLeft = indent > 0 ? `${indent * 2 + 1}rem` : undefined\n // Same presentation as the quote page: an existing item removed on an\n // Amend / Renew quote reads as \"remove\", struck through.\n const isRemoved = isArcCancelledLine(line, quoteType)\n\n return (\n <div>\n <div\n className={`flex items-center gap-3 px-4 py-3 hover:bg-muted/30 transition-colors cursor-pointer ${isRemoved ? 'bg-muted/20' : ''}`}\n style={paddingLeft ? { paddingLeft } : undefined}\n onClick={onToggle}\n data-testid=\"cpq-order-line\"\n data-removed={isRemoved ? 'true' : undefined}\n >\n <button className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:text-foreground\">\n <svg className={`h-4 w-4 transition-transform ${isExpanded ? 'rotate-90' : ''}`} fill=\"none\" viewBox=\"0 0 24 24\" strokeWidth=\"2\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" />\n </svg>\n </button>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n {indent > 0 && <span className=\"text-muted-foreground text-xs\">\u2514</span>}\n {isBundle && (\n <Tag variant=\"brand\" className=\"px-1.5 text-xs\">bundle</Tag>\n )}\n <span className={`font-medium text-sm ${isRemoved ? 'text-muted-foreground line-through' : ''}`}>{line.offeringName}</span>\n {line.quantity > 1 && <span className=\"rounded bg-muted px-1.5 py-0.5 text-xs font-medium\">\u00D7{line.quantity}</span>}\n <LineActionBadge\n action={line.action}\n label={isRemoved ? t('cpq.quotes.arc.lineRemoved', 'remove') : undefined}\n title={isRemoved ? t('cpq.quotes.arc.lineRemovedHint', 'This item is cancelled on the subscription when the order is activated') : undefined}\n />\n {isBundle && <span className=\"text-xs text-muted-foreground\">{childCount} component{childCount !== 1 ? 's' : ''}</span>}\n </div>\n </div>\n\n <div className=\"shrink-0 text-right text-xs space-y-0.5\">\n {isRemoved && line.arcSource && line.arcSource.mrcAmount > 0 && (\n <div>\n <span className=\"text-muted-foreground\">MRC </span>\n <span className=\"font-mono text-muted-foreground line-through\">{fmt(line.arcSource.mrcAmount, currency)}</span>\n </div>\n )}\n {line.nrcTotal > 0 && <div><span className=\"text-muted-foreground\">NRC </span><span className=\"font-mono font-medium\">{fmt(line.nrcTotal, currency)}</span></div>}\n {line.mrcTotal > 0 && <div><span className=\"text-muted-foreground\">MRC </span><span className=\"font-mono font-medium\">{fmt(line.mrcTotal, currency)}</span></div>}\n </div>\n </div>\n\n {isExpanded && (() => {\n const internalKeys = new Set(['_arcMirroredName', '_arcMirroredFromItemId', '_arcPriceFrozen', '_arcRepriced', 'offeringName'])\n const visibleConfigEntries = Object.entries(line.configuration).filter(\n ([key]) => !internalKeys.has(key),\n )\n const hasConfig = visibleConfigEntries.length > 0\n const hasCharges = line.charges.length > 0\n const hasTerm = !!(line.startDate || line.termMonths || line.endDate)\n const hasArcDiff = !!line.arcSource\n const mirroredFromItemId = (line.configuration?._arcMirroredFromItemId as string | undefined) ?? null\n const isEmpty = !hasConfig && !hasCharges && !hasTerm && !hasArcDiff\n\n return (\n <div\n className=\"border-t bg-muted/10 px-12 py-3 space-y-3\"\n style={paddingLeft ? { paddingLeft: `calc(${paddingLeft} + 2rem)` } : undefined}\n >\n {isEmpty ? (\n <div className=\"text-xs text-muted-foreground space-y-1\">\n <div>\n No detailed breakdown for this line.\n {mirroredFromItemId && ' Mirrored from existing subscription item.'}\n </div>\n {(line.nrcTotal > 0 || line.mrcTotal > 0) && (\n <div className=\"flex gap-4\">\n {line.nrcTotal > 0 && (\n <span>NRC <span className=\"font-mono font-medium text-foreground\">{fmt(line.nrcTotal, currency)}</span></span>\n )}\n {line.mrcTotal > 0 && (\n <span>MRC <span className=\"font-mono font-medium text-foreground\">{fmt(line.mrcTotal, currency)}</span></span>\n )}\n </div>\n )}\n </div>\n ) : (\n <>\n {hasConfig && (\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-1\">Configuration</p>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1\">\n {visibleConfigEntries.map(([key, value]) => (\n <div key={key} className=\"text-xs\">\n <span className=\"text-muted-foreground\">{key}: </span>\n <span className=\"font-medium\">{String(value)}</span>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {hasCharges && (\n <ChargeBreakdownTable\n charges={line.charges.map(toChargeRow)}\n currency={currency}\n openDetailIndex={pricingDetail?.lineIdx === lineIdx ? pricingDetail.chargeIdx : null}\n onToggleDetail={(chargeIdx) => onTogglePricingDetail(lineIdx, chargeIdx)}\n />\n )}\n\n {hasTerm && (\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n {line.startDate && <span>Start: {new Date(line.startDate).toLocaleDateString()}</span>}\n {line.termMonths && <span>Term: {line.termMonths}mo</span>}\n {line.endDate && <span>End: {new Date(line.endDate).toLocaleDateString()}</span>}\n </div>\n )}\n\n {/* XD-250 ARC: before/after preview below the charges, as on the quote. */}\n {hasArcDiff && <ArcLineDiff line={line} currency={currency} />}\n </>\n )}\n </div>\n )\n })()}\n </div>\n )\n}\n\nfunction Spinner() {\n return (\n <svg className=\"h-4 w-4 animate-spin\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z\" />\n </svg>\n )\n}\n\nfunction ErrorBanner({ message, onDismiss }: { message: string; onDismiss?: () => void }) {\n return (\n <Alert variant=\"destructive\" className=\"flex items-start gap-2\">\n <span className=\"flex-1\">{message}</span>\n {onDismiss && (\n <button\n onClick={onDismiss}\n className=\"text-destructive/70 hover:text-destructive text-sm font-bold\"\n aria-label=\"Dismiss\"\n >\n \u00D7\n </button>\n )}\n </Alert>\n )\n}\n"],
5
+ "mappings": ";AA0PM,SAsDI,UArDF,KADF;AAzPN,YAAY,WAAW;AACvB,SAAS,WAAW,iBAAiB;AACrC,SAAS,YAAY,cAAc,MAAM,SAAS;AAClD,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB,SAAS,WAAW;AACpB,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,kBAAkB;AAC3B,SAAS,4BAAgD;AACzD,SAAS,0BAAmD;AAC5D,SAAS,aAAa,uBAAuB;AAC7C,SAAS,yBAAyB;AAClC,SAAS,0BAA0B;AACnC,SAAS,YAAY;AAgCrB,SAAS,YAAY,GAAkC;AACrD,SAAO,EAAE,GAAG,GAAG,YAAY,EAAE,cAAc,UAAK,YAAa,EAAE,cAAc,OAAmC,WAAW,EAAE,aAAa,GAAG,YAAY,EAAE,cAAc,KAAK;AAChL;AAsCA,MAAM,qBAAoE;AAAA,EACxE,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AACV;AAKA,SAAS,IAAI,QAAgB,UAA0B;AACrD,SAAO,IAAI,KAAK,aAAa,SAAS,EAAE,OAAO,YAAY,SAAS,CAAC,EAAE,OAAO,MAAM;AACtF;AAEA,eAAe,QAAW,KAAa,MAAgC;AACrE,QAAM,MAAM,MAAM,MAAM,KAAK,IAAI;AACjC,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,MAAM,SAAS,mBAAmB,IAAI,MAAM,GAAG;AAC5E,SAAO;AACT;AAIe,SAAR,mBAAoC,OAAqC;AAC9E,QAAM,SAAS,UAAU;AACzB,QAAM,YAAY,UAAU;AAC5B,QAAM,aAAc,MAAM,QAAQ,MAAM,WAAW;AAEnD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAA6B,IAAI;AACjE,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,IAAI;AACjD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAsB,oBAAI,IAAI,CAAC;AAC/E,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAwD,IAAI;AAC5G,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAwB,IAAI;AAC1E,QAAM,CAAC,qBAAqB,sBAAsB,IAAI,MAAM,SAKxD,CAAC,CAAC;AAQN,QAAM,2BAA2B,MAAM;AAAA,IACrC,OAAO,eAAuB,kBAAiC;AAC7D,YAAM,YAAY;AAAA,QAChB,kDAAkD,aAAa;AAAA,MACjE,EACG,KAAK,CAAC,MAAO,EAAE,KAAK,EAAE,KAAK,IAAI,IAAK,EACpC,KAAK,CAAC,MAAO,GAAG,UAAU,MAAM,QAAQ,CAAC,IAAI,IAAI,CAAC,EAAwE,EAC1H,MAAM,MAAM,CAAC,CAAC;AAEjB,YAAM,YAAY,gBACd,MAAM,mBAAmB,aAAa,uBAAuB,EAC1D,KAAK,CAAC,MAAO,EAAE,KAAK,EAAE,KAAK,IAAI,IAAK,EACpC,KAAK,OAAO,MAAM;AACjB,cAAM,UAA6C,GAAG,SAAS,CAAC;AAChE,cAAM,UAAU,MAAM,QAAQ;AAAA,UAC5B,QAAQ;AAAA,YAAI,CAAC,MACX,MAAM,uCAAuC,EAAE,cAAc,EAAE,EAC5D,KAAK,CAAC,MAAO,EAAE,KAAK,EAAE,KAAK,IAAI,IAAK,EACpC,MAAM,MAAM,IAAI;AAAA,UACrB;AAAA,QACF;AACA,eAAO,QAAQ,OAAO,OAAO;AAAA,MAC/B,CAAC,EACA,MAAM,MAAM,CAAC,CAAsE,IACtF,QAAQ,QAAQ,CAAC,CAAsE;AAE3F,YAAM,CAAC,GAAG,CAAC,IAAI,MAAM,QAAQ,IAAI,CAAC,WAAW,SAAS,CAAC;AAGvD,YAAM,OAAO,oBAAI,IAAY;AAC7B,YAAM,SAA4E,CAAC;AACnF,iBAAW,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG;AAC9B,YAAI,CAAC,OAAO,KAAK,IAAI,IAAI,EAAE,EAAG;AAC9B,aAAK,IAAI,IAAI,EAAE;AACf,eAAO,KAAK,EAAE,IAAI,IAAI,IAAI,MAAM,IAAI,MAAM,MAAM,IAAI,MAAM,QAAQ,IAAI,OAAO,CAAC;AAAA,MAChF;AACA,6BAAuB,MAAM;AAAA,IAC/B;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI,YAAY;AAChB,mBAAe,OAAO;AACpB,UAAI;AACF,cAAM,SAAS,MAAM,QAAqB,mBAAmB,UAAU,EAAE;AACzE,YAAI,CAAC,WAAW;AACd,mBAAS,MAAM;AACf,gBAAM,+BAA+B,OAAO,UAAU,aAAa,EAChE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,EACpB,KAAK,CAAC,SAAS;AACd,kBAAM,QAAQ,KAAK,SAAS,QAAQ,CAAC;AACrC,kBAAM,IAAI,MAAM,CAAC;AACjB,gBAAI,KAAK,CAAC,UAAW,iBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,QAAQ,IAAI;AAAA,UACxF,CAAC,EACA,MAAM,MAAM;AAAA,UAAC,CAAC;AACjB,cAAI,OAAO,cAAc,YAAY,OAAO,cAAc,aAAa;AACrE,iBAAK,yBAAyB,OAAO,IAAI,OAAO,aAAa;AAAA,UAC/D;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AACZ,YAAI,CAAC,UAAW,UAAU,IAAc,OAAO;AAAA,MACjD,UAAE;AACA,YAAI,CAAC,UAAW,YAAW,KAAK;AAAA,MAClC;AAAA,IACF;AACA,SAAK;AACL,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAK;AAAA,EAClC,GAAG,CAAC,YAAY,wBAAwB,CAAC;AAEzC,QAAM,gBAAgB,YAAY;AAChC,QAAI,CAAC,MAAO;AACZ,QAAI;AACF,oBAAc,IAAI;AAClB,eAAS,IAAI;AACb,YAAM,SAAS,MAAM,QAAqB,mBAAmB,MAAM,EAAE,aAAa;AAAA,QAChF,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,CAAC,CAAC;AAAA,MACzB,CAAC;AACD,eAAS,MAAM;AACf,WAAK,yBAAyB,OAAO,IAAI,OAAO,aAAa;AAAA,IAC/D,SAAS,KAAK;AACZ,eAAU,IAAc,OAAO;AAAA,IACjC,UAAE;AACA,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,cAAc,YAAY;AAC9B,QAAI,CAAC,MAAO;AACZ,QAAI;AACF,eAAS,IAAI;AACb,YAAM,SAAS,MAAM,QAAqB,mBAAmB,MAAM,EAAE,WAAW;AAAA,QAC9E,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,cAAc,YAAY,CAAC;AAAA,MACpD,CAAC;AACD,eAAS,MAAM;AAAA,IACjB,SAAS,KAAK;AACZ,eAAU,IAAc,OAAO;AAAA,IACjC;AAAA,EACF;AAEA,QAAM,aAAa,CAAC,OAAe;AACjC,qBAAiB,CAAC,SAAS;AAAE,YAAM,IAAI,IAAI,IAAI,IAAI;AAAG,QAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE;AAAG,aAAO;AAAA,IAAE,CAAC;AAAA,EACxG;AAIA,MAAI,SAAS;AACX,WACE,qBAAC,SAAI,WAAU,8EACb;AAAA,0BAAC,WAAQ;AAAA,MAAE;AAAA,OACb;AAAA,EAEJ;AAEA,MAAI,CAAC,OAAO;AACV,WACE,qBAAC,SAAI,WAAU,aACb;AAAA,0BAAC,YAAO,SAAS,MAAM,OAAO,KAAK,qBAAqB,GAAG,WAAU,uDAAsD,mCAAgB;AAAA,MAC1I,SAAS,oBAAC,eAAY,SAAS,OAAO;AAAA,MACvC,oBAAC,SAAI,WAAU,2EAA0E,8BAAgB;AAAA,OAC3G;AAAA,EAEJ;AAIA,QAAM,cAAc,MAAM,cAAc,WAAW,MAAM,cAAc;AACvE,QAAM,YAAY,MAAM,cAAc,WAAW,MAAM,cAAc,wBAAwB,MAAM,cAAc;AACjH,QAAM,WAAW,MAAM;AAEvB,QAAM,sBAAsB,CAAC,SAAiB,cAAsB;AAClE;AAAA,MAAiB,CAAC,SAChB,MAAM,YAAY,WAAW,MAAM,cAAc,YAAY,OAAO,EAAE,SAAS,UAAU;AAAA,IAC3F;AAAA,EACF;AAEA,SACE,qBAAC,SAAI,WAAU,aAMb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,UAAS;AAAA,QACT,WAAU;AAAA,QACV,iBAAgB;AAAA,QAChB,OAAO,MAAM,eAAe,MAAM,QAAQ,MAAM,GAAG,CAAC;AAAA,QACpD,aACE,qBAAC,SAAI,WAAU,0CACb;AAAA,8BAAC,OAAI,SAAS,kBAAkB,MAAM,SAA2B,KAAK,WAAW,KAAG,MACjF,4BAAkB,MAAM,SAAS,GACpC;AAAA,UACC,MAAM,aAAa,MAAM,cAAc,SACtC,oBAAC,OAAI,SAAS,mBAAmB,MAAM,SAAS,KAAK,WAAW,WAAU,aACvE,gBAAM,WACT;AAAA,WAEJ;AAAA,QAEF,gBACE,iCACE;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,SAAS,MAAM,OAAO,KAAK,yBAAyB,MAAM,OAAO,EAAE;AAAA,cAEnE;AAAA,oCAAC,gBAAa,WAAU,eAAc;AAAA,gBAAE;AAAA;AAAA;AAAA,UAE1C;AAAA,UACC,MAAM,iBACL;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,SAAS,MAAM,OAAO,KAAK,uBAAuB,MAAM,aAAa,EAAE;AAAA,cACxE;AAAA;AAAA,UAED;AAAA,UAED,eACC,qBAAC,UAAO,MAAK,UAAS,SAAS,eAAe,UAAU,YACrD;AAAA,yBAAa,oBAAC,WAAQ,IAAK,oBAAC,QAAK,WAAU,WAAU;AAAA,YAAG;AAAA,aAE3D;AAAA,UAED,aACC,qBAAC,UAAO,MAAK,UAAS,SAAQ,uBAAsB,SAAS,aAC3D;AAAA,gCAAC,KAAE,WAAU,WAAU;AAAA,YAAE;AAAA,aAE3B;AAAA,WAEJ;AAAA;AAAA,IAEJ;AAAA,IAKA,oBAAC,SAAI,WAAU,qCACb;AAAA,MAAC;AAAA;AAAA,QAIC,SAAS,MAAM;AAAA,QACf,MAAM,CAAC,SAAS,sBAAsB,UAAU,WAAW;AAAA,QAC3D,WAAW,CAAC,WAAW;AAAA,QACvB,WAAW;AAAA,QACX,WAAU;AAAA;AAAA,IACZ,GACF;AAAA,IAEC,SAAS,oBAAC,eAAY,SAAS,OAAO,WAAW,MAAM,SAAS,IAAI,GAAG;AAAA,IAMvE,oBAAoB,SAAS,KAC5B,oBAAC,SAAI,WAAU,aACZ,8BAAoB,IAAI,CAAC,QACxB;AAAA,MAAC;AAAA;AAAA,QAEC,WAAU;AAAA,QAEV;AAAA,+BAAC,SAAI,WAAU,4BACb;AAAA,iCAAC,SAAI,WAAU,qCACb;AAAA,kCAAC,UAAK,WAAU,yEAAwE,iCAExF;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,IAAI,WAAW,WAAW,YAAY;AAAA,kBAC/C,KAAG;AAAA,kBACH,WAAU;AAAA,kBAET,cAAI;AAAA;AAAA,cACP;AAAA,eACF;AAAA,YACA,qBAAC,SAAI,WAAU,uBACZ;AAAA,kBAAI;AAAA,cAAM;AAAA,cACX,qBAAC,UAAK,WAAU,2CAA0C;AAAA;AAAA,gBAAE,IAAI;AAAA,gBAAK;AAAA,iBAAC;AAAA,eACxE;AAAA,YACC,MAAM,eACL,qBAAC,SAAI,WAAU,iCAAgC;AAAA;AAAA,cAC/B,IAAI,KAAK,MAAM,WAAW,EAAE,eAAe;AAAA,eAC3D;AAAA,aAEJ;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,SAAS,MAAM,OAAO,KAAK,wCAAwC,IAAI,EAAE,EAAE;AAAA,cAE3E;AAAA,oCAAC,cAAW,WAAU,eAAc;AAAA,gBAAE;AAAA;AAAA;AAAA,UAExC;AAAA;AAAA;AAAA,MAjCK,IAAI;AAAA,IAkCX,CACD,GACH;AAAA,IAIF,qBAAC,SAAI,WAAU,wDACb;AAAA,0BAAC,YAAS,OAAM,YACb,yBACC,oBAAC,UAAK,WAAU,eAAe,wBAAa,IAE5C,qBAAC,UAAK,WAAU,2CACb;AAAA,cAAM,WAAW,MAAM,GAAG,CAAC;AAAA,QAAE;AAAA,SAChC,GAEJ;AAAA,MACA,oBAAC,YAAS,OAAM,WACd,8BAAC,UAAK,WAAU,eAAe,cAAI,KAAK,MAAM,SAAS,EAAE,mBAAmB,GAAE,GAChF;AAAA,MACA,oBAAC,YAAS,OAAM,YACd,8BAAC,UAAK,WAAU,eAAe,oBAAS,GAC1C;AAAA,OACF;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM;AAAA,QACf;AAAA,QACA,UAAU,MAAM,cAAc,WAAW,MAAM,cAAc,UACzD,kBAAkB,MAAM,MAAM,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,QAAQ,EAAE,OAAsC,EAAE,CAAC,IACrG;AAAA;AAAA,IACN;AAAA,IAGA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,MAAM;AAAA,QACb,WAAW,MAAM;AAAA,QACjB;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc;AAAA,QACd,uBAAuB;AAAA;AAAA,IACzB;AAAA,KACF;AAEJ;AAEA,SAAS,SAAS,EAAE,OAAO,SAAS,GAAiD;AACnF,SACE,qBAAC,SAAI,WAAU,iCACb;AAAA,wBAAC,SAAI,WAAU,yEACZ,iBACH;AAAA,IACA,oBAAC,SAAI,WAAU,gBAAgB,UAAS;AAAA,KAC1C;AAEJ;AAIA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAQG;AACD,QAAM,YAAY,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,YAAY;AACrD,QAAM,mBAAmB,oBAAI,IAA+B;AAC5D,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,cAAc;AACrB,YAAM,MAAM,iBAAiB,IAAI,KAAK,YAAY,KAAK,CAAC;AACxD,UAAI,KAAK,IAAI;AACb,uBAAiB,IAAI,KAAK,cAAc,GAAG;AAAA,IAC7C;AAAA,EACF;AAIA,QAAM,UAAU,oBAAI,IAAoB;AACxC,QAAM,QAAQ,CAAC,GAAG,MAAM,QAAQ,IAAI,EAAE,QAAQ,CAAC,CAAC;AAEhD,SACE,qBAAC,SACC;AAAA,yBAAC,QAAG,WAAU,iCAAgC;AAAA;AAAA,MAAa,MAAM;AAAA,MAAO;AAAA,OAAC;AAAA,IACxE,MAAM,WAAW,IAChB,oBAAC,SAAI,WAAU,4EAA2E,4BAE1F,IAEA,oBAAC,SAAI,WAAU,sCACZ,oBAAU,IAAI,CAAC,SAAS;AACvB,YAAM,WAAW,KAAK,iBAAiB;AACvC,YAAM,WAAW,iBAAiB,IAAI,KAAK,MAAM,KAAK,CAAC;AACvD,YAAM,aAAa,cAAc,IAAI,KAAK,MAAM;AAChD,YAAM,UAAU,QAAQ,IAAI,KAAK,MAAM,KAAK;AAC5C,aACE,qBAAC,SACC;AAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,YAAY,SAAS;AAAA,YACrB,QAAQ;AAAA,YACR;AAAA,YACA,UAAU,MAAM,aAAa,KAAK,MAAM;AAAA,YACxC;AAAA;AAAA,QACF;AAAA,QACC,YAAY,cAAc,SAAS,SAAS,KAC3C,qBAAC,SAAI,WAAU,mEACb;AAAA,8BAAC,SAAI,WAAU,eACb,+BAAC,UAAK,WAAU,sEAAqE;AAAA;AAAA,YAAoB,SAAS;AAAA,YAAO;AAAA,aAAC,GAC5H;AAAA,UACC,SAAS,IAAI,CAAC,UAAU;AACvB,kBAAM,WAAW,QAAQ,IAAI,MAAM,MAAM,KAAK;AAC9C,mBACE;AAAA,cAAC;AAAA;AAAA,gBAEC,MAAM;AAAA,gBACN,SAAS;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA,YAAY,cAAc,IAAI,MAAM,MAAM;AAAA,gBAC1C,UAAU;AAAA,gBACV,YAAY;AAAA,gBACZ,QAAQ;AAAA,gBACR;AAAA,gBACA,UAAU,MAAM,aAAa,MAAM,MAAM;AAAA,gBACzC;AAAA;AAAA,cAXK,MAAM;AAAA,YAYb;AAAA,UAEJ,CAAC;AAAA,WACH;AAAA,WAtCM,KAAK,MAwCf;AAAA,IAEJ,CAAC,GACH;AAAA,KAEJ;AAEJ;AAEA,SAAS,aAAa,EAAE,MAAM,SAAS,WAAW,UAAU,YAAY,UAAU,YAAY,QAAQ,eAAe,UAAU,sBAAsB,GAKlJ;AACD,QAAM,IAAI,KAAK;AACf,QAAM,cAAc,SAAS,IAAI,GAAG,SAAS,IAAI,CAAC,QAAQ;AAG1D,QAAM,YAAY,mBAAmB,MAAM,SAAS;AAEpD,SACE,qBAAC,SACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,wFAAwF,YAAY,gBAAgB,EAAE;AAAA,QACjI,OAAO,cAAc,EAAE,YAAY,IAAI;AAAA,QACvC,SAAS;AAAA,QACT,eAAY;AAAA,QACZ,gBAAc,YAAY,SAAS;AAAA,QAEnC;AAAA,8BAAC,YAAO,WAAU,yGAChB,8BAAC,SAAI,WAAW,gCAAgC,aAAa,cAAc,EAAE,IAAI,MAAK,QAAO,SAAQ,aAAY,aAAY,KAAI,QAAO,gBACtI,8BAAC,UAAK,eAAc,SAAQ,gBAAe,SAAQ,GAAE,6BAA4B,GACnF,GACF;AAAA,UAEA,oBAAC,SAAI,WAAU,kBACb,+BAAC,SAAI,WAAU,2BACZ;AAAA,qBAAS,KAAK,oBAAC,UAAK,WAAU,iCAAgC,oBAAC;AAAA,YAC/D,YACC,oBAAC,OAAI,SAAQ,SAAQ,WAAU,kBAAiB,oBAAM;AAAA,YAExD,oBAAC,UAAK,WAAW,uBAAuB,YAAY,uCAAuC,EAAE,IAAK,eAAK,cAAa;AAAA,YACnH,KAAK,WAAW,KAAK,qBAAC,UAAK,WAAU,sDAAqD;AAAA;AAAA,cAAE,KAAK;AAAA,eAAS;AAAA,YAC3G;AAAA,cAAC;AAAA;AAAA,gBACC,QAAQ,KAAK;AAAA,gBACb,OAAO,YAAY,EAAE,8BAA8B,QAAQ,IAAI;AAAA,gBAC/D,OAAO,YAAY,EAAE,kCAAkC,wEAAwE,IAAI;AAAA;AAAA,YACrI;AAAA,YACC,YAAY,qBAAC,UAAK,WAAU,iCAAiC;AAAA;AAAA,cAAW;AAAA,cAAW,eAAe,IAAI,MAAM;AAAA,eAAG;AAAA,aAClH,GACF;AAAA,UAEA,qBAAC,SAAI,WAAU,2CACZ;AAAA,yBAAa,KAAK,aAAa,KAAK,UAAU,YAAY,KACzD,qBAAC,SACC;AAAA,kCAAC,UAAK,WAAU,yBAAwB,kBAAI;AAAA,cAC5C,oBAAC,UAAK,WAAU,gDAAgD,cAAI,KAAK,UAAU,WAAW,QAAQ,GAAE;AAAA,eAC1G;AAAA,YAED,KAAK,WAAW,KAAK,qBAAC,SAAI;AAAA,kCAAC,UAAK,WAAU,yBAAwB,kBAAI;AAAA,cAAO,oBAAC,UAAK,WAAU,yBAAyB,cAAI,KAAK,UAAU,QAAQ,GAAE;AAAA,eAAO;AAAA,YAC1J,KAAK,WAAW,KAAK,qBAAC,SAAI;AAAA,kCAAC,UAAK,WAAU,yBAAwB,kBAAI;AAAA,cAAO,oBAAC,UAAK,WAAU,yBAAyB,cAAI,KAAK,UAAU,QAAQ,GAAE;AAAA,eAAO;AAAA,aAC7J;AAAA;AAAA;AAAA,IACF;AAAA,IAEC,eAAe,MAAM;AACpB,YAAM,eAAe,oBAAI,IAAI,CAAC,oBAAoB,0BAA0B,mBAAmB,gBAAgB,cAAc,CAAC;AAC9H,YAAM,uBAAuB,OAAO,QAAQ,KAAK,aAAa,EAAE;AAAA,QAC9D,CAAC,CAAC,GAAG,MAAM,CAAC,aAAa,IAAI,GAAG;AAAA,MAClC;AACA,YAAM,YAAY,qBAAqB,SAAS;AAChD,YAAM,aAAa,KAAK,QAAQ,SAAS;AACzC,YAAM,UAAU,CAAC,EAAE,KAAK,aAAa,KAAK,cAAc,KAAK;AAC7D,YAAM,aAAa,CAAC,CAAC,KAAK;AAC1B,YAAM,qBAAsB,KAAK,eAAe,0BAAiD;AACjG,YAAM,UAAU,CAAC,aAAa,CAAC,cAAc,CAAC,WAAW,CAAC;AAE1D,aACE;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO,cAAc,EAAE,aAAa,QAAQ,WAAW,WAAW,IAAI;AAAA,UAErE,oBACC,qBAAC,SAAI,WAAU,2CACb;AAAA,iCAAC,SAAI;AAAA;AAAA,cAEF,sBAAsB;AAAA,eACzB;AAAA,aACE,KAAK,WAAW,KAAK,KAAK,WAAW,MACrC,qBAAC,SAAI,WAAU,cACZ;AAAA,mBAAK,WAAW,KACf,qBAAC,UAAK;AAAA;AAAA,gBAAI,oBAAC,UAAK,WAAU,yCAAyC,cAAI,KAAK,UAAU,QAAQ,GAAE;AAAA,iBAAO;AAAA,cAExG,KAAK,WAAW,KACf,qBAAC,UAAK;AAAA;AAAA,gBAAI,oBAAC,UAAK,WAAU,yCAAyC,cAAI,KAAK,UAAU,QAAQ,GAAE;AAAA,iBAAO;AAAA,eAE3G;AAAA,aAEJ,IAEA,iCACG;AAAA,yBACC,qBAAC,SACC;AAAA,kCAAC,OAAE,WAAU,kDAAiD,2BAAa;AAAA,cAC3E,oBAAC,SAAI,WAAU,mDACZ,+BAAqB,IAAI,CAAC,CAAC,KAAK,KAAK,MACpC,qBAAC,SAAc,WAAU,WACvB;AAAA,qCAAC,UAAK,WAAU,yBAAyB;AAAA;AAAA,kBAAI;AAAA,mBAAE;AAAA,gBAC/C,oBAAC,UAAK,WAAU,eAAe,iBAAO,KAAK,GAAE;AAAA,mBAFrC,GAGV,CACD,GACH;AAAA,eACF;AAAA,YAGD,cACC;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,KAAK,QAAQ,IAAI,WAAW;AAAA,gBACrC;AAAA,gBACA,iBAAiB,eAAe,YAAY,UAAU,cAAc,YAAY;AAAA,gBAChF,gBAAgB,CAAC,cAAc,sBAAsB,SAAS,SAAS;AAAA;AAAA,YACzE;AAAA,YAGD,WACC,qBAAC,SAAI,WAAU,4CACZ;AAAA,mBAAK,aAAa,qBAAC,UAAK;AAAA;AAAA,gBAAQ,IAAI,KAAK,KAAK,SAAS,EAAE,mBAAmB;AAAA,iBAAE;AAAA,cAC9E,KAAK,cAAc,qBAAC,UAAK;AAAA;AAAA,gBAAO,KAAK;AAAA,gBAAW;AAAA,iBAAE;AAAA,cAClD,KAAK,WAAW,qBAAC,UAAK;AAAA;AAAA,gBAAM,IAAI,KAAK,KAAK,OAAO,EAAE,mBAAmB;AAAA,iBAAE;AAAA,eAC3E;AAAA,YAID,cAAc,oBAAC,eAAY,MAAY,UAAoB;AAAA,aAC9D;AAAA;AAAA,MAEJ;AAAA,IAEJ,GAAG;AAAA,KACL;AAEJ;AAEA,SAAS,UAAU;AACjB,SACE,qBAAC,SAAI,WAAU,wBAAuB,SAAQ,aAAY,MAAK,QAC7D;AAAA,wBAAC,YAAO,WAAU,cAAa,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK,QAAO,gBAAe,aAAY,KAAI;AAAA,IAC5F,oBAAC,UAAK,WAAU,cAAa,MAAK,gBAAe,GAAE,uCAAsC;AAAA,KAC3F;AAEJ;AAEA,SAAS,YAAY,EAAE,SAAS,UAAU,GAAgD;AACxF,SACE,qBAAC,SAAM,SAAQ,eAAc,WAAU,0BACrC;AAAA,wBAAC,UAAK,WAAU,UAAU,mBAAQ;AAAA,IACjC,aACC;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,QACT,WAAU;AAAA,QACV,cAAW;AAAA,QACZ;AAAA;AAAA,IAED;AAAA,KAEJ;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/cpq/backend/cpq/orders/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 { ColumnDef } from '@tanstack/react-table'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { CpqListView, useCpqListData } from '../../../components/CpqListView'\nimport {\n formatStatusLabel,\n orderCpqStatusMap,\n type OrderCpqStatus,\n} from '../../../components/statusMaps'\n\ntype OrderConfig = {\n id: string\n orderId: string\n orderNumber: string | null\n customerId: string\n cpqStatus: string\n currencyCode: string\n pricingSummary: {\n nrcTotal?: number\n mrcTotal?: number\n } | null\n createdAt: string\n activatedAt: string | null\n}\n\nconst PAGE_SIZE = 50\n\nfunction fmt(amount: number | undefined, currency: string): string {\n if (amount == null || amount === 0) return '\u2014'\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)\n}\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.cpqStatus === 'string' && values.cpqStatus) {\n params.set('cpqStatus', values.cpqStatus)\n }\n if (typeof values.currencyCode === 'string' && values.currencyCode.trim()) {\n params.set('currencyCode', values.currencyCode.trim().toUpperCase())\n }\n}\n\nexport default function CpqOrdersListPage() {\n const router = useRouter()\n const t = useT()\n\n const data = useCpqListData<OrderConfig>({\n endpoint: '/api/cpq/orders',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.orders.list.error.load', 'Failed to load orders'),\n })\n\n const statusOptions = React.useMemo(\n () => [\n { value: 'draft', label: 'Draft' },\n { value: 'pending_activation', label: 'Pending Activation' },\n { value: 'active', label: 'Active' },\n { value: 'fulfilled', label: 'Fulfilled' },\n { value: 'cancelled', label: 'Cancelled' },\n ],\n [],\n )\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'cpqStatus',\n label: t('cpq.orders.filters.status', 'Status'),\n type: 'select',\n options: statusOptions,\n },\n {\n id: 'currencyCode',\n label: t('cpq.orders.filters.currency', 'Currency'),\n type: 'text',\n },\n ],\n [statusOptions, t],\n )\n\n const columns = React.useMemo<ColumnDef<OrderConfig>[]>(\n () => [\n {\n id: 'orderNumber',\n header: t('cpq.orders.table.order', 'Order'),\n cell: ({ row }) => (\n <span className=\"text-sm font-medium text-primary\">\n {row.original.orderNumber ?? `${row.original.orderId?.slice(0, 8)}\u2026`}\n </span>\n ),\n },\n {\n accessorKey: 'cpqStatus',\n header: t('cpq.orders.table.status', 'Status'),\n cell: ({ row }) => (\n <Tag variant={orderCpqStatusMap[row.original.cpqStatus as OrderCpqStatus] ?? 'neutral'} dot>\n {formatStatusLabel(row.original.cpqStatus)}\n </Tag>\n ),\n },\n {\n accessorKey: 'currencyCode',\n header: t('cpq.orders.table.currency', 'Currency'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">{row.original.currencyCode}</span>,\n },\n {\n id: 'nrcTotal',\n header: t('cpq.orders.table.nrcTotal', 'NRC Total'),\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">\n {fmt(row.original.pricingSummary?.nrcTotal, row.original.currencyCode)}\n </span>\n ),\n },\n {\n id: 'mrcTotal',\n header: t('cpq.orders.table.mrcTotal', 'MRC Total'),\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">\n {fmt(row.original.pricingSummary?.mrcTotal, row.original.currencyCode)}\n </span>\n ),\n },\n {\n accessorKey: 'createdAt',\n header: t('cpq.orders.table.createdAt', 'Created'),\n cell: ({ row }) => (\n <span className=\"text-xs text-muted-foreground\">\n {new Date(row.original.createdAt).toLocaleDateString()}\n </span>\n ),\n },\n {\n accessorKey: 'activatedAt',\n header: t('cpq.orders.table.activatedAt', 'Activated'),\n cell: ({ row }) => (\n <span className=\"text-xs text-muted-foreground\">\n {row.original.activatedAt ? new Date(row.original.activatedAt).toLocaleDateString() : '\u2014'}\n </span>\n ),\n },\n ],\n [t],\n )\n\n return (\n <CpqListView<OrderConfig>\n title={t('cpq.orders.list.title', 'CPQ Orders')}\n tableId=\"cpq.orders.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.orders.search.placeholder', 'Search by order / customer id...')}\n onRowClick={(row) => router.push(`/backend/cpq/orders/${row.id}`)}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'open',\n label: t('cpq.orders.actions.open', 'Open'),\n href: `/backend/cpq/orders/${row.id}`,\n },\n ]}\n />\n )}\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t(\n 'cpq.orders.empty',\n 'No CPQ orders found. Convert an accepted quote to create an order.',\n )}\n </div>\n }\n />\n )\n}\n"],
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 type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { CpqListView, useCpqListData } from '../../../components/CpqListView'\nimport {\n formatStatusLabel,\n orderCpqStatusMap,\n type OrderCpqStatus,\n} from '../../../components/statusMaps'\n\ntype OrderConfig = {\n id: string\n orderId: string\n orderNumber: string | null\n customerId: string\n cpqStatus: string\n currencyCode: string\n pricingSummary: {\n nrcTotal?: number\n mrcTotal?: number\n } | null\n createdAt: string\n activatedAt: string | null\n}\n\nconst PAGE_SIZE = 50\n\nfunction fmt(amount: number | undefined, currency: string): string {\n if (amount == null || amount === 0) return '\u2014'\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)\n}\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.cpqStatus === 'string' && values.cpqStatus) {\n params.set('cpqStatus', values.cpqStatus)\n }\n if (typeof values.currencyCode === 'string' && values.currencyCode.trim()) {\n params.set('currencyCode', values.currencyCode.trim().toUpperCase())\n }\n}\n\nexport default function CpqOrdersListPage() {\n const router = useRouter()\n const t = useT()\n\n const data = useCpqListData<OrderConfig>({\n endpoint: '/api/cpq/orders',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.orders.list.error.load', 'Failed to load orders'),\n })\n\n const statusOptions = React.useMemo(\n () => [\n { value: 'draft', label: 'Draft' },\n { value: 'pending_activation', label: 'Pending Activation' },\n { value: 'active', label: 'Active' },\n { value: 'fulfilled', label: 'Fulfilled' },\n { value: 'cancelled', label: 'Cancelled' },\n ],\n [],\n )\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'cpqStatus',\n label: t('cpq.orders.filters.status', 'Status'),\n type: 'select',\n options: statusOptions,\n },\n {\n id: 'currencyCode',\n label: t('cpq.orders.filters.currency', 'Currency'),\n type: 'text',\n },\n ],\n [statusOptions, t],\n )\n\n const columns = React.useMemo<ColumnDef<OrderConfig>[]>(\n () => [\n {\n id: 'orderNumber',\n header: t('cpq.orders.table.order', 'Order'),\n cell: ({ row }) => (\n <span className=\"text-sm font-medium text-primary\">\n {row.original.orderNumber ?? `${row.original.orderId?.slice(0, 8)}\u2026`}\n </span>\n ),\n },\n {\n accessorKey: 'cpqStatus',\n header: t('cpq.orders.table.status', 'Status'),\n cell: ({ row }) => (\n <Tag variant={orderCpqStatusMap[row.original.cpqStatus as OrderCpqStatus] ?? 'neutral'} dot>\n {formatStatusLabel(row.original.cpqStatus)}\n </Tag>\n ),\n },\n {\n accessorKey: 'currencyCode',\n header: t('cpq.orders.table.currency', 'Currency'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">{row.original.currencyCode}</span>,\n },\n {\n id: 'nrcTotal',\n header: t('cpq.orders.table.nrcTotal', 'NRC Total'),\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">\n {fmt(row.original.pricingSummary?.nrcTotal, row.original.currencyCode)}\n </span>\n ),\n },\n {\n id: 'mrcTotal',\n header: t('cpq.orders.table.mrcTotal', 'MRC Total'),\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">\n {fmt(row.original.pricingSummary?.mrcTotal, row.original.currencyCode)}\n </span>\n ),\n },\n {\n accessorKey: 'createdAt',\n header: t('cpq.orders.table.createdAt', 'Created'),\n cell: ({ row }) => (\n <span className=\"text-xs text-muted-foreground\">\n {new Date(row.original.createdAt).toLocaleDateString()}\n </span>\n ),\n },\n {\n accessorKey: 'activatedAt',\n header: t('cpq.orders.table.activatedAt', 'Activated'),\n cell: ({ row }) => (\n <span className=\"text-xs text-muted-foreground\">\n {row.original.activatedAt ? new Date(row.original.activatedAt).toLocaleDateString() : '\u2014'}\n </span>\n ),\n },\n ],\n [t],\n )\n\n return (\n <CpqListView<OrderConfig>\n title={t('cpq.orders.list.title', 'CPQ Orders')}\n tableId=\"cpq.orders.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.orders.search.placeholder', 'Search by order / customer id...')}\n onRowClick={(row) => router.push(`/backend/cpq/orders/${row.id}`)}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'open',\n label: t('cpq.orders.actions.open', 'Open'),\n href: `/backend/cpq/orders/${row.id}`,\n },\n ]}\n />\n )}\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t(\n 'cpq.orders.empty',\n 'No CPQ orders found. Convert an accepted quote to create an order.',\n )}\n </div>\n }\n />\n )\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -8,7 +8,7 @@ const metadata = {
8
8
  pageTitleKey: "cpq.orders.title",
9
9
  pageGroup: "CPQ",
10
10
  pageGroupKey: "customers~sales~cpq.nav.group",
11
- pageOrder: 91,
11
+ pageOrder: 2,
12
12
  icon: ordersIcon,
13
13
  breadcrumb: [{ label: "CPQ Orders", labelKey: "cpq.orders.title" }]
14
14
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/cpq/backend/cpq/orders/page.meta.ts"],
4
- "sourcesContent": ["import React from 'react'\nimport { ClipboardList } from 'lucide-react'\n\nconst ordersIcon = React.createElement(ClipboardList, { size: 16 })\n\nexport const metadata = {\n requireAuth: true,\n requireFeatures: ['cpq.orders.view'],\n pageTitle: 'CPQ Orders',\n pageTitleKey: 'cpq.orders.title',\n pageGroup: 'CPQ',\n pageGroupKey: 'customers~sales~cpq.nav.group',\n pageOrder: 91,\n icon: ordersIcon,\n breadcrumb: [{ label: 'CPQ Orders', labelKey: 'cpq.orders.title' }],\n} as const\n"],
4
+ "sourcesContent": ["import React from 'react'\nimport { ClipboardList } from 'lucide-react'\n\nconst ordersIcon = React.createElement(ClipboardList, { size: 16 })\n\nexport const metadata = {\n requireAuth: true,\n requireFeatures: ['cpq.orders.view'],\n pageTitle: 'CPQ Orders',\n pageTitleKey: 'cpq.orders.title',\n pageGroup: 'CPQ',\n pageGroupKey: 'customers~sales~cpq.nav.group',\n pageOrder: 2,\n icon: ordersIcon,\n breadcrumb: [{ label: 'CPQ Orders', labelKey: 'cpq.orders.title' }],\n} as const\n"],
5
5
  "mappings": "AAAA,OAAO,WAAW;AAClB,SAAS,qBAAqB;AAE9B,MAAM,aAAa,MAAM,cAAc,eAAe,EAAE,MAAM,GAAG,CAAC;AAE3D,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,iBAAiB;AAAA,EACnC,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,MAAM;AAAA,EACN,YAAY,CAAC,EAAE,OAAO,cAAc,UAAU,mBAAmB,CAAC;AACpE;",
6
6
  "names": []
7
7
  }
@@ -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 { ColumnDef } from '@tanstack/react-table'\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"],
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
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/cpq/backend/cpq/pricing/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useRouter } from 'next/navigation'\nimport type { ColumnDef } from '@tanstack/react-table'\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'\n\ntype PricingTable = {\n id: string\n code: string\n name: string\n priceColumns: Array<{ key: string; label: string }>\n currencyCodeList: string[]\n isActive: boolean\n}\n\nconst PAGE_SIZE = 50\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (values.isActive === true) params.set('isActive', 'true')\n if (values.isActive === false) params.set('isActive', 'false')\n}\n\nexport default function PricingTablesPage() {\n const t = useT()\n const router = useRouter()\n\n const data = useCpqListData<PricingTable>({\n endpoint: '/api/cpq/pricing-tables',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.pricing.list.error.load', 'Failed to load pricing tables'),\n })\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'isActive',\n label: t('cpq.pricing.filters.isActive', 'Active'),\n type: 'checkbox',\n },\n ],\n [t],\n )\n\n const columns = React.useMemo<ColumnDef<PricingTable>[]>(\n () => [\n {\n accessorKey: 'name',\n header: t('cpq.pricing.name', 'Name'),\n cell: ({ row }) => <span className=\"font-medium\">{row.original.name}</span>,\n },\n {\n accessorKey: 'code',\n header: t('cpq.pricing.code', 'Code'),\n cell: ({ row }) => <span className=\"font-mono text-xs\">{row.original.code}</span>,\n },\n {\n id: 'priceColumns',\n header: t('cpq.pricing.priceColumns', 'Price Columns'),\n cell: ({ row }) => <span>{row.original.priceColumns?.length ?? 0}</span>,\n },\n {\n id: 'currencies',\n header: t('cpq.pricing.currencies', 'Currencies'),\n cell: ({ row }) => <span>{row.original.currencyCodeList?.join(', ') ?? '\u2014'}</span>,\n },\n {\n accessorKey: 'isActive',\n header: t('cpq.pricing.status', 'Status'),\n cell: ({ row }) => (\n <Tag variant={row.original.isActive ? 'success' : 'neutral'} dot>\n {row.original.isActive ? t('common.active', 'Active') : t('common.inactive', 'Inactive')}\n </Tag>\n ),\n },\n ],\n [t],\n )\n\n return (\n <CpqListView<PricingTable>\n title={t('cpq.pricing.list.title', 'Pricing Tables')}\n tableId=\"cpq.pricing.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.pricing.search.placeholder', 'Search pricing tables...')}\n actions={\n <Button asChild>\n <a href=\"/backend/cpq/pricing/new\">{t('cpq.pricing.add', 'New Pricing Table')}</a>\n </Button>\n }\n crud={{\n endpoint: '/api/cpq/pricing-tables',\n entityName: t('cpq.pricing.entityName', 'pricing table'),\n editHref: (row) => `/backend/cpq/pricing/${row.id}`,\n }}\n onRowClick={(row) => router.push(`/backend/cpq/pricing/${row.id}`)}\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t('cpq.pricing.empty', 'No pricing tables found.')}\n </div>\n }\n />\n )\n}\n"],
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useRouter } from 'next/navigation'\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'\n\ntype PricingTable = {\n id: string\n code: string\n name: string\n priceColumns: Array<{ key: string; label: string }>\n currencyCodeList: string[]\n isActive: boolean\n}\n\nconst PAGE_SIZE = 50\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (values.isActive === true) params.set('isActive', 'true')\n if (values.isActive === false) params.set('isActive', 'false')\n}\n\nexport default function PricingTablesPage() {\n const t = useT()\n const router = useRouter()\n\n const data = useCpqListData<PricingTable>({\n endpoint: '/api/cpq/pricing-tables',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.pricing.list.error.load', 'Failed to load pricing tables'),\n })\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'isActive',\n label: t('cpq.pricing.filters.isActive', 'Active'),\n type: 'checkbox',\n },\n ],\n [t],\n )\n\n const columns = React.useMemo<ColumnDef<PricingTable>[]>(\n () => [\n {\n accessorKey: 'name',\n header: t('cpq.pricing.name', 'Name'),\n cell: ({ row }) => <span className=\"font-medium\">{row.original.name}</span>,\n },\n {\n accessorKey: 'code',\n header: t('cpq.pricing.code', 'Code'),\n cell: ({ row }) => <span className=\"font-mono text-xs\">{row.original.code}</span>,\n },\n {\n id: 'priceColumns',\n header: t('cpq.pricing.priceColumns', 'Price Columns'),\n cell: ({ row }) => <span>{row.original.priceColumns?.length ?? 0}</span>,\n },\n {\n id: 'currencies',\n header: t('cpq.pricing.currencies', 'Currencies'),\n cell: ({ row }) => <span>{row.original.currencyCodeList?.join(', ') ?? '\u2014'}</span>,\n },\n {\n accessorKey: 'isActive',\n header: t('cpq.pricing.status', 'Status'),\n cell: ({ row }) => (\n <Tag variant={row.original.isActive ? 'success' : 'neutral'} dot>\n {row.original.isActive ? t('common.active', 'Active') : t('common.inactive', 'Inactive')}\n </Tag>\n ),\n },\n ],\n [t],\n )\n\n return (\n <CpqListView<PricingTable>\n title={t('cpq.pricing.list.title', 'Pricing Tables')}\n tableId=\"cpq.pricing.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.pricing.search.placeholder', 'Search pricing tables...')}\n actions={\n <Button asChild>\n <a href=\"/backend/cpq/pricing/new\">{t('cpq.pricing.add', 'New Pricing Table')}</a>\n </Button>\n }\n crud={{\n endpoint: '/api/cpq/pricing-tables',\n entityName: t('cpq.pricing.entityName', 'pricing table'),\n editHref: (row) => `/backend/cpq/pricing/${row.id}`,\n }}\n onRowClick={(row) => router.push(`/backend/cpq/pricing/${row.id}`)}\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t('cpq.pricing.empty', 'No pricing tables found.')}\n </div>\n }\n />\n )\n}\n"],
5
5
  "mappings": ";AAqD2B;AApD3B,YAAY,WAAW;AACvB,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAE1B,SAAS,cAAc;AACvB,SAAS,WAAW;AAEpB,SAAS,aAAa,sBAAsB;AAW5C,MAAM,YAAY;AAElB,SAAS,kBAAkB,QAAsB,QAAyB;AACxE,MAAI,OAAO,aAAa,KAAM,QAAO,IAAI,YAAY,MAAM;AAC3D,MAAI,OAAO,aAAa,MAAO,QAAO,IAAI,YAAY,OAAO;AAC/D;AAEe,SAAR,oBAAqC;AAC1C,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AAEzB,QAAM,OAAO,eAA6B;AAAA,IACxC,UAAU;AAAA,IACV,UAAU;AAAA,IACV;AAAA,IACA,kBAAkB,EAAE,+BAA+B,+BAA+B;AAAA,EACpF,CAAC;AAED,QAAM,UAAU,MAAM;AAAA,IACpB,MAAM;AAAA,MACJ;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,gCAAgC,QAAQ;AAAA,QACjD,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB,MAAM;AAAA,MACJ;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,oBAAoB,MAAM;AAAA,QACpC,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,UAAK,WAAU,eAAe,cAAI,SAAS,MAAK;AAAA,MACtE;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,oBAAoB,MAAM;AAAA,QACpC,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,UAAK,WAAU,qBAAqB,cAAI,SAAS,MAAK;AAAA,MAC5E;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,4BAA4B,eAAe;AAAA,QACrD,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,UAAM,cAAI,SAAS,cAAc,UAAU,GAAE;AAAA,MACnE;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,0BAA0B,YAAY;AAAA,QAChD,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,UAAM,cAAI,SAAS,kBAAkB,KAAK,IAAI,KAAK,UAAI;AAAA,MAC7E;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,sBAAsB,QAAQ;AAAA,QACxC,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,OAAI,SAAS,IAAI,SAAS,WAAW,YAAY,WAAW,KAAG,MAC7D,cAAI,SAAS,WAAW,EAAE,iBAAiB,QAAQ,IAAI,EAAE,mBAAmB,UAAU,GACzF;AAAA,MAEJ;AAAA,IACF;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,0BAA0B,gBAAgB;AAAA,MACnD,SAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU;AAAA,MACV,mBAAmB,EAAE,kCAAkC,0BAA0B;AAAA,MACjF,SACE,oBAAC,UAAO,SAAO,MACb,8BAAC,OAAE,MAAK,4BAA4B,YAAE,mBAAmB,mBAAmB,GAAE,GAChF;AAAA,MAEF,MAAM;AAAA,QACJ,UAAU;AAAA,QACV,YAAY,EAAE,0BAA0B,eAAe;AAAA,QACvD,UAAU,CAAC,QAAQ,wBAAwB,IAAI,EAAE;AAAA,MACnD;AAAA,MACA,YAAY,CAAC,QAAQ,OAAO,KAAK,wBAAwB,IAAI,EAAE,EAAE;AAAA,MACjE,YACE,oBAAC,SAAI,WAAU,2EACZ,YAAE,qBAAqB,0BAA0B,GACpD;AAAA;AAAA,EAEJ;AAEJ;",
6
6
  "names": []
7
7
  }