@mohasinac/appkit 4.59.0 โ†’ 4.60.1

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 (532) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +62 -5
  3. package/dist/_internal/client/features/maintenance/views/MaintenanceDashboardView.js +2 -2
  4. package/dist/_internal/server/features/brands/actions.js +78 -4
  5. package/dist/_internal/server/features/checkout/actions.d.ts +36 -13
  6. package/dist/_internal/server/features/checkout/actions.js +668 -183
  7. package/dist/_internal/server/features/checkout/supplier-identity.js +18 -1
  8. package/dist/_internal/server/features/gateway-events/index.d.ts +10 -0
  9. package/dist/_internal/server/features/gateway-events/index.js +10 -0
  10. package/dist/_internal/server/features/gateway-events/ingest.d.ts +127 -0
  11. package/dist/_internal/server/features/gateway-events/ingest.js +188 -0
  12. package/dist/_internal/server/features/gateway-events/redact.d.ts +66 -0
  13. package/dist/_internal/server/features/gateway-events/redact.js +151 -0
  14. package/dist/_internal/server/features/grievance/actions.d.ts +63 -0
  15. package/dist/_internal/server/features/grievance/actions.js +173 -0
  16. package/dist/_internal/server/features/grievance/index.d.ts +8 -0
  17. package/dist/_internal/server/features/grievance/index.js +8 -0
  18. package/dist/_internal/server/features/grouped/data.js +28 -3
  19. package/dist/_internal/server/features/lottery/actions.js +99 -4
  20. package/dist/_internal/server/features/order-completion/actions.d.ts +92 -0
  21. package/dist/_internal/server/features/order-completion/actions.js +336 -0
  22. package/dist/_internal/server/features/order-completion/index.d.ts +7 -0
  23. package/dist/_internal/server/features/order-completion/index.js +7 -0
  24. package/dist/_internal/server/features/orders/actions.d.ts +13 -0
  25. package/dist/_internal/server/features/orders/actions.js +56 -2
  26. package/dist/_internal/server/features/orders/adapters.d.ts +20 -2
  27. package/dist/_internal/server/features/orders/adapters.js +44 -0
  28. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +116 -0
  29. package/dist/_internal/server/features/orders/cancellation-refund.js +466 -0
  30. package/dist/_internal/server/features/orders/index.d.ts +2 -1
  31. package/dist/_internal/server/features/orders/index.js +1 -1
  32. package/dist/_internal/server/features/orders/service.d.ts +60 -0
  33. package/dist/_internal/server/features/orders/service.js +74 -0
  34. package/dist/_internal/server/features/payment-cases/disputes.d.ts +76 -0
  35. package/dist/_internal/server/features/payment-cases/disputes.js +241 -0
  36. package/dist/_internal/server/features/payment-cases/index.d.ts +9 -0
  37. package/dist/_internal/server/features/payment-cases/index.js +9 -0
  38. package/dist/_internal/server/features/payouts/actions.d.ts +28 -3
  39. package/dist/_internal/server/features/payouts/actions.js +84 -10
  40. package/dist/_internal/server/features/points/adjust.d.ts +61 -0
  41. package/dist/_internal/server/features/points/adjust.js +121 -0
  42. package/dist/_internal/server/features/points/award.d.ts +65 -0
  43. package/dist/_internal/server/features/points/award.js +145 -0
  44. package/dist/_internal/server/features/price-index/market.d.ts +33 -0
  45. package/dist/_internal/server/features/price-index/market.js +54 -1
  46. package/dist/_internal/server/features/products/data.js +40 -22
  47. package/dist/_internal/server/features/protection-claims/actions.d.ts +72 -0
  48. package/dist/_internal/server/features/protection-claims/actions.js +228 -0
  49. package/dist/_internal/server/features/protection-claims/index.d.ts +8 -0
  50. package/dist/_internal/server/features/protection-claims/index.js +8 -0
  51. package/dist/_internal/server/features/refunds/actions.d.ts +20 -0
  52. package/dist/_internal/server/features/refunds/actions.js +259 -17
  53. package/dist/_internal/server/features/refunds/capacity.d.ts +29 -0
  54. package/dist/_internal/server/features/refunds/capacity.js +37 -0
  55. package/dist/_internal/server/features/reviews/actions.js +3 -2
  56. package/dist/_internal/server/features/search/index-build/sources.js +20 -1
  57. package/dist/_internal/server/features/shipping/reconcile.d.ts +10 -1
  58. package/dist/_internal/server/features/shipping/reconcile.js +34 -3
  59. package/dist/_internal/server/features/shipping/reimbursement.d.ts +117 -0
  60. package/dist/_internal/server/features/shipping/reimbursement.js +223 -0
  61. package/dist/_internal/server/features/stores/tabs.d.ts +19 -4
  62. package/dist/_internal/server/features/stores/tabs.js +24 -4
  63. package/dist/_internal/server/functions/https.d.ts +31 -1
  64. package/dist/_internal/server/functions/https.js +52 -1
  65. package/dist/_internal/server/functions/scheduled.d.ts +72 -1
  66. package/dist/_internal/server/functions/scheduled.js +124 -1
  67. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +62 -16
  68. package/dist/_internal/server/jobs/core/boxRollupReconcile.d.ts +46 -0
  69. package/dist/_internal/server/jobs/core/boxRollupReconcile.js +142 -0
  70. package/dist/_internal/server/jobs/core/dailyDataCleanup.js +149 -1
  71. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +14 -0
  72. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +79 -1
  73. package/dist/_internal/server/jobs/core/index.d.ts +2 -0
  74. package/dist/_internal/server/jobs/core/index.js +2 -0
  75. package/dist/_internal/server/jobs/core/marketPriceRollup.d.ts +64 -0
  76. package/dist/_internal/server/jobs/core/marketPriceRollup.js +153 -0
  77. package/dist/_internal/server/jobs/core/obligationSweep.d.ts +36 -0
  78. package/dist/_internal/server/jobs/core/obligationSweep.js +503 -0
  79. package/dist/_internal/server/jobs/core/paymentReconcile.d.ts +79 -0
  80. package/dist/_internal/server/jobs/core/paymentReconcile.js +614 -0
  81. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +34 -0
  82. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +35 -4
  83. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.d.ts +2 -0
  84. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.js +15 -0
  85. package/dist/_internal/server/jobs/handlers/countersReconcileNow.d.ts +65 -0
  86. package/dist/_internal/server/jobs/handlers/countersReconcileNow.js +89 -0
  87. package/dist/_internal/server/jobs/handlers/index.d.ts +5 -0
  88. package/dist/_internal/server/jobs/handlers/index.js +5 -0
  89. package/dist/_internal/server/jobs/handlers/marketPriceRollup.d.ts +2 -0
  90. package/dist/_internal/server/jobs/handlers/marketPriceRollup.js +2 -0
  91. package/dist/_internal/server/jobs/handlers/obligationSweep.d.ts +2 -0
  92. package/dist/_internal/server/jobs/handlers/obligationSweep.js +15 -0
  93. package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +30 -1
  94. package/dist/_internal/server/jobs/handlers/paymentReconcile.d.ts +2 -0
  95. package/dist/_internal/server/jobs/handlers/paymentReconcile.js +6 -0
  96. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  97. package/dist/_internal/shared/actions/action-registry.js +71 -0
  98. package/dist/_internal/shared/checkout/coupon-discount.d.ts +73 -0
  99. package/dist/_internal/shared/checkout/coupon-discount.js +103 -0
  100. package/dist/_internal/shared/checkout/lanes.d.ts +42 -0
  101. package/dist/_internal/shared/checkout/lanes.js +46 -0
  102. package/dist/_internal/shared/checkout/order-math.d.ts +149 -1
  103. package/dist/_internal/shared/checkout/order-math.js +251 -3
  104. package/dist/_internal/shared/checkout/refundable-base.d.ts +116 -0
  105. package/dist/_internal/shared/checkout/refundable-base.js +122 -0
  106. package/dist/_internal/shared/features/auctions/config.d.ts +14 -1
  107. package/dist/_internal/shared/features/auctions/config.js +30 -1
  108. package/dist/_internal/shared/features/brands/schema.d.ts +42 -0
  109. package/dist/_internal/shared/features/brands/schema.js +18 -0
  110. package/dist/_internal/shared/features/orders/refund-copy.d.ts +22 -0
  111. package/dist/_internal/shared/features/orders/refund-copy.js +34 -0
  112. package/dist/_internal/shared/features/print/label-profiles.d.ts +198 -0
  113. package/dist/_internal/shared/features/print/label-profiles.js +350 -0
  114. package/dist/_internal/shared/features/products/to-product-item.js +33 -5
  115. package/dist/_internal/shared/fees/calculator.d.ts +31 -2
  116. package/dist/_internal/shared/fees/calculator.js +103 -5
  117. package/dist/_internal/shared/fees/india-states.d.ts +195 -0
  118. package/dist/_internal/shared/fees/india-states.js +190 -0
  119. package/dist/_internal/shared/fees/payout-hold.d.ts +80 -0
  120. package/dist/_internal/shared/fees/payout-hold.js +74 -0
  121. package/dist/_internal/shared/obligations/deadlines.d.ts +94 -0
  122. package/dist/_internal/shared/obligations/deadlines.js +155 -0
  123. package/dist/_internal/shared/shipping/quote.d.ts +96 -0
  124. package/dist/_internal/shared/shipping/quote.js +67 -0
  125. package/dist/_internal/shared/time/business-days.d.ts +91 -0
  126. package/dist/_internal/shared/time/business-days.js +185 -0
  127. package/dist/cli/index.js +0 -2
  128. package/dist/client.d.ts +10 -4
  129. package/dist/client.js +29 -3
  130. package/dist/configs/tailwind.d.ts +0 -12
  131. package/dist/configs/tailwind.js +28 -15
  132. package/dist/constants/api-endpoints.d.ts +51 -9
  133. package/dist/constants/api-endpoints.js +23 -7
  134. package/dist/constants/index.d.ts +1 -1
  135. package/dist/constants/index.js +1 -1
  136. package/dist/contracts/index.d.ts +1 -1
  137. package/dist/contracts/payment.d.ts +131 -0
  138. package/dist/features/about/components/AboutView.js +1 -1
  139. package/dist/features/about/components/DeveloperView.js +5 -2
  140. package/dist/features/about/components/FAQPageView.js +2 -1
  141. package/dist/features/about/components/FeesView.js +2 -1
  142. package/dist/features/about/components/HelpPageView.js +2 -1
  143. package/dist/features/about/components/HowAuctionsWorkView.js +2 -1
  144. package/dist/features/about/components/HowCheckoutWorksView.js +2 -1
  145. package/dist/features/about/components/HowEmiWorksView.js +2 -1
  146. package/dist/features/about/components/HowOffersWorkView.js +2 -1
  147. package/dist/features/about/components/HowOrdersWorkView.js +2 -1
  148. package/dist/features/about/components/HowPayoutsWorkView.js +2 -1
  149. package/dist/features/about/components/HowPreOrdersWorkView.js +2 -1
  150. package/dist/features/about/components/HowReviewsWorkView.js +2 -1
  151. package/dist/features/about/components/PolicyPageView.js +2 -1
  152. package/dist/features/about/components/ProseHero.d.ts +73 -0
  153. package/dist/features/about/components/ProseHero.js +18 -0
  154. package/dist/features/about/components/SecurityPrivacyView.js +2 -1
  155. package/dist/features/about/components/ShippingPolicyView.js +2 -1
  156. package/dist/features/about/components/TrackOrderView.js +2 -1
  157. package/dist/features/about/components/UnauthorizedView.js +1 -1
  158. package/dist/features/about/index.d.ts +2 -0
  159. package/dist/features/about/index.js +5 -0
  160. package/dist/features/account/actions/address-actions.d.ts +26 -2
  161. package/dist/features/account/actions/address-actions.js +25 -1
  162. package/dist/features/account/components/BuyerShoppingGuideView.js +1 -1
  163. package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
  164. package/dist/features/account/components/UserOrdersView.js +17 -0
  165. package/dist/features/account/components/UserSidebar.js +1 -1
  166. package/dist/features/account/hooks/useNotifications.js +29 -0
  167. package/dist/features/admin/actions/admin-coupon-actions.js +36 -0
  168. package/dist/features/admin/components/AdminBeysView.js +2 -2
  169. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  170. package/dist/features/admin/components/AdminBrandEditorView.js +32 -2
  171. package/dist/features/admin/components/AdminBundleEditorView.js +1 -1
  172. package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
  173. package/dist/features/admin/components/AdminCategoriesView.js +1 -1
  174. package/dist/features/admin/components/AdminCategoryEditorView.js +73 -17
  175. package/dist/features/admin/components/AdminContactEditorView.js +1 -1
  176. package/dist/features/admin/components/AdminCouponEditorView.js +26 -1
  177. package/dist/features/admin/components/AdminFaqEditorView.js +1 -1
  178. package/dist/features/admin/components/AdminFaqsView.js +2 -2
  179. package/dist/features/admin/components/AdminGatewayEventsView.d.ts +54 -0
  180. package/dist/features/admin/components/AdminGatewayEventsView.js +209 -0
  181. package/dist/features/admin/components/AdminGuideHubView.js +1 -1
  182. package/dist/features/admin/components/AdminMoneyCasesView.d.ts +60 -0
  183. package/dist/features/admin/components/AdminMoneyCasesView.js +287 -0
  184. package/dist/features/admin/components/AdminPartsView.js +2 -2
  185. package/dist/features/admin/components/AdminProductsView.js +75 -4
  186. package/dist/features/admin/components/AdminSidebar.js +2 -2
  187. package/dist/features/admin/components/AdminSiteSettingsView.js +65 -7
  188. package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
  189. package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
  190. package/dist/features/admin/components/AdminSupportTicketsView.js +2 -2
  191. package/dist/features/admin/components/AdminTesterChecklistView.js +2 -2
  192. package/dist/features/admin/components/AdminUsersView.js +72 -1
  193. package/dist/features/admin/components/AdminViewCards.d.ts +3 -1
  194. package/dist/features/admin/components/AdminViewCards.js +4 -4
  195. package/dist/features/admin/components/DataListingView.d.ts +28 -0
  196. package/dist/features/admin/components/DataListingView.js +175 -14
  197. package/dist/features/admin/components/DataTable.d.ts +12 -1
  198. package/dist/features/admin/components/DataTable.js +3 -3
  199. package/dist/features/admin/components/OwnerQuickActions.d.ts +1 -1
  200. package/dist/features/admin/components/OwnerQuickActions.js +60 -2
  201. package/dist/features/admin/components/RecordOwnerQuickActions.d.ts +1 -1
  202. package/dist/features/admin/components/RecordOwnerQuickActions.js +61 -14
  203. package/dist/features/admin/components/analytics/AdminStatCard.d.ts +35 -1
  204. package/dist/features/admin/components/analytics/AdminStatCard.js +77 -2
  205. package/dist/features/admin/components/index.d.ts +4 -0
  206. package/dist/features/admin/components/index.js +6 -0
  207. package/dist/features/admin/constants/filter-tabs.d.ts +156 -0
  208. package/dist/features/admin/constants/filter-tabs.js +106 -0
  209. package/dist/features/admin/repository/site-settings.repository.d.ts +22 -0
  210. package/dist/features/admin/repository/site-settings.repository.js +82 -1
  211. package/dist/features/admin/schemas/admin-editor-forms.d.ts +21 -0
  212. package/dist/features/admin/schemas/admin-editor-forms.js +44 -0
  213. package/dist/features/admin/schemas/firestore.js +5 -1
  214. package/dist/features/admin/schemas/site-settings-form.js +30 -1
  215. package/dist/features/auctions/actions/bid-actions.js +13 -1
  216. package/dist/features/auctions/components/AuctionBottomActions.js +2 -2
  217. package/dist/features/auctions/components/AuctionDetailPageView.js +3 -1
  218. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -0
  219. package/dist/features/auctions/components/MarketplaceAuctionCard.js +5 -3
  220. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +8 -1
  221. package/dist/features/auctions/components/PlaceBidFormClient.js +2 -2
  222. package/dist/features/audit-log/schemas/firestore.d.ts +28 -0
  223. package/dist/features/audit-log/schemas/firestore.js +14 -0
  224. package/dist/features/auth/permissions/constants.d.ts +17 -1
  225. package/dist/features/auth/permissions/constants.js +18 -0
  226. package/dist/features/blog/components/BlogPostView.js +1 -1
  227. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
  228. package/dist/features/categories/api/[id]/description-template/route.js +107 -1
  229. package/dist/features/categories/components/BrandDetailPageView.js +2 -2
  230. package/dist/features/categories/components/BreadcrumbTrail.js +7 -1
  231. package/dist/features/categories/components/BundleDetailView.js +1 -1
  232. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  233. package/dist/features/categories/components/CategoryForm.js +12 -1
  234. package/dist/features/categories/components/CategoryTemplatePicker.js +38 -5
  235. package/dist/features/categories/repository/market-price-index.repository.d.ts +123 -0
  236. package/dist/features/categories/repository/market-price-index.repository.js +105 -0
  237. package/dist/features/categories/repository/price-observation.repository.d.ts +166 -0
  238. package/dist/features/categories/repository/price-observation.repository.js +132 -0
  239. package/dist/features/events/components/EventSaleBanner.js +1 -1
  240. package/dist/features/events/components/EventStatusBadge.js +1 -1
  241. package/dist/features/faq/components/FAQAccordion.js +2 -1
  242. package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
  243. package/dist/features/faq/components/FAQHelpfulButtons.js +4 -4
  244. package/dist/features/faq/components/RelatedFAQs.js +1 -1
  245. package/dist/features/filters/FilterFacetSection.js +1 -1
  246. package/dist/features/filters/SwitchFilter.js +1 -1
  247. package/dist/features/gateway-events/repository/gateway-events.repository.d.ts +117 -0
  248. package/dist/features/gateway-events/repository/gateway-events.repository.js +274 -0
  249. package/dist/features/gateway-events/schemas/firestore.d.ts +217 -0
  250. package/dist/features/gateway-events/schemas/firestore.js +125 -0
  251. package/dist/features/gateway-events/schemas/index.d.ts +81 -0
  252. package/dist/features/gateway-events/schemas/index.js +79 -0
  253. package/dist/features/grouped/components/GroupedListingDetailView.js +1 -1
  254. package/dist/features/homepage/components/AnnouncementBar.js +37 -1
  255. package/dist/features/homepage/components/AnnouncementBar.style.css +30 -0
  256. package/dist/features/homepage/components/BrandsSection.js +1 -1
  257. package/dist/features/homepage/components/CTABannerSection.js +1 -1
  258. package/dist/features/homepage/components/FeaturedBundlesSection.js +14 -24
  259. package/dist/features/homepage/components/HeroCarousel.js +2 -2
  260. package/dist/features/homepage/components/SocialPostCard.js +1 -1
  261. package/dist/features/inventory/components/AdminMoveLedgerView.d.ts +7 -0
  262. package/dist/features/inventory/components/AdminMoveLedgerView.js +157 -0
  263. package/dist/features/inventory/components/AdminStorageBoxesView.d.ts +11 -0
  264. package/dist/features/inventory/components/AdminStorageBoxesView.js +168 -0
  265. package/dist/features/inventory/components/ReceiveStationView.d.ts +39 -0
  266. package/dist/features/inventory/components/ReceiveStationView.js +413 -0
  267. package/dist/features/inventory/components/operator-message.d.ts +29 -0
  268. package/dist/features/inventory/components/operator-message.js +41 -0
  269. package/dist/features/inventory/index.d.ts +13 -0
  270. package/dist/features/inventory/index.js +28 -0
  271. package/dist/features/inventory/repository/inventory-movements.repository.d.ts +97 -0
  272. package/dist/features/inventory/repository/inventory-movements.repository.js +186 -0
  273. package/dist/features/inventory/repository/inventory-placements.repository.d.ts +179 -0
  274. package/dist/features/inventory/repository/inventory-placements.repository.js +345 -0
  275. package/dist/features/inventory/repository/storage-boxes.repository.d.ts +83 -0
  276. package/dist/features/inventory/repository/storage-boxes.repository.js +127 -0
  277. package/dist/features/inventory/schemas/firestore.d.ts +197 -0
  278. package/dist/features/inventory/schemas/firestore.js +159 -0
  279. package/dist/features/layout/AppLayoutShell.d.ts +12 -1
  280. package/dist/features/layout/AppLayoutShell.js +25 -12
  281. package/dist/features/layout/AutoBreadcrumbs.js +162 -3
  282. package/dist/features/layout/AutoBreadcrumbs.style.css +45 -0
  283. package/dist/features/layout/BottomNavbar.d.ts +1 -1
  284. package/dist/features/layout/BottomNavbar.js +2 -2
  285. package/dist/features/media/MediaAvatar.js +1 -1
  286. package/dist/features/media/MediaImage.d.ts +24 -4
  287. package/dist/features/media/MediaImage.js +44 -4
  288. package/dist/features/media/MediaLightbox.js +1 -1
  289. package/dist/features/media/modals/ImageCropModal.js +1 -1
  290. package/dist/features/media/modals/ImageEditor.js +1 -1
  291. package/dist/features/media/upload/MediaUploadField.js +91 -3
  292. package/dist/features/obligations/constants/policies.d.ts +92 -0
  293. package/dist/features/obligations/constants/policies.js +378 -0
  294. package/dist/features/obligations/repository/obligations.repository.d.ts +68 -0
  295. package/dist/features/obligations/repository/obligations.repository.js +158 -0
  296. package/dist/features/obligations/schemas/firestore.d.ts +160 -0
  297. package/dist/features/obligations/schemas/firestore.js +87 -0
  298. package/dist/features/orders/actions/order-actions.d.ts +43 -4
  299. package/dist/features/orders/actions/order-actions.js +60 -3
  300. package/dist/features/orders/actions/refund-actions.js +70 -10
  301. package/dist/features/orders/components/OrderCompletionPanel.d.ts +27 -0
  302. package/dist/features/orders/components/OrderCompletionPanel.js +192 -0
  303. package/dist/features/orders/components/OrdersList.js +2 -2
  304. package/dist/features/orders/components/RefundHistoryTable.js +3 -1
  305. package/dist/features/orders/components/index.d.ts +2 -0
  306. package/dist/features/orders/components/index.js +6 -0
  307. package/dist/features/orders/constants/transitions.d.ts +59 -0
  308. package/dist/features/orders/constants/transitions.js +104 -0
  309. package/dist/features/orders/repository/orders.repository.d.ts +109 -6
  310. package/dist/features/orders/repository/orders.repository.js +464 -21
  311. package/dist/features/orders/schemas/firestore.d.ts +270 -0
  312. package/dist/features/orders/schemas/firestore.js +24 -0
  313. package/dist/features/orders/schemas/index.d.ts +61 -0
  314. package/dist/features/orders/schemas/index.js +22 -0
  315. package/dist/features/orders/types/index.d.ts +79 -0
  316. package/dist/features/parts/components/PartsIndexPageView.js +8 -1
  317. package/dist/features/payment-cases/repository/payment-cases.repository.d.ts +170 -0
  318. package/dist/features/payment-cases/repository/payment-cases.repository.js +342 -0
  319. package/dist/features/payment-cases/schemas/firestore.d.ts +229 -0
  320. package/dist/features/payment-cases/schemas/firestore.js +105 -0
  321. package/dist/features/payment-cases/schemas/resolve-form.d.ts +97 -0
  322. package/dist/features/payment-cases/schemas/resolve-form.js +114 -0
  323. package/dist/features/payments/repository/payout.repository.d.ts +0 -1
  324. package/dist/features/payments/repository/payout.repository.js +0 -1
  325. package/dist/features/points/repository/points.repository.d.ts +122 -0
  326. package/dist/features/points/repository/points.repository.js +304 -1
  327. package/dist/features/points/schemas/adjust-form.d.ts +58 -0
  328. package/dist/features/points/schemas/adjust-form.js +63 -0
  329. package/dist/features/points/schemas/firestore.d.ts +108 -1
  330. package/dist/features/points/schemas/firestore.js +84 -0
  331. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -3
  332. package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
  333. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
  334. package/dist/features/price-index/components/PriceIndexToolView.d.ts +23 -4
  335. package/dist/features/price-index/components/PriceIndexToolView.js +71 -6
  336. package/dist/features/price-index/market-stats.d.ts +82 -0
  337. package/dist/features/price-index/market-stats.js +230 -0
  338. package/dist/features/print/components/BoxLabelPrintView.d.ts +25 -0
  339. package/dist/features/print/components/BoxLabelPrintView.js +66 -0
  340. package/dist/features/print/components/LabelProfilePicker.d.ts +13 -0
  341. package/dist/features/print/components/LabelProfilePicker.js +38 -0
  342. package/dist/features/print/components/labels.d.ts +117 -0
  343. package/dist/features/print/components/labels.js +258 -0
  344. package/dist/features/print/components/labels.style.css +46 -0
  345. package/dist/features/print/index.d.ts +6 -0
  346. package/dist/features/print/index.js +16 -0
  347. package/dist/features/products/columns/productTableColumns.js +5 -4
  348. package/dist/features/products/components/MarketplaceBundleCard.js +2 -2
  349. package/dist/features/products/components/MarketplacePrizeDrawCard.js +6 -6
  350. package/dist/features/products/components/PrizeDrawCollage.js +1 -1
  351. package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
  352. package/dist/features/products/components/PrizeRevealModal.js +2 -2
  353. package/dist/features/products/components/ProductDetailPageView.js +18 -4
  354. package/dist/features/products/components/ProductForm.js +34 -23
  355. package/dist/features/products/components/ProductGrid.js +86 -62
  356. package/dist/features/products/constants/card-badges.d.ts +97 -0
  357. package/dist/features/products/constants/card-badges.js +100 -0
  358. package/dist/features/products/repository/products.repository.js +34 -1
  359. package/dist/features/products/schemas/firestore.d.ts +46 -3
  360. package/dist/features/products/schemas/firestore.js +21 -0
  361. package/dist/features/promotions/components/PromotionsView.js +1 -1
  362. package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +32 -0
  363. package/dist/features/promotions/repository/claimed-coupons.repository.js +58 -0
  364. package/dist/features/promotions/schemas/firestore.js +30 -3
  365. package/dist/features/promotions/schemas/index.d.ts +6 -6
  366. package/dist/features/protection-claims/repository/protection-claims.repository.d.ts +54 -0
  367. package/dist/features/protection-claims/repository/protection-claims.repository.js +174 -0
  368. package/dist/features/protection-claims/schemas/firestore.d.ts +168 -0
  369. package/dist/features/protection-claims/schemas/firestore.js +130 -0
  370. package/dist/features/reviews/actions/review-actions.js +3 -2
  371. package/dist/features/reviews/components/ReviewModal.js +1 -1
  372. package/dist/features/reviews/components/ReviewsList.js +1 -1
  373. package/dist/features/reviews/schemas/firestore.d.ts +20 -1
  374. package/dist/features/scams/constants/scam-types.d.ts +2 -0
  375. package/dist/features/scams/constants/scam-types.js +18 -0
  376. package/dist/features/scams/schemas/report-form.d.ts +3 -3
  377. package/dist/features/scams/schemas/report-form.js +26 -1
  378. package/dist/features/search/components/Search.js +9 -9
  379. package/dist/features/search/components/SearchResultsClient.js +18 -3
  380. package/dist/features/search/components/SearchResultsPageView.d.ts +1 -6
  381. package/dist/features/search/components/SearchResultsPageView.js +2 -2
  382. package/dist/features/seller/actions/offer-actions.js +16 -1
  383. package/dist/features/seller/actions/seller-actions.js +14 -3
  384. package/dist/features/seller/components/CategoryInlineSelect.js +75 -11
  385. package/dist/features/seller/components/PrintCenterView.d.ts +5 -0
  386. package/dist/features/seller/components/PrintCenterView.js +41 -30
  387. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  388. package/dist/features/seller/components/SellerPayoutRequestView.js +16 -5
  389. package/dist/features/seller/components/SellerPayoutSettingsView.js +1 -1
  390. package/dist/features/seller/components/SellerProductShell.d.ts +16 -0
  391. package/dist/features/seller/components/SellerProductShell.js +123 -13
  392. package/dist/features/seller/components/SellerReviewsView.js +4 -2
  393. package/dist/features/seller/components/SellerSidebar.js +1 -1
  394. package/dist/features/seller/schemas/coupon-form.d.ts +26 -0
  395. package/dist/features/seller/schemas/coupon-form.js +27 -0
  396. package/dist/features/seller/schemas/product-form.d.ts +3 -0
  397. package/dist/features/seller/schemas/product-form.js +5 -0
  398. package/dist/features/shell/FormShell.js +2 -2
  399. package/dist/features/shell/QuickFormDrawer.js +14 -0
  400. package/dist/features/shell/SectionForm.d.ts +19 -0
  401. package/dist/features/shell/SectionForm.js +133 -9
  402. package/dist/features/shell/SectionForm.style.css +54 -0
  403. package/dist/features/shipments/components/AdminShipmentsView.js +16 -1
  404. package/dist/features/shipments/repository/shipment-items.repository.d.ts +9 -0
  405. package/dist/features/shipments/repository/shipment-items.repository.js +14 -0
  406. package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
  407. package/dist/features/sticker-trace/components/StickerCommitPanel.js +1 -1
  408. package/dist/features/store-extensions/schemas/report-review-form.d.ts +8 -8
  409. package/dist/features/stores/components/StoreGuideHubView.js +1 -1
  410. package/dist/features/stores/components/StoreHeader.js +1 -1
  411. package/dist/features/support/schemas/firestore.d.ts +73 -2
  412. package/dist/features/support/schemas/firestore.js +59 -0
  413. package/dist/index.d.ts +16 -24
  414. package/dist/index.js +38 -47
  415. package/dist/next/ErrorBoundary.js +1 -1
  416. package/dist/next/routing/route-map.d.ts +24 -0
  417. package/dist/next/routing/route-map.js +40 -0
  418. package/dist/providers/payment-manual/index.d.ts +15 -1
  419. package/dist/providers/payment-manual/index.js +37 -0
  420. package/dist/providers/payment-phonepe/index.d.ts +14 -1
  421. package/dist/providers/payment-phonepe/index.js +36 -0
  422. package/dist/providers/payment-razorpay/index.d.ts +44 -1
  423. package/dist/providers/payment-razorpay/index.js +99 -0
  424. package/dist/providers/shipping-shiprocket/index.d.ts +18 -0
  425. package/dist/providers/shipping-shiprocket/index.js +18 -0
  426. package/dist/schemas/registry.d.ts +44 -11
  427. package/dist/server-entry.d.ts +2 -0
  428. package/dist/server-entry.js +27 -0
  429. package/dist/server.d.ts +43 -0
  430. package/dist/server.js +98 -0
  431. package/dist/styles.css +418 -149
  432. package/dist/tailwind-utilities.css +1 -1
  433. package/dist/tokens/themes/default-dark.js +15 -12
  434. package/dist/tokens/themes/default-light.js +14 -11
  435. package/dist/tokens/tokens.css +106 -23
  436. package/dist/ui/DataTable.style.css +4 -4
  437. package/dist/ui/columns/cell-renderers.d.ts +20 -0
  438. package/dist/ui/columns/cell-renderers.js +26 -1
  439. package/dist/ui/columns/index.d.ts +1 -1
  440. package/dist/ui/columns/index.js +4 -1
  441. package/dist/ui/components/Badge.style.css +1 -1
  442. package/dist/ui/components/Breadcrumb.js +19 -1
  443. package/dist/ui/components/BulkActionBar.style.css +4 -4
  444. package/dist/ui/components/Button.style.css +1 -1
  445. package/dist/ui/components/Checkbox.style.css +1 -1
  446. package/dist/ui/components/FallbackShell.js +1 -1
  447. package/dist/ui/components/Form.d.ts +14 -1
  448. package/dist/ui/components/Form.js +2 -2
  449. package/dist/ui/components/FormField.js +18 -4
  450. package/dist/ui/components/FormField.style.css +43 -3
  451. package/dist/ui/components/GatedPrice.style.css +1 -1
  452. package/dist/ui/components/IconButton.style.css +1 -1
  453. package/dist/ui/components/Input.js +22 -1
  454. package/dist/ui/components/Input.style.css +38 -22
  455. package/dist/ui/components/ListingFilterDrawer.js +1 -1
  456. package/dist/ui/components/ListingToolbar.js +1 -1
  457. package/dist/ui/components/PaginatedSelect.js +0 -0
  458. package/dist/ui/components/Radio.style.css +1 -1
  459. package/dist/ui/components/Repeater.d.ts +53 -0
  460. package/dist/ui/components/Repeater.js +7 -0
  461. package/dist/ui/components/RichTextEditor.d.ts +20 -1
  462. package/dist/ui/components/RichTextEditor.js +23 -2
  463. package/dist/ui/components/Select.style.css +7 -21
  464. package/dist/ui/components/Semantic.js +27 -3
  465. package/dist/ui/components/TagInput.style.css +4 -9
  466. package/dist/ui/components/Textarea.style.css +4 -18
  467. package/dist/ui/components/Toggle.style.css +1 -1
  468. package/dist/ui/components/Typography.d.ts +3 -3
  469. package/dist/ui/components/Typography.js +15 -15
  470. package/dist/ui/components/ViewToggle.style.css +4 -4
  471. package/dist/ui/forms/AddAnotherToggle.d.ts +16 -0
  472. package/dist/ui/forms/AddAnotherToggle.js +56 -0
  473. package/dist/ui/forms/ColorPickerField.js +15 -2
  474. package/dist/ui/forms/FieldCheckbox.js +15 -2
  475. package/dist/ui/forms/FieldInput.d.ts +22 -1
  476. package/dist/ui/forms/FieldInput.js +19 -5
  477. package/dist/ui/forms/FieldLabel.d.ts +35 -0
  478. package/dist/ui/forms/FieldLabel.js +70 -0
  479. package/dist/ui/forms/FieldSelect.d.ts +4 -1
  480. package/dist/ui/forms/FieldSelect.js +27 -5
  481. package/dist/ui/forms/FieldTextarea.d.ts +4 -1
  482. package/dist/ui/forms/FieldTextarea.js +22 -5
  483. package/dist/ui/forms/FormShell.d.ts +17 -1
  484. package/dist/ui/forms/FormShell.js +36 -2
  485. package/dist/ui/forms/FormShell.style.css +41 -33
  486. package/dist/ui/forms/index.d.ts +5 -1
  487. package/dist/ui/forms/index.js +2 -0
  488. package/dist/ui/icons/icon-registry.d.ts +1 -0
  489. package/dist/ui/icons/icon-registry.js +17 -1
  490. package/dist/utils/id-generators.d.ts +80 -0
  491. package/dist/utils/id-generators.js +93 -0
  492. package/dist/utils/search-txt-builders.d.ts +12 -0
  493. package/dist/utils/search-txt-builders.js +17 -0
  494. package/package.json +1 -1
  495. package/scripts/seed-cli.mjs +106 -5
  496. package/dist/features/loyalty/api/admin/loyalty/grant/route.d.ts +0 -12
  497. package/dist/features/loyalty/api/admin/loyalty/grant/route.js +0 -38
  498. package/dist/features/loyalty/api/balance/route.d.ts +0 -12
  499. package/dist/features/loyalty/api/balance/route.js +0 -41
  500. package/dist/features/loyalty/api/earn/route.d.ts +0 -12
  501. package/dist/features/loyalty/api/earn/route.js +0 -38
  502. package/dist/features/loyalty/api/history/route.d.ts +0 -12
  503. package/dist/features/loyalty/api/history/route.js +0 -58
  504. package/dist/features/loyalty/api/redeem/route.d.ts +0 -12
  505. package/dist/features/loyalty/api/redeem/route.js +0 -38
  506. package/dist/features/loyalty/columns/index.d.ts +0 -17
  507. package/dist/features/loyalty/columns/index.js +0 -40
  508. package/dist/features/loyalty/components/CoinsDisplay.d.ts +0 -12
  509. package/dist/features/loyalty/components/CoinsDisplay.js +0 -10
  510. package/dist/features/loyalty/components/index.d.ts +0 -1
  511. package/dist/features/loyalty/components/index.js +0 -1
  512. package/dist/features/loyalty/hooks/useLoyaltyBalance.d.ts +0 -4
  513. package/dist/features/loyalty/hooks/useLoyaltyBalance.js +0 -25
  514. package/dist/features/loyalty/index.d.ts +0 -6
  515. package/dist/features/loyalty/index.js +0 -6
  516. package/dist/features/loyalty/manifest.d.ts +0 -2
  517. package/dist/features/loyalty/manifest.js +0 -16
  518. package/dist/features/loyalty/messages/en.json +0 -19
  519. package/dist/features/loyalty/repository/loyalty.repository.d.ts +0 -8
  520. package/dist/features/loyalty/repository/loyalty.repository.js +0 -22
  521. package/dist/features/loyalty/schemas/index.d.ts +0 -85
  522. package/dist/features/loyalty/schemas/index.js +0 -40
  523. package/dist/features/loyalty/server.d.ts +0 -6
  524. package/dist/features/loyalty/server.js +0 -6
  525. package/dist/features/loyalty/types/index.d.ts +0 -31
  526. package/dist/features/loyalty/types/index.js +0 -1
  527. package/dist/features/loyalty/types/loyalty-config.d.ts +0 -7
  528. package/dist/features/loyalty/types/loyalty-config.js +0 -1
  529. package/dist/features/loyalty/types/loyalty-math.d.ts +0 -9
  530. package/dist/features/loyalty/types/loyalty-math.js +0 -27
  531. package/dist/features/products/components/ProductOwnerQuickActions.d.ts +0 -11
  532. package/dist/features/products/components/ProductOwnerQuickActions.js +0 -120
