@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
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `protectionClaims` — a buyer's claim against protection cover they paid for.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this collection is the point of Phase 6
|
|
5
|
+
*
|
|
6
|
+
* We charge **2% (min ₹30)** shipment protection and **3% (min ₹49)** sale
|
|
7
|
+
* protection. Both platform flags are seeded `true` and sellers can self-enable
|
|
8
|
+
* in payout settings, so the fee is collectable today. And every piece needed to
|
|
9
|
+
* honour it existed with **zero callers**: `protectionAvailability()`,
|
|
10
|
+
* `adjudicateClaim()`, `returnDestination()`, `RETURN_CUSTODY_STATES`,
|
|
11
|
+
* `SALE_PROTECTION_MAX_VALUE`, `PROTECTION_CLAIM_WINDOW_HOURS`, and the
|
|
12
|
+
* `returnCustody` / `returnPostedAt` / `returnReceivedAt` / `returnInspection`
|
|
13
|
+
* fields on the order (D11).
|
|
14
|
+
*
|
|
15
|
+
* So the money moved and the promise did not. A FAQ said *"we pursue the
|
|
16
|
+
* courier and refund you"* and *"refunded no questions asked"* with nothing
|
|
17
|
+
* behind either sentence. This collection is what gives those pieces their
|
|
18
|
+
* first writer.
|
|
19
|
+
*
|
|
20
|
+
* ## 🛑 The claim WINDOW is not stored here
|
|
21
|
+
*
|
|
22
|
+
* It is the `claim-shipping` / `claim-sale` obligations (Phase 5b), derived from
|
|
23
|
+
* `deliveryDate` through `PROTECTION_CLAIM_WINDOW_HOURS` — 48h for shipping,
|
|
24
|
+
* 168h for sale. Storing a deadline on the claim would be a second copy that
|
|
25
|
+
* drifts from the policy (Root Cause #42), and the obligation already closes
|
|
26
|
+
* the window and earns the premium.
|
|
27
|
+
*/
|
|
28
|
+
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
29
|
+
import type { StatusChangeEntry } from "../../../_internal/shared/history/types";
|
|
30
|
+
import type { ClaimFinding, ProtectionKind } from "../../../_internal/shared/features/shipping/protections";
|
|
31
|
+
export declare const PROTECTION_CLAIMS_COLLECTION = "protectionClaims";
|
|
32
|
+
/**
|
|
33
|
+
* Lifecycle.
|
|
34
|
+
*
|
|
35
|
+
* | state | meaning |
|
|
36
|
+
* |---|---|
|
|
37
|
+
* | `open` | the buyer filed it; nobody has looked |
|
|
38
|
+
* | `awaiting-return` | the buyer must post the item back (sale cover only) |
|
|
39
|
+
* | `inspecting` | it arrived and is being opened |
|
|
40
|
+
* | `upheld` | `adjudicateClaim` said refund the buyer |
|
|
41
|
+
* | `rejected` | the buyer is not refunded — only reachable on UNPROTECTED cover |
|
|
42
|
+
* | `withdrawn` | the buyer withdrew it |
|
|
43
|
+
* | `expired` | the claim window closed with no action |
|
|
44
|
+
*/
|
|
45
|
+
export type ProtectionClaimStatus = "open" | "awaiting-return" | "inspecting" | "upheld" | "rejected" | "withdrawn" | "expired";
|
|
46
|
+
/**
|
|
47
|
+
* Why the buyer says the cover applies.
|
|
48
|
+
*
|
|
49
|
+
* 🛑 Deliberately NOT the same vocabulary as `ClaimFinding`. The buyer's
|
|
50
|
+
* REASON is what they assert; the `finding` is what a human concluded on
|
|
51
|
+
* opening the parcel, and conflating them would let a buyer's own words decide
|
|
52
|
+
* who pays. `adjudicateClaim(protectedSale, finding)` takes the finding.
|
|
53
|
+
*/
|
|
54
|
+
export type ProtectionClaimReason = "not-delivered" | "damaged-in-transit" | "not-as-described" | "counterfeit" | "missing-parts" | "other";
|
|
55
|
+
export interface ProtectionClaimDocument extends BaseDocument {
|
|
56
|
+
/** Which cover is being claimed against. */
|
|
57
|
+
kind: ProtectionKind;
|
|
58
|
+
orderId: string;
|
|
59
|
+
/** Denormalised so the admin queue needs no join to show who and where. */
|
|
60
|
+
storeId: string;
|
|
61
|
+
buyerId: string;
|
|
62
|
+
/** Absent means the whole order — the same convention refunds use. */
|
|
63
|
+
itemIds?: string[];
|
|
64
|
+
status: ProtectionClaimStatus;
|
|
65
|
+
reason: ProtectionClaimReason;
|
|
66
|
+
/** The buyer's own account, in their words. */
|
|
67
|
+
detail: string;
|
|
68
|
+
/** Photos. Media slugs, never raw Storage URLs. */
|
|
69
|
+
evidenceUrls?: string[];
|
|
70
|
+
/**
|
|
71
|
+
* Decimal rupees. What the buyer paid for the claimed items, which is the
|
|
72
|
+
* ceiling on the refund.
|
|
73
|
+
*
|
|
74
|
+
* 🛑 Snapshotted at filing, not re-derived at adjudication. A seller cannot
|
|
75
|
+
* reprice an item after a claim is filed, and `SALE_PROTECTION_MAX_VALUE`
|
|
76
|
+
* (₹25,000) is tested against the amount actually paid.
|
|
77
|
+
*/
|
|
78
|
+
claimedAmount: number;
|
|
79
|
+
/**
|
|
80
|
+
* The human finding, which is what decides WHO PAYS — never whether a
|
|
81
|
+
* protected buyer is refunded.
|
|
82
|
+
*
|
|
83
|
+
* 🛑 `returnInspection.finding` on the order says the same thing, and that is
|
|
84
|
+
* intentional rather than duplication: the order's copy is the custody
|
|
85
|
+
* record for the parcel, this one is the claim's own audit trail, and a claim
|
|
86
|
+
* can be adjudicated without a return at all (a never-delivered parcel has
|
|
87
|
+
* nothing to inspect).
|
|
88
|
+
*/
|
|
89
|
+
finding?: ClaimFinding;
|
|
90
|
+
/** The outcome `adjudicateClaim` produced, recorded verbatim. */
|
|
91
|
+
outcome?: {
|
|
92
|
+
refundBuyer: boolean;
|
|
93
|
+
costBorneBy: "seller" | "platform" | "buyer";
|
|
94
|
+
raisesReport: boolean;
|
|
95
|
+
};
|
|
96
|
+
adjudicatedBy?: string;
|
|
97
|
+
adjudicatedAt?: Date;
|
|
98
|
+
/** The refund this claim produced, so the two are joinable. */
|
|
99
|
+
refundId?: string;
|
|
100
|
+
statusHistory?: StatusChangeEntry[];
|
|
101
|
+
statusHistoryTruncated?: number;
|
|
102
|
+
isTestData?: boolean;
|
|
103
|
+
}
|
|
104
|
+
export declare const PROTECTION_CLAIM_FIELDS: {
|
|
105
|
+
readonly ID: "id";
|
|
106
|
+
readonly KIND: "kind";
|
|
107
|
+
readonly ORDER_ID: "orderId";
|
|
108
|
+
readonly STORE_ID: "storeId";
|
|
109
|
+
readonly BUYER_ID: "buyerId";
|
|
110
|
+
readonly STATUS: "status";
|
|
111
|
+
readonly REASON: "reason";
|
|
112
|
+
readonly CLAIMED_AMOUNT: "claimedAmount";
|
|
113
|
+
readonly FINDING: "finding";
|
|
114
|
+
readonly ADJUDICATED_AT: "adjudicatedAt";
|
|
115
|
+
readonly REFUND_ID: "refundId";
|
|
116
|
+
readonly CREATED_AT: "createdAt";
|
|
117
|
+
};
|
|
118
|
+
/** Statuses where the claim is still somebody's work. */
|
|
119
|
+
export declare const PROTECTION_CLAIM_ACTIVE_STATUSES: readonly ProtectionClaimStatus[];
|
|
120
|
+
export declare const PROTECTION_CLAIM_DETAIL_MIN = 20;
|
|
121
|
+
export declare const PROTECTION_CLAIM_DETAIL_MAX = 2000;
|
|
122
|
+
/**
|
|
123
|
+
* 🛑 ONE OPEN CLAIM PER (ORDER, KIND), and the id enforces it.
|
|
124
|
+
*
|
|
125
|
+
* Deterministic, so `.create()` refuses a second claim on the same cover for
|
|
126
|
+
* the same order. Without that a buyer could file five sale-protection claims
|
|
127
|
+
* on one order and — because a protected claim is refunded unconditionally —
|
|
128
|
+
* each one would be a separate refund against the same goods.
|
|
129
|
+
*
|
|
130
|
+
* Keyed on (order, kind) rather than on (order, kind, item) deliberately: a
|
|
131
|
+
* claim names its `itemIds`, so a buyer with a genuine second problem amends
|
|
132
|
+
* the open claim rather than opening a parallel one. Two concurrent claims on
|
|
133
|
+
* one order is an operational mess with no upside.
|
|
134
|
+
*/
|
|
135
|
+
export declare function createProtectionClaimId(orderId: string, kind: ProtectionKind): string;
|
|
136
|
+
export declare const ALL_PROTECTION_CLAIM_STATUSES: ProtectionClaimStatus[];
|
|
137
|
+
export declare const ALL_PROTECTION_CLAIM_REASONS: ProtectionClaimReason[];
|
|
138
|
+
/**
|
|
139
|
+
* Which cover a reason can be claimed under.
|
|
140
|
+
*
|
|
141
|
+
* 🛑 `not-delivered` and `damaged-in-transit` are TRANSIT failures — shipment
|
|
142
|
+
* cover. Everything else is about the item itself — sale cover. Letting a buyer
|
|
143
|
+
* pick the cover themselves would let them claim a non-delivery against the
|
|
144
|
+
* 7-day sale window after the 48-hour transit window had closed, which is the
|
|
145
|
+
* window rule defeated by a dropdown.
|
|
146
|
+
*/
|
|
147
|
+
/**
|
|
148
|
+
* Buyer-facing labels for the six claim reasons.
|
|
149
|
+
*
|
|
150
|
+
* Lives beside the enum rather than in the claim form, matching
|
|
151
|
+
* `RETURN_REASON_LABEL` and for the same stated reason: these strings appear in
|
|
152
|
+
* the buyer's form, the admin queue and the notification body, and three copies
|
|
153
|
+
* of them would drift. It is also the half of `PROTECTION_COPY` the plan
|
|
154
|
+
* records as missing — `protections.ts` owns the RULES and says its own
|
|
155
|
+
* `ProtectionUnavailableReason` "is rendered to the buyer, so it is copy",
|
|
156
|
+
* while every buyer string sat inline in `StoreAddonsPicker`.
|
|
157
|
+
*
|
|
158
|
+
* `Record<ProtectionClaimReason, string>`, so a seventh reason is a COMPILE
|
|
159
|
+
* error here rather than a radio option rendering `undefined`.
|
|
160
|
+
*
|
|
161
|
+
* 🛑 Phrased as what HAPPENED, never as what the buyer wants. The reason
|
|
162
|
+
* decides the cover kind through `REASON_TO_KIND`, so it is a statement of
|
|
163
|
+
* fact that a window is measured against — not a request. "It never arrived"
|
|
164
|
+
* is a transit failure; "I changed my mind" is not a claim at all and is
|
|
165
|
+
* deliberately absent from the union.
|
|
166
|
+
*/
|
|
167
|
+
export declare const PROTECTION_CLAIM_REASON_LABEL: Record<ProtectionClaimReason, string>;
|
|
168
|
+
export declare const REASON_TO_KIND: Record<ProtectionClaimReason, ProtectionKind>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `protectionClaims` — a buyer's claim against protection cover they paid for.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this collection is the point of Phase 6
|
|
5
|
+
*
|
|
6
|
+
* We charge **2% (min ₹30)** shipment protection and **3% (min ₹49)** sale
|
|
7
|
+
* protection. Both platform flags are seeded `true` and sellers can self-enable
|
|
8
|
+
* in payout settings, so the fee is collectable today. And every piece needed to
|
|
9
|
+
* honour it existed with **zero callers**: `protectionAvailability()`,
|
|
10
|
+
* `adjudicateClaim()`, `returnDestination()`, `RETURN_CUSTODY_STATES`,
|
|
11
|
+
* `SALE_PROTECTION_MAX_VALUE`, `PROTECTION_CLAIM_WINDOW_HOURS`, and the
|
|
12
|
+
* `returnCustody` / `returnPostedAt` / `returnReceivedAt` / `returnInspection`
|
|
13
|
+
* fields on the order (D11).
|
|
14
|
+
*
|
|
15
|
+
* So the money moved and the promise did not. A FAQ said *"we pursue the
|
|
16
|
+
* courier and refund you"* and *"refunded no questions asked"* with nothing
|
|
17
|
+
* behind either sentence. This collection is what gives those pieces their
|
|
18
|
+
* first writer.
|
|
19
|
+
*
|
|
20
|
+
* ## 🛑 The claim WINDOW is not stored here
|
|
21
|
+
*
|
|
22
|
+
* It is the `claim-shipping` / `claim-sale` obligations (Phase 5b), derived from
|
|
23
|
+
* `deliveryDate` through `PROTECTION_CLAIM_WINDOW_HOURS` — 48h for shipping,
|
|
24
|
+
* 168h for sale. Storing a deadline on the claim would be a second copy that
|
|
25
|
+
* drifts from the policy (Root Cause #42), and the obligation already closes
|
|
26
|
+
* the window and earns the premium.
|
|
27
|
+
*/
|
|
28
|
+
export const PROTECTION_CLAIMS_COLLECTION = "protectionClaims";
|
|
29
|
+
export const PROTECTION_CLAIM_FIELDS = {
|
|
30
|
+
ID: "id",
|
|
31
|
+
KIND: "kind",
|
|
32
|
+
ORDER_ID: "orderId",
|
|
33
|
+
STORE_ID: "storeId",
|
|
34
|
+
BUYER_ID: "buyerId",
|
|
35
|
+
STATUS: "status",
|
|
36
|
+
REASON: "reason",
|
|
37
|
+
CLAIMED_AMOUNT: "claimedAmount",
|
|
38
|
+
FINDING: "finding",
|
|
39
|
+
ADJUDICATED_AT: "adjudicatedAt",
|
|
40
|
+
REFUND_ID: "refundId",
|
|
41
|
+
CREATED_AT: "createdAt",
|
|
42
|
+
};
|
|
43
|
+
/** Statuses where the claim is still somebody's work. */
|
|
44
|
+
export const PROTECTION_CLAIM_ACTIVE_STATUSES = [
|
|
45
|
+
"open",
|
|
46
|
+
"awaiting-return",
|
|
47
|
+
"inspecting",
|
|
48
|
+
];
|
|
49
|
+
export const PROTECTION_CLAIM_DETAIL_MIN = 20;
|
|
50
|
+
export const PROTECTION_CLAIM_DETAIL_MAX = 2000;
|
|
51
|
+
/**
|
|
52
|
+
* 🛑 ONE OPEN CLAIM PER (ORDER, KIND), and the id enforces it.
|
|
53
|
+
*
|
|
54
|
+
* Deterministic, so `.create()` refuses a second claim on the same cover for
|
|
55
|
+
* the same order. Without that a buyer could file five sale-protection claims
|
|
56
|
+
* on one order and — because a protected claim is refunded unconditionally —
|
|
57
|
+
* each one would be a separate refund against the same goods.
|
|
58
|
+
*
|
|
59
|
+
* Keyed on (order, kind) rather than on (order, kind, item) deliberately: a
|
|
60
|
+
* claim names its `itemIds`, so a buyer with a genuine second problem amends
|
|
61
|
+
* the open claim rather than opening a parallel one. Two concurrent claims on
|
|
62
|
+
* one order is an operational mess with no upside.
|
|
63
|
+
*/
|
|
64
|
+
export function createProtectionClaimId(orderId, kind) {
|
|
65
|
+
return `${orderId}__${kind}`.replace(/[^A-Za-z0-9_.:-]/g, "_").slice(0, 1500);
|
|
66
|
+
}
|
|
67
|
+
const PROTECTION_CLAIM_STATUS_MAP = {
|
|
68
|
+
open: true,
|
|
69
|
+
"awaiting-return": true,
|
|
70
|
+
inspecting: true,
|
|
71
|
+
upheld: true,
|
|
72
|
+
rejected: true,
|
|
73
|
+
withdrawn: true,
|
|
74
|
+
expired: true,
|
|
75
|
+
};
|
|
76
|
+
export const ALL_PROTECTION_CLAIM_STATUSES = Object.keys(PROTECTION_CLAIM_STATUS_MAP);
|
|
77
|
+
const PROTECTION_CLAIM_REASON_MAP = {
|
|
78
|
+
"not-delivered": true,
|
|
79
|
+
"damaged-in-transit": true,
|
|
80
|
+
"not-as-described": true,
|
|
81
|
+
counterfeit: true,
|
|
82
|
+
"missing-parts": true,
|
|
83
|
+
other: true,
|
|
84
|
+
};
|
|
85
|
+
export const ALL_PROTECTION_CLAIM_REASONS = Object.keys(PROTECTION_CLAIM_REASON_MAP);
|
|
86
|
+
/**
|
|
87
|
+
* Which cover a reason can be claimed under.
|
|
88
|
+
*
|
|
89
|
+
* 🛑 `not-delivered` and `damaged-in-transit` are TRANSIT failures — shipment
|
|
90
|
+
* cover. Everything else is about the item itself — sale cover. Letting a buyer
|
|
91
|
+
* pick the cover themselves would let them claim a non-delivery against the
|
|
92
|
+
* 7-day sale window after the 48-hour transit window had closed, which is the
|
|
93
|
+
* window rule defeated by a dropdown.
|
|
94
|
+
*/
|
|
95
|
+
/**
|
|
96
|
+
* Buyer-facing labels for the six claim reasons.
|
|
97
|
+
*
|
|
98
|
+
* Lives beside the enum rather than in the claim form, matching
|
|
99
|
+
* `RETURN_REASON_LABEL` and for the same stated reason: these strings appear in
|
|
100
|
+
* the buyer's form, the admin queue and the notification body, and three copies
|
|
101
|
+
* of them would drift. It is also the half of `PROTECTION_COPY` the plan
|
|
102
|
+
* records as missing — `protections.ts` owns the RULES and says its own
|
|
103
|
+
* `ProtectionUnavailableReason` "is rendered to the buyer, so it is copy",
|
|
104
|
+
* while every buyer string sat inline in `StoreAddonsPicker`.
|
|
105
|
+
*
|
|
106
|
+
* `Record<ProtectionClaimReason, string>`, so a seventh reason is a COMPILE
|
|
107
|
+
* error here rather than a radio option rendering `undefined`.
|
|
108
|
+
*
|
|
109
|
+
* 🛑 Phrased as what HAPPENED, never as what the buyer wants. The reason
|
|
110
|
+
* decides the cover kind through `REASON_TO_KIND`, so it is a statement of
|
|
111
|
+
* fact that a window is measured against — not a request. "It never arrived"
|
|
112
|
+
* is a transit failure; "I changed my mind" is not a claim at all and is
|
|
113
|
+
* deliberately absent from the union.
|
|
114
|
+
*/
|
|
115
|
+
export const PROTECTION_CLAIM_REASON_LABEL = {
|
|
116
|
+
"not-delivered": "It never arrived",
|
|
117
|
+
"damaged-in-transit": "It arrived damaged",
|
|
118
|
+
"not-as-described": "It is not what the listing described",
|
|
119
|
+
counterfeit: "It is counterfeit or not authentic",
|
|
120
|
+
"missing-parts": "Parts are missing",
|
|
121
|
+
other: "Something else is wrong with the item",
|
|
122
|
+
};
|
|
123
|
+
export const REASON_TO_KIND = {
|
|
124
|
+
"not-delivered": "shipping",
|
|
125
|
+
"damaged-in-transit": "shipping",
|
|
126
|
+
"not-as-described": "sale",
|
|
127
|
+
counterfeit: "sale",
|
|
128
|
+
"missing-parts": "sale",
|
|
129
|
+
other: "sale",
|
|
130
|
+
};
|
|
@@ -50,8 +50,9 @@ export async function createReview(userId, input) {
|
|
|
50
50
|
duration: 0,
|
|
51
51
|
}
|
|
52
52
|
: undefined,
|
|
53
|
-
verified
|
|
54
|
-
|
|
53
|
+
/* `verified` and `status` are set by `createReview` itself and are
|
|
54
|
+
omitted from `ReviewCreateInput` — passing them here duplicated the
|
|
55
|
+
repository default and was the reason the input type demanded them. */
|
|
55
56
|
});
|
|
56
57
|
}
|
|
57
58
|
export async function updateReview(userId, reviewId, input) {
|
|
@@ -24,5 +24,5 @@ export function ViewReviewModal({ review, isOpen, onClose, }) {
|
|
|
24
24
|
day: "numeric",
|
|
25
25
|
})
|
|
26
26
|
: "";
|
|
27
|
-
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Review Details", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { gap: "3", children: [review.userAvatar ? (_jsx(Div, { className: "relative h-10 w-10 flex-shrink-0", rounded: "full", overflow: "hidden", children: _jsx(MediaImage, { src: review.userAvatar, alt: displayName, size: "avatar" }) })) : (_jsx(Row, { textWeight: "medium", textSize: "sm", className: "h-10 w-10 flex-shrink-0 bg-neutral-200 text-[var(--appkit-color-text-muted)]", align: "center", justify: "center", rounded: "full", children: displayName.charAt(0).toUpperCase() })), _jsxs(Div, { children: [_jsxs(Row, { gap: "sm", children: [_jsx(Text, { color: "inverse", className: "text-[var(--appkit-color-text)] dark:", weight: "medium", children: displayName }), review.verified && (_jsx(Text, { className: THEMED_TEXT_SUCCESS, size: "xs", children: "\u2713 Verified purchase" }))] }), _jsxs(Row, { className: "mt-0.5", gap: "sm", children: [_jsx(StarRating, { value: review.rating, size: "sm", readOnly: true }), date && (_jsx(Text, { size: "xs", color: "faint", children: date }))] })] })] }), review.title && (_jsx(Heading, { color: "inverse", level: 4, className: "text-[var(--appkit-color-text)] dark:", weight: "semibold", children: review.title })), review.comment && (_jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)]
|
|
27
|
+
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Review Details", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { gap: "3", children: [review.userAvatar ? (_jsx(Div, { className: "relative h-10 w-10 flex-shrink-0", rounded: "full", overflow: "hidden", children: _jsx(MediaImage, { src: review.userAvatar, alt: displayName, size: "avatar" }) })) : (_jsx(Row, { textWeight: "medium", textSize: "sm", className: "h-10 w-10 flex-shrink-0 bg-neutral-200 text-[var(--appkit-color-text-muted)]", align: "center", justify: "center", rounded: "full", children: displayName.charAt(0).toUpperCase() })), _jsxs(Div, { children: [_jsxs(Row, { gap: "sm", children: [_jsx(Text, { color: "inverse", className: "text-[var(--appkit-color-text)] dark:", weight: "medium", children: displayName }), review.verified && (_jsx(Text, { className: THEMED_TEXT_SUCCESS, size: "xs", children: "\u2713 Verified purchase" }))] }), _jsxs(Row, { className: "mt-0.5", gap: "sm", children: [_jsx(StarRating, { value: review.rating, size: "sm", readOnly: true }), date && (_jsx(Text, { size: "xs", color: "faint", children: date }))] })] })] }), review.title && (_jsx(Heading, { color: "inverse", level: 4, className: "text-[var(--appkit-color-text)] dark:", weight: "semibold", children: review.title })), review.comment && (_jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)]" })), review.images && review.images.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", children: review.images.map((img, i) => (_jsx(Button, { variant: "ghost", type: "button", "aria-label": `View image ${i + 1}`, onClick: () => setLightboxIdx(i), rounded: "lg", paddingX: "none", paddingY: "none", className: `h-20 w-20 overflow-hidden transition hover:opacity-80 flex-shrink-0 ${lightboxIdx === i ? "ring-2 ring-primary-500" : " border-[var(--appkit-color-border)]"}`, children: _jsx(MediaImage, { src: img, alt: `Review thumbnail ${i + 1}`, size: "thumbnail" }) }, i))) })), (review.helpfulCount ?? 0) > 0 && (_jsxs(Text, { size: "xs", color: "faint", children: [review.helpfulCount, " people found this helpful"] })), _jsx(Row, { justify: "end", padding: "t-xs", children: _jsx(Button, { variant: "ghost", onClick: onClose, children: "Close" }) })] }) }));
|
|
28
28
|
}
|
|
@@ -29,7 +29,7 @@ export function ReviewCard({ review, context = "general", className = "" }) {
|
|
|
29
29
|
const showProductLink = context !== "listing" && !!productHref;
|
|
30
30
|
const showProfileLink = !!profileHref;
|
|
31
31
|
const hasFooter = showStoreLink || showProductLink || showProfileLink;
|
|
32
|
-
return (_jsxs(Stack, { padding: "5", shadow: "hover-md", className: `group h-full border border-neutral-200 transition ${className}`, rounded: "xl", surface: "default", children: [_jsxs(Link, { href: reviewHref, className: "flex flex-col flex-1 min-h-0", children: [_jsxs(Row, { align: "start", gap: "3", children: [review.userAvatar ? (_jsx(Div, { className: "relative h-9 w-9 flex-shrink-0", rounded: "full", overflow: "hidden", children: _jsx(MediaImage, { src: review.userAvatar, alt: displayName, size: "avatar" }) })) : (_jsx(Row, { textWeight: "medium", textSize: "sm", className: "h-9 w-9 flex-shrink-0 bg-neutral-200 text-[var(--appkit-color-text-muted)]", align: "center", justify: "center", rounded: "full", children: initials })), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Span, { color: "inverse", weight: "medium", className: "text-[var(--appkit-color-text)] dark:", children: displayName }), review.verified && (_jsx(Span, { color: "success", surface: "success-surface", size: "xs", weight: "medium", rounded: "full", padding: "pill-xs", children: "Verified" })), date && (_jsx(Span, { size: "xs", color: "muted", children: date }))] }), _jsx(Div, { className: "mt-1", children: _jsx(StarRating, { value: review.rating, size: "sm", readOnly: true }) })] })] }), review.title && (_jsx(Heading, { color: "inverse", level: 4, className: "mt-3 text-[var(--appkit-color-text)] dark:", weight: "semibold", children: review.title })), review.comment && (_jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "mt-2 text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)]
|
|
32
|
+
return (_jsxs(Stack, { padding: "5", shadow: "hover-md", className: `group h-full border border-neutral-200 transition ${className}`, rounded: "xl", surface: "default", children: [_jsxs(Link, { href: reviewHref, className: "flex flex-col flex-1 min-h-0", children: [_jsxs(Row, { align: "start", gap: "3", children: [review.userAvatar ? (_jsx(Div, { className: "relative h-9 w-9 flex-shrink-0", rounded: "full", overflow: "hidden", children: _jsx(MediaImage, { src: review.userAvatar, alt: displayName, size: "avatar" }) })) : (_jsx(Row, { textWeight: "medium", textSize: "sm", className: "h-9 w-9 flex-shrink-0 bg-neutral-200 text-[var(--appkit-color-text-muted)]", align: "center", justify: "center", rounded: "full", children: initials })), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Span, { color: "inverse", weight: "medium", className: "text-[var(--appkit-color-text)] dark:", children: displayName }), review.verified && (_jsx(Span, { color: "success", surface: "success-surface", size: "xs", weight: "medium", rounded: "full", padding: "pill-xs", children: "Verified" })), date && (_jsx(Span, { size: "xs", color: "muted", children: date }))] }), _jsx(Div, { className: "mt-1", children: _jsx(StarRating, { value: review.rating, size: "sm", readOnly: true }) })] })] }), review.title && (_jsx(Heading, { color: "inverse", level: 4, className: "mt-3 text-[var(--appkit-color-text)] dark:", weight: "semibold", children: review.title })), review.comment && (_jsx(RichText, { html: normalizeRichTextHtml(review.comment), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "mt-2 text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)]" })), review.images && review.images.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mt-3", children: review.images.map((img, i) => (_jsx(Div, { className: "relative h-16 w-16 flex-shrink-0 border border-neutral-100", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: img, alt: `Review image ${i + 1}`, size: "thumbnail" }) }, i))) })), (review.helpfulCount ?? 0) > 0 && (_jsxs(Text, { className: "mt-3", color: "faint", size: "xs", children: [review.helpfulCount, " found this helpful"] }))] }), hasFooter && (_jsxs(Stack, { gap: "xs", className: "mt-3 border-t border-neutral-100", padding: "t-sm", children: [showStoreLink && (_jsxs(Link, { href: String(ROUTES.PUBLIC.STORE_DETAIL(review.storeSlug)), className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-primary hover:underline", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83C\uDFEA" }), _jsx(Span, { truncate: true, children: review.storeName })] })), showProductLink && (_jsxs(Link, { href: productHref, className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)]", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDCE6" }), _jsx(Span, { truncate: true, children: review.productTitle ?? "View Product" }), _jsx(Span, { "aria-hidden": "true", className: "ml-auto text-primary group-hover:translate-x-0.5 transition-transform", children: "\u2192" })] })), showProfileLink && (_jsxs(Link, { href: profileHref, className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)]", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDC64" }), _jsx(Span, { truncate: true, children: displayName })] }))] }))] }));
|
|
33
33
|
}
|
|
34
34
|
export function ReviewsList({ reviews, context = "general", isLoading, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No reviews yet", }) {
|
|
35
35
|
if (isLoading) {
|
|
@@ -42,6 +42,25 @@ export interface ReviewDocument extends BaseDocument {
|
|
|
42
42
|
userSlug?: string;
|
|
43
43
|
userName: Encrypted<string>;
|
|
44
44
|
userAvatar?: string;
|
|
45
|
+
/**
|
|
46
|
+
* The order whose line this review rates.
|
|
47
|
+
*
|
|
48
|
+
* 🛑 Optional, because every review written before this field existed has
|
|
49
|
+
* none — and any query over it must therefore treat a missing `orderId` as
|
|
50
|
+
* "unknown", never as "not from an order". A default-exclusive filter would
|
|
51
|
+
* hide every pre-existing review and manufacture a backfill nobody budgeted
|
|
52
|
+
* for (the same rule `serverErrors.entityType` carries).
|
|
53
|
+
*/
|
|
54
|
+
orderId?: string;
|
|
55
|
+
/**
|
|
56
|
+
* The specific order LINE, when the order carries `items[]`.
|
|
57
|
+
*
|
|
58
|
+
* Needed because one order can contain the same `productId` twice — a
|
|
59
|
+
* grouped line and a standalone purchase of the same bey — and Zomato-style
|
|
60
|
+
* rating is per LINE, not per product. Without it, rating one of the two
|
|
61
|
+
* lines would mark both as rated.
|
|
62
|
+
*/
|
|
63
|
+
orderItemId?: string;
|
|
45
64
|
rating: number;
|
|
46
65
|
title: string;
|
|
47
66
|
comment: string;
|
|
@@ -78,7 +97,7 @@ export declare const REVIEW_INDEXED_FIELDS: readonly ["searchTxt", "productId",
|
|
|
78
97
|
export declare const DEFAULT_REVIEW_DATA: Partial<ReviewDocument>;
|
|
79
98
|
export declare const REVIEW_PUBLIC_FIELDS: readonly ["id", "productId", "userName", "userAvatar", "rating", "title", "comment", "helpfulCount", "verified", "images", "createdAt"];
|
|
80
99
|
export declare const REVIEW_UPDATABLE_FIELDS: readonly ["rating", "title", "comment", "images"];
|
|
81
|
-
export type ReviewCreateInput = Omit<ReviewPlain, "id" | "createdAt" | "updatedAt" | "helpfulCount" | "reportCount" | "moderatorId" | "moderatorNote" | "approvedAt" | "rejectedAt">;
|
|
100
|
+
export type ReviewCreateInput = Omit<ReviewPlain, "id" | "createdAt" | "updatedAt" | "status" | "verified" | "helpfulCount" | "reportCount" | "moderatorId" | "moderatorNote" | "approvedAt" | "rejectedAt">;
|
|
82
101
|
export type ReviewUpdateInput = Partial<Pick<ReviewDocument, (typeof REVIEW_UPDATABLE_FIELDS)[number]>>;
|
|
83
102
|
export type ReviewModerationInput = {
|
|
84
103
|
status: ReviewStatus;
|
|
@@ -29,6 +29,8 @@ export declare const SCAM_CATEGORIES: ScamCategoryDefinition[];
|
|
|
29
29
|
export declare const SCAM_TYPES: ScamTypeDefinition[];
|
|
30
30
|
/** Get a scam type definition by its ID. */
|
|
31
31
|
export declare function getScamType(id: ScamType): ScamTypeDefinition | undefined;
|
|
32
|
+
/** Is `value` one of the 27 documented scam-type ids? */
|
|
33
|
+
export declare function isScamType(value: string): value is ScamType;
|
|
32
34
|
/** Get all scam types in a given category. */
|
|
33
35
|
export declare function getScamTypesByCategory(category: ScamCategory): ScamTypeDefinition[];
|
|
34
36
|
/** Get the category definition for a given scam type ID. */
|
|
@@ -427,6 +427,24 @@ export const SCAM_TYPES = [
|
|
|
427
427
|
export function getScamType(id) {
|
|
428
428
|
return SCAM_TYPES.find((t) => t.id === id);
|
|
429
429
|
}
|
|
430
|
+
/**
|
|
431
|
+
* The set of real ids, for narrowing an arbitrary string.
|
|
432
|
+
*
|
|
433
|
+
* `getScamType` already takes a `ScamType`, so it cannot be used to VALIDATE
|
|
434
|
+
* one — which is why the scam-report form had nothing to check against and
|
|
435
|
+
* `POST /api/scams/reports` accepted `z.string().min(1)`. A free-text value
|
|
436
|
+
* stores fine (the repository's `create` does no enum check), and then the
|
|
437
|
+
* registry's type chips and `SCAM_TYPE_LABELS` can never match it: the profile
|
|
438
|
+
* renders no type at all and the report is unfilterable.
|
|
439
|
+
*
|
|
440
|
+
* Derived from `SCAM_TYPES` rather than restating the union, so documenting a
|
|
441
|
+
* 28th pattern needs one edit and cannot drift from what the form offers.
|
|
442
|
+
*/
|
|
443
|
+
const SCAM_TYPE_ID_SET = new Set(SCAM_TYPES.map((t) => t.id));
|
|
444
|
+
/** Is `value` one of the 27 documented scam-type ids? */
|
|
445
|
+
export function isScamType(value) {
|
|
446
|
+
return SCAM_TYPE_ID_SET.has(value);
|
|
447
|
+
}
|
|
430
448
|
/** Get all scam types in a given category. */
|
|
431
449
|
export function getScamTypesByCategory(category) {
|
|
432
450
|
return SCAM_TYPES.filter((t) => t.category === category);
|
|
@@ -4,7 +4,7 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
4
4
|
phones: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
5
5
|
upiIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
6
6
|
emails: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
7
|
-
scamType: z.ZodString
|
|
7
|
+
scamType: z.ZodEffects<z.ZodString, import("../constants/scam-types").ScamType, string>;
|
|
8
8
|
scamPlatform: z.ZodString;
|
|
9
9
|
amountLost: z.ZodOptional<z.ZodNumber>;
|
|
10
10
|
itemInvolved: z.ZodOptional<z.ZodString>;
|
|
@@ -14,7 +14,7 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
14
14
|
}, "strip", z.ZodTypeAny, {
|
|
15
15
|
displayName: string;
|
|
16
16
|
description: string;
|
|
17
|
-
scamType:
|
|
17
|
+
scamType: import("../constants/scam-types").ScamType;
|
|
18
18
|
scamPlatform: string;
|
|
19
19
|
agreed: true;
|
|
20
20
|
reportedByAnon?: boolean | undefined;
|
|
@@ -38,7 +38,7 @@ export declare const scamReportFormSchema: z.ZodEffects<z.ZodObject<{
|
|
|
38
38
|
}>, {
|
|
39
39
|
displayName: string;
|
|
40
40
|
description: string;
|
|
41
|
-
scamType:
|
|
41
|
+
scamType: import("../constants/scam-types").ScamType;
|
|
42
42
|
scamPlatform: string;
|
|
43
43
|
agreed: true;
|
|
44
44
|
reportedByAnon?: boolean | undefined;
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
*/
|
|
34
34
|
import { z } from "zod";
|
|
35
35
|
import { annotate } from "../../shell/field-ui-meta.js";
|
|
36
|
+
import { isScamType } from "../constants/scam-types.js";
|
|
36
37
|
const UPI_VPA_RE = /^[\w.\-_]{2,256}@[a-zA-Z]{2,64}$/;
|
|
37
38
|
const PHONE_RE = /^(\+?91[-\s]?)?[6-9]\d{9}$/;
|
|
38
39
|
export const scamReportFormSchema = z
|
|
@@ -42,8 +43,32 @@ export const scamReportFormSchema = z
|
|
|
42
43
|
phones: annotate(z.array(z.string().trim().regex(PHONE_RE, "Enter a 10-digit Indian mobile number.")).max(10).optional(), { section: "who", order: 2, row: "full", kind: "list" }),
|
|
43
44
|
upiIds: annotate(z.array(z.string().trim().regex(UPI_VPA_RE, "Enter a valid UPI ID, e.g. name@bank.")).max(10).optional(), { section: "who", order: 3, row: "full", kind: "list" }),
|
|
44
45
|
emails: annotate(z.array(z.string().trim().toLowerCase().email("Enter a valid email address.")).max(10).optional(), { section: "who", order: 4, row: "full", kind: "list" }),
|
|
45
|
-
|
|
46
|
+
/*
|
|
47
|
+
* 🛑 `kind: "select"` IS LOAD-BEARING, not decoration.
|
|
48
|
+
*
|
|
49
|
+
* `buildSectionsFromSchema` reads `opts.options[name]` only inside its
|
|
50
|
+
* `case "select"` branch. With no `kind` the generator inferred `"text"`
|
|
51
|
+
* from the `ZodString` — so the page's 27-entry `SCAM_TYPE_OPTIONS` list
|
|
52
|
+
* was passed in and ignored, and the field rendered as a FREE-TEXT input.
|
|
53
|
+
* There was no way to choose a scam type at all.
|
|
54
|
+
*
|
|
55
|
+
* Verified by running the shipped resolver against this schema:
|
|
56
|
+
* scamType | kind: text ← before
|
|
57
|
+
* scamPlatform | kind: select ← its sibling, which was annotated
|
|
58
|
+
*
|
|
59
|
+
* The sibling being correct is why this read as a styling quirk rather
|
|
60
|
+
* than a dead control.
|
|
61
|
+
*
|
|
62
|
+
* The refine is the other half. `min(1)` accepted any non-empty string,
|
|
63
|
+
* matching the route, and nothing downstream validates: the repository's
|
|
64
|
+
* `create` has no enum check, so a typed value stored and then matched no
|
|
65
|
+
* chip in the registry and no key in `SCAM_TYPE_LABELS`. A select cannot
|
|
66
|
+
* produce a bad value, but a direct POST can — see the mirror of this on
|
|
67
|
+
* `POST /api/scams/reports`.
|
|
68
|
+
*/
|
|
69
|
+
scamType: annotate(z.string().trim().refine(isScamType, { message: "Choose what kind of scam this was." }), {
|
|
46
70
|
section: "what", sectionLabel: "What happened", sectionRequired: true, quick: true, order: 1, row: "pair",
|
|
71
|
+
kind: "select",
|
|
47
72
|
}),
|
|
48
73
|
/*
|
|
49
74
|
* Required, matching `POST /api/scams/reports` (`z.string().min(1)`).
|
|
@@ -28,7 +28,7 @@ const DEFAULT_TYPE_ICONS = {
|
|
|
28
28
|
product: "📦",
|
|
29
29
|
};
|
|
30
30
|
const DEFAULT_TYPE_BADGES = {
|
|
31
|
-
page: "bg-[var(--appkit-color-surface
|
|
31
|
+
page: "bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text-muted)]",
|
|
32
32
|
category: "bg-info-surface text-info dark:bg-info-surface dark:text-info",
|
|
33
33
|
blog: "bg-fuchsia-100 text-fuchsia-600 dark:bg-fuchsia-900/40 dark:text-fuchsia-300",
|
|
34
34
|
event: "bg-warning-surface text-warning dark:bg-warning-surface dark:text-warning",
|
|
@@ -36,7 +36,7 @@ const DEFAULT_TYPE_BADGES = {
|
|
|
36
36
|
};
|
|
37
37
|
function SuggestionItem({ suggestion, itemIndex, activeIndex, onSelect, onHover, typeIcons, typeBadges, }) {
|
|
38
38
|
const isActive = activeIndex === itemIndex;
|
|
39
|
-
return (_jsx(Li, { children: _jsxs(Button, { type: "button", variant: "ghost", onClick: () => onSelect(suggestion), onMouseEnter: () => onHover(itemIndex), className: `w-full flex items-center gap-[var(--appkit-space-3)] px-[var(--appkit-space-4)] py-[var(--appkit-space-3)] text-left transition-colors border-b border-[var(--appkit-color-border)] last:border-b-0 ${isActive ? "bg-[var(--appkit-color-surface
|
|
39
|
+
return (_jsx(Li, { children: _jsxs(Button, { type: "button", variant: "ghost", onClick: () => onSelect(suggestion), onMouseEnter: () => onHover(itemIndex), className: `w-full flex items-center gap-[var(--appkit-space-3)] px-[var(--appkit-space-4)] py-[var(--appkit-space-3)] text-left transition-colors border-b border-[var(--appkit-color-border)] last:border-b-0 ${isActive ? "bg-[var(--appkit-color-surface-elevated)]" : CLS_HOVER_ROW}`, children: [_jsx(Span, { size: "sm", children: typeIcons[suggestion.type] }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", className: CLS_TRUNCATE, children: suggestion.title }), suggestion.subtitle && (_jsx(Text, { variant: "secondary", size: "xs", className: "truncate", children: suggestion.subtitle }))] }), _jsx(Span, { size: "xs", className: `${typeBadges[suggestion.type]}`, rounded: "full", padding: "pill-xs", children: suggestion.type })] }) }));
|
|
40
40
|
}
|
|
41
41
|
export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, placeholder, deferred = true, debounceMs = 300, onClear, className, router, quickLinks = [], labels, suggestionTypeIcons, suggestionTypeBadges, resourceTypes, defaultResourceType = "products", storageKey = "letitrip_search_type", searchIndexEnabled, }) {
|
|
42
42
|
const isInlineMode = value !== undefined;
|
|
@@ -211,7 +211,7 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
|
|
|
211
211
|
return (_jsx(Li, { children: _jsxs(Button, { type: "button", variant: "ghost", onClick: () => {
|
|
212
212
|
setIsInlineOpen(false);
|
|
213
213
|
router.push(link.href);
|
|
214
|
-
}, onMouseEnter: () => setActiveIndex(index), className: `w-full flex items-center gap-[var(--appkit-space-3)] px-[var(--appkit-space-4)] py-[var(--appkit-space-2-5)] text-left transition-colors border-b border-[var(--appkit-color-border)] ${isActive ? "bg-[var(--appkit-color-surface
|
|
214
|
+
}, onMouseEnter: () => setActiveIndex(index), className: `w-full flex items-center gap-[var(--appkit-space-3)] px-[var(--appkit-space-4)] py-[var(--appkit-space-2-5)] text-left transition-colors border-b border-[var(--appkit-color-border)] ${isActive ? "bg-[var(--appkit-color-surface-elevated)]" : CLS_HOVER_ROW}`, children: [_jsx(Icon, { className: CLS_ICON_ROW }), _jsx(Text, { size: "sm", className: CLS_TRUNCATE, children: link.label })] }) }, `inline-link-${link.href}`));
|
|
215
215
|
};
|
|
216
216
|
const handleInlineKeyDown = (event) => {
|
|
217
217
|
if (event.key === "ArrowDown") {
|
|
@@ -257,15 +257,15 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
|
|
|
257
257
|
if (inlineBlurRef.current)
|
|
258
258
|
clearTimeout(inlineBlurRef.current);
|
|
259
259
|
inlineBlurRef.current = setTimeout(() => setIsInlineOpen(false), 120);
|
|
260
|
-
}, placeholder: placeholder ?? labels.placeholder, className: "w-full rounded-lg border
|
|
260
|
+
}, placeholder: placeholder ?? labels.placeholder, className: "w-full rounded-lg border pl-9 text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] dark:text-white dark:placeholder:text-slate-500" }), query && (_jsx(Button, { rounded: "full", textColor: "faint", type: "button", variant: "ghost", onClick: handleClear, className: "absolute right-3 p-[0.125rem] hover:text-[var(--appkit-color-text-muted)] dark:hover:text-[var(--appkit-color-text)] transition-colors", "aria-label": labels.clearAriaLabel, children: _jsx("svg", { className: "w-4 h-4 shrink-0", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }))] }), resourceTypes && resourceTypes.length > 0 && (_jsx(Select, { options: resourceTypes, value: selectedType, onValueChange: handleTypeChange, "aria-label": labels.resourceTypeLabel ?? "Search in", wrapperClassName: "flex-shrink-0 max-w-[92px] sm:max-w-[150px]" })), deferred && (_jsx(Button, { rounded: "lg", paddingX: "sm", paddingY: "sm", type: "button", variant: "primary", onClick: handleDeferredSubmit, "aria-label": labels.ariaLabel, className: "flex-shrink-0", children: _jsx("svg", { className: "w-4 h-4 shrink-0", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }) })), isInlineOpen && (filteredQuickLinks.length > 0 || query) && (_jsxs(Div, { rounded: "xl", border: "default", shadow: "lg", className: "absolute top-full left-0 right-0 mt-2 overflow-hidden bg-[var(--appkit-color-surface)] [z-index:var(--appkit-z-dropdown)]", onMouseDown: (event) => event.preventDefault(), children: [filteredQuickLinks.length > 0 && (_jsx(Ul, { children: filteredQuickLinks.map((link, index) => renderInlineQuickLink(link, index)) })), query && suggestionsLoading && (_jsx(Div, { padding: "inline", children: _jsx(Text, { variant: "secondary", size: "sm", children: labels.searching }) })), query &&
|
|
261
261
|
suggestions.slice(0, 5).map((suggestion, suggestionIndex) => {
|
|
262
262
|
const itemIndex = inlineQuickLinkItems.length + suggestionIndex;
|
|
263
263
|
const isActive = activeIndex === itemIndex;
|
|
264
|
-
return (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => handleSuggestionClick(suggestion), onMouseEnter: () => setActiveIndex(itemIndex), className: `w-full flex items-center gap-[var(--appkit-space-3)] px-[var(--appkit-space-4)] py-[var(--appkit-space-2-5)] text-left transition-colors border-b border-[var(--appkit-color-border)] ${isActive ? "bg-[var(--appkit-color-surface
|
|
264
|
+
return (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => handleSuggestionClick(suggestion), onMouseEnter: () => setActiveIndex(itemIndex), className: `w-full flex items-center gap-[var(--appkit-space-3)] px-[var(--appkit-space-4)] py-[var(--appkit-space-2-5)] text-left transition-colors border-b border-[var(--appkit-color-border)] ${isActive ? "bg-[var(--appkit-color-surface-elevated)]" : CLS_HOVER_ROW}`, children: [_jsx(Span, { size: "sm", children: typeIcons[suggestion.type] }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", className: CLS_TRUNCATE, children: suggestion.title }), suggestion.subtitle && (_jsx(Text, { variant: "secondary", size: "xs", className: "truncate", children: suggestion.subtitle }))] })] }, `inline-suggestion-${suggestion.objectID}`));
|
|
265
265
|
}), query.trim() && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => {
|
|
266
266
|
handleDeferredSubmit();
|
|
267
267
|
setIsInlineOpen(false);
|
|
268
|
-
}, onMouseEnter: () => setActiveIndex(inlineItems.length - 1), className: `w-full flex items-center gap-[var(--appkit-space-2)] px-[var(--appkit-space-4)] py-[var(--appkit-space-3)] text-left transition-colors ${activeIndex === inlineItems.length - 1 ? "bg-[var(--appkit-color-surface
|
|
268
|
+
}, onMouseEnter: () => setActiveIndex(inlineItems.length - 1), className: `w-full flex items-center gap-[var(--appkit-space-2)] px-[var(--appkit-space-4)] py-[var(--appkit-space-3)] text-left transition-colors ${activeIndex === inlineItems.length - 1 ? "bg-[var(--appkit-color-surface-elevated)]" : CLS_HOVER_ROW}`, children: [_jsx("svg", { className: CLS_ICON_ROW, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }), _jsx(Text, { size: "sm", className: CLS_TRUNCATE, children: labels.browseProducts(query) })] }))] }))] }));
|
|
269
269
|
}
|
|
270
270
|
const overlayQuery = query.toLowerCase();
|
|
271
271
|
const filteredSiteLinks = query
|
|
@@ -336,13 +336,13 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
|
|
|
336
336
|
return (_jsx(Div, { border: "bottom-subtle", className: "animate-[fadeIn_150ms_ease-out]", surface: "default", shadow: "md", children: _jsxs(Div, { className: "container mx-auto md:py-[1rem] relative", padding: "inline", children: [_jsxs(Row, { gap: "sm", className: "md:gap-[0.75rem]", children: [_jsx(Div, { className: "flex-1 relative min-w-0", children: _jsx(Input, { ref: inputRef, bare: true, type: "search", placeholder: placeholder ?? labels.placeholder, value: query, onFocus: () => setSearchIntent(true), onChange: (event) => {
|
|
337
337
|
setSearchIntent(true);
|
|
338
338
|
setQuery(event.target.value);
|
|
339
|
-
}, onKeyDown: handleOverlayKeyDown, className: "w-full rounded-lg border
|
|
339
|
+
}, onKeyDown: handleOverlayKeyDown, className: "w-full rounded-lg border text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] dark:text-white dark:placeholder:text-slate-500" }) }), resourceTypes && resourceTypes.length > 0 && (_jsx(Select, { options: resourceTypes, value: selectedType, onValueChange: handleTypeChange, "aria-label": labels.resourceTypeLabel ?? "Search in", wrapperClassName: "flex-shrink-0 max-w-[92px] sm:max-w-[150px]" })), _jsxs(Button, { gap: "md", onClick: handleOverlaySearch, variant: "primary", size: "md", className: "hidden sm:", children: [_jsx("svg", { className: "w-5 h-5 shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }), labels.title] }), _jsx(Button, { rounded: "xl", variant: "ghost", onClick: () => onClose?.(), className: "p-[var(--appkit-space-2-5)] md:p-[var(--appkit-space-3)] transition-colors text-[var(--appkit-color-text-muted)] hover:bg-surface-hover hover:text-[var(--appkit-color-text)] flex-shrink-0", "aria-label": labels.closeAriaLabel, children: _jsx("svg", { className: "w-6 h-6 md:w-7 md:h-7 shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), (filteredSiteLinks.length > 0 || query) && (_jsxs(Stack, { className: "absolute top-full left-0 right-0 z-50 pt-[0.5rem] pb-[1rem]", padding: "x-md", gap: "sm", children: [filteredSiteLinks.length > 0 && !suggestionsLoading && (_jsxs(Div, { className: `${__O.hidden}`, rounded: "xl", shadow: "lg", surface: "default", border: "default", children: [_jsx(Div, { border: "default", className: "border-b", paddingY: "y-xs", paddingX: "x-md", children: _jsx(Text, { variant: "secondary", size: "xs", className: "uppercase tracking-wider", weight: "semibold", children: labels.quickLinks }) }), _jsx(Ul, { children: filteredSiteLinks.map((link) => {
|
|
340
340
|
const Icon = link.icon;
|
|
341
341
|
const itemIndex = quickLinkItems.findIndex((item) => item.link.href === link.href);
|
|
342
342
|
const isActive = activeIndex === itemIndex;
|
|
343
343
|
return (_jsx(Li, { children: _jsxs(Button, { type: "button", variant: "ghost", onClick: () => {
|
|
344
344
|
onClose?.();
|
|
345
345
|
router.push(link.href);
|
|
346
|
-
}, onMouseEnter: () => setActiveIndex(itemIndex), className: `w-full flex items-center gap-[var(--appkit-space-3)] px-[var(--appkit-space-4)] py-[var(--appkit-space-2-5)] text-left transition-colors border-b border-[var(--appkit-color-border)] last:border-b-0 ${isActive ? "bg-[var(--appkit-color-surface
|
|
347
|
-
}) })] })), query && (_jsxs(Div, { className: `${__O.hidden}`, rounded: "xl", shadow: "lg", surface: "default", border: "default", children: [suggestionsLoading ? (_jsx(Div, { padding: "inline", children: _jsx(Text, { variant: "secondary", size: "sm", children: labels.searching }) })) : suggestions.length > 0 ? (_jsx(Ul, { children: suggestions.map((suggestion, suggestionIndex) => (_jsx(SuggestionItem, { suggestion: suggestion, itemIndex: getOverlaySuggestionIndex(suggestionIndex), activeIndex: activeIndex, onSelect: handleSuggestionClick, onHover: setActiveIndex, typeIcons: typeIcons, typeBadges: typeBadges }, suggestion.objectID))) })) : null, _jsxs(Button, { type: "button", variant: "ghost", onClick: handleOverlaySearch, onMouseEnter: () => setActiveIndex(overlayItems.length - 1), className: `w-full flex items-center gap-[var(--appkit-space-2)] px-[var(--appkit-space-4)] py-[var(--appkit-space-3)] text-left transition-colors ${activeIndex === overlayItems.length - 1 ? "bg-[var(--appkit-color-surface
|
|
346
|
+
}, onMouseEnter: () => setActiveIndex(itemIndex), className: `w-full flex items-center gap-[var(--appkit-space-3)] px-[var(--appkit-space-4)] py-[var(--appkit-space-2-5)] text-left transition-colors border-b border-[var(--appkit-color-border)] last:border-b-0 ${isActive ? "bg-[var(--appkit-color-surface-elevated)]" : CLS_HOVER_ROW}`, children: [_jsx(Icon, { className: CLS_ICON_ROW }), _jsx(Text, { size: "sm", weight: "medium", children: link.label })] }) }, link.href));
|
|
347
|
+
}) })] })), query && (_jsxs(Div, { className: `${__O.hidden}`, rounded: "xl", shadow: "lg", surface: "default", border: "default", children: [suggestionsLoading ? (_jsx(Div, { padding: "inline", children: _jsx(Text, { variant: "secondary", size: "sm", children: labels.searching }) })) : suggestions.length > 0 ? (_jsx(Ul, { children: suggestions.map((suggestion, suggestionIndex) => (_jsx(SuggestionItem, { suggestion: suggestion, itemIndex: getOverlaySuggestionIndex(suggestionIndex), activeIndex: activeIndex, onSelect: handleSuggestionClick, onHover: setActiveIndex, typeIcons: typeIcons, typeBadges: typeBadges }, suggestion.objectID))) })) : null, _jsxs(Button, { type: "button", variant: "ghost", onClick: handleOverlaySearch, onMouseEnter: () => setActiveIndex(overlayItems.length - 1), className: `w-full flex items-center gap-[var(--appkit-space-2)] px-[var(--appkit-space-4)] py-[var(--appkit-space-3)] text-left transition-colors ${activeIndex === overlayItems.length - 1 ? "bg-[var(--appkit-color-surface-elevated)]" : CLS_HOVER_ROW}${suggestions.length > 0 ? " border-t border-[var(--appkit-color-border)]" : ""}`, children: [_jsx("svg", { className: CLS_ICON_ROW, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }), _jsx(Text, { size: "sm", weight: "medium", children: labels.browseProducts(query) })] })] }))] }))] }) }));
|
|
348
348
|
}
|
|
@@ -84,9 +84,24 @@ export function SearchResultsClient({ q, initialResult, indexEnabled, isSignedIn
|
|
|
84
84
|
setSelectedFacets([]);
|
|
85
85
|
}, [usingLocal, selectedFacets.length]);
|
|
86
86
|
const waiting = local.status === "loading" && q.trim().length > 0;
|
|
87
|
-
return (_jsx(SearchResultsPageView, { result: waiting ? { ...result, groups: [], totalHits: 0 } : result,
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
return (_jsx(SearchResultsPageView, { result: waiting ? { ...result, groups: [], totalHits: 0 } : result,
|
|
88
|
+
/*
|
|
89
|
+
* 🛑 NO FOOTNOTE. This read "Catalogue index updated 19/10/2026,
|
|
90
|
+
* 16:21:48." underneath a shopper's search results.
|
|
91
|
+
*
|
|
92
|
+
* It is an implementation detail of how the client-side index is built,
|
|
93
|
+
* phrased in our vocabulary, and a shopper can do nothing with it. Worse
|
|
94
|
+
* than useless: a visible timestamp implies the results might be STALE,
|
|
95
|
+
* on the one surface where a shopper needs to trust that what they see
|
|
96
|
+
* is what we have.
|
|
97
|
+
*
|
|
98
|
+
* The two messages below are kept, and the contrast is the point —
|
|
99
|
+
* "couldn't search your account" and the index-failure line both tell
|
|
100
|
+
* the user something that changes what they should believe about the
|
|
101
|
+
* results in front of them. A build timestamp on a SUCCESSFUL search
|
|
102
|
+
* does not.
|
|
103
|
+
*/
|
|
104
|
+
renderAboveGroups: () => (_jsxs(_Fragment, { children: [waiting && _jsx(Text, { color: "muted", children: "Searching\u2026" }), usingLocal && (_jsx(SearchFacetRail, { facets: local.facets, selected: selectedFacets, onToggle: toggleFacet })), scoped.status === "failed" && (
|
|
90
105
|
/*
|
|
91
106
|
* Said out loud, for the same reason as the line below it. A
|
|
92
107
|
* seller whose draft is missing because the scoped fetch failed
|