@mohasinac/appkit 3.0.3 → 3.0.6
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/_internal/client/features/filters/filter-load-options.js +11 -1
- package/dist/_internal/client/features/layout/filterNavItems.js +0 -1
- package/dist/_internal/client/features/layout/index.js +0 -1
- package/dist/_internal/client/features/maintenance/views/MaintenanceDashboardView.js +0 -1
- package/dist/_internal/client/features/maintenance/views/ServerErrorDetailView.js +0 -1
- package/dist/_internal/server/features/auctions/data.d.ts +2 -4
- package/dist/_internal/server/features/auctions/data.js +3 -9
- package/dist/_internal/server/features/checkout/actions.js +9 -9
- package/dist/_internal/server/features/classified/adapters.d.ts +2 -15
- package/dist/_internal/server/features/classified/adapters.js +2 -17
- package/dist/_internal/server/features/classified/data.d.ts +1 -6
- package/dist/_internal/server/features/classified/data.js +2 -10
- package/dist/_internal/server/features/digital-code/adapters.d.ts +2 -15
- package/dist/_internal/server/features/digital-code/adapters.js +2 -17
- package/dist/_internal/server/features/digital-code/data.d.ts +1 -6
- package/dist/_internal/server/features/digital-code/data.js +2 -10
- package/dist/_internal/server/features/live/adapters.d.ts +2 -15
- package/dist/_internal/server/features/live/adapters.js +2 -17
- package/dist/_internal/server/features/live/data.d.ts +1 -6
- package/dist/_internal/server/features/live/data.js +2 -10
- package/dist/_internal/server/features/pre-orders/data.d.ts +2 -4
- package/dist/_internal/server/features/pre-orders/data.js +3 -9
- package/dist/_internal/server/features/prize-draws/actions.d.ts +2 -0
- package/dist/_internal/server/features/prize-draws/actions.js +22 -0
- package/dist/_internal/server/features/prize-draws/data.d.ts +1 -17
- package/dist/_internal/server/features/prize-draws/data.js +2 -20
- package/dist/_internal/server/features/prize-draws/index.d.ts +3 -1
- package/dist/_internal/server/features/prize-draws/index.js +2 -0
- package/dist/_internal/server/features/prize-draws/service.d.ts +3 -0
- package/dist/_internal/server/features/prize-draws/service.js +19 -0
- package/dist/_internal/server/features/shared/listing-adapters.d.ts +19 -0
- package/dist/_internal/server/features/shared/listing-adapters.js +20 -0
- package/dist/_internal/server/features/shared/listing-data-factory.d.ts +9 -0
- package/dist/_internal/server/features/shared/listing-data-factory.js +33 -0
- package/dist/_internal/server/features/stores/data.d.ts +3 -14
- package/dist/_internal/server/features/stores/data.js +6 -44
- package/dist/_internal/server/jobs/core/cleanupRtdbEvents.js +1 -1
- package/dist/_internal/shared/features/cart/schema.d.ts +8 -8
- package/dist/_internal/shared/features/cart/schema.js +3 -0
- package/dist/_internal/shared/listing-types/_registry.js +2 -0
- package/dist/_internal/shared/listing-types/action-tracker.js +1 -1
- package/dist/_internal/shared/listing-types/auction/schema.d.ts +3 -1
- package/dist/_internal/shared/listing-types/auction/schema.js +7 -1
- package/dist/_internal/shared/listing-types/classified/schema.d.ts +2 -1
- package/dist/_internal/shared/listing-types/classified/schema.js +3 -1
- package/dist/_internal/shared/listing-types/digital-code/schema.d.ts +3 -1
- package/dist/_internal/shared/listing-types/digital-code/schema.js +7 -1
- package/dist/_internal/shared/listing-types/live/schema.d.ts +2 -1
- package/dist/_internal/shared/listing-types/live/schema.js +3 -1
- package/dist/_internal/shared/listing-types/pre-order/schema.d.ts +3 -1
- package/dist/_internal/shared/listing-types/pre-order/schema.js +7 -1
- package/dist/_internal/shared/listing-types/prize-draw/schema.d.ts +3 -1
- package/dist/_internal/shared/listing-types/prize-draw/schema.js +13 -1
- package/dist/_internal/shared/listing-types/standard/schema.d.ts +2 -1
- package/dist/_internal/shared/listing-types/standard/schema.js +4 -1
- package/dist/_internal/shared/types/base-document.d.ts +15 -0
- package/dist/_internal/shared/types/base-document.js +1 -0
- package/dist/_internal/shared/types/index.d.ts +2 -0
- package/dist/_internal/shared/types/index.js +1 -0
- package/dist/client/api/ApiError.d.ts +10 -4
- package/dist/client/api/surface-error.js +0 -1
- package/dist/client.d.ts +8 -4
- package/dist/client.js +4 -2
- package/dist/constants/api-endpoints.d.ts +12 -0
- package/dist/constants/api-endpoints.js +4 -0
- package/dist/core/hooks/useSyncManager.js +4 -4
- package/dist/features/_guide-cls.d.ts +1 -1
- package/dist/features/_guide-cls.js +1 -1
- package/dist/features/about/components/AboutView.js +1 -4
- package/dist/features/about/components/FAQPageView.js +1 -1
- package/dist/features/about/components/FeesView.js +1 -1
- package/dist/features/about/components/HelpPageView.js +1 -1
- package/dist/features/about/components/HowOrdersWorkView.js +1 -1
- package/dist/features/about/components/PublicProfileView.js +6 -6
- package/dist/features/about/components/TrackOrderView.js +1 -1
- package/dist/features/account/components/AddressBook.js +1 -1
- package/dist/features/account/components/AddressForm.js +10 -2
- package/dist/features/account/components/AddressesIndexListing.js +1 -1
- package/dist/features/account/components/BuyerAccountGuideView.js +1 -1
- package/dist/features/account/components/BuyerAuctionsGuideView.js +1 -1
- package/dist/features/account/components/BuyerOrdersGuideView.js +1 -1
- package/dist/features/account/components/BuyerShoppingGuideView.js +1 -1
- package/dist/features/account/components/ChatWindow.js +1 -1
- package/dist/features/account/components/MessagesView.js +1 -1
- package/dist/features/account/components/NotificationBell.js +25 -10
- package/dist/features/account/components/NotificationPreferencesPanel.js +2 -2
- package/dist/features/account/components/UserOffersPanel.js +1 -0
- package/dist/features/account/components/UserSidebar.js +1 -6
- package/dist/features/account/components/UserSupportView.js +1 -1
- package/dist/features/addresses/schemas/firestore.d.ts +2 -4
- package/dist/features/admin/components/AdminAnalyticsGuideView.js +2 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +1 -1
- package/dist/features/admin/components/AdminBundlesView.js +1 -1
- package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
- package/dist/features/admin/components/AdminCatalogGuideView.js +2 -2
- package/dist/features/admin/components/AdminContentGuideView.js +2 -2
- package/dist/features/admin/components/AdminEmployeeEditorView.js +2 -2
- package/dist/features/admin/components/AdminFulfillmentView.d.ts +6 -0
- package/dist/features/admin/components/AdminFulfillmentView.js +107 -0
- package/dist/features/admin/components/AdminGuideHubView.js +1 -5
- package/dist/features/admin/components/AdminMediaView.js +1 -1
- package/dist/features/admin/components/AdminNavigationView.js +1 -1
- package/dist/features/admin/components/AdminOrdersGuideView.js +3 -3
- package/dist/features/admin/components/AdminPayoutsView.js +1 -4
- package/dist/features/admin/components/AdminProductEditorView.js +21 -9
- package/dist/features/admin/components/AdminProductsView.js +3 -0
- package/dist/features/admin/components/AdminSidebar.js +1 -6
- package/dist/features/admin/components/AdminSiteConfigGuideView.js +1 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +9 -1
- package/dist/features/admin/components/AdminStoreEditorView.js +1 -1
- package/dist/features/admin/components/AdminStoresGuideView.js +2 -2
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +7 -7
- package/dist/features/admin/components/AdminTeamGuideView.js +2 -2
- package/dist/features/admin/components/AdminTrustGuideView.js +3 -3
- package/dist/features/admin/components/AdminUsersGuideView.js +3 -3
- package/dist/features/admin/components/DashboardStats.js +6 -5
- package/dist/features/admin/components/DataListingView.d.ts +1 -0
- package/dist/features/admin/components/DataTable.js +2 -2
- package/dist/features/admin/components/analytics/AdminAnalyticsCharts.js +4 -6
- package/dist/features/admin/components/analytics/AdminStatCard.js +4 -3
- package/dist/features/admin/components/analytics/AdminTopProductsTable.js +1 -1
- package/dist/features/admin/components/index.d.ts +2 -0
- package/dist/features/admin/components/index.js +1 -0
- package/dist/features/admin/schemas/firestore.d.ts +3 -8
- package/dist/features/auctions/components/AuctionBidsTable.js +1 -1
- package/dist/features/auctions/components/AuctionDetailPageView.js +1 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +3 -3
- package/dist/features/auctions/schemas/firestore.d.ts +2 -4
- package/dist/features/auctions/schemas/index.d.ts +2 -2
- package/dist/features/auth/components/ForgotPasswordView.js +19 -8
- package/dist/features/auth/components/LoginForm.js +10 -2
- package/dist/features/auth/components/RegisterForm.js +10 -2
- package/dist/features/auth/components/ResetPasswordView.js +19 -8
- package/dist/features/auth/components/SocialAuthButtons.js +1 -1
- package/dist/features/before-after/components/BeforeAfterSlider.js +1 -1
- package/dist/features/before-after/schemas/index.d.ts +2 -2
- package/dist/features/blog/api/[slug]/route.js +1 -1
- package/dist/features/blog/components/BlogFeaturedCard.js +1 -1
- package/dist/features/blog/components/BlogListView.js +1 -1
- package/dist/features/blog/components/BlogPostView.js +1 -1
- package/dist/features/blog/schemas/firestore.d.ts +2 -4
- package/dist/features/blog/schemas/index.d.ts +6 -6
- package/dist/features/cart/components/CartDrawer.js +2 -2
- package/dist/features/cart/components/CartView.js +1 -1
- package/dist/features/cart/components/CheckoutView.js +1 -1
- package/dist/features/cart/schemas/firestore.d.ts +2 -4
- package/dist/features/categories/components/BrandDetailPageView.js +2 -1
- package/dist/features/categories/components/BundleDetailView.js +1 -1
- package/dist/features/categories/components/BundleItemsPicker.js +1 -1
- package/dist/features/categories/components/CategoryBundlesListing.js +3 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +1 -1
- package/dist/features/categories/components/CategoryGrid.js +2 -1
- package/dist/features/categories/components/CategoryTableColumns.js +5 -2
- package/dist/features/categories/components/CategoryTree.js +3 -2
- package/dist/features/categories/components/ConcernCard.js +1 -1
- package/dist/features/categories/schemas/firestore.d.ts +2 -4
- package/dist/features/categories/schemas/index.d.ts +6 -6
- package/dist/features/checkout/components/CheckoutStepper.js +1 -1
- package/dist/features/collections/components/CollectionCard.js +1 -1
- package/dist/features/contact/components/ContactPageView.js +27 -27
- package/dist/features/contact/email.js +4 -4
- package/dist/features/copilot/components/AdminCopilotView.js +3 -3
- package/dist/features/email/primitives.d.ts +4 -0
- package/dist/features/email/primitives.js +4 -0
- package/dist/features/events/components/EventCard.js +2 -2
- package/dist/features/events/components/EventOfferCard.js +1 -1
- package/dist/features/events/components/EventRaffleEntryForm.js +1 -1
- package/dist/features/events/components/EventRaffleWinnerView.js +2 -2
- package/dist/features/events/components/EventSaleBanner.js +0 -1
- package/dist/features/events/schemas/firestore.d.ts +2 -4
- package/dist/features/events/schemas/index.d.ts +4 -4
- package/dist/features/faq/components/ContactCTA.js +1 -1
- package/dist/features/faq/components/FAQAccordion.js +2 -2
- package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
- package/dist/features/faq/components/FAQHelpfulButtons.js +2 -2
- package/dist/features/faq/components/RelatedFAQs.js +1 -1
- package/dist/features/faq/schemas/firestore.d.ts +2 -4
- package/dist/features/faq/schemas/index.d.ts +4 -4
- package/dist/features/filters/AsyncFacetSection.js +3 -3
- package/dist/features/filters/FilterFacetSection.js +6 -6
- package/dist/features/filters/RangeFilter.js +2 -2
- package/dist/features/filters/SwitchFilter.js +1 -1
- package/dist/features/forms/Checkbox.js +1 -1
- package/dist/features/forms/Slider.js +7 -2
- package/dist/features/forms/utils.d.ts +2 -2
- package/dist/features/forms/utils.js +2 -2
- package/dist/features/grouped/schemas/index.d.ts +2 -2
- package/dist/features/history/hooks/useHistory.js +1 -1
- package/dist/features/homepage/components/AdSlot.js +3 -3
- package/dist/features/homepage/components/AdvertisementBanner.js +5 -4
- package/dist/features/homepage/components/BeforeAfterCard.js +9 -1
- package/dist/features/homepage/components/CharacterHotspot.js +42 -49
- package/dist/features/homepage/components/CharacterHotspotForm.js +18 -20
- package/dist/features/homepage/components/CollectionCardsSection.js +2 -2
- package/dist/features/homepage/components/CustomCardsSection.js +3 -6
- package/dist/features/homepage/components/CustomerReviewsSection.js +1 -1
- package/dist/features/homepage/components/FAQSection.js +2 -2
- package/dist/features/homepage/components/FeaturedResultsSection.js +1 -1
- package/dist/features/homepage/components/GoogleReviewsSection.js +4 -4
- package/dist/features/homepage/components/HeroBanner.js +12 -63
- package/dist/features/homepage/components/HeroCarousel.js +6 -6
- package/dist/features/homepage/components/HeroSection.js +1 -1
- package/dist/features/homepage/components/HowItWorksInfoView.js +1 -1
- package/dist/features/homepage/components/HowItWorksSection.js +5 -3
- package/dist/features/homepage/components/NewsletterBanner.js +3 -7
- package/dist/features/homepage/components/PromoGrid.js +2 -26
- package/dist/features/homepage/components/SectionCarousel.js +1 -1
- package/dist/features/homepage/components/SecurityHighlightsSection.js +6 -3
- package/dist/features/homepage/components/ShopByCategorySection.js +3 -3
- package/dist/features/homepage/components/SiteFeaturesSection.js +1 -1
- package/dist/features/homepage/components/SocialPostCard.js +2 -3
- package/dist/features/homepage/components/StatsCounterSection.js +6 -3
- package/dist/features/homepage/components/TestimonialsCarousel.js +2 -25
- package/dist/features/homepage/components/TrustBadges.js +1 -9
- package/dist/features/homepage/components/TrustFeaturesSection.js +6 -3
- package/dist/features/homepage/components/TrustIndicatorsSection.js +1 -1
- package/dist/features/homepage/components/WelcomeSection.js +2 -2
- package/dist/features/homepage/components/WhatsAppCommunitySection.js +1 -1
- package/dist/features/homepage/lib/franchise-colors.d.ts +7 -0
- package/dist/features/homepage/lib/franchise-colors.js +7 -0
- package/dist/features/homepage/lib/live-stats.js +5 -5
- package/dist/features/homepage/schemas/firestore.d.ts +4 -12
- package/dist/features/homepage/schemas/index.d.ts +2 -2
- package/dist/features/layout/AppLayoutShell.js +7 -9
- package/dist/features/layout/BottomNavbar.js +1 -2
- package/dist/features/messages/hooks/useConversation.js +2 -1
- package/dist/features/messages/hooks/useConversations.js +1 -0
- package/dist/features/messages/schemas/firestore.d.ts +2 -4
- package/dist/features/messages/schemas/index.d.ts +2 -2
- package/dist/features/orders/components/OrdersList.js +1 -1
- package/dist/features/orders/components/RefundHistoryTable.d.ts +6 -0
- package/dist/features/orders/components/RefundHistoryTable.js +3 -2
- package/dist/features/orders/repository/orders.repository.d.ts +5 -0
- package/dist/features/orders/repository/orders.repository.js +26 -0
- package/dist/features/orders/schemas/firestore.d.ts +9 -5
- package/dist/features/orders/schemas/firestore.js +3 -0
- package/dist/features/payments/schemas/firestore.d.ts +2 -4
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +2 -2
- package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +2 -1
- package/dist/features/pre-orders/schemas/index.d.ts +2 -2
- package/dist/features/products/components/CompareOverlay.js +11 -8
- package/dist/features/products/components/CustomSectionTabContent.js +1 -1
- package/dist/features/products/components/FeatureBadge.js +11 -1
- package/dist/features/products/components/GroupSettingsPanel.js +2 -2
- package/dist/features/products/components/MarketplaceBundleCard.js +1 -1
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +2 -2
- package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
- package/dist/features/products/components/PrizeDrawsIndexListing.js +1 -1
- package/dist/features/products/components/ProductCardMetadataSection.js +0 -1
- package/dist/features/products/components/ProductDetailPageView.js +1 -1
- package/dist/features/products/components/ProductFeatureBadges.js +1 -1
- package/dist/features/products/components/ProductGalleryClient.js +1 -1
- package/dist/features/products/components/ProductGrid.js +7 -7
- package/dist/features/products/components/ShareButton.js +1 -1
- package/dist/features/products/components/ShowGroupSection.js +7 -5
- package/dist/features/products/components/SublistingCarouselSection.js +2 -2
- package/dist/features/products/constants/action-defs.d.ts +16 -0
- package/dist/features/products/constants/action-defs.js +16 -0
- package/dist/features/products/constants/listing-tabs.d.ts +48 -0
- package/dist/features/products/constants/listing-tabs.js +12 -0
- package/dist/features/products/repository/products.repository.d.ts +37 -0
- package/dist/features/products/repository/products.repository.js +28 -1
- package/dist/features/products/schemas/firestore.d.ts +8 -6
- package/dist/features/products/schemas/firestore.js +2 -0
- package/dist/features/products/schemas/index.d.ts +8 -8
- package/dist/features/products/types/index.d.ts +2 -0
- package/dist/features/promotions/components/CouponCard.js +2 -2
- package/dist/features/promotions/schemas/firestore.d.ts +2 -4
- package/dist/features/promotions/schemas/index.d.ts +6 -6
- package/dist/features/reviews/components/ReviewDetailShell.js +3 -3
- package/dist/features/reviews/components/ReviewModal.js +2 -4
- package/dist/features/reviews/components/ReviewSummary.js +3 -2
- package/dist/features/reviews/components/ReviewsList.js +1 -1
- package/dist/features/reviews/schemas/firestore.d.ts +2 -4
- package/dist/features/reviews/schemas/index.d.ts +2 -2
- package/dist/features/scams/actions/scam-actions.js +1 -1
- package/dist/features/scams/components/ScamProfileView.js +1 -1
- package/dist/features/scams/schemas/firestore.d.ts +2 -4
- package/dist/features/scams/schemas/index.d.ts +4 -4
- package/dist/features/search/components/Search.js +2 -2
- package/dist/features/search/components/SearchFiltersRow.js +1 -1
- package/dist/features/search/schemas/index.d.ts +3 -3
- package/dist/features/seller/components/BarcodeField.d.ts +17 -0
- package/dist/features/seller/components/BarcodeField.js +30 -0
- package/dist/features/seller/components/FulfillmentView.d.ts +5 -0
- package/dist/features/seller/components/FulfillmentView.js +120 -0
- package/dist/features/seller/components/PhysicalLocationModal.js +2 -3
- package/dist/features/seller/components/PrintCenterView.d.ts +3 -1
- package/dist/features/seller/components/PrintCenterView.js +52 -4
- package/dist/features/seller/components/SellerAddressesView.js +2 -1
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +1 -1
- package/dist/features/seller/components/SellerBidsView.js +1 -1
- package/dist/features/seller/components/SellerCouponEditorView.js +1 -1
- package/dist/features/seller/components/SellerGoogleReviewsView.js +1 -1
- package/dist/features/seller/components/SellerLiveView.js +1 -1
- package/dist/features/seller/components/SellerOffersPanel.js +1 -0
- package/dist/features/seller/components/SellerOrdersView.js +1 -1
- package/dist/features/seller/components/SellerPayoutMethodsView.js +8 -2
- package/dist/features/seller/components/SellerPayoutRequestView.js +1 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +1 -1
- package/dist/features/seller/components/SellerProductShell.d.ts +2 -0
- package/dist/features/seller/components/SellerProductShell.js +4 -3
- package/dist/features/seller/components/SellerProductsCards.js +1 -1
- package/dist/features/seller/components/SellerProductsView.js +1 -1
- package/dist/features/seller/components/SellerReviewsView.js +2 -1
- package/dist/features/seller/components/SellerShippingConfigsView.js +9 -3
- package/dist/features/seller/components/SellerShippingView.js +1 -1
- package/dist/features/seller/components/SellerSidebar.js +3 -8
- package/dist/features/seller/components/SellerStoreCategoriesView.js +11 -3
- package/dist/features/seller/components/SellerTemplatesView.js +8 -2
- package/dist/features/seller/components/analytics/SellerRevenueChart.js +1 -1
- package/dist/features/seller/components/analytics/SellerTopProducts.js +1 -1
- package/dist/features/seller/components/index.d.ts +4 -0
- package/dist/features/seller/components/index.js +2 -0
- package/dist/features/seller/schemas/index.d.ts +4 -4
- package/dist/features/shell/FormShell.js +2 -2
- package/dist/features/shell/QuickFormDrawer.js +1 -1
- package/dist/features/site-settings/components/NavPermissionsManager.js +1 -1
- package/dist/features/store-extensions/schemas/firestore.d.ts +12 -44
- package/dist/features/stores/components/StoreCapabilitiesGuideView.js +1 -1
- package/dist/features/stores/components/StoreClassifiedsListing.d.ts +5 -0
- package/dist/features/stores/components/StoreClassifiedsListing.js +48 -0
- package/dist/features/stores/components/StoreClassifiedsPageView.d.ts +4 -0
- package/dist/features/stores/components/StoreClassifiedsPageView.js +20 -0
- package/dist/features/stores/components/StoreDetailLayoutView.js +23 -2
- package/dist/features/stores/components/StoreDigitalCodesListing.d.ts +5 -0
- package/dist/features/stores/components/StoreDigitalCodesListing.js +48 -0
- package/dist/features/stores/components/StoreDigitalCodesPageView.d.ts +4 -0
- package/dist/features/stores/components/StoreDigitalCodesPageView.js +20 -0
- package/dist/features/stores/components/StoreGuideHubView.js +2 -9
- package/dist/features/stores/components/StoreHeader.js +1 -1
- package/dist/features/stores/components/StoreListingsGuideView.js +4 -4
- package/dist/features/stores/components/StoreLiveItemsListing.d.ts +5 -0
- package/dist/features/stores/components/StoreLiveItemsListing.js +48 -0
- package/dist/features/stores/components/StoreLiveItemsPageView.d.ts +4 -0
- package/dist/features/stores/components/StoreLiveItemsPageView.js +20 -0
- package/dist/features/stores/components/StoreOrdersGuideView.js +2 -2
- package/dist/features/stores/components/StoresIndexListing.js +1 -1
- package/dist/features/stores/components/index.d.ts +9 -0
- package/dist/features/stores/components/index.js +3 -0
- package/dist/features/stores/schemas/firestore.d.ts +2 -4
- package/dist/features/stores/schemas/index.d.ts +4 -4
- package/dist/features/support/schemas/firestore.d.ts +2 -4
- package/dist/features/support/schemas/index.d.ts +2 -2
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +2 -2
- package/dist/features/wishlist/components/WishlistPage.js +1 -1
- package/dist/features/wishlist/components/WishlistToggleButton.js +3 -3
- package/dist/features/wishlist/hooks/useWishlistCount.js +1 -1
- package/dist/http/ApiClient.d.ts +1 -0
- package/dist/http/ApiClient.js +20 -0
- package/dist/monitoring/error-tracking.d.ts +0 -3
- package/dist/monitoring/error-tracking.js +1 -1
- package/dist/monitoring/server-logger.js +3 -3
- package/dist/next/components/GlobalError.js +1 -1
- package/dist/next/routing/route-map.d.ts +11 -0
- package/dist/next/routing/route-map.js +5 -0
- package/dist/react/ErrorBoundary.js +1 -1
- package/dist/schemas/registry.d.ts +24 -24
- package/dist/seed/grouped-listings-seed-data.d.ts +2 -1
- package/dist/seed/products-preorders-seed-data.d.ts +2 -1
- package/dist/seed/products-standard-seed-data.js +8 -0
- package/dist/server-entry.d.ts +3 -0
- package/dist/server-entry.js +3 -0
- package/dist/server.d.ts +4 -4
- package/dist/styles.css +45 -7
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-light.js +3 -0
- package/dist/tokens/tokens.css +43 -6
- package/dist/ui/components/Anchor.d.ts +8 -1
- package/dist/ui/components/Anchor.js +16 -2
- package/dist/ui/components/Button.d.ts +5 -2
- package/dist/ui/components/Button.js +5 -2
- package/dist/ui/components/Details.d.ts +33 -2
- package/dist/ui/components/Details.js +73 -4
- package/dist/ui/components/Div.d.ts +3 -0
- package/dist/ui/components/Div.js +12 -1
- package/dist/ui/components/DynamicBgDiv.d.ts +13 -0
- package/dist/ui/components/DynamicBgDiv.js +18 -0
- package/dist/ui/components/Form.d.ts +3 -19
- package/dist/ui/components/Form.js +8 -1
- package/dist/ui/components/Layout.d.ts +12 -1
- package/dist/ui/components/Layout.js +9 -1
- package/dist/ui/components/Progress.d.ts +1 -1
- package/dist/ui/components/Progress.js +1 -0
- package/dist/ui/components/Progress.style.css +1 -0
- package/dist/ui/components/ProgressBarFill.d.ts +6 -0
- package/dist/ui/components/ProgressBarFill.js +11 -0
- package/dist/ui/components/Semantic.d.ts +26 -7
- package/dist/ui/components/Semantic.js +33 -8
- package/dist/ui/components/TextLink.d.ts +36 -1
- package/dist/ui/components/TextLink.js +30 -2
- package/dist/ui/components/Typography.d.ts +26 -6
- package/dist/ui/components/Typography.js +37 -13
- package/dist/ui/components/VacationBanner.js +0 -1
- package/dist/ui/components/surface-tokens.d.ts +12 -2
- package/dist/ui/components/surface-tokens.js +19 -1
- package/dist/ui/rich-text/RichText.js +1 -1
- package/dist/utils/id-generators.d.ts +8 -0
- package/dist/utils/id-generators.js +14 -0
- package/dist/validation/schemas.d.ts +1 -1
- package/package.json +2 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from "react";
|
|
4
|
-
import { Button, Div, Heading,
|
|
4
|
+
import { Button, Div, Heading, Modal, RichText, Row, Stack, Text } from "../../../ui";
|
|
5
5
|
import { StarRating } from "../../../ui";
|
|
6
6
|
import { maskName } from "../../../security";
|
|
7
7
|
import { THEMED_TEXT_SUCCESS } from "../../../_internal/shared/styles/themed";
|
|
@@ -23,7 +23,5 @@ export function ViewReviewModal({ review, isOpen, onClose, }) {
|
|
|
23
23
|
day: "numeric",
|
|
24
24
|
})
|
|
25
25
|
: "";
|
|
26
|
-
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Review Details", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { gap: "3", children: [review.userAvatar ? (_jsx(
|
|
27
|
-
backgroundImage: `url(${img.thumbnailUrl ?? img.url})`,
|
|
28
|
-
}, icon: _jsx(Div, {}) }, 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" }) })] }) }));
|
|
26
|
+
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Review Details", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { gap: "3", children: [review.userAvatar ? (_jsx("img", { src: review.userAvatar, alt: displayName, className: "h-10 w-10 flex-shrink-0 object-cover rounded-full" })) : (_jsx(Row, { textWeight: "medium", textSize: "sm", className: "h-10 w-10 flex-shrink-0 bg-neutral-200 text-neutral-600 dark:text-neutral-400", 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-neutral-900 dark:text-neutral-100 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-neutral-900 dark:text-neutral-100 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-sm leading-relaxed text-neutral-600 dark:text-zinc-400" })), review.images && review.images.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", children: review.images.map((img, i) => (_jsx("button", { type: "button", "aria-label": `View image ${i + 1}`, onClick: () => setLightboxIdx(i), className: `h-20 w-20 rounded-lg border overflow-hidden transition hover:opacity-80 p-0 flex-shrink-0 ${lightboxIdx === i ? "ring-2 ring-primary-500" : "border-neutral-100 dark:border-zinc-700"}`, children: _jsx("img", { src: img.thumbnailUrl ?? img.url, alt: `Review thumbnail ${i + 1}`, className: "w-full h-full object-cover" }) }, 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" }) })] }) }));
|
|
29
27
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Div, Row, Span, Stack, Text } from "../../../ui";
|
|
3
3
|
import { StarRating } from "../../../ui";
|
|
4
|
+
import { ProgressBarFill } from "../../../ui/components/ProgressBarFill";
|
|
4
5
|
const CLS_STAR = "text-warning";
|
|
5
6
|
const CLS_BAR = "h-full rounded-full bg-warning-surface transition-all duration-300";
|
|
6
7
|
/**
|
|
@@ -13,9 +14,9 @@ const CLS_BAR = "h-full rounded-full bg-warning-surface transition-all duration-
|
|
|
13
14
|
*/
|
|
14
15
|
export function ReviewSummary({ averageRating, totalReviews, distribution, className = "", }) {
|
|
15
16
|
const stars = [5, 4, 3, 2, 1];
|
|
16
|
-
return (_jsxs(Stack, { align: "center", className: `sm:flex-row sm: sm:gap-
|
|
17
|
+
return (_jsxs(Stack, { align: "center", className: `sm:[flex-direction:row] sm: sm:gap-[2rem] ${className}`, gap: "md", children: [_jsxs(Stack, { className: "sm:items-[flex-start]", align: "center", gap: "xs", children: [_jsx(Span, { color: "inverse", weight: "bold", className: "leading-none text-neutral-900 dark:text-neutral-100 dark:", size: "5xl", children: averageRating.toFixed(1) }), _jsx(StarRating, { value: averageRating, size: "md", readOnly: true }), _jsxs(Text, { size: "sm", className: "text-neutral-500 dark:text-neutral-400", children: [totalReviews.toLocaleString(), " review", totalReviews !== 1 ? "s" : ""] })] }), _jsx(Stack, { className: "flex-1", gap: "sm", children: stars.map((star) => {
|
|
17
18
|
const count = distribution[star] ?? 0;
|
|
18
19
|
const pct = totalReviews > 0 ? Math.round((count / totalReviews) * 100) : 0;
|
|
19
|
-
return (_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Span, { size: "xs", weight: "medium", className: "w-4 text-neutral-600 dark:text-neutral-400 tabular-nums", align: "end", children: star }), _jsx(Span, { size: "xs", className: CLS_STAR, children: "\u2605" }), _jsx(Div, { className: "flex-1 h-2 bg-neutral-100 dark:bg-neutral-800 overflow-hidden", rounded: "full", role: "progressbar", "aria-valuenow": pct, "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": `${star} star: ${pct}%`, children: _jsx(
|
|
20
|
+
return (_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Span, { size: "xs", weight: "medium", className: "w-4 text-neutral-600 dark:text-neutral-400 tabular-nums", align: "end", children: star }), _jsx(Span, { size: "xs", className: CLS_STAR, children: "\u2605" }), _jsx(Div, { className: "flex-1 h-2 bg-neutral-100 dark:bg-neutral-800 overflow-hidden", rounded: "full", role: "progressbar", "aria-valuenow": pct, "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": `${star} star: ${pct}%`, children: _jsx(ProgressBarFill, { pct: pct, className: CLS_BAR }) }), _jsxs(Span, { size: "xs", className: "w-8 tabular-nums", color: "faint", align: "end", children: [pct, "%"] })] }, star));
|
|
20
21
|
}) })] }));
|
|
21
22
|
}
|
|
@@ -27,7 +27,7 @@ export function ReviewCard({ review, context = "general", className = "" }) {
|
|
|
27
27
|
const showProductLink = context !== "listing" && !!productHref;
|
|
28
28
|
const showProfileLink = !!profileHref;
|
|
29
29
|
const hasFooter = showStoreLink || showProductLink || showProfileLink;
|
|
30
|
-
return (_jsxs(Stack, { padding: "5", className: `group h-full border border-neutral-200 transition
|
|
30
|
+
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("img", { src: review.userAvatar, alt: displayName, className: "h-9 w-9 flex-shrink-0 object-cover rounded-full" })) : (_jsx(Row, { textWeight: "medium", textSize: "sm", className: "h-9 w-9 flex-shrink-0 bg-neutral-200 text-neutral-600 dark:text-neutral-400", 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-neutral-900 dark:text-neutral-100 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-neutral-900 dark:text-neutral-100 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-sm leading-relaxed text-neutral-600 dark:text-zinc-400" })), review.images && review.images.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mt-3", children: review.images.map((img, i) => (_jsx("img", { src: img.thumbnailUrl ?? img.url, alt: `Review image ${i + 1}`, className: "h-16 w-16 object-cover rounded-lg border border-neutral-100" }, 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-1.5 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-1.5 text-xs font-medium text-neutral-500 dark:text-zinc-400 hover:text-neutral-700 dark:hover:text-zinc-200", 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-1.5 text-xs font-medium text-neutral-500 dark:text-zinc-400 hover:text-neutral-700 dark:hover:text-zinc-200", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDC64" }), _jsx(Span, { truncate: true, children: displayName })] }))] }))] }));
|
|
31
31
|
}
|
|
32
32
|
export function ReviewsList({ reviews, context = "general", isLoading, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No reviews yet", }) {
|
|
33
33
|
if (isLoading) {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Reviews Firestore Document Types & Constants
|
|
3
3
|
*/
|
|
4
4
|
import type { ReviewStatus } from "../types";
|
|
5
|
+
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
5
6
|
/** Runtime-accessible review status values — use instead of bare string literals. */
|
|
6
7
|
export declare const ReviewStatusValues: {
|
|
7
8
|
readonly PENDING: "pending";
|
|
@@ -15,8 +16,7 @@ export interface ReviewVideoField {
|
|
|
15
16
|
trimStart?: number;
|
|
16
17
|
trimEnd?: number;
|
|
17
18
|
}
|
|
18
|
-
export interface ReviewDocument {
|
|
19
|
-
id: string;
|
|
19
|
+
export interface ReviewDocument extends BaseDocument {
|
|
20
20
|
productId: string;
|
|
21
21
|
productTitle: string;
|
|
22
22
|
storeId?: string;
|
|
@@ -44,8 +44,6 @@ export interface ReviewDocument {
|
|
|
44
44
|
isAnonymous?: boolean;
|
|
45
45
|
sellerReply?: string;
|
|
46
46
|
sellerRepliedAt?: Date;
|
|
47
|
-
createdAt: Date;
|
|
48
|
-
updatedAt: Date;
|
|
49
47
|
approvedAt?: Date;
|
|
50
48
|
rejectedAt?: Date;
|
|
51
49
|
}
|
|
@@ -103,8 +103,8 @@ export declare const reviewFirestoreSchema: z.ZodObject<{
|
|
|
103
103
|
createdAt: string | Date | z.objectOutputType<{
|
|
104
104
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
105
105
|
}, z.ZodTypeAny, "passthrough">;
|
|
106
|
-
id: string;
|
|
107
106
|
title: string;
|
|
107
|
+
id: string;
|
|
108
108
|
updatedAt: string | Date | z.objectOutputType<{
|
|
109
109
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
110
110
|
}, z.ZodTypeAny, "passthrough">;
|
|
@@ -150,8 +150,8 @@ export declare const reviewFirestoreSchema: z.ZodObject<{
|
|
|
150
150
|
createdAt: string | Date | z.objectInputType<{
|
|
151
151
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
152
152
|
}, z.ZodTypeAny, "passthrough">;
|
|
153
|
-
id: string;
|
|
154
153
|
title: string;
|
|
154
|
+
id: string;
|
|
155
155
|
updatedAt: string | Date | z.objectInputType<{
|
|
156
156
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
157
157
|
}, z.ZodTypeAny, "passthrough">;
|
|
@@ -43,7 +43,7 @@ export async function getPublicScammerById(id) {
|
|
|
43
43
|
if (!doc || doc.status !== "verified")
|
|
44
44
|
return null;
|
|
45
45
|
// Increment views — non-blocking.
|
|
46
|
-
scammerRepository.incrementViews(doc.id).catch(
|
|
46
|
+
scammerRepository.incrementViews(doc.id).catch(console.error);
|
|
47
47
|
return doc;
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
@@ -57,5 +57,5 @@ export function ScamProfileView({ scammer, isAuthenticated, incidents = [], comm
|
|
|
57
57
|
{ label: "Home", href: "/" },
|
|
58
58
|
{ label: "Scam Registry", href: registryHref },
|
|
59
59
|
{ label: scammer.displayNames[0] ?? "Profile" },
|
|
60
|
-
] }) }) }), _jsx(Section, { padding: "y-2xl", children: _jsx(Container, { size: "xl", children: _jsxs(Grid, { cols: "twoThird", gap: "lg", children: [_jsxs(Stack, { gap: "lg", children: [_jsx(ScammerHeaderCard, { scammer: scammer }), (scammer.phones.length > 0 || scammer.upiIds.length > 0 || scammer.emails.length > 0) && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: "Contact Identifiers" }), _jsxs(Grid, { cols: 2, gap: "sm", children: [scammer.phones.map((p) => (_jsx(IdentityChip, { label: "Phone", value: p, icon: _jsx(Phone, { className: "h-4 w-4" }) }, p))), scammer.upiIds.map((u) => (_jsx(IdentityChip, { label: "UPI ID", value: u, icon: _jsx(Wallet, { className: "h-4 w-4" }) }, u))), scammer.emails.map((e) => (_jsx(IdentityChip, { label: "Email", value: e, icon: _jsx(Mail, { className: "h-4 w-4" }) }, e)))] })] })), scammer.socialMedia.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: "Social Media Profiles" }), _jsx(Stack, { gap: "xs", children: scammer.socialMedia.map((sm, i) => (_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(ExternalLink, { className: "h-4 w-4 shrink-0 text-[color:var(--appkit-color-text-muted,theme(colors.zinc.400))]" }), _jsxs(Text, { size: "sm", weight: "medium", children: [SOCIAL_PLATFORM_LABELS[sm.platform] ?? sm.platform, ":"] }), sm.url ? (_jsx(Anchor, { href: sm.url, size: "sm", children: sm.handle })) : (_jsx(Text, { variant: "secondary", size: "sm", children: sm.handle }))] }, i))) })] })), scammer.itemInvolved && (_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: "Item Involved" }), _jsx(Text, { variant: "secondary", size: "sm", children: scammer.itemInvolved })] })), _jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: "What Happened" }), _jsx(Card, { variant: "flat", padding: "md", children: _jsx(Text, { className: "leading-relaxed", size: "sm", children: scammer.description }) })] }), scamTypeDef && scamTypeDef.howToAvoid.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: "How to Avoid This Scam" }), _jsx(Card, { variant: "flat", padding: "md", children: _jsx(Stack, { gap: "xs", as: "ol", children: scamTypeDef.howToAvoid.map((tip, i) => (_jsxs(Row, { gap: "sm", align: "start", as: "li", children: [_jsx(Span, { layout: "flex-center", size: "xs", weight: "bold", className: "h-5 w-5 shrink-0 bg-[color:var(--appkit-color-success,theme(colors.green.600))]/10 text-[color:var(--appkit-color-success,theme(colors.green.700))]", rounded: "full", children: i + 1 }), _jsx(Text, { variant: "secondary", className: "leading-relaxed", size: "sm", children: tip })] }, i))) }) })] })), scammer.evidence.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: "Evidence" }), _jsx(Row, { gap: "sm", wrap: true, children: scammer.evidence.map((url, i) => (_jsx(Anchor, { href: url, underline: "none", className: "block h-32 w-32
|
|
60
|
+
] }) }) }), _jsx(Section, { padding: "y-2xl", children: _jsx(Container, { size: "xl", children: _jsxs(Grid, { cols: "twoThird", gap: "lg", children: [_jsxs(Stack, { gap: "lg", children: [_jsx(ScammerHeaderCard, { scammer: scammer }), (scammer.phones.length > 0 || scammer.upiIds.length > 0 || scammer.emails.length > 0) && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: "Contact Identifiers" }), _jsxs(Grid, { cols: 2, gap: "sm", children: [scammer.phones.map((p) => (_jsx(IdentityChip, { label: "Phone", value: p, icon: _jsx(Phone, { className: "h-4 w-4" }) }, p))), scammer.upiIds.map((u) => (_jsx(IdentityChip, { label: "UPI ID", value: u, icon: _jsx(Wallet, { className: "h-4 w-4" }) }, u))), scammer.emails.map((e) => (_jsx(IdentityChip, { label: "Email", value: e, icon: _jsx(Mail, { className: "h-4 w-4" }) }, e)))] })] })), scammer.socialMedia.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: "Social Media Profiles" }), _jsx(Stack, { gap: "xs", children: scammer.socialMedia.map((sm, i) => (_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(ExternalLink, { className: "h-4 w-4 shrink-0 text-[color:var(--appkit-color-text-muted,theme(colors.zinc.400))]" }), _jsxs(Text, { size: "sm", weight: "medium", children: [SOCIAL_PLATFORM_LABELS[sm.platform] ?? sm.platform, ":"] }), sm.url ? (_jsx(Anchor, { href: sm.url, size: "sm", children: sm.handle })) : (_jsx(Text, { variant: "secondary", size: "sm", children: sm.handle }))] }, i))) })] })), scammer.itemInvolved && (_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: "Item Involved" }), _jsx(Text, { variant: "secondary", size: "sm", children: scammer.itemInvolved })] })), _jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: "What Happened" }), _jsx(Card, { variant: "flat", padding: "md", children: _jsx(Text, { className: "leading-relaxed", size: "sm", children: scammer.description }) })] }), scamTypeDef && scamTypeDef.howToAvoid.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: "How to Avoid This Scam" }), _jsx(Card, { variant: "flat", padding: "md", children: _jsx(Stack, { gap: "xs", as: "ol", children: scamTypeDef.howToAvoid.map((tip, i) => (_jsxs(Row, { gap: "sm", align: "start", as: "li", children: [_jsx(Span, { layout: "flex-center", size: "xs", weight: "bold", className: "h-5 w-5 shrink-0 bg-[color:var(--appkit-color-success,theme(colors.green.600))]/10 text-[color:var(--appkit-color-success,theme(colors.green.700))]", rounded: "full", children: i + 1 }), _jsx(Text, { variant: "secondary", className: "leading-relaxed", size: "sm", children: tip })] }, i))) }) })] })), scammer.evidence.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: "Evidence" }), _jsx(Row, { gap: "sm", wrap: true, children: scammer.evidence.map((url, i) => (_jsx(Anchor, { href: url, underline: "none", rounded: "lg", shadow: "sm", className: "block h-32 w-32 overflow-hidden border hover:opacity-90", children: _jsx(MediaImage, { src: url, alt: `Evidence ${i + 1}`, size: "thumbnail" }) }, i))) })] })), _jsx(ScammerIncidentsSection, { incidents: incidents }), _jsx(ScammerCommentsSection, { comments: comments, isAuthenticated: isAuthenticated, scammerId: scammer.id }), relatedScammers.length > 0 && (_jsxs(Stack, { gap: "sm", children: [_jsx(Heading, { level: 2, size: "base", weight: "semibold", children: "Related Profiles" }), _jsx(Stack, { gap: "sm", children: relatedScammers.map((rel) => (_jsx(Link, { href: String(ROUTES.PUBLIC.SCAM_DETAIL(rel.id)), className: "block", children: _jsx(Card, { variant: "outlined", padding: "sm", className: "hover:opacity-80 transition-opacity", children: _jsxs(Row, { gap: "sm", align: "center", justify: "between", children: [_jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", children: rel.displayNames[0] }), _jsx(Text, { variant: "secondary", size: "xs", children: SCAM_TYPE_LABELS[rel.scamType] ?? rel.scamType })] }), _jsxs(Row, { gap: "xs", align: "center", children: [_jsx(Badge, { variant: statusVariant(rel.status), children: SCAMMER_STATUS_LABELS[rel.status] ?? rel.status }), _jsx(Link2, { className: "h-4 w-4 text-[color:var(--appkit-color-text-muted,theme(colors.zinc.400))]" })] })] }) }) }, rel.id))) })] }))] }), _jsx(ScammerActionsColumn, { scammer: scammer, isAuthenticated: isAuthenticated, reportHref: reportHref, contestLoginHref: contestLoginHref })] }) }) })] }));
|
|
61
61
|
}
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
* - SCAMMER_MATCH_FIELDS lists the Firestore array-contains fields used for this query
|
|
26
26
|
*/
|
|
27
27
|
import type { ScamType } from "../constants/scam-types";
|
|
28
|
+
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
28
29
|
export declare const ScammerStatusValues: {
|
|
29
30
|
readonly PENDING_REVIEW: "pending_review";
|
|
30
31
|
readonly VERIFIED: "verified";
|
|
@@ -83,8 +84,7 @@ export interface ScammerSocialMedia {
|
|
|
83
84
|
handle: string;
|
|
84
85
|
url?: string;
|
|
85
86
|
}
|
|
86
|
-
export interface ScammerDocument {
|
|
87
|
-
id: string;
|
|
87
|
+
export interface ScammerDocument extends BaseDocument {
|
|
88
88
|
/** URL-safe slug — used in /scams/[slug]. Same as id. */
|
|
89
89
|
seoSlug: string;
|
|
90
90
|
/**
|
|
@@ -159,8 +159,6 @@ export interface ScammerDocument {
|
|
|
159
159
|
* Reset to false when all contests are reviewed.
|
|
160
160
|
*/
|
|
161
161
|
isContested?: boolean;
|
|
162
|
-
createdAt: Date;
|
|
163
|
-
updatedAt: Date;
|
|
164
162
|
}
|
|
165
163
|
/**
|
|
166
164
|
* One victim's report linked to an existing scammer profile.
|
|
@@ -83,9 +83,9 @@ export declare const scammerFirestoreSchema: z.ZodObject<{
|
|
|
83
83
|
createdAt: string | Date | z.objectOutputType<{
|
|
84
84
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
85
85
|
}, z.ZodTypeAny, "passthrough">;
|
|
86
|
-
id: string;
|
|
87
86
|
description: string;
|
|
88
87
|
tags: string[];
|
|
88
|
+
id: string;
|
|
89
89
|
updatedAt: string | Date | z.objectOutputType<{
|
|
90
90
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
91
91
|
}, z.ZodTypeAny, "passthrough">;
|
|
@@ -123,9 +123,9 @@ export declare const scammerFirestoreSchema: z.ZodObject<{
|
|
|
123
123
|
createdAt: string | Date | z.objectInputType<{
|
|
124
124
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
125
125
|
}, z.ZodTypeAny, "passthrough">;
|
|
126
|
-
id: string;
|
|
127
126
|
description: string;
|
|
128
127
|
tags: string[];
|
|
128
|
+
id: string;
|
|
129
129
|
updatedAt: string | Date | z.objectInputType<{
|
|
130
130
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
131
131
|
}, z.ZodTypeAny, "passthrough">;
|
|
@@ -195,8 +195,8 @@ export declare const scammerIncidentFirestoreSchema: z.ZodObject<{
|
|
|
195
195
|
createdAt: string | Date | z.objectOutputType<{
|
|
196
196
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
197
197
|
}, z.ZodTypeAny, "passthrough">;
|
|
198
|
-
id: string;
|
|
199
198
|
description: string;
|
|
199
|
+
id: string;
|
|
200
200
|
scamType: string;
|
|
201
201
|
reportedBy: string;
|
|
202
202
|
scammerId: string;
|
|
@@ -218,8 +218,8 @@ export declare const scammerIncidentFirestoreSchema: z.ZodObject<{
|
|
|
218
218
|
createdAt: string | Date | z.objectInputType<{
|
|
219
219
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
220
220
|
}, z.ZodTypeAny, "passthrough">;
|
|
221
|
-
id: string;
|
|
222
221
|
description: string;
|
|
222
|
+
id: string;
|
|
223
223
|
scamType: string;
|
|
224
224
|
reportedBy: string;
|
|
225
225
|
scammerId: string;
|
|
@@ -216,7 +216,7 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
|
|
|
216
216
|
if (inlineBlurRef.current)
|
|
217
217
|
clearTimeout(inlineBlurRef.current);
|
|
218
218
|
inlineBlurRef.current = setTimeout(() => setIsInlineOpen(false), 120);
|
|
219
|
-
}, placeholder: placeholder ?? labels.placeholder, className: "w-full rounded-lg border border-zinc-300 bg-white pl-9 text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 dark:border-slate-700 dark:bg-slate-900 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.
|
|
219
|
+
}, placeholder: placeholder ?? labels.placeholder, className: "w-full rounded-lg border border-zinc-300 bg-white pl-9 text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 dark:border-slate-700 dark:bg-slate-900 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", "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", { value: selectedType, onChange: (e) => handleTypeChange(e.target.value), "aria-label": labels.resourceTypeLabel ?? "Search in", className: "flex-shrink-0 rounded-lg border border-zinc-300 bg-white px-2 py-2 text-sm text-zinc-700 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 dark:border-slate-700 dark:bg-slate-900 dark:text-zinc-300 cursor-pointer", children: resourceTypes.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value))) })), 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", "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", { className: "absolute top-full left-0 right-0 mt-2 rounded-xl overflow-hidden border border-zinc-200 bg-white shadow-lg dark:border-slate-700 dark:bg-slate-900 [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 &&
|
|
220
220
|
suggestions.slice(0, 5).map((suggestion, suggestionIndex) => {
|
|
221
221
|
const itemIndex = inlineQuickLinkItems.length + suggestionIndex;
|
|
222
222
|
const isActive = activeIndex === itemIndex;
|
|
@@ -292,7 +292,7 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
|
|
|
292
292
|
};
|
|
293
293
|
if (!isOpen)
|
|
294
294
|
return null;
|
|
295
|
-
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-
|
|
295
|
+
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", children: _jsx(Input, { ref: inputRef, bare: true, type: "search", placeholder: placeholder ?? labels.placeholder, value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: handleOverlayKeyDown, className: "w-full rounded-lg border border-zinc-300 bg-white text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 dark:border-slate-700 dark:bg-slate-900 dark:text-white dark:placeholder:text-slate-500" }) }), resourceTypes && resourceTypes.length > 0 && (_jsx("select", { value: selectedType, onChange: (e) => handleTypeChange(e.target.value), "aria-label": labels.resourceTypeLabel ?? "Search in", className: "flex-shrink-0 rounded-lg border border-zinc-300 bg-white px-2 py-2 text-sm text-zinc-700 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 dark:border-slate-700 dark:bg-slate-900 dark:text-zinc-300 cursor-pointer", children: resourceTypes.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value))) })), _jsxs(Button, { gap: "md", onClick: handleOverlaySearch, variant: "primary", size: "md", className: "hidden sm:", children: [_jsx("svg", { className: "w-5 h-5", 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-2.5 md:p-3 transition-colors text-zinc-500 hover:bg-zinc-100 hover:text-zinc-700 dark:text-zinc-400 dark:hover:bg-slate-800 dark:hover:text-zinc-200 flex-shrink-0", "aria-label": labels.closeAriaLabel, children: _jsx("svg", { className: "w-6 h-6 md:w-7 md:h-7", 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) => {
|
|
296
296
|
const Icon = link.icon;
|
|
297
297
|
const itemIndex = quickLinkItems.findIndex((item) => item.link.href === link.href);
|
|
298
298
|
const isActive = activeIndex === itemIndex;
|
|
@@ -17,5 +17,5 @@ export function SearchFiltersRow({ urlCategory, categories, urlMinPrice, urlMaxP
|
|
|
17
17
|
return (_jsxs(Row, { wrap: true, gap: "md", align: "end", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "muted", children: L.categoryFilter }), _jsx(Select, { value: urlCategory, onValueChange: (value) => onCategoryChange(value), options: [
|
|
18
18
|
{ value: "", label: L.allCategories },
|
|
19
19
|
...categories.map((cat) => ({ value: cat.id, label: cat.name })),
|
|
20
|
-
], className: "rounded-lg border border-zinc-200 dark:border-slate-700 bg-white dark:bg-slate-800/60 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-primary-500/20" })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "muted", children: L.priceRange }), _jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Input, { type: "number", min: 0, value: minPrice, onChange: (e) => setMinPrice(e.target.value), placeholder: L.minPrice, className: "w-28 rounded-lg border border-zinc-200 dark:border-slate-700 bg-white dark:bg-slate-800/60 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-primary-500/20" }), _jsx(Span, { size: "sm", color: "muted", children: "\u2013" }), _jsx(Input, { type: "number", min: 0, value: maxPrice, onChange: (e) => setMaxPrice(e.target.value), placeholder: L.maxPrice, className: "w-28 rounded-lg border border-zinc-200 dark:border-slate-700 bg-white dark:bg-slate-800/60 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-primary-500/20" }), _jsx(Button, { rounded: "lg", type: "button", variant: "primary", size: "sm", onClick: () => onPriceFilter(minPrice, maxPrice), className: "h-10 px-4 bg-primary-600 hover:bg-primary-700 text-white text-sm font-medium transition-colors", children: L.apply })] })] }), showClear && (_jsx(Button, { rounded: "lg", type: "button", variant: "outline", size: "sm", onClick: onClearFilters, paddingX: "md", textSize: "sm", className: "h-10 border border-
|
|
20
|
+
], className: "rounded-lg border border-zinc-200 dark:border-slate-700 bg-white dark:bg-slate-800/60 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-primary-500/20" })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "muted", children: L.priceRange }), _jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Input, { type: "number", min: 0, value: minPrice, onChange: (e) => setMinPrice(e.target.value), placeholder: L.minPrice, className: "w-28 rounded-lg border border-zinc-200 dark:border-slate-700 bg-white dark:bg-slate-800/60 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-primary-500/20" }), _jsx(Span, { size: "sm", color: "muted", children: "\u2013" }), _jsx(Input, { type: "number", min: 0, value: maxPrice, onChange: (e) => setMaxPrice(e.target.value), placeholder: L.maxPrice, className: "w-28 rounded-lg border border-zinc-200 dark:border-slate-700 bg-white dark:bg-slate-800/60 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-primary-500/20" }), _jsx(Button, { rounded: "lg", type: "button", variant: "primary", size: "sm", onClick: () => onPriceFilter(minPrice, maxPrice), className: "h-10 px-4 bg-primary-600 hover:bg-primary-700 text-white text-sm font-medium transition-colors", children: L.apply })] })] }), showClear && (_jsx(Button, { rounded: "lg", type: "button", variant: "outline", size: "sm", onClick: onClearFilters, paddingX: "md", textSize: "sm", className: "h-10 border border-[var(--appkit-color-border)] text-[var(--appkit-color-text)] hover:bg-[var(--appkit-color-surface-elevated)] transition-colors", children: L.clearFilters }))] }));
|
|
21
21
|
}
|
|
@@ -21,9 +21,9 @@ export declare const searchProductItemSchema: z.ZodObject<{
|
|
|
21
21
|
isPromoted: z.ZodOptional<z.ZodBoolean>;
|
|
22
22
|
slug: z.ZodString;
|
|
23
23
|
}, "strip", z.ZodTypeAny, {
|
|
24
|
-
id: string;
|
|
25
24
|
title: string;
|
|
26
25
|
slug: string;
|
|
26
|
+
id: string;
|
|
27
27
|
status?: string | undefined;
|
|
28
28
|
currency?: string | undefined;
|
|
29
29
|
featured?: boolean | undefined;
|
|
@@ -32,9 +32,9 @@ export declare const searchProductItemSchema: z.ZodObject<{
|
|
|
32
32
|
price?: number | undefined;
|
|
33
33
|
mainImage?: string | undefined;
|
|
34
34
|
}, {
|
|
35
|
-
id: string;
|
|
36
35
|
title: string;
|
|
37
36
|
slug: string;
|
|
37
|
+
id: string;
|
|
38
38
|
status?: string | undefined;
|
|
39
39
|
currency?: string | undefined;
|
|
40
40
|
featured?: boolean | undefined;
|
|
@@ -54,8 +54,8 @@ export declare const searchQuerySchema: z.ZodObject<{
|
|
|
54
54
|
sort: z.ZodOptional<z.ZodString>;
|
|
55
55
|
page: z.ZodDefault<z.ZodNumber>;
|
|
56
56
|
}, "strip", z.ZodTypeAny, {
|
|
57
|
-
q: string;
|
|
58
57
|
page: number;
|
|
58
|
+
q: string;
|
|
59
59
|
sort?: string | undefined;
|
|
60
60
|
category?: string | undefined;
|
|
61
61
|
minPrice?: number | undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface BarcodeFieldProps {
|
|
2
|
+
label?: string;
|
|
3
|
+
value: string;
|
|
4
|
+
onChange: (value: string) => void;
|
|
5
|
+
/** Called when the scanner fires Enter after rapid character input (< 50 ms between keys). */
|
|
6
|
+
onScan?: (value: string) => void;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
helperText?: string;
|
|
9
|
+
autoFocus?: boolean;
|
|
10
|
+
readOnly?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Text field that detects USB/Bluetooth barcode scanner input.
|
|
14
|
+
* Scanners type characters rapidly (< 50 ms per keydown) then send Enter.
|
|
15
|
+
* onScan is called when Enter follows rapid input; manual typing is ignored.
|
|
16
|
+
*/
|
|
17
|
+
export declare function BarcodeField({ label, value, onChange, onScan, placeholder, helperText, autoFocus, readOnly, }: BarcodeFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useRef, useCallback } from "react";
|
|
4
|
+
import { FieldInput } from "../../../ui/forms/FieldInput";
|
|
5
|
+
import { Text } from "../../../ui/components/Typography";
|
|
6
|
+
import { Stack } from "../../../ui/components/Layout";
|
|
7
|
+
/**
|
|
8
|
+
* Text field that detects USB/Bluetooth barcode scanner input.
|
|
9
|
+
* Scanners type characters rapidly (< 50 ms per keydown) then send Enter.
|
|
10
|
+
* onScan is called when Enter follows rapid input; manual typing is ignored.
|
|
11
|
+
*/
|
|
12
|
+
export function BarcodeField({ label = "Barcode / Sticker ID", value, onChange, onScan, placeholder = "Scan or type barcode…", helperText, autoFocus, readOnly, }) {
|
|
13
|
+
const lastKeyTime = useRef(0);
|
|
14
|
+
const buffer = useRef("");
|
|
15
|
+
const handleKeyDown = useCallback((e) => {
|
|
16
|
+
const now = Date.now();
|
|
17
|
+
if (e.key === "Enter") {
|
|
18
|
+
e.preventDefault();
|
|
19
|
+
if (buffer.current) {
|
|
20
|
+
onScan?.(buffer.current);
|
|
21
|
+
buffer.current = "";
|
|
22
|
+
}
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const interval = now - lastKeyTime.current;
|
|
26
|
+
buffer.current = interval < 50 ? buffer.current + e.key : e.key;
|
|
27
|
+
lastKeyTime.current = now;
|
|
28
|
+
}, [onScan]);
|
|
29
|
+
return (_jsxs(Stack, { gap: "xs", children: [_jsx(FieldInput, { name: "barcodeId", label: label, type: "text", value: value, onChange: onChange, onKeyDown: handleKeyDown, placeholder: placeholder, autoFocus: autoFocus, readOnly: readOnly }), helperText && (_jsx(Text, { size: "xs", color: "muted", children: helperText }))] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useState } from "react";
|
|
4
|
+
import { Heading, Text } from "../../../ui/components/Typography";
|
|
5
|
+
import { Stack, Row } from "../../../ui/components/Layout";
|
|
6
|
+
import { Button } from "../../../ui/components/Button";
|
|
7
|
+
import { Badge } from "../../../ui/components/Badge";
|
|
8
|
+
import { apiClient } from "../../../http";
|
|
9
|
+
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { BarcodeField } from "./BarcodeField";
|
|
11
|
+
import { normalizeError } from "../../../errors/normalize";
|
|
12
|
+
import { useToast } from "../../../ui";
|
|
13
|
+
function statusBadgeVariant(status) {
|
|
14
|
+
if (status === "confirmed")
|
|
15
|
+
return "warning";
|
|
16
|
+
if (status === "processing")
|
|
17
|
+
return "info";
|
|
18
|
+
if (status === "shipped")
|
|
19
|
+
return "success";
|
|
20
|
+
return "default";
|
|
21
|
+
}
|
|
22
|
+
export function FulfillmentView({ currentUserId, }) {
|
|
23
|
+
const [mode, setMode] = useState("scan");
|
|
24
|
+
const [orders, setOrders] = useState([]);
|
|
25
|
+
const [scanValue, setScanValue] = useState("");
|
|
26
|
+
const [scanResult, setScanResult] = useState(null);
|
|
27
|
+
const [scanError, setScanError] = useState(null);
|
|
28
|
+
const [filterMine, setFilterMine] = useState(false);
|
|
29
|
+
const [isLoadingOrders, setIsLoadingOrders] = useState(true);
|
|
30
|
+
const [isConfirming, setIsConfirming] = useState(false);
|
|
31
|
+
const [actionLoadingId, setActionLoadingId] = useState(null);
|
|
32
|
+
const { showToast } = useToast();
|
|
33
|
+
const fetchOrders = useCallback(async () => {
|
|
34
|
+
// toast-intentionally-silent: background data loader — stale list is kept on error
|
|
35
|
+
setIsLoadingOrders(true);
|
|
36
|
+
try {
|
|
37
|
+
const res = await apiClient.get(SELLER_ENDPOINTS.ORDERS_FULFILLMENT);
|
|
38
|
+
setOrders(res.orders ?? []);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// keep stale list on error
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
setIsLoadingOrders(false);
|
|
45
|
+
}
|
|
46
|
+
}, []);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
void fetchOrders();
|
|
49
|
+
}, [fetchOrders]);
|
|
50
|
+
const handleScan = useCallback(async (barcode) => {
|
|
51
|
+
setScanError(null);
|
|
52
|
+
setScanResult(null);
|
|
53
|
+
try {
|
|
54
|
+
const product = await apiClient.get(SELLER_ENDPOINTS.PRODUCTS_SCAN(barcode));
|
|
55
|
+
const matchedOrder = orders.find((o) => o.productId === product.id ||
|
|
56
|
+
o.items?.some((i) => i.productId === product.id));
|
|
57
|
+
if (!matchedOrder) {
|
|
58
|
+
setScanError(`No pending order found for barcode "${barcode}".`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
setScanResult({ product, order: matchedOrder });
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
setScanError(normalizeError(err).message);
|
|
65
|
+
}
|
|
66
|
+
}, [orders]);
|
|
67
|
+
const confirmPick = useCallback(async () => {
|
|
68
|
+
if (!scanResult)
|
|
69
|
+
return;
|
|
70
|
+
setIsConfirming(true);
|
|
71
|
+
try {
|
|
72
|
+
await apiClient.patch(SELLER_ENDPOINTS.ORDERS_BY_ID(scanResult.order.id), { markPicked: true });
|
|
73
|
+
setScanResult(null);
|
|
74
|
+
setScanValue("");
|
|
75
|
+
setScanError(null);
|
|
76
|
+
showToast("Order marked as picked!", "success");
|
|
77
|
+
await fetchOrders();
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
setScanError(normalizeError(err).message);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
setIsConfirming(false);
|
|
84
|
+
}
|
|
85
|
+
}, [scanResult, fetchOrders, showToast]);
|
|
86
|
+
const handleMarkPicked = useCallback(async (orderId) => {
|
|
87
|
+
setActionLoadingId(orderId + ":picked");
|
|
88
|
+
try {
|
|
89
|
+
await apiClient.patch(SELLER_ENDPOINTS.ORDERS_BY_ID(orderId), {
|
|
90
|
+
markPicked: true,
|
|
91
|
+
});
|
|
92
|
+
await fetchOrders();
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
setActionLoadingId(null);
|
|
96
|
+
}
|
|
97
|
+
}, [fetchOrders]);
|
|
98
|
+
const handleMarkPacked = useCallback(async (orderId) => {
|
|
99
|
+
setActionLoadingId(orderId + ":packed");
|
|
100
|
+
try {
|
|
101
|
+
await apiClient.patch(SELLER_ENDPOINTS.ORDERS_BY_ID(orderId), {
|
|
102
|
+
markPacked: true,
|
|
103
|
+
});
|
|
104
|
+
await fetchOrders();
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
setActionLoadingId(null);
|
|
108
|
+
}
|
|
109
|
+
}, [fetchOrders]);
|
|
110
|
+
const visibleOrders = filterMine
|
|
111
|
+
? orders.filter((o) => o.assignedWorkerId === currentUserId)
|
|
112
|
+
: orders;
|
|
113
|
+
return (_jsxs(Stack, { gap: "md", padding: "md", children: [_jsxs(Row, { align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsx(Heading, { level: 3, children: "Pick & Pack" }), _jsxs(Row, { gap: "xs", children: [_jsx(Button, { onClick: () => setMode("scan"), variant: mode === "scan" ? "primary" : "secondary", size: "sm", children: "Scan Mode" }), _jsx(Button, { onClick: () => setMode("list"), variant: mode === "list" ? "primary" : "secondary", size: "sm", children: "List Mode" })] })] }), mode === "scan" && (_jsxs(Stack, { gap: "md", children: [_jsx(BarcodeField, { autoFocus: true, value: scanValue, onChange: setScanValue, onScan: handleScan, helperText: "Point scanner at an item sticker or type the barcode and press Enter." }), scanError && (_jsx(Text, { color: "error", size: "sm", children: scanError })), scanResult && (_jsxs(Stack, { surface: "card", padding: "md", rounded: "md", gap: "sm", children: [_jsx(Text, { size: "xs", color: "muted", children: "Scanned item" }), _jsx(Heading, { level: 5, children: scanResult.product.title ?? scanResult.product.id }), _jsxs(Row, { gap: "md", wrap: true, children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Order" }), _jsxs(Text, { size: "sm", weight: "medium", children: ["#", scanResult.order.id] })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Buyer" }), _jsx(Text, { size: "sm", weight: "medium", children: scanResult.order.userName })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Status" }), _jsx(Badge, { variant: statusBadgeVariant(scanResult.order.status), children: scanResult.order.status })] })] }), _jsxs(Row, { gap: "xs", children: [_jsx(Button, { variant: "primary", onClick: confirmPick, isLoading: isConfirming, children: "Confirm Pick" }), _jsx(Button, { variant: "secondary", onClick: () => {
|
|
114
|
+
setScanResult(null);
|
|
115
|
+
setScanValue("");
|
|
116
|
+
setScanError(null);
|
|
117
|
+
}, children: "Cancel" })] })] })), !scanResult && !scanError && (_jsx(Text, { color: "muted", size: "sm", children: orders.length === 0 && !isLoadingOrders
|
|
118
|
+
? "No orders waiting — great work!"
|
|
119
|
+
: `${orders.length} order${orders.length === 1 ? "" : "s"} in queue.` }))] })), mode === "list" && (_jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Button, { variant: filterMine ? "primary" : "secondary", size: "sm", onClick: () => setFilterMine((v) => !v), children: filterMine ? "Showing My Orders" : "My Orders" }), _jsx(Text, { size: "xs", color: "muted", children: isLoadingOrders ? "Loading…" : `${visibleOrders.length} order${visibleOrders.length === 1 ? "" : "s"}` })] }), visibleOrders.length === 0 && !isLoadingOrders && (_jsx(Text, { color: "muted", children: "No orders waiting \u2014 great work!" })), visibleOrders.map((order) => (_jsxs(Stack, { surface: "card", padding: "sm", rounded: "md", gap: "xs", children: [_jsxs(Row, { align: "center", gap: "xs", wrap: true, children: [_jsxs(Text, { size: "sm", weight: "medium", children: ["#", order.id] }), _jsx(Badge, { variant: statusBadgeVariant(order.status), children: order.status }), order.pickedAt && (_jsx(Badge, { variant: "success", children: "Picked" })), order.packedAt && (_jsx(Badge, { variant: "success", children: "Packed" }))] }), _jsxs(Text, { size: "xs", color: "muted", children: [order.userName, order.items && order.items.length > 0 && (_jsxs(_Fragment, { children: [" \u00B7 ", order.items.map((i) => i.productTitle).join(", ")] }))] }), order.assignedWorkerId && (_jsxs(Text, { size: "xs", color: "muted", children: ["Assigned: ", order.assignedWorkerId === currentUserId ? "Me" : order.assignedWorkerId] })), _jsxs(Row, { gap: "xs", children: [!order.pickedAt && (_jsx(Button, { size: "sm", variant: "secondary", onClick: () => handleMarkPicked(order.id), isLoading: actionLoadingId === order.id + ":picked", children: "Mark Picked" })), order.pickedAt && !order.packedAt && (_jsx(Button, { size: "sm", variant: "secondary", onClick: () => handleMarkPacked(order.id), isLoading: actionLoadingId === order.id + ":packed", children: "Mark Packed" }))] })] }, order.id)))] }))] }));
|
|
120
|
+
}
|
|
@@ -4,8 +4,7 @@ import { useState } from "react";
|
|
|
4
4
|
import { Modal } from "../../../ui/components/Modal";
|
|
5
5
|
import { Input } from "../../../ui/components/Input";
|
|
6
6
|
import { Button } from "../../../ui/components/Button";
|
|
7
|
-
import { Row } from "../../../ui/components/Layout";
|
|
8
|
-
import { Div } from "../../../ui/components/Div";
|
|
7
|
+
import { Row, Stack } from "../../../ui/components/Layout";
|
|
9
8
|
import { Text } from "../../../ui/components/Typography";
|
|
10
9
|
import { normalizeError } from "../../../errors/normalize";
|
|
11
10
|
export function PhysicalLocationModal({ count, onSave, onClose }) {
|
|
@@ -29,5 +28,5 @@ export function PhysicalLocationModal({ count, onSave, onClose }) {
|
|
|
29
28
|
setSaving(false);
|
|
30
29
|
}
|
|
31
30
|
};
|
|
32
|
-
return (_jsx(Modal, { isOpen: true, onClose: onClose, title: `Set Location — ${count} item${count !== 1 ? "s" : ""}`, size: "sm", actions: _jsxs(Row, { gap: "sm", justify: "end", children: [_jsx(Button, { variant: "ghost", onClick: onClose, disabled: saving, children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: handleSave, disabled: saving, children: saving ? "Saving..." : "Save" })] }), children: _jsxs(
|
|
31
|
+
return (_jsx(Modal, { isOpen: true, onClose: onClose, title: `Set Location — ${count} item${count !== 1 ? "s" : ""}`, size: "sm", actions: _jsxs(Row, { gap: "sm", justify: "end", children: [_jsx(Button, { variant: "ghost", onClick: onClose, disabled: saving, children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: handleSave, disabled: saving, children: saving ? "Saving..." : "Save" })] }), children: _jsxs(Stack, { gap: "3", children: [_jsx(Text, { variant: "muted", children: "Assign a physical storage location to the selected items. All fields are optional." }), _jsx(Input, { label: "Zone (e.g. A, B, Storage-1)", value: zone, onChange: e => setZone(e.target.value), placeholder: "A" }), _jsx(Input, { label: "Shelf (e.g. 3, Top, Middle)", value: shelf, onChange: e => setShelf(e.target.value), placeholder: "3" }), _jsx(Input, { label: "Bin (e.g. Blue, Box-12)", value: bin, onChange: e => setBin(e.target.value), placeholder: "Blue" }), error && _jsx(Text, { variant: "error", children: error })] }) }));
|
|
33
32
|
}
|
|
@@ -14,6 +14,7 @@ interface PrintCenterProduct {
|
|
|
14
14
|
condition?: string;
|
|
15
15
|
stockCount?: number;
|
|
16
16
|
physicalLocation?: string;
|
|
17
|
+
barcodeId?: string;
|
|
17
18
|
}
|
|
18
19
|
interface PrintCenterOrder {
|
|
19
20
|
id: string;
|
|
@@ -25,6 +26,7 @@ interface PrintCenterOrder {
|
|
|
25
26
|
productName: string;
|
|
26
27
|
quantity: number;
|
|
27
28
|
price: number;
|
|
29
|
+
barcodeId?: string;
|
|
28
30
|
}[];
|
|
29
31
|
physicalLocation?: string;
|
|
30
32
|
}
|
|
@@ -36,5 +38,5 @@ interface PrintCenterViewProps {
|
|
|
36
38
|
initialProducts?: PrintCenterProduct[];
|
|
37
39
|
initialOrders?: PrintCenterOrder[];
|
|
38
40
|
}
|
|
39
|
-
export declare function PrintCenterView({ store, brandName }: PrintCenterViewProps): import("react/jsx-runtime").JSX.Element;
|
|
41
|
+
export declare function PrintCenterView({ store, brandName, initialProducts, initialOrders, }: PrintCenterViewProps): import("react/jsx-runtime").JSX.Element;
|
|
40
42
|
export {};
|