@dainamite/cpq 0.2.2

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 (504) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/LICENSE +21 -0
  3. package/MIGRATION.md +236 -0
  4. package/README.md +108 -0
  5. package/dist/modules/cpq/acl.js +30 -0
  6. package/dist/modules/cpq/acl.js.map +7 -0
  7. package/dist/modules/cpq/api/_helpers/handleArcError.js +37 -0
  8. package/dist/modules/cpq/api/_helpers/handleArcError.js.map +7 -0
  9. package/dist/modules/cpq/api/attribute-options/route.js +47 -0
  10. package/dist/modules/cpq/api/attribute-options/route.js.map +7 -0
  11. package/dist/modules/cpq/api/bundle-slots/route.js +101 -0
  12. package/dist/modules/cpq/api/bundle-slots/route.js.map +7 -0
  13. package/dist/modules/cpq/api/bundle-tree/route.js +28 -0
  14. package/dist/modules/cpq/api/bundle-tree/route.js.map +7 -0
  15. package/dist/modules/cpq/api/context.js +29 -0
  16. package/dist/modules/cpq/api/context.js.map +7 -0
  17. package/dist/modules/cpq/api/inventory/assets/route.js +113 -0
  18. package/dist/modules/cpq/api/inventory/assets/route.js.map +7 -0
  19. package/dist/modules/cpq/api/inventory/assets/status/route.js +35 -0
  20. package/dist/modules/cpq/api/inventory/assets/status/route.js.map +7 -0
  21. package/dist/modules/cpq/api/inventory/customer/[customerId]/parent-items/route.js +30 -0
  22. package/dist/modules/cpq/api/inventory/customer/[customerId]/parent-items/route.js.map +7 -0
  23. package/dist/modules/cpq/api/inventory/customer/[customerId]/route.js +31 -0
  24. package/dist/modules/cpq/api/inventory/customer/[customerId]/route.js.map +7 -0
  25. package/dist/modules/cpq/api/inventory/subscription-items/route.js +99 -0
  26. package/dist/modules/cpq/api/inventory/subscription-items/route.js.map +7 -0
  27. package/dist/modules/cpq/api/inventory/subscriptions/[subscriptionId]/change-log/route.js +57 -0
  28. package/dist/modules/cpq/api/inventory/subscriptions/[subscriptionId]/change-log/route.js.map +7 -0
  29. package/dist/modules/cpq/api/inventory/subscriptions/expiring/route.js +62 -0
  30. package/dist/modules/cpq/api/inventory/subscriptions/expiring/route.js.map +7 -0
  31. package/dist/modules/cpq/api/inventory/subscriptions/route.js +116 -0
  32. package/dist/modules/cpq/api/inventory/subscriptions/route.js.map +7 -0
  33. package/dist/modules/cpq/api/inventory/subscriptions/status/route.js +35 -0
  34. package/dist/modules/cpq/api/inventory/subscriptions/status/route.js.map +7 -0
  35. package/dist/modules/cpq/api/offering-components/route.js +91 -0
  36. package/dist/modules/cpq/api/offering-components/route.js.map +7 -0
  37. package/dist/modules/cpq/api/orders/[orderId]/activate/route.js +39 -0
  38. package/dist/modules/cpq/api/orders/[orderId]/activate/route.js.map +7 -0
  39. package/dist/modules/cpq/api/orders/[orderId]/route.js +28 -0
  40. package/dist/modules/cpq/api/orders/[orderId]/route.js.map +7 -0
  41. package/dist/modules/cpq/api/orders/[orderId]/status/route.js +38 -0
  42. package/dist/modules/cpq/api/orders/[orderId]/status/route.js.map +7 -0
  43. package/dist/modules/cpq/api/orders/route.js +71 -0
  44. package/dist/modules/cpq/api/orders/route.js.map +7 -0
  45. package/dist/modules/cpq/api/price-rules/route.js +180 -0
  46. package/dist/modules/cpq/api/price-rules/route.js.map +7 -0
  47. package/dist/modules/cpq/api/pricing-table-entries/route.js +142 -0
  48. package/dist/modules/cpq/api/pricing-table-entries/route.js.map +7 -0
  49. package/dist/modules/cpq/api/pricing-tables/route.js +179 -0
  50. package/dist/modules/cpq/api/pricing-tables/route.js.map +7 -0
  51. package/dist/modules/cpq/api/product-attributes/route.js +160 -0
  52. package/dist/modules/cpq/api/product-attributes/route.js.map +7 -0
  53. package/dist/modules/cpq/api/product-charges/route.js +166 -0
  54. package/dist/modules/cpq/api/product-charges/route.js.map +7 -0
  55. package/dist/modules/cpq/api/product-offerings/route.js +294 -0
  56. package/dist/modules/cpq/api/product-offerings/route.js.map +7 -0
  57. package/dist/modules/cpq/api/product-relationships/route.js +144 -0
  58. package/dist/modules/cpq/api/product-relationships/route.js.map +7 -0
  59. package/dist/modules/cpq/api/product-specifications/route.js +292 -0
  60. package/dist/modules/cpq/api/product-specifications/route.js.map +7 -0
  61. package/dist/modules/cpq/api/quotes/[quoteId]/cancel-meta/route.js +71 -0
  62. package/dist/modules/cpq/api/quotes/[quoteId]/cancel-meta/route.js.map +7 -0
  63. package/dist/modules/cpq/api/quotes/[quoteId]/clone/route.js +29 -0
  64. package/dist/modules/cpq/api/quotes/[quoteId]/clone/route.js.map +7 -0
  65. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js +54 -0
  66. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js.map +7 -0
  67. package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js +27 -0
  68. package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js.map +7 -0
  69. package/dist/modules/cpq/api/quotes/[quoteId]/merge-meta/route.js +72 -0
  70. package/dist/modules/cpq/api/quotes/[quoteId]/merge-meta/route.js.map +7 -0
  71. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js +35 -0
  72. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js.map +7 -0
  73. package/dist/modules/cpq/api/quotes/[quoteId]/route.js +102 -0
  74. package/dist/modules/cpq/api/quotes/[quoteId]/route.js.map +7 -0
  75. package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js +27 -0
  76. package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js.map +7 -0
  77. package/dist/modules/cpq/api/quotes/[quoteId]/target-subscriptions/[targetId]/route.js +79 -0
  78. package/dist/modules/cpq/api/quotes/[quoteId]/target-subscriptions/[targetId]/route.js.map +7 -0
  79. package/dist/modules/cpq/api/quotes/[quoteId]/target-subscriptions/route.js +95 -0
  80. package/dist/modules/cpq/api/quotes/[quoteId]/target-subscriptions/route.js.map +7 -0
  81. package/dist/modules/cpq/api/quotes/attributes/route.js +52 -0
  82. package/dist/modules/cpq/api/quotes/attributes/route.js.map +7 -0
  83. package/dist/modules/cpq/api/quotes/from-subscription/route.js +72 -0
  84. package/dist/modules/cpq/api/quotes/from-subscription/route.js.map +7 -0
  85. package/dist/modules/cpq/api/quotes/price/route.js +69 -0
  86. package/dist/modules/cpq/api/quotes/price/route.js.map +7 -0
  87. package/dist/modules/cpq/api/quotes/route.js +97 -0
  88. package/dist/modules/cpq/api/quotes/route.js.map +7 -0
  89. package/dist/modules/cpq/api/quotes/validate-relationships/route.js +86 -0
  90. package/dist/modules/cpq/api/quotes/validate-relationships/route.js.map +7 -0
  91. package/dist/modules/cpq/api/resolveQuotingService.js +7 -0
  92. package/dist/modules/cpq/api/resolveQuotingService.js.map +7 -0
  93. package/dist/modules/cpq/api/wizards/resolve/route.js +38 -0
  94. package/dist/modules/cpq/api/wizards/resolve/route.js.map +7 -0
  95. package/dist/modules/cpq/api/wizards/route.js +111 -0
  96. package/dist/modules/cpq/api/wizards/route.js.map +7 -0
  97. package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js +136 -0
  98. package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +7 -0
  99. package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js +185 -0
  100. package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +7 -0
  101. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js +343 -0
  102. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js.map +7 -0
  103. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js +13 -0
  104. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js.map +7 -0
  105. package/dist/modules/cpq/backend/cpq/inventory/page.js +35 -0
  106. package/dist/modules/cpq/backend/cpq/inventory/page.js.map +7 -0
  107. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +18 -0
  108. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +7 -0
  109. package/dist/modules/cpq/backend/cpq/inventory/shared.js +20 -0
  110. package/dist/modules/cpq/backend/cpq/inventory/shared.js.map +7 -0
  111. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +794 -0
  112. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +7 -0
  113. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js +13 -0
  114. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js.map +7 -0
  115. package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js +917 -0
  116. package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js.map +7 -0
  117. package/dist/modules/cpq/backend/cpq/offerings/page.js +405 -0
  118. package/dist/modules/cpq/backend/cpq/offerings/page.js.map +7 -0
  119. package/dist/modules/cpq/backend/cpq/offerings/page.meta.js +18 -0
  120. package/dist/modules/cpq/backend/cpq/offerings/page.meta.js.map +7 -0
  121. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +677 -0
  122. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +7 -0
  123. package/dist/modules/cpq/backend/cpq/orders/page.js +134 -0
  124. package/dist/modules/cpq/backend/cpq/orders/page.js.map +7 -0
  125. package/dist/modules/cpq/backend/cpq/orders/page.meta.js +18 -0
  126. package/dist/modules/cpq/backend/cpq/orders/page.meta.js.map +7 -0
  127. package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js +272 -0
  128. package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js.map +7 -0
  129. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js +407 -0
  130. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js.map +7 -0
  131. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js +13 -0
  132. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js.map +7 -0
  133. package/dist/modules/cpq/backend/cpq/price-rules/page.js +211 -0
  134. package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +7 -0
  135. package/dist/modules/cpq/backend/cpq/price-rules/page.meta.js +18 -0
  136. package/dist/modules/cpq/backend/cpq/price-rules/page.meta.js.map +7 -0
  137. package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js +275 -0
  138. package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js.map +7 -0
  139. package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js +112 -0
  140. package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js.map +7 -0
  141. package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js +492 -0
  142. package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js.map +7 -0
  143. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxExport.js +50 -0
  144. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxExport.js.map +7 -0
  145. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js +249 -0
  146. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js.map +7 -0
  147. package/dist/modules/cpq/backend/cpq/pricing/page.js +87 -0
  148. package/dist/modules/cpq/backend/cpq/pricing/page.js.map +7 -0
  149. package/dist/modules/cpq/backend/cpq/pricing/page.meta.js +18 -0
  150. package/dist/modules/cpq/backend/cpq/pricing/page.meta.js.map +7 -0
  151. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js +652 -0
  152. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js.map +7 -0
  153. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +1289 -0
  154. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +7 -0
  155. package/dist/modules/cpq/backend/cpq/quotes/page.js +283 -0
  156. package/dist/modules/cpq/backend/cpq/quotes/page.js.map +7 -0
  157. package/dist/modules/cpq/backend/cpq/quotes/page.meta.js +18 -0
  158. package/dist/modules/cpq/backend/cpq/quotes/page.meta.js.map +7 -0
  159. package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js +214 -0
  160. package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js.map +7 -0
  161. package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js +1399 -0
  162. package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js.map +7 -0
  163. package/dist/modules/cpq/backend/cpq/specifications/page.js +148 -0
  164. package/dist/modules/cpq/backend/cpq/specifications/page.js.map +7 -0
  165. package/dist/modules/cpq/backend/cpq/specifications/page.meta.js +18 -0
  166. package/dist/modules/cpq/backend/cpq/specifications/page.meta.js.map +7 -0
  167. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js +204 -0
  168. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js.map +7 -0
  169. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js +191 -0
  170. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js.map +7 -0
  171. package/dist/modules/cpq/backend/cpq/wizards/page.js +119 -0
  172. package/dist/modules/cpq/backend/cpq/wizards/page.js.map +7 -0
  173. package/dist/modules/cpq/backend/cpq/wizards/page.meta.js +18 -0
  174. package/dist/modules/cpq/backend/cpq/wizards/page.meta.js.map +7 -0
  175. package/dist/modules/cpq/ce.js +151 -0
  176. package/dist/modules/cpq/ce.js.map +7 -0
  177. package/dist/modules/cpq/cli.js +11 -0
  178. package/dist/modules/cpq/cli.js.map +7 -0
  179. package/dist/modules/cpq/components/CpqListView.js +265 -0
  180. package/dist/modules/cpq/components/CpqListView.js.map +7 -0
  181. package/dist/modules/cpq/components/NumberInput.js +95 -0
  182. package/dist/modules/cpq/components/NumberInput.js.map +7 -0
  183. package/dist/modules/cpq/components/StatusPath.js +60 -0
  184. package/dist/modules/cpq/components/StatusPath.js.map +7 -0
  185. package/dist/modules/cpq/components/statusMaps.js +66 -0
  186. package/dist/modules/cpq/components/statusMaps.js.map +7 -0
  187. package/dist/modules/cpq/components/useCpqRowActions.js +125 -0
  188. package/dist/modules/cpq/components/useCpqRowActions.js.map +7 -0
  189. package/dist/modules/cpq/data/entities.js +1556 -0
  190. package/dist/modules/cpq/data/entities.js.map +7 -0
  191. package/dist/modules/cpq/data/validators.js +575 -0
  192. package/dist/modules/cpq/data/validators.js.map +7 -0
  193. package/dist/modules/cpq/di.js +57 -0
  194. package/dist/modules/cpq/di.js.map +7 -0
  195. package/dist/modules/cpq/events.js +50 -0
  196. package/dist/modules/cpq/events.js.map +7 -0
  197. package/dist/modules/cpq/index.js +16 -0
  198. package/dist/modules/cpq/index.js.map +7 -0
  199. package/dist/modules/cpq/lib/seeds/api.js +32 -0
  200. package/dist/modules/cpq/lib/seeds/api.js.map +7 -0
  201. package/dist/modules/cpq/lib/seeds/tenant-provisioning.js +158 -0
  202. package/dist/modules/cpq/lib/seeds/tenant-provisioning.js.map +7 -0
  203. package/dist/modules/cpq/lib/seeds.js +6 -0
  204. package/dist/modules/cpq/lib/seeds.js.map +7 -0
  205. package/dist/modules/cpq/migrations/Migration20260220220000_cpq.js +40 -0
  206. package/dist/modules/cpq/migrations/Migration20260220220000_cpq.js.map +7 -0
  207. package/dist/modules/cpq/migrations/Migration20260330000000_cpq_product_domain.js +54 -0
  208. package/dist/modules/cpq/migrations/Migration20260330000000_cpq_product_domain.js.map +7 -0
  209. package/dist/modules/cpq/migrations/Migration20260331000000_cpq_charge_unique_per_offering.js +15 -0
  210. package/dist/modules/cpq/migrations/Migration20260331000000_cpq_charge_unique_per_offering.js.map +7 -0
  211. package/dist/modules/cpq/migrations/Migration20260401000000_cpq_quoting_v2.js +62 -0
  212. package/dist/modules/cpq/migrations/Migration20260401000000_cpq_quoting_v2.js.map +7 -0
  213. package/dist/modules/cpq/migrations/Migration20260402000000_cpq_workflows.js +56 -0
  214. package/dist/modules/cpq/migrations/Migration20260402000000_cpq_workflows.js.map +7 -0
  215. package/dist/modules/cpq/migrations/Migration20260407000000_cpq_pricing_tables_v2.js +26 -0
  216. package/dist/modules/cpq/migrations/Migration20260407000000_cpq_pricing_tables_v2.js.map +7 -0
  217. package/dist/modules/cpq/migrations/Migration20260407100000_cpq_price_rules.js +37 -0
  218. package/dist/modules/cpq/migrations/Migration20260407100000_cpq_price_rules.js.map +7 -0
  219. package/dist/modules/cpq/migrations/Migration20260408000000_cpq_price_rules.js +35 -0
  220. package/dist/modules/cpq/migrations/Migration20260408000000_cpq_price_rules.js.map +7 -0
  221. package/dist/modules/cpq/migrations/Migration20260410000000_cpq_drop_datacentres_inventory.js +21 -0
  222. package/dist/modules/cpq/migrations/Migration20260410000000_cpq_drop_datacentres_inventory.js.map +7 -0
  223. package/dist/modules/cpq/migrations/Migration20260410000000_cpq_wizard_rename.js +46 -0
  224. package/dist/modules/cpq/migrations/Migration20260410000000_cpq_wizard_rename.js.map +7 -0
  225. package/dist/modules/cpq/migrations/Migration20260410100000_cpq_inventory.js +118 -0
  226. package/dist/modules/cpq/migrations/Migration20260410100000_cpq_inventory.js.map +7 -0
  227. package/dist/modules/cpq/migrations/Migration20260411000000_cpq_orders.js +63 -0
  228. package/dist/modules/cpq/migrations/Migration20260411000000_cpq_orders.js.map +7 -0
  229. package/dist/modules/cpq/migrations/Migration20260412000000_cpq_bundled_offerings.js +28 -0
  230. package/dist/modules/cpq/migrations/Migration20260412000000_cpq_bundled_offerings.js.map +7 -0
  231. package/dist/modules/cpq/migrations/Migration20260504064621_cpq_arc.js +36 -0
  232. package/dist/modules/cpq/migrations/Migration20260504064621_cpq_arc.js.map +7 -0
  233. package/dist/modules/cpq/migrations/Migration20260514000000_cpq_spec_merge_retired_deprecated.js +14 -0
  234. package/dist/modules/cpq/migrations/Migration20260514000000_cpq_spec_merge_retired_deprecated.js.map +7 -0
  235. package/dist/modules/cpq/migrations/Migration20260514100000_cpq_offering_merge_retired_deprecated.js +14 -0
  236. package/dist/modules/cpq/migrations/Migration20260514100000_cpq_offering_merge_retired_deprecated.js.map +7 -0
  237. package/dist/modules/cpq/migrations/Migration20260514110000_cpq_list_search_indexes.js +58 -0
  238. package/dist/modules/cpq/migrations/Migration20260514110000_cpq_list_search_indexes.js.map +7 -0
  239. package/dist/modules/cpq/migrations/Migration20260515000000_cpq_inventory_subscriptions_search_indexes.js +29 -0
  240. package/dist/modules/cpq/migrations/Migration20260515000000_cpq_inventory_subscriptions_search_indexes.js.map +7 -0
  241. package/dist/modules/cpq/services/cpqBundleService.js +447 -0
  242. package/dist/modules/cpq/services/cpqBundleService.js.map +7 -0
  243. package/dist/modules/cpq/services/cpqInventoryService.js +1371 -0
  244. package/dist/modules/cpq/services/cpqInventoryService.js.map +7 -0
  245. package/dist/modules/cpq/services/cpqOrderService.js +901 -0
  246. package/dist/modules/cpq/services/cpqOrderService.js.map +7 -0
  247. package/dist/modules/cpq/services/cpqPricingService.js +289 -0
  248. package/dist/modules/cpq/services/cpqPricingService.js.map +7 -0
  249. package/dist/modules/cpq/services/cpqProductService.js +204 -0
  250. package/dist/modules/cpq/services/cpqProductService.js.map +7 -0
  251. package/dist/modules/cpq/services/cpqQuotingService.js +1867 -0
  252. package/dist/modules/cpq/services/cpqQuotingService.js.map +7 -0
  253. package/dist/modules/cpq/services/cpqValidationService.js +234 -0
  254. package/dist/modules/cpq/services/cpqValidationService.js.map +7 -0
  255. package/dist/modules/cpq/services/cpqWizardService.js +137 -0
  256. package/dist/modules/cpq/services/cpqWizardService.js.map +7 -0
  257. package/dist/modules/cpq/services/types.js +114 -0
  258. package/dist/modules/cpq/services/types.js.map +7 -0
  259. package/dist/modules/cpq/setup.js +49 -0
  260. package/dist/modules/cpq/setup.js.map +7 -0
  261. package/dist/modules/cpq/subscribers/subscription-amended-log.js +18 -0
  262. package/dist/modules/cpq/subscribers/subscription-amended-log.js.map +7 -0
  263. package/dist/modules/cpq/subscribers/subscription-cancelled-log.js +19 -0
  264. package/dist/modules/cpq/subscribers/subscription-cancelled-log.js.map +7 -0
  265. package/dist/modules/cpq/subscribers/subscription-merged-log.js +20 -0
  266. package/dist/modules/cpq/subscribers/subscription-merged-log.js.map +7 -0
  267. package/dist/modules/cpq/subscribers/subscription-renewed-log.js +18 -0
  268. package/dist/modules/cpq/subscribers/subscription-renewed-log.js.map +7 -0
  269. package/dist/modules/cpq/subscribers/subscription-superseded-log.js +18 -0
  270. package/dist/modules/cpq/subscribers/subscription-superseded-log.js.map +7 -0
  271. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js +192 -0
  272. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js.map +7 -0
  273. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.js +17 -0
  274. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.js.map +7 -0
  275. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js +79 -0
  276. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js.map +7 -0
  277. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.js +17 -0
  278. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.js.map +7 -0
  279. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js +181 -0
  280. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js.map +7 -0
  281. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.js +17 -0
  282. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.js.map +7 -0
  283. package/dist/modules/cpq/widgets/injection-table.js +44 -0
  284. package/dist/modules/cpq/widgets/injection-table.js.map +7 -0
  285. package/dist/modules/cpq/workflows/WizardRunner.js +113 -0
  286. package/dist/modules/cpq/workflows/WizardRunner.js.map +7 -0
  287. package/dist/modules/cpq/workflows/WizardTrigger.js +116 -0
  288. package/dist/modules/cpq/workflows/WizardTrigger.js.map +7 -0
  289. package/dist/modules/cpq/workflows/engine.js +188 -0
  290. package/dist/modules/cpq/workflows/engine.js.map +7 -0
  291. package/dist/modules/cpq/workflows/registry.js +16 -0
  292. package/dist/modules/cpq/workflows/registry.js.map +7 -0
  293. package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js +119 -0
  294. package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js.map +7 -0
  295. package/dist/modules/cpq/workflows/steps/InventorySelectStep.js +24 -0
  296. package/dist/modules/cpq/workflows/steps/InventorySelectStep.js.map +7 -0
  297. package/dist/modules/cpq/workflows/steps/ItemListStep.js +115 -0
  298. package/dist/modules/cpq/workflows/steps/ItemListStep.js.map +7 -0
  299. package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js +327 -0
  300. package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js.map +7 -0
  301. package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js +231 -0
  302. package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js.map +7 -0
  303. package/dist/modules/cpq/workflows/steps/ReviewStep.js +152 -0
  304. package/dist/modules/cpq/workflows/steps/ReviewStep.js.map +7 -0
  305. package/dist/modules/cpq/workflows/steps/index.js +49 -0
  306. package/dist/modules/cpq/workflows/steps/index.js.map +7 -0
  307. package/dist/modules/cpq/workflows/types.js +1 -0
  308. package/dist/modules/cpq/workflows/types.js.map +7 -0
  309. package/package.json +150 -0
  310. package/src/modules/cpq/__integration__/TC-CPQ-001.spec.ts +177 -0
  311. package/src/modules/cpq/__integration__/TC-CPQ-002.spec.ts +123 -0
  312. package/src/modules/cpq/__integration__/TC-CPQ-003.spec.ts +115 -0
  313. package/src/modules/cpq/__integration__/TC-CPQ-004.spec.ts +184 -0
  314. package/src/modules/cpq/__integration__/TC-CPQ-005.spec.ts +43 -0
  315. package/src/modules/cpq/__integration__/TC-CPQ-006.spec.ts +87 -0
  316. package/src/modules/cpq/__integration__/TC-CPQ-007.spec.ts +79 -0
  317. package/src/modules/cpq/__integration__/TC-CPQ-008.spec.ts +55 -0
  318. package/src/modules/cpq/__integration__/TC-CPQ-009.spec.ts +45 -0
  319. package/src/modules/cpq/__integration__/TC-CPQ-010.spec.ts +54 -0
  320. package/src/modules/cpq/__integration__/TC-CPQ-011.spec.ts +24 -0
  321. package/src/modules/cpq/__integration__/TC-CPQ-012.spec.ts +62 -0
  322. package/src/modules/cpq/__integration__/TC-CPQ-ARC-001.spec.ts +52 -0
  323. package/src/modules/cpq/__integration__/TC-CPQ-ARC-002.spec.ts +65 -0
  324. package/src/modules/cpq/__integration__/TC-CPQ-ARC-003.spec.ts +107 -0
  325. package/src/modules/cpq/__integration__/TC-CPQ-ARC-004.spec.ts +74 -0
  326. package/src/modules/cpq/__integration__/TC-CPQ-ARC-005.spec.ts +86 -0
  327. package/src/modules/cpq/__integration__/TC-CPQ-ARC-006.spec.ts +79 -0
  328. package/src/modules/cpq/__integration__/TC-CPQ-ARC-007.spec.ts +63 -0
  329. package/src/modules/cpq/__integration__/TC-CPQ-ARC-008.spec.ts +62 -0
  330. package/src/modules/cpq/__integration__/TC-CPQ-ARC-009.spec.ts +62 -0
  331. package/src/modules/cpq/__integration__/TC-CPQ-ARC-010.spec.ts +59 -0
  332. package/src/modules/cpq/__integration__/TC-CPQ-ARC-011.spec.ts +128 -0
  333. package/src/modules/cpq/__integration__/_helpers/cpqArcFixtures.ts +208 -0
  334. package/src/modules/cpq/__tests__/package-isolation.test.ts +232 -0
  335. package/src/modules/cpq/acl.ts +26 -0
  336. package/src/modules/cpq/api/_helpers/__tests__/handleArcError.test.ts +117 -0
  337. package/src/modules/cpq/api/_helpers/handleArcError.ts +81 -0
  338. package/src/modules/cpq/api/attribute-options/route.ts +55 -0
  339. package/src/modules/cpq/api/bundle-slots/route.ts +112 -0
  340. package/src/modules/cpq/api/bundle-tree/route.ts +31 -0
  341. package/src/modules/cpq/api/context.ts +39 -0
  342. package/src/modules/cpq/api/inventory/assets/route.ts +123 -0
  343. package/src/modules/cpq/api/inventory/assets/status/route.ts +34 -0
  344. package/src/modules/cpq/api/inventory/customer/[customerId]/parent-items/route.ts +30 -0
  345. package/src/modules/cpq/api/inventory/customer/[customerId]/route.ts +31 -0
  346. package/src/modules/cpq/api/inventory/subscription-items/route.ts +107 -0
  347. package/src/modules/cpq/api/inventory/subscriptions/[subscriptionId]/change-log/route.ts +64 -0
  348. package/src/modules/cpq/api/inventory/subscriptions/expiring/route.ts +66 -0
  349. package/src/modules/cpq/api/inventory/subscriptions/route.ts +126 -0
  350. package/src/modules/cpq/api/inventory/subscriptions/status/route.ts +34 -0
  351. package/src/modules/cpq/api/offering-components/route.ts +103 -0
  352. package/src/modules/cpq/api/orders/[orderId]/activate/route.ts +48 -0
  353. package/src/modules/cpq/api/orders/[orderId]/route.ts +28 -0
  354. package/src/modules/cpq/api/orders/[orderId]/status/route.ts +38 -0
  355. package/src/modules/cpq/api/orders/route.ts +77 -0
  356. package/src/modules/cpq/api/price-rules/route.ts +210 -0
  357. package/src/modules/cpq/api/pricing-table-entries/route.ts +159 -0
  358. package/src/modules/cpq/api/pricing-tables/route.ts +202 -0
  359. package/src/modules/cpq/api/product-attributes/route.ts +175 -0
  360. package/src/modules/cpq/api/product-charges/route.ts +181 -0
  361. package/src/modules/cpq/api/product-offerings/route.ts +329 -0
  362. package/src/modules/cpq/api/product-relationships/route.ts +156 -0
  363. package/src/modules/cpq/api/product-specifications/route.ts +325 -0
  364. package/src/modules/cpq/api/quotes/[quoteId]/cancel-meta/route.ts +77 -0
  365. package/src/modules/cpq/api/quotes/[quoteId]/clone/route.ts +28 -0
  366. package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.ts +55 -0
  367. package/src/modules/cpq/api/quotes/[quoteId]/items/route.ts +26 -0
  368. package/src/modules/cpq/api/quotes/[quoteId]/merge-meta/route.ts +78 -0
  369. package/src/modules/cpq/api/quotes/[quoteId]/recalculate/route.ts +34 -0
  370. package/src/modules/cpq/api/quotes/[quoteId]/route.ts +109 -0
  371. package/src/modules/cpq/api/quotes/[quoteId]/status/route.ts +26 -0
  372. package/src/modules/cpq/api/quotes/[quoteId]/target-subscriptions/[targetId]/route.ts +91 -0
  373. package/src/modules/cpq/api/quotes/[quoteId]/target-subscriptions/route.ts +107 -0
  374. package/src/modules/cpq/api/quotes/attributes/route.ts +61 -0
  375. package/src/modules/cpq/api/quotes/from-subscription/route.ts +85 -0
  376. package/src/modules/cpq/api/quotes/price/route.ts +81 -0
  377. package/src/modules/cpq/api/quotes/route.ts +114 -0
  378. package/src/modules/cpq/api/quotes/validate-relationships/route.ts +100 -0
  379. package/src/modules/cpq/api/resolveQuotingService.ts +11 -0
  380. package/src/modules/cpq/api/wizards/resolve/route.ts +39 -0
  381. package/src/modules/cpq/api/wizards/route.ts +127 -0
  382. package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +166 -0
  383. package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +251 -0
  384. package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.ts +9 -0
  385. package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.tsx +440 -0
  386. package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +16 -0
  387. package/src/modules/cpq/backend/cpq/inventory/page.tsx +49 -0
  388. package/src/modules/cpq/backend/cpq/inventory/shared.tsx +32 -0
  389. package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.ts +9 -0
  390. package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +1085 -0
  391. package/src/modules/cpq/backend/cpq/offerings/[id]/page.tsx +1165 -0
  392. package/src/modules/cpq/backend/cpq/offerings/page.meta.ts +16 -0
  393. package/src/modules/cpq/backend/cpq/offerings/page.tsx +468 -0
  394. package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +921 -0
  395. package/src/modules/cpq/backend/cpq/orders/page.meta.ts +16 -0
  396. package/src/modules/cpq/backend/cpq/orders/page.tsx +182 -0
  397. package/src/modules/cpq/backend/cpq/price-rules/PriceRuleForm.tsx +310 -0
  398. package/src/modules/cpq/backend/cpq/price-rules/[id]/page.meta.ts +9 -0
  399. package/src/modules/cpq/backend/cpq/price-rules/[id]/page.tsx +472 -0
  400. package/src/modules/cpq/backend/cpq/price-rules/page.meta.ts +16 -0
  401. package/src/modules/cpq/backend/cpq/price-rules/page.tsx +253 -0
  402. package/src/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.tsx +431 -0
  403. package/src/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.tsx +122 -0
  404. package/src/modules/cpq/backend/cpq/pricing/[id]/page.tsx +588 -0
  405. package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxExport.ts +90 -0
  406. package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.ts +338 -0
  407. package/src/modules/cpq/backend/cpq/pricing/page.meta.ts +16 -0
  408. package/src/modules/cpq/backend/cpq/pricing/page.tsx +111 -0
  409. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.tsx +737 -0
  410. package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +1721 -0
  411. package/src/modules/cpq/backend/cpq/quotes/page.meta.ts +16 -0
  412. package/src/modules/cpq/backend/cpq/quotes/page.tsx +370 -0
  413. package/src/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.tsx +300 -0
  414. package/src/modules/cpq/backend/cpq/specifications/[id]/page.tsx +1722 -0
  415. package/src/modules/cpq/backend/cpq/specifications/page.meta.ts +16 -0
  416. package/src/modules/cpq/backend/cpq/specifications/page.tsx +191 -0
  417. package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.tsx +319 -0
  418. package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/page.tsx +234 -0
  419. package/src/modules/cpq/backend/cpq/wizards/page.meta.ts +16 -0
  420. package/src/modules/cpq/backend/cpq/wizards/page.tsx +141 -0
  421. package/src/modules/cpq/ce.ts +150 -0
  422. package/src/modules/cpq/cli.ts +10 -0
  423. package/src/modules/cpq/components/CpqListView.tsx +431 -0
  424. package/src/modules/cpq/components/NumberInput.tsx +166 -0
  425. package/src/modules/cpq/components/StatusPath.tsx +116 -0
  426. package/src/modules/cpq/components/statusMaps.ts +148 -0
  427. package/src/modules/cpq/components/useCpqRowActions.tsx +183 -0
  428. package/src/modules/cpq/data/__tests__/validators.arc.test.ts +271 -0
  429. package/src/modules/cpq/data/__tests__/validators.test.ts +782 -0
  430. package/src/modules/cpq/data/entities.ts +1467 -0
  431. package/src/modules/cpq/data/validators.ts +663 -0
  432. package/src/modules/cpq/di.ts +70 -0
  433. package/src/modules/cpq/events.ts +62 -0
  434. package/src/modules/cpq/index.ts +15 -0
  435. package/src/modules/cpq/lib/seeds/__tests__/api.test.ts +86 -0
  436. package/src/modules/cpq/lib/seeds/__tests__/tenant-provisioning.test.ts +349 -0
  437. package/src/modules/cpq/lib/seeds/api.ts +119 -0
  438. package/src/modules/cpq/lib/seeds/tenant-provisioning.ts +239 -0
  439. package/src/modules/cpq/lib/seeds.ts +18 -0
  440. package/src/modules/cpq/migrations/.snapshot-open-mercato.json +7473 -0
  441. package/src/modules/cpq/migrations/Migration20260220220000_cpq.ts +51 -0
  442. package/src/modules/cpq/migrations/Migration20260330000000_cpq_product_domain.ts +70 -0
  443. package/src/modules/cpq/migrations/Migration20260331000000_cpq_charge_unique_per_offering.ts +15 -0
  444. package/src/modules/cpq/migrations/Migration20260401000000_cpq_quoting_v2.ts +73 -0
  445. package/src/modules/cpq/migrations/Migration20260402000000_cpq_workflows.ts +59 -0
  446. package/src/modules/cpq/migrations/Migration20260407000000_cpq_pricing_tables_v2.ts +30 -0
  447. package/src/modules/cpq/migrations/Migration20260407100000_cpq_price_rules.ts +36 -0
  448. package/src/modules/cpq/migrations/Migration20260408000000_cpq_price_rules.ts +33 -0
  449. package/src/modules/cpq/migrations/Migration20260410000000_cpq_drop_datacentres_inventory.ts +22 -0
  450. package/src/modules/cpq/migrations/Migration20260410000000_cpq_wizard_rename.ts +56 -0
  451. package/src/modules/cpq/migrations/Migration20260410100000_cpq_inventory.ts +118 -0
  452. package/src/modules/cpq/migrations/Migration20260411000000_cpq_orders.ts +62 -0
  453. package/src/modules/cpq/migrations/Migration20260412000000_cpq_bundled_offerings.ts +33 -0
  454. package/src/modules/cpq/migrations/Migration20260504064621_cpq_arc.ts +68 -0
  455. package/src/modules/cpq/migrations/Migration20260514000000_cpq_spec_merge_retired_deprecated.ts +30 -0
  456. package/src/modules/cpq/migrations/Migration20260514100000_cpq_offering_merge_retired_deprecated.ts +27 -0
  457. package/src/modules/cpq/migrations/Migration20260514110000_cpq_list_search_indexes.ts +82 -0
  458. package/src/modules/cpq/migrations/Migration20260515000000_cpq_inventory_subscriptions_search_indexes.ts +52 -0
  459. package/src/modules/cpq/services/__tests__/_helpers/mockEm.ts +59 -0
  460. package/src/modules/cpq/services/__tests__/cpqBundleService.test.ts +661 -0
  461. package/src/modules/cpq/services/__tests__/cpqInventoryService.arc.test.ts +357 -0
  462. package/src/modules/cpq/services/__tests__/cpqInventoryService.test.ts +718 -0
  463. package/src/modules/cpq/services/__tests__/cpqOrderService.test.ts +603 -0
  464. package/src/modules/cpq/services/__tests__/cpqPricingService.test.ts +1012 -0
  465. package/src/modules/cpq/services/__tests__/cpqProductService.test.ts +603 -0
  466. package/src/modules/cpq/services/__tests__/cpqQuotingService.arc.test.ts +143 -0
  467. package/src/modules/cpq/services/__tests__/cpqQuotingService.test.ts +658 -0
  468. package/src/modules/cpq/services/__tests__/cpqValidationService.test.ts +811 -0
  469. package/src/modules/cpq/services/__tests__/cpqWizardService.test.ts +389 -0
  470. package/src/modules/cpq/services/__tests__/types.test.ts +359 -0
  471. package/src/modules/cpq/services/cpqBundleService.ts +671 -0
  472. package/src/modules/cpq/services/cpqInventoryService.ts +1929 -0
  473. package/src/modules/cpq/services/cpqOrderService.ts +1230 -0
  474. package/src/modules/cpq/services/cpqPricingService.ts +412 -0
  475. package/src/modules/cpq/services/cpqProductService.ts +311 -0
  476. package/src/modules/cpq/services/cpqQuotingService.ts +2638 -0
  477. package/src/modules/cpq/services/cpqValidationService.ts +323 -0
  478. package/src/modules/cpq/services/cpqWizardService.ts +204 -0
  479. package/src/modules/cpq/services/types.ts +303 -0
  480. package/src/modules/cpq/setup.ts +63 -0
  481. package/src/modules/cpq/subscribers/subscription-amended-log.ts +20 -0
  482. package/src/modules/cpq/subscribers/subscription-cancelled-log.ts +19 -0
  483. package/src/modules/cpq/subscribers/subscription-merged-log.ts +22 -0
  484. package/src/modules/cpq/subscribers/subscription-renewed-log.ts +18 -0
  485. package/src/modules/cpq/subscribers/subscription-superseded-log.ts +19 -0
  486. package/src/modules/cpq/widgets/injection/customer-inventory/widget.client.tsx +339 -0
  487. package/src/modules/cpq/widgets/injection/customer-inventory/widget.ts +16 -0
  488. package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.client.tsx +96 -0
  489. package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.ts +16 -0
  490. package/src/modules/cpq/widgets/injection/quote-configurator/widget.client.tsx +265 -0
  491. package/src/modules/cpq/widgets/injection/quote-configurator/widget.ts +16 -0
  492. package/src/modules/cpq/widgets/injection-table.ts +42 -0
  493. package/src/modules/cpq/workflows/WizardRunner.tsx +158 -0
  494. package/src/modules/cpq/workflows/WizardTrigger.tsx +142 -0
  495. package/src/modules/cpq/workflows/engine.tsx +278 -0
  496. package/src/modules/cpq/workflows/registry.ts +15 -0
  497. package/src/modules/cpq/workflows/steps/CustomerSelectStep.tsx +136 -0
  498. package/src/modules/cpq/workflows/steps/InventorySelectStep.tsx +34 -0
  499. package/src/modules/cpq/workflows/steps/ItemListStep.tsx +146 -0
  500. package/src/modules/cpq/workflows/steps/OfferingSelectStep.tsx +438 -0
  501. package/src/modules/cpq/workflows/steps/ProductConfigureStep.tsx +287 -0
  502. package/src/modules/cpq/workflows/steps/ReviewStep.tsx +219 -0
  503. package/src/modules/cpq/workflows/steps/index.ts +51 -0
  504. package/src/modules/cpq/workflows/types.ts +102 -0
