@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,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/cpq/backend/cpq/wizards/page.meta.ts"],
4
+ "sourcesContent": ["import React from 'react'\nimport { Wand2 } from 'lucide-react'\n\nconst wizardsIcon = React.createElement(Wand2, { size: 16 })\n\nexport const metadata = {\n requireAuth: true,\n requireFeatures: ['cpq.wizards.view'],\n pageTitle: 'Wizards',\n pageTitleKey: 'cpq.wizards.title',\n pageGroup: 'CPQ',\n pageGroupKey: 'customers~sales~cpq.nav.group',\n pageOrder: 35,\n icon: wizardsIcon,\n breadcrumb: [{ label: 'Wizards', labelKey: 'cpq.wizards.title' }],\n} as const\n"],
5
+ "mappings": "AAAA,OAAO,WAAW;AAClB,SAAS,aAAa;AAEtB,MAAM,cAAc,MAAM,cAAc,OAAO,EAAE,MAAM,GAAG,CAAC;AAEpD,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,kBAAkB;AAAA,EACpC,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,MAAM;AAAA,EACN,YAAY,CAAC,EAAE,OAAO,WAAW,UAAU,oBAAoB,CAAC;AAClE;",
6
+ "names": []
7
+ }
@@ -0,0 +1,151 @@
1
+ const entities = [
2
+ {
3
+ id: "cpq:cpq_product_specification",
4
+ label: "Product Specification",
5
+ description: "CPQ product blueprint \u2014 defines attributes, dependencies, and relationships.",
6
+ labelField: "name",
7
+ showInSidebar: false,
8
+ fields: []
9
+ },
10
+ {
11
+ id: "cpq:cpq_product_offering",
12
+ label: "Product Offering",
13
+ description: "Sellable instance of a product specification with design-time values and charges.",
14
+ labelField: "name",
15
+ showInSidebar: false,
16
+ fields: []
17
+ },
18
+ {
19
+ id: "cpq:cpq_product_relationship",
20
+ label: "Product Relationship",
21
+ description: "Structural relationship between product specifications (parent_child, requires, excludes).",
22
+ labelField: "id",
23
+ showInSidebar: false,
24
+ fields: []
25
+ },
26
+ {
27
+ id: "cpq:cpq_product_attribute",
28
+ label: "Product Attribute",
29
+ description: "Configurable attribute definition for a CPQ product.",
30
+ labelField: "name",
31
+ showInSidebar: false,
32
+ fields: []
33
+ },
34
+ {
35
+ id: "cpq:cpq_product_charge",
36
+ label: "Product Charge",
37
+ description: "Charge definition (NRC/MRC/usage) for a CPQ product.",
38
+ labelField: "name",
39
+ showInSidebar: false,
40
+ fields: []
41
+ },
42
+ {
43
+ id: "cpq:cpq_pricing_table",
44
+ label: "Pricing Table",
45
+ description: "Multi-dimensional pricing table with named price columns.",
46
+ labelField: "name",
47
+ showInSidebar: false,
48
+ fields: []
49
+ },
50
+ {
51
+ id: "cpq:cpq_pricing_table_entry",
52
+ label: "Pricing Table Entry",
53
+ description: "Individual row in a pricing table with dimension values and prices.",
54
+ labelField: "id",
55
+ showInSidebar: false,
56
+ fields: []
57
+ },
58
+ {
59
+ id: "cpq:cpq_price_rule",
60
+ label: "Price Rule",
61
+ description: "Post-calculation price adjustment rule (discount, surcharge, override).",
62
+ labelField: "name",
63
+ showInSidebar: false,
64
+ fields: []
65
+ },
66
+ {
67
+ id: "cpq:cpq_bundle_slot",
68
+ label: "Bundle Slot",
69
+ description: "Structural slot within a bundle specification \u2014 defines what component specs can fill it.",
70
+ labelField: "name",
71
+ showInSidebar: false,
72
+ fields: []
73
+ },
74
+ {
75
+ id: "cpq:cpq_offering_component",
76
+ label: "Offering Component",
77
+ description: "Links a bundle offering to a specific component offering within a slot.",
78
+ labelField: "id",
79
+ showInSidebar: false,
80
+ fields: []
81
+ },
82
+ {
83
+ id: "cpq:cpq_quote_configuration",
84
+ label: "Quote Configuration",
85
+ description: "CPQ-specific configuration data for a sales quote.",
86
+ labelField: "id",
87
+ showInSidebar: false,
88
+ fields: []
89
+ },
90
+ {
91
+ id: "cpq:cpq_quote_line_configuration",
92
+ label: "Quote Line Configuration",
93
+ description: "CPQ-specific configuration for a sales quote line (one per offering).",
94
+ labelField: "id",
95
+ showInSidebar: false,
96
+ fields: []
97
+ },
98
+ {
99
+ id: "cpq:cpq_wizard_definition",
100
+ label: "Wizard Definition",
101
+ description: "CPQ wizard definition \u2014 multi-step guided quoting flow.",
102
+ labelField: "name",
103
+ showInSidebar: false,
104
+ fields: []
105
+ },
106
+ {
107
+ id: "cpq:cpq_inventory_subscription",
108
+ label: "Inventory Subscription",
109
+ description: "Commercial wrapper grouping subscription items under shared contractual terms.",
110
+ showInSidebar: false,
111
+ fields: []
112
+ },
113
+ {
114
+ id: "cpq:cpq_inventory_subscription_item",
115
+ label: "Inventory Subscription Item",
116
+ description: "Concrete product/service record within an inventory subscription.",
117
+ labelField: "name",
118
+ showInSidebar: false,
119
+ fields: []
120
+ },
121
+ {
122
+ id: "cpq:cpq_inventory_asset",
123
+ label: "Inventory Asset",
124
+ description: "Tangible or intangible thing the customer owns \u2014 hardware, licenses, fees.",
125
+ labelField: "name",
126
+ showInSidebar: false,
127
+ fields: []
128
+ },
129
+ {
130
+ id: "cpq:cpq_order_configuration",
131
+ label: "Order Configuration",
132
+ description: "CPQ-specific configuration data for a sales order.",
133
+ labelField: "id",
134
+ showInSidebar: false,
135
+ fields: []
136
+ },
137
+ {
138
+ id: "cpq:cpq_order_line_configuration",
139
+ label: "Order Line Configuration",
140
+ description: "CPQ-specific configuration for a sales order line (one per offering).",
141
+ labelField: "id",
142
+ showInSidebar: false,
143
+ fields: []
144
+ }
145
+ ];
146
+ var ce_default = entities;
147
+ export {
148
+ ce_default as default,
149
+ entities
150
+ };
151
+ //# sourceMappingURL=ce.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/cpq/ce.ts"],
4
+ "sourcesContent": ["import type { CustomEntitySpec } from '@open-mercato/shared/modules/entities'\n\nconst entities: CustomEntitySpec[] = [\n {\n id: 'cpq:cpq_product_specification',\n label: 'Product Specification',\n description: 'CPQ product blueprint \u2014 defines attributes, dependencies, and relationships.',\n labelField: 'name',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_product_offering',\n label: 'Product Offering',\n description: 'Sellable instance of a product specification with design-time values and charges.',\n labelField: 'name',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_product_relationship',\n label: 'Product Relationship',\n description: 'Structural relationship between product specifications (parent_child, requires, excludes).',\n labelField: 'id',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_product_attribute',\n label: 'Product Attribute',\n description: 'Configurable attribute definition for a CPQ product.',\n labelField: 'name',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_product_charge',\n label: 'Product Charge',\n description: 'Charge definition (NRC/MRC/usage) for a CPQ product.',\n labelField: 'name',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_pricing_table',\n label: 'Pricing Table',\n description: 'Multi-dimensional pricing table with named price columns.',\n labelField: 'name',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_pricing_table_entry',\n label: 'Pricing Table Entry',\n description: 'Individual row in a pricing table with dimension values and prices.',\n labelField: 'id',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_price_rule',\n label: 'Price Rule',\n description: 'Post-calculation price adjustment rule (discount, surcharge, override).',\n labelField: 'name',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_bundle_slot',\n label: 'Bundle Slot',\n description: 'Structural slot within a bundle specification \u2014 defines what component specs can fill it.',\n labelField: 'name',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_offering_component',\n label: 'Offering Component',\n description: 'Links a bundle offering to a specific component offering within a slot.',\n labelField: 'id',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_quote_configuration',\n label: 'Quote Configuration',\n description: 'CPQ-specific configuration data for a sales quote.',\n labelField: 'id',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_quote_line_configuration',\n label: 'Quote Line Configuration',\n description: 'CPQ-specific configuration for a sales quote line (one per offering).',\n labelField: 'id',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_wizard_definition',\n label: 'Wizard Definition',\n description: 'CPQ wizard definition \u2014 multi-step guided quoting flow.',\n labelField: 'name',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_inventory_subscription',\n label: 'Inventory Subscription',\n description: 'Commercial wrapper grouping subscription items under shared contractual terms.',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_inventory_subscription_item',\n label: 'Inventory Subscription Item',\n description: 'Concrete product/service record within an inventory subscription.',\n labelField: 'name',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_inventory_asset',\n label: 'Inventory Asset',\n description: 'Tangible or intangible thing the customer owns \u2014 hardware, licenses, fees.',\n labelField: 'name',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_order_configuration',\n label: 'Order Configuration',\n description: 'CPQ-specific configuration data for a sales order.',\n labelField: 'id',\n showInSidebar: false,\n fields: [],\n },\n {\n id: 'cpq:cpq_order_line_configuration',\n label: 'Order Line Configuration',\n description: 'CPQ-specific configuration for a sales order line (one per offering).',\n labelField: 'id',\n showInSidebar: false,\n fields: [],\n },\n]\n\nexport { entities }\nexport default entities\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,11 @@
1
+ const seedCommand = {
2
+ command: "seed",
3
+ async run() {
4
+ console.log("CPQ seed: no default seed data. Configure specifications, offerings, and pricing via the admin UI.");
5
+ }
6
+ };
7
+ var cli_default = [seedCommand];
8
+ export {
9
+ cli_default as default
10
+ };
11
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/cpq/cli.ts"],
4
+ "sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\n\nconst seedCommand: ModuleCli = {\n command: 'seed',\n async run() {\n console.log('CPQ seed: no default seed data. Configure specifications, offerings, and pricing via the admin UI.')\n },\n}\n\nexport default [seedCommand]\n"],
5
+ "mappings": "AAEA,MAAM,cAAyB;AAAA,EAC7B,SAAS;AAAA,EACT,MAAM,MAAM;AACV,YAAQ,IAAI,oGAAoG;AAAA,EAClH;AACF;AAEA,IAAO,cAAQ,CAAC,WAAW;",
6
+ "names": []
7
+ }
@@ -0,0 +1,265 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { DataTable } from "@open-mercato/ui/backend/DataTable";
5
+ import { Page, PageBody } from "@open-mercato/ui/backend/Page";
6
+ import { RowActions } from "@open-mercato/ui/backend/RowActions";
7
+ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
8
+ import { flash } from "@open-mercato/ui/backend/FlashMessages";
9
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
10
+ import { useCpqRowActions } from "./useCpqRowActions.js";
11
+ function useCpqListData(options) {
12
+ const {
13
+ endpoint,
14
+ pageSize = 50,
15
+ initialSorting = [{ id: "createdAt", desc: true }],
16
+ buildFilterParams,
17
+ loadErrorMessage
18
+ } = options;
19
+ const t = useT();
20
+ const [rows, setRows] = React.useState([]);
21
+ const [total, setTotal] = React.useState(0);
22
+ const [totalPages, setTotalPages] = React.useState(1);
23
+ const [page, setPageState] = React.useState(1);
24
+ const [isLoading, setIsLoading] = React.useState(true);
25
+ const [durationMs, setDurationMs] = React.useState(null);
26
+ const [reloadToken, setReloadToken] = React.useState(0);
27
+ const [search, setSearchState] = React.useState("");
28
+ const [sorting, setSorting] = React.useState(initialSorting);
29
+ const [filterValues, setFilterValuesState] = React.useState({});
30
+ const queryString = React.useMemo(() => {
31
+ const params = new URLSearchParams();
32
+ params.set("page", String(page));
33
+ params.set("pageSize", String(pageSize));
34
+ if (search.trim()) params.set("search", search.trim());
35
+ const sort = sorting[0];
36
+ if (sort?.id) {
37
+ params.set("sortField", sort.id);
38
+ params.set("sortDir", sort.desc ? "desc" : "asc");
39
+ }
40
+ if (buildFilterParams) buildFilterParams(filterValues, params);
41
+ return params.toString();
42
+ }, [buildFilterParams, filterValues, page, pageSize, search, sorting]);
43
+ React.useEffect(() => {
44
+ let cancelled = false;
45
+ async function load() {
46
+ setIsLoading(true);
47
+ const startedAt = typeof performance !== "undefined" ? performance.now() : Date.now();
48
+ try {
49
+ const fallback = { items: [], total: 0, totalPages: 1 };
50
+ const call = await apiCall(
51
+ `${endpoint}?${queryString}`,
52
+ void 0,
53
+ { fallback }
54
+ );
55
+ if (cancelled) return;
56
+ if (!call.ok) {
57
+ flash(loadErrorMessage ?? t("cpq.list.error.load", "Failed to load"), "error");
58
+ return;
59
+ }
60
+ const payload = call.result ?? fallback;
61
+ const items = Array.isArray(payload.items) ? payload.items : [];
62
+ setRows(items);
63
+ setTotal(typeof payload.total === "number" ? payload.total : items.length);
64
+ setTotalPages(typeof payload.totalPages === "number" ? payload.totalPages : 1);
65
+ const finishedAt = typeof performance !== "undefined" ? performance.now() : Date.now();
66
+ setDurationMs(Math.max(0, finishedAt - startedAt));
67
+ } finally {
68
+ if (!cancelled) setIsLoading(false);
69
+ }
70
+ }
71
+ load();
72
+ return () => {
73
+ cancelled = true;
74
+ };
75
+ }, [endpoint, loadErrorMessage, queryString, reloadToken, t]);
76
+ const setSearch = React.useCallback((value) => {
77
+ setSearchState(value);
78
+ setPageState(1);
79
+ }, []);
80
+ const setFilterValues = React.useCallback((values) => {
81
+ setFilterValuesState(values);
82
+ setPageState(1);
83
+ }, []);
84
+ const clearFilters = React.useCallback(() => {
85
+ setFilterValuesState({});
86
+ setPageState(1);
87
+ }, []);
88
+ const reload = React.useCallback(() => {
89
+ setReloadToken((token) => token + 1);
90
+ }, []);
91
+ return {
92
+ rows,
93
+ total,
94
+ totalPages,
95
+ page,
96
+ isLoading,
97
+ durationMs,
98
+ search,
99
+ sorting,
100
+ filterValues,
101
+ setPage: setPageState,
102
+ setSearch,
103
+ setSorting,
104
+ setFilterValues,
105
+ clearFilters,
106
+ reload,
107
+ queryString
108
+ };
109
+ }
110
+ function CpqListView({
111
+ title,
112
+ tableId,
113
+ selectionScopeKey,
114
+ data,
115
+ columns,
116
+ filters,
117
+ searchPlaceholder,
118
+ actions,
119
+ bulkActions,
120
+ rowActions,
121
+ crud,
122
+ onRowClick,
123
+ emptyState,
124
+ pageSize = 50,
125
+ toolbarContent,
126
+ footerContent
127
+ }) {
128
+ const t = useT();
129
+ const crudActions = useCpqRowActions({
130
+ endpoint: crud?.endpoint ?? "",
131
+ entityName: crud?.entityName ?? "item",
132
+ editHref: crud?.editHref ?? (() => "#"),
133
+ onReload: data.reload,
134
+ canDelete: crud?.canDelete
135
+ });
136
+ const resolvedRowActions = React.useMemo(() => {
137
+ if (rowActions) return rowActions;
138
+ if (!crud) return void 0;
139
+ return (row) => /* @__PURE__ */ jsx(RowActions, { items: crudActions.buildItems(row, crud.extraRowItems?.(row)) });
140
+ }, [crud, crudActions, rowActions]);
141
+ const resolvedBulkActions = React.useMemo(() => {
142
+ if (!crud) return bulkActions;
143
+ const deleteAction = {
144
+ id: "delete",
145
+ label: t("cpq.list.bulk.deleteSelected", "Delete selected"),
146
+ destructive: true,
147
+ onExecute: crudActions.bulkDelete
148
+ };
149
+ return bulkActions ? [...bulkActions, deleteAction] : [deleteAction];
150
+ }, [bulkActions, crud, crudActions.bulkDelete, t]);
151
+ const resolvedFooter = crud ? /* @__PURE__ */ jsxs(Fragment, { children: [
152
+ footerContent,
153
+ crudActions.ConfirmDialogElement
154
+ ] }) : footerContent;
155
+ return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsxs(PageBody, { className: "cpq-list-view space-y-6", children: [
156
+ /* @__PURE__ */ jsx(CpqInteractionStyles, {}),
157
+ toolbarContent,
158
+ /* @__PURE__ */ jsx(
159
+ DataTable,
160
+ {
161
+ title,
162
+ actions,
163
+ refreshButton: {
164
+ label: t("cpq.list.actions.refresh", "Refresh"),
165
+ onRefresh: data.reload,
166
+ isRefreshing: data.isLoading
167
+ },
168
+ columns,
169
+ data: data.rows,
170
+ searchValue: data.search,
171
+ onSearchChange: data.setSearch,
172
+ searchPlaceholder: searchPlaceholder ?? t("cpq.list.search.placeholder", "Search..."),
173
+ filters,
174
+ filterValues: data.filterValues,
175
+ onFiltersApply: data.setFilterValues,
176
+ onFiltersClear: data.clearFilters,
177
+ sorting: data.sorting,
178
+ onSortingChange: data.setSorting,
179
+ bulkActions: resolvedBulkActions,
180
+ selectionScopeKey: selectionScopeKey ?? tableId,
181
+ onRowClick,
182
+ rowActions: resolvedRowActions,
183
+ perspective: { tableId },
184
+ columnChooser: { auto: true },
185
+ pagination: {
186
+ page: data.page,
187
+ pageSize,
188
+ total: data.total,
189
+ totalPages: data.totalPages,
190
+ onPageChange: data.setPage,
191
+ durationMs: data.durationMs
192
+ },
193
+ isLoading: data.isLoading,
194
+ emptyState
195
+ }
196
+ ),
197
+ resolvedFooter
198
+ ] }) });
199
+ }
200
+ function CpqInteractionStyles() {
201
+ return /* @__PURE__ */ jsx("style", { children: `
202
+ .cpq-list-view tbody tr.cursor-pointer,
203
+ .cpq-page tbody tr.cursor-pointer {
204
+ cursor: pointer;
205
+ }
206
+ .cpq-list-view tbody tr.cursor-pointer:hover,
207
+ .cpq-page tbody tr.cursor-pointer:hover {
208
+ background-color: rgba(0, 0, 0, 0.045);
209
+ }
210
+ .dark .cpq-list-view tbody tr.cursor-pointer:hover,
211
+ .dark .cpq-page tbody tr.cursor-pointer:hover {
212
+ background-color: rgba(255, 255, 255, 0.06);
213
+ }
214
+ .cpq-list-view button:not(:disabled),
215
+ .cpq-list-view [role="button"]:not([aria-disabled="true"]),
216
+ .cpq-list-view a[href],
217
+ .cpq-list-view [data-clickable="true"],
218
+ .cpq-page button:not(:disabled),
219
+ .cpq-page [role="button"]:not([aria-disabled="true"]),
220
+ .cpq-page a[href],
221
+ .cpq-page [data-clickable="true"] {
222
+ cursor: pointer;
223
+ }
224
+ /* Row actions \`\u22EF\` trigger: OM's hover:bg-accent is barely visible in
225
+ the light theme. Make hover (and the open state) clearly distinct so
226
+ users see what they're aiming at. */
227
+ .cpq-list-view [aria-haspopup="menu"]:hover,
228
+ .cpq-page [aria-haspopup="menu"]:hover,
229
+ .cpq-list-view [aria-haspopup="menu"][aria-expanded="true"],
230
+ .cpq-page [aria-haspopup="menu"][aria-expanded="true"] {
231
+ background-color: rgba(0, 0, 0, 0.08);
232
+ }
233
+ .dark .cpq-list-view [aria-haspopup="menu"]:hover,
234
+ .dark .cpq-page [aria-haspopup="menu"]:hover,
235
+ .dark .cpq-list-view [aria-haspopup="menu"][aria-expanded="true"],
236
+ .dark .cpq-page [aria-haspopup="menu"][aria-expanded="true"] {
237
+ background-color: rgba(255, 255, 255, 0.1);
238
+ }
239
+ /* Items inside the row-actions dropdown menu \u2014 strengthen hover so the
240
+ item under the pointer is unmistakable. The menu portal renders at the
241
+ document body, so we can't scope it to .cpq-list-view; targeting the
242
+ role attribute is enough. */
243
+ [role="menu"] [role="menuitem"]:hover,
244
+ [role="menu"] [role="menuitem"]:focus-visible {
245
+ background-color: rgba(0, 0, 0, 0.06);
246
+ }
247
+ .dark [role="menu"] [role="menuitem"]:hover,
248
+ .dark [role="menu"] [role="menuitem"]:focus-visible {
249
+ background-color: rgba(255, 255, 255, 0.08);
250
+ }
251
+ /* Align padding between <a href> menu items (OM's px-2) and the <button>
252
+ variant used for onSelect items (OM's Button size=sm = px-3). Without
253
+ this the Delete label visibly indents past the Edit label. */
254
+ [role="menu"] button[role="menuitem"] {
255
+ padding-left: 0.5rem;
256
+ padding-right: 0.5rem;
257
+ }
258
+ ` });
259
+ }
260
+ export {
261
+ CpqInteractionStyles,
262
+ CpqListView,
263
+ useCpqListData
264
+ };
265
+ //# sourceMappingURL=CpqListView.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/cpq/components/CpqListView.tsx"],
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { DataTable, type BulkAction } from '@open-mercato/ui/backend/DataTable'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { RowActions, type RowActionItem } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useCpqRowActions, type UseCpqRowActionsOptions } from './useCpqRowActions'\n\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype PaginatedResponse<T> = {\n items?: T[]\n total?: number\n totalPages?: number\n}\n\nexport type CpqListData<T> = {\n rows: T[]\n total: number\n totalPages: number\n page: number\n isLoading: boolean\n /** Last successful fetch duration in milliseconds. Surfaces in the\n * pagination footer as `... in 1.5s`, matching OM's standard list views. */\n durationMs: number | null\n search: string\n sorting: SortingState\n filterValues: FilterValues\n setPage: (page: number) => void\n setSearch: (value: string) => void\n setSorting: React.Dispatch<React.SetStateAction<SortingState>>\n setFilterValues: (values: FilterValues) => void\n clearFilters: () => void\n reload: () => void\n queryString: string\n}\n\nexport type UseCpqListDataOptions = {\n /** API endpoint without query string, e.g. `/api/cpq/product-offerings` */\n endpoint: string\n /** Default 50 \u2014 keep at or below 100. */\n pageSize?: number\n /** Initial sort state. Default `[{ id: 'createdAt', desc: true }]`. */\n initialSorting?: SortingState\n /** Map filter values to URL params (only call set on the params arg). */\n buildFilterParams?: (values: FilterValues, params: URLSearchParams) => void\n /** Override the flash message shown when the load fails. */\n loadErrorMessage?: string\n}\n\n/**\n * Server-side paginated list state for CPQ list views.\n *\n * Designed for 100k+ records:\n * - search / sort / filter all go to the server\n * - default pageSize stays at or below 100\n * - debouncing is handled by FilterBar's search input (1s)\n */\nexport function useCpqListData<T extends { id: string }>(\n options: UseCpqListDataOptions,\n): CpqListData<T> {\n const {\n endpoint,\n pageSize = 50,\n initialSorting = [{ id: 'createdAt', desc: true }],\n buildFilterParams,\n loadErrorMessage,\n } = options\n const t = useT()\n\n const [rows, setRows] = React.useState<T[]>([])\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [page, setPageState] = React.useState(1)\n const [isLoading, setIsLoading] = React.useState(true)\n const [durationMs, setDurationMs] = React.useState<number | null>(null)\n const [reloadToken, setReloadToken] = React.useState(0)\n const [search, setSearchState] = React.useState('')\n const [sorting, setSorting] = React.useState<SortingState>(initialSorting)\n const [filterValues, setFilterValuesState] = React.useState<FilterValues>({})\n\n const queryString = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', String(pageSize))\n if (search.trim()) params.set('search', search.trim())\n const sort = sorting[0]\n if (sort?.id) {\n params.set('sortField', sort.id)\n params.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n if (buildFilterParams) buildFilterParams(filterValues, params)\n return params.toString()\n }, [buildFilterParams, filterValues, page, pageSize, search, sorting])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n // Use `performance.now()` so the wall-clock change during the request\n // (NTP, DST, manual clock skew) can never produce a negative duration.\n const startedAt = typeof performance !== 'undefined' ? performance.now() : Date.now()\n try {\n const fallback: PaginatedResponse<T> = { items: [], total: 0, totalPages: 1 }\n const call = await apiCall<PaginatedResponse<T>>(\n `${endpoint}?${queryString}`,\n undefined,\n { fallback },\n )\n if (cancelled) return\n if (!call.ok) {\n flash(loadErrorMessage ?? t('cpq.list.error.load', 'Failed to load'), 'error')\n return\n }\n const payload = call.result ?? fallback\n const items = Array.isArray(payload.items) ? payload.items : []\n setRows(items)\n setTotal(typeof payload.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)\n const finishedAt = typeof performance !== 'undefined' ? performance.now() : Date.now()\n setDurationMs(Math.max(0, finishedAt - startedAt))\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [endpoint, loadErrorMessage, queryString, reloadToken, t])\n\n const setSearch = React.useCallback((value: string) => {\n setSearchState(value)\n setPageState(1)\n }, [])\n\n const setFilterValues = React.useCallback((values: FilterValues) => {\n setFilterValuesState(values)\n setPageState(1)\n }, [])\n\n const clearFilters = React.useCallback(() => {\n setFilterValuesState({})\n setPageState(1)\n }, [])\n\n const reload = React.useCallback(() => {\n setReloadToken((token) => token + 1)\n }, [])\n\n return {\n rows,\n total,\n totalPages,\n page,\n isLoading,\n durationMs,\n search,\n sorting,\n filterValues,\n setPage: setPageState,\n setSearch,\n setSorting,\n setFilterValues,\n clearFilters,\n reload,\n queryString,\n }\n}\n\n// \u2500\u2500\u2500 Component \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Declarative shortcut for the standard CRUD-list row actions.\n *\n * When provided, CpqListView:\n * - registers `useCpqRowActions` internally\n * - renders Edit + Delete in every row's `\u22EF` menu (with optional\n * `extraRowItems` per row prepended / appended)\n * - adds a \"Delete selected\" bulk action (after any `bulkActions` you\n * pass explicitly, so module-specific bulk ops come first)\n * - mounts the confirm-dialog portal in the page footer\n *\n * Pass `rowActions` / `bulkActions` directly instead when the page needs\n * fully custom semantics (e.g. Quotes / Orders have no DELETE endpoint).\n */\nexport type CpqListViewCrudConfig<T extends { id: string }> = Omit<\n UseCpqRowActionsOptions<T>,\n 'onReload'\n> & {\n /**\n * Extra items added to each row's actions menu.\n * `prepend` renders before Edit, `append` between Edit and Delete.\n */\n extraRowItems?: (row: T) => { prepend?: RowActionItem[]; append?: RowActionItem[] }\n}\n\nexport type CpqListViewProps<T extends { id: string }> = {\n /** Page + table title */\n title: React.ReactNode\n /** Stable id for perspectives (e.g. `cpq.offerings.list`) */\n tableId: string\n /** Stable id for DataTable's selection state (defaults to tableId) */\n selectionScopeKey?: string\n /** List state \u2014 typically built from `useCpqListData` in the parent */\n data: CpqListData<T>\n /** Table columns */\n columns: ColumnDef<T>[]\n /** Filter definitions for the FilterBar overlay */\n filters?: FilterDef[]\n /** Search input placeholder */\n searchPlaceholder?: string\n /** Header actions (e.g. Create button) */\n actions?: React.ReactNode\n /** Bulk actions toolbar shown when rows are selected */\n bulkActions?: BulkAction<T>[]\n /** Per-row action menu */\n rowActions?: (row: T) => React.ReactNode\n /**\n * Declarative CRUD config. When set, CpqListView produces the standard\n * `\u22EF \u2192 Edit / Delete` menu, a \"Delete selected\" bulk action, and mounts\n * the confirm-dialog portal \u2014 replacing the ~30 lines of boilerplate\n * each list page used to repeat. Override `rowActions` / `bulkActions`\n * to take full control instead.\n */\n crud?: CpqListViewCrudConfig<T>\n /** Click handler for whole row (typically navigates to detail) */\n onRowClick?: (row: T) => void\n /** Custom empty state */\n emptyState?: React.ReactNode\n /** Pagination size (must match `useCpqListData` pageSize) */\n pageSize?: number\n /**\n * Custom content rendered ABOVE the DataTable inside the same Page wrapper.\n * Use for inline forms, customer pickers, tab switchers, alerts.\n */\n toolbarContent?: React.ReactNode\n /**\n * Custom content rendered BELOW the DataTable (e.g. confirm dialog portals).\n */\n footerContent?: React.ReactNode\n}\n\n/**\n * Generic CPQ list view. Wraps DataTable + FilterBar + Page in the\n * Open Mercato standard, with stronger row-hover affordance so users\n * always know what's clickable.\n */\nexport function CpqListView<T extends { id: string }>({\n title,\n tableId,\n selectionScopeKey,\n data,\n columns,\n filters,\n searchPlaceholder,\n actions,\n bulkActions,\n rowActions,\n crud,\n onRowClick,\n emptyState,\n pageSize = 50,\n toolbarContent,\n footerContent,\n}: CpqListViewProps<T>) {\n const t = useT()\n\n // Wire `crud` into RowActions + bulkActions + footer confirm dialog so the\n // call-site stays a single declarative config block.\n // The hook is always called (Rules of Hooks); the helpers below decide\n // whether to actually use its output based on the presence of `crud`.\n const crudActions = useCpqRowActions<T>({\n endpoint: crud?.endpoint ?? '',\n entityName: crud?.entityName ?? 'item',\n editHref: crud?.editHref ?? (() => '#'),\n onReload: data.reload,\n canDelete: crud?.canDelete,\n })\n\n const resolvedRowActions = React.useMemo(() => {\n if (rowActions) return rowActions\n if (!crud) return undefined\n return (row: T) => (\n <RowActions items={crudActions.buildItems(row, crud.extraRowItems?.(row))} />\n )\n }, [crud, crudActions, rowActions])\n\n const resolvedBulkActions = React.useMemo<BulkAction<T>[] | undefined>(() => {\n if (!crud) return bulkActions\n const deleteAction: BulkAction<T> = {\n id: 'delete',\n label: t('cpq.list.bulk.deleteSelected', 'Delete selected'),\n destructive: true,\n onExecute: crudActions.bulkDelete,\n }\n return bulkActions ? [...bulkActions, deleteAction] : [deleteAction]\n }, [bulkActions, crud, crudActions.bulkDelete, t])\n\n const resolvedFooter = crud ? (\n <>\n {footerContent}\n {crudActions.ConfirmDialogElement}\n </>\n ) : (\n footerContent\n )\n\n return (\n <Page>\n <PageBody className=\"cpq-list-view space-y-6\">\n <CpqInteractionStyles />\n {toolbarContent}\n <DataTable<T>\n title={title}\n actions={actions}\n refreshButton={{\n label: t('cpq.list.actions.refresh', 'Refresh'),\n onRefresh: data.reload,\n isRefreshing: data.isLoading,\n }}\n columns={columns}\n data={data.rows}\n searchValue={data.search}\n onSearchChange={data.setSearch}\n searchPlaceholder={searchPlaceholder ?? t('cpq.list.search.placeholder', 'Search...')}\n filters={filters}\n filterValues={data.filterValues}\n onFiltersApply={data.setFilterValues}\n onFiltersClear={data.clearFilters}\n sorting={data.sorting}\n onSortingChange={data.setSorting}\n bulkActions={resolvedBulkActions}\n selectionScopeKey={selectionScopeKey ?? tableId}\n onRowClick={onRowClick}\n rowActions={resolvedRowActions}\n perspective={{ tableId }}\n columnChooser={{ auto: true }}\n pagination={{\n page: data.page,\n pageSize,\n total: data.total,\n totalPages: data.totalPages,\n onPageChange: data.setPage,\n durationMs: data.durationMs,\n }}\n isLoading={data.isLoading}\n emptyState={emptyState}\n />\n {resolvedFooter}\n </PageBody>\n </Page>\n )\n}\n\n// \u2500\u2500\u2500 Shared interaction styles \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Inline style block applied inside `.cpq-list-view` (and any `.cpq-page`)\n * wrappers. Strengthens the OM hover affordance so users can clearly tell\n * which row their mouse is on, and forces `cursor: pointer` on every\n * clickable element inside CPQ pages.\n *\n * Kept inline to avoid coupling the package to a global stylesheet.\n */\nexport function CpqInteractionStyles() {\n return (\n <style>{`\n.cpq-list-view tbody tr.cursor-pointer,\n.cpq-page tbody tr.cursor-pointer {\n cursor: pointer;\n}\n.cpq-list-view tbody tr.cursor-pointer:hover,\n.cpq-page tbody tr.cursor-pointer:hover {\n background-color: rgba(0, 0, 0, 0.045);\n}\n.dark .cpq-list-view tbody tr.cursor-pointer:hover,\n.dark .cpq-page tbody tr.cursor-pointer:hover {\n background-color: rgba(255, 255, 255, 0.06);\n}\n.cpq-list-view button:not(:disabled),\n.cpq-list-view [role=\"button\"]:not([aria-disabled=\"true\"]),\n.cpq-list-view a[href],\n.cpq-list-view [data-clickable=\"true\"],\n.cpq-page button:not(:disabled),\n.cpq-page [role=\"button\"]:not([aria-disabled=\"true\"]),\n.cpq-page a[href],\n.cpq-page [data-clickable=\"true\"] {\n cursor: pointer;\n}\n/* Row actions \\`\u22EF\\` trigger: OM's hover:bg-accent is barely visible in\n the light theme. Make hover (and the open state) clearly distinct so\n users see what they're aiming at. */\n.cpq-list-view [aria-haspopup=\"menu\"]:hover,\n.cpq-page [aria-haspopup=\"menu\"]:hover,\n.cpq-list-view [aria-haspopup=\"menu\"][aria-expanded=\"true\"],\n.cpq-page [aria-haspopup=\"menu\"][aria-expanded=\"true\"] {\n background-color: rgba(0, 0, 0, 0.08);\n}\n.dark .cpq-list-view [aria-haspopup=\"menu\"]:hover,\n.dark .cpq-page [aria-haspopup=\"menu\"]:hover,\n.dark .cpq-list-view [aria-haspopup=\"menu\"][aria-expanded=\"true\"],\n.dark .cpq-page [aria-haspopup=\"menu\"][aria-expanded=\"true\"] {\n background-color: rgba(255, 255, 255, 0.1);\n}\n/* Items inside the row-actions dropdown menu \u2014 strengthen hover so the\n item under the pointer is unmistakable. The menu portal renders at the\n document body, so we can't scope it to .cpq-list-view; targeting the\n role attribute is enough. */\n[role=\"menu\"] [role=\"menuitem\"]:hover,\n[role=\"menu\"] [role=\"menuitem\"]:focus-visible {\n background-color: rgba(0, 0, 0, 0.06);\n}\n.dark [role=\"menu\"] [role=\"menuitem\"]:hover,\n.dark [role=\"menu\"] [role=\"menuitem\"]:focus-visible {\n background-color: rgba(255, 255, 255, 0.08);\n}\n/* Align padding between <a href> menu items (OM's px-2) and the <button>\n variant used for onSelect items (OM's Button size=sm = px-3). Without\n this the Delete label visibly indents past the Edit label. */\n[role=\"menu\"] button[role=\"menuitem\"] {\n padding-left: 0.5rem;\n padding-right: 0.5rem;\n}\n`}</style>\n )\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,95 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Input } from "@open-mercato/ui/primitives/input";
5
+ function isIntermediateInput(raw, integer) {
6
+ if (raw === "" || raw === "-") return true;
7
+ if (!integer && (raw.endsWith(".") || raw.endsWith("e") || raw.endsWith("e-"))) return true;
8
+ return false;
9
+ }
10
+ const NumberInput = React.forwardRef(
11
+ function NumberInput2({
12
+ value,
13
+ onChange,
14
+ selectOnFocus = true,
15
+ integer = false,
16
+ min,
17
+ max,
18
+ onFocus,
19
+ onBlur,
20
+ ...rest
21
+ }, ref) {
22
+ const [text, setText] = React.useState(value == null ? "" : String(value));
23
+ const lastEmittedRef = React.useRef(value);
24
+ React.useEffect(() => {
25
+ if (lastEmittedRef.current === value) return;
26
+ setText(value == null ? "" : String(value));
27
+ lastEmittedRef.current = value;
28
+ }, [value]);
29
+ const clamp = React.useCallback(
30
+ (n) => {
31
+ let result = n;
32
+ if (typeof min === "number" && result < min) result = min;
33
+ if (typeof max === "number" && result > max) result = max;
34
+ return result;
35
+ },
36
+ [max, min]
37
+ );
38
+ const handleChange = React.useCallback(
39
+ (e) => {
40
+ const raw = e.currentTarget.value;
41
+ const allowed = integer ? /[^\d-]/g : /[^\d.\-eE]/g;
42
+ const cleaned = raw.replace(allowed, "");
43
+ if (isIntermediateInput(cleaned, integer)) {
44
+ setText(cleaned);
45
+ if (lastEmittedRef.current !== null) {
46
+ lastEmittedRef.current = null;
47
+ onChange(null);
48
+ }
49
+ return;
50
+ }
51
+ const parsed = integer ? parseInt(cleaned, 10) : parseFloat(cleaned);
52
+ if (Number.isNaN(parsed)) return;
53
+ setText(cleaned);
54
+ const next = clamp(parsed);
55
+ if (lastEmittedRef.current !== next) {
56
+ lastEmittedRef.current = next;
57
+ onChange(next);
58
+ }
59
+ },
60
+ [clamp, integer, onChange]
61
+ );
62
+ const handleBlur = React.useCallback(
63
+ (e) => {
64
+ if (lastEmittedRef.current == null) {
65
+ if (text !== "") setText("");
66
+ } else {
67
+ const normalised = String(lastEmittedRef.current);
68
+ if (text !== normalised) setText(normalised);
69
+ }
70
+ onBlur?.(e);
71
+ },
72
+ [onBlur, text]
73
+ );
74
+ return /* @__PURE__ */ jsx(
75
+ Input,
76
+ {
77
+ ref,
78
+ ...rest,
79
+ type: "text",
80
+ inputMode: integer ? "numeric" : "decimal",
81
+ value: text,
82
+ onFocus: (e) => {
83
+ if (selectOnFocus) e.currentTarget.select();
84
+ onFocus?.(e);
85
+ },
86
+ onChange: handleChange,
87
+ onBlur: handleBlur
88
+ }
89
+ );
90
+ }
91
+ );
92
+ export {
93
+ NumberInput
94
+ };
95
+ //# sourceMappingURL=NumberInput.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/cpq/components/NumberInput.tsx"],
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { Input, type InputProps } from '@open-mercato/ui/primitives/input'\n\n// \u2500\u2500\u2500 Why this exists \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Plain `<input type=\"number\" value={state}>` is hostile when `state === 0`:\n// the field renders \"0\", the caret lands AFTER it, and typing \"5\" produces\n// \"05\" which most browsers normalise back to `5` \u2014 the user typed five but\n// the field swallowed the digit they wanted to keep. Same trap when editing\n// any single-digit existing value.\n//\n// NumberInput fixes this with two layered behaviours:\n//\n// 1. **Select on focus.** Tabbing or clicking into the field selects the\n// whole text, so the next keystroke replaces the existing value\n// instead of prepending to it. This single change removes ~95 % of the\n// friction without changing the controlled-component contract.\n//\n// 2. **Internal string state.** The input is rendered with a string we\n// own, not `String(numericState)`. That lets intermediate edits like\n// `\"-\"`, `\"1.\"`, or empty stay visible while the parent only ever sees\n// a parsed `number | null`. Parent updates (programmatic reset after\n// save, form initial load) still flow in via the effect below.\n//\n// It also drops `type=\"number\"` in favour of `type=\"text\"` +\n// `inputMode={'numeric' | 'decimal'}` so we get the right mobile keyboard\n// without the spinner buttons and the silent step/min/max coercion that\n// HTML number inputs apply on blur.\n\nexport type NumberInputProps = Omit<\n InputProps,\n 'value' | 'onChange' | 'type' | 'inputMode' | 'defaultValue'\n> & {\n /**\n * Numeric value. `null`/`undefined` renders the field empty \u2014 that's how\n * callers signal \"no value yet\" without colliding with `0` as a real\n * default.\n */\n value: number | null | undefined\n /** Called with the parsed number, or `null` when the field is cleared. */\n onChange: (value: number | null) => void\n /** Selects the input contents on focus. Default true. */\n selectOnFocus?: boolean\n /** Restrict input to integers (rejects `.`, `e`, etc). Default false. */\n integer?: boolean\n /** Optional minimum (enforced on commit, not while typing). */\n min?: number\n /** Optional maximum (enforced on commit, not while typing). */\n max?: number\n}\n\nfunction isIntermediateInput(raw: string, integer: boolean): boolean {\n // Allow the user to type these without us replacing the field mid-edit.\n // - empty / lone `-` \u2192 not parseable yet\n // - trailing `.` for decimals \u2192 not parseable yet\n // - trailing `e` / `e-` \u2192 scientific notation in progress\n if (raw === '' || raw === '-') return true\n if (!integer && (raw.endsWith('.') || raw.endsWith('e') || raw.endsWith('e-'))) return true\n return false\n}\n\nexport const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(\n function NumberInput(\n {\n value,\n onChange,\n selectOnFocus = true,\n integer = false,\n min,\n max,\n onFocus,\n onBlur,\n ...rest\n },\n ref,\n ) {\n const [text, setText] = React.useState<string>(value == null ? '' : String(value))\n const lastEmittedRef = React.useRef<number | null | undefined>(value)\n\n // Sync external updates (form reset after save, initial load, etc.) into\n // the local text state \u2014 but only when the parent's value actually\n // diverges from what we last emitted, so we don't clobber intermediate\n // text like \"1.\" while the user is mid-edit.\n React.useEffect(() => {\n if (lastEmittedRef.current === value) return\n setText(value == null ? '' : String(value))\n lastEmittedRef.current = value\n }, [value])\n\n const clamp = React.useCallback(\n (n: number): number => {\n let result = n\n if (typeof min === 'number' && result < min) result = min\n if (typeof max === 'number' && result > max) result = max\n return result\n },\n [max, min],\n )\n\n const handleChange = React.useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const raw = e.currentTarget.value\n\n // Strip characters that are never valid in our numeric formats; this\n // lets us keep `type=\"text\"` while still rejecting alphabetic input.\n const allowed = integer ? /[^\\d-]/g : /[^\\d.\\-eE]/g\n const cleaned = raw.replace(allowed, '')\n\n if (isIntermediateInput(cleaned, integer)) {\n setText(cleaned)\n // Treat intermediates as \"no committed value yet\" \u2014 emit null so\n // the parent doesn't store a half-typed number. The full value\n // commits on the next valid keystroke or on blur.\n if (lastEmittedRef.current !== null) {\n lastEmittedRef.current = null\n onChange(null)\n }\n return\n }\n\n const parsed = integer ? parseInt(cleaned, 10) : parseFloat(cleaned)\n if (Number.isNaN(parsed)) return // Reject \u2014 keep previous state.\n\n setText(cleaned)\n const next = clamp(parsed)\n if (lastEmittedRef.current !== next) {\n lastEmittedRef.current = next\n onChange(next)\n }\n },\n [clamp, integer, onChange],\n )\n\n const handleBlur = React.useCallback(\n (e: React.FocusEvent<HTMLInputElement>) => {\n // On blur, normalise the displayed text to match the committed value\n // (e.g. \"01\" \u2192 \"1\", \"1.\" \u2192 \"1\"). Empty stays empty.\n if (lastEmittedRef.current == null) {\n if (text !== '') setText('')\n } else {\n const normalised = String(lastEmittedRef.current)\n if (text !== normalised) setText(normalised)\n }\n onBlur?.(e)\n },\n [onBlur, text],\n )\n\n return (\n <Input\n ref={ref}\n {...rest}\n type=\"text\"\n inputMode={integer ? 'numeric' : 'decimal'}\n value={text}\n onFocus={(e) => {\n if (selectOnFocus) e.currentTarget.select()\n onFocus?.(e)\n }}\n onChange={handleChange}\n onBlur={handleBlur}\n />\n )\n },\n)\n"],
5
+ "mappings": ";AAsJM;AArJN,YAAY,WAAW;AACvB,SAAS,aAA8B;AAkDvC,SAAS,oBAAoB,KAAa,SAA2B;AAKnE,MAAI,QAAQ,MAAM,QAAQ,IAAK,QAAO;AACtC,MAAI,CAAC,YAAY,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS,IAAI,GAAI,QAAO;AACvF,SAAO;AACT;AAEO,MAAM,cAAc,MAAM;AAAA,EAC/B,SAASA,aACP;AAAA,IACE;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GACA,KACA;AACA,UAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAiB,SAAS,OAAO,KAAK,OAAO,KAAK,CAAC;AACjF,UAAM,iBAAiB,MAAM,OAAkC,KAAK;AAMpE,UAAM,UAAU,MAAM;AACpB,UAAI,eAAe,YAAY,MAAO;AACtC,cAAQ,SAAS,OAAO,KAAK,OAAO,KAAK,CAAC;AAC1C,qBAAe,UAAU;AAAA,IAC3B,GAAG,CAAC,KAAK,CAAC;AAEV,UAAM,QAAQ,MAAM;AAAA,MAClB,CAAC,MAAsB;AACrB,YAAI,SAAS;AACb,YAAI,OAAO,QAAQ,YAAY,SAAS,IAAK,UAAS;AACtD,YAAI,OAAO,QAAQ,YAAY,SAAS,IAAK,UAAS;AACtD,eAAO;AAAA,MACT;AAAA,MACA,CAAC,KAAK,GAAG;AAAA,IACX;AAEA,UAAM,eAAe,MAAM;AAAA,MACzB,CAAC,MAA2C;AAC1C,cAAM,MAAM,EAAE,cAAc;AAI5B,cAAM,UAAU,UAAU,YAAY;AACtC,cAAM,UAAU,IAAI,QAAQ,SAAS,EAAE;AAEvC,YAAI,oBAAoB,SAAS,OAAO,GAAG;AACzC,kBAAQ,OAAO;AAIf,cAAI,eAAe,YAAY,MAAM;AACnC,2BAAe,UAAU;AACzB,qBAAS,IAAI;AAAA,UACf;AACA;AAAA,QACF;AAEA,cAAM,SAAS,UAAU,SAAS,SAAS,EAAE,IAAI,WAAW,OAAO;AACnE,YAAI,OAAO,MAAM,MAAM,EAAG;AAE1B,gBAAQ,OAAO;AACf,cAAM,OAAO,MAAM,MAAM;AACzB,YAAI,eAAe,YAAY,MAAM;AACnC,yBAAe,UAAU;AACzB,mBAAS,IAAI;AAAA,QACf;AAAA,MACF;AAAA,MACA,CAAC,OAAO,SAAS,QAAQ;AAAA,IAC3B;AAEA,UAAM,aAAa,MAAM;AAAA,MACvB,CAAC,MAA0C;AAGzC,YAAI,eAAe,WAAW,MAAM;AAClC,cAAI,SAAS,GAAI,SAAQ,EAAE;AAAA,QAC7B,OAAO;AACL,gBAAM,aAAa,OAAO,eAAe,OAAO;AAChD,cAAI,SAAS,WAAY,SAAQ,UAAU;AAAA,QAC7C;AACA,iBAAS,CAAC;AAAA,MACZ;AAAA,MACA,CAAC,QAAQ,IAAI;AAAA,IACf;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACC,GAAG;AAAA,QACJ,MAAK;AAAA,QACL,WAAW,UAAU,YAAY;AAAA,QACjC,OAAO;AAAA,QACP,SAAS,CAAC,MAAM;AACd,cAAI,cAAe,GAAE,cAAc,OAAO;AAC1C,oBAAU,CAAC;AAAA,QACb;AAAA,QACA,UAAU;AAAA,QACV,QAAQ;AAAA;AAAA,IACV;AAAA,EAEJ;AACF;",
6
+ "names": ["NumberInput"]
7
+ }