@mohasinac/appkit 4.53.2 → 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/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 -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/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
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useRef, useState, useEffect, useCallback, } from "react";
|
|
4
4
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
5
|
+
import { CarouselNav } from "./CarouselNav.js";
|
|
5
6
|
const BREAKPOINTS = [
|
|
6
7
|
["2xl", 1536],
|
|
7
8
|
["xl", 1280],
|
|
@@ -21,12 +22,23 @@ function resolvePerView(perView, containerWidth) {
|
|
|
21
22
|
}
|
|
22
23
|
return 1;
|
|
23
24
|
}
|
|
24
|
-
export function HorizontalScroller({ children, className = "", gap = 16, snapToItems, showArrows, arrowSize = "md", arrowStyle = "circle", showScrollbar, scrollContainerRef: externalRef, onScroll, items, renderItem, keyExtractor, perView, rows = 1, autoScroll, autoScrollInterval = 3500, minItemWidth, pauseOnHover = false, itemClassName = "", loop = false, }) {
|
|
25
|
+
export function HorizontalScroller({ children, className = "", gap = 16, snapToItems, showArrows, arrowSize = "md", arrowStyle = "circle", showScrollbar, scrollContainerRef: externalRef, onScroll, items, renderItem, keyExtractor, perView, rows = 1, autoScroll, autoScrollInterval = 3500, minItemWidth, pauseOnHover = false, itemClassName = "", loop = false, showNav = false, navAriaLabel, }) {
|
|
25
26
|
const [itemWidth, setItemWidth] = useState(undefined);
|
|
26
27
|
const [colCount, setColCount] = useState(typeof perView === "number" ? perView : 3);
|
|
27
28
|
const [isPaused, setIsPaused] = useState(false);
|
|
28
29
|
const [atStart, setAtStart] = useState(true);
|
|
29
30
|
const [atEnd, setAtEnd] = useState(true);
|
|
31
|
+
/*
|
|
32
|
+
* Page position, for the optional `showNav` indicator.
|
|
33
|
+
*
|
|
34
|
+
* 🛑 Derived from scroll geometry, never stored as the source of truth: the
|
|
35
|
+
* rail is a native scroller and a user can drag it to any offset, so an index
|
|
36
|
+
* this component *decided* would disagree with where the content actually is
|
|
37
|
+
* the first time someone swipes. One page = one viewport width, matching what
|
|
38
|
+
* `scrollBy` already moves.
|
|
39
|
+
*/
|
|
40
|
+
const [pageIndex, setPageIndex] = useState(0);
|
|
41
|
+
const [pageCount, setPageCount] = useState(1);
|
|
30
42
|
const internalRef = useRef(null);
|
|
31
43
|
const containerRef = (externalRef ??
|
|
32
44
|
internalRef);
|
|
@@ -91,6 +103,11 @@ export function HorizontalScroller({ children, className = "", gap = 16, snapToI
|
|
|
91
103
|
return;
|
|
92
104
|
// 1px tolerance for sub-pixel rounding
|
|
93
105
|
const maxScroll = el.scrollWidth - el.clientWidth;
|
|
106
|
+
const width = el.clientWidth;
|
|
107
|
+
if (width > 0) {
|
|
108
|
+
setPageCount(Math.max(1, Math.ceil(el.scrollWidth / width)));
|
|
109
|
+
setPageIndex(Math.round(el.scrollLeft / width));
|
|
110
|
+
}
|
|
94
111
|
if (maxScroll <= 1) {
|
|
95
112
|
setAtStart(true);
|
|
96
113
|
setAtEnd(true);
|
|
@@ -99,6 +116,12 @@ export function HorizontalScroller({ children, className = "", gap = 16, snapToI
|
|
|
99
116
|
setAtStart(el.scrollLeft <= 1);
|
|
100
117
|
setAtEnd(el.scrollLeft >= maxScroll - 1);
|
|
101
118
|
}, [containerRef]);
|
|
119
|
+
const goToPage = useCallback((page) => {
|
|
120
|
+
const el = containerRef.current;
|
|
121
|
+
if (!el)
|
|
122
|
+
return;
|
|
123
|
+
el.scrollTo({ left: page * el.clientWidth, behavior: "smooth" });
|
|
124
|
+
}, [containerRef]);
|
|
102
125
|
const handleKeyDown = useCallback((e) => {
|
|
103
126
|
if (e.key === "ArrowRight") {
|
|
104
127
|
e.preventDefault();
|
|
@@ -196,6 +219,23 @@ export function HorizontalScroller({ children, className = "", gap = 16, snapToI
|
|
|
196
219
|
updateExtents();
|
|
197
220
|
onScroll?.();
|
|
198
221
|
};
|
|
222
|
+
/*
|
|
223
|
+
* The in-flow position indicator.
|
|
224
|
+
*
|
|
225
|
+
* 🛑 This is the fix for "no affordance on a phone", and it deliberately does
|
|
226
|
+
* NOT un-hide the absolute arrows below 640px. Those are `display:none` there
|
|
227
|
+
* for a documented reason — a 44px circle has nowhere to go over a
|
|
228
|
+
* single-column card, and `--with-arrows` reserves 2 × 44px of wrapper gutter
|
|
229
|
+
* to seat them, which on a narrow screen is width taken from the card itself.
|
|
230
|
+
* An in-flow control needs no gutter and no breakpoint, so the affordance
|
|
231
|
+
* exists at every width instead of appearing at one.
|
|
232
|
+
*
|
|
233
|
+
* No `progressMs`: a rail's autoScroll advances by 0.8 of a viewport rather
|
|
234
|
+
* than one page, so a fill timed to the interval would not line up with the
|
|
235
|
+
* segment it sits in. A position cue that is right beats a progress cue that
|
|
236
|
+
* is approximately right.
|
|
237
|
+
*/
|
|
238
|
+
const navNode = showNav && pageCount > 1 ? (_jsx(CarouselNav, { count: pageCount, index: pageIndex, onSelect: goToPage, onPrev: () => scrollBy(-1), onNext: () => scrollBy(1), tone: "on-surface", atStart: atStart, atEnd: atEnd, loop: loop, paused: isPaused, ariaLabel: navAriaLabel ?? "Scroll position", className: "appkit-hscroller__nav" })) : null;
|
|
199
239
|
if (showArrows) {
|
|
200
240
|
const prevDisabled = !loop && atStart;
|
|
201
241
|
const nextDisabled = !loop && atEnd;
|
|
@@ -206,9 +246,9 @@ export function HorizontalScroller({ children, className = "", gap = 16, snapToI
|
|
|
206
246
|
className,
|
|
207
247
|
]
|
|
208
248
|
.filter(Boolean)
|
|
209
|
-
.join(" "), tabIndex: 0, onKeyDown: handleKeyDown, ...interactionHandlers, "data-section": "horizontalscroller-div-511", children: [!arrowsHidden && (_jsx("button", { type: "button", onClick: () => scrollBy(-1), "aria-label": "Previous", "aria-disabled": prevDisabled || undefined, disabled: prevDisabled, className: `appkit-hscroller__arrow appkit-hscroller__arrow--prev appkit-hscroller__arrow--${arrowSize} ${arrowStyle === "full-height" ? "appkit-hscroller__arrow--full-height" : ""}`, children: _jsx(ChevronLeft, { className: "w-4 h-4" }) })), _jsx("div", { ref: containerRef, onScroll: combinedOnScroll, className: scrollerCls(snapToItems, showScrollbar), style: { gap: `${gap}px` }, "data-section": "horizontalscroller-div-512", children: content }), !arrowsHidden && (_jsx("button", { type: "button", onClick: () => scrollBy(1), "aria-label": "Next", "aria-disabled": nextDisabled || undefined, disabled: nextDisabled, className: `appkit-hscroller__arrow appkit-hscroller__arrow--next appkit-hscroller__arrow--${arrowSize} ${arrowStyle === "full-height" ? "appkit-hscroller__arrow--full-height" : ""}`, children: _jsx(ChevronRight, { className: "w-4 h-4" }) }))] }));
|
|
249
|
+
.join(" "), tabIndex: 0, onKeyDown: handleKeyDown, ...interactionHandlers, "data-section": "horizontalscroller-div-511", children: [!arrowsHidden && (_jsx("button", { type: "button", onClick: () => scrollBy(-1), "aria-label": "Previous", "aria-disabled": prevDisabled || undefined, disabled: prevDisabled, className: `appkit-hscroller__arrow appkit-hscroller__arrow--prev appkit-hscroller__arrow--${arrowSize} ${arrowStyle === "full-height" ? "appkit-hscroller__arrow--full-height" : ""}`, children: _jsx(ChevronLeft, { className: "w-4 h-4" }) })), _jsx("div", { ref: containerRef, onScroll: combinedOnScroll, className: scrollerCls(snapToItems, showScrollbar), style: { gap: `${gap}px` }, "data-section": "horizontalscroller-div-512", children: content }), !arrowsHidden && (_jsx("button", { type: "button", onClick: () => scrollBy(1), "aria-label": "Next", "aria-disabled": nextDisabled || undefined, disabled: nextDisabled, className: `appkit-hscroller__arrow appkit-hscroller__arrow--next appkit-hscroller__arrow--${arrowSize} ${arrowStyle === "full-height" ? "appkit-hscroller__arrow--full-height" : ""}`, children: _jsx(ChevronRight, { className: "w-4 h-4" }) })), navNode] }));
|
|
210
250
|
}
|
|
211
|
-
return (
|
|
251
|
+
return (_jsxs("div", { className: ["appkit-hscroller", className].filter(Boolean).join(" "), tabIndex: 0, onKeyDown: handleKeyDown, ...interactionHandlers, "data-section": "horizontalscroller-div-513", children: [_jsx("div", { ref: containerRef, onScroll: combinedOnScroll, className: scrollerCls(snapToItems, showScrollbar), style: { gap: `${gap}px` }, "data-section": "horizontalscroller-div-514", children: content }), navNode] }));
|
|
212
252
|
}
|
|
213
253
|
function scrollerCls(snapToItems, showScrollbar) {
|
|
214
254
|
return [
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
/** Below this there is nothing to arrange, so the caller should render nothing. */
|
|
3
3
|
export declare const MOSAIC_MIN = 3;
|
|
4
|
+
/**
|
|
5
|
+
* `band` — the category-hero strip this was built for (180/260px).
|
|
6
|
+
* `hero` — the homepage hero's right column, roughly double height.
|
|
7
|
+
*/
|
|
8
|
+
export type ImageMosaicSize = "band" | "hero";
|
|
4
9
|
export interface ImageMosaicProps {
|
|
5
10
|
/** Image URLs, most important first. Fewer than `MOSAIC_MIN` renders null. */
|
|
6
11
|
images: readonly string[];
|
|
7
12
|
/** Describes the SUBJECT, not the images — one alt for a decorative group. */
|
|
8
13
|
alt: string;
|
|
14
|
+
/** Height preset. Defaults to `"band"`, the original behaviour. */
|
|
15
|
+
size?: ImageMosaicSize;
|
|
9
16
|
className?: string;
|
|
10
17
|
}
|
|
11
18
|
/**
|
|
@@ -40,4 +47,4 @@ export interface ImageMosaicProps {
|
|
|
40
47
|
* breaks SSR/hydration agreement and makes the page feel unstable — and it is
|
|
41
48
|
* the shape Root Cause #25 is about on the seed side.
|
|
42
49
|
*/
|
|
43
|
-
export declare function ImageMosaic({ images, alt, className }: ImageMosaicProps): React.JSX.Element | null;
|
|
50
|
+
export declare function ImageMosaic({ images, alt, size, className }: ImageMosaicProps): React.JSX.Element | null;
|
|
@@ -37,10 +37,11 @@ export const MOSAIC_MIN = 3;
|
|
|
37
37
|
* breaks SSR/hydration agreement and makes the page feel unstable — and it is
|
|
38
38
|
* the shape Root Cause #25 is about on the seed side.
|
|
39
39
|
*/
|
|
40
|
-
export function ImageMosaic({ images, alt, className }) {
|
|
40
|
+
export function ImageMosaic({ images, alt, size = "band", className }) {
|
|
41
41
|
const tiles = images.filter(Boolean).slice(0, MOSAIC_MAX);
|
|
42
42
|
if (tiles.length < MOSAIC_MIN)
|
|
43
43
|
return null;
|
|
44
44
|
const [lead, ...rest] = tiles;
|
|
45
|
-
|
|
45
|
+
const sizeClass = size === "hero" ? "appkit-image-mosaic--hero" : "";
|
|
46
|
+
return (_jsxs(Div, { className: `appkit-image-mosaic ${sizeClass} ${className ?? ""}`, role: "img", "aria-label": alt, children: [_jsx(Div, { className: "appkit-image-mosaic__lead", children: _jsx(MediaImage, { src: lead, alt: "", size: "card" }) }), _jsx(Div, { className: "appkit-image-mosaic__rest", children: rest.map((src, i) => (_jsx(Div, { className: "appkit-image-mosaic__tile", children: _jsx(MediaImage, { src: src, alt: "", size: "thumbnail" }) }, `${src}-${i}`))) })] }));
|
|
46
47
|
}
|
|
@@ -27,6 +27,28 @@
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/*
|
|
31
|
+
* `size="hero"` — the homepage hero's right column.
|
|
32
|
+
*
|
|
33
|
+
* 🛑 A separate modifier rather than a caller `className` height, because the
|
|
34
|
+
* fixed-height rule above is this primitive's OWN decision (see the header) and
|
|
35
|
+
* a consumer utility would simply fight it — with `important: true` in both
|
|
36
|
+
* Tailwind configs, which of the two wins is decided by emission order, not by
|
|
37
|
+
* the author. The band height is tuned for a category title; a hero column
|
|
38
|
+
* standing beside a `size="7xl"` headline needs roughly double or the mosaic
|
|
39
|
+
* reads as a letterboxed strip next to the copy.
|
|
40
|
+
*/
|
|
41
|
+
.appkit-image-mosaic--hero {
|
|
42
|
+
height: clamp(320px, 34vw, 460px);
|
|
43
|
+
border-radius: var(--appkit-radius-2xl);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@media (min-width: 768px) {
|
|
47
|
+
.appkit-image-mosaic--hero {
|
|
48
|
+
height: clamp(360px, 34vw, 480px);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
30
52
|
.appkit-image-mosaic__lead,
|
|
31
53
|
.appkit-image-mosaic__tile {
|
|
32
54
|
position: relative;
|
|
@@ -324,6 +324,12 @@
|
|
|
324
324
|
.appkit-section--tone-page-header { background-image: var(--appkit-gradient-page-header); }
|
|
325
325
|
.appkit-section--tone-hero { background-image: var(--appkit-gradient-section-mesh); }
|
|
326
326
|
.appkit-section--tone-accent-banner { background-image: var(--appkit-gradient-accent-banner); }
|
|
327
|
+
/* The variant layer finally exposing --appkit-gradient-whatsapp-card, which
|
|
328
|
+
had existed in the token layer with that exact name and NO consumer. The
|
|
329
|
+
community card used `accent-banner` instead — the same primary→secondary
|
|
330
|
+
gradient as CTABannerSection two sections below it, which is a real part of
|
|
331
|
+
why the homepage read monotonous. */
|
|
332
|
+
.appkit-section--tone-whatsapp { background-image: var(--appkit-gradient-whatsapp-card); }
|
|
327
333
|
|
|
328
334
|
/* Semantic — Ul/Ol marker presets */
|
|
329
335
|
.appkit-list--marker-disc { list-style: disc; padding-left: 1.25rem; }
|
|
@@ -41,7 +41,7 @@ import type { SurfaceProps, RoundedKey, PaddingKey, BorderKey, SurfaceKey } from
|
|
|
41
41
|
* `--appkit-gradient-*` CSS variable so admin custom themes restyle the
|
|
42
42
|
* section without consumer changes.
|
|
43
43
|
*/
|
|
44
|
-
export type SectionTone = "plain" | "page-header" | "hero" | "accent-banner";
|
|
44
|
+
export type SectionTone = "plain" | "page-header" | "hero" | "accent-banner" | "whatsapp";
|
|
45
45
|
type SemanticColor = "default" | "primary" | "muted" | "faint" | "inverse" | "success" | "warning" | "error" | "info";
|
|
46
46
|
type SectionLayout = "default" | "flex" | "flex-col" | "flex-sm-row" | "flex-sm-row-between";
|
|
47
47
|
type SectionGap = "none" | "xs" | "sm" | "md" | "lg";
|
|
@@ -62,7 +62,8 @@ export interface SectionProps extends React.HTMLAttributes<HTMLElement>, Surface
|
|
|
62
62
|
/**
|
|
63
63
|
* Themed background tone. Defaults to `"plain"`. Use `"page-header"` for
|
|
64
64
|
* subtle gradient page-headers, `"hero"` for radial mesh hero backgrounds,
|
|
65
|
-
* `"accent-banner"` for primary→secondary brand banners
|
|
65
|
+
* `"accent-banner"` for primary→secondary brand banners, `"whatsapp"` for
|
|
66
|
+
* the WhatsApp-brand green→teal community card.
|
|
66
67
|
*/
|
|
67
68
|
tone?: SectionTone;
|
|
68
69
|
/** Scoped color/gradient/image background — an alternative to `tone` for admin/CMS-configured section backgrounds. Renders behind `children`, absolutely positioned within the section. */
|
|
@@ -6,6 +6,7 @@ const SECTION_TONE_MAP = {
|
|
|
6
6
|
"page-header": "appkit-section--tone-page-header",
|
|
7
7
|
hero: "appkit-section--tone-hero",
|
|
8
8
|
"accent-banner": "appkit-section--tone-accent-banner",
|
|
9
|
+
whatsapp: "appkit-section--tone-whatsapp",
|
|
9
10
|
};
|
|
10
11
|
const SEMANTIC_COLOR_MAP = {
|
|
11
12
|
default: "",
|
|
@@ -44,6 +44,8 @@ declare const TYPOGRAPHY: {
|
|
|
44
44
|
readonly inherit: "";
|
|
45
45
|
readonly accent: "appkit-color--accent";
|
|
46
46
|
readonly inverse: "appkit-color--inverse";
|
|
47
|
+
/** Second tier on a dark/branded surface. See Typography.style.css. */
|
|
48
|
+
readonly "inverse-muted": "appkit-color--inverse-muted";
|
|
47
49
|
};
|
|
48
50
|
};
|
|
49
51
|
export type ColorVariant = keyof typeof TYPOGRAPHY.colorVariant;
|
|
@@ -46,6 +46,8 @@ const TYPOGRAPHY = {
|
|
|
46
46
|
inherit: "",
|
|
47
47
|
accent: "appkit-color--accent",
|
|
48
48
|
inverse: "appkit-color--inverse",
|
|
49
|
+
/** Second tier on a dark/branded surface. See Typography.style.css. */
|
|
50
|
+
"inverse-muted": "appkit-color--inverse-muted",
|
|
49
51
|
},
|
|
50
52
|
};
|
|
51
53
|
const DROP_SHADOW_MAP = {
|
|
@@ -165,6 +165,25 @@
|
|
|
165
165
|
color: var(--appkit-color-text-on-primary, #ffffff);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
/* Secondary text on a surface that stays dark/branded in EVERY theme.
|
|
169
|
+
*
|
|
170
|
+
* WHY: eight live sites hand-rolled a second tier on a branded surface and
|
|
171
|
+
* none of them worked the same way. Three in WhatsAppCommunitySection were
|
|
172
|
+
* `className="/80"` / `"/90"` / `"/75"` — codemod residue from a sweep that
|
|
173
|
+
* stripped `text-white` off `text-white/80` and left the modifier. Tailwind
|
|
174
|
+
* cannot generate a class named `/80`, so all three were silent no-ops and
|
|
175
|
+
* the intended hierarchy rendered flat. Five more in HeroCarousel used raw
|
|
176
|
+
* `opacity-70`/`-80`/`-90`, which fades the whole element, not just its ink.
|
|
177
|
+
*
|
|
178
|
+
* Reads --appkit-color-text-on-primary rather than literal white, so a theme
|
|
179
|
+
* whose on-primary ink is not #fff still gets a correct second tier. Three
|
|
180
|
+
* different alphas for one idea is the drift that produced this; there is one
|
|
181
|
+
* tier here on purpose.
|
|
182
|
+
*/
|
|
183
|
+
.appkit-color--inverse-muted {
|
|
184
|
+
color: color-mix(in srgb, var(--appkit-color-text-on-primary, #ffffff) 78%, transparent);
|
|
185
|
+
}
|
|
186
|
+
|
|
168
187
|
.dark .appkit-color--primary {
|
|
169
188
|
color: var(--appkit-color-text);
|
|
170
189
|
}
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
@import "./DescriptionField.style.css";
|
|
41
41
|
@import "./FormGrid.style.css";
|
|
42
42
|
@import "./HorizontalScroller.style.css";
|
|
43
|
+
@import "./CarouselNav.style.css";
|
|
43
44
|
@import "./NavigationLoader.style.css";
|
|
44
45
|
@import "./ImageLightbox.style.css";
|
|
45
46
|
@import "./SideModal.style.css";
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -106,7 +106,7 @@ export { StarRating } from "./components/StarRating";
|
|
|
106
106
|
export type { BreadcrumbProps, BreadcrumbItem } from "./components/Breadcrumb";
|
|
107
107
|
export { Breadcrumb } from "./components/Breadcrumb";
|
|
108
108
|
export { ImageMosaic, MOSAIC_MIN } from "./components/ImageMosaic";
|
|
109
|
-
export type { ImageMosaicProps } from "./components/ImageMosaic";
|
|
109
|
+
export type { ImageMosaicProps, ImageMosaicSize } from "./components/ImageMosaic";
|
|
110
110
|
export { SplitTile } from "./components/SplitTile";
|
|
111
111
|
export type { SplitTileProps, SplitTilePane } from "./components/SplitTile";
|
|
112
112
|
export type { ImageLightboxProps, LightboxImage, } from "./components/ImageLightbox";
|
|
@@ -133,6 +133,8 @@ export type { ItemRowProps } from "./components/ItemRow";
|
|
|
133
133
|
export { ItemRow } from "./components/ItemRow";
|
|
134
134
|
export type { HorizontalScrollerProps, PerViewConfig, } from "./components/HorizontalScroller";
|
|
135
135
|
export { HorizontalScroller } from "./components/HorizontalScroller";
|
|
136
|
+
export { CarouselNav, CAROUSEL_RAIL_MAX } from "./components/CarouselNav";
|
|
137
|
+
export type { CarouselNavProps, CarouselNavTone } from "./components/CarouselNav";
|
|
136
138
|
export type { FormGridProps, FormFieldProps } from "./components/FormGrid";
|
|
137
139
|
export { FormGrid, FormField as FormGridField } from "./components/FormGrid";
|
|
138
140
|
export type { SettingsSectionProps, ToggleRowProps } from "./components/SettingsSection";
|
package/dist/ui/index.js
CHANGED
|
@@ -98,6 +98,10 @@ export { SummaryCard } from "./components/SummaryCard.js";
|
|
|
98
98
|
export { CountdownDisplay } from "./components/CountdownDisplay.js";
|
|
99
99
|
export { ItemRow } from "./components/ItemRow.js";
|
|
100
100
|
export { HorizontalScroller } from "./components/HorizontalScroller.js";
|
|
101
|
+
/* The ONE carousel position/navigation control — replaces the hero's bespoke
|
|
102
|
+
pill dots, the rail arrows that vanished below 640px, and the testimonial
|
|
103
|
+
rail's complete absence of an indicator. */
|
|
104
|
+
export { CarouselNav, CAROUSEL_RAIL_MAX } from "./components/CarouselNav.js";
|
|
101
105
|
export { FormGrid, FormField as FormGridField } from "./components/FormGrid.js";
|
|
102
106
|
export { SettingsSection, ToggleRow } from "./components/SettingsSection.js";
|
|
103
107
|
export { DetailPageHero } from "./components/DetailPageHero.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mohasinac/appkit",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.54.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -254,7 +254,8 @@
|
|
|
254
254
|
"driver.js": "^1.8.0",
|
|
255
255
|
"jsbarcode": "^3.12.3",
|
|
256
256
|
"motion": "^13.1.1",
|
|
257
|
-
"phonepe-pg-sdk-node": "
|
|
257
|
+
"phonepe-pg-sdk-node": ">=2.0.2",
|
|
258
|
+
"razorpay": "^2.9.8",
|
|
258
259
|
"react-advanced-cropper": "^0.20.1",
|
|
259
260
|
"tailwind-merge": "^3.3.0",
|
|
260
261
|
"zod": "^3.24.0",
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core: async WhatsApp Business Catalog import (`whatsappCatalogImport` job).
|
|
3
|
-
*
|
|
4
|
-
* `POST /api/store/whatsapp-settings/catalog-import` used to do this inline:
|
|
5
|
-
* one Meta fetch capped at `limit=250`, then a sequential per-item loop doing
|
|
6
|
-
* up to 2 `findBySlug` reads plus 1 `create` write EACH — a real 10s-timeout
|
|
7
|
-
* risk on Vercel Hobby (CLAUDE.md Rule #6) well before the cap was reached.
|
|
8
|
-
* It also declared `paging.next` in its response type and never followed it,
|
|
9
|
-
* so a catalog larger than 250 items silently imported only the first page.
|
|
10
|
-
*
|
|
11
|
-
* Running here instead buys the Function's 300s budget, which is what makes
|
|
12
|
-
* following `paging.next` safe rather than making the timeout worse.
|
|
13
|
-
*
|
|
14
|
-
* Two efficiency changes vs. the old route:
|
|
15
|
-
* - Existing-slug detection is ONE `findByStore` read into a Set, not 2 reads
|
|
16
|
-
* per item. A seller's WA catalog is populated by pushing their own store's
|
|
17
|
-
* products, so store-scoped lookup is equivalent to the old global
|
|
18
|
-
* `findBySlug` here while costing a single query.
|
|
19
|
-
* - That Set is updated as we create, so duplicate rows within one Meta
|
|
20
|
-
* response can't produce two products.
|
|
21
|
-
*/
|
|
22
|
-
import type { JobContext } from "../runtime/types";
|
|
23
|
-
import type { JobRunResult } from "./jobRunners";
|
|
24
|
-
export interface WhatsAppCatalogImportPayload {
|
|
25
|
-
storeSlug: string;
|
|
26
|
-
}
|
|
27
|
-
export declare function runWhatsAppCatalogImport(payload: WhatsAppCatalogImportPayload, ctx: JobContext): Promise<JobRunResult>;
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core: async WhatsApp Business Catalog import (`whatsappCatalogImport` job).
|
|
3
|
-
*
|
|
4
|
-
* `POST /api/store/whatsapp-settings/catalog-import` used to do this inline:
|
|
5
|
-
* one Meta fetch capped at `limit=250`, then a sequential per-item loop doing
|
|
6
|
-
* up to 2 `findBySlug` reads plus 1 `create` write EACH — a real 10s-timeout
|
|
7
|
-
* risk on Vercel Hobby (CLAUDE.md Rule #6) well before the cap was reached.
|
|
8
|
-
* It also declared `paging.next` in its response type and never followed it,
|
|
9
|
-
* so a catalog larger than 250 items silently imported only the first page.
|
|
10
|
-
*
|
|
11
|
-
* Running here instead buys the Function's 300s budget, which is what makes
|
|
12
|
-
* following `paging.next` safe rather than making the timeout worse.
|
|
13
|
-
*
|
|
14
|
-
* Two efficiency changes vs. the old route:
|
|
15
|
-
* - Existing-slug detection is ONE `findByStore` read into a Set, not 2 reads
|
|
16
|
-
* per item. A seller's WA catalog is populated by pushing their own store's
|
|
17
|
-
* products, so store-scoped lookup is equivalent to the old global
|
|
18
|
-
* `findBySlug` here while costing a single query.
|
|
19
|
-
* - That Set is updated as we create, so duplicate rows within one Meta
|
|
20
|
-
* response can't produce two products.
|
|
21
|
-
*/
|
|
22
|
-
import { normalizeError } from "../../../../errors/normalize.js";
|
|
23
|
-
import { storeRepository, productRepository } from "../../../../repositories/index.js";
|
|
24
|
-
import { decryptPii } from "../../../../security/index.js";
|
|
25
|
-
const META_GRAPH_BASE = "https://graph.facebook.com/v20.0";
|
|
26
|
-
const SLUG_PREFIX = "product-";
|
|
27
|
-
const PAGE_SIZE = 250;
|
|
28
|
-
/**
|
|
29
|
-
* Defensive stop. 40 pages x 250 = 10,000 items, far beyond any realistic
|
|
30
|
-
* seller catalog — this exists so a malformed/looping `paging.next` from Meta
|
|
31
|
-
* can't spin until the Function's 300s ceiling kills the job mid-write.
|
|
32
|
-
*/
|
|
33
|
-
const MAX_PAGES = 40;
|
|
34
|
-
/** Meta returns "<amount> <currency>", e.g. "450.00 INR". */
|
|
35
|
-
function parseMetaPrice(raw) {
|
|
36
|
-
if (!raw)
|
|
37
|
-
return 0;
|
|
38
|
-
const digits = raw.replace(/[^0-9.]/g, "");
|
|
39
|
-
return Math.round((parseFloat(digits) || 0) * 100) / 100;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* The LetItRip slug a Meta item claims to correspond to, if any. Items we
|
|
43
|
-
* pushed carry it in `description` (see catalog-sync's field mapping) and/or
|
|
44
|
-
* `retailer_id`; items authored natively in Meta carry neither.
|
|
45
|
-
*/
|
|
46
|
-
function claimedSlug(item) {
|
|
47
|
-
const description = (item.description ?? "").trim();
|
|
48
|
-
if (description.startsWith(SLUG_PREFIX))
|
|
49
|
-
return description;
|
|
50
|
-
const retailerId = (item.retailer_id ?? "").trim();
|
|
51
|
-
if (retailerId.startsWith(SLUG_PREFIX))
|
|
52
|
-
return retailerId;
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
export async function runWhatsAppCatalogImport(payload, ctx) {
|
|
56
|
-
const { storeSlug } = payload;
|
|
57
|
-
const empty = { total: 0, succeeded: 0, skipped: 0, failed: 0 };
|
|
58
|
-
const store = await storeRepository.findBySlug(storeSlug);
|
|
59
|
-
if (!store) {
|
|
60
|
-
return {
|
|
61
|
-
summary: empty,
|
|
62
|
-
succeeded: [],
|
|
63
|
-
skipped: [],
|
|
64
|
-
failed: [{ id: storeSlug, reason: "Store not found" }],
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
const cfg = store.whatsappConfig;
|
|
68
|
-
const token = cfg?.accessToken ? decryptPii(cfg.accessToken) : null;
|
|
69
|
-
// Re-checked here even though the route gates on the same conditions — the
|
|
70
|
-
// job doc outlives the request, and the seller could disconnect in between.
|
|
71
|
-
if (!cfg?.connected || !cfg.catalogId || !token) {
|
|
72
|
-
return {
|
|
73
|
-
summary: empty,
|
|
74
|
-
succeeded: [],
|
|
75
|
-
skipped: [],
|
|
76
|
-
failed: [{ id: storeSlug, reason: "WhatsApp is not connected for this store" }],
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
const existing = await productRepository.findByStore(store.storeSlug);
|
|
80
|
-
const knownSlugs = new Set(existing.map((p) => p.slug).filter((s) => Boolean(s)));
|
|
81
|
-
const imported = [];
|
|
82
|
-
const skipped = [];
|
|
83
|
-
const failed = [];
|
|
84
|
-
let url = `${META_GRAPH_BASE}/${cfg.catalogId}/products` +
|
|
85
|
-
`?fields=name,price,currency,image_url,description,retailer_id&limit=${PAGE_SIZE}`;
|
|
86
|
-
let page = 0;
|
|
87
|
-
let truncated = false;
|
|
88
|
-
while (url) {
|
|
89
|
-
if (page >= MAX_PAGES) {
|
|
90
|
-
truncated = true;
|
|
91
|
-
ctx.logger.warn("whatsappCatalogImport: page cap reached — catalog truncated", {
|
|
92
|
-
storeSlug,
|
|
93
|
-
maxPages: MAX_PAGES,
|
|
94
|
-
importedSoFar: imported.length,
|
|
95
|
-
});
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
page++;
|
|
99
|
-
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
|
|
100
|
-
if (!res.ok) {
|
|
101
|
-
const body = (await res.json().catch(() => ({})));
|
|
102
|
-
const reason = body.error?.message ?? res.statusText;
|
|
103
|
-
ctx.logger.error("whatsappCatalogImport: Meta fetch failed", null, { storeSlug, page, reason });
|
|
104
|
-
failed.push({ id: `page-${page}`, reason: `Meta fetch failed: ${reason}` });
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
const body = (await res.json());
|
|
108
|
-
const items = body.data ?? [];
|
|
109
|
-
for (const item of items) {
|
|
110
|
-
const slug = claimedSlug(item);
|
|
111
|
-
if (slug && knownSlugs.has(slug)) {
|
|
112
|
-
skipped.push(item.id);
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
const input = {
|
|
116
|
-
title: item.name ?? `WhatsApp Import ${item.id}`,
|
|
117
|
-
description: "",
|
|
118
|
-
slug: "",
|
|
119
|
-
listingType: "standard",
|
|
120
|
-
status: "draft",
|
|
121
|
-
price: parseMetaPrice(item.price),
|
|
122
|
-
currency: "INR",
|
|
123
|
-
mainImage: item.image_url ?? "",
|
|
124
|
-
images: [],
|
|
125
|
-
stockQuantity: 1,
|
|
126
|
-
condition: "new",
|
|
127
|
-
storeId: store.id,
|
|
128
|
-
storeName: store.storeName,
|
|
129
|
-
tags: ["whatsapp-import"],
|
|
130
|
-
categorySlugs: [],
|
|
131
|
-
featured: false,
|
|
132
|
-
};
|
|
133
|
-
try {
|
|
134
|
-
const created = await productRepository.create(input);
|
|
135
|
-
// Guard against the same item appearing twice in one Meta response.
|
|
136
|
-
if (slug)
|
|
137
|
-
knownSlugs.add(slug);
|
|
138
|
-
if (created.slug)
|
|
139
|
-
knownSlugs.add(created.slug);
|
|
140
|
-
imported.push(created.id);
|
|
141
|
-
}
|
|
142
|
-
catch (err) {
|
|
143
|
-
void normalizeError(err);
|
|
144
|
-
failed.push({
|
|
145
|
-
id: item.id,
|
|
146
|
-
reason: err instanceof Error ? err.message : "Product create failed",
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
url = body.paging?.next;
|
|
151
|
-
}
|
|
152
|
-
ctx.logger.info("whatsappCatalogImport: complete", {
|
|
153
|
-
storeSlug,
|
|
154
|
-
pages: page,
|
|
155
|
-
imported: imported.length,
|
|
156
|
-
skipped: skipped.length,
|
|
157
|
-
failed: failed.length,
|
|
158
|
-
truncated,
|
|
159
|
-
});
|
|
160
|
-
return {
|
|
161
|
-
summary: {
|
|
162
|
-
total: imported.length + skipped.length + failed.length,
|
|
163
|
-
succeeded: imported.length,
|
|
164
|
-
skipped: skipped.length,
|
|
165
|
-
failed: failed.length,
|
|
166
|
-
},
|
|
167
|
-
succeeded: imported,
|
|
168
|
-
skipped,
|
|
169
|
-
failed,
|
|
170
|
-
data: { imported: imported.length, skipped: skipped.length, pages: page, truncated },
|
|
171
|
-
};
|
|
172
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core: async WhatsApp notification dispatch (`whatsappNotify` job).
|
|
3
|
-
*
|
|
4
|
-
* `sendNotification()` (appkit/src/features/admin/actions/notification-actions.ts)
|
|
5
|
-
* no longer calls the Meta Cloud API synchronously in-request — it enqueues
|
|
6
|
-
* this job instead (CLAUDE.md Rule #6 async job primitive) and returns
|
|
7
|
-
* immediately. This runner does the actual send, with a bounded retry, and
|
|
8
|
-
* writes the outcome back onto `notifications/{notificationId}` so admins
|
|
9
|
-
* have visibility beyond the `jobs/{jobId}` doc itself.
|
|
10
|
-
*
|
|
11
|
-
* Retry note: `sendWhatsAppTemplateMessage`/`sendWhatsAppBusinessMessage`
|
|
12
|
-
* return a plain boolean (not a status code), so this runner cannot cleanly
|
|
13
|
-
* distinguish a transient 5xx from a terminal 4xx (bad token, unknown
|
|
14
|
-
* template) the way a status-code-aware client could. It retries any
|
|
15
|
-
* failure up to MAX_ATTEMPTS with a short backoff — an acceptable
|
|
16
|
-
* simplification since this runs off the critical path, well inside the
|
|
17
|
-
* Firebase Function's 300s budget, and a terminal error just burns two
|
|
18
|
-
* extra fast failures before giving up.
|
|
19
|
-
*/
|
|
20
|
-
import type { JobContext } from "../runtime/types";
|
|
21
|
-
import type { JobRunResult } from "./jobRunners";
|
|
22
|
-
export interface WhatsAppNotifyPayload {
|
|
23
|
-
toPhone: string;
|
|
24
|
-
title: string;
|
|
25
|
-
message: string;
|
|
26
|
-
type: string;
|
|
27
|
-
/** Approved Meta template name for this notification type, if configured. */
|
|
28
|
-
templateName?: string;
|
|
29
|
-
/** BCP-47 language code the template was approved in. Defaults to "en". */
|
|
30
|
-
templateLanguage?: string;
|
|
31
|
-
notificationId: string;
|
|
32
|
-
phoneNumberId: string;
|
|
33
|
-
accessToken: string;
|
|
34
|
-
}
|
|
35
|
-
export declare function runWhatsAppNotify(payload: WhatsAppNotifyPayload, ctx: JobContext): Promise<JobRunResult>;
|