@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
|
@@ -8,3 +8,28 @@ export declare const PAYMENT_WINDOW_MS: number;
|
|
|
8
8
|
/** Cancellation reasons this feature writes to `OrderDocument.cancellationReason` (a free-form string field). */
|
|
9
9
|
export declare const PAYMENT_WINDOW_EXPIRED_REASON = "payment_window_expired";
|
|
10
10
|
export declare const PAYMENT_FRAUD_REJECTED_REASON = "payment_fraud_rejected";
|
|
11
|
+
/**
|
|
12
|
+
* The `paymentMethod` values that settle out-of-band and therefore require a
|
|
13
|
+
* buyer-uploaded proof + a human review pass. Single source of truth for
|
|
14
|
+
* `attachPaymentProofAction`'s guard, the admin payment-review queue, and the
|
|
15
|
+
* buyer/seller "needs payment proof" UI — previously each site inlined its own
|
|
16
|
+
* `pm !== "cash" && pm !== "upi_manual" && ...` chain and they could drift.
|
|
17
|
+
*/
|
|
18
|
+
export declare const MANUAL_PAYMENT_METHODS: readonly string[];
|
|
19
|
+
export declare function isManualPaymentMethod(method: string | undefined | null): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* The two states an unresolved manual payment can be in. Derived at query
|
|
22
|
+
* time from `paymentProofUrl` + `paymentReviewOutcome` rather than stored as
|
|
23
|
+
* its own field — a denormalised mirror would drift the moment any write path
|
|
24
|
+
* forgot it (CLAUDE.md Root Cause Pattern #42).
|
|
25
|
+
*/
|
|
26
|
+
export type PaymentReviewQueueMode = "awaiting_proof" | "awaiting_verification";
|
|
27
|
+
export declare const PAYMENT_REVIEW_QUEUE_MODES: readonly PaymentReviewQueueMode[];
|
|
28
|
+
export declare function isPaymentReviewQueueMode(value: string): value is PaymentReviewQueueMode;
|
|
29
|
+
/**
|
|
30
|
+
* Hard bound on the payment-review queue's Firestore scan (Rule #6 — the
|
|
31
|
+
* Vercel 10s sync ceiling). Safe because an order only sits in this queue for
|
|
32
|
+
* 15 minutes (awaiting proof) or 2 hours (awaiting verification) before a
|
|
33
|
+
* scheduled sweep resolves it, so the live set is inherently small.
|
|
34
|
+
*/
|
|
35
|
+
export declare const PAYMENT_REVIEW_QUEUE_SCAN_LIMIT = 500;
|
|
@@ -8,3 +8,28 @@ export const PAYMENT_WINDOW_MS = PAYMENT_WINDOW_MINUTES * 60 * 1000;
|
|
|
8
8
|
/** Cancellation reasons this feature writes to `OrderDocument.cancellationReason` (a free-form string field). */
|
|
9
9
|
export const PAYMENT_WINDOW_EXPIRED_REASON = "payment_window_expired";
|
|
10
10
|
export const PAYMENT_FRAUD_REJECTED_REASON = "payment_fraud_rejected";
|
|
11
|
+
/**
|
|
12
|
+
* The `paymentMethod` values that settle out-of-band and therefore require a
|
|
13
|
+
* buyer-uploaded proof + a human review pass. Single source of truth for
|
|
14
|
+
* `attachPaymentProofAction`'s guard, the admin payment-review queue, and the
|
|
15
|
+
* buyer/seller "needs payment proof" UI — previously each site inlined its own
|
|
16
|
+
* `pm !== "cash" && pm !== "upi_manual" && ...` chain and they could drift.
|
|
17
|
+
*/
|
|
18
|
+
export const MANUAL_PAYMENT_METHODS = ["cash", "upi_manual", "emi"];
|
|
19
|
+
export function isManualPaymentMethod(method) {
|
|
20
|
+
return !!method && MANUAL_PAYMENT_METHODS.includes(method);
|
|
21
|
+
}
|
|
22
|
+
export const PAYMENT_REVIEW_QUEUE_MODES = [
|
|
23
|
+
"awaiting_proof",
|
|
24
|
+
"awaiting_verification",
|
|
25
|
+
];
|
|
26
|
+
export function isPaymentReviewQueueMode(value) {
|
|
27
|
+
return PAYMENT_REVIEW_QUEUE_MODES.includes(value);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Hard bound on the payment-review queue's Firestore scan (Rule #6 — the
|
|
31
|
+
* Vercel 10s sync ceiling). Safe because an order only sits in this queue for
|
|
32
|
+
* 15 minutes (awaiting proof) or 2 hours (awaiting verification) before a
|
|
33
|
+
* scheduled sweep resolves it, so the live set is inherently small.
|
|
34
|
+
*/
|
|
35
|
+
export const PAYMENT_REVIEW_QUEUE_SCAN_LIMIT = 500;
|
|
@@ -3,6 +3,7 @@ import type { FirebaseSieveResult, SieveModel } from "../../../providers/db-fire
|
|
|
3
3
|
import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
|
|
4
4
|
import { type OrderCreateInput, type OrderDocument, type OrderRefundEvent } from "../schemas";
|
|
5
5
|
import type { OrderStatus, PaymentStatus } from "../types";
|
|
6
|
+
import { type PaymentReviewQueueMode } from "../constants/payment-window";
|
|
6
7
|
declare class OrderRepository extends BaseRepository<OrderDocument> {
|
|
7
8
|
constructor();
|
|
8
9
|
private decryptOrder;
|
|
@@ -217,6 +218,37 @@ declare class OrderRepository extends BaseRepository<OrderDocument> {
|
|
|
217
218
|
ref: DocumentReference;
|
|
218
219
|
data: OrderDocument;
|
|
219
220
|
}>>;
|
|
221
|
+
/**
|
|
222
|
+
* Admin/seller payment-review queue — the manual-payment orders that are
|
|
223
|
+
* waiting on somebody. Two modes:
|
|
224
|
+
*
|
|
225
|
+
* - `awaiting_proof` — buyer hasn't uploaded a screenshot yet
|
|
226
|
+
* (the 15-minute `paymentDeadline` window).
|
|
227
|
+
* - `awaiting_verification` — proof submitted, no admin decision recorded
|
|
228
|
+
* yet (the 2-hour auto-approve window).
|
|
229
|
+
*
|
|
230
|
+
* Both refinements — proof presence and manual `paymentMethod` — are done
|
|
231
|
+
* in-memory rather than as Firestore clauses, deliberately:
|
|
232
|
+
*
|
|
233
|
+
* - `paymentProofUrl != null` would silently exclude every doc where the
|
|
234
|
+
* field was never set at all, which is exactly the `awaiting_proof` set
|
|
235
|
+
* (same reasoning as `getExpiredPaymentDeadlines` /
|
|
236
|
+
* `getUnreviewedProofPastDeadline` above).
|
|
237
|
+
* - keeping `paymentMethod` out of the query means this reuses the
|
|
238
|
+
* existing `(status, paymentStatus, createdAt)` composite index instead
|
|
239
|
+
* of needing a new four-field one.
|
|
240
|
+
*
|
|
241
|
+
* The scan is hard-bounded at {@link PAYMENT_REVIEW_QUEUE_SCAN_LIMIT} for
|
|
242
|
+
* the Vercel 10s ceiling. That bound is generous for this queue by
|
|
243
|
+
* construction: an order only sits in it for 15 minutes (awaiting proof) or
|
|
244
|
+
* 2 hours (awaiting verification) before a scheduled sweep resolves it, so
|
|
245
|
+
* the live set is inherently small. `total` therefore counts matches within
|
|
246
|
+
* the scan window, not an unbounded collection-wide count.
|
|
247
|
+
*/
|
|
248
|
+
listPaymentReviewQueue(mode: PaymentReviewQueueMode, opts?: {
|
|
249
|
+
page?: number;
|
|
250
|
+
pageSize?: number;
|
|
251
|
+
}): Promise<FirebaseSieveResult<OrderDocument>>;
|
|
220
252
|
/**
|
|
221
253
|
* Cloud Functions: find delivered orders eligible for the weekly payout sweep.
|
|
222
254
|
*
|
|
@@ -4,6 +4,7 @@ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../..
|
|
|
4
4
|
import { decryptPiiFields, encryptPiiFields, ORDER_PII_FIELDS, } from "../../../security";
|
|
5
5
|
import { serverTimestamp } from "../../../contracts/field-ops";
|
|
6
6
|
import { createOrderId, ORDER_COLLECTION, OrderStatusValues, } from "../schemas";
|
|
7
|
+
import { MANUAL_PAYMENT_METHODS, PAYMENT_REVIEW_QUEUE_SCAN_LIMIT, } from "../constants/payment-window";
|
|
7
8
|
import { ORDER_FIELDS } from "../../../constants/field-names";
|
|
8
9
|
/**
|
|
9
10
|
* Statuses that count toward a user's per-product / per-bundle purchase
|
|
@@ -340,6 +341,61 @@ class OrderRepository extends BaseRepository {
|
|
|
340
341
|
}))
|
|
341
342
|
.filter((entry) => !!entry.data.paymentProofUrl && !entry.data.paymentReviewOutcome);
|
|
342
343
|
}
|
|
344
|
+
/**
|
|
345
|
+
* Admin/seller payment-review queue — the manual-payment orders that are
|
|
346
|
+
* waiting on somebody. Two modes:
|
|
347
|
+
*
|
|
348
|
+
* - `awaiting_proof` — buyer hasn't uploaded a screenshot yet
|
|
349
|
+
* (the 15-minute `paymentDeadline` window).
|
|
350
|
+
* - `awaiting_verification` — proof submitted, no admin decision recorded
|
|
351
|
+
* yet (the 2-hour auto-approve window).
|
|
352
|
+
*
|
|
353
|
+
* Both refinements — proof presence and manual `paymentMethod` — are done
|
|
354
|
+
* in-memory rather than as Firestore clauses, deliberately:
|
|
355
|
+
*
|
|
356
|
+
* - `paymentProofUrl != null` would silently exclude every doc where the
|
|
357
|
+
* field was never set at all, which is exactly the `awaiting_proof` set
|
|
358
|
+
* (same reasoning as `getExpiredPaymentDeadlines` /
|
|
359
|
+
* `getUnreviewedProofPastDeadline` above).
|
|
360
|
+
* - keeping `paymentMethod` out of the query means this reuses the
|
|
361
|
+
* existing `(status, paymentStatus, createdAt)` composite index instead
|
|
362
|
+
* of needing a new four-field one.
|
|
363
|
+
*
|
|
364
|
+
* The scan is hard-bounded at {@link PAYMENT_REVIEW_QUEUE_SCAN_LIMIT} for
|
|
365
|
+
* the Vercel 10s ceiling. That bound is generous for this queue by
|
|
366
|
+
* construction: an order only sits in it for 15 minutes (awaiting proof) or
|
|
367
|
+
* 2 hours (awaiting verification) before a scheduled sweep resolves it, so
|
|
368
|
+
* the live set is inherently small. `total` therefore counts matches within
|
|
369
|
+
* the scan window, not an unbounded collection-wide count.
|
|
370
|
+
*/
|
|
371
|
+
async listPaymentReviewQueue(mode, opts = {}) {
|
|
372
|
+
const page = Math.max(1, opts.page ?? 1);
|
|
373
|
+
const pageSize = Math.min(50, Math.max(1, opts.pageSize ?? 25));
|
|
374
|
+
const snap = await this.db
|
|
375
|
+
.collection(this.collection)
|
|
376
|
+
.where(ORDER_FIELDS.STATUS, "==", OrderStatusValues.PENDING)
|
|
377
|
+
.where(ORDER_FIELDS.PAYMENT_STATUS, "==", "pending")
|
|
378
|
+
.orderBy(ORDER_FIELDS.CREATED_AT, "desc")
|
|
379
|
+
.limit(PAYMENT_REVIEW_QUEUE_SCAN_LIMIT)
|
|
380
|
+
.get();
|
|
381
|
+
const matches = snap.docs
|
|
382
|
+
.map((d) => this.decryptOrder({ id: d.id, ...d.data() }))
|
|
383
|
+
.filter((order) => MANUAL_PAYMENT_METHODS.includes(order.paymentMethod ?? ""))
|
|
384
|
+
.filter((order) => mode === "awaiting_proof"
|
|
385
|
+
? !order.paymentProofUrl
|
|
386
|
+
: !!order.paymentProofUrl && !order.paymentReviewOutcome);
|
|
387
|
+
const total = matches.length;
|
|
388
|
+
const totalPages = total === 0 ? 0 : Math.max(1, Math.ceil(total / pageSize));
|
|
389
|
+
const start = (page - 1) * pageSize;
|
|
390
|
+
return {
|
|
391
|
+
items: matches.slice(start, start + pageSize),
|
|
392
|
+
total,
|
|
393
|
+
page,
|
|
394
|
+
pageSize,
|
|
395
|
+
totalPages,
|
|
396
|
+
hasMore: page < totalPages,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
343
399
|
/**
|
|
344
400
|
* Cloud Functions: find delivered orders eligible for the weekly payout sweep.
|
|
345
401
|
*
|
|
@@ -570,15 +570,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
570
570
|
bin: string;
|
|
571
571
|
} | undefined;
|
|
572
572
|
payoutStatus?: "paid" | "eligible" | "requested" | undefined;
|
|
573
|
-
items?: {
|
|
574
|
-
productId: string;
|
|
575
|
-
productTitle: string;
|
|
576
|
-
quantity: number;
|
|
577
|
-
unitPrice: number;
|
|
578
|
-
totalPrice: number;
|
|
579
|
-
listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
|
|
580
|
-
prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
|
|
581
|
-
}[] | undefined;
|
|
582
573
|
paymentId?: string | undefined;
|
|
583
574
|
shippingAddress?: string | undefined;
|
|
584
575
|
trackingNumber?: string | undefined;
|
|
@@ -601,6 +592,15 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
601
592
|
shippingMethod?: "custom" | undefined;
|
|
602
593
|
shippingCarrier?: string | undefined;
|
|
603
594
|
trackingUrl?: string | undefined;
|
|
595
|
+
items?: {
|
|
596
|
+
productId: string;
|
|
597
|
+
productTitle: string;
|
|
598
|
+
quantity: number;
|
|
599
|
+
unitPrice: number;
|
|
600
|
+
totalPrice: number;
|
|
601
|
+
listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
|
|
602
|
+
prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
|
|
603
|
+
}[] | undefined;
|
|
604
604
|
bundleId?: string | undefined;
|
|
605
605
|
prizeWon?: {
|
|
606
606
|
title: string;
|
|
@@ -612,6 +612,8 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
612
612
|
} | undefined;
|
|
613
613
|
prizeDrawProductId?: string | undefined;
|
|
614
614
|
couponCode?: string | undefined;
|
|
615
|
+
emiEnabled?: boolean | undefined;
|
|
616
|
+
emiTenureMonths?: number | undefined;
|
|
615
617
|
orderType?: string | undefined;
|
|
616
618
|
imageUrls?: string[] | undefined;
|
|
617
619
|
refundFeeDeducted?: number | undefined;
|
|
@@ -627,8 +629,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
627
629
|
couponId?: string | undefined;
|
|
628
630
|
}[] | undefined;
|
|
629
631
|
codHandlingFee?: number | undefined;
|
|
630
|
-
emiEnabled?: boolean | undefined;
|
|
631
|
-
emiTenureMonths?: number | undefined;
|
|
632
632
|
emiTokenAmount?: number | undefined;
|
|
633
633
|
emiSurchargeAmount?: number | undefined;
|
|
634
634
|
emiInstallments?: {
|
|
@@ -718,15 +718,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
718
718
|
bin: string;
|
|
719
719
|
} | undefined;
|
|
720
720
|
payoutStatus?: "paid" | "eligible" | "requested" | undefined;
|
|
721
|
-
items?: {
|
|
722
|
-
productId: string;
|
|
723
|
-
productTitle: string;
|
|
724
|
-
quantity: number;
|
|
725
|
-
unitPrice: number;
|
|
726
|
-
totalPrice: number;
|
|
727
|
-
listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
|
|
728
|
-
prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
|
|
729
|
-
}[] | undefined;
|
|
730
721
|
paymentId?: string | undefined;
|
|
731
722
|
shippingAddress?: string | undefined;
|
|
732
723
|
trackingNumber?: string | undefined;
|
|
@@ -749,6 +740,15 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
749
740
|
shippingMethod?: "custom" | undefined;
|
|
750
741
|
shippingCarrier?: string | undefined;
|
|
751
742
|
trackingUrl?: string | undefined;
|
|
743
|
+
items?: {
|
|
744
|
+
productId: string;
|
|
745
|
+
productTitle: string;
|
|
746
|
+
quantity: number;
|
|
747
|
+
unitPrice: number;
|
|
748
|
+
totalPrice: number;
|
|
749
|
+
listingType?: "classified" | "standard" | "auction" | "pre-order" | "prize-draw" | "digital-code" | "live" | undefined;
|
|
750
|
+
prizeRevealStatus?: "closed" | "open" | "pending" | "revealed" | undefined;
|
|
751
|
+
}[] | undefined;
|
|
752
752
|
bundleId?: string | undefined;
|
|
753
753
|
prizeWon?: {
|
|
754
754
|
title: string;
|
|
@@ -760,6 +760,8 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
760
760
|
} | undefined;
|
|
761
761
|
prizeDrawProductId?: string | undefined;
|
|
762
762
|
couponCode?: string | undefined;
|
|
763
|
+
emiEnabled?: boolean | undefined;
|
|
764
|
+
emiTenureMonths?: number | undefined;
|
|
763
765
|
orderType?: string | undefined;
|
|
764
766
|
imageUrls?: string[] | undefined;
|
|
765
767
|
refundFeeDeducted?: number | undefined;
|
|
@@ -775,8 +777,6 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
775
777
|
couponId?: string | undefined;
|
|
776
778
|
}[] | undefined;
|
|
777
779
|
codHandlingFee?: number | undefined;
|
|
778
|
-
emiEnabled?: boolean | undefined;
|
|
779
|
-
emiTenureMonths?: number | undefined;
|
|
780
780
|
emiTokenAmount?: number | undefined;
|
|
781
781
|
emiSurchargeAmount?: number | undefined;
|
|
782
782
|
emiInstallments?: {
|
|
@@ -962,6 +962,7 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
962
962
|
[k: string]: unknown;
|
|
963
963
|
};
|
|
964
964
|
userId: string;
|
|
965
|
+
paymentStatus: string;
|
|
965
966
|
items: {
|
|
966
967
|
title: string;
|
|
967
968
|
price: number;
|
|
@@ -972,7 +973,6 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
972
973
|
storeId?: string | undefined;
|
|
973
974
|
attributes?: Record<string, string> | undefined;
|
|
974
975
|
}[];
|
|
975
|
-
paymentStatus: string;
|
|
976
976
|
total: number;
|
|
977
977
|
subtotal: number;
|
|
978
978
|
orderStatus: "pending" | "cancelled" | "processing" | "refunded" | "delivered" | "confirmed" | "shipped" | "return_requested" | "returned";
|
|
@@ -999,6 +999,7 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
999
999
|
[k: string]: unknown;
|
|
1000
1000
|
};
|
|
1001
1001
|
userId: string;
|
|
1002
|
+
paymentStatus: string;
|
|
1002
1003
|
items: {
|
|
1003
1004
|
title: string;
|
|
1004
1005
|
price: number;
|
|
@@ -1009,7 +1010,6 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
1009
1010
|
storeId?: string | undefined;
|
|
1010
1011
|
attributes?: Record<string, string> | undefined;
|
|
1011
1012
|
}[];
|
|
1012
|
-
paymentStatus: string;
|
|
1013
1013
|
total: number;
|
|
1014
1014
|
subtotal: number;
|
|
1015
1015
|
orderStatus: "pending" | "cancelled" | "processing" | "refunded" | "delivered" | "confirmed" | "shipped" | "return_requested" | "returned";
|
|
@@ -67,6 +67,8 @@ export interface Order {
|
|
|
67
67
|
couponCode?: string;
|
|
68
68
|
trackingNumber?: string;
|
|
69
69
|
shippingCarrier?: string;
|
|
70
|
+
/** Carrier tracking page URL — rendered as an external link that opens in a new tab. No live-tracking API integration; this is just the passthrough URL a seller/admin entered. */
|
|
71
|
+
trackingUrl?: string;
|
|
70
72
|
notes?: string;
|
|
71
73
|
timeline?: OrderTimeline[];
|
|
72
74
|
physicalLocation?: {
|
|
@@ -76,10 +78,41 @@ export interface Order {
|
|
|
76
78
|
};
|
|
77
79
|
createdAt?: string;
|
|
78
80
|
updatedAt?: string;
|
|
81
|
+
/** Real per-status timestamps written by the order-status-update path — used to render a genuine status timeline (not fabricated/estimated). */
|
|
82
|
+
orderDate?: string;
|
|
83
|
+
shippingDate?: string;
|
|
84
|
+
deliveryDate?: string;
|
|
85
|
+
cancellationDate?: string;
|
|
79
86
|
/** Tier PP — true when the 2-hour auto-approve sweep confirmed this order's payment instead of a manual admin review. Drives the "Raise a dispute" CTA. */
|
|
80
87
|
autoApproved?: boolean;
|
|
81
88
|
disputeRaised?: boolean;
|
|
82
89
|
disputeStatus?: "open" | "resolved";
|
|
90
|
+
/**
|
|
91
|
+
* Manual-payment (cash / UPI / EMI) fields. Every one of these already
|
|
92
|
+
* existed on `OrderDocument` but was dropped by `orderDocumentToOrder`,
|
|
93
|
+
* so the buyer-facing surfaces that need them read `undefined`:
|
|
94
|
+
* `/user/orders/[id]/payment` gated its whole render on `paymentMethod`
|
|
95
|
+
* and therefore told every buyer "this order does not require manual
|
|
96
|
+
* payment upload", and the 15-minute countdown + UPI ID never rendered.
|
|
97
|
+
* (CLAUDE.md Root Cause Pattern #38 — adapter/serializer field drift.)
|
|
98
|
+
*/
|
|
99
|
+
paymentMethod?: string;
|
|
100
|
+
/** UPI ID the buyer was told to pay to — shown on the payment page and diffed against `buyerReportedUpiId` during admin review. */
|
|
101
|
+
displayedUpiId?: string;
|
|
102
|
+
/** ISO end of the 15-minute payment window; drives the buyer countdown. */
|
|
103
|
+
paymentDeadline?: string;
|
|
104
|
+
paymentProofUrl?: string;
|
|
105
|
+
paymentProofUploadedAt?: string;
|
|
106
|
+
paymentTransactionId?: string;
|
|
107
|
+
buyerReportedUpiId?: string;
|
|
108
|
+
buyerMarkedPaid?: boolean;
|
|
109
|
+
paymentUpiMismatch?: boolean;
|
|
110
|
+
/** Admin decision on the submitted proof; unset means "not reviewed yet". */
|
|
111
|
+
paymentReviewOutcome?: "approved" | "reupload_requested" | "rejected_fraud";
|
|
112
|
+
/** Admin's note — the reason shown to the buyer on a re-upload request or fraud rejection. */
|
|
113
|
+
paymentReviewNote?: string;
|
|
114
|
+
/** Why the order was cancelled — distinguishes a payment-window expiry / fraud rejection from an ordinary cancellation. */
|
|
115
|
+
cancellationReason?: string;
|
|
83
116
|
/**
|
|
84
117
|
* Reveal-mode prize-draw fields (SB4-H/SB8-C). Set once the winner has
|
|
85
118
|
* been automatically assigned to this order (on payment confirmation,
|
|
@@ -30,6 +30,10 @@ import { ShareButton } from "../../products/components/ShareButton";
|
|
|
30
30
|
import { SublistingCarouselSection } from "../../products/components/SublistingCarouselSection";
|
|
31
31
|
import { HistoryTracker } from "../../history/components/HistoryTracker";
|
|
32
32
|
import { ShowGroupSection } from "../../products/components/ShowGroupSection";
|
|
33
|
+
import { RelatedItemsSection } from "../../products/components/RelatedItemsSection";
|
|
34
|
+
import { computeRelatedItems } from "../../../_internal/server/features/products/data";
|
|
35
|
+
import { GroupedListingsCarousel } from "../../grouped/components/GroupedListingsCarousel";
|
|
36
|
+
import { getGroupsWithItemsForProduct } from "../../../_internal/server/features/grouped/data";
|
|
33
37
|
function toDescriptionHtml(raw) {
|
|
34
38
|
if (!raw)
|
|
35
39
|
return "";
|
|
@@ -141,6 +145,10 @@ export async function PreOrderDetailPageView({ id, initialPreOrder, onReserveNow
|
|
|
141
145
|
const groupId = typeof p.groupId === "string" ? p.groupId : null;
|
|
142
146
|
const isGroupParent = p.isGroupParent === true;
|
|
143
147
|
const groupTitle = typeof p.groupTitle === "string" ? p.groupTitle : undefined;
|
|
148
|
+
const [{ relatedItems, relatedByBrand, relatedByTags, relatedByStore }, groups] = await Promise.all([
|
|
149
|
+
computeRelatedItems(product),
|
|
150
|
+
getGroupsWithItemsForProduct(product.id),
|
|
151
|
+
]);
|
|
144
152
|
return (_jsxs(Main, { children: [_jsx(HistoryTracker, { productId: String(p.id ?? p.slug ?? ""), productType: "preorder", snapshot: {
|
|
145
153
|
title: typeof p.title === "string" ? p.title : undefined,
|
|
146
154
|
thumb: typeof p.mainImage === "string"
|
|
@@ -159,5 +167,5 @@ export async function PreOrderDetailPageView({ id, initialPreOrder, onReserveNow
|
|
|
159
167
|
id: s.id,
|
|
160
168
|
label: s.title,
|
|
161
169
|
content: _jsx(CustomSectionTabContent, { section: s }),
|
|
162
|
-
})) })), renderBuyBar: () => (_jsx(PreOrderBuyBarPanel, { reserveTarget: reserveTarget, reservedCount: reservedCount, progressPct: progressPct, productId: String(product.id), price: price, currency: currency, depositAmount: depositAmount, depositPercent: depositPercent, isCancellable: isCancellable, tags: tags, onReserveNow: onReserveNow })) }), _jsx(PreOrderBottomActions, { price: price, currency: currency })] })] }));
|
|
170
|
+
})) })), renderBuyBar: () => (_jsx(PreOrderBuyBarPanel, { reserveTarget: reserveTarget, reservedCount: reservedCount, progressPct: progressPct, productId: String(product.id), price: price, currency: currency, depositAmount: depositAmount, depositPercent: depositPercent, isCancellable: isCancellable, tags: tags, onReserveNow: onReserveNow })), renderRelated: () => (_jsxs(Stack, { gap: "xl", children: [_jsx(GroupedListingsCarousel, { groups: groups }), _jsx(RelatedItemsSection, { relatedItems: relatedItems, relatedByBrand: relatedByBrand, relatedByTags: relatedByTags, relatedByStore: relatedByStore, categoryLabel: categoryName || category || undefined, brandLabel: brand || undefined, storeLabel: storeName || undefined })] })) }), _jsx(PreOrderBottomActions, { price: price, currency: currency })] })] }));
|
|
163
171
|
}
|
|
@@ -21,6 +21,7 @@ export declare const preorderItemSchema: z.ZodObject<{
|
|
|
21
21
|
thumbnailUrl: z.ZodOptional<z.ZodString>;
|
|
22
22
|
source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
|
|
23
23
|
youtubeId: z.ZodOptional<z.ZodString>;
|
|
24
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
24
25
|
}, "strip", z.ZodTypeAny, {
|
|
25
26
|
type: "video" | "image" | "file";
|
|
26
27
|
url: string;
|
|
@@ -28,6 +29,7 @@ export declare const preorderItemSchema: z.ZodObject<{
|
|
|
28
29
|
alt?: string | undefined;
|
|
29
30
|
thumbnailUrl?: string | undefined;
|
|
30
31
|
youtubeId?: string | undefined;
|
|
32
|
+
duration?: number | undefined;
|
|
31
33
|
}, {
|
|
32
34
|
type: "video" | "image" | "file";
|
|
33
35
|
url: string;
|
|
@@ -35,6 +37,7 @@ export declare const preorderItemSchema: z.ZodObject<{
|
|
|
35
37
|
alt?: string | undefined;
|
|
36
38
|
thumbnailUrl?: string | undefined;
|
|
37
39
|
youtubeId?: string | undefined;
|
|
40
|
+
duration?: number | undefined;
|
|
38
41
|
}>, "many">>;
|
|
39
42
|
salePrice: z.ZodNumber;
|
|
40
43
|
regularPrice: z.ZodNumber;
|
|
@@ -62,6 +65,7 @@ export declare const preorderItemSchema: z.ZodObject<{
|
|
|
62
65
|
alt?: string | undefined;
|
|
63
66
|
thumbnailUrl?: string | undefined;
|
|
64
67
|
youtubeId?: string | undefined;
|
|
68
|
+
duration?: number | undefined;
|
|
65
69
|
}[] | undefined;
|
|
66
70
|
franchise?: string | undefined;
|
|
67
71
|
preorderShipDate?: string | undefined;
|
|
@@ -82,6 +86,7 @@ export declare const preorderItemSchema: z.ZodObject<{
|
|
|
82
86
|
alt?: string | undefined;
|
|
83
87
|
thumbnailUrl?: string | undefined;
|
|
84
88
|
youtubeId?: string | undefined;
|
|
89
|
+
duration?: number | undefined;
|
|
85
90
|
}[] | undefined;
|
|
86
91
|
isFeatured?: boolean | undefined;
|
|
87
92
|
franchise?: string | undefined;
|
|
@@ -19,6 +19,14 @@ function formatBidDate(raw) {
|
|
|
19
19
|
return raw;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
+
/** Bidder identity is already masked server-side (`maskPublicBid`) when a
|
|
23
|
+
* display name is present; this is only a fallback for bare bidder ids. */
|
|
24
|
+
function maskBidderId(id) {
|
|
25
|
+
if (!id)
|
|
26
|
+
return "Anonymous";
|
|
27
|
+
const tail = id.slice(-4);
|
|
28
|
+
return `Bidder ***${tail}`;
|
|
29
|
+
}
|
|
22
30
|
export function BidHistory({ isLoading = false, isEmpty = false, renderList, renderBid, bids = [], renderEmpty, renderSkeleton, currency, labels = {}, className = "", }) {
|
|
23
31
|
if (isLoading) {
|
|
24
32
|
if (renderSkeleton)
|
|
@@ -32,5 +40,5 @@ export function BidHistory({ isLoading = false, isEmpty = false, renderList, ren
|
|
|
32
40
|
}
|
|
33
41
|
if (renderList)
|
|
34
42
|
return _jsx(Div, { className: className, children: renderList() });
|
|
35
|
-
return (_jsxs(Div, { className: className, children: [labels.title && (_jsx(Heading, { level: 3, className: "mb-3 tracking-wide", color: "muted", size: "sm", weight: "semibold", transform: "uppercase", children: labels.title })), _jsx(Stack, { gap: "sm", children: bids.map((bid, i) => renderBid ? (_jsx(React.Fragment, { children: renderBid(bid, i) }, bid.id)) : (_jsxs(Div, { layout: "flex", align: "center", justify: "between", rounded: "lg", padding: "sm", textSize: "sm", className: "border border-[var(--appkit-color-border-subtle)] bg-[var(--appkit-color-surface)]", "data-section": "bidhistory-div-422", children: [_jsx(Span, { weight: "bold", className: "text-primary-600 dark:text-primary-400", children: currency ? formatCurrency(bid.amount, currency) : bid.amount.toLocaleString() }), _jsx(Span, { size: "xs", color: "muted", children: formatBidDate(bid.placedAt) })] }, bid.id))) })] }));
|
|
43
|
+
return (_jsxs(Div, { className: className, children: [labels.title && (_jsx(Heading, { level: 3, className: "mb-3 tracking-wide", color: "muted", size: "sm", weight: "semibold", transform: "uppercase", children: labels.title })), _jsx(Stack, { gap: "sm", children: bids.map((bid, i) => renderBid ? (_jsx(React.Fragment, { children: renderBid(bid, i) }, bid.id)) : (_jsxs(Div, { layout: "flex", align: "center", justify: "between", rounded: "lg", padding: "sm", textSize: "sm", className: "border border-[var(--appkit-color-border-subtle)] bg-[var(--appkit-color-surface)]", "data-section": "bidhistory-div-422", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Span, { weight: "bold", className: "text-primary-600 dark:text-primary-400", children: currency ? formatCurrency(bid.amount, currency) : bid.amount.toLocaleString() }), _jsx(Span, { size: "xs", color: "muted", children: bid.bidderName || maskBidderId(bid.bidderId) })] }), _jsx(Span, { size: "xs", color: "muted", children: formatBidDate(bid.placedAt) })] }, bid.id))) })] }));
|
|
36
44
|
}
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
|
-
import { useState } from "react";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import { z } from "zod";
|
|
5
6
|
import { Button, Code, ConfirmDeleteModal, Div, FormField, Heading, Modal, Row, Select, SideDrawer, Span, Stack, Tabs, TabsContent, TabsList, TabsTrigger, Text, useToast } from "../../../ui";
|
|
7
|
+
import { FormShellContext, useFormShellState, FormErrorSummary } from "../../../ui/forms";
|
|
6
8
|
import { MediaImage } from "../../media/MediaImage";
|
|
7
9
|
import { apiClient } from "../../../http";
|
|
8
10
|
import { PRODUCT_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
11
|
import { ProductInlineSelect } from "../../seller/components/ProductInlineSelect";
|
|
12
|
+
const groupTitleSchema = z.object({
|
|
13
|
+
groupTitle: z.string().min(1, "Group title is required"),
|
|
14
|
+
});
|
|
15
|
+
const createChildSchema = z.object({
|
|
16
|
+
title: z.string().min(1, "Title is required"),
|
|
17
|
+
price: z.string().refine((v) => v.trim() !== "" && !Number.isNaN(parseFloat(v)) && parseFloat(v) > 0, "Enter a valid price"),
|
|
18
|
+
condition: z.string(),
|
|
19
|
+
});
|
|
10
20
|
const __P = {
|
|
11
21
|
p4: "p-[var(--appkit-space-4)]",
|
|
12
22
|
};
|
|
@@ -33,6 +43,11 @@ export function GroupSettingsPanel({ productId, productSlug, groupId, isGroupPar
|
|
|
33
43
|
const [linkTargets, setLinkTargets] = useState([]);
|
|
34
44
|
const [children, setChildren] = useState(null);
|
|
35
45
|
const [confirmAction, setConfirmAction] = useState(null);
|
|
46
|
+
const { shellCtx: titleShellCtx, validate: validateTitle } = useFormShellState(groupTitleSchema);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
validateTitle({ groupTitle: editTitle });
|
|
49
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
50
|
+
}, [editTitle, validateTitle]);
|
|
36
51
|
if (isAuction)
|
|
37
52
|
return null;
|
|
38
53
|
const groupEndpoint = `${storeProductsEndpoint}/${productId}/group`;
|
|
@@ -69,6 +84,8 @@ export function GroupSettingsPanel({ productId, productSlug, groupId, isGroupPar
|
|
|
69
84
|
}
|
|
70
85
|
}
|
|
71
86
|
async function saveTitle() {
|
|
87
|
+
if (Object.keys(titleShellCtx.errors).length > 0)
|
|
88
|
+
return;
|
|
72
89
|
setLoading(true);
|
|
73
90
|
try {
|
|
74
91
|
await apiClient.patch(groupEndpoint, { groupTitle: editTitle });
|
|
@@ -218,8 +235,13 @@ export function GroupSettingsPanel({ productId, productSlug, groupId, isGroupPar
|
|
|
218
235
|
setOpen(next);
|
|
219
236
|
if (next && isGroupParent && children === null)
|
|
220
237
|
loadChildren();
|
|
221
|
-
}, paddingX: "none", paddingY: "xs", justify: "between", className: "w-full text-left group", "aria-expanded": open, children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", color: "muted", children: "Group Settings" }), _jsx(Span, { size: "xs", className: "group-hover:text-[var(--appkit-color-text-muted)] dark:group-hover:text-[var(--appkit-color-text-faint)] transition-colors", color: "faint", children: open ? "▲" : "▼" })] }), open && (_jsxs(Stack, { gap: "sm", className: `mt-3 ${__P.p4}`, surface: "muted", rounded: "lg", border: "default", children: [!groupId && !groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Group related listings together \u2014 e.g. a set, bundle, or multi-part item. Parts can be sold individually but shown together." }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: startGroup, isLoading: loading, children: "Start a group" })] })), isGroupParent && groupId && (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { align: "start", gap: "sm", wrap: true, children: [_jsx(Div, { className: "flex-1 min-w-[200px]", children: _jsx(FormField, { name: "groupTitle", label: "Group title", type: "text", value: editTitle, onChange: setEditTitle, placeholder: "e.g. Human Toy Complete Set" }) }), _jsx(Div, { padding: "t-lg", children: _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: saveTitle, isLoading: loading, children: "Save title" }) })] }), _jsxs(Div, { children: [_jsxs(Text, { className: "mb-2", color: "muted", size: "xs", weight: "semibold", children: ["Members (", childSlugsCount + 1, " \u2014 including this listing)"] }), loading && !children ? (_jsx(Text, { size: "xs", color: "faint", children: "Loading\u2026" })) : (_jsxs(Div, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", children: [_jsxs(Row, { align: "center", gap: "sm", padding: "y-xs", children: [_jsx(Span, { padding: "pill-2xs", weight: "semibold", className: "bg-[var(--appkit-color-primary)]/10 text-[var(--appkit-color-primary)] text-[10px]", rounded: "default", children: "Parent" }), _jsx(Text, { className: "flex-1", color: "primary", size: "sm", children: productSlug })] }), (children ?? []).filter((c) => c.id !== productId).map((child) => (_jsxs(Row, { align: "center", gap: "sm", padding: "y-xs", children: [child.images?.[0] ? (_jsx(Div, { className: "relative w-8 h-8 flex-shrink-0 border border-[var(--appkit-color-border)]", rounded: "full", overflow: "hidden", children: _jsx(MediaImage, { src: child.images[0], alt: child.title, size: "thumbnail" }) })) : (_jsx(Div, { className: "w-8 h-8", surface: "subtle", rounded: "full" })), _jsx(Text, { className: "flex-1 truncate", color: "primary", size: "sm", children: child.title }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => requestUnlinkChild(child.id), isLoading: loading, className: `${CLS_DELETE_LINK} text-[length:var(--appkit-text-xs)]`, children: "Unlink" })] }, child.id)))] }))] }), _jsxs(Row, { gap: "sm", wrap: true, children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setShowAddModal(true), children: "Add child listing" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestDissolveGroup, isLoading: loading, className: CLS_DELETE_LINK, children: "Dissolve group" })] })] })), !isGroupParent && groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "sm", color: "muted", children: ["Part of:", " ", _jsx(Span, { weight: "medium", color: "primary", children: groupTitle ?? groupParentSlug })] }), _jsxs(Text, { size: "xs", color: "muted", children: ["Parent listing: ", _jsx(Code, { className: "font-mono", children: groupParentSlug })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestLeaveGroup, isLoading: loading, className: `${CLS_DELETE_LINK} w-fit`, children: "Leave group" })] }))] })), (groupChildSlugs?.length ?? 0) >= 4 ? (_jsx(SideDrawer, { isOpen: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, storeProductsEndpoint: storeProductsEndpoint, linkTargets: linkTargets, setLinkTargets: setLinkTargets, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })) : (_jsx(Modal, { open: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", size: "lg", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, storeProductsEndpoint: storeProductsEndpoint, linkTargets: linkTargets, setLinkTargets: setLinkTargets, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })), confirmAction && (_jsx(ConfirmDeleteModal, { isOpen: true, title: confirmAction.title, message: confirmAction.message, onConfirm: confirmAction.onConfirm, onClose: () => setConfirmAction(null), isDeleting: loading }))] }));
|
|
238
|
+
}, paddingX: "none", paddingY: "xs", justify: "between", className: "w-full text-left group", "aria-expanded": open, children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", color: "muted", children: "Group Settings" }), _jsx(Span, { size: "xs", className: "group-hover:text-[var(--appkit-color-text-muted)] dark:group-hover:text-[var(--appkit-color-text-faint)] transition-colors", color: "faint", children: open ? "▲" : "▼" })] }), open && (_jsxs(Stack, { gap: "sm", className: `mt-3 ${__P.p4}`, surface: "muted", rounded: "lg", border: "default", children: [!groupId && !groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Group related listings together \u2014 e.g. a set, bundle, or multi-part item. Parts can be sold individually but shown together." }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: startGroup, isLoading: loading, children: "Start a group" })] })), isGroupParent && groupId && (_jsxs(Stack, { gap: "md", children: [_jsxs(FormShellContext.Provider, { value: titleShellCtx, children: [_jsxs(Row, { align: "start", gap: "sm", wrap: true, children: [_jsx(Div, { className: "flex-1 min-w-[200px]", children: _jsx(FormField, { name: "groupTitle", label: "Group title", type: "text", value: editTitle, onChange: setEditTitle, placeholder: "e.g. Human Toy Complete Set" }) }), _jsx(Div, { padding: "t-lg", children: _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: saveTitle, isLoading: loading, children: "Save title" }) })] }), _jsx(FormErrorSummary, {})] }), _jsxs(Div, { children: [_jsxs(Text, { className: "mb-2", color: "muted", size: "xs", weight: "semibold", children: ["Members (", childSlugsCount + 1, " \u2014 including this listing)"] }), loading && !children ? (_jsx(Text, { size: "xs", color: "faint", children: "Loading\u2026" })) : (_jsxs(Div, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", children: [_jsxs(Row, { align: "center", gap: "sm", padding: "y-xs", children: [_jsx(Span, { padding: "pill-2xs", weight: "semibold", className: "bg-[var(--appkit-color-primary)]/10 text-[var(--appkit-color-primary)] text-[10px]", rounded: "default", children: "Parent" }), _jsx(Text, { className: "flex-1", color: "primary", size: "sm", children: productSlug })] }), (children ?? []).filter((c) => c.id !== productId).map((child) => (_jsxs(Row, { align: "center", gap: "sm", padding: "y-xs", children: [child.images?.[0] ? (_jsx(Div, { className: "relative w-8 h-8 flex-shrink-0 border border-[var(--appkit-color-border)]", rounded: "full", overflow: "hidden", children: _jsx(MediaImage, { src: child.images[0], alt: child.title, size: "thumbnail" }) })) : (_jsx(Div, { className: "w-8 h-8", surface: "subtle", rounded: "full" })), _jsx(Text, { className: "flex-1 truncate", color: "primary", size: "sm", children: child.title }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => requestUnlinkChild(child.id), isLoading: loading, className: `${CLS_DELETE_LINK} text-[length:var(--appkit-text-xs)]`, children: "Unlink" })] }, child.id)))] }))] }), _jsxs(Row, { gap: "sm", wrap: true, children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setShowAddModal(true), children: "Add child listing" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestDissolveGroup, isLoading: loading, className: CLS_DELETE_LINK, children: "Dissolve group" })] })] })), !isGroupParent && groupParentSlug && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "sm", color: "muted", children: ["Part of:", " ", _jsx(Span, { weight: "medium", color: "primary", children: groupTitle ?? groupParentSlug })] }), _jsxs(Text, { size: "xs", color: "muted", children: ["Parent listing: ", _jsx(Code, { className: "font-mono", children: groupParentSlug })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: requestLeaveGroup, isLoading: loading, className: `${CLS_DELETE_LINK} w-fit`, children: "Leave group" })] }))] })), (groupChildSlugs?.length ?? 0) >= 4 ? (_jsx(SideDrawer, { isOpen: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, storeProductsEndpoint: storeProductsEndpoint, linkTargets: linkTargets, setLinkTargets: setLinkTargets, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })) : (_jsx(Modal, { open: showAddModal, onClose: () => setShowAddModal(false), title: "Add child listing", size: "lg", children: _jsx(AddChildContent, { addTab: addTab, setAddTab: setAddTab, createForm: createForm, setCreateForm: setCreateForm, productSlug: productSlug, storeProductsEndpoint: storeProductsEndpoint, linkTargets: linkTargets, setLinkTargets: setLinkTargets, onAddCreate: addCreateChild, onAddLink: addLinkChild, loading: loading }) })), confirmAction && (_jsx(ConfirmDeleteModal, { isOpen: true, title: confirmAction.title, message: confirmAction.message, onConfirm: confirmAction.onConfirm, onClose: () => setConfirmAction(null), isDeleting: loading }))] }));
|
|
222
239
|
}
|
|
223
240
|
function AddChildContent({ addTab, setAddTab, createForm, setCreateForm, productSlug, storeProductsEndpoint, linkTargets, setLinkTargets, onAddCreate, onAddLink, loading, }) {
|
|
224
|
-
|
|
241
|
+
const { shellCtx, validate } = useFormShellState(createChildSchema);
|
|
242
|
+
useEffect(() => {
|
|
243
|
+
validate(createForm);
|
|
244
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
245
|
+
}, [createForm, validate]);
|
|
246
|
+
return (_jsx(Stack, { gap: "md", children: _jsxs(Tabs, { value: addTab, onChange: (v) => setAddTab(v), children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "create", children: "Create new child" }), _jsx(TabsTrigger, { value: "link", children: "Link existing" })] }), _jsx(TabsContent, { value: "create", children: _jsx(FormShellContext.Provider, { value: shellCtx, children: _jsxs(Stack, { gap: "sm", className: "mt-4", children: [_jsx(FormField, { name: "title", label: "Title *", type: "text", value: createForm.title, onChange: (v) => setCreateForm({ ...createForm, title: v }), placeholder: `${productSlug}-part` }), _jsx(FormField, { name: "price", label: "Price (\u20B9) *", type: "number", value: createForm.price, onChange: (v) => setCreateForm({ ...createForm, price: v }), placeholder: "0" }), _jsx(Select, { label: "Condition", value: createForm.condition, onValueChange: (v) => setCreateForm({ ...createForm, condition: v }), options: CONDITION_OPTIONS }), _jsx(Text, { className: "dark:text-[var(--appkit-color-text-faint)]/80", color: "faint", size: "xs", children: "Other fields (category, brand, shipping, return policy) are inherited from this parent listing. Need more control? Edit the full listing after saving." }), _jsx(FormErrorSummary, {}), _jsx(Button, { type: "button", onClick: onAddCreate, isLoading: loading, disabled: !createForm.title || !createForm.price, children: "Create and link child" })] }) }) }), _jsx(TabsContent, { value: "link", children: _jsxs(Stack, { gap: "sm", className: "mt-4", children: [_jsx(Text, { size: "xs", color: "muted", children: "Search your existing products or pre-orders. Auctions cannot be linked. Select multiple to bulk-link." }), _jsx(ProductInlineSelect, { scope: "store", endpoint: storeProductsEndpoint, filters: "listingType==standard", multiple: true, value: linkTargets, onChange: setLinkTargets, placeholder: "Search listings\u2026" }), _jsx(Button, { type: "button", onClick: onAddLink, isLoading: loading, disabled: linkTargets.length === 0, children: linkTargets.length > 1 ? `Link ${linkTargets.length} to group` : "Link to group" })] }) })] }) }));
|
|
225
247
|
}
|
|
@@ -18,6 +18,10 @@ import { ShareButton } from "./ShareButton";
|
|
|
18
18
|
import { PrizeDrawCollage } from "./PrizeDrawCollage";
|
|
19
19
|
import { PrizeDrawEntryActions } from "./PrizeDrawEntryActions";
|
|
20
20
|
import { HistoryTracker } from "../../history/components/HistoryTracker";
|
|
21
|
+
import { RelatedItemsSection } from "./RelatedItemsSection";
|
|
22
|
+
import { computeRelatedItems } from "../../../_internal/server/features/products/data";
|
|
23
|
+
import { GroupedListingsCarousel } from "../../grouped/components/GroupedListingsCarousel";
|
|
24
|
+
import { getGroupsWithItemsForProduct } from "../../../_internal/server/features/grouped/data";
|
|
21
25
|
function toDescriptionHtml(raw) {
|
|
22
26
|
if (!raw)
|
|
23
27
|
return "";
|
|
@@ -112,11 +116,17 @@ export async function PrizeDrawDetailPageView({ id, initialPrizeDraw, currentUse
|
|
|
112
116
|
? String(ROUTES.PUBLIC.STORE_DETAIL(storeSlug))
|
|
113
117
|
: null;
|
|
114
118
|
const descriptionHtml = toDescriptionHtml(p.description);
|
|
119
|
+
const categoryName = Array.isArray(p.categoryNames) && p.categoryNames.length > 0 ? String(p.categoryNames[0]) : undefined;
|
|
120
|
+
const brand = typeof p.brand === "string" ? p.brand : undefined;
|
|
121
|
+
const [{ relatedItems, relatedByBrand, relatedByTags, relatedByStore }, groups] = await Promise.all([
|
|
122
|
+
computeRelatedItems(product),
|
|
123
|
+
getGroupsWithItemsForProduct(product.id),
|
|
124
|
+
]);
|
|
115
125
|
return (_jsxs(Main, { children: [_jsx(HistoryTracker, { productId: String(p.id ?? p.slug ?? ""), productType: "prize-draw", snapshot: {
|
|
116
126
|
title,
|
|
117
127
|
thumb,
|
|
118
128
|
price: pricePerEntry,
|
|
119
129
|
storeId: typeof p.storeId === "string" ? p.storeId : undefined,
|
|
120
130
|
storeName: storeName ?? undefined,
|
|
121
|
-
} }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs("nav", { "aria-label": "Breadcrumb", className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)] flex-wrap", children: [_jsx(Link, { href: String(ROUTES.HOME), className: "hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRIZE_DRAWS), className: "hover:text-primary-600 transition-colors", children: "Prize Draws" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => (_jsx(PrizeDrawCollage, { items: items, hideWonState: true })), renderInfo: () => (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_PRIZE_BADGE, children: "Prize Draw" }), _jsx(Span, { size: "xs", weight: "semibold", className: `inline-block ${statusClass(revealStatus)}`, padding: "pill-sm", rounded: "full", children: statusLabel(revealStatus) }), maxPerUser !== null && (_jsxs(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: ["Limit: ", maxPerUser, " entries per customer"] })), maxPerUser !== null && userEntriesUsed !== null && (_jsxs(Span, { size: "xs", weight: "semibold", className: CLS_PRIZE_BADGE, children: ["You have used ", userEntriesUsed, "/", maxPerUser] }))] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), _jsx(Div, { className: `${__P.p4}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Entries" }), _jsxs(Text, { size: "sm", weight: "semibold", children: [current, " / ", max, " (", remaining, " left)"] })] }), revealStart && (_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Reveal window opens" }), _jsx(Text, { size: "sm", weight: "medium", children: revealStart.toLocaleString() })] })), revealEnd && (_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Reveal window closes" }), _jsx(Text, { size: "sm", weight: "medium", children: revealEnd.toLocaleString() })] }))] }) }), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" })), safeSeller && (_jsx(Div, { className: `${__P.p3}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Sold by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] })), renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })) : undefined })), renderBuyBar: () => (_jsx(Div, { padding: "5", id: "prize-draw-buy-bar", border: "subtle", surface: "muted", rounded: "xl", children: _jsx(PrizeDrawEntryActions, { productId: String(product.id), productSlug: slug, title: title,
|
|
131
|
+
} }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs("nav", { "aria-label": "Breadcrumb", className: "flex items-center gap-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)] flex-wrap", children: [_jsx(Link, { href: String(ROUTES.HOME), className: "hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRIZE_DRAWS), className: "hover:text-primary-600 transition-colors", children: "Prize Draws" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => (_jsx(PrizeDrawCollage, { items: items, hideWonState: true })), renderInfo: () => (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_PRIZE_BADGE, children: "Prize Draw" }), _jsx(Span, { size: "xs", weight: "semibold", className: `inline-block ${statusClass(revealStatus)}`, padding: "pill-sm", rounded: "full", children: statusLabel(revealStatus) }), maxPerUser !== null && (_jsxs(Span, { size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", surface: "subtle", color: "muted", children: ["Limit: ", maxPerUser, " entries per customer"] })), maxPerUser !== null && userEntriesUsed !== null && (_jsxs(Span, { size: "xs", weight: "semibold", className: CLS_PRIZE_BADGE, children: ["You have used ", userEntriesUsed, "/", maxPerUser] }))] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), _jsx(Div, { className: `${__P.p4}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Entries" }), _jsxs(Text, { size: "sm", weight: "semibold", children: [current, " / ", max, " (", remaining, " left)"] })] }), revealStart && (_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Reveal window opens" }), _jsx(Text, { size: "sm", weight: "medium", children: revealStart.toLocaleString() })] })), revealEnd && (_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Reveal window closes" }), _jsx(Text, { size: "sm", weight: "medium", children: revealEnd.toLocaleString() })] }))] }) }), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" })), safeSeller && (_jsx(Div, { className: `${__P.p3}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Sold by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] })), renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })) : undefined })), renderBuyBar: () => (_jsx(Div, { padding: "5", id: "prize-draw-buy-bar", border: "subtle", surface: "muted", rounded: "xl", children: _jsx(PrizeDrawEntryActions, { productId: String(product.id), productSlug: slug, title: title, pricePerEntry: pricePerEntry, currency: currency, remainingEntries: remaining, revealStatus: revealStatus, prizeGithubFileUrl: githubUrl }) })), renderRelated: () => (_jsxs(Stack, { gap: "xl", children: [_jsx(GroupedListingsCarousel, { groups: groups }), _jsx(RelatedItemsSection, { relatedItems: relatedItems, relatedByBrand: relatedByBrand, relatedByTags: relatedByTags, relatedByStore: relatedByStore, categoryLabel: categoryName, brandLabel: brand, storeLabel: storeName ?? undefined })] })) }), _jsx(PrizeDrawBottomActions, { pricePerEntry: pricePerEntry, currency: currency, closed: revealStatus === "closed" || remaining === 0 })] })] }));
|
|
122
132
|
}
|
|
@@ -2,14 +2,11 @@ export interface PrizeDrawEntryActionsProps {
|
|
|
2
2
|
productId: string;
|
|
3
3
|
productSlug?: string;
|
|
4
4
|
title: string;
|
|
5
|
-
thumb?: string;
|
|
6
5
|
pricePerEntry: number;
|
|
7
6
|
currency: string;
|
|
8
7
|
remainingEntries: number;
|
|
9
8
|
revealStatus: "pending" | "open" | "closed" | undefined;
|
|
10
9
|
prizeGithubFileUrl?: string;
|
|
11
|
-
storeId?: string;
|
|
12
|
-
storeName?: string;
|
|
13
10
|
}
|
|
14
11
|
/**
|
|
15
12
|
* Client buy panel for a prize-draw detail page (SB4-G).
|
|
@@ -18,4 +15,4 @@ export interface PrizeDrawEntryActionsProps {
|
|
|
18
15
|
* - On confirm → add 1 entry to guest cart and route to /user/cart
|
|
19
16
|
* - "View RNG source" link → prizeGithubFileUrl
|
|
20
17
|
*/
|
|
21
|
-
export declare function PrizeDrawEntryActions({ productId, productSlug, title,
|
|
18
|
+
export declare function PrizeDrawEntryActions({ productId, productSlug, title, pricePerEntry, currency, remainingEntries, revealStatus, prizeGithubFileUrl, }: PrizeDrawEntryActionsProps): import("react").JSX.Element;
|
|
@@ -16,7 +16,7 @@ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
|
16
16
|
* - On confirm → add 1 entry to guest cart and route to /user/cart
|
|
17
17
|
* - "View RNG source" link → prizeGithubFileUrl
|
|
18
18
|
*/
|
|
19
|
-
export function PrizeDrawEntryActions({ productId, productSlug, title,
|
|
19
|
+
export function PrizeDrawEntryActions({ productId, productSlug, title, pricePerEntry, currency, remainingEntries, revealStatus, prizeGithubFileUrl, }) {
|
|
20
20
|
const router = useRouter();
|
|
21
21
|
const { requireAuth, modalOpen, modalMessage, closeModal } = useAuthGate();
|
|
22
22
|
const [consentOpen, setConsentOpen] = useState(false);
|