@mohasinac/appkit 4.9.5 → 4.11.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/_internal/client/features/layout/DashboardLayoutClient.d.ts +6 -1
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +4 -1
- package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +1 -1
- package/dist/_internal/client/features/lottery/LotteryPullForm.js +2 -2
- package/dist/_internal/server/features/admin/daily-digest-actions.d.ts +18 -0
- package/dist/_internal/server/features/admin/daily-digest-actions.js +37 -0
- package/dist/_internal/server/features/auctions/actions.js +3 -2
- package/dist/_internal/server/features/auctions/service.d.ts +4 -3
- package/dist/_internal/server/features/auctions/service.js +6 -6
- package/dist/_internal/server/features/audit-log/actions.d.ts +25 -0
- package/dist/_internal/server/features/audit-log/actions.js +38 -0
- package/dist/_internal/server/features/brands/actions.js +0 -2
- package/dist/_internal/server/features/brands/og.d.ts +4 -1
- package/dist/_internal/server/features/brands/og.js +1 -1
- package/dist/_internal/server/features/bundles/data.d.ts +9 -0
- package/dist/_internal/server/features/bundles/data.js +14 -0
- package/dist/_internal/server/features/bundles/index.d.ts +1 -1
- package/dist/_internal/server/features/bundles/index.js +1 -1
- package/dist/_internal/server/features/catalogue/og.d.ts +20 -0
- package/dist/_internal/server/features/catalogue/og.js +27 -0
- package/dist/_internal/server/features/grouped/data.d.ts +16 -0
- package/dist/_internal/server/features/grouped/data.js +50 -1
- package/dist/_internal/server/features/grouped/index.d.ts +1 -1
- package/dist/_internal/server/features/grouped/index.js +1 -1
- package/dist/_internal/server/features/orders/actions.js +9 -0
- package/dist/_internal/server/features/orders/adapters.js +25 -0
- package/dist/_internal/server/features/products/data.d.ts +23 -0
- package/dist/_internal/server/features/products/data.js +159 -0
- package/dist/_internal/server/features/products/index.d.ts +1 -1
- package/dist/_internal/server/features/products/index.js +1 -1
- package/dist/_internal/server/functions/scheduled.d.ts +3 -1
- package/dist/_internal/server/functions/scheduled.js +17 -1
- package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +40 -0
- package/dist/_internal/server/jobs/core/dailyStatusDigest.js +164 -0
- package/dist/_internal/server/jobs/core/hardBanCascade.js +11 -0
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +1 -0
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +4 -1
- package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +12 -0
- package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +85 -0
- package/dist/_internal/server/jobs/core/whatsappNotify.js +16 -0
- package/dist/_internal/server/jobs/handlers/dailyStatusDigest.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/dailyStatusDigest.js +4 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/index.js +3 -0
- package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +2 -0
- package/dist/_internal/server/notifications/channel-health.d.ts +27 -0
- package/dist/_internal/server/notifications/channel-health.js +101 -0
- package/dist/_internal/shared/features/auctions/config.d.ts +20 -0
- package/dist/_internal/shared/features/auctions/config.js +31 -1
- package/dist/_internal/shared/features/brands/schema.d.ts +0 -6
- package/dist/_internal/shared/features/brands/schema.js +0 -1
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +7 -0
- package/dist/_internal/shared/features/categories/bundle-schemas.js +3 -0
- package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
- package/dist/_internal/shared/listing-types/art/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/art/config.js +1 -1
- package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/classified/config.js +1 -1
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/digital-code/config.js +1 -1
- package/dist/_internal/shared/listing-types/live/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/live/config.js +1 -1
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -1
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/stickers/config.js +1 -1
- package/dist/client.d.ts +21 -12
- package/dist/client.js +14 -7
- package/dist/constants/api-endpoints.d.ts +9 -9
- package/dist/constants/api-endpoints.js +3 -3
- package/dist/contracts/client-auth.d.ts +3 -7
- package/dist/contracts/email.d.ts +1 -0
- package/dist/features/account/components/AddressForm.js +24 -2
- package/dist/features/account/components/BecomeSellerView.d.ts +1 -2
- package/dist/features/account/components/BecomeSellerView.js +1 -1
- package/dist/features/account/components/NotificationBell.js +1 -1
- package/dist/features/account/components/UserBidsView.d.ts +1 -0
- package/dist/features/account/components/UserBidsView.js +47 -0
- package/dist/features/account/components/UserOrdersView.d.ts +4 -11
- package/dist/features/account/components/UserOrdersView.js +54 -4
- package/dist/features/account/components/UserReturnsView.d.ts +4 -9
- package/dist/features/account/components/UserReturnsView.js +37 -3
- package/dist/features/account/components/index.d.ts +3 -2
- package/dist/features/account/components/index.js +1 -0
- package/dist/features/admin/actions/admin-actions.js +22 -0
- package/dist/features/admin/actions/admin-coupon-actions.js +10 -0
- package/dist/features/admin/actions/notification-actions.js +4 -3
- package/dist/features/admin/components/AdminAdsView.d.ts +2 -3
- package/dist/features/admin/components/AdminAdsView.js +86 -89
- package/dist/features/admin/components/AdminAllEventEntriesView.js +18 -3
- package/dist/features/admin/components/AdminAuditLogView.d.ts +11 -0
- package/dist/features/admin/components/AdminAuditLogView.js +95 -0
- package/dist/features/admin/components/AdminBlogEditorView.js +37 -3
- package/dist/features/admin/components/AdminBrandEditorView.js +10 -6
- package/dist/features/admin/components/AdminBundleEditorView.js +18 -2
- package/dist/features/admin/components/AdminCarouselEditorView.js +2 -2
- package/dist/features/admin/components/AdminCarouselGroupEditorView.js +2 -2
- package/dist/features/admin/components/AdminCartsView.js +34 -4
- package/dist/features/admin/components/AdminCategoryEditorView.js +5 -4
- package/dist/features/admin/components/AdminCouponEditorView.js +2 -2
- package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
- package/dist/features/admin/components/AdminHistoryView.js +11 -1
- package/dist/features/admin/components/AdminNotificationsView.js +27 -1
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +10 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +4 -3
- package/dist/features/admin/components/AdminOrdersView.js +92 -24
- package/dist/features/admin/components/AdminPayoutMarkPaidModal.d.ts +7 -0
- package/dist/features/admin/components/AdminPayoutMarkPaidModal.js +47 -0
- package/dist/features/admin/components/AdminPayoutsView.js +15 -31
- package/dist/features/admin/components/AdminProductEditorView.js +26 -3
- package/dist/features/admin/components/AdminSiteSettingsView.js +31 -4
- package/dist/features/admin/components/AdminStoreAddressesView.js +22 -2
- package/dist/features/admin/components/AdminStoresView.js +7 -0
- package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +2 -2
- package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +2 -2
- package/dist/features/admin/components/AdminUserEditorView.d.ts +2 -2
- package/dist/features/admin/components/AdminUserEditorView.js +4 -2
- package/dist/features/admin/components/AdminUsersView.js +1 -1
- package/dist/features/admin/components/AdminWishlistsView.js +14 -1
- package/dist/features/admin/components/DataListingView.d.ts +2 -2
- package/dist/features/admin/components/ViewAuditLogEntryModal.d.ts +19 -0
- package/dist/features/admin/components/ViewAuditLogEntryModal.js +22 -0
- package/dist/features/admin/components/ViewNotificationModal.d.ts +21 -0
- package/dist/features/admin/components/ViewNotificationModal.js +15 -0
- package/dist/features/admin/components/analytics/AdminPageViewsReportView.d.ts +4 -7
- package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +56 -36
- package/dist/features/admin/components/index.d.ts +8 -1
- package/dist/features/admin/components/index.js +4 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +49 -0
- package/dist/features/admin/constants/filter-tabs.js +27 -0
- package/dist/features/admin/hooks/useAdminListing.d.ts +6 -0
- package/dist/features/admin/hooks/useAdminListing.js +3 -1
- package/dist/features/admin/hooks/useAdminListingData.d.ts +9 -1
- package/dist/features/admin/hooks/useAdminListingData.js +9 -2
- package/dist/features/admin/repository/site-settings.repository.d.ts +5 -0
- package/dist/features/admin/schemas/firestore.d.ts +21 -0
- package/dist/features/admin/schemas/firestore.js +6 -0
- package/dist/features/analytics/types.d.ts +1 -1
- package/dist/features/analytics/types.js +8 -0
- package/dist/features/auctions/actions/bid-actions.js +4 -1
- package/dist/features/auctions/components/AuctionBottomActions.d.ts +2 -1
- package/dist/features/auctions/components/AuctionBottomActions.js +14 -1
- package/dist/features/auctions/components/AuctionDetailPageView.js +21 -9
- package/dist/features/auctions/components/LiveMinIncrement.d.ts +18 -0
- package/dist/features/auctions/components/LiveMinIncrement.js +17 -0
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +5 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +7 -3
- package/dist/features/auctions/schemas/index.d.ts +5 -0
- package/dist/features/audit-log/repository/audit-log.repository.d.ts +45 -0
- package/dist/features/audit-log/repository/audit-log.repository.js +30 -0
- package/dist/features/audit-log/schemas/firestore.d.ts +59 -0
- package/dist/features/audit-log/schemas/firestore.js +35 -0
- package/dist/features/auth/actions/index.d.ts +0 -1
- package/dist/features/auth/actions/index.js +0 -1
- package/dist/features/auth/hooks/useAuth.d.ts +8 -35
- package/dist/features/auth/hooks/useAuth.js +21 -37
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/permissions/constants.js +2 -0
- package/dist/features/blog/schemas/index.d.ts +32 -0
- package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +31 -3
- package/dist/features/catalogue/components/CatalogueItemEditorView.js +2 -1
- package/dist/features/catalogue/components/PublicCatalogueItemDetailView.d.ts +11 -0
- package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +33 -0
- package/dist/features/catalogue/components/PublicCatalogueView.js +2 -1
- package/dist/features/catalogue/index.d.ts +2 -0
- package/dist/features/catalogue/index.js +1 -0
- package/dist/features/categories/components/BrandDetailPageView.js +14 -12
- package/dist/features/categories/components/BundleDetailView.d.ts +3 -1
- package/dist/features/categories/components/BundleDetailView.js +3 -2
- package/dist/features/categories/components/CategoryDetailPageView.js +7 -3
- package/dist/features/categories/components/CategoryHighlightsAndFaqSection.d.ts +14 -0
- package/dist/features/categories/components/CategoryHighlightsAndFaqSection.js +22 -0
- package/dist/features/categories/components/CategoryProductsView.d.ts +1 -5
- package/dist/features/categories/components/CategoryProductsView.js +1 -1
- package/dist/features/categories/components/CategorySelectorCreate.js +16 -2
- package/dist/features/categories/schemas/firestore.d.ts +19 -2
- package/dist/features/categories/schemas/index.d.ts +24 -3
- package/dist/features/categories/schemas/index.js +3 -1
- package/dist/features/categories/types/index.d.ts +7 -0
- package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +17 -3
- package/dist/features/checkout/actions/checkout-value-otp-actions.js +101 -22
- package/dist/features/classified/components/ClassifiedContactSellerPanel.d.ts +12 -0
- package/dist/features/classified/components/ClassifiedContactSellerPanel.js +49 -0
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +7 -0
- package/dist/features/classified/components/ClassifiedDetailPageView.js +69 -0
- package/dist/features/contact/components/ContactForm.js +17 -4
- package/dist/features/contact/components/ContactPageView.js +8 -2
- package/dist/features/contact/email.d.ts +1 -0
- package/dist/features/contact/email.js +3 -0
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.d.ts +19 -0
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +79 -0
- package/dist/features/events/components/AdminEventEditorView.js +54 -2
- package/dist/features/events/components/AdminEventEntriesView.d.ts +2 -16
- package/dist/features/events/components/AdminEventEntriesView.js +56 -89
- package/dist/features/events/components/EventFormDrawer.js +5 -1
- package/dist/features/events/components/EventRaffleEntryForm.d.ts +1 -3
- package/dist/features/events/components/EventRaffleEntryForm.js +1 -1
- package/dist/features/events/schemas/index.d.ts +22 -0
- package/dist/features/grouped/components/GroupedListingsCarousel.d.ts +13 -0
- package/dist/features/grouped/components/GroupedListingsCarousel.js +31 -0
- package/dist/features/grouped/repository/grouped-listings.repository.d.ts +12 -0
- package/dist/features/grouped/repository/grouped-listings.repository.js +34 -0
- package/dist/features/homepage/components/CharacterHotspotForm.js +202 -187
- package/dist/features/homepage/components/SectionCarousel.d.ts +1 -7
- package/dist/features/homepage/components/SectionCarousel.js +1 -1
- package/dist/features/layout/BottomActions.js +4 -3
- package/dist/features/layout/BottomActionsContext.d.ts +10 -0
- package/dist/features/layout/BottomActionsContext.js +5 -1
- package/dist/features/layout/TitleBarLayout.d.ts +1 -1
- package/dist/features/layout/TitleBarLayout.js +3 -3
- package/dist/features/layout/hooks/useBottomActions.d.ts +2 -0
- package/dist/features/layout/hooks/useBottomActions.js +4 -1
- package/dist/features/live/components/LiveItemDetailPageView.d.ts +9 -0
- package/dist/features/live/components/LiveItemDetailPageView.js +74 -0
- package/dist/features/media/MediaImage.js +39 -3
- package/dist/features/media/MediaVideo.js +17 -1
- package/dist/features/media/types/index.d.ts +10 -0
- package/dist/features/media/types/index.js +1 -0
- package/dist/features/media/upload/MediaUploadField.d.ts +7 -1
- package/dist/features/media/upload/MediaUploadField.js +11 -7
- package/dist/features/media/upload/video-poster.d.ts +2 -0
- package/dist/features/media/upload/video-poster.js +16 -1
- package/dist/features/media/upload/video-poster.test.js +1 -1
- package/dist/features/orders/components/OrderStatusTimeline.d.ts +12 -0
- package/dist/features/orders/components/OrderStatusTimeline.js +55 -0
- package/dist/features/orders/components/index.d.ts +2 -0
- package/dist/features/orders/components/index.js +1 -0
- package/dist/features/orders/constants/payment-window.d.ts +25 -0
- package/dist/features/orders/constants/payment-window.js +25 -0
- package/dist/features/orders/repository/orders.repository.d.ts +32 -0
- package/dist/features/orders/repository/orders.repository.js +56 -0
- package/dist/features/orders/schemas/index.d.ts +24 -24
- package/dist/features/orders/types/index.d.ts +33 -0
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +9 -1
- package/dist/features/pre-orders/schemas/index.d.ts +5 -0
- package/dist/features/products/components/BidHistory.js +9 -1
- package/dist/features/products/components/GroupSettingsPanel.js +25 -3
- package/dist/features/products/components/PrizeDrawDetailPageView.js +11 -1
- package/dist/features/products/components/PrizeDrawEntryActions.d.ts +1 -4
- package/dist/features/products/components/PrizeDrawEntryActions.js +1 -1
- package/dist/features/products/components/ProductDetailActions.d.ts +1 -2
- package/dist/features/products/components/ProductDetailActions.js +1 -1
- package/dist/features/products/components/ProductDetailPageView.js +11 -116
- package/dist/features/products/components/ProductGrid.js +15 -1
- package/dist/features/products/components/RelatedItemsSection.d.ts +16 -0
- package/dist/features/products/components/RelatedItemsSection.js +17 -0
- package/dist/features/products/constants/action-defs.d.ts +1 -0
- package/dist/features/products/constants/action-defs.js +1 -0
- package/dist/features/products/schemas/index.d.ts +5 -0
- package/dist/features/promotions/components/CouponsIndexListing.d.ts +1 -3
- package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
- package/dist/features/search/components/SearchView.d.ts +1 -3
- package/dist/features/search/components/SearchView.js +1 -1
- package/dist/features/seller/components/QuickProductForm.js +2 -1
- package/dist/features/seller/components/SellerBidsView.js +66 -107
- package/dist/features/seller/components/SellerGoogleReviewsView.js +17 -1
- package/dist/features/seller/components/SellerOrdersView.d.ts +15 -3
- package/dist/features/seller/components/SellerOrdersView.js +134 -163
- package/dist/features/seller/components/SellerPayoutMethodsView.js +60 -48
- package/dist/features/seller/components/SellerPayoutSettingsView.js +69 -4
- package/dist/features/seller/components/SellerPayoutsView.d.ts +12 -0
- package/dist/features/seller/components/SellerPayoutsView.js +25 -5
- package/dist/features/seller/components/SellerProductShell.d.ts +10 -3
- package/dist/features/seller/components/SellerProductShell.js +90 -37
- package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +2 -7
- package/dist/features/seller/components/SellerProductsFilterDrawer.js +4 -4
- package/dist/features/seller/components/SellerProductsView.d.ts +2 -4
- package/dist/features/seller/components/SellerProductsView.js +131 -189
- package/dist/features/seller/components/SellerReviewsView.js +95 -133
- package/dist/features/seller/components/SellerShippingConfigsView.js +47 -67
- package/dist/features/seller/components/SellerShippingView.js +41 -4
- package/dist/features/seller/components/SellerStoreCategoriesView.js +51 -59
- package/dist/features/seller/components/SellerStorefrontView.d.ts +6 -1
- package/dist/features/seller/components/SellerStorefrontView.js +42 -5
- package/dist/features/seller/components/index.d.ts +2 -2
- package/dist/features/seller/components/index.js +2 -2
- package/dist/features/shell/FormShell.d.ts +24 -4
- package/dist/features/shell/FormShell.js +12 -6
- package/dist/features/shell/QuickFormDrawer.js +67 -12
- package/dist/features/shell/StepForm.d.ts +30 -1
- package/dist/features/shell/StepForm.js +47 -4
- package/dist/features/shell/index.d.ts +1 -1
- package/dist/features/shell/index.js +1 -1
- package/dist/features/shipments/components/AdminShipmentEditorView.js +2 -2
- package/dist/features/shipments/schemas/validation.d.ts +14 -4
- package/dist/features/tester/components/AdminTesterFeedbackListView.js +23 -2
- package/dist/features/tester/components/TesterChecklistStepRow.js +1 -1
- package/dist/features/tester/components/TesterHubView.js +3 -2
- package/dist/features/tester/seed-data/orders-tester-seed-data.js +9 -1
- package/dist/features/tester/seed-data/products-tester-seed-data.js +43 -32
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +487 -18
- package/dist/index.d.ts +17 -3
- package/dist/index.js +23 -5
- package/dist/next/routing/route-map.d.ts +13 -0
- package/dist/next/routing/route-map.js +12 -0
- package/dist/providers/email-resend/provider.js +1 -0
- package/dist/providers/firebase-client/auth.d.ts +1 -0
- package/dist/providers/firebase-client/auth.js +7 -1
- package/dist/repositories/index.d.ts +1 -0
- package/dist/repositories/index.js +1 -0
- package/dist/schemas/registry.d.ts +61 -25
- package/dist/security/pii-mask.js +1 -1
- package/dist/seed/bids-seed-data.js +48 -3
- package/dist/seed/categories-seed-data.js +69 -0
- package/dist/seed/grouped-listings-seed-data.js +167 -0
- package/dist/seed/payouts-seed-data.js +4 -4
- package/dist/seed/products-art-seed-data.js +188 -5
- package/dist/seed/products-auctions-seed-data.js +226 -6
- package/dist/seed/products-classifieds-seed-data.js +255 -8
- package/dist/seed/products-digital-codes-seed-data.js +258 -19
- package/dist/seed/products-live-items-seed-data.js +143 -10
- package/dist/seed/products-preorders-seed-data.js +213 -8
- package/dist/seed/products-prize-draws-seed-data.js +198 -0
- package/dist/seed/products-standard-seed-data.js +234 -4
- package/dist/seed/products-stickers-seed-data.js +178 -5
- package/dist/seed/site-settings-seed-data.js +23 -30
- package/dist/seed/store-extensions-seed-data.js +2 -2
- package/dist/server-entry.d.ts +6 -2
- package/dist/server-entry.js +24 -3
- package/dist/server.d.ts +10 -3
- package/dist/server.js +11 -4
- package/dist/styles.css +29 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Form.d.ts +11 -2
- package/dist/ui/components/Form.js +4 -3
- package/dist/ui/components/FormField.d.ts +3 -1
- package/dist/ui/components/FormField.js +22 -4
- package/dist/ui/components/ImageLightbox.js +37 -3
- package/dist/ui/components/ImageLightbox.style.css +28 -0
- package/dist/ui/components/ListingLayout.d.ts +1 -2
- package/dist/ui/components/ListingLayout.js +1 -1
- package/dist/ui/components/RecordDetailModal.d.ts +68 -0
- package/dist/ui/components/RecordDetailModal.js +8 -0
- package/dist/ui/components/SlottedListingView.js +9 -1
- package/dist/ui/forms/FormErrorSummary.d.ts +25 -0
- package/dist/ui/forms/FormErrorSummary.js +37 -0
- package/dist/ui/forms/FormShell.d.ts +37 -2
- package/dist/ui/forms/FormShell.js +37 -7
- package/dist/ui/forms/FormShell.style.css +17 -0
- package/dist/ui/forms/index.d.ts +2 -0
- package/dist/ui/forms/index.js +1 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +5 -0
- package/dist/utils/media-url.d.ts +12 -1
- package/dist/utils/media-url.js +59 -1
- package/dist/validation/schemas.d.ts +4 -4
- package/package.json +1 -1
- package/dist/_internal/client/features/classified/ClassifiedDetailView.d.ts +0 -9
- package/dist/_internal/client/features/classified/ClassifiedDetailView.js +0 -55
- package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.d.ts +0 -18
- package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +0 -21
- package/dist/_internal/client/features/live/LiveItemDetailView.d.ts +0 -9
- package/dist/_internal/client/features/live/LiveItemDetailView.js +0 -25
- package/dist/_internal/server/jobs/core/prizeRevealClose.d.ts +0 -2
- package/dist/_internal/server/jobs/core/prizeRevealClose.js +0 -28
- package/dist/_internal/server/jobs/core/prizeRevealExpiry.d.ts +0 -2
- package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +0 -61
- package/dist/_internal/server/jobs/core/prizeRevealOpen.d.ts +0 -2
- package/dist/_internal/server/jobs/core/prizeRevealOpen.js +0 -68
- package/dist/_internal/server/jobs/core/prizeRevealReminder.d.ts +0 -2
- package/dist/_internal/server/jobs/core/prizeRevealReminder.js +0 -49
- package/dist/_internal/server/jobs/handlers/prizeRevealClose.d.ts +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealClose.js +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.d.ts +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.js +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealOpen.d.ts +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealOpen.js +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealReminder.d.ts +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealReminder.js +0 -2
- package/dist/features/auth/consent-otp.d.ts +0 -57
- package/dist/features/auth/consent-otp.js +0 -127
- package/dist/features/cart/components/CheckoutOtpModal.d.ts +0 -15
- package/dist/features/cart/components/CheckoutOtpModal.js +0 -12
- package/dist/features/checkout/actions/checkout-actions.d.ts +0 -31
- package/dist/features/checkout/actions/checkout-actions.js +0 -124
- package/dist/features/classified/components/ClassifiedIndexListing.d.ts +0 -5
- package/dist/features/classified/components/ClassifiedIndexListing.js +0 -123
- package/dist/features/classified/components/ClassifiedListView.d.ts +0 -6
- package/dist/features/classified/components/ClassifiedListView.js +0 -44
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +0 -5
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +0 -114
- package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +0 -6
- package/dist/features/digital-codes/components/DigitalCodesListView.js +0 -38
- package/dist/features/live/components/LiveItemsIndexListing.d.ts +0 -5
- package/dist/features/live/components/LiveItemsIndexListing.js +0 -115
- package/dist/features/live/components/LiveItemsListView.d.ts +0 -6
- package/dist/features/live/components/LiveItemsListView.js +0 -44
- package/dist/features/seller/components/SellerTemplatesView.d.ts +0 -12
- package/dist/features/seller/components/SellerTemplatesView.js +0 -276
- package/dist/react/hooks/useVisibleItems.d.ts +0 -37
- package/dist/react/hooks/useVisibleItems.js +0 -59
- package/dist/ui/components/TabStrip.d.ts +0 -34
- package/dist/ui/components/TabStrip.js +0 -56
- package/dist/ui/components/TabStrip.style.css +0 -97
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Consent OTP - Shared constants, path helpers, and utilities
|
|
3
|
-
*/
|
|
4
|
-
import type { FirestoreDocument } from "@mohasinac/appkit";
|
|
5
|
-
import { getAdminDb } from "../../providers/db-firebase";
|
|
6
|
-
/** OTP validity window (10 minutes). */
|
|
7
|
-
export declare const CONSENT_OTP_EXPIRY_MS: number;
|
|
8
|
-
/** Used in email body text so it stays in sync with CONSENT_OTP_EXPIRY_MS. */
|
|
9
|
-
export declare const CONSENT_OTP_EXPIRY_MINUTES = 10;
|
|
10
|
-
/** Per-user send cooldown (15 minutes). */
|
|
11
|
-
export declare const CONSENT_OTP_COOLDOWN_MS: number;
|
|
12
|
-
/** Maximum number of partial-order bypass credits a user can accumulate. */
|
|
13
|
-
export declare const CONSENT_OTP_MAX_BYPASS_CREDITS = 3;
|
|
14
|
-
/** Maximum failed verification attempts before the OTP is locked. */
|
|
15
|
-
export declare const CONSENT_OTP_MAX_ATTEMPTS = 5;
|
|
16
|
-
/** Rate-limit config for the verify endpoint / action (10 attempts per 5 min). */
|
|
17
|
-
export declare const CONSENT_OTP_VERIFY_RATE_LIMIT: {
|
|
18
|
-
readonly limit: 10;
|
|
19
|
-
readonly window: 300;
|
|
20
|
-
};
|
|
21
|
-
type Db = ReturnType<typeof getAdminDb>;
|
|
22
|
-
/** Reference to a user's consent OTP document for a given address. */
|
|
23
|
-
export declare function consentOtpRef(db: Db, uid: string, addressId: string): FirebaseFirestore.DocumentReference<FirebaseFirestore.DocumentData, FirebaseFirestore.DocumentData>;
|
|
24
|
-
/** Reference to a user's consent OTP rate-limit metadata document. */
|
|
25
|
-
export declare function consentOtpRateLimitRef(db: Db, uid: string): FirebaseFirestore.DocumentReference<FirebaseFirestore.DocumentData, FirebaseFirestore.DocumentData>;
|
|
26
|
-
/** HMAC-SHA256 hash of an OTP code. */
|
|
27
|
-
export declare function hashOtp(code: string): string;
|
|
28
|
-
/** Generate a cryptographically-secure random 6-digit OTP string. */
|
|
29
|
-
export declare function generateOtpCode(): string;
|
|
30
|
-
/** Obfuscate an email address for display (e.g. j***e@example.com). */
|
|
31
|
-
export declare function maskEmail(email: string): string;
|
|
32
|
-
/** Shape of a consent OTP document as returned from Firestore. */
|
|
33
|
-
export interface ConsentOtpDoc {
|
|
34
|
-
codeHash: string;
|
|
35
|
-
expiresAt: FirebaseFirestore.Timestamp;
|
|
36
|
-
attempts: number;
|
|
37
|
-
verified: boolean;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Enforce the per-user send rate-limit.
|
|
41
|
-
* Throws AuthorizationError("consentOtpRateLimit") when throttled and no bypass credits remain.
|
|
42
|
-
*/
|
|
43
|
-
export declare function enforceConsentOtpRateLimit(uid: string): Promise<void>;
|
|
44
|
-
/** Write (or overwrite) a consent OTP document for a given user + address. */
|
|
45
|
-
export declare function saveConsentOtp(uid: string, addressId: string, data: FirestoreDocument): Promise<void>;
|
|
46
|
-
/** Read a consent OTP document. Returns null when the document does not exist. */
|
|
47
|
-
export declare function readConsentOtp(uid: string, addressId: string): Promise<ConsentOtpDoc | null>;
|
|
48
|
-
/** Partially update a consent OTP document. */
|
|
49
|
-
export declare function patchConsentOtp(uid: string, addressId: string, data: FirestoreDocument): Promise<void>;
|
|
50
|
-
/** Build the consent OTP WhatsApp message body. */
|
|
51
|
-
export declare function buildConsentOtpWhatsApp(recipientName: string, code: string, siteName: string): string;
|
|
52
|
-
/** Build the consent OTP email subject and HTML body. */
|
|
53
|
-
export declare function buildConsentOtpEmail(recipientName: string, code: string, siteName: string): {
|
|
54
|
-
subject: string;
|
|
55
|
-
html: string;
|
|
56
|
-
};
|
|
57
|
-
export {};
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Consent OTP - Shared constants, path helpers, and utilities
|
|
3
|
-
*/
|
|
4
|
-
import { createHmac, randomInt } from "crypto";
|
|
5
|
-
import { getAdminDb } from "../../providers/db-firebase";
|
|
6
|
-
import { AuthorizationError } from "../../errors";
|
|
7
|
-
import { escapeHtml, resolveDate } from "../../utils";
|
|
8
|
-
import { USER_COLLECTION } from "./schemas";
|
|
9
|
-
const HMAC_KEY = process.env.CONSENT_OTP_HMAC_KEY ||
|
|
10
|
-
process.env.HMAC_SECRET ||
|
|
11
|
-
"consent-otp-secret";
|
|
12
|
-
/** OTP validity window (10 minutes). */
|
|
13
|
-
export const CONSENT_OTP_EXPIRY_MS = 10 * 60 * 1000;
|
|
14
|
-
/** Used in email body text so it stays in sync with CONSENT_OTP_EXPIRY_MS. */
|
|
15
|
-
export const CONSENT_OTP_EXPIRY_MINUTES = 10;
|
|
16
|
-
/** Per-user send cooldown (15 minutes). */
|
|
17
|
-
export const CONSENT_OTP_COOLDOWN_MS = 15 * 60 * 1000;
|
|
18
|
-
/** Maximum number of partial-order bypass credits a user can accumulate. */
|
|
19
|
-
export const CONSENT_OTP_MAX_BYPASS_CREDITS = 3;
|
|
20
|
-
/** Maximum failed verification attempts before the OTP is locked. */
|
|
21
|
-
export const CONSENT_OTP_MAX_ATTEMPTS = 5;
|
|
22
|
-
/** Rate-limit config for the verify endpoint / action (10 attempts per 5 min). */
|
|
23
|
-
export const CONSENT_OTP_VERIFY_RATE_LIMIT = {
|
|
24
|
-
limit: 10,
|
|
25
|
-
window: 300,
|
|
26
|
-
};
|
|
27
|
-
/** Reference to a user's consent OTP document for a given address. */
|
|
28
|
-
export function consentOtpRef(db, uid, addressId) {
|
|
29
|
-
return db
|
|
30
|
-
.collection(USER_COLLECTION)
|
|
31
|
-
.doc(uid)
|
|
32
|
-
.collection("consentOtps")
|
|
33
|
-
.doc(addressId);
|
|
34
|
-
}
|
|
35
|
-
/** Reference to a user's consent OTP rate-limit metadata document. */
|
|
36
|
-
export function consentOtpRateLimitRef(db, uid) {
|
|
37
|
-
return db
|
|
38
|
-
.collection(USER_COLLECTION)
|
|
39
|
-
.doc(uid)
|
|
40
|
-
.collection("consentOtpRateLimit")
|
|
41
|
-
.doc("meta");
|
|
42
|
-
}
|
|
43
|
-
/** HMAC-SHA256 hash of an OTP code. */
|
|
44
|
-
export function hashOtp(code) {
|
|
45
|
-
return createHmac("sha256", HMAC_KEY).update(code).digest("hex");
|
|
46
|
-
}
|
|
47
|
-
/** Generate a cryptographically-secure random 6-digit OTP string. */
|
|
48
|
-
export function generateOtpCode() {
|
|
49
|
-
return String(randomInt(100000, 1000000));
|
|
50
|
-
}
|
|
51
|
-
/** Obfuscate an email address for display (e.g. j***e@example.com). */
|
|
52
|
-
export function maskEmail(email) {
|
|
53
|
-
const [local, domain] = email.split("@");
|
|
54
|
-
if (!domain)
|
|
55
|
-
return "***";
|
|
56
|
-
const masked = local.length <= 2
|
|
57
|
-
? "*".repeat(local.length)
|
|
58
|
-
: local[0] + "*".repeat(local.length - 2) + local[local.length - 1];
|
|
59
|
-
return `${masked}@${domain}`;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Enforce the per-user send rate-limit.
|
|
63
|
-
* Throws AuthorizationError("consentOtpRateLimit") when throttled and no bypass credits remain.
|
|
64
|
-
*/
|
|
65
|
-
export async function enforceConsentOtpRateLimit(uid) {
|
|
66
|
-
const db = getAdminDb();
|
|
67
|
-
const metaRef = consentOtpRateLimitRef(db, uid);
|
|
68
|
-
await db.runTransaction(async (tx) => {
|
|
69
|
-
const metaSnap = await tx.get(metaRef);
|
|
70
|
-
const meta = metaSnap.exists
|
|
71
|
-
? metaSnap.data()
|
|
72
|
-
: null;
|
|
73
|
-
const lastSentMs = resolveDate(meta?.lastSentAt)?.getTime() ?? 0;
|
|
74
|
-
const bypassCredits = meta?.bypassCredits ?? 0;
|
|
75
|
-
const elapsed = Date.now() - lastSentMs;
|
|
76
|
-
if (elapsed < CONSENT_OTP_COOLDOWN_MS) {
|
|
77
|
-
if (bypassCredits <= 0) {
|
|
78
|
-
throw new AuthorizationError("consentOtpRateLimit");
|
|
79
|
-
}
|
|
80
|
-
tx.set(metaRef, { lastSentAt: new Date(), bypassCredits: bypassCredits - 1 }, { merge: true });
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
tx.set(metaRef, { lastSentAt: new Date() }, { merge: true });
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
/** Write (or overwrite) a consent OTP document for a given user + address. */
|
|
88
|
-
export async function saveConsentOtp(uid, addressId, data) {
|
|
89
|
-
const db = getAdminDb();
|
|
90
|
-
await consentOtpRef(db, uid, addressId).set(data);
|
|
91
|
-
}
|
|
92
|
-
/** Read a consent OTP document. Returns null when the document does not exist. */
|
|
93
|
-
export async function readConsentOtp(uid, addressId) {
|
|
94
|
-
const db = getAdminDb();
|
|
95
|
-
const snap = await consentOtpRef(db, uid, addressId).get();
|
|
96
|
-
if (!snap.exists)
|
|
97
|
-
return null;
|
|
98
|
-
return snap.data();
|
|
99
|
-
}
|
|
100
|
-
/** Partially update a consent OTP document. */
|
|
101
|
-
export async function patchConsentOtp(uid, addressId, data) {
|
|
102
|
-
const db = getAdminDb();
|
|
103
|
-
await consentOtpRef(db, uid, addressId).update(data);
|
|
104
|
-
}
|
|
105
|
-
/** Build the consent OTP WhatsApp message body. */
|
|
106
|
-
export function buildConsentOtpWhatsApp(recipientName, code, siteName) {
|
|
107
|
-
return (`*${siteName} — Consent OTP*\n\n` +
|
|
108
|
-
`You are shipping to *${recipientName}*.\n\n` +
|
|
109
|
-
`Your verification code is: *${code}*\n\n` +
|
|
110
|
-
`This code expires in ${CONSENT_OTP_EXPIRY_MINUTES} minutes. ` +
|
|
111
|
-
`If you did not request this, please ignore this message.`);
|
|
112
|
-
}
|
|
113
|
-
/** Build the consent OTP email subject and HTML body. */
|
|
114
|
-
export function buildConsentOtpEmail(recipientName, code, siteName) {
|
|
115
|
-
return {
|
|
116
|
-
subject: `${siteName}: Consent verification code - shipping to ${recipientName}`,
|
|
117
|
-
html: `
|
|
118
|
-
<div style="font-family:sans-serif;max-width:480px;margin:0 auto;padding:24px">
|
|
119
|
-
<h2 style="margin-bottom:8px">Third-Party Shipping Consent</h2>
|
|
120
|
-
<p style="color:#555">You are about to place an order that will be shipped to <strong>${escapeHtml(recipientName)}</strong>.</p>
|
|
121
|
-
<p style="color:#555">Enter this code to confirm your consent:</p>
|
|
122
|
-
<div style="font-size:36px;font-weight:bold;letter-spacing:8px;text-align:center;margin:24px 0;padding:16px;background:#f3f4f6;border-radius:8px">${code}</div>
|
|
123
|
-
<p style="color:#888;font-size:12px">This code expires in ${CONSENT_OTP_EXPIRY_MINUTES} minutes. If you did not request this, please ignore this email.</p>
|
|
124
|
-
</div>
|
|
125
|
-
`,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
export interface CheckoutOtpModalProps {
|
|
3
|
-
isOpen: boolean;
|
|
4
|
-
title?: string;
|
|
5
|
-
onClose: () => void;
|
|
6
|
-
renderContainer: (context: {
|
|
7
|
-
isOpen: boolean;
|
|
8
|
-
title?: string;
|
|
9
|
-
onClose: () => void;
|
|
10
|
-
children: React.ReactNode;
|
|
11
|
-
}) => React.ReactNode;
|
|
12
|
-
renderBody: () => React.ReactNode;
|
|
13
|
-
className?: string;
|
|
14
|
-
}
|
|
15
|
-
export declare function CheckoutOtpModal({ isOpen, title, onClose, renderContainer, renderBody, className, }: CheckoutOtpModalProps): React.JSX.Element | null;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Div } from "../../../ui";
|
|
3
|
-
export function CheckoutOtpModal({ isOpen, title, onClose, renderContainer, renderBody, className = "", }) {
|
|
4
|
-
if (!isOpen)
|
|
5
|
-
return null;
|
|
6
|
-
return (_jsx(_Fragment, { children: renderContainer({
|
|
7
|
-
isOpen,
|
|
8
|
-
title,
|
|
9
|
-
onClose,
|
|
10
|
-
children: _jsx(Div, { className: className, children: renderBody() }),
|
|
11
|
-
}) }));
|
|
12
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Checkout Domain Actions (appkit)
|
|
3
|
-
*
|
|
4
|
-
* OTP-based checkout consent flow — delegates entirely to already-appkit-owned
|
|
5
|
-
* helpers (consent-otp, auth repository, address repository).
|
|
6
|
-
* Auth, rate-limiting, and Next.js specifics are handled by the consumer.
|
|
7
|
-
*/
|
|
8
|
-
import { userRepository } from "../../auth/repository/user.repository";
|
|
9
|
-
/** Returns masked email so the UI can confirm which inbox to check. */
|
|
10
|
-
export declare function sendCheckoutConsentOtp(userId: string, userEmail: string, addressId: string): Promise<{
|
|
11
|
-
maskedEmail: string;
|
|
12
|
-
}>;
|
|
13
|
-
/** Verifies the 6-digit consent OTP and marks the Firestore record verified. */
|
|
14
|
-
export declare function verifyCheckoutConsentOtp(userId: string, addressId: string, code: string): Promise<void>;
|
|
15
|
-
/**
|
|
16
|
-
* Grants consent via SMS when the shipping address phone matches the buyer's
|
|
17
|
-
* registered phone number.
|
|
18
|
-
*/
|
|
19
|
-
export declare function grantCheckoutConsentViaSms(userId: string, userPhone: string | undefined, addressId: string): Promise<void>;
|
|
20
|
-
/**
|
|
21
|
-
* Grants checkout consent for an admin test order, bypassing the normal OTP flow.
|
|
22
|
-
*
|
|
23
|
-
* The resulting Firestore doc is identical to a verified consent OTP so that
|
|
24
|
-
* `createCheckoutOrderAction` can proceed without modification. The
|
|
25
|
-
* `verifiedVia: "admin_bypass"` marker prevents bypass-credit grants.
|
|
26
|
-
*
|
|
27
|
-
* Security: callers MUST verify the requesting user is an admin AND that
|
|
28
|
-
* sieveFilter("siteSettings.featureFlags.adminCheckoutBypass", SIEVE_OP.EQ, "= true") before calling this.
|
|
29
|
-
*/
|
|
30
|
-
export declare function grantAdminCheckoutBypass(userId: string, addressId: string, bypassingAdminUid: string): Promise<void>;
|
|
31
|
-
export { userRepository };
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Checkout Domain Actions (appkit)
|
|
3
|
-
*
|
|
4
|
-
* OTP-based checkout consent flow — delegates entirely to already-appkit-owned
|
|
5
|
-
* helpers (consent-otp, auth repository, address repository).
|
|
6
|
-
* Auth, rate-limiting, and Next.js specifics are handled by the consumer.
|
|
7
|
-
*/
|
|
8
|
-
import { timingSafeEqual } from "crypto";
|
|
9
|
-
import { ValidationError } from "../../../errors";
|
|
10
|
-
import { serverLogger } from "../../../monitoring";
|
|
11
|
-
import { sendEmail } from "../../contact/email";
|
|
12
|
-
import { CONSENT_OTP_EXPIRY_MS, CONSENT_OTP_MAX_ATTEMPTS, hashOtp, maskEmail, generateOtpCode, buildConsentOtpEmail, enforceConsentOtpRateLimit, saveConsentOtp, readConsentOtp, patchConsentOtp, } from "../../auth/consent-otp";
|
|
13
|
-
import { addressesRepository } from "../../addresses/repository/addresses.repository";
|
|
14
|
-
const ERR_ADDRESS_NOT_FOUND = "Address not found.";
|
|
15
|
-
async function findUserAddress(userId, addressId) {
|
|
16
|
-
const address = await addressesRepository.findById(addressId);
|
|
17
|
-
if (!address || address.ownerType !== "user" || address.ownerId !== userId) {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
return address;
|
|
21
|
-
}
|
|
22
|
-
import { userRepository } from "../../auth/repository/user.repository";
|
|
23
|
-
import { resolveDate } from "../../../utils";
|
|
24
|
-
/** Returns masked email so the UI can confirm which inbox to check. */
|
|
25
|
-
export async function sendCheckoutConsentOtp(userId, userEmail, addressId) {
|
|
26
|
-
const address = await findUserAddress(userId, addressId);
|
|
27
|
-
if (!address)
|
|
28
|
-
throw new ValidationError(ERR_ADDRESS_NOT_FOUND);
|
|
29
|
-
await enforceConsentOtpRateLimit(userId);
|
|
30
|
-
const code = generateOtpCode();
|
|
31
|
-
const codeHash = hashOtp(code);
|
|
32
|
-
const expiresAt = new Date(Date.now() + CONSENT_OTP_EXPIRY_MS);
|
|
33
|
-
await saveConsentOtp(userId, addressId, {
|
|
34
|
-
codeHash,
|
|
35
|
-
expiresAt,
|
|
36
|
-
attempts: 0,
|
|
37
|
-
verified: false,
|
|
38
|
-
addressId,
|
|
39
|
-
createdAt: new Date(),
|
|
40
|
-
});
|
|
41
|
-
const siteName = process.env.NEXT_PUBLIC_SITE_NAME || "LetItRip";
|
|
42
|
-
const { subject, html } = buildConsentOtpEmail(address.fullName, code, siteName);
|
|
43
|
-
await sendEmail({ to: userEmail, subject, html });
|
|
44
|
-
serverLogger.info(`Consent OTP sent: uid=${userId} addressId=${addressId}`);
|
|
45
|
-
return { maskedEmail: maskEmail(userEmail) };
|
|
46
|
-
}
|
|
47
|
-
/** Verifies the 6-digit consent OTP and marks the Firestore record verified. */
|
|
48
|
-
export async function verifyCheckoutConsentOtp(userId, addressId, code) {
|
|
49
|
-
const otpDoc = await readConsentOtp(userId, addressId);
|
|
50
|
-
if (!otpDoc)
|
|
51
|
-
throw new ValidationError("No consent OTP found. Please request a new code.");
|
|
52
|
-
if (otpDoc.verified)
|
|
53
|
-
return;
|
|
54
|
-
if (otpDoc.attempts >= CONSENT_OTP_MAX_ATTEMPTS) {
|
|
55
|
-
throw new ValidationError("Too many failed attempts. Please request a new code.");
|
|
56
|
-
}
|
|
57
|
-
if (Date.now() > (resolveDate(otpDoc.expiresAt)?.getTime() ?? 0)) {
|
|
58
|
-
throw new ValidationError("Code expired. Please request a new one.");
|
|
59
|
-
}
|
|
60
|
-
const inputHash = hashOtp(code);
|
|
61
|
-
if (!timingSafeEqual(Buffer.from(inputHash, "hex"), Buffer.from(otpDoc.codeHash, "hex"))) {
|
|
62
|
-
await patchConsentOtp(userId, addressId, {
|
|
63
|
-
attempts: otpDoc.attempts + 1,
|
|
64
|
-
});
|
|
65
|
-
throw new ValidationError("Invalid code. Please check and try again.");
|
|
66
|
-
}
|
|
67
|
-
await patchConsentOtp(userId, addressId, {
|
|
68
|
-
verified: true,
|
|
69
|
-
verifiedAt: new Date(),
|
|
70
|
-
});
|
|
71
|
-
serverLogger.info(`Consent OTP verified: uid=${userId} addressId=${addressId}`);
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Grants consent via SMS when the shipping address phone matches the buyer's
|
|
75
|
-
* registered phone number.
|
|
76
|
-
*/
|
|
77
|
-
export async function grantCheckoutConsentViaSms(userId, userPhone, addressId) {
|
|
78
|
-
if (!userPhone)
|
|
79
|
-
throw new ValidationError("No phone number registered on your account.");
|
|
80
|
-
const address = await findUserAddress(userId, addressId);
|
|
81
|
-
if (!address)
|
|
82
|
-
throw new ValidationError(ERR_ADDRESS_NOT_FOUND);
|
|
83
|
-
const normalizePhone = (s) => s.replace(/[^0-9]/g, "").slice(-10);
|
|
84
|
-
if (normalizePhone(address.phone) !== normalizePhone(userPhone)) {
|
|
85
|
-
throw new ValidationError("Phone number does not match the shipping address. Please use email verification for this address.");
|
|
86
|
-
}
|
|
87
|
-
await saveConsentOtp(userId, addressId, {
|
|
88
|
-
codeHash: "",
|
|
89
|
-
expiresAt: new Date(Date.now() + CONSENT_OTP_EXPIRY_MS),
|
|
90
|
-
attempts: 0,
|
|
91
|
-
verified: true,
|
|
92
|
-
verifiedVia: "sms",
|
|
93
|
-
addressId,
|
|
94
|
-
createdAt: new Date(),
|
|
95
|
-
});
|
|
96
|
-
serverLogger.info(`Checkout consent granted via SMS: uid=${userId} addressId=${addressId}`);
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Grants checkout consent for an admin test order, bypassing the normal OTP flow.
|
|
100
|
-
*
|
|
101
|
-
* The resulting Firestore doc is identical to a verified consent OTP so that
|
|
102
|
-
* `createCheckoutOrderAction` can proceed without modification. The
|
|
103
|
-
* `verifiedVia: "admin_bypass"` marker prevents bypass-credit grants.
|
|
104
|
-
*
|
|
105
|
-
* Security: callers MUST verify the requesting user is an admin AND that
|
|
106
|
-
* sieveFilter("siteSettings.featureFlags.adminCheckoutBypass", SIEVE_OP.EQ, "= true") before calling this.
|
|
107
|
-
*/
|
|
108
|
-
export async function grantAdminCheckoutBypass(userId, addressId, bypassingAdminUid) {
|
|
109
|
-
const address = await findUserAddress(userId, addressId);
|
|
110
|
-
if (!address)
|
|
111
|
-
throw new ValidationError(ERR_ADDRESS_NOT_FOUND);
|
|
112
|
-
await saveConsentOtp(userId, addressId, {
|
|
113
|
-
codeHash: "",
|
|
114
|
-
expiresAt: new Date(Date.now() + CONSENT_OTP_EXPIRY_MS),
|
|
115
|
-
attempts: 0,
|
|
116
|
-
verified: true,
|
|
117
|
-
verifiedVia: "admin_bypass",
|
|
118
|
-
adminBypassBy: bypassingAdminUid,
|
|
119
|
-
addressId,
|
|
120
|
-
createdAt: new Date(),
|
|
121
|
-
});
|
|
122
|
-
serverLogger.info(`Admin checkout bypass granted: uid=${userId} addressId=${addressId} bypassedBy=${bypassingAdminUid}`);
|
|
123
|
-
}
|
|
124
|
-
export { userRepository };
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useCallback } from "react";
|
|
4
|
-
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
|
-
import { useProducts } from "../../products/hooks/useProducts";
|
|
6
|
-
import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, Text, StickyToolbar } from "../../../ui";
|
|
7
|
-
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
8
|
-
import { useAuthGate } from "../../../react/hooks/useAuthGate";
|
|
9
|
-
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
10
|
-
import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
|
|
11
|
-
import { pushWishlistOp } from "../../cart/utils/pending-ops";
|
|
12
|
-
import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
|
|
13
|
-
import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
14
|
-
import { sortBy } from "../../../constants/sort";
|
|
15
|
-
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
16
|
-
import { ROUTES } from "../../../next";
|
|
17
|
-
import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
|
|
18
|
-
import { ClassifiedFilters } from "./ClassifiedFilters";
|
|
19
|
-
import { ACTION_ID } from "../../products/constants/action-defs";
|
|
20
|
-
import { useToast } from "../../../ui";
|
|
21
|
-
import { useBottomActions } from "../../layout";
|
|
22
|
-
const __P = {
|
|
23
|
-
p3: "p-[var(--appkit-space-3)]",
|
|
24
|
-
};
|
|
25
|
-
const __O = {
|
|
26
|
-
hidden: "overflow-hidden",
|
|
27
|
-
};
|
|
28
|
-
const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
|
|
29
|
-
const SORT_OPTIONS = [
|
|
30
|
-
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
|
|
31
|
-
{ value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
|
|
32
|
-
{ value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: "Price: Low to High" },
|
|
33
|
-
{ value: sortBy(PRODUCT_FIELDS.PRICE), label: "Price: High to Low" },
|
|
34
|
-
];
|
|
35
|
-
const FILTER_KEYS = [
|
|
36
|
-
TABLE_KEYS.CATEGORY,
|
|
37
|
-
TABLE_KEYS.CITY,
|
|
38
|
-
TABLE_KEYS.MIN_PRICE,
|
|
39
|
-
TABLE_KEYS.MAX_PRICE,
|
|
40
|
-
TABLE_KEYS.NEGOTIABLE,
|
|
41
|
-
TABLE_KEYS.ACCEPTS_SHIPPING,
|
|
42
|
-
];
|
|
43
|
-
export function ClassifiedIndexListing({ initialData }) {
|
|
44
|
-
const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
|
|
45
|
-
const { showToast } = useToast();
|
|
46
|
-
const { requireAuth, modalOpen, modalMessage, closeModal } = useAuthGate();
|
|
47
|
-
const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
|
|
48
|
-
const [filterOpen, setFilterOpen] = useState(false);
|
|
49
|
-
const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
|
|
50
|
-
const localWishlist = useGuestWishlist();
|
|
51
|
-
const { categories } = useCategoryTree();
|
|
52
|
-
const categoryOptions = categoriesToFacetOptions(categories);
|
|
53
|
-
const wishlistedIds = new Set(localWishlist.items.filter((i) => i.type === "classified").map((i) => i.itemId));
|
|
54
|
-
const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onResetAll, onFilterReset } = usePendingTable(table, FILTER_KEYS);
|
|
55
|
-
const openFilters = useCallback(() => {
|
|
56
|
-
onFilterReset();
|
|
57
|
-
setFilterOpen(true);
|
|
58
|
-
}, [onFilterReset]);
|
|
59
|
-
const applyFilters = useCallback(() => {
|
|
60
|
-
onFilterApply();
|
|
61
|
-
setFilterOpen(false);
|
|
62
|
-
}, [onFilterApply]);
|
|
63
|
-
const resetAll = useCallback(() => {
|
|
64
|
-
onResetAll({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" });
|
|
65
|
-
setSearchInput("");
|
|
66
|
-
}, [onResetAll]);
|
|
67
|
-
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
68
|
-
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
69
|
-
filterActiveCount > 0;
|
|
70
|
-
const params = {
|
|
71
|
-
q: table.get(TABLE_KEYS.QUERY) || undefined,
|
|
72
|
-
category: table.get(TABLE_KEYS.CATEGORY) || undefined,
|
|
73
|
-
minPrice: table.get(TABLE_KEYS.MIN_PRICE) ? Number(table.get(TABLE_KEYS.MIN_PRICE)) : undefined,
|
|
74
|
-
maxPrice: table.get(TABLE_KEYS.MAX_PRICE) ? Number(table.get(TABLE_KEYS.MAX_PRICE)) : undefined,
|
|
75
|
-
sort: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
|
|
76
|
-
page: table.getNumber(TABLE_KEYS.PAGE, 1),
|
|
77
|
-
perPage: table.getNumber(TABLE_KEYS.PAGE_SIZE, 24),
|
|
78
|
-
listingType: "classified",
|
|
79
|
-
};
|
|
80
|
-
const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
|
|
81
|
-
const commitSearch = useCallback(() => {
|
|
82
|
-
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
83
|
-
}, [searchInput, table]);
|
|
84
|
-
const selection = useBulkSelection({ items: products, keyExtractor: (p) => p.id });
|
|
85
|
-
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
|
|
86
|
-
const wishlistActions = {
|
|
87
|
-
addToWishlist: (productId) => {
|
|
88
|
-
requireAuth(ACTION_ID.ADD_TO_WISHLIST, () => {
|
|
89
|
-
localWishlist.add(productId, "classified");
|
|
90
|
-
pushWishlistOp({ op: "add", itemId: productId, type: "classified" });
|
|
91
|
-
showToast("Added to wishlist", "success");
|
|
92
|
-
});
|
|
93
|
-
return Promise.resolve();
|
|
94
|
-
},
|
|
95
|
-
removeFromWishlist: (productId) => {
|
|
96
|
-
requireAuth(ACTION_ID.REMOVE_FROM_WISHLIST, () => {
|
|
97
|
-
localWishlist.remove(productId, "classified");
|
|
98
|
-
pushWishlistOp({ op: "remove", itemId: productId, type: "classified" });
|
|
99
|
-
showToast("Removed from wishlist", "info");
|
|
100
|
-
});
|
|
101
|
-
return Promise.resolve();
|
|
102
|
-
},
|
|
103
|
-
isWishlisted: (productId) => wishlistedIds.has(productId),
|
|
104
|
-
};
|
|
105
|
-
function handleToggleWishlist(id) {
|
|
106
|
-
if (wishlistActions.isWishlisted(id)) {
|
|
107
|
-
wishlistActions.removeFromWishlist(id);
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
wishlistActions.addToWishlist(id);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] } } : {});
|
|
114
|
-
return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search classified listings\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: (e) => { if (e.key === "Enter")
|
|
115
|
-
commitSearch(); }, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: (v) => { if (v !== "table")
|
|
116
|
-
setView(v); }, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, toggles: [
|
|
117
|
-
// Inline quick-filter: the highest-frequency drawer-only facet promoted
|
|
118
|
-
// to the sticky toolbar so it doesn't require open-drawer → check → Apply
|
|
119
|
-
// → close for every toggle. The full facet set (category/city/price/
|
|
120
|
-
// negotiable) remains in <FilterDrawer> below.
|
|
121
|
-
{ label: "Ships nationwide", active: table.get(TABLE_KEYS.ACCEPTS_SHIPPING) === "true", onChange: (next) => table.set(TABLE_KEYS.ACCEPTS_SHIPPING, next ? "true" : "") },
|
|
122
|
-
] }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" })] })] }, i))) })) : products.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No classified listings found." })) : (_jsx(Div, { className: view === "list" ? "flex flex-col divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)] rounded-xl border border-[var(--appkit-color-border-subtle)]" : gridClass, children: products.map((product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.CLASSIFIED_DETAIL(product.id)), variant: view === "list" ? "list" : "grid", isWishlisted: wishlistActions.isWishlisted(product.id), onToggleWishlist: handleToggleWishlist, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ClassifiedFilters, { table: pendingTable, categoryOptions: categoryOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
|
|
123
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
type SearchParams = Record<string, string | string[]>;
|
|
2
|
-
export interface ClassifiedListViewProps {
|
|
3
|
-
searchParams?: SearchParams;
|
|
4
|
-
}
|
|
5
|
-
export declare function ClassifiedListView({ searchParams }: ClassifiedListViewProps): Promise<import("react").JSX.Element>;
|
|
6
|
-
export {};
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
3
|
-
import { productRepository } from "../../../repositories";
|
|
4
|
-
import { Container, Heading, Main, Section } from "../../../ui";
|
|
5
|
-
import { AdSlot } from "../../homepage/components/AdSlot";
|
|
6
|
-
import { parseListingSearchParams } from "../../../utils/listing-params";
|
|
7
|
-
import { ClassifiedIndexListing } from "./ClassifiedIndexListing";
|
|
8
|
-
const DEFAULT_PAGE = 1;
|
|
9
|
-
const DEFAULT_PAGE_SIZE = 24;
|
|
10
|
-
const DEFAULT_SORT = "-createdAt";
|
|
11
|
-
function sp(params, key) {
|
|
12
|
-
const v = params[key];
|
|
13
|
-
return Array.isArray(v) ? v[0] ?? "" : v ?? "";
|
|
14
|
-
}
|
|
15
|
-
function buildClassifiedFilters(params) {
|
|
16
|
-
const parts = ["status==published", "listingType==classified"];
|
|
17
|
-
const minPrice = sp(params, "minPrice");
|
|
18
|
-
const maxPrice = sp(params, "maxPrice");
|
|
19
|
-
if (minPrice)
|
|
20
|
-
parts.push(sieveFilter("price", SIEVE_OP.GTE, minPrice));
|
|
21
|
-
if (maxPrice)
|
|
22
|
-
parts.push(sieveFilter("price", SIEVE_OP.LTE, maxPrice));
|
|
23
|
-
const city = sp(params, "city");
|
|
24
|
-
if (city)
|
|
25
|
-
parts.push(sieveFilter("classifiedCity", SIEVE_OP.EQ, city));
|
|
26
|
-
const negotiable = sp(params, "negotiable");
|
|
27
|
-
if (negotiable === "true")
|
|
28
|
-
parts.push(sieveFilter("classifiedNegotiable", SIEVE_OP.EQ, "true"));
|
|
29
|
-
const acceptsShipping = sp(params, "acceptsShipping");
|
|
30
|
-
if (acceptsShipping === "true")
|
|
31
|
-
parts.push(sieveFilter("classifiedAcceptsShipping", SIEVE_OP.EQ, "true"));
|
|
32
|
-
return parts.join(",");
|
|
33
|
-
}
|
|
34
|
-
export async function ClassifiedListView({ searchParams = {} }) {
|
|
35
|
-
const std = parseListingSearchParams(searchParams);
|
|
36
|
-
const sort = std.sorts ?? DEFAULT_SORT;
|
|
37
|
-
const page = std.page ?? DEFAULT_PAGE;
|
|
38
|
-
const pageSize = std.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
39
|
-
const filters = buildClassifiedFilters(searchParams);
|
|
40
|
-
const result = await productRepository
|
|
41
|
-
.list({ filters, sorts: sort, page, pageSize })
|
|
42
|
-
.catch(() => null);
|
|
43
|
-
return (_jsx(Main, { children: _jsx(Section, { padding: "y-2xl", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-8", color: "primary", size: "3xl", weight: "semibold", children: "Classifieds" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(ClassifiedIndexListing, { initialData: result ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
|
|
44
|
-
}
|