@mohasinac/appkit 4.53.1 → 4.54.0
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/theme/ThemeProvider.js +15 -1
- package/dist/_internal/server/features/auth/capabilities.js +7 -5
- package/dist/_internal/server/features/categories/price-guidance-resolve.d.ts +66 -0
- package/dist/_internal/server/features/categories/price-guidance-resolve.js +138 -0
- package/dist/_internal/server/features/checkout/actions.d.ts +22 -6
- package/dist/_internal/server/features/checkout/actions.js +96 -51
- package/dist/_internal/server/features/checkout/index.d.ts +1 -1
- package/dist/_internal/server/features/checkout/index.js +1 -1
- package/dist/_internal/server/features/checkout/supplier-identity.js +28 -6
- package/dist/_internal/server/features/orders/adapters.js +1 -3
- package/dist/_internal/server/features/price-index/market.d.ts +57 -0
- package/dist/_internal/server/features/price-index/market.js +120 -0
- package/dist/_internal/server/features/products/list-public.d.ts +12 -0
- package/dist/_internal/server/features/products/list-public.js +10 -1
- package/dist/_internal/server/features/products/listing-tab-counts.d.ts +25 -1
- package/dist/_internal/server/features/products/listing-tab-counts.js +184 -10
- package/dist/_internal/server/features/search/index-build/sources.js +43 -2
- package/dist/_internal/server/features/site-settings/adapters.js +1 -1
- package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
- package/dist/_internal/server/features/stores/adapters.js +0 -1
- package/dist/_internal/server/features/stores/tabs.d.ts +7 -2
- package/dist/_internal/server/features/stores/tabs.js +5 -3
- package/dist/_internal/server/jobs/core/analyticsAlertEvaluate.js +2 -5
- package/dist/_internal/server/jobs/core/jobRunners.js +0 -10
- package/dist/_internal/server/jobs/core/onOrderCreate.js +10 -104
- package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +0 -2
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +0 -1
- package/dist/_internal/server/jobs/core/onScamReportCreate.js +0 -1
- package/dist/_internal/server/jobs/core/onScamReportRejected.js +0 -1
- package/dist/_internal/server/jobs/core/onScamReportVerified.js +0 -1
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +0 -1
- package/dist/_internal/server/jobs/core/pendingOrderTimeout.js +0 -1
- package/dist/_internal/server/notifications/channel-health.d.ts +7 -3
- package/dist/_internal/server/notifications/channel-health.js +2 -2
- package/dist/_internal/server/notifications/send-guard.d.ts +1 -1
- package/dist/_internal/server/notifications/send-guard.js +2 -4
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +128 -28
- package/dist/_internal/shared/checkout/order-math.d.ts +1 -1
- package/dist/_internal/shared/checkout/order-math.js +1 -1
- package/dist/_internal/shared/features/categories/bundle-copy.d.ts +1 -1
- package/dist/_internal/shared/features/checkout/config.d.ts +0 -2
- package/dist/_internal/shared/features/checkout/config.js +0 -2
- package/dist/_internal/shared/features/messaging/config.d.ts +7 -5
- package/dist/_internal/shared/features/messaging/config.js +1 -4
- package/dist/_internal/shared/features/site-settings/fees.d.ts +0 -2
- package/dist/_internal/shared/features/site-settings/fees.js +0 -2
- package/dist/_internal/shared/features/site-settings/supplier-of-record.d.ts +56 -0
- package/dist/_internal/shared/features/site-settings/supplier-of-record.js +57 -0
- package/dist/_internal/shared/fees/calculator.d.ts +0 -8
- package/dist/_internal/shared/fees/calculator.js +0 -7
- package/dist/_internal/shared/listing-types/tiers.d.ts +94 -0
- package/dist/_internal/shared/listing-types/tiers.js +150 -0
- package/dist/_internal/shared/search/match.js +28 -2
- package/dist/cli/index.js +0 -2
- package/dist/client.d.ts +25 -4
- package/dist/client.js +36 -5
- package/dist/constants/api-endpoints.d.ts +28 -15
- package/dist/constants/api-endpoints.js +14 -8
- package/dist/constants/field-names.d.ts +1 -0
- package/dist/constants/field-names.js +1 -0
- package/dist/constants/notification-types.d.ts +1 -1
- package/dist/constants/notification-types.js +1 -1
- package/dist/core/integration-keys.d.ts +5 -5
- package/dist/core/integration-keys.js +21 -10
- package/dist/features/about/components/FeesView.js +0 -6
- package/dist/features/about/components/GuidesHubView.js +15 -2
- package/dist/features/account/components/NotificationPreferencesPanel.js +3 -4
- package/dist/features/account/components/UserOrdersView.js +26 -7
- package/dist/features/account/types/index.d.ts +0 -2
- package/dist/features/addresses/utils/postal-autofill.d.ts +39 -0
- package/dist/features/addresses/utils/postal-autofill.js +54 -0
- package/dist/features/admin/actions/notification-actions.d.ts +2 -8
- package/dist/features/admin/actions/notification-actions.js +9 -104
- package/dist/features/admin/components/AdminGuideHubView.js +1 -8
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +1 -1
- package/dist/features/admin/components/AdminOrdersView.js +0 -2
- package/dist/features/admin/components/AdminSectionsView.js +3 -2
- package/dist/features/admin/components/AdminSiteSettingsView.js +22 -68
- package/dist/features/admin/components/AdminStoreEditorView.js +72 -34
- package/dist/features/admin/components/OwnerQuickActions.d.ts +29 -0
- package/dist/features/admin/components/OwnerQuickActions.js +142 -0
- package/dist/features/admin/components/sections/adminSectionsTypes.js +13 -4
- package/dist/features/admin/components/sections/icon-options.d.ts +21 -0
- package/dist/features/admin/components/sections/icon-options.js +23 -0
- package/dist/features/admin/components/sections/sectionTemplates.js +4 -1
- package/dist/features/admin/constants/site-settings-tabs.d.ts +7 -12
- package/dist/features/admin/constants/site-settings-tabs.js +6 -12
- package/dist/features/admin/repository/site-settings.repository.js +1 -1
- package/dist/features/admin/schemas/admin-editor-forms.d.ts +1 -1
- package/dist/features/admin/schemas/admin-editor-forms.js +4 -1
- package/dist/features/admin/schemas/admin-user-form.d.ts +0 -61
- package/dist/features/admin/schemas/admin-user-form.js +3 -44
- package/dist/features/admin/schemas/firestore.d.ts +28 -54
- package/dist/features/admin/schemas/firestore.js +40 -18
- package/dist/features/auctions/components/AuctionDetailPageView.js +12 -10
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +9 -2
- package/dist/features/auth/checkout-value-otp.d.ts +1 -1
- package/dist/features/auth/checkout-value-otp.js +1 -1
- package/dist/features/auth/permissions/constants.d.ts +29 -2
- package/dist/features/auth/permissions/constants.js +67 -1
- package/dist/features/blog/components/BlogFeaturedCard.d.ts +11 -1
- package/dist/features/blog/components/BlogFeaturedCard.js +9 -2
- package/dist/features/cart/components/CartDrawer.js +3 -2
- package/dist/features/cart/components/CartGroupLineRow.js +2 -1
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +0 -2
- package/dist/features/cart/components/CartPriceBreakdown.js +1 -1
- package/dist/features/cart/components/StoreAddonsPicker.d.ts +2 -5
- package/dist/features/cart/components/StoreAddonsPicker.js +4 -5
- package/dist/features/cart/hooks/useAddToCart.js +10 -5
- package/dist/features/cart/repository/cart.repository.js +1 -2
- package/dist/features/cart/schemas/firestore.d.ts +1 -2
- package/dist/features/cart/utils/pending-ops.d.ts +11 -1
- package/dist/features/cart/utils/pending-ops.js +14 -2
- package/dist/features/categories/api/[id]/price-guidance/route.js +1 -24
- package/dist/features/categories/components/BrandDetailPageView.js +15 -6
- package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -1
- package/dist/features/categories/components/BrandDetailTabs.js +18 -3
- package/dist/features/categories/components/BundleCollage.js +3 -2
- package/dist/features/categories/components/BundleDetailView.js +2 -1
- package/dist/features/categories/components/BundleItemsPicker.js +3 -4
- package/dist/features/categories/components/CategoryDetailPageView.js +19 -8
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +7 -1
- package/dist/features/categories/components/CategoryDetailTabs.js +29 -7
- package/dist/features/categories/components/CategoryGrid.d.ts +11 -1
- package/dist/features/categories/components/CategoryGrid.js +9 -2
- package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +10 -5
- package/dist/features/checkout/actions/checkout-value-otp-actions.js +2 -88
- package/dist/features/classified/components/ClassifiedDetailPageView.js +12 -2
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +12 -2
- package/dist/features/events/components/EventCard.d.ts +11 -1
- package/dist/features/events/components/EventCard.js +9 -2
- package/dist/features/grouped/components/GroupedListingDetailView.js +3 -1
- package/dist/features/homepage/components/CTABannerSection.d.ts +3 -1
- package/dist/features/homepage/components/CTABannerSection.js +6 -3
- package/dist/features/homepage/components/HeroCarousel.js +8 -6
- package/dist/features/homepage/components/MarketplaceHomepageView.js +33 -7
- package/dist/features/homepage/components/SectionCarousel.js +13 -5
- package/dist/features/homepage/components/SecurityHighlightsSection.d.ts +8 -3
- package/dist/features/homepage/components/SecurityHighlightsSection.js +2 -2
- package/dist/features/homepage/components/StatsCounterSection.d.ts +9 -4
- package/dist/features/homepage/components/StatsCounterSection.js +2 -2
- package/dist/features/homepage/components/TestimonialsCarousel.js +1 -1
- package/dist/features/homepage/components/TrustFeaturesSection.d.ts +18 -4
- package/dist/features/homepage/components/TrustFeaturesSection.js +5 -5
- package/dist/features/homepage/components/WelcomeSection.d.ts +13 -2
- package/dist/features/homepage/components/WelcomeSection.js +3 -3
- package/dist/features/homepage/components/WhatsAppCommunitySection.d.ts +28 -0
- package/dist/features/homepage/components/WhatsAppCommunitySection.js +42 -6
- package/dist/features/homepage/components/WhatsAppGlyph.d.ts +20 -0
- package/dist/features/homepage/components/WhatsAppGlyph.js +20 -0
- package/dist/features/homepage/constants/hero-carousel-styles.d.ts +8 -6
- package/dist/features/homepage/constants/hero-carousel-styles.js +8 -6
- package/dist/features/homepage/constants/section-copy.d.ts +12 -10
- package/dist/features/homepage/constants/section-copy.js +28 -6
- package/dist/features/homepage/constants/stat-icons.d.ts +12 -0
- package/dist/features/homepage/constants/stat-icons.js +17 -0
- package/dist/features/homepage/constants/trust-icon-aliases.d.ts +8 -0
- package/dist/features/homepage/constants/trust-icon-aliases.js +75 -0
- package/dist/features/homepage/lib/section-defaults.d.ts +16 -1
- package/dist/features/homepage/lib/section-defaults.js +24 -4
- package/dist/features/homepage/lib/section-renderer.js +48 -8
- package/dist/features/homepage/schemas/firestore.d.ts +18 -1
- package/dist/features/layout/AppLayoutShell.js +2 -1
- package/dist/features/layout/BottomActions.d.ts +1 -0
- package/dist/features/layout/BottomActions.js +330 -0
- package/dist/features/layout/BottomChrome.d.ts +45 -0
- package/dist/features/layout/BottomChrome.js +5 -17
- package/dist/features/layout/SideActionDock.js +36 -13
- package/dist/features/layout/index.d.ts +2 -7
- package/dist/features/layout/index.js +7 -7
- package/dist/features/layout/useIsDesktop.d.ts +36 -0
- package/dist/features/layout/useIsDesktop.js +48 -0
- package/dist/features/live/components/LiveItemDetailPageView.js +12 -2
- package/dist/features/orders/actions/refund-actions.js +0 -1
- package/dist/features/orders/components/OrderAddonBadges.d.ts +1 -3
- package/dist/features/orders/components/OrderAddonBadges.js +3 -4
- package/dist/features/orders/components/OrderPaymentSummary.js +15 -2
- package/dist/features/orders/components/YourPurchaseStrip.d.ts +24 -0
- package/dist/features/orders/components/YourPurchaseStrip.js +77 -0
- package/dist/features/orders/components/index.d.ts +2 -1
- package/dist/features/orders/components/index.js +1 -1
- package/dist/features/orders/repository/orders.repository.d.ts +0 -8
- package/dist/features/orders/repository/orders.repository.js +0 -2
- package/dist/features/orders/schemas/firestore.d.ts +17 -9
- package/dist/features/orders/types/index.d.ts +2 -5
- package/dist/features/orders/utils/order-announcement-message.d.ts +18 -0
- package/dist/features/orders/utils/order-announcement-message.js +16 -0
- package/dist/features/orders/utils/payment-proof-message.d.ts +20 -0
- package/dist/features/orders/utils/payment-proof-message.js +17 -0
- package/dist/features/parts/components/PartDetailPageView.js +96 -1
- package/dist/features/parts/schemas/firestore.d.ts +247 -0
- package/dist/features/parts/schemas/firestore.js +6 -6
- package/dist/features/pre-orders/components/MarketplacePreorderCard.d.ts +11 -1
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +10 -3
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +12 -10
- package/dist/features/price-index/components/PriceIndexToolView.d.ts +21 -0
- package/dist/features/price-index/components/PriceIndexToolView.js +230 -27
- package/dist/features/price-index/lookup-target.d.ts +48 -0
- package/dist/features/price-index/lookup-target.js +107 -0
- package/dist/features/products/api/[id]/route.js +8 -1
- package/dist/features/products/api/route.js +16 -4
- package/dist/features/products/components/AvailabilityScopeHint.d.ts +22 -0
- package/dist/features/products/components/AvailabilityScopeHint.js +27 -0
- package/dist/features/products/components/CompareOverlay.d.ts +2 -1
- package/dist/features/products/components/CompareOverlay.js +2 -1
- package/dist/features/products/components/GroupSettingsPanel.js +2 -1
- package/dist/features/products/components/InteractiveProductCard.d.ts +7 -1
- package/dist/features/products/components/InteractiveProductCard.js +16 -4
- package/dist/features/products/components/MarketplaceBundleCard.d.ts +11 -1
- package/dist/features/products/components/MarketplaceBundleCard.js +9 -2
- package/dist/features/products/components/MarketplacePrizeDrawCard.d.ts +11 -1
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +9 -2
- package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -1
- package/dist/features/products/components/ProductDetailPageView.js +34 -4
- package/dist/features/products/components/ProductGrid.d.ts +14 -1
- package/dist/features/products/components/ProductGrid.js +22 -5
- package/dist/features/products/components/ProductsIndexListing.js +2 -2
- package/dist/features/products/components/SublistingCarouselSection.js +2 -1
- package/dist/features/products/repository/products.repository.d.ts +1 -0
- package/dist/features/products/repository/products.repository.js +5 -1
- package/dist/features/products/utils/group-member-image.d.ts +9 -2
- package/dist/features/products/utils/group-member-image.js +2 -1
- package/dist/features/reviews/api/[id]/route.js +83 -23
- package/dist/features/reviews/components/PurchaseReviewAffordance.d.ts +6 -0
- package/dist/features/reviews/components/PurchaseReviewAffordance.js +42 -0
- package/dist/features/reviews/components/ReviewFormDrawer.d.ts +34 -0
- package/dist/features/reviews/components/ReviewFormDrawer.js +127 -0
- package/dist/features/reviews/components/index.d.ts +4 -0
- package/dist/features/reviews/components/index.js +2 -0
- package/dist/features/scams/schemas/index.d.ts +10 -10
- package/dist/features/search/action-index/settings-entries.js +0 -9
- package/dist/features/seller/actions/seller-actions.d.ts +1 -1
- package/dist/features/seller/actions/seller-actions.js +49 -3
- package/dist/features/seller/components/SellerPayoutSettingsView.js +30 -2
- package/dist/features/seller/components/SellerProductShell.d.ts +17 -1
- package/dist/features/seller/components/SellerProductsView.js +3 -2
- package/dist/features/seller/hooks/useStoreListingEntitlements.d.ts +8 -0
- package/dist/features/seller/hooks/useStoreListingEntitlements.js +70 -0
- package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +1 -6
- package/dist/features/store-extensions/repository/store-extensions.repositories.js +1 -10
- package/dist/features/store-extensions/schemas/firestore.d.ts +1 -17
- package/dist/features/store-extensions/schemas/firestore.js +0 -10
- package/dist/features/stores/components/InteractiveStoreCard.d.ts +11 -1
- package/dist/features/stores/components/InteractiveStoreCard.js +9 -2
- package/dist/features/stores/components/StoreAuctionsListing.js +2 -1
- package/dist/features/stores/components/StoreCapabilitiesGuideView.js +0 -1
- package/dist/features/stores/components/StoreGuideHubView.js +2 -8
- package/dist/features/stores/components/StoreSettingsGuideView.js +2 -6
- package/dist/features/stores/components/index.d.ts +0 -2
- package/dist/features/stores/components/index.js +0 -1
- package/dist/features/stores/repository/store.repository.d.ts +1 -26
- package/dist/features/stores/repository/store.repository.js +5 -60
- package/dist/features/stores/schemas/firestore.d.ts +6 -25
- package/dist/features/stores/schemas/firestore.js +6 -3
- package/dist/features/stores/schemas/index.d.ts +1 -87
- package/dist/features/stores/schemas/index.js +10 -15
- package/dist/features/tester/seed-data/authored/admin__site-system.js +0 -48
- package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +0 -40
- package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.js +114 -2
- package/dist/features/tester/seed-data/authored/selling__quick-add-minimum-details.js +0 -16
- package/dist/features/tester/seed-data/authored/selling__seller-guide.js +1 -1
- package/dist/features/tester/seed-data/authored/selling__seller-marketing-extras.js +0 -79
- package/dist/features/tester/seed-data/authored/selling__seller-orders.js +1 -1
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +427 -69
- package/dist/index.d.ts +20 -32
- package/dist/index.js +34 -43
- package/dist/next/routing/route-map.d.ts +2 -8
- package/dist/next/routing/route-map.js +21 -4
- package/dist/providers/db-firebase/base.repository.d.ts +1 -1
- package/dist/providers/db-firebase/base.repository.js +1 -1
- package/dist/providers/payment-razorpay/index.d.ts +125 -0
- package/dist/providers/payment-razorpay/index.js +232 -0
- package/dist/providers/payment-razorpay/signature.d.ts +53 -0
- package/dist/providers/payment-razorpay/signature.js +71 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +98 -0
- package/dist/providers/shipping-shiprocket/index.js +356 -0
- package/dist/providers.d.ts +3 -0
- package/dist/providers.js +3 -0
- package/dist/react/hooks/useCanSeePrices.d.ts +0 -27
- package/dist/react/hooks/useCanSeePrices.js +25 -0
- package/dist/repositories/index.d.ts +1 -1
- package/dist/repositories/index.js +1 -1
- package/dist/schemas/registry.d.ts +10 -115
- package/dist/schemas/registry.js +1 -2
- package/dist/security/pii-encrypt.js +1 -1
- package/dist/seed/_helpers/category-forest.js +27 -0
- package/dist/seed/_helpers/category-parts-glossary-map.js +1816 -86
- package/dist/seed/_helpers/category-parts.d.ts +1 -1
- package/dist/seed/_helpers/category-parts.js +13412 -3020
- package/dist/seed/carousel-slides-seed-data.js +47 -10
- package/dist/seed/categories-seed-data.js +48 -6
- package/dist/seed/faq-seed-data.js +2 -2
- package/dist/seed/homepage-sections-seed-data.js +286 -54
- package/dist/seed/index.d.ts +1 -1
- package/dist/seed/index.js +1 -1
- package/dist/seed/manifest.d.ts +1 -1
- package/dist/seed/manifest.js +1 -2
- package/dist/seed/model-composition-seed-data.js +6 -8
- package/dist/seed/part-glossary/chunk0.d.ts +9 -0
- package/dist/seed/part-glossary/chunk0.js +35448 -0
- package/dist/seed/part-glossary/chunk1.d.ts +9 -0
- package/dist/seed/part-glossary/chunk1.js +40494 -0
- package/dist/seed/part-glossary/chunk2.d.ts +9 -0
- package/dist/seed/part-glossary/chunk2.js +51242 -0
- package/dist/seed/part-glossary/chunk3.d.ts +9 -0
- package/dist/seed/part-glossary/chunk3.js +53337 -0
- package/dist/seed/part-glossary/chunk4.d.ts +9 -0
- package/dist/seed/part-glossary/chunk4.js +51454 -0
- package/dist/seed/part-glossary-seed-data.d.ts +1 -1
- package/dist/seed/part-glossary-seed-data.js +23 -47483
- package/dist/seed/products-beyblade-x-seed-data.js +36 -1
- package/dist/seed/site-settings-seed-data.js +34 -35
- package/dist/seed/store-extensions-seed-data.d.ts +1 -2
- package/dist/seed/store-extensions-seed-data.js +1 -23
- package/dist/seed/stores-seed-data.js +0 -1
- package/dist/server-entry.d.ts +1 -1
- package/dist/server-entry.js +1 -1
- package/dist/server.d.ts +10 -10
- package/dist/server.js +24 -22
- package/dist/styles.css +392 -43
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +1 -1
- package/dist/tokens/themes/default-light.js +2 -1
- package/dist/tokens/tokens.css +6 -2
- package/dist/ui/components/Button.d.ts +11 -0
- package/dist/ui/components/Button.js +11 -0
- package/dist/ui/components/Button.style.css +29 -0
- package/dist/ui/components/CarouselNav.d.ts +57 -0
- package/dist/ui/components/CarouselNav.js +25 -0
- package/dist/ui/components/CarouselNav.style.css +265 -0
- package/dist/ui/components/GatedPrice.js +30 -3
- package/dist/ui/components/GatedPrice.style.css +43 -40
- package/dist/ui/components/HorizontalScroller.d.ts +12 -1
- package/dist/ui/components/HorizontalScroller.js +43 -3
- package/dist/ui/components/ImageMosaic.d.ts +8 -1
- package/dist/ui/components/ImageMosaic.js +3 -2
- package/dist/ui/components/ImageMosaic.style.css +22 -0
- package/dist/ui/components/Layout.style.css +6 -0
- package/dist/ui/components/Semantic.d.ts +3 -2
- package/dist/ui/components/Semantic.js +1 -0
- package/dist/ui/components/Typography.d.ts +2 -0
- package/dist/ui/components/Typography.js +2 -0
- package/dist/ui/components/Typography.style.css +19 -0
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/index.d.ts +3 -1
- package/dist/ui/index.js +4 -0
- package/package.json +3 -2
- package/scripts/seed-cli.mjs +133 -27
- package/dist/_internal/server/jobs/core/whatsappCatalogImport.d.ts +0 -27
- package/dist/_internal/server/jobs/core/whatsappCatalogImport.js +0 -172
- package/dist/_internal/server/jobs/core/whatsappNotify.d.ts +0 -35
- package/dist/_internal/server/jobs/core/whatsappNotify.js +0 -140
- package/dist/features/admin/components/AdminWhatsAppGuideView.d.ts +0 -2
- package/dist/features/admin/components/AdminWhatsAppGuideView.js +0 -35
- package/dist/features/orders/components/MarketplaceOrderCard.d.ts +0 -36
- package/dist/features/orders/components/MarketplaceOrderCard.js +0 -57
- package/dist/features/stores/components/StoreWhatsAppGuideView.d.ts +0 -3
- package/dist/features/stores/components/StoreWhatsAppGuideView.js +0 -12
- package/dist/features/whatsapp-bot/api/send-status/route.d.ts +0 -12
- package/dist/features/whatsapp-bot/api/send-status/route.js +0 -38
- package/dist/features/whatsapp-bot/api/webhook/route.d.ts +0 -12
- package/dist/features/whatsapp-bot/api/webhook/route.js +0 -43
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.d.ts +0 -6
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +0 -270
- package/dist/features/whatsapp-bot/components/WhatsAppChatButton.d.ts +0 -9
- package/dist/features/whatsapp-bot/components/WhatsAppChatButton.js +0 -7
- package/dist/features/whatsapp-bot/components/index.d.ts +0 -3
- package/dist/features/whatsapp-bot/components/index.js +0 -2
- package/dist/features/whatsapp-bot/helpers/payment-proof-message.d.ts +0 -14
- package/dist/features/whatsapp-bot/helpers/payment-proof-message.js +0 -17
- package/dist/features/whatsapp-bot/helpers/whatsapp.d.ts +0 -52
- package/dist/features/whatsapp-bot/helpers/whatsapp.js +0 -283
- package/dist/features/whatsapp-bot/index.d.ts +0 -3
- package/dist/features/whatsapp-bot/index.js +0 -3
- package/dist/features/whatsapp-bot/manifest.d.ts +0 -2
- package/dist/features/whatsapp-bot/manifest.js +0 -13
- package/dist/features/whatsapp-bot/messages/en.json +0 -7
- package/dist/features/whatsapp-bot/server.d.ts +0 -7
- package/dist/features/whatsapp-bot/server.js +0 -7
- package/dist/features/whatsapp-bot/types/index.d.ts +0 -126
- package/dist/features/whatsapp-bot/types/index.js +0 -1
|
@@ -88,6 +88,23 @@ const payoutSettingsDraftSchema = z.object({
|
|
|
88
88
|
*/
|
|
89
89
|
supportsShippingProtection: annotate(z.boolean(), { section: "preferences", order: 5, row: "quarter" }),
|
|
90
90
|
supportsSaleProtection: annotate(z.boolean(), { section: "preferences", order: 6, row: "quarter" }),
|
|
91
|
+
/*
|
|
92
|
+
* Advanced listing types — pre-orders, prize draws, classifieds, digital
|
|
93
|
+
* codes, live items, art and stickers. Everyone can already list
|
|
94
|
+
* `standard`, `spares` and `auction`.
|
|
95
|
+
*
|
|
96
|
+
* 🛑 Alongside payout settings for the same reason the protections are:
|
|
97
|
+
* each of these carries an obligation, not just a form. A pre-order takes
|
|
98
|
+
* money for stock that does not exist yet, a prize draw is a regulated
|
|
99
|
+
* promotion, a classified happens off-platform with no escrow, a
|
|
100
|
+
* digital-code listing must be delivered from a pool the seller has to keep
|
|
101
|
+
* stocked, and a live listing is an animal. A new seller should meet those
|
|
102
|
+
* deliberately rather than by discovering a nav item.
|
|
103
|
+
*
|
|
104
|
+
* OFF by default, and the copy has to say what enabling ACCEPTS — the same
|
|
105
|
+
* reason the protection toggles say what declining costs.
|
|
106
|
+
*/
|
|
107
|
+
advancedListingTypesEnabled: annotate(z.boolean(), { section: "preferences", order: 7, row: "quarter" }),
|
|
91
108
|
}).superRefine((v, ctx) => {
|
|
92
109
|
if (v.method === "upi") {
|
|
93
110
|
if (!v.upiId.trim()) {
|
|
@@ -128,6 +145,13 @@ const DEFAULT_DRAFT = {
|
|
|
128
145
|
// OFF by default — matches StoreDocument, and absence must read as declined.
|
|
129
146
|
supportsShippingProtection: false,
|
|
130
147
|
supportsSaleProtection: false,
|
|
148
|
+
/*
|
|
149
|
+
* OFF by default, which is also the direction the server gate fails. A
|
|
150
|
+
* default of `true` here would be worse than cosmetic: this draft is what
|
|
151
|
+
* gets PATCHed, so it would silently GRANT the capability on the next
|
|
152
|
+
* unrelated save of this form.
|
|
153
|
+
*/
|
|
154
|
+
advancedListingTypesEnabled: false,
|
|
131
155
|
};
|
|
132
156
|
export function SellerPayoutSettingsView({ apiBase = SELLER_ENDPOINTS.PAYOUT_SETTINGS }) {
|
|
133
157
|
const [draft, setDraft] = useState(DEFAULT_DRAFT);
|
|
@@ -175,6 +199,9 @@ export function SellerPayoutSettingsView({ apiBase = SELLER_ENDPOINTS.PAYOUT_SET
|
|
|
175
199
|
pricesIncludeGst: res?.data?.pricesIncludeGst ?? true,
|
|
176
200
|
supportsShippingProtection: res?.data?.supportsShippingProtection ?? false,
|
|
177
201
|
supportsSaleProtection: res?.data?.supportsSaleProtection ?? false,
|
|
202
|
+
// Absent means NOT entitled. Defaulting this to `true` would be a
|
|
203
|
+
// silent GRANT on the next save, since this draft is what gets sent.
|
|
204
|
+
advancedListingTypesEnabled: res?.data?.advancedListingTypesEnabled ?? false,
|
|
178
205
|
});
|
|
179
206
|
})
|
|
180
207
|
.catch(console.error)
|
|
@@ -205,6 +232,7 @@ export function SellerPayoutSettingsView({ apiBase = SELLER_ENDPOINTS.PAYOUT_SET
|
|
|
205
232
|
pricesIncludeGst: draft.pricesIncludeGst,
|
|
206
233
|
supportsShippingProtection: draft.supportsShippingProtection,
|
|
207
234
|
supportsSaleProtection: draft.supportsSaleProtection,
|
|
235
|
+
advancedListingTypesEnabled: draft.advancedListingTypesEnabled,
|
|
208
236
|
// Flat, matching the route's schema. A nested `taxInfo` object was
|
|
209
237
|
// stripped by `z.object()` and never reached Firestore.
|
|
210
238
|
gstin: draft.gstin.trim(),
|
|
@@ -271,8 +299,8 @@ export function SellerPayoutSettingsView({ apiBase = SELLER_ENDPOINTS.PAYOUT_SET
|
|
|
271
299
|
{
|
|
272
300
|
id: "preferences",
|
|
273
301
|
label: "Preferences",
|
|
274
|
-
fields: ["autoPayout", "minimumThreshold", "emiEnabled", "pricesIncludeGst", "supportsShippingProtection", "supportsSaleProtection"],
|
|
275
|
-
render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Payout Preferences" }), _jsx(Toggle, { checked: values.autoPayout, onChange: (checked) => onChange({ autoPayout: checked }), label: "Enable auto-payout \u2014 automatically transfer earnings on schedule", disabled: busy }), _jsx(FormField, { name: "minimumThreshold", label: "Minimum Payout Threshold (\u20B9)", type: "number", value: values.minimumThreshold, onChange: (v) => onChange({ minimumThreshold: v }), placeholder: "e.g. 500", helpText: "Payouts are held until your balance exceeds this amount. Leave blank for no minimum.", disabled: busy }), _jsxs(Div, { className: `${__P.p3} bg-[var(--appkit-color-surface-alt)] border border-[var(--appkit-color-border)]`, rounded: "lg", children: [_jsx(Text, { className: "mb-1", size: "sm", weight: "medium", children: "Payout Schedule" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Auto-payouts run every Monday for the previous week's settled orders. Manual payouts can be requested from the Payouts page at any time." })] }), _jsx(Toggle, { checked: values.emiEnabled, onChange: (checked) => onChange({ emiEnabled: checked }), label: "Offer EMI financing on eligible orders", disabled: busy }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Only shown to buyers when platform-wide EMI is also enabled and their cart subtotal from your store exceeds the site's minimum order value. Orders on an EMI plan hold your payout until every installment is paid, unless you mark the item \"ship before EMI completes\"." }), _jsx(Toggle, { checked: values.pricesIncludeGst, onChange: (checked) => onChange({ pricesIncludeGst: checked }), label: "The prices I enter already include GST", disabled: busy }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: ["Buyers always see a tax-inclusive price, so this only changes how your own entry is read. Leave it", " ", _jsx(Span, { weight: "semibold", children: "on" }), " and a \u20B9999 listing at 5% GST means the buyer pays \u20B9999 and you earn \u20B9951.43 \u2014 the \u20B947.57 is GST we collect and remit for you. Turn it", " ", _jsx(Span, { weight: "semibold", children: "off" }), " and \u20B9999 is treated as your ex-GST price: the listing is shown at \u20B91,048.95 and you earn \u20B9999."] }), _jsx(Heading, { level: 3, className: "mb-2 mt-4", children: "Buyer Protections" }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: ["Two separate covers a buyer can pay for at checkout. Supporting one earns a safety badge on your listings;", " ", _jsx(Span, { weight: "semibold", children: "declining shows buyers a risk notice instead" }), " \u2014 there is no neutral option, because a buyer who is told nothing cannot tell a seller who declined from one who was never asked."] }), _jsx(Toggle, { checked: values.supportsShippingProtection, onChange: (checked) => onChange({ supportsShippingProtection: checked }), label: "Offer shipment protection \u2014 loss or damage in transit", disabled: busy }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "The parcel never arrived or arrived damaged. We pursue the courier, not you, and the buyer is refunded from the funds we hold \u2014 so a genuine transit loss costs you the item, not an argument." }), _jsx(Toggle, { checked: values.supportsSaleProtection, onChange: (checked) => onChange({ supportsSaleProtection: checked }), label: "Offer sale protection \u2014 item not as described", disabled: busy }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: ["A no-questions-asked refund on the item itself. Be clear about what this means:", " ", _jsx(Span, { weight: "semibold", children: "a claim we inspect and find genuine is charged back to you" }), " ", "\u2014 that is the cover. A claim we find to be a change of mind is absorbed by us, which is what the buyer's fee pays for. Not offered on your final-sale listings, since \"no returns\" and \"no questions asked\" cannot both be true."] }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Changing this does not re-price listings you have already published. It applies to products you save from now on." })] })),
|
|
302
|
+
fields: ["autoPayout", "minimumThreshold", "emiEnabled", "pricesIncludeGst", "supportsShippingProtection", "supportsSaleProtection", "advancedListingTypesEnabled"],
|
|
303
|
+
render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Payout Preferences" }), _jsx(Toggle, { checked: values.autoPayout, onChange: (checked) => onChange({ autoPayout: checked }), label: "Enable auto-payout \u2014 automatically transfer earnings on schedule", disabled: busy }), _jsx(FormField, { name: "minimumThreshold", label: "Minimum Payout Threshold (\u20B9)", type: "number", value: values.minimumThreshold, onChange: (v) => onChange({ minimumThreshold: v }), placeholder: "e.g. 500", helpText: "Payouts are held until your balance exceeds this amount. Leave blank for no minimum.", disabled: busy }), _jsxs(Div, { className: `${__P.p3} bg-[var(--appkit-color-surface-alt)] border border-[var(--appkit-color-border)]`, rounded: "lg", children: [_jsx(Text, { className: "mb-1", size: "sm", weight: "medium", children: "Payout Schedule" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Auto-payouts run every Monday for the previous week's settled orders. Manual payouts can be requested from the Payouts page at any time." })] }), _jsx(Toggle, { checked: values.emiEnabled, onChange: (checked) => onChange({ emiEnabled: checked }), label: "Offer EMI financing on eligible orders", disabled: busy }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Only shown to buyers when platform-wide EMI is also enabled and their cart subtotal from your store exceeds the site's minimum order value. Orders on an EMI plan hold your payout until every installment is paid, unless you mark the item \"ship before EMI completes\"." }), _jsx(Toggle, { checked: values.pricesIncludeGst, onChange: (checked) => onChange({ pricesIncludeGst: checked }), label: "The prices I enter already include GST", disabled: busy }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: ["Buyers always see a tax-inclusive price, so this only changes how your own entry is read. Leave it", " ", _jsx(Span, { weight: "semibold", children: "on" }), " and a \u20B9999 listing at 5% GST means the buyer pays \u20B9999 and you earn \u20B9951.43 \u2014 the \u20B947.57 is GST we collect and remit for you. Turn it", " ", _jsx(Span, { weight: "semibold", children: "off" }), " and \u20B9999 is treated as your ex-GST price: the listing is shown at \u20B91,048.95 and you earn \u20B9999."] }), _jsx(Heading, { level: 3, className: "mb-2 mt-4", children: "Buyer Protections" }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: ["Two separate covers a buyer can pay for at checkout. Supporting one earns a safety badge on your listings;", " ", _jsx(Span, { weight: "semibold", children: "declining shows buyers a risk notice instead" }), " \u2014 there is no neutral option, because a buyer who is told nothing cannot tell a seller who declined from one who was never asked."] }), _jsx(Toggle, { checked: values.supportsShippingProtection, onChange: (checked) => onChange({ supportsShippingProtection: checked }), label: "Offer shipment protection \u2014 loss or damage in transit", disabled: busy }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "The parcel never arrived or arrived damaged. We pursue the courier, not you, and the buyer is refunded from the funds we hold \u2014 so a genuine transit loss costs you the item, not an argument." }), _jsx(Toggle, { checked: values.supportsSaleProtection, onChange: (checked) => onChange({ supportsSaleProtection: checked }), label: "Offer sale protection \u2014 item not as described", disabled: busy }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: ["A no-questions-asked refund on the item itself. Be clear about what this means:", " ", _jsx(Span, { weight: "semibold", children: "a claim we inspect and find genuine is charged back to you" }), " ", "\u2014 that is the cover. A claim we find to be a change of mind is absorbed by us, which is what the buyer's fee pays for. Not offered on your final-sale listings, since \"no returns\" and \"no questions asked\" cannot both be true."] }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Changing this does not re-price listings you have already published. It applies to products you save from now on." }), _jsx(Toggle, { checked: values.advancedListingTypesEnabled, onChange: (checked) => onChange({ advancedListingTypesEnabled: checked }), label: "Enable advanced listing types", disabled: busy }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: ["Adds pre-orders, prize draws, classifieds, digital codes, live items, art and stickers alongside the products, spares and auctions you can already list. Each carries its own obligation:", " ", _jsx(Span, { weight: "semibold", children: "a pre-order takes payment for stock you do not have yet, a prize draw is a regulated promotion, a classified happens off-platform with no escrow protection, a digital code must be delivered from a pool you keep stocked, and a live listing is an animal." }), " ", "Turning this off later does not unpublish listings you have already created \u2014 it only stops you making new ones."] })] })),
|
|
276
304
|
},
|
|
277
305
|
], [busy, current]);
|
|
278
306
|
const { openIds, setOpenIds, goToSection, fieldToSectionIndex, sectionMeta } = useSectionFormNav(sections, draft);
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { MediaField } from "../../media/types";
|
|
3
|
-
|
|
3
|
+
import type { ListingType } from "../../products/types/index";
|
|
4
|
+
/**
|
|
5
|
+
* What this shell is editing: any listing type, or a bundle.
|
|
6
|
+
*
|
|
7
|
+
* 🛑 DERIVED from `ListingType`, not restated. This was a hand-written copy of
|
|
8
|
+
* the union plus `"bundle"` — the eleventh such copy in the codebase — and the
|
|
9
|
+
* whole point of `ALL_LISTING_TYPES_MAP` being a `Record<ListingType, true>`
|
|
10
|
+
* is that a new member cannot be silently omitted from a list like this one.
|
|
11
|
+
* Root Cause #61.
|
|
12
|
+
*
|
|
13
|
+
* `"bundle"` is a deliberate EXTRA, not an oversight: bundles stopped being a
|
|
14
|
+
* `listingType` in SB-UNI-D (they are a `categoryType` on `categories` now),
|
|
15
|
+
* but this shell still edits them, which is what `pluginForMode`'s
|
|
16
|
+
* `BUNDLE_PLUGIN_FALLBACK` is for. Spelling it as a union with one added
|
|
17
|
+
* literal says that, where a twelve-item list said nothing.
|
|
18
|
+
*/
|
|
19
|
+
export type ProductListingMode = ListingType | "bundle";
|
|
4
20
|
export interface SellerProductDraft {
|
|
5
21
|
title?: string;
|
|
6
22
|
slug?: string;
|
|
@@ -34,6 +34,7 @@ import { DataListingView } from "../../admin/components/DataListingView.js";
|
|
|
34
34
|
import { AssemblyDrawer, } from "../../parts/components/AssemblyDrawer.js";
|
|
35
35
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry.js";
|
|
36
36
|
import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers.js";
|
|
37
|
+
import { productImageUrls } from "../../products/schemas/firestore.js";
|
|
37
38
|
const FILTER_KEYS = [
|
|
38
39
|
"status",
|
|
39
40
|
"category",
|
|
@@ -579,8 +580,8 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, }) {
|
|
|
579
580
|
secondary: [priceLabel, stockLabel, secondaryBase, statusLabel].filter(Boolean).join(" · "),
|
|
580
581
|
status: statusLabel,
|
|
581
582
|
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
582
|
-
imageUrl: toStringValue(item.mainImage
|
|
583
|
-
image: toStringValue(item.mainImage
|
|
583
|
+
imageUrl: toStringValue(item.mainImage, undefined) || productImageUrls(item.images)[0],
|
|
584
|
+
image: toStringValue(item.mainImage, undefined) || productImageUrls(item.images)[0],
|
|
584
585
|
listingKind: kind,
|
|
585
586
|
price: priceRaw ? `₹${priceRaw.toLocaleString("en-IN")}` : "—",
|
|
586
587
|
stock: stockLabel || "—",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ListingType } from "../../products/types/index";
|
|
2
|
+
export interface StoreListingEntitlements {
|
|
3
|
+
/** Types this store may create. Never empty — basics are always included. */
|
|
4
|
+
allowed: Set<ListingType>;
|
|
5
|
+
advancedEnabled: boolean;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function useStoreListingEntitlements(): StoreListingEntitlements;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* Which listing types the signed-in seller's own store may create.
|
|
4
|
+
*
|
|
5
|
+
* ── Why this needs a fetch at all ─────────────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* 🛑 `StoreDocument.capabilities` is listed in `PRIVATE_STORE_FIELDS` —
|
|
8
|
+
* "internal feature gating; server-enforced, never client-read" — so it is
|
|
9
|
+
* absent from every public store payload and from the session user. A client
|
|
10
|
+
* component genuinely cannot derive this locally, and the alternative a
|
|
11
|
+
* previous draft reached for (publish the capability array to the browser)
|
|
12
|
+
* would ship `lower_commission_rate`, `priority_support` and fifteen more
|
|
13
|
+
* admin-granted entitlements to anyone who opened devtools. Root Cause #70.
|
|
14
|
+
*
|
|
15
|
+
* So the server sends a PROJECTION: `allowedListingTypes`, already resolved by
|
|
16
|
+
* `listingTypesAllowedFor` on the same capability array the write-path gate
|
|
17
|
+
* uses. Client and server therefore agree by construction rather than by two
|
|
18
|
+
* copies of one rule (Root Cause #30).
|
|
19
|
+
*
|
|
20
|
+
* ── Why the loading state is not "denied" ─────────────────────────────────
|
|
21
|
+
*
|
|
22
|
+
* `isLoading` is returned and callers must respect it. The three-state rule
|
|
23
|
+
* from `useCanSeePrices`: "not known yet" rendering as "no" shows an entitled
|
|
24
|
+
* seller a refusal on every page load. The server refuses authoritatively in
|
|
25
|
+
* `createSellerProduct` / `sellerUpdateProduct`, so an optimistic render while
|
|
26
|
+
* the fetch is in flight costs nothing worse than a late refusal.
|
|
27
|
+
*
|
|
28
|
+
* @tag domain:seller
|
|
29
|
+
* @tag layer:hooks
|
|
30
|
+
* @tag access:client
|
|
31
|
+
*/
|
|
32
|
+
import { useMemo } from "react";
|
|
33
|
+
import { useApiQuery } from "../../../client/api/useApiQuery.js";
|
|
34
|
+
import { apiClient } from "../../../http/index.js";
|
|
35
|
+
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
36
|
+
import { BASIC_LISTING_TYPES } from "../../../_internal/shared/listing-types/tiers.js";
|
|
37
|
+
export function useStoreListingEntitlements() {
|
|
38
|
+
const query = useApiQuery({
|
|
39
|
+
queryKey: ["store", "listing-entitlements"],
|
|
40
|
+
queryFn: async () => {
|
|
41
|
+
const res = await apiClient.get(SELLER_ENDPOINTS.PAYOUT_SETTINGS);
|
|
42
|
+
return res?.data ?? {};
|
|
43
|
+
},
|
|
44
|
+
/*
|
|
45
|
+
* The answer changes only when an admin or the seller flips a capability,
|
|
46
|
+
* so a short window is pointless churn — but it must not be `Infinity`,
|
|
47
|
+
* because a seller who has just ticked the box in another tab should not
|
|
48
|
+
* have to hard-reload to see the nav and forms open up.
|
|
49
|
+
*/
|
|
50
|
+
staleTime: 60000,
|
|
51
|
+
silentBackgroundFailures: true,
|
|
52
|
+
});
|
|
53
|
+
return useMemo(() => {
|
|
54
|
+
/*
|
|
55
|
+
* 🛑 Falls back to the BASIC types, not to everything and not to nothing.
|
|
56
|
+
*
|
|
57
|
+
* Everything would offer a form that the server then refuses — the exact
|
|
58
|
+
* "filled in a long wizard and got rejected at publish" experience this
|
|
59
|
+
* hook exists to prevent. Nothing would lock a seller out of `standard`
|
|
60
|
+
* on a transient network blip, which is the one type every store has.
|
|
61
|
+
*/
|
|
62
|
+
const list = query.data?.allowedListingTypes;
|
|
63
|
+
const allowed = new Set(Array.isArray(list) && list.length > 0 ? list : BASIC_LISTING_TYPES);
|
|
64
|
+
return {
|
|
65
|
+
allowed,
|
|
66
|
+
advancedEnabled: query.data?.advancedListingTypesEnabled === true,
|
|
67
|
+
isLoading: query.isLoading,
|
|
68
|
+
};
|
|
69
|
+
}, [query.data, query.isLoading]);
|
|
70
|
+
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { BaseRepository } from "../../../providers/db-firebase";
|
|
16
16
|
import type { DocumentSnapshot } from "firebase-admin/firestore";
|
|
17
|
-
import { type AnalyticsAlertDocument, type AnalyticsCardDocument, type ItemRequestDocument, type ItemRequestReply, type ListingTemplateDocument, type ModerationQueueDocument, type PayoutMethodDocument, type ReportDocument, type ShippingConfigDocument, type StoreCategoryDocument, type StoreGoogleConfigDocument
|
|
17
|
+
import { type AnalyticsAlertDocument, type AnalyticsCardDocument, type ItemRequestDocument, type ItemRequestReply, type ListingTemplateDocument, type ModerationQueueDocument, type PayoutMethodDocument, type ReportDocument, type ShippingConfigDocument, type StoreCategoryDocument, type StoreGoogleConfigDocument } from "../schemas/firestore";
|
|
18
18
|
export declare class PayoutMethodsRepository extends BaseRepository<PayoutMethodDocument> {
|
|
19
19
|
/**
|
|
20
20
|
* Bank/UPI details, encrypted at rest by BaseRepository's write hooks.
|
|
@@ -103,10 +103,6 @@ export declare class ItemRequestsRepository extends BaseRepository<ItemRequestDo
|
|
|
103
103
|
*/
|
|
104
104
|
appendReply(id: string, reply: ItemRequestReply): Promise<void>;
|
|
105
105
|
}
|
|
106
|
-
export declare class StoreWhatsAppConfigRepository extends BaseRepository<StoreWhatsAppConfigDocument> {
|
|
107
|
-
constructor();
|
|
108
|
-
getByStore(storeId: string): Promise<StoreWhatsAppConfigDocument | null>;
|
|
109
|
-
}
|
|
110
106
|
export declare class StoreGoogleConfigRepository extends BaseRepository<StoreGoogleConfigDocument> {
|
|
111
107
|
constructor();
|
|
112
108
|
getByStore(storeId: string): Promise<StoreGoogleConfigDocument | null>;
|
|
@@ -120,5 +116,4 @@ export declare const listingTemplatesRepository: ListingTemplatesRepository;
|
|
|
120
116
|
export declare const moderationQueueRepository: ModerationQueueRepository;
|
|
121
117
|
export declare const reportsRepository: ReportsRepository;
|
|
122
118
|
export declare const itemRequestsRepository: ItemRequestsRepository;
|
|
123
|
-
export declare const storeWhatsAppConfigRepository: StoreWhatsAppConfigRepository;
|
|
124
119
|
export declare const storeGoogleConfigRepository: StoreGoogleConfigRepository;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { BaseRepository } from "../../../providers/db-firebase/index.js";
|
|
16
16
|
import { arrayUnion, increment } from "../../../contracts/field-ops.js";
|
|
17
17
|
import { decryptPiiFields } from "../../../security/pii-encrypt.js";
|
|
18
|
-
import { ANALYTICS_ALERTS_COLLECTION, ANALYTICS_CARDS_COLLECTION, ITEM_REQUESTS_COLLECTION, LISTING_TEMPLATES_COLLECTION, MODERATION_QUEUE_COLLECTION, PAYOUT_METHODS_COLLECTION, REPORTS_COLLECTION, SHIPPING_CONFIGS_COLLECTION, STORE_CATEGORIES_COLLECTION, STORE_GOOGLE_CONFIG_COLLECTION,
|
|
18
|
+
import { ANALYTICS_ALERTS_COLLECTION, ANALYTICS_CARDS_COLLECTION, ITEM_REQUESTS_COLLECTION, LISTING_TEMPLATES_COLLECTION, MODERATION_QUEUE_COLLECTION, PAYOUT_METHODS_COLLECTION, REPORTS_COLLECTION, SHIPPING_CONFIGS_COLLECTION, STORE_CATEGORIES_COLLECTION, STORE_GOOGLE_CONFIG_COLLECTION, } from "../schemas/firestore.js";
|
|
19
19
|
export class PayoutMethodsRepository extends BaseRepository {
|
|
20
20
|
constructor() {
|
|
21
21
|
super(PAYOUT_METHODS_COLLECTION);
|
|
@@ -149,14 +149,6 @@ export class ItemRequestsRepository extends BaseRepository {
|
|
|
149
149
|
});
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
-
export class StoreWhatsAppConfigRepository extends BaseRepository {
|
|
153
|
-
constructor() {
|
|
154
|
-
super(STORE_WHATSAPP_CONFIG_COLLECTION);
|
|
155
|
-
}
|
|
156
|
-
async getByStore(storeId) {
|
|
157
|
-
return this.findOneBy("storeId", storeId);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
152
|
export class StoreGoogleConfigRepository extends BaseRepository {
|
|
161
153
|
constructor() {
|
|
162
154
|
super(STORE_GOOGLE_CONFIG_COLLECTION);
|
|
@@ -174,5 +166,4 @@ export const listingTemplatesRepository = new ListingTemplatesRepository();
|
|
|
174
166
|
export const moderationQueueRepository = new ModerationQueueRepository();
|
|
175
167
|
export const reportsRepository = new ReportsRepository();
|
|
176
168
|
export const itemRequestsRepository = new ItemRequestsRepository();
|
|
177
|
-
export const storeWhatsAppConfigRepository = new StoreWhatsAppConfigRepository();
|
|
178
169
|
export const storeGoogleConfigRepository = new StoreGoogleConfigRepository();
|
|
@@ -18,7 +18,6 @@ import type { BaseDocument } from "../../../_internal/shared/types/base-document
|
|
|
18
18
|
* 7. moderationQueue — video / media moderation queue items
|
|
19
19
|
* 8. reports — buyer-submitted reports against listings/stores
|
|
20
20
|
* 9. itemRequests — community item request bulletin board
|
|
21
|
-
* 10. storeWhatsAppConfig — per-store WhatsApp business catalogue + auto-replies
|
|
22
21
|
* 11. storeGoogleConfig — per-store Google Business reviews integration
|
|
23
22
|
*/
|
|
24
23
|
export type PayoutMethodType = "upi" | "bank" | "card" | "other";
|
|
@@ -89,7 +88,7 @@ export interface AnalyticsAlertDocument extends BaseDocument {
|
|
|
89
88
|
threshold: number;
|
|
90
89
|
windowHours: number;
|
|
91
90
|
isActive: boolean;
|
|
92
|
-
notifyChannels: Array<"in-app" | "email"
|
|
91
|
+
notifyChannels: Array<"in-app" | "email">;
|
|
93
92
|
/**
|
|
94
93
|
* When this alert last fired.
|
|
95
94
|
*
|
|
@@ -221,21 +220,6 @@ export interface ItemRequestDocument extends BaseDocument {
|
|
|
221
220
|
export declare const ITEM_REQUESTS_COLLECTION: "itemRequests";
|
|
222
221
|
export declare const ITEM_REQUEST_INDEXED_FIELDS: readonly ["opUserId", "status", "category", "createdAt"];
|
|
223
222
|
export declare const DEFAULT_ITEM_REQUEST_DATA: Partial<ItemRequestDocument>;
|
|
224
|
-
export interface StoreWhatsAppConfigDocument extends BaseDocument {
|
|
225
|
-
storeId: string;
|
|
226
|
-
isConnected: boolean;
|
|
227
|
-
isPaid: boolean;
|
|
228
|
-
phoneNumber?: string;
|
|
229
|
-
businessProfileName?: string;
|
|
230
|
-
catalogUrl?: string;
|
|
231
|
-
autoReply?: string;
|
|
232
|
-
welcomeMessage?: string;
|
|
233
|
-
/** Status of pending WhatsApp Business onboarding (Meta Cloud API). */
|
|
234
|
-
onboardingStatus?: "pending" | "approved" | "rejected";
|
|
235
|
-
}
|
|
236
|
-
export declare const STORE_WHATSAPP_CONFIG_COLLECTION: "storeWhatsAppConfig";
|
|
237
|
-
export declare const STORE_WHATSAPP_CONFIG_INDEXED_FIELDS: readonly ["storeId", "isConnected", "isPaid"];
|
|
238
|
-
export declare const DEFAULT_STORE_WHATSAPP_CONFIG_DATA: Partial<StoreWhatsAppConfigDocument>;
|
|
239
223
|
export interface StoreGoogleConfigDocument extends BaseDocument {
|
|
240
224
|
storeId: string;
|
|
241
225
|
isConnected: boolean;
|
|
@@ -182,16 +182,6 @@ export const DEFAULT_ITEM_REQUEST_DATA = {
|
|
|
182
182
|
replies: [],
|
|
183
183
|
replyCount: 0,
|
|
184
184
|
};
|
|
185
|
-
export const STORE_WHATSAPP_CONFIG_COLLECTION = "storeWhatsAppConfig";
|
|
186
|
-
export const STORE_WHATSAPP_CONFIG_INDEXED_FIELDS = [
|
|
187
|
-
"storeId",
|
|
188
|
-
"isConnected",
|
|
189
|
-
"isPaid",
|
|
190
|
-
];
|
|
191
|
-
export const DEFAULT_STORE_WHATSAPP_CONFIG_DATA = {
|
|
192
|
-
isConnected: false,
|
|
193
|
-
isPaid: false,
|
|
194
|
-
};
|
|
195
185
|
export const STORE_GOOGLE_CONFIG_COLLECTION = "storeGoogleConfig";
|
|
196
186
|
export const STORE_GOOGLE_CONFIG_INDEXED_FIELDS = [
|
|
197
187
|
"storeId",
|
|
@@ -12,5 +12,15 @@ export interface InteractiveStoreCardProps {
|
|
|
12
12
|
visitStore?: string;
|
|
13
13
|
};
|
|
14
14
|
className?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Owner-only pencil / clone / bin, for an admin or whoever owns this record.
|
|
17
|
+
* Pass `<OwnerQuickActions>`; it renders nothing for anyone else.
|
|
18
|
+
*
|
|
19
|
+
* 🛑 Rendered OUTSIDE this card's own `<Link>`/`<TextLink>`, as a sibling.
|
|
20
|
+
* A `<button>` inside an `<a>` is invalid HTML that browsers resolve by
|
|
21
|
+
* closing the anchor early, which breaks the CARD's navigation rather than
|
|
22
|
+
* just the button's — `stopPropagation` stops a click but cannot fix a parse.
|
|
23
|
+
*/
|
|
24
|
+
renderOwnerActions?: () => React.ReactNode;
|
|
15
25
|
}
|
|
16
|
-
export declare function InteractiveStoreCard({ store, href, selectable, isSelected, onSelect, labels, className, }: InteractiveStoreCardProps): import("react").JSX.Element;
|
|
26
|
+
export declare function InteractiveStoreCard({ store, href, selectable, isSelected, onSelect, labels, className, renderOwnerActions, }: InteractiveStoreCardProps): import("react").JSX.Element;
|
|
@@ -10,13 +10,13 @@ const __O = {
|
|
|
10
10
|
hidden: "overflow-hidden",
|
|
11
11
|
};
|
|
12
12
|
const CLS_RATING_BADGE = "absolute top-2 right-2 flex items-center gap-[var(--appkit-space-1)] rounded-full bg-black/50 backdrop-blur-sm px-[var(--appkit-space-2)] py-[var(--appkit-space-0-5)] text-[length:var(--appkit-text-xs)] font-semibold text-warning";
|
|
13
|
-
export function InteractiveStoreCard({ store, href, selectable, isSelected = false, onSelect, labels = {}, className = "", }) {
|
|
13
|
+
export function InteractiveStoreCard({ store, href, selectable, isSelected = false, onSelect, labels = {}, className = "", renderOwnerActions, }) {
|
|
14
14
|
const initial = store.storeName[0]?.toUpperCase() ?? "S";
|
|
15
15
|
const [logoBroken, setLogoBroken] = useState(false);
|
|
16
16
|
const hasLogo = Boolean(store.storeLogoURL) && !logoBroken;
|
|
17
17
|
const hasBanner = Boolean(store.storeBannerURL);
|
|
18
18
|
const longPress = useLongPress(() => onSelect?.(store.id, !isSelected));
|
|
19
|
-
|
|
19
|
+
const card = (_jsxs(Div, { className: [
|
|
20
20
|
"group relative flex flex-col h-full overflow-hidden rounded-xl border bg-[var(--appkit-color-surface)] shadow-sm transition-shadow hover:shadow-lg",
|
|
21
21
|
isSelected
|
|
22
22
|
? "border-[var(--appkit-color-primary,theme(colors.violet.600))] outline outline-2 outline-[var(--appkit-color-primary,theme(colors.violet.600))]"
|
|
@@ -26,4 +26,11 @@ export function InteractiveStoreCard({ store, href, selectable, isSelected = fal
|
|
|
26
26
|
e.preventDefault();
|
|
27
27
|
onSelect(store.id, !isSelected);
|
|
28
28
|
}, label: isSelected ? "Deselect store" : "Select store", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto transition-opacity" })), _jsxs(Link, { href: href, className: "flex flex-col flex-1 min-h-0", children: [_jsxs(Div, { surface: "muted", className: `relative aspect-video w-full ${__O.hidden} flex-shrink-0`, children: [hasBanner ? (_jsx(MediaImage, { src: store.storeBannerURL, alt: `${store.storeName} banner`, size: "banner", className: "h-full w-full object-cover transition-transform duration-500 group-hover:scale-105" })) : (_jsx(Row, { className: "absolute inset-0", align: "center", justify: "center", children: _jsx(Span, { className: "opacity-20 select-none", size: "5xl", "aria-hidden": "true", children: "\uD83C\uDFEA" }) })), _jsx(Scrim, { direction: "bottom-up", intensity: "subtle", className: "absolute inset-x-0 bottom-0 h-12" }), store.averageRating != null && store.averageRating > 0 && (_jsxs(Div, { className: CLS_RATING_BADGE, children: ["\u2605 ", store.averageRating.toFixed(1)] }))] }), _jsxs(Stack, { paddingY: "b-md", className: "flex-1", padding: "x-md", children: [_jsx(Row, { className: "-mt-5 mb-2", align: "end", justify: "between", children: _jsx(Div, { className: "flex-shrink-0", children: hasLogo ? (_jsx(Div, { className: "relative h-10 w-10 border-2 border-[var(--appkit-color-border-subtle)] bg-[var(--appkit-color-surface)]", rounded: "lg", shadow: "md", overflow: "hidden", children: _jsx(MediaImage, { src: store.storeLogoURL, alt: store.storeName, size: "thumbnail", onError: () => setLogoBroken(true) }) })) : (_jsx(Row, { textWeight: "bold", textSize: "base", className: "h-10 w-10 border-2 border-white bg-primary/10 dark:bg-primary/20 text-primary", align: "center", justify: "center", rounded: "lg", shadow: "md", children: initial })) }) }), _jsx(Heading, { level: 3, className: `group-hover:text-primary transition-colors`, truncate: true, size: "sm", weight: "bold", color: "primary", children: store.storeName }), store.storeDescription ? (_jsx(RichText, { html: normalizeRichTextHtml(store.storeDescription), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "mt-1 line-clamp-2 text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)] flex-1" })) : (_jsx(Div, { className: "flex-1" })), _jsxs(Row, { color: "muted", textSize: "xs", gap: "sm", className: "mt-2.5", wrap: true, children: [store.totalProducts != null && store.totalProducts > 0 && (_jsxs(Span, { layout: "flex", gap: "2xs", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDCE6" }), " ", store.totalProducts, " ", labels.products ?? "products"] })), store.itemsSold != null && store.itemsSold > 0 && (_jsxs(Span, { layout: "flex", gap: "2xs", children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDECD\uFE0F" }), " ", store.itemsSold, " ", labels.sold ?? "sold"] }))] }), _jsxs(Row, { border: "top-subtle", paddingY: "y-xs-tall", className: "mt-3", align: "center", justify: "between", children: [_jsxs(Span, { size: "xs", weight: "semibold", className: "text-primary group-hover:underline transition-colors", children: [labels.visitStore ?? "Visit store", " \u2192"] }), store.totalReviews != null && store.totalReviews > 0 && (_jsxs(Span, { size: "xs", color: "muted", children: [store.totalReviews, " ", labels.reviews ?? "reviews"] }))] })] })] })] }));
|
|
29
|
+
if (!renderOwnerActions)
|
|
30
|
+
return card;
|
|
31
|
+
/*
|
|
32
|
+
* Sibling of the card, never a child — see the prop's docstring. `z-20`
|
|
33
|
+
* beats this card's own overlays, which all sit at `z-10`.
|
|
34
|
+
*/
|
|
35
|
+
return (_jsxs(Div, { className: "relative h-full", children: [card, _jsx(Div, { className: "absolute right-2 bottom-2 z-20", children: renderOwnerActions() })] }));
|
|
29
36
|
}
|
|
@@ -16,6 +16,7 @@ import { AUCTION_PUBLIC_SORT_OPTIONS } from "../../products/constants/sieve.js";
|
|
|
16
16
|
import { AvailabilityTabs } from "../../products/components/AvailabilityTabs.js";
|
|
17
17
|
import { AVAILABILITY_VALUES } from "../../../constants/field-names.js";
|
|
18
18
|
import { TABLE_KEYS } from "../../../constants/table-keys.js";
|
|
19
|
+
import { productImageUrls } from "../../products/schemas/firestore.js";
|
|
19
20
|
const STORE_AUCTION_TYPES = ["auction"];
|
|
20
21
|
const __P = {
|
|
21
22
|
p3: "p-[var(--appkit-space-3)]",
|
|
@@ -75,7 +76,7 @@ export function StoreAuctionsListing({ storeId, initialData }) {
|
|
|
75
76
|
description: p.description,
|
|
76
77
|
price: p.price ?? 0,
|
|
77
78
|
currency: p.currency || getDefaultCurrency(),
|
|
78
|
-
mainImage: p.mainImage
|
|
79
|
+
mainImage: p.mainImage || productImageUrls(p.images)[0],
|
|
79
80
|
images: p.images,
|
|
80
81
|
listingType: "auction",
|
|
81
82
|
auctionEndDate: p.auctionEndDate,
|
|
@@ -45,7 +45,6 @@ const CAPABILITY_GROUPS = [
|
|
|
45
45
|
{ cap: "lower_commission_rate", unlocks: "Negotiated reduced platform fee. Requires a signed agreement and minimum volume.", defaultOn: false },
|
|
46
46
|
{ cap: "early_access_features", unlocks: "Access to beta features before they are publicly available.", defaultOn: false },
|
|
47
47
|
{ cap: "advanced_analytics", unlocks: "Cohort retention, basket analysis, and top-buyer breakdown (beyond summary stats).", defaultOn: false },
|
|
48
|
-
{ cap: "whatsapp_catalog_sync", unlocks: "Sync your store products to your WhatsApp Business Catalog automatically.", defaultOn: false },
|
|
49
48
|
],
|
|
50
49
|
},
|
|
51
50
|
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Package, ShoppingBag, Wallet, Settings, Shield, Headphones, CheckCircle2, Circle, BookOpen,
|
|
2
|
+
import { Package, ShoppingBag, Wallet, Settings, Shield, Headphones, CheckCircle2, Circle, BookOpen, } from "lucide-react";
|
|
3
3
|
import { Div, Grid, Heading, Progress, Row, Section, Stack, Text, TextLink } from "../../../ui/index.js";
|
|
4
4
|
import { ROUTES } from "../../../next/routing/route-map.js";
|
|
5
5
|
const CLS_CHECK_ICON = "w-5 h-5 flex-shrink-0 text-emerald-500";
|
|
@@ -27,7 +27,7 @@ const GUIDE_CARDS = [
|
|
|
27
27
|
{
|
|
28
28
|
Icon: Settings,
|
|
29
29
|
title: "Settings",
|
|
30
|
-
description: "Store profile, shipping config, return policy, vacation mode
|
|
30
|
+
description: "Store profile, shipping config, return policy, and vacation mode.",
|
|
31
31
|
href: String(ROUTES.STORE.GUIDE_SETTINGS),
|
|
32
32
|
},
|
|
33
33
|
{
|
|
@@ -36,12 +36,6 @@ const GUIDE_CARDS = [
|
|
|
36
36
|
description: "Admin-granted feature flags — auctions, pre-orders, lower commissions, and more.",
|
|
37
37
|
href: String(ROUTES.STORE.GUIDE_CAPABILITIES),
|
|
38
38
|
},
|
|
39
|
-
{
|
|
40
|
-
Icon: MessageSquare,
|
|
41
|
-
title: "WhatsApp Catalog Sync",
|
|
42
|
-
description: "Push your product catalog to Meta Commerce Manager for WhatsApp, Facebook, and Instagram Shops.",
|
|
43
|
-
href: String(ROUTES.STORE.GUIDE_WHATSAPP),
|
|
44
|
-
},
|
|
45
39
|
{
|
|
46
40
|
Icon: Headphones,
|
|
47
41
|
title: "Support",
|
|
@@ -26,11 +26,7 @@ function ReturnPolicySection() {
|
|
|
26
26
|
function VacationModeSection() {
|
|
27
27
|
return (_jsxs(Div, { className: SECTION_BODY, children: [_jsxs(Div, { children: [_jsx(Text, { className: SUBHEADING, children: "What vacation mode does" }), _jsxs(Ul, { spacing: "tight", indent: "lg", children: [_jsx(Li, { className: LIST_DISC, children: "All your published listings are hidden from search results and browse pages." }), _jsx(Li, { className: LIST_DISC, children: "Your store profile page shows a \"On Vacation\" banner." }), _jsx(Li, { className: LIST_DISC, children: "Buyers cannot add your items to cart or checkout while on vacation." }), _jsx(Li, { className: LIST_DISC, children: "Existing orders that were placed before vacation mode must still be fulfilled." })] })] }), _jsxs(Div, { children: [_jsx(Text, { className: SUBHEADING, children: "How to enable" }), _jsx(Text, { className: "leading-relaxed", children: "Go to your Store Dashboard \u2192 Settings \u2192 Storefront tab. Toggle \"Vacation mode\" on. Your listings re-appear instantly when you toggle it off." })] }), _jsx(Alert, { variant: "warning", title: "Ongoing orders are not paused", children: "Enabling vacation mode does not pause or cancel any existing orders. You are still responsible for fulfilling orders placed before vacation mode was enabled." })] }));
|
|
28
28
|
}
|
|
29
|
-
// -- Section 5:
|
|
30
|
-
function WhatsAppSection() {
|
|
31
|
-
return (_jsxs(Div, { className: SECTION_BODY, children: [_jsxs(Text, { className: "leading-relaxed", children: ["The WhatsApp integration allows your customers to contact you via WhatsApp from your store profile and lets you sync your product catalog to your WhatsApp Business Catalog (requires the", _jsx(Code, { className: "mx-1", size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "whatsapp_catalog_sync" }), " capability)."] }), _jsxs(Text, { className: "leading-relaxed", children: ["Full settings and the catalog sync toggle are at", " ", _jsx(TextLink, { variant: "bare", href: String(ROUTES.STORE.WHATSAPP), className: "underline text-[var(--appkit-color-primary)] hover:opacity-80", children: "Settings \u2192 WhatsApp" }), ". See the WhatsApp settings page for step-by-step setup instructions."] })] }));
|
|
32
|
-
}
|
|
33
|
-
// -- Section 6: Visibility states ---------------------------------------------
|
|
29
|
+
// -- Section 5: Visibility states ---------------------------------------------
|
|
34
30
|
function VisibilityStatesSection() {
|
|
35
31
|
const STATES = [
|
|
36
32
|
{
|
|
@@ -53,5 +49,5 @@ function VisibilityStatesSection() {
|
|
|
53
49
|
return (_jsx(Stack, { gap: "3", children: STATES.map(({ state, note }) => (_jsxs(Row, { textSize: "sm", gap: "3", children: [_jsx(Text, { className: "shrink-0 w-32 text-[var(--appkit-color-text)]", weight: "semibold", children: state }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] leading-relaxed", children: note })] }, state))) }));
|
|
54
50
|
}
|
|
55
51
|
export function StoreSettingsGuideView(_props) {
|
|
56
|
-
return (_jsxs(Stack, { gap: "lg", padding: "b-2xl", children: [_jsxs(TextLink, { variant: "bare", href: String(ROUTES.STORE.GUIDE), size: "sm", layout: "inline-flex", align: "center", gap: "sm", className: "text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)] transition-colors", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), "Back to Seller Guide"] }), _jsxs(Div, { children: [_jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)]", size: "2xl", weight: "bold", children: "Settings Guide" }), _jsx(Text, { className: "mt-1 text-[var(--appkit-color-text-muted)]", size: "sm", children: "Store profile, shipping, returns, vacation mode,
|
|
52
|
+
return (_jsxs(Stack, { gap: "lg", padding: "b-2xl", children: [_jsxs(TextLink, { variant: "bare", href: String(ROUTES.STORE.GUIDE), size: "sm", layout: "inline-flex", align: "center", gap: "sm", className: "text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)] transition-colors", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), "Back to Seller Guide"] }), _jsxs(Div, { children: [_jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)]", size: "2xl", weight: "bold", children: "Settings Guide" }), _jsx(Text, { className: "mt-1 text-[var(--appkit-color-text-muted)]", size: "sm", children: "Store profile, shipping, returns, vacation mode, and visibility." })] }), _jsx(GuideSection, { title: "1. Store profile", children: _jsx(StoreProfileSection, {}) }), _jsx(GuideSection, { title: "2. Shipping configuration", children: _jsx(ShippingSection, {}) }), _jsx(GuideSection, { title: "3. Return policy", children: _jsx(ReturnPolicySection, {}) }), _jsx(GuideSection, { title: "4. Vacation mode", children: _jsx(VacationModeSection, {}) }), _jsx(GuideSection, { title: "5. Visibility states", children: _jsx(VisibilityStatesSection, {}) })] }));
|
|
57
53
|
}
|
|
@@ -47,5 +47,3 @@ export { StoreCapabilitiesGuideView } from "./StoreCapabilitiesGuideView";
|
|
|
47
47
|
export type { StoreCapabilitiesGuideViewProps } from "./StoreCapabilitiesGuideView";
|
|
48
48
|
export { StoreSettingsGuideView } from "./StoreSettingsGuideView";
|
|
49
49
|
export type { StoreSettingsGuideViewProps } from "./StoreSettingsGuideView";
|
|
50
|
-
export { StoreWhatsAppGuideView } from "./StoreWhatsAppGuideView";
|
|
51
|
-
export type { StoreWhatsAppGuideViewProps } from "./StoreWhatsAppGuideView";
|
|
@@ -20,4 +20,3 @@ export { StoreOrdersGuideView } from "./StoreOrdersGuideView.js";
|
|
|
20
20
|
export { StoreFinanceGuideView } from "./StoreFinanceGuideView.js";
|
|
21
21
|
export { StoreCapabilitiesGuideView } from "./StoreCapabilitiesGuideView.js";
|
|
22
22
|
export { StoreSettingsGuideView } from "./StoreSettingsGuideView.js";
|
|
23
|
-
export { StoreWhatsAppGuideView } from "./StoreWhatsAppGuideView.js";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Data access layer for the `stores` Firestore collection.
|
|
5
5
|
* One store per seller; identified by storeSlug (used as document ID).
|
|
6
6
|
*/
|
|
7
|
-
import { BaseRepository, type SieveModel, type FirebaseSieveResult
|
|
7
|
+
import { BaseRepository, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
8
8
|
import { StoreDocument } from "../schemas";
|
|
9
9
|
import { type HistoryActor } from "../../../_internal/shared/history/index";
|
|
10
10
|
import type { JsonValue } from "../../../schemas/types";
|
|
@@ -17,31 +17,6 @@ export interface StoreWriteContext {
|
|
|
17
17
|
}
|
|
18
18
|
export declare class StoreRepository extends BaseRepository<StoreDocument> {
|
|
19
19
|
constructor();
|
|
20
|
-
private encryptSecrets;
|
|
21
|
-
private decryptSecrets;
|
|
22
|
-
protected mapDoc<D = StoreDocument>(snap: DocumentSnapshot): D;
|
|
23
|
-
/**
|
|
24
|
-
* LIST reads never carry the WhatsApp token — not even encrypted.
|
|
25
|
-
*
|
|
26
|
-
* `mapDoc` above decrypts it, and `listStores(model, activeOnly)` backs the
|
|
27
|
-
* PUBLIC `/stores` page. Once `sieveQuery` started routing list rows through
|
|
28
|
-
* `mapDoc` (it previously mapped documents itself and skipped every
|
|
29
|
-
* override), that decrypt would have put a plaintext Meta OAuth bearer token
|
|
30
|
-
* on a public listing — the exact shape of Root Cause #70.
|
|
31
|
-
*
|
|
32
|
-
* So the list path deliberately does NOT call `decryptSecrets`, and drops
|
|
33
|
-
* `accessToken` outright: a list has no legitimate use for it, and a value
|
|
34
|
-
* that is never in the payload cannot be leaked by a projection someone
|
|
35
|
-
* forgets to write.
|
|
36
|
-
*
|
|
37
|
-
* Detail reads are unchanged — `findBySlug`/`findById` still decrypt, so the
|
|
38
|
-
* WhatsApp settings and catalog-sync routes keep working exactly as before.
|
|
39
|
-
* Removing the decrypt from `mapDoc` itself is a separate, riskier change
|
|
40
|
-
* that CLAUDE.md explicitly defers; this closes the public-exposure half
|
|
41
|
-
* without touching the encryption round-trip at all.
|
|
42
|
-
*/
|
|
43
|
-
protected mapDocForList<D = StoreDocument>(snap: DocumentSnapshot): D;
|
|
44
|
-
update(id: string, data: Partial<StoreDocument>): Promise<StoreDocument>;
|
|
45
20
|
/**
|
|
46
21
|
* Create a new store.
|
|
47
22
|
* The document ID is set to storeSlug for easy URL-based lookups.
|
|
@@ -9,7 +9,6 @@ import { increment } from "../../../contracts/field-ops.js";
|
|
|
9
9
|
import { STORE_COLLECTION, STORE_FIELDS, DEFAULT_STORE_DATA, StoreStatusValues, STORE_TRACKED_FIELDS, STORE_HISTORY_PII_FIELDS, } from "../schemas/index.js";
|
|
10
10
|
import { DatabaseError } from "../../../errors/index.js";
|
|
11
11
|
import { normalizeError } from "../../../errors/normalize.js";
|
|
12
|
-
import { encryptSecret, decryptSecret, } from "../../../security/settings-encryption.js";
|
|
13
12
|
import { withHistory } from "../../../_internal/shared/history/index.js";
|
|
14
13
|
import { buildStoreSearchTxt } from "../../../utils/search-txt-builders.js";
|
|
15
14
|
import { planSearchTxt, beginSearchWindow, emptySearchResult, } from "../../../utils/search-txt-query.js";
|
|
@@ -17,57 +16,6 @@ export class StoreRepository extends BaseRepository {
|
|
|
17
16
|
constructor() {
|
|
18
17
|
super(STORE_COLLECTION);
|
|
19
18
|
}
|
|
20
|
-
encryptSecrets(data) {
|
|
21
|
-
const token = data.whatsappConfig?.accessToken;
|
|
22
|
-
if (!token)
|
|
23
|
-
return data;
|
|
24
|
-
return {
|
|
25
|
-
...data,
|
|
26
|
-
whatsappConfig: { ...data.whatsappConfig, accessToken: encryptSecret(token) },
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
decryptSecrets(data) {
|
|
30
|
-
const token = data.whatsappConfig?.accessToken;
|
|
31
|
-
if (!token)
|
|
32
|
-
return data;
|
|
33
|
-
return {
|
|
34
|
-
...data,
|
|
35
|
-
whatsappConfig: { ...data.whatsappConfig, accessToken: decryptSecret(token) },
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
mapDoc(snap) {
|
|
39
|
-
return this.decryptSecrets(super.mapDoc(snap));
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* LIST reads never carry the WhatsApp token — not even encrypted.
|
|
43
|
-
*
|
|
44
|
-
* `mapDoc` above decrypts it, and `listStores(model, activeOnly)` backs the
|
|
45
|
-
* PUBLIC `/stores` page. Once `sieveQuery` started routing list rows through
|
|
46
|
-
* `mapDoc` (it previously mapped documents itself and skipped every
|
|
47
|
-
* override), that decrypt would have put a plaintext Meta OAuth bearer token
|
|
48
|
-
* on a public listing — the exact shape of Root Cause #70.
|
|
49
|
-
*
|
|
50
|
-
* So the list path deliberately does NOT call `decryptSecrets`, and drops
|
|
51
|
-
* `accessToken` outright: a list has no legitimate use for it, and a value
|
|
52
|
-
* that is never in the payload cannot be leaked by a projection someone
|
|
53
|
-
* forgets to write.
|
|
54
|
-
*
|
|
55
|
-
* Detail reads are unchanged — `findBySlug`/`findById` still decrypt, so the
|
|
56
|
-
* WhatsApp settings and catalog-sync routes keep working exactly as before.
|
|
57
|
-
* Removing the decrypt from `mapDoc` itself is a separate, riskier change
|
|
58
|
-
* that CLAUDE.md explicitly defers; this closes the public-exposure half
|
|
59
|
-
* without touching the encryption round-trip at all.
|
|
60
|
-
*/
|
|
61
|
-
mapDocForList(snap) {
|
|
62
|
-
const doc = super.mapDoc(snap);
|
|
63
|
-
if (!doc.whatsappConfig)
|
|
64
|
-
return doc;
|
|
65
|
-
const { accessToken: _dropped, ...safeConfig } = doc.whatsappConfig;
|
|
66
|
-
return { ...doc, whatsappConfig: safeConfig };
|
|
67
|
-
}
|
|
68
|
-
async update(id, data) {
|
|
69
|
-
return super.update(id, this.encryptSecrets(data));
|
|
70
|
-
}
|
|
71
19
|
/**
|
|
72
20
|
* Create a new store.
|
|
73
21
|
* The document ID is set to storeSlug for easy URL-based lookups.
|
|
@@ -93,7 +41,7 @@ export class StoreRepository extends BaseRepository {
|
|
|
93
41
|
await this.db
|
|
94
42
|
.collection(this.collection)
|
|
95
43
|
.doc(input.storeSlug)
|
|
96
|
-
.create(prepareForFirestore(
|
|
44
|
+
.create(prepareForFirestore(storeData));
|
|
97
45
|
}
|
|
98
46
|
catch (err) {
|
|
99
47
|
void normalizeError(err);
|
|
@@ -169,9 +117,8 @@ export class StoreRepository extends BaseRepository {
|
|
|
169
117
|
trigger: ctx?.trigger ?? defaultTrigger,
|
|
170
118
|
reason: ctx?.reason,
|
|
171
119
|
note: ctx?.note,
|
|
172
|
-
// `
|
|
173
|
-
//
|
|
174
|
-
// `encryptPiiFields` never descends into arrays.
|
|
120
|
+
// `encryptPiiFields` never descends into arrays, so a nested secret
|
|
121
|
+
// would be stored in plaintext — the list below is the scrub.
|
|
175
122
|
piiFields: STORE_HISTORY_PII_FIELDS,
|
|
176
123
|
});
|
|
177
124
|
return withEntry ?? patch;
|
|
@@ -233,9 +180,7 @@ export class StoreRepository extends BaseRepository {
|
|
|
233
180
|
options: { maxPageSize: win.maxPageSize(100) },
|
|
234
181
|
fields: sieveFields,
|
|
235
182
|
// Called DIRECTLY, not through `sieveQuery`, so the mapper has to be
|
|
236
|
-
// passed by hand
|
|
237
|
-
// `activeOnly` this is the public /stores listing; `mapDoc` decrypts the
|
|
238
|
-
// WhatsApp bearer token and a list has no use for it.
|
|
183
|
+
// passed by hand. With `activeOnly` this is the public /stores listing.
|
|
239
184
|
mapDoc: (snap) => this.mapDocForList(snap),
|
|
240
185
|
});
|
|
241
186
|
const searched = win.finish(result);
|
|
@@ -372,7 +317,7 @@ export class StoreRepository extends BaseRepository {
|
|
|
372
317
|
const batch = this.db.batch();
|
|
373
318
|
const newRef = this.db.collection(this.collection).doc(newSlug);
|
|
374
319
|
const oldRef = this.db.collection(this.collection).doc(currentSlug);
|
|
375
|
-
batch.create(newRef, prepareForFirestore(
|
|
320
|
+
batch.create(newRef, prepareForFirestore(newDoc));
|
|
376
321
|
batch.delete(oldRef);
|
|
377
322
|
await batch.commit();
|
|
378
323
|
return { id: newSlug, ...newDoc };
|