@mohasinac/appkit 4.53.1 → 4.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/theme/ThemeProvider.js +15 -1
- package/dist/_internal/server/features/auth/capabilities.js +7 -5
- package/dist/_internal/server/features/categories/price-guidance-resolve.d.ts +66 -0
- package/dist/_internal/server/features/categories/price-guidance-resolve.js +138 -0
- package/dist/_internal/server/features/checkout/actions.d.ts +22 -6
- package/dist/_internal/server/features/checkout/actions.js +96 -51
- package/dist/_internal/server/features/checkout/index.d.ts +1 -1
- package/dist/_internal/server/features/checkout/index.js +1 -1
- package/dist/_internal/server/features/checkout/supplier-identity.js +28 -6
- package/dist/_internal/server/features/orders/adapters.js +1 -3
- package/dist/_internal/server/features/price-index/market.d.ts +57 -0
- package/dist/_internal/server/features/price-index/market.js +120 -0
- package/dist/_internal/server/features/products/list-public.d.ts +12 -0
- package/dist/_internal/server/features/products/list-public.js +10 -1
- package/dist/_internal/server/features/products/listing-tab-counts.d.ts +25 -1
- package/dist/_internal/server/features/products/listing-tab-counts.js +184 -10
- package/dist/_internal/server/features/search/index-build/sources.js +43 -2
- package/dist/_internal/server/features/site-settings/adapters.js +1 -1
- package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
- package/dist/_internal/server/features/stores/adapters.js +0 -1
- package/dist/_internal/server/features/stores/tabs.d.ts +7 -2
- package/dist/_internal/server/features/stores/tabs.js +5 -3
- package/dist/_internal/server/jobs/core/analyticsAlertEvaluate.js +2 -5
- package/dist/_internal/server/jobs/core/jobRunners.js +0 -10
- package/dist/_internal/server/jobs/core/onOrderCreate.js +10 -104
- package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +0 -2
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +0 -1
- package/dist/_internal/server/jobs/core/onScamReportCreate.js +0 -1
- package/dist/_internal/server/jobs/core/onScamReportRejected.js +0 -1
- package/dist/_internal/server/jobs/core/onScamReportVerified.js +0 -1
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +0 -1
- package/dist/_internal/server/jobs/core/pendingOrderTimeout.js +0 -1
- package/dist/_internal/server/notifications/channel-health.d.ts +7 -3
- package/dist/_internal/server/notifications/channel-health.js +2 -2
- package/dist/_internal/server/notifications/send-guard.d.ts +1 -1
- package/dist/_internal/server/notifications/send-guard.js +2 -4
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +128 -28
- package/dist/_internal/shared/checkout/order-math.d.ts +1 -1
- package/dist/_internal/shared/checkout/order-math.js +1 -1
- package/dist/_internal/shared/features/categories/bundle-copy.d.ts +1 -1
- package/dist/_internal/shared/features/checkout/config.d.ts +0 -2
- package/dist/_internal/shared/features/checkout/config.js +0 -2
- package/dist/_internal/shared/features/messaging/config.d.ts +7 -5
- package/dist/_internal/shared/features/messaging/config.js +1 -4
- package/dist/_internal/shared/features/site-settings/fees.d.ts +0 -2
- package/dist/_internal/shared/features/site-settings/fees.js +0 -2
- package/dist/_internal/shared/features/site-settings/supplier-of-record.d.ts +56 -0
- package/dist/_internal/shared/features/site-settings/supplier-of-record.js +57 -0
- package/dist/_internal/shared/fees/calculator.d.ts +0 -8
- package/dist/_internal/shared/fees/calculator.js +0 -7
- package/dist/_internal/shared/listing-types/tiers.d.ts +94 -0
- package/dist/_internal/shared/listing-types/tiers.js +150 -0
- package/dist/_internal/shared/search/match.js +28 -2
- package/dist/cli/index.js +0 -2
- package/dist/client.d.ts +25 -4
- package/dist/client.js +36 -5
- package/dist/constants/api-endpoints.d.ts +28 -15
- package/dist/constants/api-endpoints.js +14 -8
- package/dist/constants/field-names.d.ts +1 -0
- package/dist/constants/field-names.js +1 -0
- package/dist/constants/notification-types.d.ts +1 -1
- package/dist/constants/notification-types.js +1 -1
- package/dist/core/integration-keys.d.ts +5 -5
- package/dist/core/integration-keys.js +21 -10
- package/dist/features/about/components/FeesView.js +0 -6
- package/dist/features/about/components/GuidesHubView.js +15 -2
- package/dist/features/account/components/NotificationPreferencesPanel.js +3 -4
- package/dist/features/account/components/UserOrdersView.js +26 -7
- package/dist/features/account/types/index.d.ts +0 -2
- package/dist/features/addresses/utils/postal-autofill.d.ts +39 -0
- package/dist/features/addresses/utils/postal-autofill.js +54 -0
- package/dist/features/admin/actions/notification-actions.d.ts +2 -8
- package/dist/features/admin/actions/notification-actions.js +9 -104
- package/dist/features/admin/components/AdminGuideHubView.js +1 -8
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +1 -1
- package/dist/features/admin/components/AdminOrdersView.js +0 -2
- package/dist/features/admin/components/AdminSectionsView.js +3 -2
- package/dist/features/admin/components/AdminSiteSettingsView.js +22 -68
- package/dist/features/admin/components/AdminStoreEditorView.js +72 -34
- package/dist/features/admin/components/OwnerQuickActions.d.ts +29 -0
- package/dist/features/admin/components/OwnerQuickActions.js +142 -0
- package/dist/features/admin/components/sections/adminSectionsTypes.js +13 -4
- package/dist/features/admin/components/sections/icon-options.d.ts +21 -0
- package/dist/features/admin/components/sections/icon-options.js +23 -0
- package/dist/features/admin/components/sections/sectionTemplates.js +4 -1
- package/dist/features/admin/constants/site-settings-tabs.d.ts +7 -12
- package/dist/features/admin/constants/site-settings-tabs.js +6 -12
- package/dist/features/admin/repository/site-settings.repository.js +1 -1
- package/dist/features/admin/schemas/admin-editor-forms.d.ts +1 -1
- package/dist/features/admin/schemas/admin-editor-forms.js +4 -1
- package/dist/features/admin/schemas/admin-user-form.d.ts +0 -61
- package/dist/features/admin/schemas/admin-user-form.js +3 -44
- package/dist/features/admin/schemas/firestore.d.ts +28 -54
- package/dist/features/admin/schemas/firestore.js +40 -18
- package/dist/features/auctions/components/AuctionDetailPageView.js +12 -10
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +9 -2
- package/dist/features/auth/checkout-value-otp.d.ts +1 -1
- package/dist/features/auth/checkout-value-otp.js +1 -1
- package/dist/features/auth/permissions/constants.d.ts +29 -2
- package/dist/features/auth/permissions/constants.js +67 -1
- package/dist/features/blog/components/BlogFeaturedCard.d.ts +11 -1
- package/dist/features/blog/components/BlogFeaturedCard.js +9 -2
- package/dist/features/cart/components/CartDrawer.js +3 -2
- package/dist/features/cart/components/CartGroupLineRow.js +2 -1
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +0 -2
- package/dist/features/cart/components/CartPriceBreakdown.js +1 -1
- package/dist/features/cart/components/StoreAddonsPicker.d.ts +2 -5
- package/dist/features/cart/components/StoreAddonsPicker.js +4 -5
- package/dist/features/cart/hooks/useAddToCart.js +10 -5
- package/dist/features/cart/repository/cart.repository.js +1 -2
- package/dist/features/cart/schemas/firestore.d.ts +1 -2
- package/dist/features/cart/utils/pending-ops.d.ts +11 -1
- package/dist/features/cart/utils/pending-ops.js +14 -2
- package/dist/features/categories/api/[id]/price-guidance/route.js +1 -24
- package/dist/features/categories/components/BrandDetailPageView.js +15 -6
- package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -1
- package/dist/features/categories/components/BrandDetailTabs.js +18 -3
- package/dist/features/categories/components/BundleCollage.js +3 -2
- package/dist/features/categories/components/BundleDetailView.js +2 -1
- package/dist/features/categories/components/BundleItemsPicker.js +3 -4
- package/dist/features/categories/components/CategoryDetailPageView.js +19 -8
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +7 -1
- package/dist/features/categories/components/CategoryDetailTabs.js +29 -7
- package/dist/features/categories/components/CategoryGrid.d.ts +11 -1
- package/dist/features/categories/components/CategoryGrid.js +9 -2
- package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +10 -5
- package/dist/features/checkout/actions/checkout-value-otp-actions.js +2 -88
- package/dist/features/classified/components/ClassifiedDetailPageView.js +12 -2
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +12 -2
- package/dist/features/events/components/EventCard.d.ts +11 -1
- package/dist/features/events/components/EventCard.js +9 -2
- package/dist/features/grouped/components/GroupedListingDetailView.js +3 -1
- package/dist/features/homepage/components/CTABannerSection.d.ts +3 -1
- package/dist/features/homepage/components/CTABannerSection.js +6 -3
- package/dist/features/homepage/components/HeroCarousel.js +8 -6
- package/dist/features/homepage/components/MarketplaceHomepageView.js +33 -7
- package/dist/features/homepage/components/SectionCarousel.js +13 -5
- package/dist/features/homepage/components/SecurityHighlightsSection.d.ts +8 -3
- package/dist/features/homepage/components/SecurityHighlightsSection.js +2 -2
- package/dist/features/homepage/components/StatsCounterSection.d.ts +9 -4
- package/dist/features/homepage/components/StatsCounterSection.js +2 -2
- package/dist/features/homepage/components/TestimonialsCarousel.js +1 -1
- package/dist/features/homepage/components/TrustFeaturesSection.d.ts +18 -4
- package/dist/features/homepage/components/TrustFeaturesSection.js +5 -5
- package/dist/features/homepage/components/WelcomeSection.d.ts +13 -2
- package/dist/features/homepage/components/WelcomeSection.js +3 -3
- package/dist/features/homepage/components/WhatsAppCommunitySection.d.ts +28 -0
- package/dist/features/homepage/components/WhatsAppCommunitySection.js +42 -6
- package/dist/features/homepage/components/WhatsAppGlyph.d.ts +20 -0
- package/dist/features/homepage/components/WhatsAppGlyph.js +20 -0
- package/dist/features/homepage/constants/hero-carousel-styles.d.ts +8 -6
- package/dist/features/homepage/constants/hero-carousel-styles.js +8 -6
- package/dist/features/homepage/constants/section-copy.d.ts +12 -10
- package/dist/features/homepage/constants/section-copy.js +28 -6
- package/dist/features/homepage/constants/stat-icons.d.ts +12 -0
- package/dist/features/homepage/constants/stat-icons.js +17 -0
- package/dist/features/homepage/constants/trust-icon-aliases.d.ts +8 -0
- package/dist/features/homepage/constants/trust-icon-aliases.js +75 -0
- package/dist/features/homepage/lib/section-defaults.d.ts +16 -1
- package/dist/features/homepage/lib/section-defaults.js +24 -4
- package/dist/features/homepage/lib/section-renderer.js +48 -8
- package/dist/features/homepage/schemas/firestore.d.ts +18 -1
- package/dist/features/layout/AppLayoutShell.js +2 -1
- package/dist/features/layout/BottomActions.d.ts +1 -0
- package/dist/features/layout/BottomActions.js +330 -0
- package/dist/features/layout/BottomChrome.d.ts +45 -0
- package/dist/features/layout/BottomChrome.js +5 -17
- package/dist/features/layout/SideActionDock.js +36 -13
- package/dist/features/layout/index.d.ts +2 -7
- package/dist/features/layout/index.js +7 -7
- package/dist/features/layout/useIsDesktop.d.ts +36 -0
- package/dist/features/layout/useIsDesktop.js +48 -0
- package/dist/features/live/components/LiveItemDetailPageView.js +12 -2
- package/dist/features/orders/actions/refund-actions.js +0 -1
- package/dist/features/orders/components/OrderAddonBadges.d.ts +1 -3
- package/dist/features/orders/components/OrderAddonBadges.js +3 -4
- package/dist/features/orders/components/OrderPaymentSummary.js +15 -2
- package/dist/features/orders/components/YourPurchaseStrip.d.ts +24 -0
- package/dist/features/orders/components/YourPurchaseStrip.js +77 -0
- package/dist/features/orders/components/index.d.ts +2 -1
- package/dist/features/orders/components/index.js +1 -1
- package/dist/features/orders/repository/orders.repository.d.ts +0 -8
- package/dist/features/orders/repository/orders.repository.js +0 -2
- package/dist/features/orders/schemas/firestore.d.ts +17 -9
- package/dist/features/orders/types/index.d.ts +2 -5
- package/dist/features/orders/utils/order-announcement-message.d.ts +18 -0
- package/dist/features/orders/utils/order-announcement-message.js +16 -0
- package/dist/features/orders/utils/payment-proof-message.d.ts +20 -0
- package/dist/features/orders/utils/payment-proof-message.js +17 -0
- package/dist/features/parts/components/PartDetailPageView.js +96 -1
- package/dist/features/parts/schemas/firestore.d.ts +247 -0
- package/dist/features/parts/schemas/firestore.js +6 -6
- package/dist/features/pre-orders/components/MarketplacePreorderCard.d.ts +11 -1
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +10 -3
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +12 -10
- package/dist/features/price-index/components/PriceIndexToolView.d.ts +21 -0
- package/dist/features/price-index/components/PriceIndexToolView.js +230 -27
- package/dist/features/price-index/lookup-target.d.ts +48 -0
- package/dist/features/price-index/lookup-target.js +107 -0
- package/dist/features/products/api/[id]/route.js +8 -1
- package/dist/features/products/api/route.js +16 -4
- package/dist/features/products/components/AvailabilityScopeHint.d.ts +22 -0
- package/dist/features/products/components/AvailabilityScopeHint.js +27 -0
- package/dist/features/products/components/CompareOverlay.d.ts +2 -1
- package/dist/features/products/components/CompareOverlay.js +2 -1
- package/dist/features/products/components/GroupSettingsPanel.js +2 -1
- package/dist/features/products/components/InteractiveProductCard.d.ts +7 -1
- package/dist/features/products/components/InteractiveProductCard.js +16 -4
- package/dist/features/products/components/MarketplaceBundleCard.d.ts +11 -1
- package/dist/features/products/components/MarketplaceBundleCard.js +9 -2
- package/dist/features/products/components/MarketplacePrizeDrawCard.d.ts +11 -1
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +9 -2
- package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -1
- package/dist/features/products/components/ProductDetailPageView.js +34 -4
- package/dist/features/products/components/ProductGrid.d.ts +14 -1
- package/dist/features/products/components/ProductGrid.js +22 -5
- package/dist/features/products/components/ProductsIndexListing.js +2 -2
- package/dist/features/products/components/SublistingCarouselSection.js +2 -1
- package/dist/features/products/repository/products.repository.d.ts +1 -0
- package/dist/features/products/repository/products.repository.js +5 -1
- package/dist/features/products/utils/group-member-image.d.ts +9 -2
- package/dist/features/products/utils/group-member-image.js +2 -1
- package/dist/features/reviews/api/[id]/route.js +83 -23
- package/dist/features/reviews/components/PurchaseReviewAffordance.d.ts +6 -0
- package/dist/features/reviews/components/PurchaseReviewAffordance.js +42 -0
- package/dist/features/reviews/components/ReviewFormDrawer.d.ts +34 -0
- package/dist/features/reviews/components/ReviewFormDrawer.js +127 -0
- package/dist/features/reviews/components/index.d.ts +4 -0
- package/dist/features/reviews/components/index.js +2 -0
- package/dist/features/scams/schemas/index.d.ts +10 -10
- package/dist/features/search/action-index/settings-entries.js +0 -9
- package/dist/features/seller/actions/seller-actions.d.ts +1 -1
- package/dist/features/seller/actions/seller-actions.js +49 -3
- package/dist/features/seller/components/SellerPayoutSettingsView.js +30 -2
- package/dist/features/seller/components/SellerProductShell.d.ts +17 -1
- package/dist/features/seller/components/SellerProductsView.js +3 -2
- package/dist/features/seller/hooks/useStoreListingEntitlements.d.ts +8 -0
- package/dist/features/seller/hooks/useStoreListingEntitlements.js +70 -0
- package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +1 -6
- package/dist/features/store-extensions/repository/store-extensions.repositories.js +1 -10
- package/dist/features/store-extensions/schemas/firestore.d.ts +1 -17
- package/dist/features/store-extensions/schemas/firestore.js +0 -10
- package/dist/features/stores/components/InteractiveStoreCard.d.ts +11 -1
- package/dist/features/stores/components/InteractiveStoreCard.js +9 -2
- package/dist/features/stores/components/StoreAuctionsListing.js +2 -1
- package/dist/features/stores/components/StoreCapabilitiesGuideView.js +0 -1
- package/dist/features/stores/components/StoreGuideHubView.js +2 -8
- package/dist/features/stores/components/StoreSettingsGuideView.js +2 -6
- package/dist/features/stores/components/index.d.ts +0 -2
- package/dist/features/stores/components/index.js +0 -1
- package/dist/features/stores/repository/store.repository.d.ts +1 -26
- package/dist/features/stores/repository/store.repository.js +5 -60
- package/dist/features/stores/schemas/firestore.d.ts +6 -25
- package/dist/features/stores/schemas/firestore.js +6 -3
- package/dist/features/stores/schemas/index.d.ts +1 -87
- package/dist/features/stores/schemas/index.js +10 -15
- package/dist/features/tester/seed-data/authored/admin__site-system.js +0 -48
- package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +0 -40
- package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.js +114 -2
- package/dist/features/tester/seed-data/authored/selling__quick-add-minimum-details.js +0 -16
- package/dist/features/tester/seed-data/authored/selling__seller-guide.js +1 -1
- package/dist/features/tester/seed-data/authored/selling__seller-marketing-extras.js +0 -79
- package/dist/features/tester/seed-data/authored/selling__seller-orders.js +1 -1
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +427 -69
- package/dist/index.d.ts +20 -32
- package/dist/index.js +34 -43
- package/dist/next/routing/route-map.d.ts +2 -8
- package/dist/next/routing/route-map.js +21 -4
- package/dist/providers/db-firebase/base.repository.d.ts +1 -1
- package/dist/providers/db-firebase/base.repository.js +1 -1
- package/dist/providers/payment-razorpay/index.d.ts +125 -0
- package/dist/providers/payment-razorpay/index.js +232 -0
- package/dist/providers/payment-razorpay/signature.d.ts +53 -0
- package/dist/providers/payment-razorpay/signature.js +71 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +98 -0
- package/dist/providers/shipping-shiprocket/index.js +356 -0
- package/dist/providers.d.ts +3 -0
- package/dist/providers.js +3 -0
- package/dist/react/hooks/useCanSeePrices.d.ts +0 -27
- package/dist/react/hooks/useCanSeePrices.js +25 -0
- package/dist/repositories/index.d.ts +1 -1
- package/dist/repositories/index.js +1 -1
- package/dist/schemas/registry.d.ts +10 -115
- package/dist/schemas/registry.js +1 -2
- package/dist/security/pii-encrypt.js +1 -1
- package/dist/seed/_helpers/category-forest.js +27 -0
- package/dist/seed/_helpers/category-parts-glossary-map.js +1816 -86
- package/dist/seed/_helpers/category-parts.d.ts +1 -1
- package/dist/seed/_helpers/category-parts.js +13412 -3020
- package/dist/seed/carousel-slides-seed-data.js +47 -10
- package/dist/seed/categories-seed-data.js +48 -6
- package/dist/seed/faq-seed-data.js +2 -2
- package/dist/seed/homepage-sections-seed-data.js +286 -54
- package/dist/seed/index.d.ts +1 -1
- package/dist/seed/index.js +1 -1
- package/dist/seed/manifest.d.ts +1 -1
- package/dist/seed/manifest.js +1 -2
- package/dist/seed/model-composition-seed-data.js +6 -8
- package/dist/seed/part-glossary/chunk0.d.ts +9 -0
- package/dist/seed/part-glossary/chunk0.js +35448 -0
- package/dist/seed/part-glossary/chunk1.d.ts +9 -0
- package/dist/seed/part-glossary/chunk1.js +40494 -0
- package/dist/seed/part-glossary/chunk2.d.ts +9 -0
- package/dist/seed/part-glossary/chunk2.js +51242 -0
- package/dist/seed/part-glossary/chunk3.d.ts +9 -0
- package/dist/seed/part-glossary/chunk3.js +53337 -0
- package/dist/seed/part-glossary/chunk4.d.ts +9 -0
- package/dist/seed/part-glossary/chunk4.js +51454 -0
- package/dist/seed/part-glossary-seed-data.d.ts +1 -1
- package/dist/seed/part-glossary-seed-data.js +23 -47483
- package/dist/seed/products-beyblade-x-seed-data.js +36 -1
- package/dist/seed/site-settings-seed-data.js +34 -35
- package/dist/seed/store-extensions-seed-data.d.ts +1 -2
- package/dist/seed/store-extensions-seed-data.js +1 -23
- package/dist/seed/stores-seed-data.js +0 -1
- package/dist/server-entry.d.ts +1 -1
- package/dist/server-entry.js +1 -1
- package/dist/server.d.ts +10 -10
- package/dist/server.js +24 -22
- package/dist/styles.css +392 -43
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +1 -1
- package/dist/tokens/themes/default-light.js +2 -1
- package/dist/tokens/tokens.css +6 -2
- package/dist/ui/components/Button.d.ts +11 -0
- package/dist/ui/components/Button.js +11 -0
- package/dist/ui/components/Button.style.css +29 -0
- package/dist/ui/components/CarouselNav.d.ts +57 -0
- package/dist/ui/components/CarouselNav.js +25 -0
- package/dist/ui/components/CarouselNav.style.css +265 -0
- package/dist/ui/components/GatedPrice.js +30 -3
- package/dist/ui/components/GatedPrice.style.css +43 -40
- package/dist/ui/components/HorizontalScroller.d.ts +12 -1
- package/dist/ui/components/HorizontalScroller.js +43 -3
- package/dist/ui/components/ImageMosaic.d.ts +8 -1
- package/dist/ui/components/ImageMosaic.js +3 -2
- package/dist/ui/components/ImageMosaic.style.css +22 -0
- package/dist/ui/components/Layout.style.css +6 -0
- package/dist/ui/components/Semantic.d.ts +3 -2
- package/dist/ui/components/Semantic.js +1 -0
- package/dist/ui/components/Typography.d.ts +2 -0
- package/dist/ui/components/Typography.js +2 -0
- package/dist/ui/components/Typography.style.css +19 -0
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/index.d.ts +3 -1
- package/dist/ui/index.js +4 -0
- package/package.json +3 -2
- package/scripts/seed-cli.mjs +133 -27
- package/dist/_internal/server/jobs/core/whatsappCatalogImport.d.ts +0 -27
- package/dist/_internal/server/jobs/core/whatsappCatalogImport.js +0 -172
- package/dist/_internal/server/jobs/core/whatsappNotify.d.ts +0 -35
- package/dist/_internal/server/jobs/core/whatsappNotify.js +0 -140
- package/dist/features/admin/components/AdminWhatsAppGuideView.d.ts +0 -2
- package/dist/features/admin/components/AdminWhatsAppGuideView.js +0 -35
- package/dist/features/orders/components/MarketplaceOrderCard.d.ts +0 -36
- package/dist/features/orders/components/MarketplaceOrderCard.js +0 -57
- package/dist/features/stores/components/StoreWhatsAppGuideView.d.ts +0 -3
- package/dist/features/stores/components/StoreWhatsAppGuideView.js +0 -12
- package/dist/features/whatsapp-bot/api/send-status/route.d.ts +0 -12
- package/dist/features/whatsapp-bot/api/send-status/route.js +0 -38
- package/dist/features/whatsapp-bot/api/webhook/route.d.ts +0 -12
- package/dist/features/whatsapp-bot/api/webhook/route.js +0 -43
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.d.ts +0 -6
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +0 -270
- package/dist/features/whatsapp-bot/components/WhatsAppChatButton.d.ts +0 -9
- package/dist/features/whatsapp-bot/components/WhatsAppChatButton.js +0 -7
- package/dist/features/whatsapp-bot/components/index.d.ts +0 -3
- package/dist/features/whatsapp-bot/components/index.js +0 -2
- package/dist/features/whatsapp-bot/helpers/payment-proof-message.d.ts +0 -14
- package/dist/features/whatsapp-bot/helpers/payment-proof-message.js +0 -17
- package/dist/features/whatsapp-bot/helpers/whatsapp.d.ts +0 -52
- package/dist/features/whatsapp-bot/helpers/whatsapp.js +0 -283
- package/dist/features/whatsapp-bot/index.d.ts +0 -3
- package/dist/features/whatsapp-bot/index.js +0 -3
- package/dist/features/whatsapp-bot/manifest.d.ts +0 -2
- package/dist/features/whatsapp-bot/manifest.js +0 -13
- package/dist/features/whatsapp-bot/messages/en.json +0 -7
- package/dist/features/whatsapp-bot/server.d.ts +0 -7
- package/dist/features/whatsapp-bot/server.js +0 -7
- package/dist/features/whatsapp-bot/types/index.d.ts +0 -126
- package/dist/features/whatsapp-bot/types/index.js +0 -1
|
@@ -11,8 +11,21 @@
|
|
|
11
11
|
import { NextResponse } from "next/server.js";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
import { mediaUrlSchema } from "../../../../validation/schemas.js";
|
|
14
|
-
import { getProviders } from "../../../../contracts/index.js";
|
|
15
14
|
import { createRouteHandler } from "../../../../next/api/routeHandler.js";
|
|
15
|
+
/*
|
|
16
|
+
* 🛑 The real repository, not `db.getRepository<Review>("reviews")`.
|
|
17
|
+
*
|
|
18
|
+
* The generic FirebaseRepository knows nothing about this collection, so every
|
|
19
|
+
* read and write through it skipped ReviewRepository's overrides:
|
|
20
|
+
*
|
|
21
|
+
* - `update` encrypts `userName` (PII) and rebuilds `searchTxt`. Writing
|
|
22
|
+
* through the generic repo left the tokens describing the OLD text, so an
|
|
23
|
+
* edited review was findable only by what it used to say.
|
|
24
|
+
* - `mapDoc` decrypts `userName` and strips `helpfulVoterIds`. Reading
|
|
25
|
+
* through the generic repo returned the name as raw `enc:v1:…` ciphertext
|
|
26
|
+
* and published the voter list.
|
|
27
|
+
*/
|
|
28
|
+
import { reviewRepository } from "../../repository/reviews.repository.js";
|
|
16
29
|
import { normalizeError } from "../../../../errors/normalize.js";
|
|
17
30
|
import { isAdminUser, isModeratorUser } from "../../../auth/role-predicates.js";
|
|
18
31
|
const ERR_REVIEW_NOT_FOUND = "Review not found";
|
|
@@ -20,12 +33,14 @@ const ERR_REVIEW_NOT_FOUND = "Review not found";
|
|
|
20
33
|
export async function GET(_request, context) {
|
|
21
34
|
try {
|
|
22
35
|
const { id } = await context.params;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
36
|
+
/*
|
|
37
|
+
* Through `reviewRepository`, so `mapDoc` runs: `userName` is decrypted
|
|
38
|
+
* and `helpfulVoterIds` is stripped. The generic repo did neither, so this
|
|
39
|
+
* public endpoint returned the reviewer's name as raw `enc:v1:…`
|
|
40
|
+
* ciphertext and published the list of everyone who had voted the review
|
|
41
|
+
* helpful.
|
|
42
|
+
*/
|
|
43
|
+
const review = await reviewRepository.findById(id);
|
|
29
44
|
if (!review) {
|
|
30
45
|
return NextResponse.json({ success: false, error: ERR_REVIEW_NOT_FOUND }, { status: 404 });
|
|
31
46
|
}
|
|
@@ -39,6 +54,19 @@ export async function GET(_request, context) {
|
|
|
39
54
|
}
|
|
40
55
|
export { GET as reviewItemGET };
|
|
41
56
|
// ---- PATCH /api/reviews/[id] -------------------------------------------------
|
|
57
|
+
/*
|
|
58
|
+
* 🛑 `.strict()`, not `.passthrough()`.
|
|
59
|
+
*
|
|
60
|
+
* It was passthrough, so any unlisted key reached the write — including
|
|
61
|
+
* `userId`, `verified`, `helpfulCount` and `featured`. A reviewer could have
|
|
62
|
+
* marked their own review a verified purchase, or featured it on the homepage,
|
|
63
|
+
* by adding one field to the request body. Nothing in the handler below looked
|
|
64
|
+
* at the extra keys; they went straight into Firestore.
|
|
65
|
+
*
|
|
66
|
+
* `.strict()` rejects them with a 400 naming the field rather than silently
|
|
67
|
+
* dropping them, which is the honest answer to a caller sending something this
|
|
68
|
+
* endpoint does not own.
|
|
69
|
+
*/
|
|
42
70
|
const reviewUpdateSchema = z
|
|
43
71
|
.object({
|
|
44
72
|
rating: z
|
|
@@ -53,10 +81,28 @@ const reviewUpdateSchema = z
|
|
|
53
81
|
title: z.string().min(1).max(100).optional(),
|
|
54
82
|
comment: z.string().min(1).max(2000).optional(),
|
|
55
83
|
images: z.array(mediaUrlSchema).max(10).optional(),
|
|
56
|
-
video: mediaUrlSchema.optional(),
|
|
57
84
|
status: z.enum(["pending", "approved", "rejected"]).optional(),
|
|
58
85
|
})
|
|
59
|
-
.
|
|
86
|
+
.strict();
|
|
87
|
+
/*
|
|
88
|
+
* 🛑 `video` is deliberately NOT accepted here, and that is a fix rather than
|
|
89
|
+
* an omission.
|
|
90
|
+
*
|
|
91
|
+
* It used to be declared as a bare `mediaUrlSchema` — a URL string — while
|
|
92
|
+
* `ReviewDocument.video` is a `ReviewVideoField` object. So a caller sending
|
|
93
|
+
* the real shape failed validation, and a caller sending a string wrote a
|
|
94
|
+
* string into a field every renderer reads as an object, leaving `video.url`
|
|
95
|
+
* undefined and the player with nothing.
|
|
96
|
+
*
|
|
97
|
+
* Accepting the object instead would still be wrong on this route: the URL
|
|
98
|
+
* would arrive as an unfinalised `tmp/` reference, because nothing here calls
|
|
99
|
+
* `finalizeStagedMediaUrl`. `updateReview` (features/reviews/actions) is the
|
|
100
|
+
* path that owns review media — it finalises the upload and builds the full
|
|
101
|
+
* field — and `REVIEW_UPDATABLE_FIELDS` already documents the updatable set as
|
|
102
|
+
* rating / title / comment / images, with no video in it.
|
|
103
|
+
*
|
|
104
|
+
* One owner for media writes, and this is not it.
|
|
105
|
+
*/
|
|
60
106
|
export const reviewItemPATCH = createRouteHandler({
|
|
61
107
|
auth: true,
|
|
62
108
|
schema: reviewUpdateSchema,
|
|
@@ -64,11 +110,7 @@ export const reviewItemPATCH = createRouteHandler({
|
|
|
64
110
|
const url = new URL(request.url);
|
|
65
111
|
const segments = url.pathname.split("/");
|
|
66
112
|
const id = segments[segments.length - 1];
|
|
67
|
-
const
|
|
68
|
-
if (!db)
|
|
69
|
-
return NextResponse.json({ success: false, error: "DB not configured" }, { status: 503 });
|
|
70
|
-
const repo = db.getRepository("reviews");
|
|
71
|
-
const review = await repo.findById(id);
|
|
113
|
+
const review = await reviewRepository.findById(id);
|
|
72
114
|
if (!review)
|
|
73
115
|
return NextResponse.json({ success: false, error: ERR_REVIEW_NOT_FOUND }, { status: 404 });
|
|
74
116
|
const isOwner = review.userId === user.uid;
|
|
@@ -77,13 +119,34 @@ export const reviewItemPATCH = createRouteHandler({
|
|
|
77
119
|
if (!isOwner && !isModerator && !isAdmin) {
|
|
78
120
|
return NextResponse.json({ success: false, error: "Forbidden" }, { status: 403 });
|
|
79
121
|
}
|
|
80
|
-
|
|
122
|
+
/*
|
|
123
|
+
* Owners edit content, not moderation state. Pinning `status` rather than
|
|
124
|
+
* rejecting the field is deliberate and pre-existing: an approved review
|
|
125
|
+
* edited by its author stays approved and is NOT sent back for
|
|
126
|
+
* re-moderation.
|
|
127
|
+
*/
|
|
81
128
|
const patch = isOwner && !isModerator && !isAdmin
|
|
82
129
|
? { ...body, status: review.status }
|
|
83
130
|
: body;
|
|
84
|
-
|
|
131
|
+
/*
|
|
132
|
+
* 🛑 `hasImages` is DERIVED and has to move with `images`, or the two
|
|
133
|
+
* disagree. It is a real queryable field (`hasImages` is `canFilter` in
|
|
134
|
+
* SIEVE_FIELDS and backs the "with photos" review filter), so a review
|
|
135
|
+
* whose last photo was removed stayed in that filter for ever, and one
|
|
136
|
+
* that gained its first was absent from it.
|
|
137
|
+
*/
|
|
138
|
+
const nextImages = body?.images;
|
|
139
|
+
const derived = nextImages !== undefined ? { hasImages: nextImages.length > 0 } : {};
|
|
140
|
+
/*
|
|
141
|
+
* 🛑 A `Date`, not an ISO string. Everything else in this codebase writes
|
|
142
|
+
* `Date` to `updatedAt`, and `reviewRepository`'s own Sieve config marks it
|
|
143
|
+
* sortable — so a string here sorts lexicographically beside real
|
|
144
|
+
* timestamps and silently orders wrong.
|
|
145
|
+
*/
|
|
146
|
+
const updated = await reviewRepository.update(id, {
|
|
85
147
|
...patch,
|
|
86
|
-
|
|
148
|
+
...derived,
|
|
149
|
+
updatedAt: new Date(),
|
|
87
150
|
});
|
|
88
151
|
return NextResponse.json({ success: true, data: updated });
|
|
89
152
|
},
|
|
@@ -95,11 +158,8 @@ export const reviewItemDELETE = createRouteHandler({
|
|
|
95
158
|
const url = new URL(request.url);
|
|
96
159
|
const segments = url.pathname.split("/");
|
|
97
160
|
const id = segments[segments.length - 1];
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return NextResponse.json({ success: false, error: "DB not configured" }, { status: 503 });
|
|
101
|
-
const repo = db.getRepository("reviews");
|
|
102
|
-
const review = await repo.findById(id);
|
|
161
|
+
// Same repository as GET and PATCH — one collection, one repository.
|
|
162
|
+
const review = await reviewRepository.findById(id);
|
|
103
163
|
if (!review)
|
|
104
164
|
return NextResponse.json({ success: false, error: ERR_REVIEW_NOT_FOUND }, { status: 404 });
|
|
105
165
|
const isOwner = review.userId === user.uid;
|
|
@@ -108,7 +168,7 @@ export const reviewItemDELETE = createRouteHandler({
|
|
|
108
168
|
if (!isOwner && !isModerator && !isAdmin) {
|
|
109
169
|
return NextResponse.json({ success: false, error: "Forbidden" }, { status: 403 });
|
|
110
170
|
}
|
|
111
|
-
await
|
|
171
|
+
await reviewRepository.delete(id);
|
|
112
172
|
return NextResponse.json({ success: true });
|
|
113
173
|
},
|
|
114
174
|
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* The strip and the review drawer, connected.
|
|
5
|
+
*
|
|
6
|
+
* Exists because the two halves need shared client state (which review is
|
|
7
|
+
* open) while the product detail page that mounts them is an async Server
|
|
8
|
+
* Component. One small client island is cheaper than making the detail view
|
|
9
|
+
* take two render-prop slots and thread state between them.
|
|
10
|
+
*
|
|
11
|
+
* @tag domain:reviews
|
|
12
|
+
* @tag layer:components
|
|
13
|
+
* @tag access:client
|
|
14
|
+
*/
|
|
15
|
+
import { useCallback, useState } from "react";
|
|
16
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
17
|
+
import { YourPurchaseStrip } from "../../orders/components/YourPurchaseStrip.js";
|
|
18
|
+
import { ReviewFormDrawer } from "./ReviewFormDrawer.js";
|
|
19
|
+
export function PurchaseReviewAffordance({ productId, storeId, className, }) {
|
|
20
|
+
const queryClient = useQueryClient();
|
|
21
|
+
const [open, setOpen] = useState(false);
|
|
22
|
+
const [editingId, setEditingId] = useState(undefined);
|
|
23
|
+
/*
|
|
24
|
+
* Invalidate the strip's own query, not just the reviews list. The strip is
|
|
25
|
+
* what shows "Your review: 4/5", so saving without invalidating it leaves
|
|
26
|
+
* the buyer looking at their old rating and reasonably concluding the edit
|
|
27
|
+
* did not take.
|
|
28
|
+
*/
|
|
29
|
+
const handleSaved = useCallback(() => {
|
|
30
|
+
void queryClient.invalidateQueries({
|
|
31
|
+
queryKey: ["user", "purchases", productId],
|
|
32
|
+
});
|
|
33
|
+
void queryClient.invalidateQueries({ queryKey: ["reviews"] });
|
|
34
|
+
}, [queryClient, productId]);
|
|
35
|
+
return (_jsxs(_Fragment, { children: [_jsx(YourPurchaseStrip, { productId: productId, className: className, onEditReview: (reviewId) => {
|
|
36
|
+
setEditingId(reviewId);
|
|
37
|
+
setOpen(true);
|
|
38
|
+
}, onWriteReview: () => {
|
|
39
|
+
setEditingId(undefined);
|
|
40
|
+
setOpen(true);
|
|
41
|
+
} }), _jsx(ReviewFormDrawer, { isOpen: open, onClose: () => setOpen(false), reviewId: editingId, productId: productId, storeId: storeId, onSaved: handleSaved })] }));
|
|
42
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Mirrors the PATCH route's schema, which is `.strict()`. A field this form can
|
|
4
|
+
* send that the route does not accept is a 400 naming it — which is the point:
|
|
5
|
+
* the two were allowed to disagree before, and the route silently accepted
|
|
6
|
+
* anything.
|
|
7
|
+
*/
|
|
8
|
+
export declare const reviewFormSchema: z.ZodObject<{
|
|
9
|
+
rating: z.ZodNumber;
|
|
10
|
+
title: z.ZodString;
|
|
11
|
+
comment: z.ZodString;
|
|
12
|
+
}, "strip", z.ZodTypeAny, {
|
|
13
|
+
title: string;
|
|
14
|
+
rating: number;
|
|
15
|
+
comment: string;
|
|
16
|
+
}, {
|
|
17
|
+
title: string;
|
|
18
|
+
rating: number;
|
|
19
|
+
comment: string;
|
|
20
|
+
}>;
|
|
21
|
+
export type ReviewFormValues = z.infer<typeof reviewFormSchema>;
|
|
22
|
+
export interface ReviewFormDrawerProps {
|
|
23
|
+
isOpen: boolean;
|
|
24
|
+
onClose: () => void;
|
|
25
|
+
/** Present = editing that review. Absent = writing a new one. */
|
|
26
|
+
reviewId?: string;
|
|
27
|
+
/** Required when writing; the review is filed against this product. */
|
|
28
|
+
productId?: string;
|
|
29
|
+
storeId?: string;
|
|
30
|
+
initialValues?: Partial<ReviewFormValues>;
|
|
31
|
+
/** Called after a successful save, so the caller can refetch. */
|
|
32
|
+
onSaved?: () => void;
|
|
33
|
+
}
|
|
34
|
+
export declare function ReviewFormDrawer({ isOpen, onClose, reviewId, productId, storeId, initialValues, onSaved, }: ReviewFormDrawerProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Write or edit a review.
|
|
5
|
+
*
|
|
6
|
+
* ── This is the first review form the application has ever had ────────────
|
|
7
|
+
*
|
|
8
|
+
* 🛑 Measured before building: `useCreateReview` had ZERO component consumers,
|
|
9
|
+
* `ReviewForm` did not exist, `ViewReviewModal` is read-only, `/user/reviews`
|
|
10
|
+
* was index-only with no detail or edit route, and the ACTIONS registry had no
|
|
11
|
+
* REVIEW bucket. The repo already knew — a tester case records
|
|
12
|
+
* "createReviewAction, useCreateReview and the UI_TEXT.WRITE_REVIEW string all
|
|
13
|
+
* exist, but no .tsx consumes any of them". So there was a complete write path
|
|
14
|
+
* with nothing able to reach it.
|
|
15
|
+
*
|
|
16
|
+
* ── Field names, because the obvious guesses are all wrong ────────────────
|
|
17
|
+
*
|
|
18
|
+
* body → `comment` (NOT `body`)
|
|
19
|
+
* owner → `userId` (NOT `buyerId`)
|
|
20
|
+
* verified → `verified` (there is no `isVerifiedPurchase`)
|
|
21
|
+
* reply → `sellerReply`
|
|
22
|
+
*
|
|
23
|
+
* `REVIEW_UPDATABLE_FIELDS` is already `["rating","title","comment","images"]`,
|
|
24
|
+
* so that is exactly what this form owns — and exactly what the PATCH route
|
|
25
|
+
* accepts now that it is `.strict()`.
|
|
26
|
+
*
|
|
27
|
+
* 🛑 Images are deliberately NOT edited here. The PATCH route does not finalise
|
|
28
|
+
* staged media, so a URL submitted through it would persist as a `tmp/`
|
|
29
|
+
* reference; `updateReview` owns review media. Rather than ship a picker whose
|
|
30
|
+
* uploads silently rot, this form edits the text and the rating.
|
|
31
|
+
*
|
|
32
|
+
* @tag domain:reviews
|
|
33
|
+
* @tag layer:components
|
|
34
|
+
* @tag access:client
|
|
35
|
+
*/
|
|
36
|
+
import { useEffect, useState } from "react";
|
|
37
|
+
import { z } from "zod";
|
|
38
|
+
import { apiClient } from "../../../http/index.js";
|
|
39
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry.js";
|
|
40
|
+
import { REVIEW_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
41
|
+
import { Button, FieldInput, FieldTextarea, Form, Row, SideDrawer, Stack, Text, } from "../../../ui/index.js";
|
|
42
|
+
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary.js";
|
|
43
|
+
// Keys errors by the FULL dotted path, so a nested issue cannot collapse onto
|
|
44
|
+
// one map entry and lose all but the last.
|
|
45
|
+
import { applyZodIssues } from "../../../ui/forms/apply-zod-issues.js";
|
|
46
|
+
import { Icon } from "../../../ui/components/Icon.js";
|
|
47
|
+
import { IconButton } from "../../../ui/components/IconButton.js";
|
|
48
|
+
import { normalizeError } from "../../../errors/normalize.js";
|
|
49
|
+
import { toUserMessage } from "../../../errors/error-display-map.js";
|
|
50
|
+
/**
|
|
51
|
+
* Mirrors the PATCH route's schema, which is `.strict()`. A field this form can
|
|
52
|
+
* send that the route does not accept is a 400 naming it — which is the point:
|
|
53
|
+
* the two were allowed to disagree before, and the route silently accepted
|
|
54
|
+
* anything.
|
|
55
|
+
*/
|
|
56
|
+
export const reviewFormSchema = z.object({
|
|
57
|
+
rating: z
|
|
58
|
+
.number()
|
|
59
|
+
.int()
|
|
60
|
+
.min(1, "Pick a rating from 1 to 5")
|
|
61
|
+
.max(5, "Pick a rating from 1 to 5"),
|
|
62
|
+
title: z.string().trim().min(1, "Give your review a title").max(100),
|
|
63
|
+
comment: z
|
|
64
|
+
.string()
|
|
65
|
+
.trim()
|
|
66
|
+
.min(1, "Say something about the item")
|
|
67
|
+
.max(2000, "Keep it under 2000 characters"),
|
|
68
|
+
});
|
|
69
|
+
const EMPTY = { rating: 0, title: "", comment: "" };
|
|
70
|
+
export function ReviewFormDrawer({ isOpen, onClose, reviewId, productId, storeId, initialValues, onSaved, }) {
|
|
71
|
+
const editing = Boolean(reviewId);
|
|
72
|
+
const [values, setValues] = useState({
|
|
73
|
+
...EMPTY,
|
|
74
|
+
...initialValues,
|
|
75
|
+
});
|
|
76
|
+
const [busy, setBusy] = useState(false);
|
|
77
|
+
const [formError, setFormError] = useState(null);
|
|
78
|
+
/*
|
|
79
|
+
* Re-seed when the drawer is reopened for a DIFFERENT review. Without this
|
|
80
|
+
* the second review a buyer edits in one session shows the first one's text
|
|
81
|
+
* — the component stays mounted, so initial state alone is not enough.
|
|
82
|
+
*/
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (isOpen)
|
|
85
|
+
setValues({ ...EMPTY, ...initialValues });
|
|
86
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
87
|
+
}, [isOpen, reviewId]);
|
|
88
|
+
async function submit({ setFieldError, clearErrors, markSubmitAttempted, }) {
|
|
89
|
+
clearErrors();
|
|
90
|
+
setFormError(null);
|
|
91
|
+
markSubmitAttempted?.();
|
|
92
|
+
const parsed = reviewFormSchema.safeParse(values);
|
|
93
|
+
if (!parsed.success) {
|
|
94
|
+
applyZodIssues(parsed.error.issues, setFieldError);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
setBusy(true);
|
|
98
|
+
try {
|
|
99
|
+
if (reviewId) {
|
|
100
|
+
await apiClient.patch(REVIEW_ENDPOINTS.BY_ID(reviewId), parsed.data);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
await apiClient.post(REVIEW_ENDPOINTS.LIST, {
|
|
104
|
+
...parsed.data,
|
|
105
|
+
productId,
|
|
106
|
+
storeId,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
onSaved?.();
|
|
110
|
+
onClose();
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
const normalized = normalizeError(err);
|
|
114
|
+
/*
|
|
115
|
+
* 🛑 toUserMessage, never the server's own message. A raw `?? error`
|
|
116
|
+
* fallback is what rendered a Node MODULE_NOT_FOUND with a require
|
|
117
|
+
* stack inside a bid modal (Root Cause #86); this resolves through
|
|
118
|
+
* errors.codes.* and terminates at UNKNOWN.
|
|
119
|
+
*/
|
|
120
|
+
setFormError(toUserMessage(normalized.code ?? "UNKNOWN"));
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
setBusy(false);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return (_jsx(SideDrawer, { isOpen: isOpen, onClose: onClose, title: editing ? "Edit your review" : "Write a review", children: _jsx(Form, { schema: reviewFormSchema, onSubmit: (e) => e.preventDefault(), className: "space-y-4", children: ({ setFieldError, clearErrors, markSubmitAttempted }) => (_jsxs(Stack, { gap: "md", children: [_jsx(FormErrorSummary, {}), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Rating" }), _jsx(Row, { gap: "xs", align: "center", children: [1, 2, 3, 4, 5].map((n) => (_jsx(IconButton, { "aria-label": `${n} star${n === 1 ? "" : "s"}`, "aria-pressed": values.rating === n, size: "md", variant: "ghost", icon: _jsx(Icon, { name: "review", size: "lg", filled: n <= values.rating, tone: n <= values.rating ? "warning" : "faint" }), onClick: () => setValues((v) => ({ ...v, rating: n })) }, n))) })] }), _jsx(FieldInput, { name: "title", label: "Title", required: true, value: values.title, onChange: (title) => setValues((v) => ({ ...v, title })) }), _jsx(FieldTextarea, { name: "comment", label: "Your review", required: true, rows: 6, value: values.comment, onChange: (comment) => setValues((v) => ({ ...v, comment })) }), formError && (_jsx(Text, { size: "sm", color: "error", role: "alert", children: formError })), _jsx(Button, { type: "submit", isLoading: busy, action: editing ? ACTIONS.REVIEW.edit : ACTIONS.REVIEW.write, onClick: () => void submit({ setFieldError, clearErrors, markSubmitAttempted }) })] })) }) }));
|
|
127
|
+
}
|
|
@@ -11,3 +11,7 @@ export type { ReviewsIndexListingProps } from "./ReviewsIndexListing";
|
|
|
11
11
|
export { ReviewsListingPanel } from "./ReviewsListingPanel";
|
|
12
12
|
export type { ReviewsListingPanelProps, ReviewsSource } from "./ReviewsListingPanel";
|
|
13
13
|
export type { ReviewDetailPageViewProps } from "./ReviewDetailPageView";
|
|
14
|
+
export { ReviewFormDrawer, reviewFormSchema } from "./ReviewFormDrawer";
|
|
15
|
+
export type { ReviewFormDrawerProps, ReviewFormValues } from "./ReviewFormDrawer";
|
|
16
|
+
export { PurchaseReviewAffordance } from "./PurchaseReviewAffordance";
|
|
17
|
+
export type { PurchaseReviewAffordanceProps } from "./PurchaseReviewAffordance";
|
|
@@ -4,3 +4,5 @@ export { ReviewSummary } from "./ReviewSummary.js";
|
|
|
4
4
|
export { ViewReviewModal } from "./ReviewModal.js";
|
|
5
5
|
export { ReviewsIndexListing } from "./ReviewsIndexListing.js";
|
|
6
6
|
export { ReviewsListingPanel } from "./ReviewsListingPanel.js";
|
|
7
|
+
export { ReviewFormDrawer, reviewFormSchema } from "./ReviewFormDrawer.js";
|
|
8
|
+
export { PurchaseReviewAffordance } from "./PurchaseReviewAffordance.js";
|
|
@@ -10,11 +10,11 @@ export declare const scammerSocialMediaSchema: z.ZodObject<{
|
|
|
10
10
|
handle: z.ZodString;
|
|
11
11
|
url: z.ZodOptional<z.ZodString>;
|
|
12
12
|
}, "strip", z.ZodTypeAny, {
|
|
13
|
-
platform: "other" | "youtube" | "
|
|
13
|
+
platform: "other" | "youtube" | "whatsapp" | "instagram" | "facebook" | "telegram" | "twitter" | "olx" | "linkedin";
|
|
14
14
|
handle: string;
|
|
15
15
|
url?: string | undefined;
|
|
16
16
|
}, {
|
|
17
|
-
platform: "other" | "youtube" | "
|
|
17
|
+
platform: "other" | "youtube" | "whatsapp" | "instagram" | "facebook" | "telegram" | "twitter" | "olx" | "linkedin";
|
|
18
18
|
handle: string;
|
|
19
19
|
url?: string | undefined;
|
|
20
20
|
}>;
|
|
@@ -44,11 +44,11 @@ export declare const scammerFirestoreSchema: z.ZodObject<{
|
|
|
44
44
|
handle: z.ZodString;
|
|
45
45
|
url: z.ZodOptional<z.ZodString>;
|
|
46
46
|
}, "strip", z.ZodTypeAny, {
|
|
47
|
-
platform: "other" | "youtube" | "
|
|
47
|
+
platform: "other" | "youtube" | "whatsapp" | "instagram" | "facebook" | "telegram" | "twitter" | "olx" | "linkedin";
|
|
48
48
|
handle: string;
|
|
49
49
|
url?: string | undefined;
|
|
50
50
|
}, {
|
|
51
|
-
platform: "other" | "youtube" | "
|
|
51
|
+
platform: "other" | "youtube" | "whatsapp" | "instagram" | "facebook" | "telegram" | "twitter" | "olx" | "linkedin";
|
|
52
52
|
handle: string;
|
|
53
53
|
url?: string | undefined;
|
|
54
54
|
}>, "many">;
|
|
@@ -93,7 +93,7 @@ export declare const scammerFirestoreSchema: z.ZodObject<{
|
|
|
93
93
|
scamType: string;
|
|
94
94
|
reportedBy: string;
|
|
95
95
|
reportedByAnon: boolean;
|
|
96
|
-
scamPlatform: "other" | "
|
|
96
|
+
scamPlatform: "other" | "whatsapp" | "letitrip" | "instagram" | "facebook" | "telegram" | "twitter" | "olx" | "facebook_marketplace" | "in_person" | "phone_call";
|
|
97
97
|
evidence: string[];
|
|
98
98
|
phones: string[];
|
|
99
99
|
upiIds: string[];
|
|
@@ -102,7 +102,7 @@ export declare const scammerFirestoreSchema: z.ZodObject<{
|
|
|
102
102
|
incidentCount: number;
|
|
103
103
|
seoSlug: string;
|
|
104
104
|
socialMedia: {
|
|
105
|
-
platform: "other" | "youtube" | "
|
|
105
|
+
platform: "other" | "youtube" | "whatsapp" | "instagram" | "facebook" | "telegram" | "twitter" | "olx" | "linkedin";
|
|
106
106
|
handle: string;
|
|
107
107
|
url?: string | undefined;
|
|
108
108
|
}[];
|
|
@@ -133,7 +133,7 @@ export declare const scammerFirestoreSchema: z.ZodObject<{
|
|
|
133
133
|
scamType: string;
|
|
134
134
|
reportedBy: string;
|
|
135
135
|
reportedByAnon: boolean;
|
|
136
|
-
scamPlatform: "other" | "
|
|
136
|
+
scamPlatform: "other" | "whatsapp" | "letitrip" | "instagram" | "facebook" | "telegram" | "twitter" | "olx" | "facebook_marketplace" | "in_person" | "phone_call";
|
|
137
137
|
evidence: string[];
|
|
138
138
|
phones: string[];
|
|
139
139
|
upiIds: string[];
|
|
@@ -142,7 +142,7 @@ export declare const scammerFirestoreSchema: z.ZodObject<{
|
|
|
142
142
|
incidentCount: number;
|
|
143
143
|
seoSlug: string;
|
|
144
144
|
socialMedia: {
|
|
145
|
-
platform: "other" | "youtube" | "
|
|
145
|
+
platform: "other" | "youtube" | "whatsapp" | "instagram" | "facebook" | "telegram" | "twitter" | "olx" | "linkedin";
|
|
146
146
|
handle: string;
|
|
147
147
|
url?: string | undefined;
|
|
148
148
|
}[];
|
|
@@ -201,7 +201,7 @@ export declare const scammerIncidentFirestoreSchema: z.ZodObject<{
|
|
|
201
201
|
reportedBy: string;
|
|
202
202
|
scammerId: string;
|
|
203
203
|
reportedByAnon: boolean;
|
|
204
|
-
scamPlatform: "other" | "
|
|
204
|
+
scamPlatform: "other" | "whatsapp" | "letitrip" | "instagram" | "facebook" | "telegram" | "twitter" | "olx" | "facebook_marketplace" | "in_person" | "phone_call";
|
|
205
205
|
evidence: string[];
|
|
206
206
|
verifiedBy?: string | undefined;
|
|
207
207
|
amountLost?: number | undefined;
|
|
@@ -224,7 +224,7 @@ export declare const scammerIncidentFirestoreSchema: z.ZodObject<{
|
|
|
224
224
|
reportedBy: string;
|
|
225
225
|
scammerId: string;
|
|
226
226
|
reportedByAnon: boolean;
|
|
227
|
-
scamPlatform: "other" | "
|
|
227
|
+
scamPlatform: "other" | "whatsapp" | "letitrip" | "instagram" | "facebook" | "telegram" | "twitter" | "olx" | "facebook_marketplace" | "in_person" | "phone_call";
|
|
228
228
|
evidence: string[];
|
|
229
229
|
verifiedBy?: string | undefined;
|
|
230
230
|
amountLost?: number | undefined;
|
|
@@ -169,15 +169,6 @@ export const SITE_SETTINGS_ENTRIES = [
|
|
|
169
169
|
tab: "notifications",
|
|
170
170
|
kind: "toggle",
|
|
171
171
|
},
|
|
172
|
-
{
|
|
173
|
-
slug: "whatsapp-notifications",
|
|
174
|
-
anchored: true,
|
|
175
|
-
label: "WhatsApp notifications",
|
|
176
|
-
description: "Send order updates over WhatsApp.",
|
|
177
|
-
keywords: ["whatsapp", "order updates", "meta"],
|
|
178
|
-
tab: "notifications",
|
|
179
|
-
kind: "toggle",
|
|
180
|
-
},
|
|
181
172
|
{
|
|
182
173
|
slug: "daily-digest",
|
|
183
174
|
anchored: true,
|
|
@@ -130,7 +130,7 @@ export declare function bulkSellerOrder(userId: string, userRole: string, userDi
|
|
|
130
130
|
* only safe shape — a caller-supplied `storeId` would let a seller file a
|
|
131
131
|
* listing under someone else's store.
|
|
132
132
|
*/
|
|
133
|
-
export declare function createSellerProduct(userId: string, userName: string, userEmail: string, input: Record<string, JsonValue
|
|
133
|
+
export declare function createSellerProduct(userId: string, userName: string, userEmail: string, input: Record<string, JsonValue>, userRole?: string): Promise<void>;
|
|
134
134
|
export declare function getSellerStore(userId: string): Promise<StoreDocument | null>;
|
|
135
135
|
export declare function getSellerShipping(userId: string): Promise<import("../../auth").SellerShippingConfig | null>;
|
|
136
136
|
export declare function getSellerPayoutSettings(userId: string): Promise<SellerPayoutDetails | {
|
|
@@ -13,6 +13,7 @@ import { normalizeError } from "../../../errors/normalize.js";
|
|
|
13
13
|
import { enqueueJob } from "../../jobs/actions/enqueue-job.js";
|
|
14
14
|
import { isAdminUser, isStrictSellerUser, isTesterUser } from "../../auth/role-predicates.js";
|
|
15
15
|
import { ApiError, AuthorizationError, NotFoundError, ValidationError, } from "../../../errors/index.js";
|
|
16
|
+
import { assertListingTypeAllowed } from "../../../_internal/shared/listing-types/tiers.js";
|
|
16
17
|
import { userRepository } from "../../auth/repository/user.repository.js";
|
|
17
18
|
import { storeRepository } from "../../stores/repository/store.repository.js";
|
|
18
19
|
import { productRepository } from "../../products/repository/products.repository.js";
|
|
@@ -427,7 +428,13 @@ export async function bulkSellerOrder(userId, userRole, userDisplayName, userEma
|
|
|
427
428
|
* only safe shape — a caller-supplied `storeId` would let a seller file a
|
|
428
429
|
* listing under someone else's store.
|
|
429
430
|
*/
|
|
430
|
-
export async function createSellerProduct(userId, userName, userEmail, input
|
|
431
|
+
export async function createSellerProduct(userId, userName, userEmail, input,
|
|
432
|
+
/*
|
|
433
|
+
* 🛑 Defaults to `"seller"`, which FAILS CLOSED. A caller that forgets to
|
|
434
|
+
* pass a role gets the gate, not a bypass — the opposite default would make
|
|
435
|
+
* every un-updated call site an admin.
|
|
436
|
+
*/
|
|
437
|
+
userRole = "seller") {
|
|
431
438
|
const store = await storeRepository.findByOwnerId(userId);
|
|
432
439
|
// Refuse rather than write an orphan. A listing with no store is invisible on
|
|
433
440
|
// its own storefront, forms no order group at checkout, and cannot be paid
|
|
@@ -436,6 +443,19 @@ export async function createSellerProduct(userId, userName, userEmail, input) {
|
|
|
436
443
|
if (!store) {
|
|
437
444
|
throw new NotFoundError("No store found for this account. Create your store first.");
|
|
438
445
|
}
|
|
446
|
+
/*
|
|
447
|
+
* The advanced-listing-type gate, at the choke point every create funnels
|
|
448
|
+
* through.
|
|
449
|
+
*
|
|
450
|
+
* It lives HERE rather than only in the consumer action because this is the
|
|
451
|
+
* one place all creates meet, and because the store document is already in
|
|
452
|
+
* scope — so the check costs no extra Firestore read (Rule #6). The caller's
|
|
453
|
+
* own gate is a nicety; this one is the guarantee.
|
|
454
|
+
*/
|
|
455
|
+
assertListingTypeAllowed(input.listingType, {
|
|
456
|
+
isPrivileged: userRole === "admin",
|
|
457
|
+
capabilities: store.capabilities,
|
|
458
|
+
});
|
|
439
459
|
const finalizedData = await finalizeProductMediaReferences(input);
|
|
440
460
|
await productRepository.create({
|
|
441
461
|
...finalizedData,
|
|
@@ -592,11 +612,37 @@ export async function sellerUpdateProduct(userId, userRole, productId, input) {
|
|
|
592
612
|
if (!existing)
|
|
593
613
|
throw new NotFoundError("Product not found");
|
|
594
614
|
// existing.storeId is the store slug, not the seller's Firebase UID.
|
|
595
|
-
|
|
596
|
-
|
|
615
|
+
const isPrivileged = userRole === "admin";
|
|
616
|
+
let store = null;
|
|
617
|
+
if (!isPrivileged) {
|
|
618
|
+
store = await storeRepository.findByOwnerId(userId);
|
|
597
619
|
if (!store || existing.storeId !== store.id)
|
|
598
620
|
throw new AuthorizationError("You do not own this product");
|
|
599
621
|
}
|
|
622
|
+
/*
|
|
623
|
+
* 🛑 The UPDATE path had no listing-type gate at all, which made the
|
|
624
|
+
* create-time one bypassable in a single request: create a `standard`
|
|
625
|
+
* listing, then PATCH `listingType: "auction"` onto it and the type stuck.
|
|
626
|
+
* Gating only creates is gating nothing.
|
|
627
|
+
*
|
|
628
|
+
* 🛑 But it fires only on a CHANGE of type, and that is load-bearing. Every
|
|
629
|
+
* per-type edit page spreads its own literal into the payload
|
|
630
|
+
* (`store/art/[id]/edit` sends `listingType: "art"`), so gating on the
|
|
631
|
+
* submitted value alone would lock a seller out of editing listings they
|
|
632
|
+
* already own the moment the capability was turned off — or an admin revoked
|
|
633
|
+
* it. The rule is "you may not create or convert INTO an advanced type",
|
|
634
|
+
* never "you may not touch one".
|
|
635
|
+
*
|
|
636
|
+
* Reuses the store already loaded for the ownership check above, so a
|
|
637
|
+
* non-admin update pays no extra read. An admin skips both.
|
|
638
|
+
*/
|
|
639
|
+
if (typeof input.listingType === "string" &&
|
|
640
|
+
input.listingType !== existing.listingType) {
|
|
641
|
+
assertListingTypeAllowed(input.listingType, {
|
|
642
|
+
isPrivileged,
|
|
643
|
+
capabilities: store?.capabilities,
|
|
644
|
+
});
|
|
645
|
+
}
|
|
600
646
|
if (typeof input.status === "string" &&
|
|
601
647
|
input.status !== "published" &&
|
|
602
648
|
existing.status === "published") {
|