@@ -40,7 +40,7 @@ import type { BaseDocument } from "../../../_internal/shared/types/base-document
40
40
  * error at every exhaustive read rather than an unlabelled row. The admin
41
41
  * answer to "why do I have 53 points" is this field.
42
42
  */
43
- export declare const POINTS_ENTRY_KIND_VALUES: readonly ["shipping_saving", "redemption", "redemption_reversal", "admin_adjustment"];
43
+ export declare const POINTS_ENTRY_KIND_VALUES: readonly ["shipping_saving", "purchase_earn", "redemption", "redemption_reversal", "admin_adjustment"];
44
44
  export type PointsEntryKind = (typeof POINTS_ENTRY_KIND_VALUES)[number];
45
45
  export interface PointsLedgerEntry extends BaseDocument {
46
46
  /** Auth UID of the buyer. The ledger is keyed on the person, not the order. */
@@ -70,6 +70,57 @@ export interface PointsLedgerEntry extends BaseDocument {
70
70
  adminReason?: string;
71
71
  /** Who made an `admin_adjustment`. */
72
72
  actorUid?: string;
73
+ /**
74
+ * When this batch of points stops being spendable.
75
+ *
76
+ * ## ๐Ÿ›‘ PER ENTRY, three months, FIFO on redemption
77
+ *
78
+ * Decision 4. Per-entry rather than a single per-user expiry date because
79
+ * points arrive in batches and each batch has its own clock โ€” a credit earned
80
+ * today must not be shortened by one earned three months ago, and a single
81
+ * date can only express one of those.
82
+ *
83
+ * Redemption consumes OLDEST-FIRST so a buyer never loses points they could
84
+ * have spent. The alternative (newest-first) silently maximises expiry, which
85
+ * is the house always winning by a mechanism nobody announced.
86
+ *
87
+ * Absent on a `redemption`, a `redemption_reversal` and usually on an
88
+ * `admin_adjustment`: a spend has nothing to expire, and a reversal restores
89
+ * points whose original clock has already been consumed โ€” giving it a fresh
90
+ * three months would be a quiet extension nobody granted.
91
+ *
92
+ * ๐Ÿ›‘ Absent also means NEVER EXPIRES, which is the right default for every
93
+ * entry written before this field existed. Treating absence as "expired" would
94
+ * retroactively void live balances; treating it as "never" leaves a bounded,
95
+ * knowable residue that the reconciliation job can report.
96
+ */
97
+ expiresAt?: Date;
98
+ /**
99
+ * Points consumed out of this credit by later redemptions.
100
+ *
101
+ * ๐Ÿ›‘ FIFO needs to know how much of each batch is left, and an append-only
102
+ * ledger cannot answer that from the sum alone: two credits of 50 and a spend
103
+ * of 60 sum to 40, but WHICH 40 is left decides what expires when. So a
104
+ * redemption writes its negative entry AND increments this counter on the
105
+ * batches it drew from.
106
+ *
107
+ * That makes this the one mutable field on an otherwise append-only row, and
108
+ * it is a deliberate, narrow exception: the alternative is deriving
109
+ * consumption by replaying every entry in order on every read, which is
110
+ * O(ledger) per balance check and gets slower forever.
111
+ */
112
+ consumed?: number;
113
+ /**
114
+ * On a `redemption` only: the credit batches this spend drew from, in the
115
+ * order it drew from them.
116
+ *
117
+ * ๐Ÿ›‘ Recorded ON the spend rather than derived later. A reversal needs to know
118
+ * what to give back, and by then those batches may themselves have expired โ€”
119
+ * "return it where it came from" is only answerable if the spend wrote it
120
+ * down. Deriving it afterwards would mean replaying the whole ledger and
121
+ * guessing at a FIFO order that may since have changed.
122
+ */
123
+ drawnFrom?: string[];
73
124
  }
74
125
  /**
75
126
  * The ledger's own invariant, stated once so every writer can be checked
@@ -96,6 +147,8 @@ export declare const POINTS_FIELDS: {
96
147
  readonly AMOUNT: "amount";
97
148
  readonly ORDER_ID: "orderId";
98
149
  readonly CREATED_AT: "createdAt";
150
+ readonly EXPIRES_AT: "expiresAt";
151
+ readonly CONSUMED: "consumed";
99
152
  };
100
153
  /**
101
154
  * ๐Ÿ›‘ Points cannot pay for the whole order, and this is the cap that enforces
@@ -116,3 +169,57 @@ export declare const POINTS_MAX_REDEMPTION_PERCENT = 50;
116
169
  * credited.
117
170
  */
118
171
  export declare function maxRedeemablePoints(orderTotal: number, balance: number): number;
172
+ /**
173
+ * How long a credit stays spendable. Decision 4: three months from issue.
174
+ *
175
+ * ๐Ÿ›‘ A tighter bound than inactivity-based expiry on purpose. The liability
176
+ * here is unbounded without one (D49: there is no expiry at all today), and
177
+ * "three months from this batch" is a figure a buyer can check against their
178
+ * own ledger โ€” whereas "twelve months since you last did anything" is a date
179
+ * that moves for reasons they cannot see.
180
+ */
181
+ export declare const POINTS_EXPIRY_MONTHS = 3;
182
+ /**
183
+ * Percentage of an order's post-discount GOODS value earned as points.
184
+ *
185
+ * ๐Ÿ›‘ GOODS, never the order total. "5% of what you paid" read literally
186
+ * includes shipping and the platform fee โ€” so we would issue points against
187
+ * postage handed to a courier. And it excludes the points TENDER itself, or
188
+ * points breed points: pay โ‚น100 with points, earn 5, pay with those, earn
189
+ * again. It converges, but it leaks value and is indefensible on a balance
190
+ * sheet.
191
+ */
192
+ export declare const POINTS_EARN_PERCENT = 5;
193
+ /**
194
+ * The largest single manual adjustment an admin may make, in points (= โ‚น).
195
+ *
196
+ * ๐Ÿ›‘ The realistic failure here is a TYPO, not malice. `isPointsEntryWellFormed`
197
+ * checks sign and reason but says nothing about magnitude, so `50000` where
198
+ * `500` was meant passes every check and mints โ‚น50,000 of spendable liability
199
+ * in one click. Bounded on the ABSOLUTE value so the debit direction is guarded
200
+ * too โ€” a mistyped debit wipes a balance the buyer earned, and the only undo is
201
+ * another adjustment.
202
+ *
203
+ * A genuinely larger correction is several deliberate adjustments, each with
204
+ * its own reason and its own `adminAuditLog` row. That is the right amount of
205
+ * friction for a number this size.
206
+ */
207
+ export declare const POINTS_ADJUSTMENT_MAX = 5000;
208
+ /**
209
+ * Minimum length of an adjustment's `adminReason`.
210
+ *
211
+ * Deliberately the same 10 characters as `paymentCaseResolveFormSchema`, so an
212
+ * operator meets ONE rule across the admin surfaces rather than two that differ
213
+ * for no reason. A length rather than mere presence because the reader is the
214
+ * next admin asking "why does this account have 53 points", and "fix" answers
215
+ * nothing.
216
+ */
217
+ export declare const POINTS_ADJUSTMENT_REASON_MIN = 10;
218
+ /**
219
+ * When does a batch of points issued now expire?
220
+ *
221
+ * A function rather than a constant offset because months are not a fixed
222
+ * number of days โ€” `setMonth` handles the 31st-of-a-short-month case the way a
223
+ * human reading "three months" expects, which a `+ 90 * 86400000` does not.
224
+ */
225
+ export declare function pointsExpiryFrom(issuedAt: Date): Date;
@@ -42,6 +42,29 @@
42
42
  export const POINTS_ENTRY_KIND_VALUES = [
43
43
  /** Shipping charged minus shipping actually spent, credited after dispatch. */
44
44
  "shipping_saving",
45
+ /**
46
+ * Earned on a COMPLETED purchase โ€” the owner's rule, 2026-10-21.
47
+ *
48
+ * ๐Ÿ›‘ There was NO earn kind before this. The four original values covered
49
+ * shipping savings, redemption, its reversal and an admin adjustment โ€” so
50
+ * the programme could give points back and take them away but had no way to
51
+ * issue them for a purchase, which is the entire point of it.
52
+ *
53
+ * Awarded at COMPLETION, not at checkout: *"points only get issued on
54
+ * successful purchases after buyer put reviews, then onlyโ€ฆ they are also
55
+ * assigned if the user [raises] no issues after 7 days of delivery."* So
56
+ * completion has two triggers and they converge on one fact (`completedAt`),
57
+ * which is why the award must be keyed on something written once โ€” see
58
+ * `pointsAwardedAt` on the order.
59
+ *
60
+ * ๐Ÿ›‘ The STORE bears it (owner: *"so stores bear it"*), as a discount
61
+ * expense: *"the cost is like a sale discount for getting a repeat customer
62
+ * you get to pay few loss."* It is a deduction term in the payout, never a
63
+ * clawback โ€” `payout-release` is `blockedBy: ["order-complete"]`, so the
64
+ * award is ALWAYS strictly before the payout is computed, by construction
65
+ * rather than by timing luck.
66
+ */
67
+ "purchase_earn",
45
68
  /** Spent against an order's total at checkout. Always negative. */
46
69
  "redemption",
47
70
  /** A redemption returned because the order it paid for was cancelled. */
@@ -65,6 +88,7 @@ export function isPointsEntryWellFormed(entry) {
65
88
  return entry.amount < 0;
66
89
  case "shipping_saving":
67
90
  case "redemption_reversal":
91
+ case "purchase_earn":
68
92
  return entry.amount > 0;
69
93
  case "admin_adjustment":
70
94
  return Boolean(entry.adminReason && entry.adminReason.trim().length >= 10);
@@ -86,6 +110,8 @@ export const POINTS_FIELDS = {
86
110
  AMOUNT: "amount",
87
111
  ORDER_ID: "orderId",
88
112
  CREATED_AT: "createdAt",
113
+ EXPIRES_AT: "expiresAt",
114
+ CONSUMED: "consumed",
89
115
  };
90
116
  /**
91
117
  * ๐Ÿ›‘ Points cannot pay for the whole order, and this is the cap that enforces
@@ -114,3 +140,61 @@ export function maxRedeemablePoints(orderTotal, balance) {
114
140
  const cap = Math.floor((orderTotal * POINTS_MAX_REDEMPTION_PERCENT) / 100);
115
141
  return Math.max(0, Math.min(Math.floor(balance), cap));
116
142
  }
143
+ /**
144
+ * How long a credit stays spendable. Decision 4: three months from issue.
145
+ *
146
+ * ๐Ÿ›‘ A tighter bound than inactivity-based expiry on purpose. The liability
147
+ * here is unbounded without one (D49: there is no expiry at all today), and
148
+ * "three months from this batch" is a figure a buyer can check against their
149
+ * own ledger โ€” whereas "twelve months since you last did anything" is a date
150
+ * that moves for reasons they cannot see.
151
+ */
152
+ export const POINTS_EXPIRY_MONTHS = 3;
153
+ /**
154
+ * Percentage of an order's post-discount GOODS value earned as points.
155
+ *
156
+ * ๐Ÿ›‘ GOODS, never the order total. "5% of what you paid" read literally
157
+ * includes shipping and the platform fee โ€” so we would issue points against
158
+ * postage handed to a courier. And it excludes the points TENDER itself, or
159
+ * points breed points: pay โ‚น100 with points, earn 5, pay with those, earn
160
+ * again. It converges, but it leaks value and is indefensible on a balance
161
+ * sheet.
162
+ */
163
+ export const POINTS_EARN_PERCENT = 5;
164
+ /**
165
+ * The largest single manual adjustment an admin may make, in points (= โ‚น).
166
+ *
167
+ * ๐Ÿ›‘ The realistic failure here is a TYPO, not malice. `isPointsEntryWellFormed`
168
+ * checks sign and reason but says nothing about magnitude, so `50000` where
169
+ * `500` was meant passes every check and mints โ‚น50,000 of spendable liability
170
+ * in one click. Bounded on the ABSOLUTE value so the debit direction is guarded
171
+ * too โ€” a mistyped debit wipes a balance the buyer earned, and the only undo is
172
+ * another adjustment.
173
+ *
174
+ * A genuinely larger correction is several deliberate adjustments, each with
175
+ * its own reason and its own `adminAuditLog` row. That is the right amount of
176
+ * friction for a number this size.
177
+ */
178
+ export const POINTS_ADJUSTMENT_MAX = 5000;
179
+ /**
180
+ * Minimum length of an adjustment's `adminReason`.
181
+ *
182
+ * Deliberately the same 10 characters as `paymentCaseResolveFormSchema`, so an
183
+ * operator meets ONE rule across the admin surfaces rather than two that differ
184
+ * for no reason. A length rather than mere presence because the reader is the
185
+ * next admin asking "why does this account have 53 points", and "fix" answers
186
+ * nothing.
187
+ */
188
+ export const POINTS_ADJUSTMENT_REASON_MIN = 10;
189
+ /**
190
+ * When does a batch of points issued now expire?
191
+ *
192
+ * A function rather than a constant offset because months are not a fixed
193
+ * number of days โ€” `setMonth` handles the 31st-of-a-short-month case the way a
194
+ * human reading "three months" expects, which a `+ 90 * 86400000` does not.
195
+ */
196
+ export function pointsExpiryFrom(issuedAt) {
197
+ const out = new Date(issuedAt.getTime());
198
+ out.setMonth(out.getMonth() + POINTS_EXPIRY_MONTHS);
199
+ return out;
200
+ }
@@ -19,7 +19,7 @@ import { normalizeRichTextHtml } from "../../../utils/string.formatter.js";
19
19
  import { PreorderBadge } from "./PreorderCard.js";
20
20
  import { WishlistHeartButton } from "../../wishlist/components/WishlistHeartButton.js";
21
21
  import { CardStoreLine } from "../../products/components/CardStoreLine.js";
22
- import { ProductOwnerQuickActions } from "../../products/components/ProductOwnerQuickActions.js";
22
+ import { RecordOwnerQuickActions } from "../../admin/components/RecordOwnerQuickActions.js";
23
23
  const DEFAULT_LABELS = {
24
24
  preOrderBadge: "Pre-order",
25
25
  featuredBadge: "Featured",
@@ -80,7 +80,7 @@ export function MarketplacePreorderCard({ product, className = "", variant = "gr
80
80
  const ownerActions = renderOwnerActions
81
81
  ? renderOwnerActions()
82
82
  : product.id
83
- ? (_jsx(ProductOwnerQuickActions, { productId: String(product.id), storeId: product.storeId ? String(product.storeId) : undefined, recordLabel: product.title, placement: "card" }))
83
+ ? (_jsx(RecordOwnerQuickActions, { resource: "PRODUCT", recordId: String(product.id), storeId: product.storeId ? String(product.storeId) : undefined, recordLabel: product.title, placement: "card" }))
84
84
  : null;
85
85
  if (!ownerActions)
86
86
  return card;
@@ -88,5 +88,5 @@ export function MarketplacePreorderCard({ product, className = "", variant = "gr
88
88
  * Sibling of the card, never a child โ€” see the prop's docstring. `z-20`
89
89
  * clears this card's own overlays, which all sit at `z-10`.
90
90
  */
91
- return (_jsxs(Div, { className: "relative h-full", children: [card, _jsx(Div, { className: "absolute right-2 bottom-2 z-20", children: ownerActions })] }));
91
+ return (_jsxs(Div, { className: "flex h-full flex-col", children: [card, _jsx(Div, { className: "flex justify-end px-[var(--appkit-space-3)] pb-[var(--appkit-space-3)]", children: ownerActions })] }));
92
92
  }
@@ -5,7 +5,7 @@ const STATUS_MAP = {
5
5
  pending: { label: "Pending", color: "bg-warning-surface text-warning" },
6
6
  confirmed: { label: "Confirmed", color: "bg-info-surface text-info" },
7
7
  ready: { label: "Ready", color: "bg-success-surface text-success" },
8
- fulfilled: { label: "Fulfilled", color: "bg-[var(--appkit-color-surface)] bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text-muted)]" },
8
+ fulfilled: { label: "Fulfilled", color: "bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text-muted)]" },
9
9
  cancelled: { label: "Cancelled", color: "bg-error-surface text-error" },
10
10
  };
11
11
  export function PreOrderBadge({ status, className = "" }) {
@@ -50,7 +50,7 @@ const PRODUCTION_STATUS_LABELS = {
50
50
  ready_to_ship: "Ready to Ship",
51
51
  };
52
52
  function PreOrderInfoSection({ title, productId, storeId, productionStatus, maxPerUser, deliveryDate, featured, freeShipping, condition, isCancellable, finalSale, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref, }) {
53
- return (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_PREORDER_BADGE, children: "Pre-Order" }), productionStatus && (_jsx(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: PRODUCTION_STATUS_LABELS[productionStatus] ?? productionStatus })), maxPerUser !== null && (_jsxs(Span, { size: "xs", weight: "medium", className: CLS_STATUS_BADGE, children: ["Limit: ", maxPerUser, " per customer"] }))] }), productId && (_jsx(OwnerQuickActions, { resource: "PRODUCT", placement: "detail", recordLabel: title, storeId: storeId, editHref: String(ROUTES.STORE.PRODUCTS_EDIT(productId)) })), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), deliveryDate && (_jsxs(Row, { color: "muted", textSize: "sm", align: "center", gap: "xs", children: [_jsx(Span, { children: "\uD83D\uDCC5" }), _jsx(Span, { children: "Estimated delivery:" }), _jsx(Span, { weight: "medium", children: deliveryDate.toLocaleDateString(undefined, { year: "numeric", month: "long" }) })] })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, condition: condition ?? undefined, returnable: isCancellable, finalSale: finalSale, labels: {
53
+ return (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_PREORDER_BADGE, children: "Pre-Order" }), productionStatus && (_jsx(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: PRODUCTION_STATUS_LABELS[productionStatus] ?? productionStatus })), maxPerUser !== null && (_jsxs(Span, { size: "xs", weight: "medium", className: CLS_STATUS_BADGE, children: ["Limit: ", maxPerUser, " per customer"] }))] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title }), productId && (_jsx(OwnerQuickActions, { resource: "PRODUCT", placement: "detail", recordLabel: title, storeId: storeId, editHref: String(ROUTES.STORE.PRODUCTS_EDIT(productId)) }))] }), deliveryDate && (_jsxs(Row, { color: "muted", textSize: "sm", align: "center", gap: "xs", children: [_jsx(Span, { children: "\uD83D\uDCC5" }), _jsx(Span, { children: "Estimated delivery:" }), _jsx(Span, { weight: "medium", children: deliveryDate.toLocaleDateString(undefined, { year: "numeric", month: "long" }) })] })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, condition: condition ?? undefined, returnable: isCancellable, finalSale: finalSale, labels: {
54
54
  featured: "Featured",
55
55
  fasterDelivery: "Faster Delivery",
56
56
  ratedSeller: "Rated Seller",
@@ -80,14 +80,24 @@ export interface PriceIndexToolResponse {
80
80
  minConfidentSamples: number;
81
81
  }
82
82
  /**
83
- * The competitor-crawl band for one leaf. Mirrors `PriceIndexMarketRow`.
83
+ * The asking-price band for one leaf. Mirrors `PriceIndexMarketRow`.
84
84
  *
85
- * Every field but `median` and `categoryId` is optional because the crawl
86
- * genuinely did not measure them everywhere, and an absent figure must render
87
- * as absent. See `price-bands.md`: "a band with `n=2` is not evidence".
85
+ * Every field but `median`, `categoryId` and `origin` is optional, because the
86
+ * two kinds of row carry genuinely different evidence and an absent figure
87
+ * must render as absent. See `price-bands.md`: "a band with `n=2` is not
88
+ * evidence".
89
+ *
90
+ * ๐Ÿ›‘ `origin` decides what the card is ALLOWED to claim, and the two cases are
91
+ * not cosmetic variants of each other:
92
+ *
93
+ * `computed` a mode over dated sightings of THIS leaf. May be called "most
94
+ * common" and, when `basedOnRecent`, "last 6 months".
95
+ * `seeded` the 2026-10 crawl's median for an entire GENERATION, frozen at
96
+ * one date. May be called neither.
88
97
  */
89
98
  export interface PriceIndexMarketRowView {
90
99
  categoryId: string;
100
+ origin: "computed" | "seeded";
91
101
  median: number;
92
102
  p25?: number;
93
103
  p75?: number;
@@ -95,6 +105,15 @@ export interface PriceIndexMarketRowView {
95
105
  asOf?: string;
96
106
  soldOutShare?: number;
97
107
  source?: string;
108
+ /** `computed` only โ€” the price sightings cluster on. */
109
+ mode?: number;
110
+ modeCount?: number;
111
+ low?: number;
112
+ high?: number;
113
+ /** `false` โ‡’ the figures are all-time, not the last six months. */
114
+ basedOnRecent?: boolean;
115
+ windowDays?: number;
116
+ lastSeenAt?: string;
98
117
  inherited: boolean;
99
118
  viaCategoryId?: string;
100
119
  viaName?: string;
@@ -79,9 +79,16 @@ function relativeDay(iso) {
79
79
  const months = Math.floor(days / 30);
80
80
  return months === 1 ? "1 month ago" : `${months} months ago`;
81
81
  }
82
- /** One figure with its label. Four of these, never one called "the price". */
83
- function Figure({ label, value }) {
84
- return (_jsxs(Stack, { gap: "none", className: "min-w-0", children: [_jsx(Text, { size: "xs", color: "muted", transform: "uppercase", className: "tracking-wide", children: label }), _jsx(Text, { size: "lg", weight: "semibold", numeric: true, className: "tabular-nums", children: value })] }));
82
+ /**
83
+ * One figure with its label. Four of these, never one called "the price".
84
+ *
85
+ * `dim` renders the figure at body size in muted ink. It is how a GENERATION
86
+ * band is demoted below a per-model one โ€” the numbers stay readable, because a
87
+ * ballpark is better than nothing, but they stop looking like a measurement of
88
+ * the thing the card is headed by.
89
+ */
90
+ function Figure({ label, value, dim }) {
91
+ return (_jsxs(Stack, { gap: "none", className: "min-w-0", children: [_jsx(Text, { size: "xs", color: "muted", transform: "uppercase", className: "tracking-wide", children: label }), _jsx(Text, { size: dim ? "base" : "lg", weight: dim ? "medium" : "semibold", ...(dim ? { color: "muted" } : {}), numeric: true, className: "tabular-nums", children: value })] }));
85
92
  }
86
93
  function LowConfidence({ sampleSize, threshold }) {
87
94
  return (_jsxs(Text, { size: "sm", color: "muted", children: ["Only ", sampleSize, " ", sampleSize === 1 ? "sale" : "sales", " recorded \u2014 not enough to publish a figure yet. We show a price once there are", " ", threshold, "."] }));
@@ -119,12 +126,59 @@ function BandCard({ band, threshold }) {
119
126
  * Burst DB" is true, "typical for Rage Longinus" is not.
120
127
  */
121
128
  function MarketCard({ market }) {
122
- const hasRange = market.p25 !== undefined && market.p75 !== undefined;
123
129
  const soldOutPct = market.soldOutShare === undefined ? null : Math.round(market.soldOutShare * 100);
124
- return (_jsxs(Stack, { gap: "sm", padding: "sm", rounded: "lg", border: "default", className: "min-w-0", children: [_jsxs(Row, { align: "center", justify: "between", gap: "sm", wrap: true, className: "min-w-0", children: [_jsx(Badge, { variant: "secondary", children: "Typical asking price" }), market.sampleSize !== undefined && (_jsxs(Text, { size: "xs", color: "muted", children: [market.sampleSize, " ", market.sampleSize === 1 ? "listing" : "listings"] }))] }), _jsxs(Row, { gap: "lg", wrap: true, className: "min-w-0", children: [hasRange && _jsx(Figure, { label: "Usual low", value: money(market.p25) }), _jsx(Figure, { label: "Typical ask", value: money(market.median) }), hasRange && _jsx(Figure, { label: "Usual high", value: money(market.p75) })] }), market.inherited && (_jsxs(Text, { size: "sm", color: "muted", children: ["Measured for", " ", _jsx(Span, { weight: "semibold", children: market.viaName ?? market.viaCategoryId }), " as a whole \u2014 the crawl priced the generation, not this exact item."] })), soldOutPct !== null && (_jsxs(Text, { size: "sm", color: "muted", children: ["These are asking prices from other shops, and about ", soldOutPct, "% of listings at this level are sold out \u2014 so an ask is not proof anything changed hands at it."] })), _jsxs(Text, { size: "xs", color: "muted", className: "min-w-0 break-words", children: [market.source ? `Source: ${market.source}. ` : "", "Tax-inclusive", market.asOf
130
+ /*
131
+ * ๐Ÿ›‘ A SEEDED band is a generation median and a COMPUTED one is a mode over
132
+ * dated sightings of this exact leaf. They are rendered differently because
133
+ * they support different claims, not because one looks nicer โ€” the single
134
+ * worst thing this card could do is print a generation's figure in the
135
+ * typography of a per-model measurement, which is what it used to do and is
136
+ * why Dragoon MS and Strata Dragoon MS read as the same bey.
137
+ */
138
+ const isComputed = market.origin === "computed";
139
+ if (isComputed)
140
+ return _jsx(ComputedMarketCard, { market: market });
141
+ const hasRange = market.p25 !== undefined && market.p75 !== undefined;
142
+ return (_jsxs(Stack, { gap: "sm", padding: "sm", rounded: "lg", border: "subtle", surface: "muted", className: "min-w-0", children: [_jsxs(Row, { align: "center", justify: "between", gap: "sm", wrap: true, className: "min-w-0", children: [_jsx(Badge, { variant: "secondary", children: market.inherited ? "Generation estimate" : "Typical asking price" }), market.sampleSize !== undefined && (_jsxs(Text, { size: "xs", color: "muted", children: [market.sampleSize, " ", market.sampleSize === 1 ? "listing" : "listings"] }))] }), market.inherited && (_jsxs(Text, { size: "sm", color: "muted", children: ["We have no figure for this exact item. These are typical asks for", " ", _jsx(Span, { weight: "semibold", children: market.viaName ?? market.viaCategoryId }), " as a whole, so every item in that generation shows the same range."] })), _jsxs(Row, { gap: "lg", wrap: true, className: "min-w-0", children: [hasRange && (_jsx(Figure, { label: "Usual low", value: money(market.p25), dim: market.inherited })), _jsx(Figure, { label: "Typical ask", value: money(market.median), dim: market.inherited }), hasRange && (_jsx(Figure, { label: "Usual high", value: money(market.p75), dim: market.inherited }))] }), soldOutPct !== null && (_jsxs(Text, { size: "sm", color: "muted", children: ["These are asking prices from other shops, and about ", soldOutPct, "% of listings at this level are sold out \u2014 so an ask is not proof anything changed hands at it."] })), _jsxs(Text, { size: "xs", color: "muted", className: "min-w-0 break-words", children: ["Aggregated from Indian shop listings", market.source ? ` ยท ${market.source}` : "", " \u00B7 tax-inclusive", market.asOf
125
143
  ? ` ยท measured ${new Date(market.asOf).toLocaleDateString("en-IN")}`
126
144
  : ""] })] }));
127
145
  }
146
+ /**
147
+ * The per-leaf band computed from dated sightings.
148
+ *
149
+ * ## ๐Ÿ›‘ The headline is the MODE, and it is labelled as one
150
+ *
151
+ * "Most commonly listed at" is a different claim from "typical ask" and the
152
+ * card has to make the right one. A median answers "what is the middle of this
153
+ * list"; a mode answers "what do people actually charge", and on a collectible
154
+ * those diverge โ€” six sightings at โ‚น6,500 and three clearance ones at โ‚น3,000
155
+ * have a median that moves to โ‚น3,000 the moment two more cheap ones appear,
156
+ * while the going rate has not moved at all.
157
+ *
158
+ * ## ๐Ÿ›‘ Where there is no mode, the label CHANGES
159
+ *
160
+ * `mode` is absent when no price bucket held two or more sightings โ€” every
161
+ * observed price was different, so there is no cluster. The card then shows the
162
+ * median under "Typical ask". Printing a lone sighting under "most common"
163
+ * would dress one data point as a consensus.
164
+ *
165
+ * ## ๐Ÿ›‘ `basedOnRecent` decides the low/high label, and it must
166
+ *
167
+ * False means the six-month window held too little to stand alone, so the
168
+ * figures are all-time. A card that says "last 6 months" over a two-year-old
169
+ * sighting is indistinguishable from an accurate one once printed โ€” which is
170
+ * the whole reason the flag travels with the band instead of being inferred.
171
+ */
172
+ function ComputedMarketCard({ market }) {
173
+ const recent = market.basedOnRecent === true;
174
+ const rangeLabel = recent ? "last 6 months" : "all time";
175
+ const hasRange = market.low !== undefined && market.high !== undefined;
176
+ return (_jsxs(Stack, { gap: "sm", padding: "sm", rounded: "lg", border: "default", className: "min-w-0", children: [_jsxs(Row, { align: "center", justify: "between", gap: "sm", wrap: true, className: "min-w-0", children: [_jsx(Badge, { variant: "primary", children: recent ? "Going rate ยท last 6 months" : "Going rate ยท all time" }), market.sampleSize !== undefined && (_jsxs(Text, { size: "xs", color: "muted", children: [market.sampleSize, " ", market.sampleSize === 1 ? "sighting" : "sightings"] }))] }), _jsxs(Row, { gap: "lg", wrap: true, className: "min-w-0", children: [hasRange && (_jsx(Figure, { label: `Lowest ยท ${rangeLabel}`, value: money(market.low) })), market.mode !== undefined ? (_jsx(Figure, { label: "Most commonly listed at", value: money(market.mode) })) : (_jsx(Figure, { label: "Typical ask", value: money(market.median) })), hasRange && (_jsx(Figure, { label: `Highest ยท ${rangeLabel}`, value: money(market.high) }))] }), market.mode !== undefined && market.modeCount !== undefined && (_jsxs(Text, { size: "sm", color: "muted", children: [market.modeCount, " of ", market.sampleSize, " sightings were around this price \u2014 it is the figure this item is listed at most often, not the middle of the range."] })), !recent && (_jsxs(Text, { size: "sm", color: "muted", children: ["Too few sightings in the last six months to stand on their own, so these cover everything we have recorded", market.lastSeenAt && relativeDay(market.lastSeenAt)
177
+ ? ` โ€” the newest is from ${relativeDay(market.lastSeenAt)}`
178
+ : "", "."] })), _jsxs(Text, { size: "xs", color: "muted", className: "min-w-0 break-words", children: ["Asking prices from Indian shops and listings \u00B7 tax-inclusive", market.lastSeenAt
179
+ ? ` ยท most recent sighting ${relativeDay(market.lastSeenAt) || new Date(market.lastSeenAt).toLocaleDateString("en-IN")}`
180
+ : ""] })] }));
181
+ }
128
182
  /**
129
183
  * What a signed-out visitor sees where the market band would be.
130
184
  *
@@ -155,7 +209,18 @@ function ResultCard({ row, threshold, market, canSeeMarket, }) {
155
209
  */
156
210
  _jsx(Text, { size: "sm", color: "muted", children: market
157
211
  ? "Nothing has sold here on LetItRip yet, so the figures above are asking prices elsewhere rather than our own sales."
158
- : "No completed sales recorded here yet. Prices appear once something sells on LetItRip." }))] }));
212
+ : canSeeMarket
213
+ ? /*
214
+ * ๐Ÿ›‘ BOTH halves are empty here, and saying so is the point.
215
+ * The old copy named only the sold half, so a lookup for an
216
+ * item nobody has crawled either โ€” Dragoon G and every other
217
+ * Engine Gear top, an era the 2026-10 crawl skipped entirely โ€”
218
+ * read as "it just has not sold yet" when the truth is that we
219
+ * have no price evidence of any kind. Those are different
220
+ * states and only one of them is about our marketplace.
221
+ */
222
+ "No price evidence for this one yet โ€” nothing sold here, and no asking prices recorded for it either. It appears as soon as either exists."
223
+ : "No completed sales recorded here yet. Prices appear once something sells on LetItRip." }))] }));
159
224
  }
160
225
  /** Stable identity โ€” a `useMemo` dependency inside `useSiteSearch`. */
161
226
  const identityLabel = (typeKey) => typeKey;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Six months, the window the owner specified for "recent".
3
+ *
4
+ * 182 rather than 180: it is half of 365, so a year's data splits into exactly
5
+ * two windows and "the last six months" cannot quietly mean a different span
6
+ * depending on which months they were.
7
+ */
8
+ export declare const RECENT_WINDOW_DAYS = 182;
9
+ /**
10
+ * Below this, NO band is published for a leaf.
11
+ *
12
+ * Three, matching `PRICE_INDEX_MIN_CONFIDENT_SAMPLES` on the sold half โ€” the
13
+ * two halves of this page must not disagree about what counts as evidence, and
14
+ * the sold half's reasoning ("a median of two sales is a coin flip presented as
15
+ * data") is about sample size, not about which side of the page it is on.
16
+ */
17
+ export declare const MIN_BAND_OBSERVATIONS = 3;
18
+ /**
19
+ * Below this, the band publishes a MEDIAN and no mode.
20
+ *
21
+ * A "most common price" derived from a bucket containing one sighting is not a
22
+ * most-common anything โ€” it is that one sighting, relabelled. Two is the
23
+ * smallest number for which the word means what it says.
24
+ */
25
+ export declare const MIN_MODE_OBSERVATIONS = 2;
26
+ /** The minimum a sighting must carry to be counted. */
27
+ export interface PriceObservationSample {
28
+ /** Decimal rupees, tax-inclusive โ€” the basis every price in this app is on. */
29
+ amount: number;
30
+ /** When it was SEEN. Not when we wrote the row. */
31
+ seenAt: Date;
32
+ }
33
+ /** One computed band, plus everything a renderer needs to describe it. */
34
+ export interface MarketStats {
35
+ /**
36
+ * The headline: the price sightings cluster on.
37
+ *
38
+ * ๐Ÿ›‘ `undefined` when no bucket reached `MIN_MODE_OBSERVATIONS` โ€” every
39
+ * sighting was a different price, so there is no cluster to report. The
40
+ * renderer falls back to `median` and must change its label when it does.
41
+ */
42
+ mode?: number;
43
+ /** How many sightings sat in the winning bucket. Absent with no mode. */
44
+ modeCount?: number;
45
+ /** Always present. The fallback headline, and context beside the mode. */
46
+ median: number;
47
+ low: number;
48
+ high: number;
49
+ sampleSize: number;
50
+ /**
51
+ * ๐Ÿ›‘ FALSE means `low`/`high`/`mode` describe ALL TIME, because the
52
+ * six-month window held too little to stand on its own. The renderer must
53
+ * relabel rather than silently presenting old figures as current.
54
+ */
55
+ basedOnRecent: boolean;
56
+ /** The span actually used โ€” `RECENT_WINDOW_DAYS`, or the full data span. */
57
+ windowDays: number;
58
+ /** The most recent sighting in the set the band was computed from. */
59
+ lastSeenAt: Date;
60
+ }
61
+ /**
62
+ * Bucket width by magnitude.
63
+ *
64
+ * Proportional rather than fixed, because a โ‚น50 bucket is three separate
65
+ * buckets across โ‚น99โ€“โ‚น249 part pricing and a rounding error on a โ‚น9,999 grail.
66
+ * The widths are ~2โ€“5% of the value throughout, so "the same price" means the
67
+ * same thing at every price point.
68
+ */
69
+ export declare function priceBucketWidth(amount: number): number;
70
+ /**
71
+ * The band for one leaf, or `null` when there is not enough to say anything.
72
+ *
73
+ * ๐Ÿ›‘ `null`, never a zeroed band. A band of โ‚น0 renders as a real figure meaning
74
+ * "this sells for nothing", which is strictly worse than silence โ€” the same
75
+ * rule `CategoryPriceGuidance.sold` is held to ("absent until there are some โ€”
76
+ * never zeroed").
77
+ *
78
+ * The recency decision happens ONCE here and is reported in `basedOnRecent`, so
79
+ * the job, the CLI and the renderer cannot each arrive at a different answer
80
+ * about which window a figure came from.
81
+ */
82
+ export declare function computeMarketStats(samples: readonly PriceObservationSample[], now?: Date): MarketStats | null;