@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.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +62 -5
- package/dist/_internal/client/features/maintenance/views/MaintenanceDashboardView.js +2 -2
- package/dist/_internal/server/features/brands/actions.js +78 -4
- package/dist/_internal/server/features/checkout/actions.d.ts +36 -13
- package/dist/_internal/server/features/checkout/actions.js +668 -183
- package/dist/_internal/server/features/checkout/supplier-identity.js +18 -1
- package/dist/_internal/server/features/gateway-events/index.d.ts +10 -0
- package/dist/_internal/server/features/gateway-events/index.js +10 -0
- package/dist/_internal/server/features/gateway-events/ingest.d.ts +127 -0
- package/dist/_internal/server/features/gateway-events/ingest.js +188 -0
- package/dist/_internal/server/features/gateway-events/redact.d.ts +66 -0
- package/dist/_internal/server/features/gateway-events/redact.js +151 -0
- package/dist/_internal/server/features/grievance/actions.d.ts +63 -0
- package/dist/_internal/server/features/grievance/actions.js +173 -0
- package/dist/_internal/server/features/grievance/index.d.ts +8 -0
- package/dist/_internal/server/features/grievance/index.js +8 -0
- package/dist/_internal/server/features/grouped/data.js +28 -3
- package/dist/_internal/server/features/lottery/actions.js +99 -4
- package/dist/_internal/server/features/order-completion/actions.d.ts +92 -0
- package/dist/_internal/server/features/order-completion/actions.js +336 -0
- package/dist/_internal/server/features/order-completion/index.d.ts +7 -0
- package/dist/_internal/server/features/order-completion/index.js +7 -0
- package/dist/_internal/server/features/orders/actions.d.ts +13 -0
- package/dist/_internal/server/features/orders/actions.js +56 -2
- package/dist/_internal/server/features/orders/adapters.d.ts +20 -2
- package/dist/_internal/server/features/orders/adapters.js +44 -0
- package/dist/_internal/server/features/orders/cancellation-refund.d.ts +116 -0
- package/dist/_internal/server/features/orders/cancellation-refund.js +466 -0
- package/dist/_internal/server/features/orders/index.d.ts +2 -1
- package/dist/_internal/server/features/orders/index.js +1 -1
- package/dist/_internal/server/features/orders/service.d.ts +60 -0
- package/dist/_internal/server/features/orders/service.js +74 -0
- package/dist/_internal/server/features/payment-cases/disputes.d.ts +76 -0
- package/dist/_internal/server/features/payment-cases/disputes.js +241 -0
- package/dist/_internal/server/features/payment-cases/index.d.ts +9 -0
- package/dist/_internal/server/features/payment-cases/index.js +9 -0
- package/dist/_internal/server/features/payouts/actions.d.ts +28 -3
- package/dist/_internal/server/features/payouts/actions.js +84 -10
- package/dist/_internal/server/features/points/adjust.d.ts +61 -0
- package/dist/_internal/server/features/points/adjust.js +121 -0
- package/dist/_internal/server/features/points/award.d.ts +65 -0
- package/dist/_internal/server/features/points/award.js +145 -0
- package/dist/_internal/server/features/price-index/market.d.ts +33 -0
- package/dist/_internal/server/features/price-index/market.js +54 -1
- package/dist/_internal/server/features/products/data.js +40 -22
- package/dist/_internal/server/features/protection-claims/actions.d.ts +72 -0
- package/dist/_internal/server/features/protection-claims/actions.js +228 -0
- package/dist/_internal/server/features/protection-claims/index.d.ts +8 -0
- package/dist/_internal/server/features/protection-claims/index.js +8 -0
- package/dist/_internal/server/features/refunds/actions.d.ts +20 -0
- package/dist/_internal/server/features/refunds/actions.js +259 -17
- package/dist/_internal/server/features/refunds/capacity.d.ts +29 -0
- package/dist/_internal/server/features/refunds/capacity.js +37 -0
- package/dist/_internal/server/features/reviews/actions.js +3 -2
- package/dist/_internal/server/features/search/index-build/sources.js +20 -1
- package/dist/_internal/server/features/shipping/reconcile.d.ts +10 -1
- package/dist/_internal/server/features/shipping/reconcile.js +34 -3
- package/dist/_internal/server/features/shipping/reimbursement.d.ts +117 -0
- package/dist/_internal/server/features/shipping/reimbursement.js +223 -0
- package/dist/_internal/server/features/stores/tabs.d.ts +19 -4
- package/dist/_internal/server/features/stores/tabs.js +24 -4
- package/dist/_internal/server/functions/https.d.ts +31 -1
- package/dist/_internal/server/functions/https.js +52 -1
- package/dist/_internal/server/functions/scheduled.d.ts +72 -1
- package/dist/_internal/server/functions/scheduled.js +124 -1
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +62 -16
- package/dist/_internal/server/jobs/core/boxRollupReconcile.d.ts +46 -0
- package/dist/_internal/server/jobs/core/boxRollupReconcile.js +142 -0
- package/dist/_internal/server/jobs/core/dailyDataCleanup.js +149 -1
- package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +14 -0
- package/dist/_internal/server/jobs/core/dailyStatusDigest.js +79 -1
- package/dist/_internal/server/jobs/core/index.d.ts +2 -0
- package/dist/_internal/server/jobs/core/index.js +2 -0
- package/dist/_internal/server/jobs/core/marketPriceRollup.d.ts +64 -0
- package/dist/_internal/server/jobs/core/marketPriceRollup.js +153 -0
- package/dist/_internal/server/jobs/core/obligationSweep.d.ts +36 -0
- package/dist/_internal/server/jobs/core/obligationSweep.js +503 -0
- package/dist/_internal/server/jobs/core/paymentReconcile.d.ts +79 -0
- package/dist/_internal/server/jobs/core/paymentReconcile.js +614 -0
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +34 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +35 -4
- package/dist/_internal/server/jobs/handlers/boxRollupReconcile.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/boxRollupReconcile.js +15 -0
- package/dist/_internal/server/jobs/handlers/countersReconcileNow.d.ts +65 -0
- package/dist/_internal/server/jobs/handlers/countersReconcileNow.js +89 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +5 -0
- package/dist/_internal/server/jobs/handlers/index.js +5 -0
- package/dist/_internal/server/jobs/handlers/marketPriceRollup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/marketPriceRollup.js +2 -0
- package/dist/_internal/server/jobs/handlers/obligationSweep.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/obligationSweep.js +15 -0
- package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +30 -1
- package/dist/_internal/server/jobs/handlers/paymentReconcile.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/paymentReconcile.js +6 -0
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +71 -0
- package/dist/_internal/shared/checkout/coupon-discount.d.ts +73 -0
- package/dist/_internal/shared/checkout/coupon-discount.js +103 -0
- package/dist/_internal/shared/checkout/lanes.d.ts +42 -0
- package/dist/_internal/shared/checkout/lanes.js +46 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +149 -1
- package/dist/_internal/shared/checkout/order-math.js +251 -3
- package/dist/_internal/shared/checkout/refundable-base.d.ts +116 -0
- package/dist/_internal/shared/checkout/refundable-base.js +122 -0
- package/dist/_internal/shared/features/auctions/config.d.ts +14 -1
- package/dist/_internal/shared/features/auctions/config.js +30 -1
- package/dist/_internal/shared/features/brands/schema.d.ts +42 -0
- package/dist/_internal/shared/features/brands/schema.js +18 -0
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +22 -0
- package/dist/_internal/shared/features/orders/refund-copy.js +34 -0
- package/dist/_internal/shared/features/print/label-profiles.d.ts +198 -0
- package/dist/_internal/shared/features/print/label-profiles.js +350 -0
- package/dist/_internal/shared/features/products/to-product-item.js +33 -5
- package/dist/_internal/shared/fees/calculator.d.ts +31 -2
- package/dist/_internal/shared/fees/calculator.js +103 -5
- package/dist/_internal/shared/fees/india-states.d.ts +195 -0
- package/dist/_internal/shared/fees/india-states.js +190 -0
- package/dist/_internal/shared/fees/payout-hold.d.ts +80 -0
- package/dist/_internal/shared/fees/payout-hold.js +74 -0
- package/dist/_internal/shared/obligations/deadlines.d.ts +94 -0
- package/dist/_internal/shared/obligations/deadlines.js +155 -0
- package/dist/_internal/shared/shipping/quote.d.ts +96 -0
- package/dist/_internal/shared/shipping/quote.js +67 -0
- package/dist/_internal/shared/time/business-days.d.ts +91 -0
- package/dist/_internal/shared/time/business-days.js +185 -0
- package/dist/cli/index.js +0 -2
- package/dist/client.d.ts +10 -4
- package/dist/client.js +29 -3
- package/dist/configs/tailwind.d.ts +0 -12
- package/dist/configs/tailwind.js +28 -15
- package/dist/constants/api-endpoints.d.ts +51 -9
- package/dist/constants/api-endpoints.js +23 -7
- package/dist/constants/index.d.ts +1 -1
- package/dist/constants/index.js +1 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/payment.d.ts +131 -0
- package/dist/features/about/components/AboutView.js +1 -1
- package/dist/features/about/components/DeveloperView.js +5 -2
- package/dist/features/about/components/FAQPageView.js +2 -1
- package/dist/features/about/components/FeesView.js +2 -1
- package/dist/features/about/components/HelpPageView.js +2 -1
- package/dist/features/about/components/HowAuctionsWorkView.js +2 -1
- package/dist/features/about/components/HowCheckoutWorksView.js +2 -1
- package/dist/features/about/components/HowEmiWorksView.js +2 -1
- package/dist/features/about/components/HowOffersWorkView.js +2 -1
- package/dist/features/about/components/HowOrdersWorkView.js +2 -1
- package/dist/features/about/components/HowPayoutsWorkView.js +2 -1
- package/dist/features/about/components/HowPreOrdersWorkView.js +2 -1
- package/dist/features/about/components/HowReviewsWorkView.js +2 -1
- package/dist/features/about/components/PolicyPageView.js +2 -1
- package/dist/features/about/components/ProseHero.d.ts +73 -0
- package/dist/features/about/components/ProseHero.js +18 -0
- package/dist/features/about/components/SecurityPrivacyView.js +2 -1
- package/dist/features/about/components/ShippingPolicyView.js +2 -1
- package/dist/features/about/components/TrackOrderView.js +2 -1
- package/dist/features/about/components/UnauthorizedView.js +1 -1
- package/dist/features/about/index.d.ts +2 -0
- package/dist/features/about/index.js +5 -0
- package/dist/features/account/actions/address-actions.d.ts +26 -2
- package/dist/features/account/actions/address-actions.js +25 -1
- package/dist/features/account/components/BuyerShoppingGuideView.js +1 -1
- package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
- package/dist/features/account/components/UserOrdersView.js +17 -0
- package/dist/features/account/components/UserSidebar.js +1 -1
- package/dist/features/account/hooks/useNotifications.js +29 -0
- package/dist/features/admin/actions/admin-coupon-actions.js +36 -0
- package/dist/features/admin/components/AdminBeysView.js +2 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
- package/dist/features/admin/components/AdminBrandEditorView.js +32 -2
- package/dist/features/admin/components/AdminBundleEditorView.js +1 -1
- package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
- package/dist/features/admin/components/AdminCategoriesView.js +1 -1
- package/dist/features/admin/components/AdminCategoryEditorView.js +73 -17
- package/dist/features/admin/components/AdminContactEditorView.js +1 -1
- package/dist/features/admin/components/AdminCouponEditorView.js +26 -1
- package/dist/features/admin/components/AdminFaqEditorView.js +1 -1
- package/dist/features/admin/components/AdminFaqsView.js +2 -2
- package/dist/features/admin/components/AdminGatewayEventsView.d.ts +54 -0
- package/dist/features/admin/components/AdminGatewayEventsView.js +209 -0
- package/dist/features/admin/components/AdminGuideHubView.js +1 -1
- package/dist/features/admin/components/AdminMoneyCasesView.d.ts +60 -0
- package/dist/features/admin/components/AdminMoneyCasesView.js +287 -0
- package/dist/features/admin/components/AdminPartsView.js +2 -2
- package/dist/features/admin/components/AdminProductsView.js +75 -4
- package/dist/features/admin/components/AdminSidebar.js +2 -2
- package/dist/features/admin/components/AdminSiteSettingsView.js +65 -7
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
- package/dist/features/admin/components/AdminSupportTicketsView.js +2 -2
- package/dist/features/admin/components/AdminTesterChecklistView.js +2 -2
- package/dist/features/admin/components/AdminUsersView.js +72 -1
- package/dist/features/admin/components/AdminViewCards.d.ts +3 -1
- package/dist/features/admin/components/AdminViewCards.js +4 -4
- package/dist/features/admin/components/DataListingView.d.ts +28 -0
- package/dist/features/admin/components/DataListingView.js +175 -14
- package/dist/features/admin/components/DataTable.d.ts +12 -1
- package/dist/features/admin/components/DataTable.js +3 -3
- package/dist/features/admin/components/OwnerQuickActions.d.ts +1 -1
- package/dist/features/admin/components/OwnerQuickActions.js +60 -2
- package/dist/features/admin/components/RecordOwnerQuickActions.d.ts +1 -1
- package/dist/features/admin/components/RecordOwnerQuickActions.js +61 -14
- package/dist/features/admin/components/analytics/AdminStatCard.d.ts +35 -1
- package/dist/features/admin/components/analytics/AdminStatCard.js +77 -2
- package/dist/features/admin/components/index.d.ts +4 -0
- package/dist/features/admin/components/index.js +6 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +156 -0
- package/dist/features/admin/constants/filter-tabs.js +106 -0
- package/dist/features/admin/repository/site-settings.repository.d.ts +22 -0
- package/dist/features/admin/repository/site-settings.repository.js +82 -1
- package/dist/features/admin/schemas/admin-editor-forms.d.ts +21 -0
- package/dist/features/admin/schemas/admin-editor-forms.js +44 -0
- package/dist/features/admin/schemas/firestore.js +5 -1
- package/dist/features/admin/schemas/site-settings-form.js +30 -1
- package/dist/features/auctions/actions/bid-actions.js +13 -1
- package/dist/features/auctions/components/AuctionBottomActions.js +2 -2
- package/dist/features/auctions/components/AuctionDetailPageView.js +3 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -0
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +5 -3
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +8 -1
- package/dist/features/auctions/components/PlaceBidFormClient.js +2 -2
- package/dist/features/audit-log/schemas/firestore.d.ts +28 -0
- package/dist/features/audit-log/schemas/firestore.js +14 -0
- package/dist/features/auth/permissions/constants.d.ts +17 -1
- package/dist/features/auth/permissions/constants.js +18 -0
- package/dist/features/blog/components/BlogPostView.js +1 -1
- package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
- package/dist/features/categories/api/[id]/description-template/route.js +107 -1
- package/dist/features/categories/components/BrandDetailPageView.js +2 -2
- package/dist/features/categories/components/BreadcrumbTrail.js +7 -1
- package/dist/features/categories/components/BundleDetailView.js +1 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
- package/dist/features/categories/components/CategoryForm.js +12 -1
- package/dist/features/categories/components/CategoryTemplatePicker.js +38 -5
- package/dist/features/categories/repository/market-price-index.repository.d.ts +123 -0
- package/dist/features/categories/repository/market-price-index.repository.js +105 -0
- package/dist/features/categories/repository/price-observation.repository.d.ts +166 -0
- package/dist/features/categories/repository/price-observation.repository.js +132 -0
- package/dist/features/events/components/EventSaleBanner.js +1 -1
- package/dist/features/events/components/EventStatusBadge.js +1 -1
- package/dist/features/faq/components/FAQAccordion.js +2 -1
- package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
- package/dist/features/faq/components/FAQHelpfulButtons.js +4 -4
- package/dist/features/faq/components/RelatedFAQs.js +1 -1
- package/dist/features/filters/FilterFacetSection.js +1 -1
- package/dist/features/filters/SwitchFilter.js +1 -1
- package/dist/features/gateway-events/repository/gateway-events.repository.d.ts +117 -0
- package/dist/features/gateway-events/repository/gateway-events.repository.js +274 -0
- package/dist/features/gateway-events/schemas/firestore.d.ts +217 -0
- package/dist/features/gateway-events/schemas/firestore.js +125 -0
- package/dist/features/gateway-events/schemas/index.d.ts +81 -0
- package/dist/features/gateway-events/schemas/index.js +79 -0
- package/dist/features/grouped/components/GroupedListingDetailView.js +1 -1
- package/dist/features/homepage/components/AnnouncementBar.js +37 -1
- package/dist/features/homepage/components/AnnouncementBar.style.css +30 -0
- package/dist/features/homepage/components/BrandsSection.js +1 -1
- package/dist/features/homepage/components/CTABannerSection.js +1 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +14 -24
- package/dist/features/homepage/components/HeroCarousel.js +2 -2
- package/dist/features/homepage/components/SocialPostCard.js +1 -1
- package/dist/features/inventory/components/AdminMoveLedgerView.d.ts +7 -0
- package/dist/features/inventory/components/AdminMoveLedgerView.js +157 -0
- package/dist/features/inventory/components/AdminStorageBoxesView.d.ts +11 -0
- package/dist/features/inventory/components/AdminStorageBoxesView.js +168 -0
- package/dist/features/inventory/components/ReceiveStationView.d.ts +39 -0
- package/dist/features/inventory/components/ReceiveStationView.js +413 -0
- package/dist/features/inventory/components/operator-message.d.ts +29 -0
- package/dist/features/inventory/components/operator-message.js +41 -0
- package/dist/features/inventory/index.d.ts +13 -0
- package/dist/features/inventory/index.js +28 -0
- package/dist/features/inventory/repository/inventory-movements.repository.d.ts +97 -0
- package/dist/features/inventory/repository/inventory-movements.repository.js +186 -0
- package/dist/features/inventory/repository/inventory-placements.repository.d.ts +179 -0
- package/dist/features/inventory/repository/inventory-placements.repository.js +345 -0
- package/dist/features/inventory/repository/storage-boxes.repository.d.ts +83 -0
- package/dist/features/inventory/repository/storage-boxes.repository.js +127 -0
- package/dist/features/inventory/schemas/firestore.d.ts +197 -0
- package/dist/features/inventory/schemas/firestore.js +159 -0
- package/dist/features/layout/AppLayoutShell.d.ts +12 -1
- package/dist/features/layout/AppLayoutShell.js +25 -12
- package/dist/features/layout/AutoBreadcrumbs.js +162 -3
- package/dist/features/layout/AutoBreadcrumbs.style.css +45 -0
- package/dist/features/layout/BottomNavbar.d.ts +1 -1
- package/dist/features/layout/BottomNavbar.js +2 -2
- package/dist/features/media/MediaAvatar.js +1 -1
- package/dist/features/media/MediaImage.d.ts +24 -4
- package/dist/features/media/MediaImage.js +44 -4
- package/dist/features/media/MediaLightbox.js +1 -1
- package/dist/features/media/modals/ImageCropModal.js +1 -1
- package/dist/features/media/modals/ImageEditor.js +1 -1
- package/dist/features/media/upload/MediaUploadField.js +91 -3
- package/dist/features/obligations/constants/policies.d.ts +92 -0
- package/dist/features/obligations/constants/policies.js +378 -0
- package/dist/features/obligations/repository/obligations.repository.d.ts +68 -0
- package/dist/features/obligations/repository/obligations.repository.js +158 -0
- package/dist/features/obligations/schemas/firestore.d.ts +160 -0
- package/dist/features/obligations/schemas/firestore.js +87 -0
- package/dist/features/orders/actions/order-actions.d.ts +43 -4
- package/dist/features/orders/actions/order-actions.js +60 -3
- package/dist/features/orders/actions/refund-actions.js +70 -10
- package/dist/features/orders/components/OrderCompletionPanel.d.ts +27 -0
- package/dist/features/orders/components/OrderCompletionPanel.js +192 -0
- package/dist/features/orders/components/OrdersList.js +2 -2
- package/dist/features/orders/components/RefundHistoryTable.js +3 -1
- package/dist/features/orders/components/index.d.ts +2 -0
- package/dist/features/orders/components/index.js +6 -0
- package/dist/features/orders/constants/transitions.d.ts +59 -0
- package/dist/features/orders/constants/transitions.js +104 -0
- package/dist/features/orders/repository/orders.repository.d.ts +109 -6
- package/dist/features/orders/repository/orders.repository.js +464 -21
- package/dist/features/orders/schemas/firestore.d.ts +270 -0
- package/dist/features/orders/schemas/firestore.js +24 -0
- package/dist/features/orders/schemas/index.d.ts +61 -0
- package/dist/features/orders/schemas/index.js +22 -0
- package/dist/features/orders/types/index.d.ts +79 -0
- package/dist/features/parts/components/PartsIndexPageView.js +8 -1
- package/dist/features/payment-cases/repository/payment-cases.repository.d.ts +170 -0
- package/dist/features/payment-cases/repository/payment-cases.repository.js +342 -0
- package/dist/features/payment-cases/schemas/firestore.d.ts +229 -0
- package/dist/features/payment-cases/schemas/firestore.js +105 -0
- package/dist/features/payment-cases/schemas/resolve-form.d.ts +97 -0
- package/dist/features/payment-cases/schemas/resolve-form.js +114 -0
- package/dist/features/payments/repository/payout.repository.d.ts +0 -1
- package/dist/features/payments/repository/payout.repository.js +0 -1
- package/dist/features/points/repository/points.repository.d.ts +122 -0
- package/dist/features/points/repository/points.repository.js +304 -1
- package/dist/features/points/schemas/adjust-form.d.ts +58 -0
- package/dist/features/points/schemas/adjust-form.js +63 -0
- package/dist/features/points/schemas/firestore.d.ts +108 -1
- package/dist/features/points/schemas/firestore.js +84 -0
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -3
- package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
- package/dist/features/price-index/components/PriceIndexToolView.d.ts +23 -4
- package/dist/features/price-index/components/PriceIndexToolView.js +71 -6
- package/dist/features/price-index/market-stats.d.ts +82 -0
- package/dist/features/price-index/market-stats.js +230 -0
- package/dist/features/print/components/BoxLabelPrintView.d.ts +25 -0
- package/dist/features/print/components/BoxLabelPrintView.js +66 -0
- package/dist/features/print/components/LabelProfilePicker.d.ts +13 -0
- package/dist/features/print/components/LabelProfilePicker.js +38 -0
- package/dist/features/print/components/labels.d.ts +117 -0
- package/dist/features/print/components/labels.js +258 -0
- package/dist/features/print/components/labels.style.css +46 -0
- package/dist/features/print/index.d.ts +6 -0
- package/dist/features/print/index.js +16 -0
- package/dist/features/products/columns/productTableColumns.js +5 -4
- package/dist/features/products/components/MarketplaceBundleCard.js +2 -2
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +6 -6
- package/dist/features/products/components/PrizeDrawCollage.js +1 -1
- package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
- package/dist/features/products/components/PrizeRevealModal.js +2 -2
- package/dist/features/products/components/ProductDetailPageView.js +18 -4
- package/dist/features/products/components/ProductForm.js +34 -23
- package/dist/features/products/components/ProductGrid.js +86 -62
- package/dist/features/products/constants/card-badges.d.ts +97 -0
- package/dist/features/products/constants/card-badges.js +100 -0
- package/dist/features/products/repository/products.repository.js +34 -1
- package/dist/features/products/schemas/firestore.d.ts +46 -3
- package/dist/features/products/schemas/firestore.js +21 -0
- package/dist/features/promotions/components/PromotionsView.js +1 -1
- package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +32 -0
- package/dist/features/promotions/repository/claimed-coupons.repository.js +58 -0
- package/dist/features/promotions/schemas/firestore.js +30 -3
- package/dist/features/promotions/schemas/index.d.ts +6 -6
- package/dist/features/protection-claims/repository/protection-claims.repository.d.ts +54 -0
- package/dist/features/protection-claims/repository/protection-claims.repository.js +174 -0
- package/dist/features/protection-claims/schemas/firestore.d.ts +168 -0
- package/dist/features/protection-claims/schemas/firestore.js +130 -0
- package/dist/features/reviews/actions/review-actions.js +3 -2
- package/dist/features/reviews/components/ReviewModal.js +1 -1
- package/dist/features/reviews/components/ReviewsList.js +1 -1
- package/dist/features/reviews/schemas/firestore.d.ts +20 -1
- package/dist/features/scams/constants/scam-types.d.ts +2 -0
- package/dist/features/scams/constants/scam-types.js +18 -0
- package/dist/features/scams/schemas/report-form.d.ts +3 -3
- package/dist/features/scams/schemas/report-form.js +26 -1
- package/dist/features/search/components/Search.js +9 -9
- package/dist/features/search/components/SearchResultsClient.js +18 -3
- package/dist/features/search/components/SearchResultsPageView.d.ts +1 -6
- package/dist/features/search/components/SearchResultsPageView.js +2 -2
- package/dist/features/seller/actions/offer-actions.js +16 -1
- package/dist/features/seller/actions/seller-actions.js +14 -3
- package/dist/features/seller/components/CategoryInlineSelect.js +75 -11
- package/dist/features/seller/components/PrintCenterView.d.ts +5 -0
- package/dist/features/seller/components/PrintCenterView.js +41 -30
- package/dist/features/seller/components/SellerOrdersView.js +1 -1
- package/dist/features/seller/components/SellerPayoutRequestView.js +16 -5
- package/dist/features/seller/components/SellerPayoutSettingsView.js +1 -1
- package/dist/features/seller/components/SellerProductShell.d.ts +16 -0
- package/dist/features/seller/components/SellerProductShell.js +123 -13
- package/dist/features/seller/components/SellerReviewsView.js +4 -2
- package/dist/features/seller/components/SellerSidebar.js +1 -1
- package/dist/features/seller/schemas/coupon-form.d.ts +26 -0
- package/dist/features/seller/schemas/coupon-form.js +27 -0
- package/dist/features/seller/schemas/product-form.d.ts +3 -0
- package/dist/features/seller/schemas/product-form.js +5 -0
- package/dist/features/shell/FormShell.js +2 -2
- package/dist/features/shell/QuickFormDrawer.js +14 -0
- package/dist/features/shell/SectionForm.d.ts +19 -0
- package/dist/features/shell/SectionForm.js +133 -9
- package/dist/features/shell/SectionForm.style.css +54 -0
- package/dist/features/shipments/components/AdminShipmentsView.js +16 -1
- package/dist/features/shipments/repository/shipment-items.repository.d.ts +9 -0
- package/dist/features/shipments/repository/shipment-items.repository.js +14 -0
- package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
- package/dist/features/sticker-trace/components/StickerCommitPanel.js +1 -1
- package/dist/features/store-extensions/schemas/report-review-form.d.ts +8 -8
- package/dist/features/stores/components/StoreGuideHubView.js +1 -1
- package/dist/features/stores/components/StoreHeader.js +1 -1
- package/dist/features/support/schemas/firestore.d.ts +73 -2
- package/dist/features/support/schemas/firestore.js +59 -0
- package/dist/index.d.ts +16 -24
- package/dist/index.js +38 -47
- package/dist/next/ErrorBoundary.js +1 -1
- package/dist/next/routing/route-map.d.ts +24 -0
- package/dist/next/routing/route-map.js +40 -0
- package/dist/providers/payment-manual/index.d.ts +15 -1
- package/dist/providers/payment-manual/index.js +37 -0
- package/dist/providers/payment-phonepe/index.d.ts +14 -1
- package/dist/providers/payment-phonepe/index.js +36 -0
- package/dist/providers/payment-razorpay/index.d.ts +44 -1
- package/dist/providers/payment-razorpay/index.js +99 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +18 -0
- package/dist/providers/shipping-shiprocket/index.js +18 -0
- package/dist/schemas/registry.d.ts +44 -11
- package/dist/server-entry.d.ts +2 -0
- package/dist/server-entry.js +27 -0
- package/dist/server.d.ts +43 -0
- package/dist/server.js +98 -0
- package/dist/styles.css +418 -149
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +15 -12
- package/dist/tokens/themes/default-light.js +14 -11
- package/dist/tokens/tokens.css +106 -23
- package/dist/ui/DataTable.style.css +4 -4
- package/dist/ui/columns/cell-renderers.d.ts +20 -0
- package/dist/ui/columns/cell-renderers.js +26 -1
- package/dist/ui/columns/index.d.ts +1 -1
- package/dist/ui/columns/index.js +4 -1
- package/dist/ui/components/Badge.style.css +1 -1
- package/dist/ui/components/Breadcrumb.js +19 -1
- package/dist/ui/components/BulkActionBar.style.css +4 -4
- package/dist/ui/components/Button.style.css +1 -1
- package/dist/ui/components/Checkbox.style.css +1 -1
- package/dist/ui/components/FallbackShell.js +1 -1
- package/dist/ui/components/Form.d.ts +14 -1
- package/dist/ui/components/Form.js +2 -2
- package/dist/ui/components/FormField.js +18 -4
- package/dist/ui/components/FormField.style.css +43 -3
- package/dist/ui/components/GatedPrice.style.css +1 -1
- package/dist/ui/components/IconButton.style.css +1 -1
- package/dist/ui/components/Input.js +22 -1
- package/dist/ui/components/Input.style.css +38 -22
- package/dist/ui/components/ListingFilterDrawer.js +1 -1
- package/dist/ui/components/ListingToolbar.js +1 -1
- package/dist/ui/components/PaginatedSelect.js +0 -0
- package/dist/ui/components/Radio.style.css +1 -1
- package/dist/ui/components/Repeater.d.ts +53 -0
- package/dist/ui/components/Repeater.js +7 -0
- package/dist/ui/components/RichTextEditor.d.ts +20 -1
- package/dist/ui/components/RichTextEditor.js +23 -2
- package/dist/ui/components/Select.style.css +7 -21
- package/dist/ui/components/Semantic.js +27 -3
- package/dist/ui/components/TagInput.style.css +4 -9
- package/dist/ui/components/Textarea.style.css +4 -18
- package/dist/ui/components/Toggle.style.css +1 -1
- package/dist/ui/components/Typography.d.ts +3 -3
- package/dist/ui/components/Typography.js +15 -15
- package/dist/ui/components/ViewToggle.style.css +4 -4
- package/dist/ui/forms/AddAnotherToggle.d.ts +16 -0
- package/dist/ui/forms/AddAnotherToggle.js +56 -0
- package/dist/ui/forms/ColorPickerField.js +15 -2
- package/dist/ui/forms/FieldCheckbox.js +15 -2
- package/dist/ui/forms/FieldInput.d.ts +22 -1
- package/dist/ui/forms/FieldInput.js +19 -5
- package/dist/ui/forms/FieldLabel.d.ts +35 -0
- package/dist/ui/forms/FieldLabel.js +70 -0
- package/dist/ui/forms/FieldSelect.d.ts +4 -1
- package/dist/ui/forms/FieldSelect.js +27 -5
- package/dist/ui/forms/FieldTextarea.d.ts +4 -1
- package/dist/ui/forms/FieldTextarea.js +22 -5
- package/dist/ui/forms/FormShell.d.ts +17 -1
- package/dist/ui/forms/FormShell.js +36 -2
- package/dist/ui/forms/FormShell.style.css +41 -33
- package/dist/ui/forms/index.d.ts +5 -1
- package/dist/ui/forms/index.js +2 -0
- package/dist/ui/icons/icon-registry.d.ts +1 -0
- package/dist/ui/icons/icon-registry.js +17 -1
- package/dist/utils/id-generators.d.ts +80 -0
- package/dist/utils/id-generators.js +93 -0
- package/dist/utils/search-txt-builders.d.ts +12 -0
- package/dist/utils/search-txt-builders.js +17 -0
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +106 -5
- package/dist/features/loyalty/api/admin/loyalty/grant/route.d.ts +0 -12
- package/dist/features/loyalty/api/admin/loyalty/grant/route.js +0 -38
- package/dist/features/loyalty/api/balance/route.d.ts +0 -12
- package/dist/features/loyalty/api/balance/route.js +0 -41
- package/dist/features/loyalty/api/earn/route.d.ts +0 -12
- package/dist/features/loyalty/api/earn/route.js +0 -38
- package/dist/features/loyalty/api/history/route.d.ts +0 -12
- package/dist/features/loyalty/api/history/route.js +0 -58
- package/dist/features/loyalty/api/redeem/route.d.ts +0 -12
- package/dist/features/loyalty/api/redeem/route.js +0 -38
- package/dist/features/loyalty/columns/index.d.ts +0 -17
- package/dist/features/loyalty/columns/index.js +0 -40
- package/dist/features/loyalty/components/CoinsDisplay.d.ts +0 -12
- package/dist/features/loyalty/components/CoinsDisplay.js +0 -10
- package/dist/features/loyalty/components/index.d.ts +0 -1
- package/dist/features/loyalty/components/index.js +0 -1
- package/dist/features/loyalty/hooks/useLoyaltyBalance.d.ts +0 -4
- package/dist/features/loyalty/hooks/useLoyaltyBalance.js +0 -25
- package/dist/features/loyalty/index.d.ts +0 -6
- package/dist/features/loyalty/index.js +0 -6
- package/dist/features/loyalty/manifest.d.ts +0 -2
- package/dist/features/loyalty/manifest.js +0 -16
- package/dist/features/loyalty/messages/en.json +0 -19
- package/dist/features/loyalty/repository/loyalty.repository.d.ts +0 -8
- package/dist/features/loyalty/repository/loyalty.repository.js +0 -22
- package/dist/features/loyalty/schemas/index.d.ts +0 -85
- package/dist/features/loyalty/schemas/index.js +0 -40
- package/dist/features/loyalty/server.d.ts +0 -6
- package/dist/features/loyalty/server.js +0 -6
- package/dist/features/loyalty/types/index.d.ts +0 -31
- package/dist/features/loyalty/types/index.js +0 -1
- package/dist/features/loyalty/types/loyalty-config.d.ts +0 -7
- package/dist/features/loyalty/types/loyalty-config.js +0 -1
- package/dist/features/loyalty/types/loyalty-math.d.ts +0 -9
- package/dist/features/loyalty/types/loyalty-math.js +0 -27
- package/dist/features/products/components/ProductOwnerQuickActions.d.ts +0 -11
- package/dist/features/products/components/ProductOwnerQuickActions.js +0 -120
|
@@ -598,13 +598,13 @@ export async function CategoryDetailPageView({ slug }) {
|
|
|
598
598
|
const ldFaq = category?.faqs && category.faqs.length > 0
|
|
599
599
|
? faqJsonLd(category.faqs.map((f) => ({ question: f.question, answer: f.answer })))
|
|
600
600
|
: null;
|
|
601
|
-
return (_jsxs(Main, { children: [_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(ldBreadcrumb) } }), _jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(ldCollection) } }), ldFaq && (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(ldFaq) } })), _jsxs(Section, { className: `relative ${__O.hidden} ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-[var(--appkit-color-bg)]"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx(Div, { className: "absolute inset-0", children: _jsx(MediaImage, { src: coverImage, alt: "", size: "banner" }) }), _jsx(Div, { surface: "overlay-md", className: "absolute inset-0" })] })), !hasCover && hasMosaic && (_jsx(Div, { className: "relative z-10 max-w-7xl mx-auto", padding: "x-md", children: _jsx(Div, { padding: "t-lg", children: _jsx(ImageMosaic, { images: mosaicImages, alt: `${category?.name ?? slug} on LetItRip` }) }) })), _jsxs(Div, { className: "relative z-10 max-w-7xl mx-auto", paddingY: hasCover ? "y-3xl" : "y-xl", padding: "x-md", children: [_jsx(BreadcrumbTrail, { crumbs: trail, className: "mb-4", tone: hasCover ? "on-media" : "default" }), category?.id && _jsx(LastCategoryBeacon, { categoryId: category.id }), category?.id && (_jsx(RecordOwnerQuickActions, { resource: "CATEGORY", recordId: category.id, placement: "detail",
|
|
601
|
+
return (_jsxs(Main, { children: [_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(ldBreadcrumb) } }), _jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(ldCollection) } }), ldFaq && (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(ldFaq) } })), _jsxs(Section, { className: `relative ${__O.hidden} ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-[var(--appkit-color-bg)]"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx(Div, { className: "absolute inset-0", children: _jsx(MediaImage, { src: coverImage, alt: "", size: "banner" }) }), _jsx(Div, { surface: "overlay-md", className: "absolute inset-0" })] })), !hasCover && hasMosaic && (_jsx(Div, { className: "relative z-10 max-w-7xl mx-auto", padding: "x-md", children: _jsx(Div, { padding: "t-lg", children: _jsx(ImageMosaic, { images: mosaicImages, alt: `${category?.name ?? slug} on LetItRip` }) }) })), _jsxs(Div, { className: "relative z-10 max-w-7xl mx-auto", paddingY: hasCover ? "y-3xl" : "y-xl", padding: "x-md", children: [_jsx(BreadcrumbTrail, { crumbs: trail, className: "mb-4", tone: hasCover ? "on-media" : "default" }), category?.id && _jsx(LastCategoryBeacon, { categoryId: category.id }), _jsx(Heading, { color: "inverse", level: 1, className: `mb-2 ${hasCover ? "" : "text-[var(--appkit-color-text)]"}`, size: "3xl", mdSize: "4xl", weight: "bold", children: category?.name ?? slug }), category?.id && (_jsx(RecordOwnerQuickActions, { resource: "CATEGORY", recordId: category.id, placement: "detail",
|
|
602
602
|
/*
|
|
603
603
|
* Same flag the rest of this hero branches on. Without it the
|
|
604
604
|
* pencil renders `ghost` (zinc-600) on the dark cover and is
|
|
605
605
|
* invisible β see Root Cause #67 and `OwnerQuickActions.onMedia`.
|
|
606
606
|
*/
|
|
607
|
-
onMedia: hasCover, className: "mb-3", recordLabel: category.name ?? slug })),
|
|
607
|
+
onMedia: hasCover, className: "mb-3", recordLabel: category.name ?? slug })), category?.description && typeof category.description === "string" && !category.description.startsWith("{") && (_jsx(Text, { color: hasCover ? "inverse" : "muted", className: "max-w-2xl mb-4", size: "base", children: category.description })), _jsxs(Row, { wrap: true, gap: "sm", children: [productCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: "inverse", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400", rounded: "full", padding: "pill-sm-tall", children: [productCount.toLocaleString(), " ", productCount === 1 ? "product" : "products"] })), showCountSplit && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "muted", size: "xs", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [ownHere.toLocaleString(), " here \u00B7 ", inSubcategories.toLocaleString(), " in subcategories"] })), auctionCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "warning", surface: hasCover ? undefined : "warning-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [auctionCount.toLocaleString(), " ", auctionCount === 1 ? "auction" : "auctions"] })), preOrderCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "info", surface: hasCover ? undefined : "info-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [preOrderCount.toLocaleString(), " ", preOrderCount === 1 ? "pre-order" : "pre-orders"] })), storeCount > 0 && (_jsxs(Span, { layout: "inline-flex", gap: "xs", color: hasCover ? "inverse" : "success", surface: hasCover ? undefined : "success-surface", size: "xs", weight: "medium", className: hasCover ? "bg-[rgba(255,255,255,0.2)] backdrop-blur-sm" : "", rounded: "full", padding: "pill-sm-tall", children: [storeCount.toLocaleString(), " ", storeCount === 1 ? "store" : "stores"] }))] })] })] }), category?.hasbroName && (_jsx(Section, { padding: "y-md", children: _jsx(Container, { size: "xl", children: _jsxs(Row, { gap: "md", align: "center", wrap: true, children: [category.hasbroCoverImage && (_jsx(MediaImage, { src: category.hasbroCoverImage, alt: `${category.hasbroName} logo`, size: "thumbnail", className: "h-12 w-auto flex-shrink-0" })), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", transform: "uppercase", weight: "medium", children: "Also known as" }), _jsx(Text, { weight: "semibold", children: category.hasbroName }), _jsxs(Text, { size: "xs", color: "muted", children: ["The same wave, named differently outside Japan \u2014 Takara-Tomy releases it as ", category.name, ", Hasbro as ", category.hasbroName, ". Both names find these listings."] })] })] }) }) })), contentBody && contentBody.trim().length > 0 && (_jsx(Section, { padding: "y-lg", children: _jsx(Container, { size: "xl", children: _jsx(RichTextRenderer, { html: contentBody }) }) })), _jsx(CategoryHighlightsAndFaqSection, { highlights: category?.highlights, faqs: category?.faqs }), pairedBeys.length > 0 && (_jsx(Section, { border: "subtle", surface: "default", className: "border-b", children: _jsx(Div, { className: __W, padding: "section", children: _jsxs(Stack, { gap: "sm", className: "min-w-0", children: [_jsx(Heading, { level: 2, size: "lg", children: pairedBeys.length === 1 ? "Specifications" : "Releases" }), _jsx(Stack, { gap: "xs", className: "min-w-0", children: pairedBeys.map((bey) => (_jsxs(Row, { gap: "sm", align: "center", wrap: true, className: "min-w-0", children: [_jsx(TextLink, { href: String(ROUTES.PUBLIC.BEY_DETAIL(bey.slug)), children: bey.name }), [bey.system, bey.productCodes?.[0]]
|
|
608
608
|
.filter(Boolean)
|
|
609
609
|
.map((bit) => (_jsx(Text, { size: "sm", color: "muted", children: String(bit) }, String(bit))))] }, bey.id))) })] }) }) })), compositions.length > 0 && (_jsx(Section, { border: "subtle", surface: "default", className: "border-b", children: _jsx(Div, { className: __W, padding: "section", children: _jsxs(Stack, { gap: "md", className: "min-w-0", children: [_jsx(Heading, { level: 2, size: "lg", children: "What it is made of" }), _jsx(ModelCompositionPanel, { compositions: compositions, hrefForPart: (slot, tail) => String(ROUTES.PUBLIC.PART_DETAIL(slot, tail)) })] }) }) })), (() => {
|
|
610
610
|
if (category?.tier !== 0)
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from "react";
|
|
3
4
|
import { Div, FormGroup } from "../../../ui/index.js";
|
|
4
5
|
import { Label, Checkbox, RichTextEditor, Text } from "../../../ui/index.js";
|
|
6
|
+
import { FieldLabel } from "../../../ui/forms/index.js";
|
|
5
7
|
import { ImageUpload, MediaImage, useMediaUpload } from "../../media/index.js";
|
|
6
8
|
import { FormField } from "../../../ui/index.js";
|
|
7
9
|
import { flattenCategories } from "../types/index.js";
|
|
@@ -25,6 +27,15 @@ const DEFAULT_LABELS = {
|
|
|
25
27
|
export function CategoryForm({ category, allCategories, onChange, isReadonly = false, labels = {}, stackClassName = "space-y-4", }) {
|
|
26
28
|
const { upload } = useMediaUpload();
|
|
27
29
|
const L = { ...DEFAULT_LABELS, ...labels };
|
|
30
|
+
/*
|
|
31
|
+
* Unique per instance via `useId`, not a literal. Two CategoryForms on one
|
|
32
|
+
* page would otherwise both emit the same label id, and `aria-labelledby`
|
|
33
|
+
* resolves the FIRST match β so the second editor would be named by the
|
|
34
|
+
* first formβs label. The same defect FieldInputβs own note records
|
|
35
|
+
* fixing for `htmlFor`.
|
|
36
|
+
*/
|
|
37
|
+
const reactId = useId();
|
|
38
|
+
const descriptionLabelId = `category-description-label-${reactId}`;
|
|
28
39
|
const update = (partial) => {
|
|
29
40
|
onChange({ ...category, ...partial });
|
|
30
41
|
};
|
|
@@ -35,7 +46,7 @@ export function CategoryForm({ category, allCategories, onChange, isReadonly = f
|
|
|
35
46
|
const handleNameChange = (value) => {
|
|
36
47
|
update({ name: value, slug: makeSlug(value, category.isBrand ?? false) });
|
|
37
48
|
};
|
|
38
|
-
return (_jsxs(Div, { className: stackClassName, children: [_jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "name", label: L.name, type: "text", value: category.name || "", onChange: handleNameChange, disabled: isReadonly }), _jsx(FormField, { name: "slug", label: L.slug, type: "text", value: category.slug || "", onChange: (value) => update({ slug: value }), disabled: isReadonly })] }), _jsxs(Div, { className: "appkit-form-field", children: [_jsx(
|
|
49
|
+
return (_jsxs(Div, { className: stackClassName, children: [_jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "name", label: L.name, type: "text", value: category.name || "", onChange: handleNameChange, disabled: isReadonly }), _jsx(FormField, { name: "slug", label: L.slug, type: "text", value: category.slug || "", onChange: (value) => update({ slug: value }), disabled: isReadonly })] }), _jsxs(Div, { className: "appkit-form-field", children: [_jsx(FieldLabel, { id: descriptionLabelId, children: L.description }), _jsx(RichTextEditor, { value: normalizeRichTextHtml(category.description || ""), onChange: (value) => update({ description: value }), disabled: isReadonly, minHeightClassName: "min-h-[140px]", placeholder: "Enter category description", ariaLabelledBy: descriptionLabelId }), _jsx(Text, { size: "sm", variant: "secondary", className: "appkit-form-field__hint", children: "Rich text is supported for category descriptions." })] }), !isReadonly && (_jsx(ImageUpload, { currentImage: category.display?.coverImage, onUpload: (file) => upload(file, "categories", true, {
|
|
39
50
|
type: "category-image",
|
|
40
51
|
name: category.name || "category",
|
|
41
52
|
}), onChange: (url) => update({ display: { ...category.display, coverImage: url } }), label: L.categoryImage, helperText: L.imageRecommended })), category.display?.coverImage && isReadonly && (_jsxs(Div, { children: [_jsx(Label, { className: "block mb-2", size: "sm", weight: "medium", children: L.categoryImage }), _jsx(Div, { className: `relative h-32 w-40 ${__O.hidden}`, rounded: "default", children: _jsx(MediaImage, { src: category.display.coverImage, alt: category.name || "", size: "card" }) })] })), _jsx(FormField, { name: "parentId", label: L.parentCategory, type: "select", value: category.parentId || "", onChange: (value) => update({ parentId: value || null }), disabled: isReadonly, options: [
|
|
@@ -70,12 +70,45 @@ function unwrap(res) {
|
|
|
70
70
|
const body = res?.data ?? res;
|
|
71
71
|
return body && typeof body === "object" && "template" in body ? body : null;
|
|
72
72
|
}
|
|
73
|
-
/**
|
|
74
|
-
|
|
73
|
+
/**
|
|
74
|
+
* `camelCase` / `snake_case` slot name -> words a seller reads.
|
|
75
|
+
*
|
|
76
|
+
* `categoryPath` -> "category path", `partBreakdown` -> "part breakdown".
|
|
77
|
+
* Lower-cased throughout because the note reads these mid-sentence.
|
|
78
|
+
*/
|
|
79
|
+
function humaniseSlot(name) {
|
|
80
|
+
return name
|
|
81
|
+
.replace(/[_-]+/g, " ")
|
|
82
|
+
.replace(/([a-z\d])([A-Z])/g, "$1 $2")
|
|
83
|
+
.trim()
|
|
84
|
+
.toLowerCase();
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* "2 details were left blank and are highlighted below: year, type"
|
|
88
|
+
*
|
|
89
|
+
* π NO `{{β¦}}` IN USER-FACING COPY. This printed the slot names back wrapped
|
|
90
|
+
* in braces β `2 of 9 slots could not be filled: {{year}}, {{type}}` β which
|
|
91
|
+
* is where the reported "raw {{slot}} syntax" came from. It was not a failed
|
|
92
|
+
* interpolation leaking through; the note RE-MANUFACTURED the template syntax
|
|
93
|
+
* to describe it, so the seller saw the same thing either way and no amount of
|
|
94
|
+
* fixing the templating would have removed it.
|
|
95
|
+
*
|
|
96
|
+
* "slots" went with the braces. It is the word this codebase uses for a
|
|
97
|
+
* placeholder; a seller filling in a listing has no reason to know it, and
|
|
98
|
+
* "could not be filled" reads as a system failure rather than as the three
|
|
99
|
+
* boxes they need to type in. The count stays β it is the actionable part β
|
|
100
|
+
* but it counts what is MISSING rather than reporting it as a fraction, since
|
|
101
|
+
* "2 of 9" invites the reader to wonder what the other seven were β and the
|
|
102
|
+
* denominator is why the `total` argument is gone.
|
|
103
|
+
*/
|
|
104
|
+
function unresolvedNote(unresolved) {
|
|
75
105
|
if (unresolved.length === 0)
|
|
76
106
|
return null;
|
|
77
|
-
const named = unresolved.map(
|
|
78
|
-
|
|
107
|
+
const named = unresolved.map(humaniseSlot).join(", ");
|
|
108
|
+
const count = unresolved.length;
|
|
109
|
+
return count === 1
|
|
110
|
+
? `One detail was left blank β add the ${named} before publishing.`
|
|
111
|
+
: `${count} details were left blank β add the ${named} before publishing.`;
|
|
79
112
|
}
|
|
80
113
|
/** "adds 2 specification rows Β· sets condition to Used" */
|
|
81
114
|
function describeDefaults(defaults) {
|
|
@@ -165,7 +198,7 @@ export function CategoryTemplatePicker({ categoryId, condition, context, isDescr
|
|
|
165
198
|
if (!data || !template || !filled) {
|
|
166
199
|
return (_jsx(Div, { className: PANEL_CLASS, children: _jsx(Text, { size: "xs", color: "muted", children: "No description template for this category yet \u2014 write your own, and an admin can add one so the next listing starts from it." }) }));
|
|
167
200
|
}
|
|
168
|
-
const note = unresolvedNote(filled.unresolved
|
|
201
|
+
const note = unresolvedNote(filled.unresolved);
|
|
169
202
|
const extras = describeDefaults(data.defaults);
|
|
170
203
|
const source = data.inheritedFromName
|
|
171
204
|
? `Inherited from ${data.inheritedFromName}`
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Market Price Index β the COMPUTED asking-price band for one leaf.
|
|
3
|
+
*
|
|
4
|
+
* The third collection in a deliberate trio, and the three are not
|
|
5
|
+
* interchangeable:
|
|
6
|
+
*
|
|
7
|
+
* `priceObservations` one dated sighting (raw, append-only)
|
|
8
|
+
* `marketPriceIndex` what asks cluster on (derived, this file)
|
|
9
|
+
* `priceIndex` what SOLD here (derived, our own orders)
|
|
10
|
+
*
|
|
11
|
+
* π The second and third are never blended, and the rule predates this file:
|
|
12
|
+
* *"They are rendered side by side and never averaged. The gap between them is
|
|
13
|
+
* the most useful thing we can tell a seller."* An ask is not a sale β 76β93%
|
|
14
|
+
* of crawled competitor listings are sold out without ever converting β so a
|
|
15
|
+
* blended figure would describe neither event and could not be unpicked later.
|
|
16
|
+
*
|
|
17
|
+
* ## π Why this is not written onto `categories`
|
|
18
|
+
*
|
|
19
|
+
* Same reason as its two siblings, and it is a measured one rather than a
|
|
20
|
+
* stylistic preference: `onCategoryWrite` is a `documentWritten` trigger on
|
|
21
|
+
* `categories`, and Root Cause #92 is a trigger that wrote back to the
|
|
22
|
+
* collection it watched producing **1,017,548 invocations in 24 hours**.
|
|
23
|
+
* `notifyRevalidate` also fires on every category write, so a nightly band
|
|
24
|
+
* write would drop each category page's ISR entry on a project that has
|
|
25
|
+
* already been paused once for ISR-write overage.
|
|
26
|
+
*
|
|
27
|
+
* ## π It does NOT replace the seeded band β it OUTRANKS it
|
|
28
|
+
*
|
|
29
|
+
* `CategoryPriceGuidance.market` stays exactly as seeded. A computed band wins
|
|
30
|
+
* where one exists, and where one does not the seeded generation band still
|
|
31
|
+
* shows, clearly labelled as the generation's. Overwriting the seed would mean
|
|
32
|
+
* a single bad import could erase the only evidence ~60 leaves have, with no
|
|
33
|
+
* way back β the same trap as `priceIndexRollup` being forbidden from touching
|
|
34
|
+
* `market`.
|
|
35
|
+
*
|
|
36
|
+
* @tag domain:price-index
|
|
37
|
+
* @tag layer:repository
|
|
38
|
+
* @tag pattern:repository
|
|
39
|
+
* @tag access:server-only
|
|
40
|
+
* @tag consumers:marketPriceRollup,price-guidance-resolve,price-index/market
|
|
41
|
+
* @tag sideEffects:writes marketPriceIndex
|
|
42
|
+
*/
|
|
43
|
+
import { BaseRepository } from "../../../providers/db-firebase";
|
|
44
|
+
import type { PriceObservationKind } from "./price-observation.repository";
|
|
45
|
+
/** One computed band. Mirrors `MarketStats` plus its provenance. */
|
|
46
|
+
export interface MarketPriceBand {
|
|
47
|
+
/**
|
|
48
|
+
* The price sightings cluster on β the headline.
|
|
49
|
+
*
|
|
50
|
+
* π Optional, and the renderer must branch. Absent means no bucket held two
|
|
51
|
+
* or more sightings, i.e. every observed price was different, so there is no
|
|
52
|
+
* "most common" to report and `median` is shown under a different label. A
|
|
53
|
+
* mode synthesised from a single sighting would be that sighting wearing a
|
|
54
|
+
* word that implies consensus.
|
|
55
|
+
*/
|
|
56
|
+
mode?: number;
|
|
57
|
+
modeCount?: number;
|
|
58
|
+
median: number;
|
|
59
|
+
low: number;
|
|
60
|
+
high: number;
|
|
61
|
+
sampleSize: number;
|
|
62
|
+
/**
|
|
63
|
+
* π FALSE means low/high/mode describe ALL TIME, not the last six months β
|
|
64
|
+
* the window held too little to stand alone. The renderer must relabel. A
|
|
65
|
+
* figure drawn from a two-year-old crawl is indistinguishable from a current
|
|
66
|
+
* one once printed, which is why this travels with the band rather than being
|
|
67
|
+
* inferred from `computedAt`.
|
|
68
|
+
*/
|
|
69
|
+
basedOnRecent: boolean;
|
|
70
|
+
windowDays: number;
|
|
71
|
+
/** Newest sighting in the set the band came from. Not the rollup time. */
|
|
72
|
+
lastSeenAt: Date;
|
|
73
|
+
}
|
|
74
|
+
export interface MarketPriceIndexDocument {
|
|
75
|
+
/** The leaf id β model, era, part group, whatever was observed. */
|
|
76
|
+
id: string;
|
|
77
|
+
currency: "INR";
|
|
78
|
+
/**
|
|
79
|
+
* Bands kept APART by event kind, never merged.
|
|
80
|
+
*
|
|
81
|
+
* Keyed by `PriceObservationKind`. `ask` is what shops list at; `sale` is
|
|
82
|
+
* what we have evidence changed hands at elsewhere. Most leaves will only
|
|
83
|
+
* ever have `ask`, and that is an honest state rather than a gap.
|
|
84
|
+
*/
|
|
85
|
+
byKind: Partial<Record<PriceObservationKind, MarketPriceBand>>;
|
|
86
|
+
computedAt: Date;
|
|
87
|
+
}
|
|
88
|
+
export declare const MARKET_PRICE_INDEX_COLLECTION = "marketPriceIndex";
|
|
89
|
+
export declare class MarketPriceIndexRepository extends BaseRepository<MarketPriceIndexDocument> {
|
|
90
|
+
constructor();
|
|
91
|
+
/**
|
|
92
|
+
* Bands for several leaves in ONE round trip.
|
|
93
|
+
*
|
|
94
|
+
* π `db.getAll`, not N `findById` calls β the same decision, for the same
|
|
95
|
+
* reason, as `PriceIndexRepository.getManyForLeaves`: the tool resolves up to
|
|
96
|
+
* `PRICE_INDEX_SEARCH_LIMIT` leaves per lookup and N sequential reads is N
|
|
97
|
+
* round trips against Rule #6's ~3-per-request budget.
|
|
98
|
+
*
|
|
99
|
+
* A missing document is OMITTED, never returned as an empty band β "no
|
|
100
|
+
* computed band here" and "this leaf does not exist" are different answers,
|
|
101
|
+
* and a zeroed band renders as a confident βΉ0.
|
|
102
|
+
*/
|
|
103
|
+
getManyForLeaves(leafIds: readonly string[]): Promise<Map<string, MarketPriceIndexDocument>>;
|
|
104
|
+
/**
|
|
105
|
+
* Write one leaf's bands.
|
|
106
|
+
*
|
|
107
|
+
* `byKind` is replaced WHOLESALE rather than merged: `update` merges, so a
|
|
108
|
+
* kind whose observations were all retracted would keep its stale band
|
|
109
|
+
* forever β the same reason `PriceIndexRepository.setForLeaf` passes the full
|
|
110
|
+
* map rather than a patch.
|
|
111
|
+
*/
|
|
112
|
+
setForLeaf(leafId: string, byKind: MarketPriceIndexDocument["byKind"]): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Drop a leaf's bands entirely.
|
|
115
|
+
*
|
|
116
|
+
* Reachable state, not defensive coding: a leaf whose observations are all
|
|
117
|
+
* retracted, or all fall below `MIN_BAND_OBSERVATIONS` after a retraction,
|
|
118
|
+
* must stop publishing a band. Leaving the last computed one in place would
|
|
119
|
+
* publish a figure derived from evidence that no longer exists.
|
|
120
|
+
*/
|
|
121
|
+
clearForLeaf(leafId: string): Promise<void>;
|
|
122
|
+
}
|
|
123
|
+
export declare const marketPriceIndexRepository: MarketPriceIndexRepository;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Market Price Index β the COMPUTED asking-price band for one leaf.
|
|
3
|
+
*
|
|
4
|
+
* The third collection in a deliberate trio, and the three are not
|
|
5
|
+
* interchangeable:
|
|
6
|
+
*
|
|
7
|
+
* `priceObservations` one dated sighting (raw, append-only)
|
|
8
|
+
* `marketPriceIndex` what asks cluster on (derived, this file)
|
|
9
|
+
* `priceIndex` what SOLD here (derived, our own orders)
|
|
10
|
+
*
|
|
11
|
+
* π The second and third are never blended, and the rule predates this file:
|
|
12
|
+
* *"They are rendered side by side and never averaged. The gap between them is
|
|
13
|
+
* the most useful thing we can tell a seller."* An ask is not a sale β 76β93%
|
|
14
|
+
* of crawled competitor listings are sold out without ever converting β so a
|
|
15
|
+
* blended figure would describe neither event and could not be unpicked later.
|
|
16
|
+
*
|
|
17
|
+
* ## π Why this is not written onto `categories`
|
|
18
|
+
*
|
|
19
|
+
* Same reason as its two siblings, and it is a measured one rather than a
|
|
20
|
+
* stylistic preference: `onCategoryWrite` is a `documentWritten` trigger on
|
|
21
|
+
* `categories`, and Root Cause #92 is a trigger that wrote back to the
|
|
22
|
+
* collection it watched producing **1,017,548 invocations in 24 hours**.
|
|
23
|
+
* `notifyRevalidate` also fires on every category write, so a nightly band
|
|
24
|
+
* write would drop each category page's ISR entry on a project that has
|
|
25
|
+
* already been paused once for ISR-write overage.
|
|
26
|
+
*
|
|
27
|
+
* ## π It does NOT replace the seeded band β it OUTRANKS it
|
|
28
|
+
*
|
|
29
|
+
* `CategoryPriceGuidance.market` stays exactly as seeded. A computed band wins
|
|
30
|
+
* where one exists, and where one does not the seeded generation band still
|
|
31
|
+
* shows, clearly labelled as the generation's. Overwriting the seed would mean
|
|
32
|
+
* a single bad import could erase the only evidence ~60 leaves have, with no
|
|
33
|
+
* way back β the same trap as `priceIndexRollup` being forbidden from touching
|
|
34
|
+
* `market`.
|
|
35
|
+
*
|
|
36
|
+
* @tag domain:price-index
|
|
37
|
+
* @tag layer:repository
|
|
38
|
+
* @tag pattern:repository
|
|
39
|
+
* @tag access:server-only
|
|
40
|
+
* @tag consumers:marketPriceRollup,price-guidance-resolve,price-index/market
|
|
41
|
+
* @tag sideEffects:writes marketPriceIndex
|
|
42
|
+
*/
|
|
43
|
+
import { BaseRepository } from "../../../providers/db-firebase/index.js";
|
|
44
|
+
export const MARKET_PRICE_INDEX_COLLECTION = "marketPriceIndex";
|
|
45
|
+
export class MarketPriceIndexRepository extends BaseRepository {
|
|
46
|
+
constructor() {
|
|
47
|
+
super(MARKET_PRICE_INDEX_COLLECTION);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Bands for several leaves in ONE round trip.
|
|
51
|
+
*
|
|
52
|
+
* π `db.getAll`, not N `findById` calls β the same decision, for the same
|
|
53
|
+
* reason, as `PriceIndexRepository.getManyForLeaves`: the tool resolves up to
|
|
54
|
+
* `PRICE_INDEX_SEARCH_LIMIT` leaves per lookup and N sequential reads is N
|
|
55
|
+
* round trips against Rule #6's ~3-per-request budget.
|
|
56
|
+
*
|
|
57
|
+
* A missing document is OMITTED, never returned as an empty band β "no
|
|
58
|
+
* computed band here" and "this leaf does not exist" are different answers,
|
|
59
|
+
* and a zeroed band renders as a confident βΉ0.
|
|
60
|
+
*/
|
|
61
|
+
async getManyForLeaves(leafIds) {
|
|
62
|
+
const ids = [...new Set(leafIds.filter(Boolean))];
|
|
63
|
+
const out = new Map();
|
|
64
|
+
if (ids.length === 0)
|
|
65
|
+
return out;
|
|
66
|
+
const refs = ids.map((id) => this.db.collection(MARKET_PRICE_INDEX_COLLECTION).doc(id));
|
|
67
|
+
const snaps = await this.db.getAll(...refs);
|
|
68
|
+
for (const snap of snaps) {
|
|
69
|
+
if (!snap.exists)
|
|
70
|
+
continue;
|
|
71
|
+
out.set(snap.id, { ...snap.data(), id: snap.id });
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Write one leaf's bands.
|
|
77
|
+
*
|
|
78
|
+
* `byKind` is replaced WHOLESALE rather than merged: `update` merges, so a
|
|
79
|
+
* kind whose observations were all retracted would keep its stale band
|
|
80
|
+
* forever β the same reason `PriceIndexRepository.setForLeaf` passes the full
|
|
81
|
+
* map rather than a patch.
|
|
82
|
+
*/
|
|
83
|
+
async setForLeaf(leafId, byKind) {
|
|
84
|
+
const payload = { currency: "INR", byKind, computedAt: new Date() };
|
|
85
|
+
const existing = await this.findById(leafId);
|
|
86
|
+
if (existing)
|
|
87
|
+
await this.update(leafId, payload);
|
|
88
|
+
else
|
|
89
|
+
await this.createWithId(leafId, payload);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Drop a leaf's bands entirely.
|
|
93
|
+
*
|
|
94
|
+
* Reachable state, not defensive coding: a leaf whose observations are all
|
|
95
|
+
* retracted, or all fall below `MIN_BAND_OBSERVATIONS` after a retraction,
|
|
96
|
+
* must stop publishing a band. Leaving the last computed one in place would
|
|
97
|
+
* publish a figure derived from evidence that no longer exists.
|
|
98
|
+
*/
|
|
99
|
+
async clearForLeaf(leafId) {
|
|
100
|
+
const existing = await this.findById(leafId);
|
|
101
|
+
if (existing)
|
|
102
|
+
await this.delete(leafId);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export const marketPriceIndexRepository = new MarketPriceIndexRepository();
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Price Observations β one DATED sighting of one price for one leaf.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this collection has to exist at all
|
|
5
|
+
*
|
|
6
|
+
* The competitor crawl of 2026-10 survives only as a summarised markdown table:
|
|
7
|
+
* p25/median/p75 per GENERATION, and a single `asOf` for the whole thing. That
|
|
8
|
+
* shape makes three questions permanently unanswerable, and all three were
|
|
9
|
+
* asked:
|
|
10
|
+
*
|
|
11
|
+
* "most common price" a mode needs the observations, not a summary of them
|
|
12
|
+
* "last six months" one crawl date cannot yield a window
|
|
13
|
+
* "what does THIS bey go a generation band cannot distinguish Dragoon MS
|
|
14
|
+
* for" from Strata Dragoon MS β different beys, one number
|
|
15
|
+
*
|
|
16
|
+
* None of those is a rendering problem, which is why no amount of relabelling
|
|
17
|
+
* fixed them. They are the shape of the stored data, and this is that shape
|
|
18
|
+
* changed: the row is the unit, the summary is derived, and the derivation can
|
|
19
|
+
* be re-run with a different window whenever the question changes.
|
|
20
|
+
*
|
|
21
|
+
* ## π A sibling collection, never a field on `categories`
|
|
22
|
+
*
|
|
23
|
+
* Identical reasoning to `price-index.repository.ts`, and it is not theoretical.
|
|
24
|
+
* `onCategoryWrite` is a `documentWritten` trigger on `categories`, so a job
|
|
25
|
+
* writing a band there re-triggers it per document β Root Cause #92's shape,
|
|
26
|
+
* which produced **1,017,548 invocations in 24 hours** against a 2M/month
|
|
27
|
+
* allowance and blocked `firebase deploy` with a billing denial. Worse here:
|
|
28
|
+
* `notifyRevalidate` fires on every category write, so each rolled-up band
|
|
29
|
+
* would drop that category page's ISR entry, on a project already paused once
|
|
30
|
+
* for exceeding ISR writes by 15.5Γ.
|
|
31
|
+
*
|
|
32
|
+
* ## π `sourceKey` is OPAQUE, by instruction
|
|
33
|
+
*
|
|
34
|
+
* The owner's requirement is that competitors are not named on the surface. The
|
|
35
|
+
* honest way to meet that is to not carry the name into the record a renderer
|
|
36
|
+
* can reach β not to carry it and remember never to print it, which is one
|
|
37
|
+
* careless `{...row}` away from publishing (Root Cause #70: a public payload
|
|
38
|
+
* built by spreading a document publishes every field nobody thought to delete).
|
|
39
|
+
*
|
|
40
|
+
* So the stored value is a stable opaque key (`src-a`, `src-b`, β¦). It still
|
|
41
|
+
* does the two jobs provenance needs β telling two sources apart, and letting
|
|
42
|
+
* one be re-crawled or retracted wholesale β without the name being present to
|
|
43
|
+
* leak. The keyβname mapping is operator knowledge, kept out of the database.
|
|
44
|
+
*
|
|
45
|
+
* ## Shape
|
|
46
|
+
*
|
|
47
|
+
* priceObservations/{observationId}
|
|
48
|
+
*
|
|
49
|
+
* One flat collection, no subcollections. Rows are queried by `categoryId` and
|
|
50
|
+
* `seenAt` and never listed wholesale, so the composite index
|
|
51
|
+
* `(categoryId, seenAt desc)` is the only one it needs.
|
|
52
|
+
*
|
|
53
|
+
* @tag domain:price-index
|
|
54
|
+
* @tag layer:repository
|
|
55
|
+
* @tag pattern:repository
|
|
56
|
+
* @tag access:server-only
|
|
57
|
+
* @tag consumers:marketPriceRollup,price-observations CLI
|
|
58
|
+
* @tag sideEffects:writes priceObservations
|
|
59
|
+
*/
|
|
60
|
+
import { BaseRepository } from "../../../providers/db-firebase";
|
|
61
|
+
/**
|
|
62
|
+
* What kind of event the sighting was.
|
|
63
|
+
*
|
|
64
|
+
* π The distinction `price-bands.md` is built around: *"a sold-out row proves
|
|
65
|
+
* the item is GONE, not that it sold at that price"*, with 76β93% of crawled
|
|
66
|
+
* listings sold out. An ask and a sale are different events and a band that
|
|
67
|
+
* blends them describes neither β so the kind is recorded per row and the
|
|
68
|
+
* rollup groups on it rather than averaging across it.
|
|
69
|
+
*/
|
|
70
|
+
export type PriceObservationKind = "ask" | "sale";
|
|
71
|
+
export interface PriceObservationDocument {
|
|
72
|
+
id: string;
|
|
73
|
+
/**
|
|
74
|
+
* The leaf this prices β a MODEL leaf wherever the sighting names a model.
|
|
75
|
+
*
|
|
76
|
+
* π This is the field that makes the whole collection worth building. The
|
|
77
|
+
* seeded bands hang on ~60 generation leaves, so every one of the 1,904 model
|
|
78
|
+
* leaves inherits a number measured for its entire era. Recording the model
|
|
79
|
+
* id is what lets Dragoon MS and Strata Dragoon MS hold different prices,
|
|
80
|
+
* which they do in the real market.
|
|
81
|
+
*/
|
|
82
|
+
categoryId: string;
|
|
83
|
+
/** Decimal rupees, tax-inclusive β `PRICE_GUIDANCE_TAX_BASIS`. */
|
|
84
|
+
amount: number;
|
|
85
|
+
currency: "INR";
|
|
86
|
+
kind: PriceObservationKind;
|
|
87
|
+
/**
|
|
88
|
+
* When the price was SEEN, not when the row was written.
|
|
89
|
+
*
|
|
90
|
+
* π These come apart and the gap is the entire point. Back-filling a
|
|
91
|
+
* two-year-old crawl writes rows today that describe 2024 β using
|
|
92
|
+
* `createdAt` as the date would file every one of them inside the six-month
|
|
93
|
+
* window and report stale prices as current, which is the failure this
|
|
94
|
+
* collection exists to make impossible.
|
|
95
|
+
*/
|
|
96
|
+
seenAt: Date;
|
|
97
|
+
/**
|
|
98
|
+
* `ProductDocument.condition` where the sighting stated one.
|
|
99
|
+
*
|
|
100
|
+
* Absent means NOT STATED β never "new". Condition is the dominant price
|
|
101
|
+
* variable in collectibles (hobson's "Without Box" group runs βΉ599 against
|
|
102
|
+
* βΉ4,499 for the same figures boxed), so guessing it would be guessing the
|
|
103
|
+
* price. Captured now even though the v1 rollup does not slice on it, because
|
|
104
|
+
* a column nobody recorded cannot be back-filled later.
|
|
105
|
+
*/
|
|
106
|
+
condition?: string;
|
|
107
|
+
/** True when the listing was already sold out when seen. */
|
|
108
|
+
soldOut?: boolean;
|
|
109
|
+
/** Opaque, stable, never a competitor name. See the header. */
|
|
110
|
+
sourceKey: string;
|
|
111
|
+
/** Free-text operator note. Never rendered publicly. */
|
|
112
|
+
note?: string;
|
|
113
|
+
createdAt: Date;
|
|
114
|
+
}
|
|
115
|
+
export declare const PRICE_OBSERVATION_COLLECTION = "priceObservations";
|
|
116
|
+
/**
|
|
117
|
+
* Hard ceiling on one leaf's observation read.
|
|
118
|
+
*
|
|
119
|
+
* A leaf with more sightings than this has plenty for a stable band, so the cap
|
|
120
|
+
* costs no accuracy β it exists so a mis-tagged import cannot turn one rollup
|
|
121
|
+
* iteration into an unbounded read (Rule #6).
|
|
122
|
+
*/
|
|
123
|
+
export declare const PRICE_OBSERVATION_SCAN_LIMIT = 500;
|
|
124
|
+
export declare class PriceObservationRepository extends BaseRepository<PriceObservationDocument> {
|
|
125
|
+
constructor();
|
|
126
|
+
/**
|
|
127
|
+
* Every sighting for one leaf, newest first, bounded.
|
|
128
|
+
*
|
|
129
|
+
* π Ordered by `seenAt` and NOT filtered by it. The rollup needs both the
|
|
130
|
+
* recent window and the full history β it decides between them itself
|
|
131
|
+
* (`computeMarketStats`), because that decision has to be made once, in one
|
|
132
|
+
* place, or the job and the renderer will disagree about which window a
|
|
133
|
+
* figure came from. Pushing a date filter down here would make the historic
|
|
134
|
+
* fallback impossible without a second query.
|
|
135
|
+
*
|
|
136
|
+
* π `orderBy("seenAt")` excludes any document LACKING the field β the
|
|
137
|
+
* optional-field trap of Root Cause #100, where a store's reviews read as
|
|
138
|
+
* zero because `itemsSold` had never been written. `seenAt` is required by
|
|
139
|
+
* the write path below precisely so that exclusion can never bite.
|
|
140
|
+
*/
|
|
141
|
+
findForLeaf(categoryId: string, limit?: number): Promise<PriceObservationDocument[]>;
|
|
142
|
+
/**
|
|
143
|
+
* Every leaf that has at least one observation.
|
|
144
|
+
*
|
|
145
|
+
* π Enumerates the ENTITIES, not the events β Root Cause #102's lesson, from
|
|
146
|
+
* a reconciler that built its tally from products that exist rather than
|
|
147
|
+
* categories that exist and so could only ever add. The rollup iterates this
|
|
148
|
+
* list, so a leaf whose last observation was retracted is still visited and
|
|
149
|
+
* its stale band cleared.
|
|
150
|
+
*
|
|
151
|
+
* `.select()` fetches no field data; only the ids are needed to group.
|
|
152
|
+
*/
|
|
153
|
+
listLeafIdsWithObservations(scanLimit?: number): Promise<string[]>;
|
|
154
|
+
/**
|
|
155
|
+
* Record a sighting.
|
|
156
|
+
*
|
|
157
|
+
* π `seenAt` is required from the caller and is NEVER defaulted to the write
|
|
158
|
+
* time. `BaseRepository.create` stamps `createdAt` itself β which is why the
|
|
159
|
+
* two fields exist separately and why this signature cannot accept one in
|
|
160
|
+
* place of the other. Back-filling an old crawl writes rows today that
|
|
161
|
+
* describe last year; conflating the two dates would file every one of them
|
|
162
|
+
* inside the six-month window.
|
|
163
|
+
*/
|
|
164
|
+
record(input: Omit<PriceObservationDocument, "id" | "createdAt" | "updatedAt" | "currency">): Promise<PriceObservationDocument>;
|
|
165
|
+
}
|
|
166
|
+
export declare const priceObservationRepository: PriceObservationRepository;
|