@@ -0,0 +1,1289 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useRouter, useParams, useSearchParams } from "next/navigation";
5
+ import ArcQuoteConfigurator from "./_components/ArcQuoteConfigurator.js";
6
+ import { StatusPath } from "../../../../components/StatusPath.js";
7
+ import { ArrowRight, ExternalLink, Plus } from "lucide-react";
8
+ import { Alert, AlertTitle, AlertDescription } from "@open-mercato/ui/primitives/alert";
9
+ import { Button } from "@open-mercato/ui/primitives/button";
10
+ import { Tag } from "@open-mercato/ui/primitives/tag";
11
+ import { FormHeader } from "@open-mercato/ui/backend/forms";
12
+ import { NumberInput } from "../../../../components/NumberInput.js";
13
+ import {
14
+ formatStatusLabel,
15
+ quoteCpqStatusMap
16
+ } from "../../../../components/statusMaps.js";
17
+ function fmt(amount, currency) {
18
+ return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(amount);
19
+ }
20
+ const RULE_TYPE_LABELS = {
21
+ discount_percent: "% discount",
22
+ discount_absolute: "fixed discount",
23
+ surcharge_percent: "% surcharge",
24
+ surcharge_absolute: "fixed surcharge",
25
+ price_override: "override"
26
+ };
27
+ function fmtRuleValue(adj, currency) {
28
+ if (adj.ruleType === "discount_percent" || adj.ruleType === "surcharge_percent") return `${adj.value}%`;
29
+ if (currency) return fmt(adj.value, currency);
30
+ return String(adj.value);
31
+ }
32
+ async function apiJson(url, init) {
33
+ const res = await fetch(url, init);
34
+ const json = await res.json();
35
+ if (!res.ok) throw new Error(json?.error ?? `Request failed (${res.status})`);
36
+ return json;
37
+ }
38
+ function MetaCard({ label, children }) {
39
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4", children: [
40
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: label }),
41
+ /* @__PURE__ */ jsx("div", { className: "mt-1 text-sm", children })
42
+ ] });
43
+ }
44
+ function MoreActionsDropdown({ items }) {
45
+ const [open, setOpen] = React.useState(false);
46
+ const ref = React.useRef(null);
47
+ React.useEffect(() => {
48
+ if (!open) return;
49
+ const handler = (e) => {
50
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
51
+ };
52
+ document.addEventListener("mousedown", handler);
53
+ return () => document.removeEventListener("mousedown", handler);
54
+ }, [open]);
55
+ if (items.length === 0) return null;
56
+ return /* @__PURE__ */ jsxs("div", { className: "relative", ref, children: [
57
+ /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", onClick: () => setOpen((v) => !v), children: [
58
+ "Actions",
59
+ /* @__PURE__ */ jsx("svg", { className: "h-3 w-3 ml-1", fill: "none", viewBox: "0 0 24 24", strokeWidth: "2", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19.5 8.25l-7.5 7.5-7.5-7.5" }) })
60
+ ] }),
61
+ open && /* @__PURE__ */ jsx("div", { className: "absolute right-0 top-full mt-1 z-50 min-w-[200px] rounded-md border bg-popover shadow-md py-1", children: items.map((item) => {
62
+ const isDelete = item.id === "delete";
63
+ return /* @__PURE__ */ jsxs(
64
+ "button",
65
+ {
66
+ type: "button",
67
+ onClick: () => {
68
+ setOpen(false);
69
+ item.onSelect();
70
+ },
71
+ disabled: item.disabled || item.loading,
72
+ className: "w-full px-3 py-2 text-left text-sm flex items-center gap-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent " + (isDelete ? "text-destructive hover:bg-destructive/10 focus:bg-destructive/10 focus:outline-none" : "hover:bg-accent focus:bg-accent focus:outline-none"),
73
+ children: [
74
+ item.icon ? /* @__PURE__ */ jsx(item.icon, { className: "h-4 w-4 text-muted-foreground" }) : null,
75
+ /* @__PURE__ */ jsx("span", { children: item.label })
76
+ ]
77
+ },
78
+ item.id
79
+ );
80
+ }) })
81
+ ] });
82
+ }
83
+ function buildMoreActions({
84
+ recalculate,
85
+ submitting,
86
+ deletable,
87
+ onDelete,
88
+ deleting
89
+ }) {
90
+ const items = [
91
+ {
92
+ id: "recalculate",
93
+ label: "Recalculate",
94
+ onSelect: recalculate,
95
+ loading: submitting,
96
+ disabled: submitting
97
+ }
98
+ ];
99
+ if (deletable) {
100
+ items.push({
101
+ id: "delete",
102
+ label: "Delete",
103
+ onSelect: onDelete,
104
+ disabled: deleting,
105
+ loading: deleting
106
+ });
107
+ }
108
+ return items;
109
+ }
110
+ function CpqQuoteDetailPage(props) {
111
+ const router = useRouter();
112
+ const urlParams = useParams();
113
+ const searchParams = useSearchParams();
114
+ const cpqConfigId = props.params?.id ?? urlParams?.id;
115
+ const isNew = cpqConfigId === "new";
116
+ const [cpqQuote, setCpqQuote] = React.useState(null);
117
+ const [loading, setLoading] = React.useState(true);
118
+ const [error, setError] = React.useState(null);
119
+ const [submitting, setSubmitting] = React.useState(false);
120
+ const [view, setView] = React.useState("summary");
121
+ const [offerings, setOfferings] = React.useState([]);
122
+ const [offeringsLoading, setOfferingsLoading] = React.useState(false);
123
+ const [offeringSearch, setOfferingSearch] = React.useState("");
124
+ const [selectedOffering, setSelectedOffering] = React.useState(null);
125
+ const [attributes, setAttributes] = React.useState([]);
126
+ const [config, setConfig] = React.useState({});
127
+ const [quantity, setQuantity] = React.useState(1);
128
+ const [editingLineId, setEditingLineId] = React.useState(null);
129
+ const [addingToParentLineId, setAddingToParentLineId] = React.useState(null);
130
+ const [addingTargetSubIds, setAddingTargetSubIds] = React.useState([]);
131
+ const [expandedLines, setExpandedLines] = React.useState(/* @__PURE__ */ new Set());
132
+ const [converting, setConverting] = React.useState(false);
133
+ const [transitioning, setTransitioning] = React.useState(false);
134
+ const [deleting, setDeleting] = React.useState(false);
135
+ const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);
136
+ const [pricingDetail, setPricingDetail] = React.useState(null);
137
+ const [arcTargets, setArcTargets] = React.useState([]);
138
+ const [arcQuoteType, setArcQuoteType] = React.useState("new");
139
+ const [arcDrawerOpen, setArcDrawerOpen] = React.useState(false);
140
+ const loadArcTargets = React.useCallback(async () => {
141
+ try {
142
+ const res = await fetch(`/api/cpq/quotes/${cpqConfigId}/target-subscriptions`);
143
+ if (!res.ok) return;
144
+ const data = await res.json();
145
+ const items = data.items ?? [];
146
+ setArcTargets(items);
147
+ if (items.length > 0) {
148
+ setArcQuoteType(items[0].quoteType);
149
+ } else {
150
+ setArcQuoteType("new");
151
+ }
152
+ } catch {
153
+ }
154
+ }, [cpqConfigId]);
155
+ React.useEffect(() => {
156
+ if (!cpqConfigId || isNew) return;
157
+ void loadArcTargets();
158
+ }, [cpqConfigId, isNew, loadArcTargets]);
159
+ React.useEffect(() => {
160
+ if (isNew) {
161
+ const quoteId = searchParams.get("quoteId");
162
+ const customerId = searchParams.get("customerId");
163
+ if (!customerId) {
164
+ setError("customerId is required to create a CPQ quote");
165
+ setLoading(false);
166
+ return;
167
+ }
168
+ async function create() {
169
+ try {
170
+ const body = { customerId };
171
+ if (quoteId) body.quoteId = quoteId;
172
+ const result = await apiJson("/api/cpq/quotes", {
173
+ method: "POST",
174
+ headers: { "Content-Type": "application/json" },
175
+ body: JSON.stringify(body)
176
+ });
177
+ router.replace(`/backend/cpq/quotes/${result.id}`);
178
+ } catch (err) {
179
+ setError(err.message);
180
+ setLoading(false);
181
+ }
182
+ }
183
+ create();
184
+ return;
185
+ }
186
+ let cancelled = false;
187
+ async function load() {
188
+ try {
189
+ const result = await apiJson(`/api/cpq/quotes/${cpqConfigId}`);
190
+ if (!cancelled) setCpqQuote(result);
191
+ } catch (err) {
192
+ if (!cancelled) setError(err.message);
193
+ } finally {
194
+ if (!cancelled) setLoading(false);
195
+ }
196
+ }
197
+ load();
198
+ return () => {
199
+ cancelled = true;
200
+ };
201
+ }, [cpqConfigId, isNew, searchParams, router]);
202
+ const loadOfferings = async () => {
203
+ setOfferingsLoading(true);
204
+ try {
205
+ const result = await apiJson("/api/cpq/product-offerings?pageSize=100&lifecycleStatus=active");
206
+ setOfferings(result.items ?? []);
207
+ } catch {
208
+ setOfferings([]);
209
+ } finally {
210
+ setOfferingsLoading(false);
211
+ }
212
+ };
213
+ const loadAttributes = async (offeringId, currentConfig = {}) => {
214
+ try {
215
+ const params = new URLSearchParams({ offeringId });
216
+ if (Object.keys(currentConfig).length > 0) params.set("configuration", JSON.stringify(currentConfig));
217
+ const result = await apiJson(`/api/cpq/quotes/attributes?${params}`);
218
+ setAttributes(result.attributes ?? []);
219
+ } catch {
220
+ setAttributes([]);
221
+ }
222
+ };
223
+ const addItem = async () => {
224
+ if (!cpqQuote || !selectedOffering) return;
225
+ try {
226
+ setSubmitting(true);
227
+ setError(null);
228
+ const targetsToPost = addingTargetSubIds.length > 0 ? addingTargetSubIds : [null];
229
+ let lastResult = null;
230
+ for (const tid of targetsToPost) {
231
+ const body = { offeringId: selectedOffering.id, configuration: config, quantity };
232
+ if (addingToParentLineId) body.parentLineId = addingToParentLineId;
233
+ if (tid) body.targetSubscriptionId = tid;
234
+ lastResult = await apiJson(`/api/cpq/quotes/${cpqQuote.id}/items`, {
235
+ method: "POST",
236
+ headers: { "Content-Type": "application/json" },
237
+ body: JSON.stringify(body)
238
+ });
239
+ }
240
+ if (lastResult) setCpqQuote(lastResult);
241
+ resetConfigState();
242
+ setView("summary");
243
+ } catch (err) {
244
+ setError(err.message);
245
+ } finally {
246
+ setSubmitting(false);
247
+ }
248
+ };
249
+ const updateItem = async (lineId) => {
250
+ if (!cpqQuote) return;
251
+ try {
252
+ setSubmitting(true);
253
+ setError(null);
254
+ const result = await apiJson(`/api/cpq/quotes/${cpqQuote.id}/items/${lineId}`, {
255
+ method: "PUT",
256
+ headers: { "Content-Type": "application/json" },
257
+ body: JSON.stringify({ configuration: config, quantity })
258
+ });
259
+ setCpqQuote(result);
260
+ resetConfigState();
261
+ setView("summary");
262
+ } catch (err) {
263
+ setError(err.message);
264
+ } finally {
265
+ setSubmitting(false);
266
+ }
267
+ };
268
+ const removeItem = async (lineId) => {
269
+ if (!cpqQuote) return;
270
+ try {
271
+ setError(null);
272
+ const result = await apiJson(`/api/cpq/quotes/${cpqQuote.id}/items/${lineId}`, { method: "DELETE" });
273
+ setCpqQuote(result);
274
+ } catch (err) {
275
+ setError(err.message);
276
+ }
277
+ };
278
+ const recalculate = async () => {
279
+ if (!cpqQuote) return;
280
+ try {
281
+ setSubmitting(true);
282
+ setError(null);
283
+ const result = await apiJson(`/api/cpq/quotes/${cpqQuote.id}/recalculate`, {
284
+ method: "POST",
285
+ headers: { "Content-Type": "application/json" },
286
+ body: JSON.stringify({ save: true })
287
+ });
288
+ setCpqQuote(result);
289
+ } catch (err) {
290
+ setError(err.message);
291
+ } finally {
292
+ setSubmitting(false);
293
+ }
294
+ };
295
+ const convertToOrder = async () => {
296
+ if (!cpqQuote) return;
297
+ try {
298
+ setConverting(true);
299
+ setError(null);
300
+ const result = await apiJson("/api/cpq/orders", {
301
+ method: "POST",
302
+ headers: { "Content-Type": "application/json" },
303
+ body: JSON.stringify({ quoteId: cpqQuote.id })
304
+ });
305
+ router.push(`/backend/cpq/orders/${result.id}`);
306
+ } catch (err) {
307
+ setError(err.message);
308
+ } finally {
309
+ setConverting(false);
310
+ }
311
+ };
312
+ const transitionStatus = async (targetStatus) => {
313
+ if (!cpqQuote) return;
314
+ try {
315
+ setTransitioning(true);
316
+ setError(null);
317
+ const result = await apiJson(`/api/cpq/quotes/${cpqQuote.id}/status`, {
318
+ method: "POST",
319
+ headers: { "Content-Type": "application/json" },
320
+ body: JSON.stringify({ targetStatus })
321
+ });
322
+ setCpqQuote(result);
323
+ } catch (err) {
324
+ setError(err.message);
325
+ } finally {
326
+ setTransitioning(false);
327
+ }
328
+ };
329
+ const deleteQuote = async () => {
330
+ if (!cpqQuote) return;
331
+ try {
332
+ setDeleting(true);
333
+ setError(null);
334
+ setShowDeleteConfirm(false);
335
+ await apiJson(`/api/cpq/quotes/${cpqQuote.id}`, { method: "DELETE" });
336
+ router.push("/backend/cpq/quotes");
337
+ } catch (err) {
338
+ setError(err.message);
339
+ } finally {
340
+ setDeleting(false);
341
+ }
342
+ };
343
+ const resetConfigState = () => {
344
+ setSelectedOffering(null);
345
+ setAttributes([]);
346
+ setConfig({});
347
+ setQuantity(1);
348
+ setEditingLineId(null);
349
+ setAddingToParentLineId(null);
350
+ };
351
+ const startAddOffering = () => {
352
+ resetConfigState();
353
+ setAddingTargetSubIds(arcTargets.map((t) => t.subscriptionId));
354
+ loadOfferings();
355
+ setView("add-offering");
356
+ };
357
+ const startAddComponent = async (parentLineId, offeringId) => {
358
+ setSelectedOffering(null);
359
+ setAttributes([]);
360
+ setConfig({});
361
+ setQuantity(1);
362
+ setEditingLineId(null);
363
+ setAddingToParentLineId(parentLineId);
364
+ const parent = cpqQuote?.lines.find((l) => l.lineId === parentLineId);
365
+ const inherited = parent?.targetSubscriptionId ?? arcTargets[0]?.subscriptionId;
366
+ setAddingTargetSubIds(inherited ? [inherited] : []);
367
+ if (offeringId) {
368
+ try {
369
+ const result = await apiJson("/api/cpq/product-offerings?pageSize=100&lifecycleStatus=active");
370
+ const match = (result.items ?? []).find((o) => o.id === offeringId);
371
+ if (match) {
372
+ selectOffering(match);
373
+ return;
374
+ }
375
+ } catch {
376
+ }
377
+ }
378
+ loadOfferings();
379
+ setView("add-offering");
380
+ };
381
+ const selectOffering = (o) => {
382
+ setSelectedOffering(o);
383
+ setConfig({});
384
+ setQuantity(1);
385
+ loadAttributes(o.id);
386
+ setView("configure");
387
+ };
388
+ const startEditLine = (line) => {
389
+ setEditingLineId(line.lineId);
390
+ setConfig(line.configuration ?? {});
391
+ setQuantity(line.quantity);
392
+ if (line.offeringId) loadAttributes(line.offeringId, line.configuration ?? {});
393
+ setView("edit");
394
+ };
395
+ const toggleLine = (id) => {
396
+ setExpandedLines((prev) => {
397
+ const n = new Set(prev);
398
+ n.has(id) ? n.delete(id) : n.add(id);
399
+ return n;
400
+ });
401
+ };
402
+ if (loading) {
403
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 py-12 justify-center text-sm text-muted-foreground", children: [
404
+ /* @__PURE__ */ jsx(Spinner, {}),
405
+ " Loading\u2026"
406
+ ] });
407
+ }
408
+ if (!cpqQuote) {
409
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
410
+ /* @__PURE__ */ jsx("button", { onClick: () => router.push("/backend/cpq/quotes"), className: "text-sm text-muted-foreground hover:text-foreground", children: "\u2190 Back to Quotes" }),
411
+ error && /* @__PURE__ */ jsx(ErrorBanner, { message: error }),
412
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: "Quote not found." })
413
+ ] });
414
+ }
415
+ const { pricingSummary: summary, currencyCode: currency } = cpqQuote;
416
+ const arcEditable = ["new", "incomplete", "ready"].includes(cpqQuote.cpqStatus);
417
+ const isMergeRenew = arcQuoteType === "renew" && arcTargets.length >= 2 && arcTargets.every((t) => t.mergeAction === "absorb");
418
+ const arcTargetCodeMap = /* @__PURE__ */ new Map();
419
+ for (const t of arcTargets) {
420
+ if (t.subscription?.code) arcTargetCodeMap.set(t.subscriptionId, t.subscription.code);
421
+ }
422
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
423
+ arcDrawerOpen && /* @__PURE__ */ jsx(
424
+ ArcQuoteConfigurator,
425
+ {
426
+ quoteId: cpqQuote.id,
427
+ customerId: cpqQuote.customerId,
428
+ customerName: cpqQuote.customerName,
429
+ currencyCode: cpqQuote.currencyCode,
430
+ initialType: arcQuoteType,
431
+ initialTargets: arcTargets,
432
+ editable: arcEditable,
433
+ onClose: () => setArcDrawerOpen(false),
434
+ onChanged: async () => {
435
+ await loadArcTargets();
436
+ try {
437
+ const fresh = await apiJson(`/api/cpq/quotes/${cpqQuote.id}`);
438
+ setCpqQuote(fresh);
439
+ } catch {
440
+ }
441
+ }
442
+ }
443
+ ),
444
+ /* @__PURE__ */ jsx(
445
+ FormHeader,
446
+ {
447
+ mode: "detail",
448
+ backHref: "/backend/cpq/quotes",
449
+ backLabel: "Back to CPQ Quotes",
450
+ entityTypeLabel: "CPQ Quote",
451
+ title: cpqQuote.quoteNumber || cpqQuote.customerName || "Untitled",
452
+ statusBadge: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 pt-1", children: [
453
+ /* @__PURE__ */ jsx(Tag, { variant: quoteCpqStatusMap[cpqQuote.cpqStatus] ?? "neutral", dot: true, children: formatStatusLabel(cpqQuote.cpqStatus) }),
454
+ arcQuoteType !== "new" && /* @__PURE__ */ jsx(Tag, { variant: "brand", className: "uppercase", children: arcQuoteType }),
455
+ cpqQuote.convertedOrderId && /* @__PURE__ */ jsx(Tag, { variant: "success", dot: true, children: "Converted" })
456
+ ] }),
457
+ actionsContent: /* @__PURE__ */ jsxs(Fragment, { children: [
458
+ /* @__PURE__ */ jsxs(
459
+ Button,
460
+ {
461
+ type: "button",
462
+ variant: "outline",
463
+ onClick: () => router.push(`/backend/sales/quotes/${cpqQuote.quoteId}`),
464
+ children: [
465
+ /* @__PURE__ */ jsx(ExternalLink, { className: "h-3.5 w-3.5" }),
466
+ "View Sales Quote"
467
+ ]
468
+ }
469
+ ),
470
+ cpqQuote.convertedOrderId ? /* @__PURE__ */ jsxs(
471
+ Button,
472
+ {
473
+ type: "button",
474
+ onClick: () => router.push(`/backend/cpq/orders/${cpqQuote.convertedOrderId}`),
475
+ children: [
476
+ /* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4" }),
477
+ "Go to order"
478
+ ]
479
+ }
480
+ ) : view !== "summary" ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: () => {
481
+ resetConfigState();
482
+ setView("summary");
483
+ }, children: "Cancel" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
484
+ /* @__PURE__ */ jsxs(Button, { type: "button", onClick: startAddOffering, children: [
485
+ /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
486
+ "Add Offering"
487
+ ] }),
488
+ arcEditable && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: () => setArcDrawerOpen(true), children: "Modify Subscription" }),
489
+ cpqQuote.cpqStatus === "accepted" && /* @__PURE__ */ jsxs(Button, { type: "button", onClick: convertToOrder, disabled: converting, children: [
490
+ converting ? /* @__PURE__ */ jsx(Spinner, {}) : /* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4" }),
491
+ "Convert to Order"
492
+ ] }),
493
+ /* @__PURE__ */ jsx(
494
+ MoreActionsDropdown,
495
+ {
496
+ items: buildMoreActions({
497
+ recalculate,
498
+ submitting,
499
+ deletable: !["accepted", "rejected", "cancelled"].includes(cpqQuote.cpqStatus),
500
+ onDelete: () => setShowDeleteConfirm(true),
501
+ deleting
502
+ })
503
+ }
504
+ )
505
+ ] })
506
+ ] })
507
+ }
508
+ ),
509
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 flex-wrap", children: [
510
+ /* @__PURE__ */ jsx(
511
+ StatusPath,
512
+ {
513
+ current: cpqQuote.cpqStatus,
514
+ path: cpqQuote.cpqStatus === "pre_approved" ? ["new", "incomplete", "ready", "in_approval", "pre_approved", "with_customer", "accepted"] : ["new", "incomplete", "ready", "in_approval", "approved", "with_customer", "accepted"],
515
+ terminals: ["rejected", "cancelled"],
516
+ statusMap: quoteCpqStatusMap,
517
+ onTransition: transitionStatus,
518
+ disabled: transitioning,
519
+ ariaLabel: "Quote status path"
520
+ }
521
+ ),
522
+ transitioning && /* @__PURE__ */ jsx(Spinner, {})
523
+ ] }),
524
+ error && /* @__PURE__ */ jsx(ErrorBanner, { message: error, onDismiss: () => setError(null) }),
525
+ arcQuoteType !== "new" && arcTargets.length > 0 && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-brand-violet/30 bg-brand-violet/10 px-4 py-3 text-sm text-brand-violet", children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxs("div", { children: [
526
+ /* @__PURE__ */ jsx("span", { className: "font-medium uppercase", children: arcQuoteType }),
527
+ " \u2022 ",
528
+ arcTargets.length,
529
+ " target",
530
+ arcTargets.length === 1 ? "" : "s",
531
+ ":",
532
+ " ",
533
+ arcTargets.map((t, i) => /* @__PURE__ */ jsxs("span", { children: [
534
+ i > 0 && ", ",
535
+ /* @__PURE__ */ jsx(
536
+ "button",
537
+ {
538
+ onClick: () => router.push(`/backend/cpq/inventory/subscriptions/${t.subscriptionId}`),
539
+ className: "text-brand-violet hover:underline",
540
+ children: t.subscription?.code ?? t.subscriptionId.slice(0, 8)
541
+ }
542
+ )
543
+ ] }, t.id)),
544
+ isMergeRenew && /* @__PURE__ */ jsx("span", { className: "ml-2 text-status-warning-text font-medium", children: "\u2022 Merging into a new contract" })
545
+ ] }) }) }),
546
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3", children: [
547
+ /* @__PURE__ */ jsx(MetaCard, { label: "Customer", children: cpqQuote.customerName ? /* @__PURE__ */ jsx("span", { className: "font-medium", children: cpqQuote.customerName }) : /* @__PURE__ */ jsxs("span", { className: "font-mono text-xs text-muted-foreground", children: [
548
+ cpqQuote.customerId.slice(0, 8),
549
+ "\u2026"
550
+ ] }) }),
551
+ /* @__PURE__ */ jsx(MetaCard, { label: "Version", children: /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
552
+ "v",
553
+ cpqQuote.version
554
+ ] }) }),
555
+ /* @__PURE__ */ jsx(MetaCard, { label: "Currency", children: /* @__PURE__ */ jsx("span", { className: "font-medium", children: currency }) })
556
+ ] }),
557
+ showDeleteConfirm && /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", onClick: () => setShowDeleteConfirm(false), children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-sm rounded-lg border bg-card shadow-xl", onClick: (e) => e.stopPropagation(), children: [
558
+ /* @__PURE__ */ jsx("div", { className: "px-4 py-3 border-b", children: /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: "Delete Quote" }) }),
559
+ /* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Are you sure you want to delete this quote? This will soft-delete the quote and all its line items." }) }),
560
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 px-4 py-3 border-t", children: [
561
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: () => setShowDeleteConfirm(false), children: "Cancel" }),
562
+ /* @__PURE__ */ jsxs(Button, { type: "button", variant: "destructive", onClick: deleteQuote, disabled: deleting, children: [
563
+ deleting ? /* @__PURE__ */ jsx(Spinner, {}) : null,
564
+ " Delete"
565
+ ] })
566
+ ] })
567
+ ] }) }),
568
+ view === "summary" && /* @__PURE__ */ jsxs(Fragment, { children: [
569
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4", children: [
570
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-3", children: "Pricing Summary" }),
571
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 sm:grid-cols-4 gap-4", children: [
572
+ /* @__PURE__ */ jsxs("div", { children: [
573
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Non-Recurring (NRC)" }),
574
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold font-mono", children: fmt(summary.nrcTotal, currency) })
575
+ ] }),
576
+ /* @__PURE__ */ jsxs("div", { children: [
577
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Monthly Recurring (MRC)" }),
578
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold font-mono", children: fmt(summary.mrcTotal, currency) })
579
+ ] }),
580
+ summary.usageTotalEstimated != null && /* @__PURE__ */ jsxs("div", { children: [
581
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Usage (est.)" }),
582
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold font-mono", children: fmt(summary.usageTotalEstimated, currency) })
583
+ ] }),
584
+ summary.usageCharges.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
585
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Usage Charges" }),
586
+ summary.usageCharges.map((uc, i) => /* @__PURE__ */ jsxs("p", { className: "text-xs mt-0.5", children: [
587
+ uc.chargeName,
588
+ ": ",
589
+ fmt(uc.unitPrice, currency),
590
+ "/unit"
591
+ ] }, i))
592
+ ] })
593
+ ] })
594
+ ] }),
595
+ !cpqQuote.validationResult.valid && /* @__PURE__ */ jsxs(Alert, { variant: "warning", children: [
596
+ /* @__PURE__ */ jsx(AlertTitle, { children: "Validation Issues" }),
597
+ /* @__PURE__ */ jsx(AlertDescription, { children: /* @__PURE__ */ jsx("span", { className: "text-xs space-y-0.5 block", children: cpqQuote.validationResult.errors.map((e, i) => /* @__PURE__ */ jsxs("span", { className: "block", children: [
598
+ "\u2014 ",
599
+ e.message
600
+ ] }, i)) }) })
601
+ ] }),
602
+ /* @__PURE__ */ jsx(
603
+ QuoteLineTree,
604
+ {
605
+ lines: cpqQuote.lines,
606
+ currency,
607
+ expandedLines,
608
+ pricingDetail,
609
+ targetCodes: arcTargetCodeMap,
610
+ onToggleLine: toggleLine,
611
+ onEditLine: startEditLine,
612
+ onRemoveLine: removeItem,
613
+ onAddComponent: startAddComponent,
614
+ onTogglePricingDetail: (lineIdx, chargeIdx) => setPricingDetail(
615
+ pricingDetail?.lineIdx === lineIdx && pricingDetail?.chargeIdx === chargeIdx ? null : { lineIdx, chargeIdx }
616
+ )
617
+ }
618
+ )
619
+ ] }),
620
+ view === "add-offering" && /* @__PURE__ */ jsxs(Fragment, { children: [
621
+ addingToParentLineId && /* @__PURE__ */ jsxs(Alert, { variant: "info", children: [
622
+ "Adding component to bundle: ",
623
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: cpqQuote.lines.find((l) => l.lineId === addingToParentLineId)?.offeringName ?? "Bundle" })
624
+ ] }),
625
+ /* @__PURE__ */ jsx(
626
+ OfferingBrowser,
627
+ {
628
+ offerings,
629
+ loading: offeringsLoading,
630
+ search: offeringSearch,
631
+ onSearchChange: setOfferingSearch,
632
+ onSelect: selectOffering
633
+ }
634
+ )
635
+ ] }),
636
+ view === "configure" && selectedOffering && /* @__PURE__ */ jsx(
637
+ ConfigurePanel,
638
+ {
639
+ title: addingToParentLineId ? `Add Component: ${selectedOffering.name}` : `Configure: ${selectedOffering.name}`,
640
+ attributes,
641
+ config,
642
+ quantity,
643
+ arcTargetOptions: arcTargets.length >= 2 && !addingToParentLineId ? arcTargets.map((t) => ({
644
+ subscriptionId: t.subscriptionId,
645
+ code: t.subscription?.code ?? t.subscriptionId.slice(0, 8),
646
+ name: t.subscription?.name ?? null
647
+ })) : null,
648
+ arcSelectedTargetIds: addingTargetSubIds,
649
+ onArcTargetToggle: (subId) => {
650
+ setAddingTargetSubIds(
651
+ (prev) => prev.includes(subId) ? prev.filter((id) => id !== subId) : [...prev, subId]
652
+ );
653
+ },
654
+ onConfigChange: (key, value) => {
655
+ const next = { ...config, [key]: value };
656
+ setConfig(next);
657
+ if (selectedOffering) loadAttributes(selectedOffering.id, next);
658
+ },
659
+ onQuantityChange: setQuantity,
660
+ onSubmit: addItem,
661
+ submitLabel: "Add to Quote",
662
+ submitting
663
+ }
664
+ ),
665
+ view === "edit" && editingLineId && /* @__PURE__ */ jsx(
666
+ ConfigurePanel,
667
+ {
668
+ title: `Edit: ${cpqQuote.lines.find((l) => l.lineId === editingLineId)?.offeringName ?? "Line"}`,
669
+ attributes,
670
+ config,
671
+ quantity,
672
+ arcTargetOptions: null,
673
+ arcSelectedTargetIds: [],
674
+ onArcTargetToggle: () => {
675
+ },
676
+ onConfigChange: (key, value) => {
677
+ const next = { ...config, [key]: value };
678
+ setConfig(next);
679
+ const line = cpqQuote.lines.find((l) => l.lineId === editingLineId);
680
+ if (line?.offeringId) loadAttributes(line.offeringId, next);
681
+ },
682
+ onQuantityChange: setQuantity,
683
+ onSubmit: () => updateItem(editingLineId),
684
+ submitLabel: "Update Line",
685
+ submitting
686
+ }
687
+ )
688
+ ] });
689
+ }
690
+ function QuoteLineTree({ lines, currency, expandedLines, pricingDetail, targetCodes, onToggleLine, onEditLine, onRemoveLine, onAddComponent, onTogglePricingDetail }) {
691
+ const [bundleTrees, setBundleTrees] = React.useState(/* @__PURE__ */ new Map());
692
+ const [loadingTrees, setLoadingTrees] = React.useState(/* @__PURE__ */ new Set());
693
+ const rootLines = lines.filter((l) => !l.parentLineId);
694
+ const childrenByParent = /* @__PURE__ */ new Map();
695
+ for (const line of lines) {
696
+ if (line.parentLineId) {
697
+ const arr = childrenByParent.get(line.parentLineId) ?? [];
698
+ arr.push(line);
699
+ childrenByParent.set(line.parentLineId, arr);
700
+ }
701
+ }
702
+ const fetchBundleTree = React.useCallback(async (offeringId) => {
703
+ if (bundleTrees.has(offeringId) || loadingTrees.has(offeringId)) return;
704
+ setLoadingTrees((prev) => new Set(prev).add(offeringId));
705
+ try {
706
+ const result = await apiJson(`/api/cpq/bundle-tree?offeringId=${offeringId}`);
707
+ setBundleTrees((prev) => new Map(prev).set(offeringId, result.tree));
708
+ } catch {
709
+ } finally {
710
+ setLoadingTrees((prev) => {
711
+ const n = new Set(prev);
712
+ n.delete(offeringId);
713
+ return n;
714
+ });
715
+ }
716
+ }, [bundleTrees, loadingTrees]);
717
+ return /* @__PURE__ */ jsxs("div", { children: [
718
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2 px-1", children: "Line Items" }),
719
+ lines.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card py-8 text-center text-sm text-muted-foreground", children: 'No items configured yet. Click "Add Offering" to start.' }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card divide-y", children: rootLines.map((line) => {
720
+ const lineIdx = lines.indexOf(line);
721
+ const isBundle = line.offeringType === "bundle";
722
+ const children = childrenByParent.get(line.lineId) ?? [];
723
+ const isExpanded = expandedLines.has(line.lineId);
724
+ const bundleTree = isBundle && line.offeringId ? bundleTrees.get(line.offeringId) : void 0;
725
+ const treeLoading = isBundle && line.offeringId ? loadingTrees.has(line.offeringId) : false;
726
+ if (isBundle && isExpanded && line.offeringId && !bundleTree && !treeLoading) {
727
+ fetchBundleTree(line.offeringId);
728
+ }
729
+ return /* @__PURE__ */ jsxs("div", { children: [
730
+ /* @__PURE__ */ jsx(
731
+ QuoteLineRow,
732
+ {
733
+ line,
734
+ lineIdx,
735
+ currency,
736
+ isExpanded,
737
+ pricingDetail,
738
+ isBundle,
739
+ childCount: children.length,
740
+ indent: 0,
741
+ targetCodes,
742
+ onToggle: () => onToggleLine(line.lineId),
743
+ onEdit: () => onEditLine(line),
744
+ onRemove: () => onRemoveLine(line.lineId),
745
+ onAddComponent: isBundle ? () => onAddComponent(line.lineId) : void 0,
746
+ onTogglePricingDetail
747
+ }
748
+ ),
749
+ isBundle && isExpanded && /* @__PURE__ */ jsx(
750
+ BundleSlotPanel,
751
+ {
752
+ bundleTree,
753
+ treeLoading,
754
+ children,
755
+ allLines: lines,
756
+ currency,
757
+ expandedLines,
758
+ pricingDetail,
759
+ parentLineId: line.lineId,
760
+ targetCodes,
761
+ onToggleLine,
762
+ onEditLine,
763
+ onRemoveLine,
764
+ onAddComponent,
765
+ onTogglePricingDetail
766
+ }
767
+ )
768
+ ] }, line.lineId);
769
+ }) })
770
+ ] });
771
+ }
772
+ function BundleSlotPanel({ bundleTree, treeLoading, children, allLines, currency, expandedLines, pricingDetail, parentLineId, targetCodes, onToggleLine, onEditLine, onRemoveLine, onAddComponent, onTogglePricingDetail }) {
773
+ if (treeLoading) {
774
+ return /* @__PURE__ */ jsx("div", { className: "border-t bg-muted/5 px-8 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
775
+ /* @__PURE__ */ jsx(Spinner, {}),
776
+ " Loading bundle structure\u2026"
777
+ ] }) });
778
+ }
779
+ if (!bundleTree || bundleTree.slots.length === 0) {
780
+ if (children.length === 0) return null;
781
+ return /* @__PURE__ */ jsx("div", { className: "border-t bg-muted/5", children: children.map((child) => {
782
+ const childIdx = allLines.indexOf(child);
783
+ return /* @__PURE__ */ jsx(
784
+ QuoteLineRow,
785
+ {
786
+ line: child,
787
+ lineIdx: childIdx,
788
+ currency,
789
+ isExpanded: expandedLines.has(child.lineId),
790
+ pricingDetail,
791
+ isBundle: false,
792
+ childCount: 0,
793
+ indent: 1,
794
+ targetCodes,
795
+ onToggle: () => onToggleLine(child.lineId),
796
+ onEdit: () => onEditLine(child),
797
+ onRemove: () => onRemoveLine(child.lineId),
798
+ onTogglePricingDetail
799
+ },
800
+ child.lineId
801
+ );
802
+ }) });
803
+ }
804
+ const childOfferingIds = new Set(children.map((c) => c.offeringId).filter(Boolean));
805
+ return /* @__PURE__ */ jsxs("div", { className: "border-t bg-muted/5", children: [
806
+ bundleTree.slots.map((slot) => {
807
+ const filledChildren = children.filter(
808
+ (c) => slot.components.some((comp) => comp.childOffering.offeringId === c.offeringId)
809
+ );
810
+ const filledCount = filledChildren.length;
811
+ const isRequired = slot.cardinalityMin > 0;
812
+ const isFull = slot.cardinalityMax != null && filledCount >= slot.cardinalityMax;
813
+ const canAdd = !isFull;
814
+ return /* @__PURE__ */ jsxs("div", { className: "border-b last:border-b-0", children: [
815
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-6 py-2 bg-muted/20", children: [
816
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide", children: slot.name }),
817
+ isRequired ? /* @__PURE__ */ jsx(Tag, { variant: "warning", className: "text-[10px] px-1.5", children: "required" }) : /* @__PURE__ */ jsx(Tag, { variant: "neutral", className: "text-[10px] px-1.5", children: "optional" }),
818
+ /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
819
+ filledCount,
820
+ "/",
821
+ slot.cardinalityMax ?? "\u221E",
822
+ slot.cardinalityMin > 0 && ` (min ${slot.cardinalityMin})`
823
+ ] }),
824
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
825
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: slot.components.map((comp) => {
826
+ const isFilled = childOfferingIds.has(comp.childOffering.offeringId);
827
+ return /* @__PURE__ */ jsxs(
828
+ "button",
829
+ {
830
+ onClick: () => !isFilled && canAdd && onAddComponent(parentLineId, comp.childOffering.offeringId),
831
+ disabled: isFilled || !canAdd,
832
+ className: `inline-flex items-center gap-1 rounded border px-2 py-1 text-[11px] font-medium transition-colors ${isFilled ? "border-status-success-border bg-status-success-bg text-status-success-text cursor-default" : canAdd ? "border-border hover:border-primary hover:bg-primary/5 hover:text-primary cursor-pointer" : "border-border text-muted-foreground opacity-50 cursor-not-allowed"}`,
833
+ title: isFilled ? "Already added" : canAdd ? `Add ${comp.childOffering.offeringName}` : "Slot is full",
834
+ children: [
835
+ isFilled ? /* @__PURE__ */ jsx("svg", { className: "h-3 w-3", fill: "none", viewBox: "0 0 24 24", strokeWidth: "2", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4.5 12.75l6 6 9-13.5" }) }) : /* @__PURE__ */ jsx("svg", { className: "h-3 w-3", fill: "none", viewBox: "0 0 24 24", strokeWidth: "2", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 4.5v15m7.5-7.5h-15" }) }),
836
+ comp.childOffering.offeringName
837
+ ]
838
+ },
839
+ comp.componentId
840
+ );
841
+ }) })
842
+ ] }),
843
+ filledChildren.map((child) => {
844
+ const childIdx = allLines.indexOf(child);
845
+ return /* @__PURE__ */ jsx(
846
+ QuoteLineRow,
847
+ {
848
+ line: child,
849
+ lineIdx: childIdx,
850
+ currency,
851
+ isExpanded: expandedLines.has(child.lineId),
852
+ pricingDetail,
853
+ isBundle: false,
854
+ childCount: 0,
855
+ indent: 1,
856
+ targetCodes,
857
+ onToggle: () => onToggleLine(child.lineId),
858
+ onEdit: () => onEditLine(child),
859
+ onRemove: () => onRemoveLine(child.lineId),
860
+ onTogglePricingDetail
861
+ },
862
+ child.lineId
863
+ );
864
+ })
865
+ ] }, slot.slotId);
866
+ }),
867
+ children.filter((c) => !bundleTree.slots.some((s) => s.components.some((comp) => comp.childOffering.offeringId === c.offeringId))).map((child) => {
868
+ const childIdx = allLines.indexOf(child);
869
+ return /* @__PURE__ */ jsx(
870
+ QuoteLineRow,
871
+ {
872
+ line: child,
873
+ lineIdx: childIdx,
874
+ currency,
875
+ isExpanded: expandedLines.has(child.lineId),
876
+ pricingDetail,
877
+ isBundle: false,
878
+ childCount: 0,
879
+ indent: 1,
880
+ targetCodes,
881
+ onToggle: () => onToggleLine(child.lineId),
882
+ onEdit: () => onEditLine(child),
883
+ onRemove: () => onRemoveLine(child.lineId),
884
+ onTogglePricingDetail
885
+ },
886
+ child.lineId
887
+ );
888
+ })
889
+ ] });
890
+ }
891
+ function QuoteLineRow({ line, lineIdx, currency, isExpanded, pricingDetail, isBundle, childCount, indent, targetCodes, onToggle, onEdit, onRemove, onAddComponent, onTogglePricingDetail }) {
892
+ const paddingLeft = indent > 0 ? `${indent * 2 + 1}rem` : void 0;
893
+ return /* @__PURE__ */ jsxs("div", { children: [
894
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3 hover:bg-muted/30 transition-colors", style: paddingLeft ? { paddingLeft } : void 0, children: [
895
+ /* @__PURE__ */ jsx("button", { onClick: onToggle, className: "flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ jsx("svg", { className: `h-4 w-4 transition-transform ${isExpanded ? "rotate-90" : ""}`, fill: "none", viewBox: "0 0 24 24", strokeWidth: "2", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 4.5l7.5 7.5-7.5 7.5" }) }) }),
896
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
897
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
898
+ indent > 0 && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs", children: "\u2514" }),
899
+ isBundle && /* @__PURE__ */ jsx(Tag, { variant: "brand", className: "px-1.5 text-xs", children: "bundle" }),
900
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-sm", children: line.offeringName }),
901
+ line.quantity > 1 && /* @__PURE__ */ jsxs("span", { className: "rounded bg-muted px-1.5 py-0.5 text-xs font-medium", children: [
902
+ "\xD7",
903
+ line.quantity
904
+ ] }),
905
+ /* @__PURE__ */ jsx(ActionBadge, { action: line.action }),
906
+ targetCodes.size >= 2 && line.targetSubscriptionId && targetCodes.get(line.targetSubscriptionId) && /* @__PURE__ */ jsxs(
907
+ Tag,
908
+ {
909
+ variant: "brand",
910
+ className: "px-1.5 text-xs",
911
+ title: `Acts on subscription ${targetCodes.get(line.targetSubscriptionId)}`,
912
+ children: [
913
+ "\u2192 ",
914
+ targetCodes.get(line.targetSubscriptionId)
915
+ ]
916
+ }
917
+ ),
918
+ line.isConfigured ? /* @__PURE__ */ jsx(Tag, { variant: "success", className: "px-1.5 text-xs", children: "configured" }) : /* @__PURE__ */ jsx(Tag, { variant: "warning", className: "px-1.5 text-xs", children: "incomplete" }),
919
+ isBundle && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
920
+ childCount,
921
+ " component",
922
+ childCount !== 1 ? "s" : ""
923
+ ] })
924
+ ] }),
925
+ line.validationErrors && line.validationErrors.length > 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive mt-0.5", children: line.validationErrors[0].message })
926
+ ] }),
927
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 text-right text-xs space-y-0.5", children: [
928
+ line.nrcTotal > 0 && /* @__PURE__ */ jsxs("div", { children: [
929
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "NRC " }),
930
+ /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: fmt(line.nrcTotal, currency) })
931
+ ] }),
932
+ line.mrcTotal > 0 && /* @__PURE__ */ jsxs("div", { children: [
933
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "MRC " }),
934
+ /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: fmt(line.mrcTotal, currency) })
935
+ ] })
936
+ ] }),
937
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
938
+ onAddComponent && /* @__PURE__ */ jsx("button", { onClick: onAddComponent, className: "rounded p-1.5 text-muted-foreground hover:text-primary hover:bg-primary/5 transition-colors", title: "Add Component", children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 4.5v15m7.5-7.5h-15" }) }) }),
939
+ /* @__PURE__ */ jsx("button", { onClick: onEdit, className: "rounded p-1.5 text-muted-foreground hover:text-foreground hover:bg-muted transition-colors", title: "Edit", children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" }) }) }),
940
+ /* @__PURE__ */ jsx("button", { onClick: onRemove, className: "rounded p-1.5 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors", title: "Remove", children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" }) }) })
941
+ ] })
942
+ ] }),
943
+ isExpanded && line.arcSource && /* @__PURE__ */ jsx(ArcLineDiff, { line, currency }),
944
+ isExpanded && !isBundle && line.charges.length > 0 && /* @__PURE__ */ jsx(ChargeBreakdown, { line, lineIdx, currency, pricingDetail, onTogglePricingDetail }),
945
+ isExpanded && isBundle && line.charges.length > 0 && /* @__PURE__ */ jsx(ChargeBreakdown, { line, lineIdx, currency, pricingDetail, onTogglePricingDetail }),
946
+ isExpanded && line.charges.length === 0 && !line.arcSource && /* @__PURE__ */ jsx(LineSummaryFallback, { line, currency })
947
+ ] });
948
+ }
949
+ function ArcLineDiff({ line, currency }) {
950
+ const src = line.arcSource;
951
+ if (!src) return null;
952
+ const isCancel = line.action === "cancel";
953
+ const beforeMrc = src.mrcAmount;
954
+ const beforeNrc = src.nrcAmount;
955
+ const beforeQty = src.quantity;
956
+ const afterMrc = isCancel ? 0 : line.mrcTotal;
957
+ const afterNrc = isCancel ? 0 : line.nrcTotal;
958
+ const afterQty = isCancel ? 0 : line.quantity;
959
+ const fmtVal = (v, asCurrency) => asCurrency ? fmt(v, currency) : String(v);
960
+ const rows = [
961
+ { label: "Quantity", before: beforeQty, after: afterQty, asCurrency: false },
962
+ { label: "MRC", before: beforeMrc, after: afterMrc, asCurrency: true },
963
+ { label: "NRC", before: beforeNrc, after: afterNrc, asCurrency: true }
964
+ ];
965
+ return /* @__PURE__ */ jsxs("div", { className: "border-t bg-muted/10 px-12 py-2 space-y-1.5", children: [
966
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Change Preview" }),
967
+ /* @__PURE__ */ jsx("div", { className: "rounded border bg-background overflow-hidden", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-xs", children: [
968
+ /* @__PURE__ */ jsx("thead", { className: "bg-muted/40", children: /* @__PURE__ */ jsxs("tr", { className: "text-muted-foreground", children: [
969
+ /* @__PURE__ */ jsx("th", { className: "text-left px-3 py-1.5 font-medium" }),
970
+ /* @__PURE__ */ jsx("th", { className: "text-right px-3 py-1.5 font-medium", children: "Before" }),
971
+ /* @__PURE__ */ jsx("th", { className: "text-right px-3 py-1.5 font-medium", children: "After" }),
972
+ /* @__PURE__ */ jsx("th", { className: "text-right px-3 py-1.5 font-medium", children: "\u0394" })
973
+ ] }) }),
974
+ /* @__PURE__ */ jsx("tbody", { children: rows.map((r) => {
975
+ const delta = r.after - r.before;
976
+ const showDelta = delta !== 0;
977
+ return /* @__PURE__ */ jsxs("tr", { className: "border-t border-dashed border-border/40", children: [
978
+ /* @__PURE__ */ jsx("td", { className: "px-3 py-1 text-muted-foreground", children: r.label }),
979
+ /* @__PURE__ */ jsx("td", { className: "px-3 py-1 text-right font-mono", children: fmtVal(r.before, r.asCurrency) }),
980
+ /* @__PURE__ */ jsx("td", { className: `px-3 py-1 text-right font-mono ${isCancel ? "text-muted-foreground line-through" : ""}`, children: fmtVal(r.after, r.asCurrency) }),
981
+ /* @__PURE__ */ jsx("td", { className: `px-3 py-1 text-right font-mono font-medium ${showDelta ? delta > 0 ? "text-status-success-text" : "text-destructive" : "text-muted-foreground"}`, children: showDelta ? `${delta > 0 ? "+" : ""}${fmtVal(delta, r.asCurrency)}` : "\u2014" })
982
+ ] }, r.label);
983
+ }) })
984
+ ] }) }),
985
+ isCancel && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive italic", children: "Cancellation \u2014 item will be terminated on activation." })
986
+ ] });
987
+ }
988
+ function ActionBadge({ action }) {
989
+ const variantByAction = {
990
+ add: "info",
991
+ modify: "warning",
992
+ cancel: "error"
993
+ };
994
+ return /* @__PURE__ */ jsx(Tag, { variant: variantByAction[action], className: "px-1.5 text-xs", children: action });
995
+ }
996
+ function LineSummaryFallback({ line, currency }) {
997
+ const hasTotals = line.nrcTotal > 0 || line.mrcTotal > 0;
998
+ const mirroredFromItemId = line.configuration?._arcMirroredFromItemId ?? null;
999
+ return /* @__PURE__ */ jsxs("div", { className: "border-t bg-muted/10 px-12 py-2 text-xs text-muted-foreground space-y-1", children: [
1000
+ /* @__PURE__ */ jsxs("div", { children: [
1001
+ "No detailed charge breakdown available for this line.",
1002
+ mirroredFromItemId && " Mirrored from existing subscription item."
1003
+ ] }),
1004
+ hasTotals && /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
1005
+ line.nrcTotal > 0 && /* @__PURE__ */ jsxs("span", { children: [
1006
+ "NRC ",
1007
+ /* @__PURE__ */ jsx("span", { className: "font-mono font-medium text-foreground", children: fmt(line.nrcTotal, currency) })
1008
+ ] }),
1009
+ line.mrcTotal > 0 && /* @__PURE__ */ jsxs("span", { children: [
1010
+ "MRC ",
1011
+ /* @__PURE__ */ jsx("span", { className: "font-mono font-medium text-foreground", children: fmt(line.mrcTotal, currency) })
1012
+ ] })
1013
+ ] })
1014
+ ] });
1015
+ }
1016
+ function ChargeBreakdown({ line, lineIdx, currency, pricingDetail, onTogglePricingDetail }) {
1017
+ return /* @__PURE__ */ jsx("div", { className: "border-t bg-muted/10 px-12 py-2", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-xs", children: [
1018
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "text-muted-foreground", children: [
1019
+ /* @__PURE__ */ jsx("th", { className: "text-left py-1 font-medium", children: "Charge" }),
1020
+ /* @__PURE__ */ jsx("th", { className: "text-left py-1 font-medium", children: "Type" }),
1021
+ /* @__PURE__ */ jsx("th", { className: "text-right py-1 font-medium", children: "Unit Price" }),
1022
+ /* @__PURE__ */ jsx("th", { className: "text-right py-1 font-medium", children: "Qty" }),
1023
+ /* @__PURE__ */ jsx("th", { className: "text-right py-1 font-medium", children: "Total" }),
1024
+ /* @__PURE__ */ jsx("th", { className: "w-8" })
1025
+ ] }) }),
1026
+ /* @__PURE__ */ jsx("tbody", { children: line.charges.map((c, i) => {
1027
+ const hasDetail = !!(c.breakdown?.tiers?.length || c.adjustments?.length);
1028
+ const isDetailOpen = pricingDetail?.lineIdx === lineIdx && pricingDetail?.chargeIdx === i;
1029
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
1030
+ /* @__PURE__ */ jsxs("tr", { className: "border-t border-dashed border-border/50", children: [
1031
+ /* @__PURE__ */ jsx("td", { className: "py-1", children: c.chargeName }),
1032
+ /* @__PURE__ */ jsx("td", { className: "py-1 uppercase", children: c.chargeType }),
1033
+ /* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: fmt(c.unitPrice, currency) }),
1034
+ /* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: c.quantity ?? "\u2014" }),
1035
+ /* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: c.totalPrice != null ? fmt(c.totalPrice, currency) : c.note ?? "\u2014" }),
1036
+ /* @__PURE__ */ jsx("td", { className: "py-1 text-center", children: hasDetail && /* @__PURE__ */ jsx(
1037
+ "button",
1038
+ {
1039
+ onClick: () => onTogglePricingDetail(lineIdx, i),
1040
+ className: "inline-flex items-center justify-center rounded p-0.5 hover:bg-accent text-muted-foreground hover:text-foreground transition-colors",
1041
+ title: "Show pricing breakdown",
1042
+ children: /* @__PURE__ */ jsx("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", children: /* @__PURE__ */ jsx("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" }) })
1043
+ }
1044
+ ) })
1045
+ ] }),
1046
+ isDetailOpen && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 6, className: "pb-3 pt-1", children: /* @__PURE__ */ jsxs("div", { className: "rounded border bg-background p-3 space-y-3 text-xs", children: [
1047
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1048
+ /* @__PURE__ */ jsxs("span", { className: "font-semibold text-sm", children: [
1049
+ "Pricing Details \u2014 ",
1050
+ c.chargeName
1051
+ ] }),
1052
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: c.pricingMethod ?? "flat" })
1053
+ ] }),
1054
+ c.breakdown?.tiers && c.breakdown.tiers.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
1055
+ /* @__PURE__ */ jsx("div", { className: "font-medium text-muted-foreground mb-1", children: "Tier Breakdown" }),
1056
+ /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
1057
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "text-muted-foreground", children: [
1058
+ /* @__PURE__ */ jsx("th", { className: "text-left py-0.5 font-medium", children: "Tier" }),
1059
+ /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Range" }),
1060
+ /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Price/Unit" }),
1061
+ /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Qty" }),
1062
+ /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Subtotal" })
1063
+ ] }) }),
1064
+ /* @__PURE__ */ jsxs("tbody", { children: [
1065
+ c.breakdown.tiers.map((t) => /* @__PURE__ */ jsxs("tr", { className: "border-t border-border/30", children: [
1066
+ /* @__PURE__ */ jsx("td", { className: "py-0.5", children: t.tier }),
1067
+ /* @__PURE__ */ jsxs("td", { className: "py-0.5 text-right font-mono", children: [
1068
+ t.from,
1069
+ "\u2013",
1070
+ t.to
1071
+ ] }),
1072
+ /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(t.pricePerUnit, currency) }),
1073
+ /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: t.quantity }),
1074
+ /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(t.subtotal, currency) })
1075
+ ] }, t.tier)),
1076
+ /* @__PURE__ */ jsxs("tr", { className: "border-t font-medium", children: [
1077
+ /* @__PURE__ */ jsx("td", { colSpan: 4, className: "py-0.5 text-right", children: "Tiered Total" }),
1078
+ /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(c.breakdown.tiers.reduce((s, t) => s + t.subtotal, 0), currency) })
1079
+ ] })
1080
+ ] })
1081
+ ] })
1082
+ ] }),
1083
+ c.adjustments && c.adjustments.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
1084
+ /* @__PURE__ */ jsx("div", { className: "font-medium text-muted-foreground mb-1", children: "Applied Price Rules" }),
1085
+ /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
1086
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "text-muted-foreground", children: [
1087
+ /* @__PURE__ */ jsx("th", { className: "text-left py-0.5 font-medium", children: "Rule" }),
1088
+ /* @__PURE__ */ jsx("th", { className: "text-left py-0.5 font-medium", children: "Type" }),
1089
+ /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Value" }),
1090
+ /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Before" }),
1091
+ /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "After" }),
1092
+ /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Delta" })
1093
+ ] }) }),
1094
+ /* @__PURE__ */ jsx("tbody", { children: c.adjustments.map((adj, ai) => /* @__PURE__ */ jsxs("tr", { className: "border-t border-border/30", children: [
1095
+ /* @__PURE__ */ jsx("td", { className: "py-0.5", children: adj.ruleName }),
1096
+ /* @__PURE__ */ jsx("td", { className: "py-0.5", children: RULE_TYPE_LABELS[adj.ruleType] ?? adj.ruleType }),
1097
+ /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmtRuleValue(adj, currency) }),
1098
+ /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(adj.unitPriceBefore, currency) }),
1099
+ /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(adj.unitPriceAfter, currency) }),
1100
+ /* @__PURE__ */ jsxs("td", { className: `py-0.5 text-right font-mono ${adj.delta < 0 ? "text-destructive" : adj.delta > 0 ? "text-status-success-text" : ""}`, children: [
1101
+ adj.delta > 0 ? "+" : "",
1102
+ fmt(adj.delta, currency)
1103
+ ] })
1104
+ ] }, ai)) })
1105
+ ] })
1106
+ ] }),
1107
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-1 border-t text-sm font-semibold", children: [
1108
+ /* @__PURE__ */ jsx("span", { children: "Final Total" }),
1109
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: c.totalPrice != null ? fmt(c.totalPrice, currency) : "\u2014" })
1110
+ ] })
1111
+ ] }) }) })
1112
+ ] }, i);
1113
+ }) })
1114
+ ] }) });
1115
+ }
1116
+ function Spinner() {
1117
+ return /* @__PURE__ */ jsxs("svg", { className: "h-4 w-4 animate-spin", viewBox: "0 0 24 24", fill: "none", children: [
1118
+ /* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
1119
+ /* @__PURE__ */ jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" })
1120
+ ] });
1121
+ }
1122
+ function ErrorBanner({ message, onDismiss }) {
1123
+ return /* @__PURE__ */ jsxs(Alert, { variant: "destructive", className: "flex items-start gap-2", children: [
1124
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: message }),
1125
+ onDismiss && /* @__PURE__ */ jsx(
1126
+ "button",
1127
+ {
1128
+ onClick: onDismiss,
1129
+ className: "text-destructive/70 hover:text-destructive text-sm font-bold",
1130
+ "aria-label": "Dismiss",
1131
+ children: "\xD7"
1132
+ }
1133
+ )
1134
+ ] });
1135
+ }
1136
+ function OfferingBrowser({ offerings, loading, search, onSearchChange, onSelect }) {
1137
+ const filtered = offerings.filter((o) => {
1138
+ if (!search.trim()) return true;
1139
+ const q = search.toLowerCase();
1140
+ return o.name.toLowerCase().includes(q) || o.code.toLowerCase().includes(q) || (o.description ?? "").toLowerCase().includes(q);
1141
+ });
1142
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1143
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium px-1", children: "Select Product Offering" }),
1144
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1145
+ /* @__PURE__ */ jsx("svg", { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground", fill: "none", viewBox: "0 0 24 24", strokeWidth: "2", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" }) }),
1146
+ /* @__PURE__ */ jsx(
1147
+ "input",
1148
+ {
1149
+ type: "text",
1150
+ placeholder: "Search offerings\u2026",
1151
+ value: search,
1152
+ onChange: (e) => onSearchChange(e.target.value),
1153
+ className: "w-full rounded-md border bg-background pl-10 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50"
1154
+ }
1155
+ )
1156
+ ] }),
1157
+ loading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 py-8 justify-center text-sm text-muted-foreground", children: [
1158
+ /* @__PURE__ */ jsx(Spinner, {}),
1159
+ " Loading offerings\u2026"
1160
+ ] }) : filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-sm text-muted-foreground", children: search ? "No offerings match your search." : "No active offerings available." }) : /* @__PURE__ */ jsx("div", { className: "grid gap-2 sm:grid-cols-2 lg:grid-cols-3", children: filtered.map((offering) => /* @__PURE__ */ jsxs(
1161
+ "button",
1162
+ {
1163
+ onClick: () => onSelect(offering),
1164
+ className: "rounded-lg border bg-card p-4 text-left hover:border-primary hover:bg-primary/5 transition-colors group",
1165
+ children: [
1166
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium group-hover:text-primary transition-colors", children: offering.name }),
1167
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground font-mono mt-0.5", children: offering.code }),
1168
+ offering.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1 line-clamp-2", children: offering.description })
1169
+ ]
1170
+ },
1171
+ offering.id
1172
+ )) })
1173
+ ] });
1174
+ }
1175
+ function ConfigurePanel({ title, attributes, config, quantity, arcTargetOptions, arcSelectedTargetIds, onArcTargetToggle, onConfigChange, onQuantityChange, onSubmit, submitLabel, submitting }) {
1176
+ const noTargetsPicked = arcTargetOptions !== null && arcTargetOptions.length > 0 && arcSelectedTargetIds.length === 0;
1177
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card", children: [
1178
+ /* @__PURE__ */ jsx("div", { className: "border-b px-4 py-3", children: /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: title }) }),
1179
+ /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-4", children: [
1180
+ arcTargetOptions && arcTargetOptions.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
1181
+ /* @__PURE__ */ jsxs("label", { className: "block text-sm font-medium mb-1", children: [
1182
+ "Apply to subscription(s)",
1183
+ /* @__PURE__ */ jsx("span", { className: "text-destructive ml-1", children: "*" })
1184
+ ] }),
1185
+ /* @__PURE__ */ jsx("div", { className: "space-y-1.5 rounded-md border bg-background px-3 py-2", children: arcTargetOptions.map((opt) => {
1186
+ const checked = arcSelectedTargetIds.includes(opt.subscriptionId);
1187
+ return /* @__PURE__ */ jsxs(
1188
+ "label",
1189
+ {
1190
+ className: "flex items-center gap-2 text-sm cursor-pointer hover:bg-muted/40 rounded px-1 py-0.5",
1191
+ children: [
1192
+ /* @__PURE__ */ jsx(
1193
+ "input",
1194
+ {
1195
+ type: "checkbox",
1196
+ checked,
1197
+ onChange: () => onArcTargetToggle(opt.subscriptionId),
1198
+ className: "h-4 w-4 rounded border-border"
1199
+ }
1200
+ ),
1201
+ /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: opt.code }),
1202
+ opt.name && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
1203
+ "\u2014 ",
1204
+ opt.name
1205
+ ] })
1206
+ ]
1207
+ },
1208
+ opt.subscriptionId
1209
+ );
1210
+ }) }),
1211
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: "Pick one or more \u2014 a separate line will be created on each selected subscription." }),
1212
+ noTargetsPicked && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive mt-1", children: "Select at least one target." })
1213
+ ] }),
1214
+ /* @__PURE__ */ jsxs("div", { children: [
1215
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: "Quantity" }),
1216
+ /* @__PURE__ */ jsx(
1217
+ NumberInput,
1218
+ {
1219
+ integer: true,
1220
+ min: 1,
1221
+ value: quantity,
1222
+ onChange: (n) => onQuantityChange(Math.max(1, n ?? 1)),
1223
+ className: "w-24"
1224
+ }
1225
+ )
1226
+ ] }),
1227
+ attributes.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No configurable attributes for this offering." }) : attributes.map((attr) => /* @__PURE__ */ jsx(AttributeField, { attribute: attr, value: config[attr.code], onChange: (v) => onConfigChange(attr.code, v) }, attr.code))
1228
+ ] }),
1229
+ /* @__PURE__ */ jsx("div", { className: "border-t px-4 py-3 flex justify-end", children: /* @__PURE__ */ jsxs(
1230
+ "button",
1231
+ {
1232
+ onClick: onSubmit,
1233
+ disabled: submitting || noTargetsPicked,
1234
+ className: "inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm hover:bg-primary/90 transition-colors disabled:opacity-50",
1235
+ children: [
1236
+ submitting ? /* @__PURE__ */ jsx(Spinner, {}) : null,
1237
+ " ",
1238
+ submitLabel
1239
+ ]
1240
+ }
1241
+ ) })
1242
+ ] });
1243
+ }
1244
+ function AttributeField({ attribute, value, onChange }) {
1245
+ const { code, name, attributeType, isRequired, options, constraints, helpText, defaultValue } = attribute;
1246
+ const currentValue = value ?? defaultValue ?? "";
1247
+ return /* @__PURE__ */ jsxs("div", { children: [
1248
+ /* @__PURE__ */ jsxs("label", { className: "block text-sm font-medium mb-1", children: [
1249
+ name,
1250
+ isRequired && /* @__PURE__ */ jsx("span", { className: "text-destructive ml-0.5", children: "*" })
1251
+ ] }),
1252
+ (attributeType === "select" || attributeType === "enum") && options && options.length > 0 ? /* @__PURE__ */ jsxs(
1253
+ "select",
1254
+ {
1255
+ value: String(currentValue),
1256
+ onChange: (e) => onChange(e.target.value),
1257
+ className: "w-full rounded-md border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50",
1258
+ children: [
1259
+ /* @__PURE__ */ jsx("option", { value: "", children: "\u2014 Select \u2014" }),
1260
+ options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
1261
+ ]
1262
+ }
1263
+ ) : attributeType === "boolean" ? /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2", children: [
1264
+ /* @__PURE__ */ jsx("input", { type: "checkbox", checked: Boolean(currentValue), onChange: (e) => onChange(e.target.checked), className: "rounded border-border" }),
1265
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: helpText ?? "Enable" })
1266
+ ] }) : attributeType === "number" ? /* @__PURE__ */ jsx(
1267
+ NumberInput,
1268
+ {
1269
+ value: typeof currentValue === "number" ? currentValue : currentValue === "" || currentValue == null ? null : Number(currentValue),
1270
+ min: constraints?.min,
1271
+ max: constraints?.max,
1272
+ onChange: (n) => onChange(n ?? "")
1273
+ }
1274
+ ) : /* @__PURE__ */ jsx(
1275
+ "input",
1276
+ {
1277
+ type: "text",
1278
+ value: String(currentValue),
1279
+ onChange: (e) => onChange(e.target.value),
1280
+ className: "w-full rounded-md border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50"
1281
+ }
1282
+ ),
1283
+ helpText && attributeType !== "boolean" && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: helpText })
1284
+ ] });
1285
+ }
1286
+ export {
1287
+ CpqQuoteDetailPage as default
1288
+ };
1289
+ //# sourceMappingURL=page.js.map