@mohasinac/appkit 4.53.2 → 4.54.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/server/features/auth/capabilities.js +7 -5
- 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/products/listing-tab-counts.js +81 -25
- 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/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/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 +21 -15
- package/dist/constants/api-endpoints.js +7 -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 +4 -3
- 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/components/BrandDetailPageView.js +2 -1
- package/dist/features/categories/components/BundleDetailView.js +2 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +2 -1
- 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 +2 -1
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +2 -1
- 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 +2 -1
- 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 +4 -3
- 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.js +1 -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 +27 -2
- 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/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/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/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/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 +415 -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 -88
- package/dist/seed/_helpers/category-parts.d.ts +1 -1
- package/dist/seed/_helpers/category-parts.js +13399 -3019
- 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-catalogue-seed-data.d.ts +5 -5
- package/dist/seed/products-catalogue-seed-data.js +63936 -13803
- 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 +8 -10
- package/dist/server.js +16 -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/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
|
@@ -31,10 +31,6 @@ export interface NotificationDocument extends BaseDocument {
|
|
|
31
31
|
readAt?: Date;
|
|
32
32
|
relatedId?: string;
|
|
33
33
|
relatedType?: NotificationRelatedType;
|
|
34
|
-
/** Outcome of the async whatsappNotify job for this notification's WhatsApp send, if one was enqueued. */
|
|
35
|
-
whatsappStatus?: "queued" | "sent" | "failed" | "skipped";
|
|
36
|
-
/** jobs/{id} doc that ran (or is running) the WhatsApp send for this notification. */
|
|
37
|
-
whatsappJobId?: string;
|
|
38
34
|
}
|
|
39
35
|
export declare const NOTIFICATIONS_COLLECTION: "notifications";
|
|
40
36
|
export declare const NOTIFICATIONS_INDEXED_FIELDS: readonly ["userId", "isRead", "createdAt", "type"];
|
|
@@ -99,8 +95,20 @@ export interface SiteSettingsCredentials {
|
|
|
99
95
|
/** Username/password pair configured in the PhonePe dashboard's Webhook tab (SHA/Basic auth mode) — verified via the SDK's `validateCallback()`. */
|
|
100
96
|
phonepeWebhookUsername?: string;
|
|
101
97
|
phonepeWebhookPassword?: string;
|
|
98
|
+
/**
|
|
99
|
+
* Razorpay publishable key (`rzp_test_…` / `rzp_live_…`).
|
|
100
|
+
*
|
|
101
|
+
* 🛑 The ONLY credential in this interface that is deliberately sent to the
|
|
102
|
+
* browser — Razorpay's `checkout.js` needs it to open the modal. It is
|
|
103
|
+
* published by `/api/payment/create-order`, not by the site-settings
|
|
104
|
+
* projection, so it stays in `PRIVATE_SITE_SETTINGS_FIELDS` like every
|
|
105
|
+
* other credential (see CLAUDE.md § Public Data Projections).
|
|
106
|
+
*/
|
|
107
|
+
razorpayKeyId?: string;
|
|
108
|
+
razorpayKeySecret?: string;
|
|
109
|
+
/** Webhook signing secret from Razorpay's dashboard → Webhooks. Without it `verifyWebhook()` fails closed. */
|
|
110
|
+
razorpayWebhookSecret?: string;
|
|
102
111
|
resendApiKey?: string;
|
|
103
|
-
whatsappApiKey?: string;
|
|
104
112
|
metaAppId?: string;
|
|
105
113
|
metaAppSecret?: string;
|
|
106
114
|
metaPageAccessToken?: string;
|
|
@@ -115,28 +123,6 @@ export interface SiteSettingsCredentials {
|
|
|
115
123
|
/** Google Places API for Google Business Reviews (HS4) */
|
|
116
124
|
googleMapsApiKey?: string;
|
|
117
125
|
googlePlaceId?: string;
|
|
118
|
-
/** WhatsApp Business Cloud API — platform level (for admin purchase announcements) */
|
|
119
|
-
whatsappPhoneNumberId?: string;
|
|
120
|
-
whatsappCloudApiToken?: string;
|
|
121
|
-
/** Comma-separated digits-only phone numbers that receive order announcements */
|
|
122
|
-
whatsappAdminNotifyNumbers?: string;
|
|
123
|
-
/**
|
|
124
|
-
* Approved Meta message-template names, one flat string field per
|
|
125
|
-
* order-lifecycle notification type — every `SiteSettingsCredentials`
|
|
126
|
-
* field is stored as an individually-encrypted string (see
|
|
127
|
-
* `getDecryptedCredentials`/`getCredentialsMasked`, which iterate + mask
|
|
128
|
-
* every field as a string), so this can't be a `Record<string,string>`
|
|
129
|
-
* map. Business-initiated WhatsApp sends outside the 24h customer-service
|
|
130
|
-
* window require a pre-approved template.
|
|
131
|
-
*/
|
|
132
|
-
whatsappTemplateOrderPlaced?: string;
|
|
133
|
-
whatsappTemplateOrderConfirmed?: string;
|
|
134
|
-
whatsappTemplateOrderShipped?: string;
|
|
135
|
-
whatsappTemplateOrderDelivered?: string;
|
|
136
|
-
whatsappTemplateOrderCancelled?: string;
|
|
137
|
-
whatsappTemplateRefundInitiated?: string;
|
|
138
|
-
/** BCP-47 language code the templates above were approved in, e.g. "en" or "en_US". */
|
|
139
|
-
whatsappTemplateLanguage?: string;
|
|
140
126
|
}
|
|
141
127
|
export interface SiteSettingsCredentialsMasked {
|
|
142
128
|
phonepeClientId?: string;
|
|
@@ -145,8 +131,10 @@ export interface SiteSettingsCredentialsMasked {
|
|
|
145
131
|
phonepeEnvironment?: string;
|
|
146
132
|
phonepeWebhookUsername?: string;
|
|
147
133
|
phonepeWebhookPassword?: string;
|
|
134
|
+
razorpayKeyId?: string;
|
|
135
|
+
razorpayKeySecret?: string;
|
|
136
|
+
razorpayWebhookSecret?: string;
|
|
148
137
|
resendApiKey?: string;
|
|
149
|
-
whatsappApiKey?: string;
|
|
150
138
|
metaAppId?: string;
|
|
151
139
|
metaAppSecret?: string;
|
|
152
140
|
metaPageAccessToken?: string;
|
|
@@ -158,16 +146,6 @@ export interface SiteSettingsCredentialsMasked {
|
|
|
158
146
|
deviantartClientSecret?: string;
|
|
159
147
|
googleMapsApiKey?: string;
|
|
160
148
|
googlePlaceId?: string;
|
|
161
|
-
whatsappPhoneNumberId?: string;
|
|
162
|
-
whatsappCloudApiToken?: string;
|
|
163
|
-
whatsappAdminNotifyNumbers?: string;
|
|
164
|
-
whatsappTemplateOrderPlaced?: string;
|
|
165
|
-
whatsappTemplateOrderConfirmed?: string;
|
|
166
|
-
whatsappTemplateOrderShipped?: string;
|
|
167
|
-
whatsappTemplateOrderDelivered?: string;
|
|
168
|
-
whatsappTemplateOrderCancelled?: string;
|
|
169
|
-
whatsappTemplateRefundInitiated?: string;
|
|
170
|
-
whatsappTemplateLanguage?: string;
|
|
171
149
|
}
|
|
172
150
|
export interface NotificationChannelConfig {
|
|
173
151
|
inApp: {
|
|
@@ -183,15 +161,6 @@ export interface NotificationChannelConfig {
|
|
|
183
161
|
/** Subset of NotificationTypes to email — absent means all types. */
|
|
184
162
|
types?: NotificationType[];
|
|
185
163
|
};
|
|
186
|
-
whatsapp: {
|
|
187
|
-
enabled: boolean;
|
|
188
|
-
/** Minimum priority that triggers a WhatsApp message (default: "high"). */
|
|
189
|
-
minPriority?: NotificationPriority;
|
|
190
|
-
/** Subset of NotificationTypes to WhatsApp — absent means all types. */
|
|
191
|
-
types?: NotificationType[];
|
|
192
|
-
/** When true, WhatsApp OTP is offered as an alternative to email OTP at checkout. */
|
|
193
|
-
otpEnabled?: boolean;
|
|
194
|
-
};
|
|
195
164
|
sms: {
|
|
196
165
|
enabled: boolean;
|
|
197
166
|
/** Minimum priority that triggers an SMS (default: "high"). */
|
|
@@ -368,6 +337,18 @@ export interface SiteSettingsDocument extends BaseDocument {
|
|
|
368
337
|
supportHours?: string;
|
|
369
338
|
};
|
|
370
339
|
payment: {
|
|
340
|
+
/**
|
|
341
|
+
* Razorpay (client-side card/UPI/netbanking modal).
|
|
342
|
+
*
|
|
343
|
+
* 🛑 Checked BEFORE `phonepeEnabled` in `providers.config.ts` — exactly
|
|
344
|
+
* one online gateway can be registered per process, so the order is the
|
|
345
|
+
* tie-break when both are on, not a precedence anyone should rely on.
|
|
346
|
+
* Turning this on with no credentials makes the provider constructor
|
|
347
|
+
* throw at boot; `resolveKeys()` treats a seeded `*_PLACEHOLDER` as
|
|
348
|
+
* absent, so a placeholder reads as "unconfigured" rather than failing
|
|
349
|
+
* mid-checkout.
|
|
350
|
+
*/
|
|
351
|
+
razorpayEnabled: boolean;
|
|
371
352
|
phonepeEnabled: boolean;
|
|
372
353
|
upiManualEnabled: boolean;
|
|
373
354
|
codEnabled: boolean;
|
|
@@ -433,10 +414,6 @@ export interface SiteSettingsDocument extends BaseDocument {
|
|
|
433
414
|
/** COD handling fee charged to the buyer: max(codHandlingFeeMin, subtotal × codHandlingFeePercent / 100). */
|
|
434
415
|
codHandlingFeeMin: number;
|
|
435
416
|
codHandlingFeePercent: number;
|
|
436
|
-
/** Admin master toggle — whether the WhatsApp order-updates addon is offered at checkout at all. */
|
|
437
|
-
whatsappNotifyFeeEnabled: boolean;
|
|
438
|
-
/** Flat rupee fee charged to the buyer when they opt into the WhatsApp order-updates addon. */
|
|
439
|
-
whatsappNotifyFee: number;
|
|
440
417
|
/** Admin master toggle — whether the gift-wrap addon is offered at checkout at all. */
|
|
441
418
|
giftWrapFeeEnabled: boolean;
|
|
442
419
|
/** Flat rupee fee charged to the buyer when they opt into gift wrap. */
|
|
@@ -522,8 +499,6 @@ export interface SiteSettingsDocument extends BaseDocument {
|
|
|
522
499
|
messaging?: {
|
|
523
500
|
/** User-facing email. Staff mail (daily digest, payout summary) ignores this. */
|
|
524
501
|
emailEnabled: boolean;
|
|
525
|
-
/** User-facing WhatsApp. Same exemption for staff. */
|
|
526
|
-
whatsappEnabled: boolean;
|
|
527
502
|
/**
|
|
528
503
|
* Reserved sends allowed per channel per day. Applies to staff and
|
|
529
504
|
* transactional mail too — the ceiling exists to stay inside the
|
|
@@ -531,7 +506,6 @@ export interface SiteSettingsDocument extends BaseDocument {
|
|
|
531
506
|
*/
|
|
532
507
|
dailyCeiling: {
|
|
533
508
|
email: number;
|
|
534
|
-
whatsapp: number;
|
|
535
509
|
sms: number;
|
|
536
510
|
};
|
|
537
511
|
};
|
|
@@ -15,7 +15,7 @@ export const ADMIN_CHECKOUT_BYPASS_FLAG_KEY = "adminCheckoutBypass";
|
|
|
15
15
|
* · `constants/notification-types.ts` (9) — fed the admin filter chips and
|
|
16
16
|
* the per-channel allow-list, so the admin list could not filter 18 of
|
|
17
17
|
* the 27 real types and an admin literally could not allow-list
|
|
18
|
-
* `offer_received` or `payment_review` for email
|
|
18
|
+
* `offer_received` or `payment_review` for email
|
|
19
19
|
* · `features/account/types/index.ts` (5: order|offer|promo|system|message)
|
|
20
20
|
* — a different vocabulary entirely, on a `UserNotification` type with
|
|
21
21
|
* zero real consumers
|
|
@@ -195,7 +195,6 @@ export const notificationQueryHelpers = {
|
|
|
195
195
|
export const DEFAULT_NOTIFICATION_CHANNELS = {
|
|
196
196
|
inApp: { enabled: true, readOnly: true },
|
|
197
197
|
email: { enabled: false, minPriority: "normal" },
|
|
198
|
-
whatsapp: { enabled: false, minPriority: "high", otpEnabled: false },
|
|
199
198
|
sms: { enabled: false, minPriority: "high" },
|
|
200
199
|
};
|
|
201
200
|
const PRIORITY_ORDER = {
|
|
@@ -231,6 +230,7 @@ export const DEFAULT_SITE_SETTINGS_DATA = {
|
|
|
231
230
|
motto: "Your Marketplace, Your Rules",
|
|
232
231
|
theme: DEFAULT_SITE_SETTINGS_THEME,
|
|
233
232
|
payment: {
|
|
233
|
+
razorpayEnabled: false,
|
|
234
234
|
phonepeEnabled: false,
|
|
235
235
|
upiManualEnabled: true,
|
|
236
236
|
codEnabled: true,
|
|
@@ -243,8 +243,7 @@ export const DEFAULT_SITE_SETTINGS_DATA = {
|
|
|
243
243
|
// half-configured site.
|
|
244
244
|
messaging: {
|
|
245
245
|
emailEnabled: false,
|
|
246
|
-
|
|
247
|
-
dailyCeiling: { email: 80, whatsapp: 200, sms: 50 },
|
|
246
|
+
dailyCeiling: { email: 80, sms: 50 },
|
|
248
247
|
},
|
|
249
248
|
emi: {
|
|
250
249
|
enabled: false,
|
|
@@ -267,11 +266,9 @@ export const DEFAULT_SITE_SETTINGS_DATA = {
|
|
|
267
266
|
// The buyer add-ons ship ENABLED. They were `false`, which made
|
|
268
267
|
// StoreAddonsPicker return null on both the cart and checkout, so a fresh
|
|
269
268
|
// install had no add-ons at all and no indication why. `undefined` still
|
|
270
|
-
// means disabled everywhere (
|
|
269
|
+
// means disabled everywhere (computeGiftWrapFee et al. read
|
|
271
270
|
// `if (!rates.XEnabled) return 0`) — only the DEFAULT changed, so an
|
|
272
271
|
// existing siteSettings document keeps whatever it already stored.
|
|
273
|
-
whatsappNotifyFeeEnabled: true,
|
|
274
|
-
whatsappNotifyFee: 10,
|
|
275
272
|
giftWrapFeeEnabled: true,
|
|
276
273
|
giftWrapFee: 49,
|
|
277
274
|
shipmentProtectionFeeEnabled: true,
|
|
@@ -306,28 +303,53 @@ export const DEFAULT_SITE_SETTINGS_DATA = {
|
|
|
306
303
|
autoExtendWindowMinutes: 5,
|
|
307
304
|
settlementGracePeriodHours: 24,
|
|
308
305
|
},
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
306
|
+
/*
|
|
307
|
+
* 🛑 THIS BLOCK IS NOW LOAD-BEARING, and it was not before.
|
|
308
|
+
*
|
|
309
|
+
* `isListingTypeEnabled` treats a MISSING key as ENABLED, so while every
|
|
310
|
+
* value here was `true` the block was pure documentation — a type left out
|
|
311
|
+
* rendered anyway, which is how `art`, `stickers` and `spares` sat outside it
|
|
312
|
+
* for months with nothing breaking (Root Cause #58). Now that five types ship
|
|
313
|
+
* `false`, an omission is no longer harmless in one direction only: a new
|
|
314
|
+
* union member added to `ListingType` and forgotten here goes LIVE on every
|
|
315
|
+
* site, which is the opposite of the intended default.
|
|
316
|
+
*
|
|
317
|
+
* So: every member of `ListingType` gets an explicit line, including the ones
|
|
318
|
+
* that are `true`. `spares` was missing entirely and is spelled out now.
|
|
319
|
+
*
|
|
320
|
+
* 2026-10-20 — open for business: standard, spares, pre-order, art, stickers.
|
|
321
|
+
* The other five are built, routed and reachable by URL; what is switched off
|
|
322
|
+
* is the site OFFERING them. An admin flips any of them back in
|
|
323
|
+
* Admin → Site Settings → Listings without a deploy, which is the whole point
|
|
324
|
+
* of this living in settings rather than in a constant.
|
|
325
|
+
*
|
|
326
|
+
* ⚠️ A default only reaches a site that has never written its settings
|
|
327
|
+
* document. An existing deployment already has a singleton with the old
|
|
328
|
+
* all-true map, and changing this file does NOT rewrite it — flip those in the
|
|
329
|
+
* admin UI, or re-seed.
|
|
330
|
+
*/
|
|
314
331
|
listings: {
|
|
315
332
|
listingTypes: {
|
|
316
333
|
standard: true,
|
|
317
|
-
|
|
334
|
+
spares: true,
|
|
318
335
|
"pre-order": true,
|
|
319
|
-
"prize-draw": true,
|
|
320
|
-
classified: true,
|
|
321
|
-
"digital-code": true,
|
|
322
|
-
live: true,
|
|
323
336
|
art: true,
|
|
324
337
|
stickers: true,
|
|
338
|
+
auction: false,
|
|
339
|
+
"prize-draw": false,
|
|
340
|
+
classified: false,
|
|
341
|
+
"digital-code": false,
|
|
342
|
+
live: false,
|
|
325
343
|
},
|
|
344
|
+
// `bundle: false` is the admin-side switch for bundles, which are a
|
|
345
|
+
// CATEGORY type rather than a listing type — it is what hides the
|
|
346
|
+
// homepage's featured-bundles rail and the bundle tabs on category and
|
|
347
|
+
// brand pages. Same reasoning as the five listing types above.
|
|
326
348
|
categoryTypes: {
|
|
327
349
|
category: true,
|
|
328
350
|
sublisting: true,
|
|
329
351
|
brand: true,
|
|
330
|
-
bundle:
|
|
352
|
+
bundle: false,
|
|
331
353
|
},
|
|
332
354
|
},
|
|
333
355
|
shipping: {
|
|
@@ -42,6 +42,7 @@ import { GroupedListingsCarousel } from "../../grouped/components/GroupedListing
|
|
|
42
42
|
import { getGroupsWithItemsForProduct } from "../../../_internal/server/features/grouped/data.js";
|
|
43
43
|
import { SublistingCarouselSection } from "../../products/components/SublistingCarouselSection.js";
|
|
44
44
|
import { isFinalSale } from "../../products/constants/final-sale.js";
|
|
45
|
+
import { OwnerQuickActions } from "../../admin/components/OwnerQuickActions.js";
|
|
45
46
|
function toDescriptionHtml(raw) {
|
|
46
47
|
if (!raw)
|
|
47
48
|
return "";
|
|
@@ -49,8 +50,8 @@ function toDescriptionHtml(raw) {
|
|
|
49
50
|
return normalizeRichTextHtml(s);
|
|
50
51
|
}
|
|
51
52
|
function renderAuctionInfoPanel(props) {
|
|
52
|
-
const { productId, title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, buyNowAvailable, featured, freeShipping, condition, finalSale, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref } = props;
|
|
53
|
-
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_LIVE_BADGE, children: "\uD83C\uDFF7\uFE0F Live Auction" }), _jsx(AuctionLiveStatusBadge, { auctionEndDate: endDate, initialIsEnded: isEnded })] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), _jsxs(Div, { children: [_jsx(Text, { className: "mb-0.5", color: "muted", size: "xs", children: "Current bid" }), _jsx(LiveBidPrice, { productId: productId, currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "2xl", badgeSize: "sm" }), endDate && !isEnded && (_jsxs(Text, { className: "mt-1.5", color: "muted", size: "sm", children: ["Ends in ", _jsx(Span, { weight: "medium", color: "muted", children: _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" }) })] })), endDate && _jsxs(Text, { className: "mt-0.5", color: "faint", size: "xs", children: [isEnded ? "Ended" : "Ends", " ", endDate.toLocaleString()] })] }), buyNowAvailable && buyNowPrice !== null && (_jsxs(Row, { align: "center", gap: "sm", className: "border border-[var(--appkit-color-primary-200)] dark:border-[var(--appkit-color-primary-800)] bg-primary-50 dark:bg-primary-900/20", padding: "inlineSm", rounded: "lg", children: [_jsx(Span, { size: "xs", color: "muted", children: "Buy Now:" }), _jsx(Span, { size: "base", weight: "bold", className: "text-primary-700 dark:text-primary-300", children: _jsx(GatedPrice, { label: "Sign in to see the Buy Now price", children: formatCurrency(buyNowPrice, currency) }) }), _jsx(Span, { size: "xs", color: "faint", children: "\u2014 in \u201CPlace a bid\u201D" })] })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, condition: condition ?? undefined, finalSale: finalSale, labels: { featured: "Featured", fasterDelivery: "Faster Delivery", ratedSeller: "Rated Seller", condition: "Condition", conditionNew: "New", conditionUsed: "Used", conditionBroken: "For Parts", conditionRefurbished: "Refurbished", returnable: "Returnable", finalSale: "Final Sale", freeShipping: "Free Shipping", codAvailable: "Cash on Delivery", emiAvailable: "EMI Available", wishlistCount: (n) => `${n} wishlisted`, categoryProductCount: (n, cat) => `${n} in ${cat}` } }), (categoryName || category || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [category && _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-surface hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:text-primary-400", children: categoryName || category }), !category && categoryName && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: categoryName }), brand && brandSlug && _jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-surface hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:text-primary-400", children: brand }), brand && !brandSlug && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: brand })] })), productFeatures && features.length > 0 && _jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures }), !productFeatures && features.length > 0 && (_jsxs(Div, { border: "subtle", surface: "muted", padding: "inline", rounded: "xl", children: [_jsx(Text, { className: "mb-2 tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "About this item" }), _jsx(Ul, { spacing: "comfortable", size: "sm", color: "primary", children: features.map((f, i) => _jsxs(Li, { layout: "flex-start", gap: "2", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i)) })] })), descriptionHtml && _jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" }), safeSeller && (_jsx(Div, { className: `${__P.p3}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Listed by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && _jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" })] }) }))] }));
|
|
53
|
+
const { productId, storeId, title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, buyNowAvailable, featured, freeShipping, condition, finalSale, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref } = props;
|
|
54
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_LIVE_BADGE, children: "\uD83C\uDFF7\uFE0F Live Auction" }), _jsx(AuctionLiveStatusBadge, { auctionEndDate: endDate, initialIsEnded: isEnded })] }), _jsx(OwnerQuickActions, { resource: "PRODUCT", placement: "detail", recordLabel: title, storeId: storeId, editHref: String(ROUTES.STORE.PRODUCTS_EDIT(productId)) }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), _jsxs(Div, { children: [_jsx(Text, { className: "mb-0.5", color: "muted", size: "xs", children: "Current bid" }), _jsx(LiveBidPrice, { productId: productId, currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "2xl", badgeSize: "sm" }), endDate && !isEnded && (_jsxs(Text, { className: "mt-1.5", color: "muted", size: "sm", children: ["Ends in ", _jsx(Span, { weight: "medium", color: "muted", children: _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" }) })] })), endDate && _jsxs(Text, { className: "mt-0.5", color: "faint", size: "xs", children: [isEnded ? "Ended" : "Ends", " ", endDate.toLocaleString()] })] }), buyNowAvailable && buyNowPrice !== null && (_jsxs(Row, { align: "center", gap: "sm", className: "border border-[var(--appkit-color-primary-200)] dark:border-[var(--appkit-color-primary-800)] bg-primary-50 dark:bg-primary-900/20", padding: "inlineSm", rounded: "lg", children: [_jsx(Span, { size: "xs", color: "muted", children: "Buy Now:" }), _jsx(Span, { size: "base", weight: "bold", className: "text-primary-700 dark:text-primary-300", children: _jsx(GatedPrice, { label: "Sign in to see the Buy Now price", children: formatCurrency(buyNowPrice, currency) }) }), _jsx(Span, { size: "xs", color: "faint", children: "\u2014 in \u201CPlace a bid\u201D" })] })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, condition: condition ?? undefined, finalSale: finalSale, labels: { featured: "Featured", fasterDelivery: "Faster Delivery", ratedSeller: "Rated Seller", condition: "Condition", conditionNew: "New", conditionUsed: "Used", conditionBroken: "For Parts", conditionRefurbished: "Refurbished", returnable: "Returnable", finalSale: "Final Sale", freeShipping: "Free Shipping", codAvailable: "Cash on Delivery", emiAvailable: "EMI Available", wishlistCount: (n) => `${n} wishlisted`, categoryProductCount: (n, cat) => `${n} in ${cat}` } }), (categoryName || category || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [category && _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-surface hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:text-primary-400", children: categoryName || category }), !category && categoryName && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: categoryName }), brand && brandSlug && _jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-surface hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:text-primary-400", children: brand }), brand && !brandSlug && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: brand })] })), productFeatures && features.length > 0 && _jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures }), !productFeatures && features.length > 0 && (_jsxs(Div, { border: "subtle", surface: "muted", padding: "inline", rounded: "xl", children: [_jsx(Text, { className: "mb-2 tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "About this item" }), _jsx(Ul, { spacing: "comfortable", size: "sm", color: "primary", children: features.map((f, i) => _jsxs(Li, { layout: "flex-start", gap: "2", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i)) })] })), descriptionHtml && _jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" }), safeSeller && (_jsx(Div, { className: `${__P.p3}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Listed by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && _jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" })] }) }))] }));
|
|
54
55
|
}
|
|
55
56
|
/**
|
|
56
57
|
* Store reviews for the auction's seller.
|
|
@@ -215,7 +216,7 @@ export async function AuctionDetailPageView({ id, initialAuction, onPlaceBid, on
|
|
|
215
216
|
price: typeof p.price === "number" ? p.price : undefined,
|
|
216
217
|
storeId: typeof p.storeId === "string" ? p.storeId : undefined,
|
|
217
218
|
storeName: typeof p.storeName === "string" ? p.storeName : undefined,
|
|
218
|
-
} }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.AUCTIONS), className: CLS_BREADCRUMB_LINK, children: "Auctions" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(AuctionDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, video: productVideo, productName: title })), renderInfo: () => renderAuctionInfoPanel({ productId: String(product.id), title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, buyNowAvailable, featured, freeShipping, condition, finalSale: isFinalSale(p), category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref }), renderBidForm: () => onPlaceBid ? (_jsx(Stack, { id: "auction-bid-form", gap: "3", children: _jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [_jsxs(Row, { justify: "between", align: "baseline", children: [_jsx(LiveBidPrice, { productId: String(product.id), currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "xl", badgeSize: "xs" }), !isEnded && (_jsx(LiveMinIncrement, { productId: String(product.id), currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, tiers: bidIncrementTiers, minBidIncrementOverride: minBidIncrementOverride }))] }), !isEnded && endDate && (_jsxs(Text, { align: "center", size: "xs", color: "muted", children: ["Ends in ", _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" })] })), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, tiers: bidIncrementTiers, minBidIncrementOverride: minBidIncrementOverride, currency: currency, isEnded: isEnded, auctionEndDate: endDate, buyNowPrice: buyNowPrice, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] }) })) : (
|
|
219
|
+
} }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.AUCTIONS), className: CLS_BREADCRUMB_LINK, children: "Auctions" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(AuctionDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, video: productVideo, productName: title })), renderInfo: () => renderAuctionInfoPanel({ productId: String(product.id), storeId: product.storeId ? String(product.storeId) : undefined, title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, buyNowAvailable, featured, freeShipping, condition, finalSale: isFinalSale(p), category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref }), renderBidForm: () => onPlaceBid ? (_jsx(Stack, { id: "auction-bid-form", gap: "3", children: _jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [_jsxs(Row, { justify: "between", align: "baseline", children: [_jsx(LiveBidPrice, { productId: String(product.id), currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "xl", badgeSize: "xs" }), !isEnded && (_jsx(LiveMinIncrement, { productId: String(product.id), currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, tiers: bidIncrementTiers, minBidIncrementOverride: minBidIncrementOverride }))] }), !isEnded && endDate && (_jsxs(Text, { align: "center", size: "xs", color: "muted", children: ["Ends in ", _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" })] })), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, tiers: bidIncrementTiers, minBidIncrementOverride: minBidIncrementOverride, currency: currency, isEnded: isEnded, auctionEndDate: endDate, buyNowPrice: buyNowPrice, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] }) })) : (
|
|
219
220
|
/* Read-only bid panel — shown when no bid action is wired (preview/demo) */
|
|
220
221
|
_jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "md", rounded: "xl", surface: "muted", children: [_jsx(Stack, { gap: "xs", children: _jsx(Text, { size: "xs", color: "muted", children: _jsxs(GatedPrice, { label: "Sign in to see the starting bid", children: ["Starting bid: ", formatCurrency(startingBid, currency), !isEnded && _jsxs(_Fragment, { children: [" \u00B7 min increment ", formatCurrency(initialMinBidIncrement, currency)] })] }) }) }), _jsxs(Stack, { gap: "sm", children: [_jsx(Input, { type: "number",
|
|
221
222
|
// The minimum is stated in the gated line above rather than
|
|
@@ -74,5 +74,15 @@ export interface MarketplaceAuctionCardProps {
|
|
|
74
74
|
onNavigate?: (href: string) => void;
|
|
75
75
|
wishlistActions?: WishlistToggleActions;
|
|
76
76
|
labels?: MarketplaceAuctionCardLabels;
|
|
77
|
+
/**
|
|
78
|
+
* Owner-only pencil / clone / bin. Pass `<OwnerQuickActions>`; it renders
|
|
79
|
+
* nothing for a visitor who does not own the record.
|
|
80
|
+
*
|
|
81
|
+
* 🛑 Rendered OUTSIDE this card's own anchor, as a sibling. A `<button>`
|
|
82
|
+
* inside an `<a>` is invalid HTML that browsers resolve by closing the
|
|
83
|
+
* anchor early, which breaks the CARD's navigation rather than just the
|
|
84
|
+
* button's — `stopPropagation` stops a click but cannot fix a parse.
|
|
85
|
+
*/
|
|
86
|
+
renderOwnerActions?: () => React.ReactNode;
|
|
77
87
|
}
|
|
78
|
-
export declare function MarketplaceAuctionCard({ product, buyoutPrice, className, variant, selectable, isSelected, onSelect, inWishlist: initialInWishlist, href, hrefBuilder, onNavigate, wishlistActions, labels, }: MarketplaceAuctionCardProps): import("react").JSX.Element;
|
|
88
|
+
export declare function MarketplaceAuctionCard({ product, buyoutPrice, className, variant, selectable, isSelected, onSelect, inWishlist: initialInWishlist, href, hrefBuilder, onNavigate, wishlistActions, labels, renderOwnerActions, }: MarketplaceAuctionCardProps): import("react").JSX.Element;
|
|
@@ -94,7 +94,7 @@ function formatCountdownLabel(remaining, endedLabel) {
|
|
|
94
94
|
return `${hours}h ${minutes}m ${seconds}s`;
|
|
95
95
|
return `${minutes}m ${seconds}s`;
|
|
96
96
|
}
|
|
97
|
-
export function MarketplaceAuctionCard({ product, buyoutPrice, className = "", variant = "grid", selectable = false, isSelected = false, onSelect, inWishlist: initialInWishlist = false, href, hrefBuilder, onNavigate, wishlistActions, labels, }) {
|
|
97
|
+
export function MarketplaceAuctionCard({ product, buyoutPrice, className = "", variant = "grid", selectable = false, isSelected = false, onSelect, inWishlist: initialInWishlist = false, href, hrefBuilder, onNavigate, wishlistActions, labels, renderOwnerActions, }) {
|
|
98
98
|
const router = useRouter();
|
|
99
99
|
const mergedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
100
100
|
const baseVariant = variant === "list" ? "list" : "grid";
|
|
@@ -180,5 +180,12 @@ export function MarketplaceAuctionCard({ product, buyoutPrice, className = "", v
|
|
|
180
180
|
remaining, resolvedBuyoutPrice, inWishlist, wishlistLoading, wishlistActions,
|
|
181
181
|
countdownClass, mergedLabels, handleWishlist, handleNavigate,
|
|
182
182
|
};
|
|
183
|
-
|
|
183
|
+
const card = (_jsxs(BaseListingCard, { isSelected: isSelected, isDisabled: isEnded, variant: baseVariant, className: className, onMouseDown: !isSelected ? longPress.onMouseDown : undefined, onMouseUp: !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: !isSelected ? longPress.onTouchEnd : undefined, onTouchCancel: !isSelected ? longPress.onTouchCancel : undefined, children: [renderAuctionCardHero({ product, auctionHref, currentSrc, hovered, images, imageIndex, hasVideo, isEnded, isEndingSoon, isSelected, selectable, mergedLabels, onSelect, setHovered, handleSelect }), _jsx(BaseListingCard.Info, { variant: baseVariant, children: baseVariant === "list" ? renderAuctionCardInfoList(cardInfoProps) : renderAuctionCardInfoGrid(cardInfoProps) })] }));
|
|
184
|
+
if (!renderOwnerActions)
|
|
185
|
+
return card;
|
|
186
|
+
/*
|
|
187
|
+
* Sibling of the card, never a child — see the prop's docstring. `z-20`
|
|
188
|
+
* clears this card's own overlays, which all sit at `z-10`.
|
|
189
|
+
*/
|
|
190
|
+
return (_jsxs(Div, { className: "relative h-full", children: [card, _jsx(Div, { className: "absolute right-2 bottom-2 z-20", children: renderOwnerActions() })] }));
|
|
184
191
|
}
|
|
@@ -44,6 +44,6 @@ export interface CheckoutValueOtpDoc {
|
|
|
44
44
|
* making the whole feature look broken.
|
|
45
45
|
*/
|
|
46
46
|
export declare function enforceCheckoutValueOtpRateLimit(db: Db, uid: string): Promise<void>;
|
|
47
|
-
/** Build the checkout-value OTP
|
|
47
|
+
/** Build the checkout-value OTP message body. */
|
|
48
48
|
export declare function buildCheckoutValueOtpMessage(code: string, siteName: string): string;
|
|
49
49
|
export {};
|
|
@@ -75,7 +75,7 @@ export async function enforceCheckoutValueOtpRateLimit(db, uid) {
|
|
|
75
75
|
tx.set(metaRef, { lastSentAt: new Date() }, { merge: true });
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
|
-
/** Build the checkout-value OTP
|
|
78
|
+
/** Build the checkout-value OTP message body. */
|
|
79
79
|
export function buildCheckoutValueOtpMessage(code, siteName) {
|
|
80
80
|
return (`*${siteName} — Order Verification*\n\n` +
|
|
81
81
|
`Your verification code for this high-value order is: *${code}*\n\n` +
|
|
@@ -67,8 +67,18 @@ export declare const ROUTE_PERMISSION_MAP: Record<string, Permission>;
|
|
|
67
67
|
*
|
|
68
68
|
* Defaults on store creation: ["suggest_brands", "create_coupons"]
|
|
69
69
|
*/
|
|
70
|
-
export type StoreCapability = "host_auctions" | "host_preorders" | "create_categories" | "suggest_brands" | "create_coupons" | "bulk_listing_import" | "extended_return_window" | "verified_seller" | "featured_placement" | "promotional_banner" | "priority_support" | "multiple_stores" | "custom_store_slug" | "api_access" | "lower_commission_rate" | "early_access_features" | "advanced_analytics"
|
|
71
|
-
|
|
70
|
+
export type StoreCapability = "advanced_listing_types" | "host_auctions" | "host_preorders" | "create_categories" | "suggest_brands" | "create_coupons" | "bulk_listing_import" | "extended_return_window" | "verified_seller" | "featured_placement" | "promotional_banner" | "priority_support" | "multiple_stores" | "custom_store_slug" | "api_access" | "lower_commission_rate" | "early_access_features" | "advanced_analytics";
|
|
71
|
+
export declare const ALL_STORE_CAPABILITIES: StoreCapability[];
|
|
72
|
+
/** Narrow an untrusted string to a real capability. */
|
|
73
|
+
export declare function isStoreCapability(value: unknown): value is StoreCapability;
|
|
74
|
+
/**
|
|
75
|
+
* Default capabilities granted to every new store on creation.
|
|
76
|
+
*
|
|
77
|
+
* 🛑 THE one definition. `AdminStoreEditorView` hardcoded its own copy, which
|
|
78
|
+
* is also the `??` fallback on its load path — so the two could disagree about
|
|
79
|
+
* what a store with no `capabilities` field is entitled to, and the admin
|
|
80
|
+
* editor's copy is the one that gets SAVED BACK. Import this; never restate it.
|
|
81
|
+
*/
|
|
72
82
|
export declare const DEFAULT_STORE_CAPABILITIES: StoreCapability[];
|
|
73
83
|
/**
|
|
74
84
|
* Check whether a permission array includes a required permission.
|
|
@@ -87,6 +97,23 @@ export declare function hasAllPermissions(permissions: Permission[], required: P
|
|
|
87
97
|
* Check whether a store's capabilities include a specific capability.
|
|
88
98
|
*/
|
|
89
99
|
export declare function hasCapability(capabilities: StoreCapability[], required: StoreCapability): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Resolve a store's stored `capabilities` array to its effective set.
|
|
102
|
+
*
|
|
103
|
+
* 🛑 The PURE half of `getStoreCapabilities`, extracted so a caller that
|
|
104
|
+
* already holds the store document does not pay a second Firestore read to ask
|
|
105
|
+
* what it is entitled to (Rule #6). Several routes were reading
|
|
106
|
+
* `store.capabilities ?? []` inline for exactly that reason, which made them a
|
|
107
|
+
* second, divergent reader of the field — they skipped the default merge, so
|
|
108
|
+
* they and `getStoreCapabilities` could disagree about the same store.
|
|
109
|
+
*
|
|
110
|
+
* Note the empty-array case falls back to the defaults, matching
|
|
111
|
+
* `getStoreCapabilities`' long-standing `raw.length > 0` test. That is
|
|
112
|
+
* arguably the wrong call — an admin who deliberately unticks everything gets
|
|
113
|
+
* the defaults handed back — but it is the established behaviour and changing
|
|
114
|
+
* it would silently re-grant capabilities on stores that had them removed.
|
|
115
|
+
*/
|
|
116
|
+
export declare function resolveStoreCapabilities(raw: readonly string[] | null | undefined): StoreCapability[];
|
|
90
117
|
/**
|
|
91
118
|
* Groups permission prefixes into human-labeled domains for display —
|
|
92
119
|
* used by the employee-permission editor and the read-only permissions
|
|
@@ -319,7 +319,52 @@ export const ROUTE_PERMISSION_MAP = {
|
|
|
319
319
|
"/admin/maintenance/payment-rollbacks": "admin:maintenance:view-payment-rollbacks",
|
|
320
320
|
"/admin/maintenance/analysis": "admin:maintenance:run-analysis",
|
|
321
321
|
};
|
|
322
|
-
/**
|
|
322
|
+
/**
|
|
323
|
+
* Every capability, as a runtime value.
|
|
324
|
+
*
|
|
325
|
+
* 🛑 `Record<StoreCapability, true>` rather than a plain array, for the same
|
|
326
|
+
* reason `ALL_LISTING_TYPES_MAP` is: adding a member to the union without
|
|
327
|
+
* adding it here is then a COMPILE error, not a silent omission. This is what
|
|
328
|
+
* lets `capabilities` be validated as a `z.enum` instead of
|
|
329
|
+
* `z.array(z.string())` — which accepted any string, so a typo'd capability
|
|
330
|
+
* saved successfully and the gate it was meant to open never opened, silently.
|
|
331
|
+
*
|
|
332
|
+
* It also backs the admin editor's grouping, so a new capability cannot be
|
|
333
|
+
* granted in code and remain unreachable from the admin UI.
|
|
334
|
+
*/
|
|
335
|
+
const STORE_CAPABILITY_MAP = {
|
|
336
|
+
advanced_listing_types: true,
|
|
337
|
+
host_auctions: true,
|
|
338
|
+
host_preorders: true,
|
|
339
|
+
create_categories: true,
|
|
340
|
+
suggest_brands: true,
|
|
341
|
+
create_coupons: true,
|
|
342
|
+
bulk_listing_import: true,
|
|
343
|
+
extended_return_window: true,
|
|
344
|
+
verified_seller: true,
|
|
345
|
+
featured_placement: true,
|
|
346
|
+
promotional_banner: true,
|
|
347
|
+
priority_support: true,
|
|
348
|
+
multiple_stores: true,
|
|
349
|
+
custom_store_slug: true,
|
|
350
|
+
api_access: true,
|
|
351
|
+
lower_commission_rate: true,
|
|
352
|
+
early_access_features: true,
|
|
353
|
+
advanced_analytics: true,
|
|
354
|
+
};
|
|
355
|
+
export const ALL_STORE_CAPABILITIES = Object.keys(STORE_CAPABILITY_MAP);
|
|
356
|
+
/** Narrow an untrusted string to a real capability. */
|
|
357
|
+
export function isStoreCapability(value) {
|
|
358
|
+
return typeof value === "string" && value in STORE_CAPABILITY_MAP;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Default capabilities granted to every new store on creation.
|
|
362
|
+
*
|
|
363
|
+
* 🛑 THE one definition. `AdminStoreEditorView` hardcoded its own copy, which
|
|
364
|
+
* is also the `??` fallback on its load path — so the two could disagree about
|
|
365
|
+
* what a store with no `capabilities` field is entitled to, and the admin
|
|
366
|
+
* editor's copy is the one that gets SAVED BACK. Import this; never restate it.
|
|
367
|
+
*/
|
|
323
368
|
export const DEFAULT_STORE_CAPABILITIES = [
|
|
324
369
|
"suggest_brands",
|
|
325
370
|
"create_coupons",
|
|
@@ -352,6 +397,27 @@ export function hasAllPermissions(permissions, required) {
|
|
|
352
397
|
export function hasCapability(capabilities, required) {
|
|
353
398
|
return capabilities.includes(required);
|
|
354
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* Resolve a store's stored `capabilities` array to its effective set.
|
|
402
|
+
*
|
|
403
|
+
* 🛑 The PURE half of `getStoreCapabilities`, extracted so a caller that
|
|
404
|
+
* already holds the store document does not pay a second Firestore read to ask
|
|
405
|
+
* what it is entitled to (Rule #6). Several routes were reading
|
|
406
|
+
* `store.capabilities ?? []` inline for exactly that reason, which made them a
|
|
407
|
+
* second, divergent reader of the field — they skipped the default merge, so
|
|
408
|
+
* they and `getStoreCapabilities` could disagree about the same store.
|
|
409
|
+
*
|
|
410
|
+
* Note the empty-array case falls back to the defaults, matching
|
|
411
|
+
* `getStoreCapabilities`' long-standing `raw.length > 0` test. That is
|
|
412
|
+
* arguably the wrong call — an admin who deliberately unticks everything gets
|
|
413
|
+
* the defaults handed back — but it is the established behaviour and changing
|
|
414
|
+
* it would silently re-grant capabilities on stores that had them removed.
|
|
415
|
+
*/
|
|
416
|
+
export function resolveStoreCapabilities(raw) {
|
|
417
|
+
if (Array.isArray(raw) && raw.length > 0)
|
|
418
|
+
return raw;
|
|
419
|
+
return [...DEFAULT_STORE_CAPABILITIES];
|
|
420
|
+
}
|
|
355
421
|
// ============================================================================
|
|
356
422
|
// PERMISSION DOMAINS — display grouping (S-ADMIN-7, 2026-08-19)
|
|
357
423
|
// ============================================================================
|
|
@@ -17,5 +17,15 @@ export interface BlogFeaturedCardProps {
|
|
|
17
17
|
showAuthor?: boolean;
|
|
18
18
|
/** Show the cover image. Defaults to `true`. */
|
|
19
19
|
showThumbnail?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Owner-only pencil / clone / bin, for an admin or whoever owns this record.
|
|
22
|
+
* Pass `<OwnerQuickActions>`; it renders nothing for anyone else.
|
|
23
|
+
*
|
|
24
|
+
* 🛑 Rendered OUTSIDE this card's own `<Link>`/`<TextLink>`, as a sibling.
|
|
25
|
+
* A `<button>` inside an `<a>` is invalid HTML that browsers resolve by
|
|
26
|
+
* closing the anchor early, which breaks the CARD's navigation rather than
|
|
27
|
+
* just the button's — `stopPropagation` stops a click but cannot fix a parse.
|
|
28
|
+
*/
|
|
29
|
+
renderOwnerActions?: () => React.ReactNode;
|
|
20
30
|
}
|
|
21
|
-
export declare function BlogFeaturedCard({ post, href, labels, className, selectable, isSelected, onSelect, showReadTime, showAuthor, showThumbnail, }: BlogFeaturedCardProps): React.JSX.Element;
|
|
31
|
+
export declare function BlogFeaturedCard({ post, href, labels, className, selectable, isSelected, onSelect, showReadTime, showAuthor, showThumbnail, renderOwnerActions, }: BlogFeaturedCardProps): React.JSX.Element;
|
|
@@ -20,7 +20,7 @@ const CATEGORY_BADGE = {
|
|
|
20
20
|
community: "bg-warning-surface text-warning dark:bg-warning-surface dark:text-warning",
|
|
21
21
|
};
|
|
22
22
|
const CLS_FEATURED_BADGE = "bg-warning-surface text-warning dark:bg-warning-surface dark:text-warning px-[var(--appkit-space-2)] py-[var(--appkit-space-0-5)] rounded-full";
|
|
23
|
-
export function BlogFeaturedCard({ post, href, labels = {}, className = "", selectable = false, isSelected = false, onSelect, showReadTime = true, showAuthor = true, showThumbnail = true, }) {
|
|
23
|
+
export function BlogFeaturedCard({ post, href, labels = {}, className = "", selectable = false, isSelected = false, onSelect, showReadTime = true, showAuthor = true, showThumbnail = true, renderOwnerActions, }) {
|
|
24
24
|
const longPress = useLongPress(() => onSelect?.(post.id, !isSelected));
|
|
25
25
|
const safeTitle = post.title?.trim() || "Untitled post";
|
|
26
26
|
const coverImageUrl = showThumbnail ? getMediaUrl(post.coverImage) : undefined;
|
|
@@ -31,5 +31,12 @@ export function BlogFeaturedCard({ post, href, labels = {}, className = "", sele
|
|
|
31
31
|
day: "numeric",
|
|
32
32
|
})
|
|
33
33
|
: "";
|
|
34
|
-
|
|
34
|
+
const card = (_jsxs(Article, { rounded: "xl", shadow: "hover-md", className: `group relative h-full overflow-hidden border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${isSelected ? "ring-2 ring-primary outline outline-2 outline-primary" : ""} ${className}`, onMouseDown: onSelect && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: onSelect && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: onSelect && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: onSelect && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: onSelect && !isSelected ? longPress.onTouchEnd : undefined, onTouchCancel: onSelect && !isSelected ? longPress.onTouchCancel : undefined, children: [onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(post.id, !isSelected); }, label: isSelected ? "Deselect post" : "Select post", 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(TextLink, { href: href, layout: "flex-col", className: "h-full", children: [showThumbnail && (_jsx(Div, { className: `relative aspect-video ${__O.hidden} flex-shrink-0`, children: coverImageUrl ? (_jsx(MediaImage, { src: coverImageUrl, alt: safeTitle, size: "card", className: "transition-transform duration-300 group-hover:scale-105" })) : (_jsx(Div, { role: "img", "aria-label": safeTitle, surface: "muted", className: "h-full w-full" })) })), _jsxs(Stack, { className: `flex-1 ${__P.p4}`, children: [_jsxs(Row, { gap: "xs", className: "mb-2", wrap: true, children: [_jsx(Span, { size: "xs", weight: "medium", className: `inline-block capitalize ${CATEGORY_BADGE[post.category] ?? "bg-[var(--appkit-color-bg)] text-[var(--appkit-color-text-muted)]"}`, rounded: "full", padding: "pill-xs", children: post.category }), post.isFeatured && (_jsx(Span, { size: "xs", weight: "medium", className: CLS_FEATURED_BADGE, children: labels.featuredBadge ?? "Featured" }))] }), _jsx(Heading, { level: 3, className: `leading-snug mb-1`, color: "primary", truncate: 2, size: "base", weight: "semibold", children: safeTitle }), post.excerpt && (_jsx(Text, { className: `mb-3`, color: "muted", truncate: 2, size: "sm", children: post.excerpt })), _jsxs(Row, { color: "muted", textSize: "xs", className: "mt-auto", gap: "3", wrap: true, children: [showAuthor && post.authorName && (_jsx(Span, { children: safeDisplayName(post.authorName, "Author") })), showReadTime && post.readTimeMinutes != null && (_jsxs(Span, { children: [post.readTimeMinutes, " ", labels.readTime ?? "min read"] })), date && _jsx(Span, { children: date })] })] })] })] }));
|
|
35
|
+
if (!renderOwnerActions)
|
|
36
|
+
return card;
|
|
37
|
+
/*
|
|
38
|
+
* Sibling of the card, never a child — see the prop's docstring. `z-20`
|
|
39
|
+
* beats this card's own overlays, which all sit at `z-10`.
|
|
40
|
+
*/
|
|
41
|
+
return (_jsxs(Div, { className: "relative h-full", children: [card, _jsx(Div, { className: "absolute right-2 top-2 z-20", children: renderOwnerActions() })] }));
|
|
35
42
|
}
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { Aside, Button, Div, Heading, Row, Span, Stack, Text, TextLink } from "../../../ui/index.js";
|
|
4
4
|
import { MediaImage } from "../../media/MediaImage.js";
|
|
5
5
|
import { QuantityStepper } from "../../../ui/components/QuantityStepper.js";
|
|
6
|
+
import { GatedPrice } from "../../../ui/components/GatedPrice.js";
|
|
6
7
|
import { formatCurrency } from "../../../utils/number.formatter.js";
|
|
7
8
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry.js";
|
|
8
9
|
import { useHandMode } from "../../../_internal/client/hand-mode/index.js";
|
|
@@ -19,11 +20,11 @@ export function CartItemRow({ item, onQtyChange, onRemove, href, isOutOfStock =
|
|
|
19
20
|
return (_jsxs(Div, { layout: "flex", gap: isRow ? "3" : "4", surface: isRow ? "none" : "card", padding: isRow ? "none" : "sm", className: `min-w-0 transition-opacity ${isOutOfStock ? "opacity-60" : ""}`, children: [_jsx(Div, { surface: "muted", className: `relative ${isRow ? "h-16 w-16" : "h-20 w-20"} flex-shrink-0 ${__O.hidden}`, rounded: "lg", children: item.meta.image && (_jsx(MediaImage, { src: item.meta.image, alt: item.meta.title, size: "thumbnail" })) }), _jsxs(Stack, { justify: "between", className: "flex-1 min-w-0", children: [_jsxs(Row, { gap: "xs", className: "min-w-0", align: "start", children: [href ? (_jsx(TextLink, { href: href, target: "_blank", rel: "noopener noreferrer", weight: "medium", className: "text-[var(--appkit-color-text)] hover:underline underline-offset-2 line-clamp-2", children: item.meta.title })) : (_jsx(Text, { truncate: 2, weight: "medium", color: "primary", children: item.meta.title })), isOutOfStock && (_jsx(Span, { padding: "pill-2xs", color: "error", surface: "danger-surface", weight: "semibold", className: "flex-shrink-0 text-[10px] tracking-wide", rounded: "default", transform: "uppercase", children: "Out of Stock" }))] }), item.meta.attributes &&
|
|
20
21
|
Object.keys(item.meta.attributes).length > 0 && (_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", truncate: 1, children: Object.entries(item.meta.attributes)
|
|
21
22
|
.map(([k, v]) => `${k}: ${v}`)
|
|
22
|
-
.join(", ") })), _jsxs(Row, { justify: "between", className: "min-w-0", gap: "sm", children: [_jsx(Text, { className: "text-[var(--appkit-color-text)]", weight: "semibold", children: formatCurrency(item.meta.price * item.quantity, item.meta.currency) }), onQtyChange && !isOutOfStock && (_jsx(QuantityStepper, { value: item.quantity, onChange: (next) => onQtyChange(item.id, next), min: 1, ariaLabel: `Quantity for ${item.meta.title}`, decrementLabel: ACTIONS.CART["decrease-quantity"].ariaLabel, incrementLabel: ACTIONS.CART["increase-quantity"].ariaLabel })), isOutOfStock && (_jsxs(Span, { size: "xs", color: "muted", children: ["Qty: ", item.quantity] }))] })] }), onRemove && (_jsx(Button, { onClick: () => onRemove(item.id), variant: "ghost", size: "sm", "aria-label": ACTIONS.CART["remove-item"].ariaLabel, className: CLS_REMOVE_BTN, children: "\u2715" }))] }));
|
|
23
|
+
.join(", ") })), _jsxs(Row, { justify: "between", className: "min-w-0", gap: "sm", children: [_jsx(Text, { className: "text-[var(--appkit-color-text)]", weight: "semibold", children: _jsx(GatedPrice, { children: formatCurrency(item.meta.price * item.quantity, item.meta.currency) }) }), onQtyChange && !isOutOfStock && (_jsx(QuantityStepper, { value: item.quantity, onChange: (next) => onQtyChange(item.id, next), min: 1, ariaLabel: `Quantity for ${item.meta.title}`, decrementLabel: ACTIONS.CART["decrease-quantity"].ariaLabel, incrementLabel: ACTIONS.CART["increase-quantity"].ariaLabel })), isOutOfStock && (_jsxs(Span, { size: "xs", color: "muted", children: ["Qty: ", item.quantity] }))] })] }), onRemove && (_jsx(Button, { onClick: () => onRemove(item.id), variant: "ghost", size: "sm", "aria-label": ACTIONS.CART["remove-item"].ariaLabel, className: CLS_REMOVE_BTN, children: "\u2715" }))] }));
|
|
23
24
|
}
|
|
24
25
|
export function CartDrawer({ isOpen, onClose, items, subtotal = 0, currency, isLoading, onQtyChange, onRemove, onCheckout, labels = {}, }) {
|
|
25
26
|
const { hand } = useHandMode();
|
|
26
27
|
if (!isOpen)
|
|
27
28
|
return null;
|
|
28
|
-
return (_jsxs(_Fragment, { children: [_jsx(Div, { surface: "overlay-xs", role: "presentation", className: "fixed inset-0 z-40", onClick: onClose }), _jsx(Aside, { surface: "sidePanel", shadow: "xl", className: `fixed inset-y-0 ${hand === "left" ? "left-0" : "right-0"} z-50 w-full max-w-sm`, children: _jsxs(Stack, { className: "h-full", children: [_jsxs(Row, { justify: "between", border: "bottom", className: __P.p4, reverse: hand === "left", children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", children: labels.title ?? "Cart" }), _jsx(Button, { onClick: onClose, variant: "ghost", size: "sm", "aria-label": "Close cart", textColor: "faint", className: "hover:text-[var(--appkit-color-text)] dark:hover:text-[var(--appkit-color-text)]", children: "\u2715" })] }), _jsx(Stack, { className: `flex-1 ${__O.yAuto} ${__P.p4}`, gap: "3", children: isLoading ? (_jsx(Row, { justify: "center", padding: "y-3xl", children: _jsx(Div, { className: "h-8 w-8 animate-spin border-2 border-neutral-300 border-t-neutral-800 dark:border-t-zinc-200", rounded: "full" }) })) : items.length === 0 ? (_jsx(Text, { paddingY: "3xl", className: "text-[var(--appkit-color-text-muted)]", size: "sm", align: "center", children: labels.empty ?? "Your cart is empty" })) : (items.map((item) => (_jsx(CartItemRow, { item: item, onQtyChange: onQtyChange, onRemove: onRemove }, item.id)))) }), items.length > 0 && (_jsxs(Stack, { border: "top", className: __P.p4, gap: "md", children: [_jsxs(Row, { textSize: "sm", justify: "between", children: [_jsx(Span, { className: "text-[var(--appkit-color-text-muted)]", children: labels.subtotal ?? "Subtotal" }), _jsx(Span, { weight: "semibold", children: formatCurrency(subtotal, currency) })] }), onCheckout && (_jsx(Button, { rounded: "xl", onClick: onCheckout, variant: "primary", textSize: "sm", className: "w-full bg-neutral-900 py-[0.75rem] font-[600] text-white transition hover:bg-neutral-800", children: labels.checkout ?? ACTIONS.CART["checkout"].label }))] }))] }) })] }));
|
|
29
|
+
return (_jsxs(_Fragment, { children: [_jsx(Div, { surface: "overlay-xs", role: "presentation", className: "fixed inset-0 z-40", onClick: onClose }), _jsx(Aside, { surface: "sidePanel", shadow: "xl", className: `fixed inset-y-0 ${hand === "left" ? "left-0" : "right-0"} z-50 w-full max-w-sm`, children: _jsxs(Stack, { className: "h-full", children: [_jsxs(Row, { justify: "between", border: "bottom", className: __P.p4, reverse: hand === "left", children: [_jsx(Heading, { level: 2, size: "lg", weight: "semibold", children: labels.title ?? "Cart" }), _jsx(Button, { onClick: onClose, variant: "ghost", size: "sm", "aria-label": "Close cart", textColor: "faint", className: "hover:text-[var(--appkit-color-text)] dark:hover:text-[var(--appkit-color-text)]", children: "\u2715" })] }), _jsx(Stack, { className: `flex-1 ${__O.yAuto} ${__P.p4}`, gap: "3", children: isLoading ? (_jsx(Row, { justify: "center", padding: "y-3xl", children: _jsx(Div, { className: "h-8 w-8 animate-spin border-2 border-neutral-300 border-t-neutral-800 dark:border-t-zinc-200", rounded: "full" }) })) : items.length === 0 ? (_jsx(Text, { paddingY: "3xl", className: "text-[var(--appkit-color-text-muted)]", size: "sm", align: "center", children: labels.empty ?? "Your cart is empty" })) : (items.map((item) => (_jsx(CartItemRow, { item: item, onQtyChange: onQtyChange, onRemove: onRemove }, item.id)))) }), items.length > 0 && (_jsxs(Stack, { border: "top", className: __P.p4, gap: "md", children: [_jsxs(Row, { textSize: "sm", justify: "between", children: [_jsx(Span, { className: "text-[var(--appkit-color-text-muted)]", children: labels.subtotal ?? "Subtotal" }), _jsx(Span, { weight: "semibold", children: _jsx(GatedPrice, { children: formatCurrency(subtotal, currency) }) })] }), onCheckout && (_jsx(Button, { rounded: "xl", onClick: onCheckout, variant: "primary", textSize: "sm", className: "w-full bg-neutral-900 py-[0.75rem] font-[600] text-white transition hover:bg-neutral-800", children: labels.checkout ?? ACTIONS.CART["checkout"].label }))] }))] }) })] }));
|
|
29
30
|
}
|