@mohasinac/appkit 2.6.3 → 2.6.5
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/server/features/account/data.js +2 -2
- package/dist/_internal/server/features/bundles/data.d.ts +26 -6
- package/dist/_internal/server/features/bundles/data.js +41 -6
- package/dist/_internal/server/features/bundles/index.d.ts +3 -2
- package/dist/_internal/server/features/bundles/index.js +4 -2
- package/dist/_internal/server/features/bundles/metadata.d.ts +20 -0
- package/dist/_internal/server/features/bundles/metadata.js +46 -0
- package/dist/_internal/server/features/bundles/og.d.ts +38 -0
- package/dist/_internal/server/features/bundles/og.js +122 -0
- package/dist/_internal/server/features/checkout/actions.js +192 -133
- package/dist/_internal/server/features/checkout/bundle-expansion.d.ts +57 -0
- package/dist/_internal/server/features/checkout/bundle-expansion.js +70 -0
- package/dist/_internal/server/features/checkout/prize-bundle-gates.d.ts +5 -15
- package/dist/_internal/server/features/checkout/prize-bundle-gates.js +5 -21
- package/dist/_internal/server/features/media/contextGuards.js +15 -1
- package/dist/_internal/server/features/payouts/actions.d.ts +19 -0
- package/dist/_internal/server/features/payouts/actions.js +38 -0
- package/dist/_internal/server/features/products/data.js +1 -2
- package/dist/_internal/server/features/raffle/actions.d.ts +11 -0
- package/dist/_internal/server/features/raffle/actions.js +31 -0
- package/dist/_internal/server/features/refunds/actions.d.ts +31 -0
- package/dist/_internal/server/features/refunds/actions.js +77 -0
- package/dist/_internal/server/jobs/core/adminAnalytics.d.ts +28 -0
- package/dist/_internal/server/jobs/core/adminAnalytics.js +98 -0
- package/dist/_internal/server/jobs/core/assignSpinPrize.d.ts +19 -0
- package/dist/_internal/server/jobs/core/assignSpinPrize.js +81 -0
- package/dist/_internal/server/jobs/core/auctionSettlement.d.ts +2 -0
- package/dist/_internal/server/jobs/core/auctionSettlement.js +70 -0
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.d.ts +2 -0
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +109 -0
- package/dist/_internal/server/jobs/core/bundleStockSync.d.ts +10 -0
- package/dist/_internal/server/jobs/core/bundleStockSync.js +71 -0
- package/dist/_internal/server/jobs/core/cartPrune.d.ts +2 -0
- package/dist/_internal/server/jobs/core/cartPrune.js +13 -0
- package/dist/_internal/server/jobs/core/cleanupRtdbEvents.d.ts +2 -0
- package/dist/_internal/server/jobs/core/cleanupRtdbEvents.js +45 -0
- package/dist/_internal/server/jobs/core/countersReconcile.d.ts +2 -0
- package/dist/_internal/server/jobs/core/countersReconcile.js +107 -0
- package/dist/_internal/server/jobs/core/couponExpiry.d.ts +2 -0
- package/dist/_internal/server/jobs/core/couponExpiry.js +13 -0
- package/dist/_internal/server/jobs/core/dailyDataCleanup.d.ts +2 -0
- package/dist/_internal/server/jobs/core/dailyDataCleanup.js +20 -0
- package/dist/_internal/server/jobs/core/index.d.ts +44 -0
- package/dist/_internal/server/jobs/core/index.js +47 -0
- package/dist/_internal/server/jobs/core/listingProcessor.d.ts +30 -0
- package/dist/_internal/server/jobs/core/listingProcessor.js +138 -0
- package/dist/_internal/server/jobs/core/mediaTmpCleanup.d.ts +14 -0
- package/dist/_internal/server/jobs/core/mediaTmpCleanup.js +68 -0
- package/dist/_internal/server/jobs/core/notificationPrune.d.ts +2 -0
- package/dist/_internal/server/jobs/core/notificationPrune.js +13 -0
- package/dist/_internal/server/jobs/core/offerExpiry.d.ts +2 -0
- package/dist/_internal/server/jobs/core/offerExpiry.js +50 -0
- package/dist/_internal/server/jobs/core/onBidPlaced.d.ts +15 -0
- package/dist/_internal/server/jobs/core/onBidPlaced.js +59 -0
- package/dist/_internal/server/jobs/core/onCategoryWrite.d.ts +14 -0
- package/dist/_internal/server/jobs/core/onCategoryWrite.js +134 -0
- package/dist/_internal/server/jobs/core/onOrderCreate.d.ts +18 -0
- package/dist/_internal/server/jobs/core/onOrderCreate.js +78 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +19 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +139 -0
- package/dist/_internal/server/jobs/core/onProductStockChange.d.ts +23 -0
- package/dist/_internal/server/jobs/core/onProductStockChange.js +130 -0
- package/dist/_internal/server/jobs/core/onProductWrite.d.ts +12 -0
- package/dist/_internal/server/jobs/core/onProductWrite.js +94 -0
- package/dist/_internal/server/jobs/core/onReviewWrite.d.ts +7 -0
- package/dist/_internal/server/jobs/core/onReviewWrite.js +49 -0
- package/dist/_internal/server/jobs/core/onStoreWrite.d.ts +12 -0
- package/dist/_internal/server/jobs/core/onStoreWrite.js +7 -0
- package/dist/_internal/server/jobs/core/payoutBatch.d.ts +8 -0
- package/dist/_internal/server/jobs/core/payoutBatch.js +102 -0
- package/dist/_internal/server/jobs/core/pendingOrderTimeout.d.ts +2 -0
- package/dist/_internal/server/jobs/core/pendingOrderTimeout.js +27 -0
- package/dist/_internal/server/jobs/core/positionsReconcile.d.ts +7 -0
- package/dist/_internal/server/jobs/core/positionsReconcile.js +87 -0
- package/dist/_internal/server/jobs/core/prizeRevealClose.d.ts +2 -0
- package/dist/_internal/server/jobs/core/prizeRevealClose.js +22 -0
- package/dist/_internal/server/jobs/core/prizeRevealExpiry.d.ts +2 -0
- package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +50 -0
- package/dist/_internal/server/jobs/core/prizeRevealOpen.d.ts +2 -0
- package/dist/_internal/server/jobs/core/prizeRevealOpen.js +56 -0
- package/dist/_internal/server/jobs/core/prizeRevealReminder.d.ts +2 -0
- package/dist/_internal/server/jobs/core/prizeRevealReminder.js +38 -0
- package/dist/_internal/server/jobs/core/productStatsSync.d.ts +8 -0
- package/dist/_internal/server/jobs/core/productStatsSync.js +36 -0
- package/dist/_internal/server/jobs/core/promotions.d.ts +12 -0
- package/dist/_internal/server/jobs/core/promotions.js +43 -0
- package/dist/_internal/server/jobs/core/storeAnalytics.d.ts +30 -0
- package/dist/_internal/server/jobs/core/storeAnalytics.js +109 -0
- package/dist/_internal/server/jobs/core/triggerEventRaffle.d.ts +19 -0
- package/dist/_internal/server/jobs/core/triggerEventRaffle.js +86 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.d.ts +6 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +85 -0
- package/dist/_internal/server/jobs/handlers/adminAnalytics.d.ts +2 -26
- package/dist/_internal/server/jobs/handlers/adminAnalytics.js +2 -98
- package/dist/_internal/server/jobs/handlers/assignSpinPrize.d.ts +1 -22
- package/dist/_internal/server/jobs/handlers/assignSpinPrize.js +2 -86
- package/dist/_internal/server/jobs/handlers/auctionSettlement.js +2 -70
- package/dist/_internal/server/jobs/handlers/autoPayoutEligibility.js +2 -110
- package/dist/_internal/server/jobs/handlers/bundleStockSync.d.ts +0 -16
- package/dist/_internal/server/jobs/handlers/bundleStockSync.js +2 -80
- package/dist/_internal/server/jobs/handlers/cartPrune.js +2 -13
- package/dist/_internal/server/jobs/handlers/cleanupRtdbEvents.js +2 -45
- package/dist/_internal/server/jobs/handlers/countersReconcile.js +2 -109
- package/dist/_internal/server/jobs/handlers/couponExpiry.js +2 -13
- package/dist/_internal/server/jobs/handlers/dailyDataCleanup.js +2 -20
- package/dist/_internal/server/jobs/handlers/listingProcessor.d.ts +3 -28
- package/dist/_internal/server/jobs/handlers/listingProcessor.js +3 -138
- package/dist/_internal/server/jobs/handlers/mediaTmpCleanup.d.ts +0 -12
- package/dist/_internal/server/jobs/handlers/mediaTmpCleanup.js +2 -69
- package/dist/_internal/server/jobs/handlers/notificationPrune.js +2 -13
- package/dist/_internal/server/jobs/handlers/offerExpiry.js +2 -50
- package/dist/_internal/server/jobs/handlers/onBidPlaced.d.ts +1 -10
- package/dist/_internal/server/jobs/handlers/onBidPlaced.js +2 -56
- package/dist/_internal/server/jobs/handlers/onCategoryWrite.d.ts +1 -8
- package/dist/_internal/server/jobs/handlers/onCategoryWrite.js +6 -134
- package/dist/_internal/server/jobs/handlers/onOrderCreate.d.ts +1 -12
- package/dist/_internal/server/jobs/handlers/onOrderCreate.js +2 -76
- package/dist/_internal/server/jobs/handlers/onOrderStatusChange.d.ts +1 -12
- package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +2 -139
- package/dist/_internal/server/jobs/handlers/onProductStockChange.d.ts +0 -13
- package/dist/_internal/server/jobs/handlers/onProductStockChange.js +7 -134
- package/dist/_internal/server/jobs/handlers/onProductWrite.d.ts +1 -6
- package/dist/_internal/server/jobs/handlers/onProductWrite.js +6 -106
- package/dist/_internal/server/jobs/handlers/onReviewWrite.js +2 -49
- package/dist/_internal/server/jobs/handlers/onStoreWrite.d.ts +1 -8
- package/dist/_internal/server/jobs/handlers/onStoreWrite.js +7 -8
- package/dist/_internal/server/jobs/handlers/payoutBatch.d.ts +0 -9
- package/dist/_internal/server/jobs/handlers/payoutBatch.js +2 -104
- package/dist/_internal/server/jobs/handlers/pendingOrderTimeout.d.ts +0 -6
- package/dist/_internal/server/jobs/handlers/pendingOrderTimeout.js +2 -33
- package/dist/_internal/server/jobs/handlers/positionsReconcile.d.ts +0 -5
- package/dist/_internal/server/jobs/handlers/positionsReconcile.js +2 -87
- package/dist/_internal/server/jobs/handlers/prizeRevealClose.d.ts +0 -7
- package/dist/_internal/server/jobs/handlers/prizeRevealClose.js +2 -29
- package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.d.ts +0 -8
- package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.js +2 -58
- package/dist/_internal/server/jobs/handlers/prizeRevealOpen.d.ts +0 -8
- package/dist/_internal/server/jobs/handlers/prizeRevealOpen.js +2 -65
- package/dist/_internal/server/jobs/handlers/prizeRevealReminder.d.ts +0 -7
- package/dist/_internal/server/jobs/handlers/prizeRevealReminder.js +2 -45
- package/dist/_internal/server/jobs/handlers/productStatsSync.d.ts +0 -6
- package/dist/_internal/server/jobs/handlers/productStatsSync.js +2 -36
- package/dist/_internal/server/jobs/handlers/promotions.d.ts +2 -10
- package/dist/_internal/server/jobs/handlers/promotions.js +2 -43
- package/dist/_internal/server/jobs/handlers/storeAnalytics.d.ts +2 -28
- package/dist/_internal/server/jobs/handlers/storeAnalytics.js +2 -109
- package/dist/_internal/server/jobs/handlers/triggerEventRaffle.d.ts +1 -28
- package/dist/_internal/server/jobs/handlers/triggerEventRaffle.js +2 -94
- package/dist/_internal/server/jobs/handlers/weeklyPayoutEligibility.d.ts +0 -6
- package/dist/_internal/server/jobs/handlers/weeklyPayoutEligibility.js +2 -87
- package/dist/_internal/server/jobs/index.d.ts +1 -0
- package/dist/_internal/server/jobs/index.js +1 -0
- package/dist/_internal/server/jobs/runtime/adapters/firebase.js +21 -0
- package/dist/_internal/shared/actions/action-registry.d.ts +84 -0
- package/dist/_internal/shared/actions/action-registry.js +160 -0
- package/dist/_internal/shared/checkout/rules/_defaults.d.ts +3 -0
- package/dist/_internal/shared/checkout/rules/_defaults.js +22 -0
- package/dist/_internal/shared/checkout/rules/_limits.d.ts +19 -0
- package/dist/_internal/shared/checkout/rules/_limits.js +19 -0
- package/dist/_internal/shared/checkout/rules/_registry.d.ts +44 -0
- package/dist/_internal/shared/checkout/rules/_registry.js +87 -0
- package/dist/_internal/shared/checkout/rules/auction.rule.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/auction.rule.js +10 -0
- package/dist/_internal/shared/checkout/rules/bundle.rule.d.ts +11 -0
- package/dist/_internal/shared/checkout/rules/bundle.rule.js +6 -0
- package/dist/_internal/shared/checkout/rules/classified.rule.d.ts +9 -0
- package/dist/_internal/shared/checkout/rules/classified.rule.js +9 -0
- package/dist/_internal/shared/checkout/rules/digital-code.rule.d.ts +8 -0
- package/dist/_internal/shared/checkout/rules/digital-code.rule.js +6 -0
- package/dist/_internal/shared/checkout/rules/index.d.ts +12 -0
- package/dist/_internal/shared/checkout/rules/index.js +12 -0
- package/dist/_internal/shared/checkout/rules/live.rule.d.ts +10 -0
- package/dist/_internal/shared/checkout/rules/live.rule.js +6 -0
- package/dist/_internal/shared/checkout/rules/offer.rule.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/offer.rule.js +9 -0
- package/dist/_internal/shared/checkout/rules/preorder.rule.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/preorder.rule.js +28 -0
- package/dist/_internal/shared/checkout/rules/prize-draw.rule.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +65 -0
- package/dist/_internal/shared/checkout/rules/standard.rule.d.ts +2 -0
- package/dist/_internal/shared/checkout/rules/standard.rule.js +5 -0
- package/dist/_internal/shared/checkout/rules/types.d.ts +125 -0
- package/dist/_internal/shared/checkout/rules/types.js +13 -0
- package/dist/_internal/shared/features/cart/schema.d.ts +6 -6
- package/dist/_internal/shared/features/categories/bundle-copy.d.ts +123 -0
- package/dist/_internal/shared/features/categories/bundle-copy.js +134 -0
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +318 -0
- package/dist/_internal/shared/features/categories/bundle-schemas.js +55 -0
- package/dist/_internal/shared/features/events/schema.d.ts +4 -4
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +36 -0
- package/dist/_internal/shared/features/orders/refund-copy.js +40 -0
- package/dist/_internal/shared/features/orders/schema.d.ts +4 -4
- package/dist/_internal/shared/features/products/schema.d.ts +8 -8
- package/dist/_internal/shared/listing-types/_registry.d.ts +27 -0
- package/dist/_internal/shared/listing-types/_registry.js +8 -0
- package/dist/_internal/shared/listing-types/action-tracker.d.ts +41 -0
- package/dist/_internal/shared/listing-types/action-tracker.js +70 -0
- package/dist/_internal/shared/listing-types/capabilities.js +25 -0
- package/dist/_internal/shared/listing-types/cart-shipping.d.ts +37 -0
- package/dist/_internal/shared/listing-types/cart-shipping.js +46 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +7 -0
- package/dist/_internal/shared/listing-types/classified/config.js +8 -0
- package/dist/_internal/shared/listing-types/classified/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/ctas.js +3 -0
- package/dist/_internal/shared/listing-types/classified/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/og.js +1 -0
- package/dist/_internal/shared/listing-types/classified/schema.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/schema.js +1 -0
- package/dist/_internal/shared/listing-types/classified/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/seed-factory.js +1 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +7 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +8 -0
- package/dist/_internal/shared/listing-types/digital-code/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/digital-code/ctas.js +3 -0
- package/dist/_internal/shared/listing-types/digital-code/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/digital-code/og.js +1 -0
- package/dist/_internal/shared/listing-types/digital-code/schema.d.ts +1 -0
- package/dist/_internal/shared/listing-types/digital-code/schema.js +1 -0
- package/dist/_internal/shared/listing-types/digital-code/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/digital-code/seed-factory.js +1 -0
- package/dist/_internal/shared/listing-types/feature-flags.d.ts +34 -0
- package/dist/_internal/shared/listing-types/feature-flags.js +45 -0
- package/dist/_internal/shared/listing-types/live/config.d.ts +7 -0
- package/dist/_internal/shared/listing-types/live/config.js +8 -0
- package/dist/_internal/shared/listing-types/live/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/live/ctas.js +3 -0
- package/dist/_internal/shared/listing-types/live/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/live/og.js +1 -0
- package/dist/_internal/shared/listing-types/live/schema.d.ts +1 -0
- package/dist/_internal/shared/listing-types/live/schema.js +1 -0
- package/dist/_internal/shared/listing-types/live/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/live/seed-factory.js +1 -0
- package/dist/_internal/shared/media/limits.js +8 -0
- package/dist/client.d.ts +10 -5
- package/dist/client.js +16 -3
- package/dist/configs/next.js +7 -0
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +4 -0
- package/dist/core/hooks/useSyncManager.js +13 -1
- package/dist/core/unit-of-work.d.ts +1 -1
- package/dist/core/unit-of-work.js +2 -2
- package/dist/features/account/actions/address-actions.d.ts +1 -1
- package/dist/features/account/actions/address-actions.js +15 -7
- package/dist/features/account/schemas/firestore.d.ts +8 -43
- package/dist/features/account/schemas/firestore.js +8 -54
- package/dist/features/account/schemas/index.d.ts +6 -6
- package/dist/features/account/server.d.ts +1 -1
- package/dist/features/account/server.js +3 -1
- package/dist/features/addresses/index.d.ts +2 -0
- package/dist/features/addresses/index.js +2 -0
- package/dist/features/addresses/repository/addresses.repository.d.ts +29 -0
- package/dist/features/addresses/repository/addresses.repository.js +157 -0
- package/dist/features/addresses/schemas/firestore.d.ts +53 -0
- package/dist/features/addresses/schemas/firestore.js +68 -0
- package/dist/features/{bundles → addresses}/schemas/index.d.ts +0 -1
- package/dist/features/{bundles → addresses}/schemas/index.js +0 -1
- package/dist/features/addresses/server.d.ts +2 -0
- package/dist/features/addresses/server.js +2 -0
- package/dist/features/admin/components/AdminAllEventEntriesView.js +4 -3
- package/dist/features/admin/components/AdminBidsView.js +4 -3
- package/dist/features/admin/components/AdminBlogView.js +8 -3
- package/dist/features/admin/components/AdminBundleEditorView.d.ts +9 -0
- package/dist/features/admin/components/AdminBundleEditorView.js +209 -0
- package/dist/features/admin/components/AdminBundlesView.d.ts +9 -0
- package/dist/features/admin/components/AdminBundlesView.js +59 -0
- package/dist/features/admin/components/AdminCartsView.js +13 -3
- package/dist/features/admin/components/AdminContactView.js +4 -3
- package/dist/features/admin/components/AdminCouponsView.js +32 -13
- package/dist/features/admin/components/AdminNewsletterView.js +4 -3
- package/dist/features/admin/components/AdminOrdersView.js +15 -7
- package/dist/features/admin/components/AdminPayoutsView.js +4 -3
- package/dist/features/admin/components/AdminPrizeDrawsView.d.ts +4 -0
- package/dist/features/admin/components/AdminPrizeDrawsView.js +135 -0
- package/dist/features/admin/components/AdminProductsView.js +6 -5
- package/dist/features/admin/components/AdminReviewsView.js +5 -4
- package/dist/features/admin/components/AdminStoresView.js +4 -3
- package/dist/features/admin/components/AdminUsersView.js +15 -5
- package/dist/features/admin/components/AdminWishlistsView.js +10 -1
- package/dist/features/admin/components/DataTable.d.ts +5 -1
- package/dist/features/admin/components/DataTable.js +24 -20
- package/dist/features/admin/components/index.d.ts +6 -0
- package/dist/features/admin/components/index.js +5 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +435 -0
- package/dist/features/admin/constants/filter-tabs.js +216 -0
- package/dist/features/admin/hooks/useAdminListingData.d.ts +1 -0
- package/dist/features/admin/hooks/useAdminListingData.js +1 -0
- package/dist/features/admin/schemas/firestore.d.ts +15 -0
- package/dist/features/admin/schemas/firestore.js +18 -0
- package/dist/features/admin/types/product.types.d.ts +2 -2
- package/dist/features/auctions/components/AuctionCard.d.ts +4 -1
- package/dist/features/auctions/components/AuctionCard.js +5 -3
- package/dist/features/auctions/components/AuctionDetailPageView.js +1 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +2 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +12 -2
- package/dist/features/auctions/schemas/index.d.ts +2 -2
- package/dist/features/auth/index.d.ts +1 -0
- package/dist/features/auth/index.js +1 -0
- package/dist/features/auth/role-predicates.d.ts +16 -0
- package/dist/features/auth/role-predicates.js +15 -0
- package/dist/features/blog/components/BlogFeaturedCard.d.ts +4 -1
- package/dist/features/blog/components/BlogFeaturedCard.js +5 -3
- package/dist/features/cart/actions/cart-actions.d.ts +1 -0
- package/dist/features/cart/actions/cart-actions.js +16 -0
- package/dist/features/cart/components/CartView.d.ts +19 -1
- package/dist/features/cart/components/CartView.js +2 -2
- package/dist/features/cart/components/ShippingPicker.d.ts +13 -0
- package/dist/features/cart/components/ShippingPicker.js +48 -0
- package/dist/features/cart/components/index.d.ts +3 -1
- package/dist/features/cart/components/index.js +1 -0
- package/dist/features/cart/hooks/useCartCount.js +7 -1
- package/dist/features/cart/repository/cart.repository.d.ts +1 -0
- package/dist/features/cart/repository/cart.repository.js +35 -0
- package/dist/features/cart/schemas/firestore.d.ts +27 -2
- package/dist/features/categories/components/BundleAddToCartCta.d.ts +16 -0
- package/dist/features/categories/components/BundleAddToCartCta.js +54 -0
- package/dist/features/categories/components/BundleBuyNowCta.d.ts +8 -0
- package/dist/features/categories/components/BundleBuyNowCta.js +37 -0
- package/dist/features/categories/components/BundleDetailView.d.ts +22 -0
- package/dist/features/categories/components/BundleDetailView.js +31 -0
- package/dist/features/categories/components/BundleDynamicRuleEditor.d.ts +18 -0
- package/dist/features/categories/components/BundleDynamicRuleEditor.js +60 -0
- package/dist/features/categories/components/BundleItemsPicker.d.ts +26 -0
- package/dist/features/categories/components/BundleItemsPicker.js +135 -0
- package/dist/features/categories/components/BundlesListView.d.ts +13 -0
- package/dist/features/categories/components/BundlesListView.js +27 -0
- package/dist/features/categories/components/index.d.ts +12 -0
- package/dist/features/categories/components/index.js +9 -0
- package/dist/features/checkout/actions/checkout-actions.js +10 -3
- package/dist/features/events/components/AdminEventEditorView.js +108 -2
- package/dist/features/events/components/AdminEventsView.js +16 -4
- package/dist/features/events/components/EventCard.d.ts +4 -1
- package/dist/features/events/components/EventCard.js +7 -3
- package/dist/features/events/components/EventRaffleWinnerView.d.ts +21 -0
- package/dist/features/events/components/EventRaffleWinnerView.js +18 -0
- package/dist/features/events/components/SpinWheelView.d.ts +27 -0
- package/dist/features/events/components/SpinWheelView.js +64 -0
- package/dist/features/events/components/index.d.ts +4 -0
- package/dist/features/events/components/index.js +2 -0
- package/dist/features/events/schemas/firestore.d.ts +27 -1
- package/dist/features/events/schemas/firestore.js +7 -0
- package/dist/features/events/types/index.d.ts +27 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.d.ts +16 -0
- package/dist/features/homepage/components/FeaturedBundlesSection.js +24 -0
- package/dist/features/homepage/components/MarketplaceHomepageView.js +4 -1
- package/dist/features/homepage/components/WhatsAppCommunitySection.js +2 -2
- package/dist/features/homepage/lib/section-renderer.d.ts +2 -0
- package/dist/features/homepage/lib/section-renderer.js +5 -5
- package/dist/features/layout/NavbarLayout.d.ts +3 -1
- package/dist/features/layout/NavbarLayout.js +32 -3
- package/dist/features/layout/TitleBarLayout.d.ts +6 -3
- package/dist/features/layout/TitleBarLayout.js +22 -7
- package/dist/features/media/upload/MediaUploadField.d.ts +15 -1
- package/dist/features/media/upload/MediaUploadField.js +22 -1
- package/dist/features/orders/components/MarketplaceOrderCard.js +4 -2
- package/dist/features/orders/components/OrderSiblingPayments.d.ts +8 -0
- package/dist/features/orders/components/OrderSiblingPayments.js +16 -0
- package/dist/features/orders/components/RefundHistoryTable.d.ts +6 -0
- package/dist/features/orders/components/RefundHistoryTable.js +23 -0
- package/dist/features/orders/components/RefundRequestView.d.ts +8 -0
- package/dist/features/orders/components/RefundRequestView.js +42 -0
- package/dist/features/orders/components/index.d.ts +6 -0
- package/dist/features/orders/components/index.js +3 -0
- package/dist/features/orders/index.d.ts +1 -0
- package/dist/features/orders/index.js +2 -0
- package/dist/features/orders/repository/orders.repository.d.ts +20 -1
- package/dist/features/orders/repository/orders.repository.js +30 -1
- package/dist/features/orders/schemas/firestore.d.ts +63 -13
- package/dist/features/orders/schemas/firestore.js +5 -5
- package/dist/features/orders/schemas/index.d.ts +4 -4
- package/dist/features/orders/types/index.d.ts +12 -2
- package/dist/features/orders/utils/bundle-grouping.d.ts +48 -0
- package/dist/features/orders/utils/bundle-grouping.js +54 -0
- package/dist/features/orders/utils/order-splitter.d.ts +9 -10
- package/dist/features/orders/utils/order-splitter.js +24 -30
- package/dist/features/payments/repository/payout.repository.d.ts +17 -1
- package/dist/features/payments/repository/payout.repository.js +47 -0
- package/dist/features/payments/schemas/firestore.d.ts +26 -0
- package/dist/features/pre-orders/components/PreorderCard.d.ts +4 -1
- package/dist/features/pre-orders/components/PreorderCard.js +8 -6
- package/dist/features/products/actions/product-actions.d.ts +1 -1
- package/dist/features/products/actions/product-actions.js +1 -1
- package/dist/features/products/components/CompareOverlay.d.ts +2 -2
- package/dist/features/products/components/CompareOverlay.js +4 -4
- package/dist/features/products/components/InteractiveProductCard.js +7 -12
- package/dist/features/products/components/NonRefundableConsentModal.d.ts +1 -1
- package/dist/features/products/components/NonRefundableConsentModal.js +0 -10
- package/dist/features/products/components/ProductGrid.js +28 -12
- package/dist/features/products/components/ShowGroupSection.js +3 -3
- package/dist/features/products/components/SublistingCarouselSection.js +2 -2
- package/dist/features/products/components/index.d.ts +0 -4
- package/dist/features/products/components/index.js +5 -2
- package/dist/features/products/index.d.ts +1 -1
- package/dist/features/products/index.js +3 -1
- package/dist/features/products/repository/products.repository.js +4 -0
- package/dist/features/products/schemas/catalog-product.d.ts +70 -0
- package/dist/features/products/schemas/catalog-product.js +50 -0
- package/dist/features/products/schemas/firestore.d.ts +110 -2
- package/dist/features/products/schemas/firestore.js +30 -0
- package/dist/features/products/schemas/index.d.ts +8 -8
- package/dist/features/products/types/index.d.ts +1 -1
- package/dist/features/products/utils/listing-type.d.ts +9 -0
- package/dist/features/products/utils/listing-type.js +4 -0
- package/dist/features/promotions/actions/coupon-actions.d.ts +2 -2
- package/dist/features/promotions/actions/coupon-actions.js +1 -1
- package/dist/features/promotions/components/CouponCard.d.ts +28 -10
- package/dist/features/promotions/components/CouponCard.js +116 -14
- package/dist/features/promotions/hooks/useCouponValidate.d.ts +1 -1
- package/dist/features/promotions/repository/coupons.repository.d.ts +1 -1
- package/dist/features/reviews/schemas/index.d.ts +2 -2
- package/dist/features/search/schemas/index.d.ts +2 -2
- package/dist/features/search/types/index.d.ts +2 -2
- package/dist/features/seller/components/SellerAuctionsView.js +4 -5
- package/dist/features/seller/components/SellerBidsView.js +5 -13
- package/dist/features/seller/components/SellerCouponsView.js +31 -67
- package/dist/features/seller/components/SellerOffersView.js +4 -5
- package/dist/features/seller/components/SellerOrdersView.js +4 -5
- package/dist/features/seller/components/SellerPayoutsView.js +4 -5
- package/dist/features/seller/components/SellerPreOrdersView.d.ts +4 -0
- package/dist/features/seller/components/SellerPreOrdersView.js +141 -0
- package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +4 -0
- package/dist/features/seller/components/SellerPrizeDrawsView.js +138 -0
- package/dist/features/seller/components/SellerProductsView.js +4 -8
- package/dist/features/seller/components/index.d.ts +4 -0
- package/dist/features/seller/components/index.js +2 -0
- package/dist/features/seller/schemas/index.d.ts +2 -2
- package/dist/features/stores/actions/store-address-actions.d.ts +10 -6
- package/dist/features/stores/actions/store-address-actions.js +8 -7
- package/dist/features/stores/components/InteractiveStoreCard.js +3 -10
- package/dist/features/stores/components/StoreDetailLayoutView.js +20 -5
- package/dist/features/stores/schemas/firestore.d.ts +45 -38
- package/dist/features/stores/schemas/firestore.js +2 -49
- package/dist/features/stores/server.d.ts +0 -1
- package/dist/features/stores/server.js +2 -1
- package/dist/features/wishlist/hooks/useWishlistCount.d.ts +7 -9
- package/dist/features/wishlist/hooks/useWishlistCount.js +67 -86
- package/dist/features/wishlist/types/index.d.ts +1 -1
- package/dist/index.d.ts +56 -9
- package/dist/index.js +66 -11
- package/dist/next/routing/route-map.d.ts +6 -0
- package/dist/next/routing/route-map.js +4 -0
- package/dist/providers/db-firebase/admin.d.ts +6 -2
- package/dist/providers/db-firebase/admin.js +23 -0
- package/dist/repositories/index.d.ts +1 -2
- package/dist/repositories/index.js +5 -2
- package/dist/security/authorization.d.ts +2 -1
- package/dist/security/authorization.js +3 -2
- package/dist/seed/actions/demo-seed-actions.d.ts +1 -1
- package/dist/seed/bids-seed-data.d.ts +2 -2
- package/dist/seed/bids-seed-data.js +77 -2
- package/dist/seed/cart-seed-data.d.ts +10 -10
- package/dist/seed/cart-seed-data.js +15 -15
- package/dist/seed/events-seed-data.js +77 -0
- package/dist/seed/manifest.js +16 -8
- package/dist/seed/orders-seed-data.js +41 -2
- package/dist/seed/payouts-seed-data.js +18 -2
- package/dist/seed/stores-seed-data.js +55 -0
- package/dist/server-entry.d.ts +1 -0
- package/dist/server-entry.js +2 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +8 -0
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/FilterChipGroup.d.ts +39 -0
- package/dist/ui/components/FilterChipGroup.js +15 -0
- package/dist/ui/components/HorizontalScroller.js +32 -7
- package/dist/ui/components/HorizontalScroller.style.css +6 -0
- package/dist/ui/components/SiteLogo.js +1 -1
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +2 -0
- package/dist/utils/id-generators.d.ts +11 -0
- package/dist/utils/id-generators.js +16 -0
- package/package.json +1 -1
- package/dist/_internal/shared/features/bundles/config.d.ts +0 -6
- package/dist/_internal/shared/features/bundles/config.js +0 -6
- package/dist/_internal/shared/schema-versions.d.ts +0 -76
- package/dist/_internal/shared/schema-versions.js +0 -82
- package/dist/features/account/migrations.d.ts +0 -2
- package/dist/features/account/migrations.js +0 -10
- package/dist/features/admin/migrations.d.ts +0 -2
- package/dist/features/admin/migrations.js +0 -10
- package/dist/features/auctions/migrations.d.ts +0 -2
- package/dist/features/auctions/migrations.js +0 -10
- package/dist/features/auth/migrations.d.ts +0 -2
- package/dist/features/auth/migrations.js +0 -10
- package/dist/features/blog/migrations.d.ts +0 -2
- package/dist/features/blog/migrations.js +0 -10
- package/dist/features/brands/actions/brand-actions.d.ts +0 -2
- package/dist/features/brands/actions/brand-actions.js +0 -5
- package/dist/features/brands/index.d.ts +0 -3
- package/dist/features/brands/index.js +0 -3
- package/dist/features/brands/migrations.d.ts +0 -2
- package/dist/features/brands/migrations.js +0 -10
- package/dist/features/brands/repository/brands.repository.d.ts +0 -13
- package/dist/features/brands/repository/brands.repository.js +0 -60
- package/dist/features/brands/schemas/index.d.ts +0 -33
- package/dist/features/brands/schemas/index.js +0 -15
- package/dist/features/brands/server.d.ts +0 -7
- package/dist/features/brands/server.js +0 -7
- package/dist/features/bundles/components/AdminBundleEditorView.d.ts +0 -8
- package/dist/features/bundles/components/AdminBundleEditorView.js +0 -7
- package/dist/features/bundles/components/BundleDetailPageView.d.ts +0 -9
- package/dist/features/bundles/components/BundleDetailPageView.js +0 -45
- package/dist/features/bundles/components/BundleForm.d.ts +0 -12
- package/dist/features/bundles/components/BundleForm.js +0 -126
- package/dist/features/bundles/components/BundleItemsPicker.d.ts +0 -9
- package/dist/features/bundles/components/BundleItemsPicker.js +0 -77
- package/dist/features/bundles/components/BundlesByCategoryListing.d.ts +0 -6
- package/dist/features/bundles/components/BundlesByCategoryListing.js +0 -50
- package/dist/features/bundles/components/BundlesListingView.d.ts +0 -17
- package/dist/features/bundles/components/BundlesListingView.js +0 -50
- package/dist/features/bundles/components/FeaturedBundlesSection.d.ts +0 -5
- package/dist/features/bundles/components/FeaturedBundlesSection.js +0 -55
- package/dist/features/bundles/components/SellerBundleCreateView.d.ts +0 -8
- package/dist/features/bundles/components/SellerBundleCreateView.js +0 -7
- package/dist/features/bundles/components/SellerBundleEditView.d.ts +0 -9
- package/dist/features/bundles/components/SellerBundleEditView.js +0 -7
- package/dist/features/bundles/components/index.d.ts +0 -18
- package/dist/features/bundles/components/index.js +0 -9
- package/dist/features/bundles/constants/index.d.ts +0 -32
- package/dist/features/bundles/constants/index.js +0 -35
- package/dist/features/bundles/index.d.ts +0 -2
- package/dist/features/bundles/index.js +0 -2
- package/dist/features/bundles/migrations.d.ts +0 -2
- package/dist/features/bundles/migrations.js +0 -10
- package/dist/features/bundles/repository/bundles.repository.d.ts +0 -36
- package/dist/features/bundles/repository/bundles.repository.js +0 -148
- package/dist/features/bundles/repository/index.d.ts +0 -1
- package/dist/features/bundles/repository/index.js +0 -1
- package/dist/features/bundles/schemas/firestore.d.ts +0 -88
- package/dist/features/bundles/schemas/firestore.js +0 -29
- package/dist/features/bundles/schemas/zod.d.ts +0 -377
- package/dist/features/bundles/schemas/zod.js +0 -71
- package/dist/features/cart/migrations.d.ts +0 -2
- package/dist/features/cart/migrations.js +0 -10
- package/dist/features/categories/migrations.d.ts +0 -2
- package/dist/features/categories/migrations.js +0 -10
- package/dist/features/events/migrations.d.ts +0 -2
- package/dist/features/events/migrations.js +0 -10
- package/dist/features/faq/migrations.d.ts +0 -2
- package/dist/features/faq/migrations.js +0 -10
- package/dist/features/grouped/migrations.d.ts +0 -2
- package/dist/features/grouped/migrations.js +0 -10
- package/dist/features/history/migrations.d.ts +0 -2
- package/dist/features/history/migrations.js +0 -10
- package/dist/features/messages/migrations.d.ts +0 -2
- package/dist/features/messages/migrations.js +0 -10
- package/dist/features/orders/migrations.d.ts +0 -2
- package/dist/features/orders/migrations.js +0 -10
- package/dist/features/payments/migrations.d.ts +0 -2
- package/dist/features/payments/migrations.js +0 -10
- package/dist/features/products/migrations.d.ts +0 -2
- package/dist/features/products/migrations.js +0 -10
- package/dist/features/products/repository/sublisting-categories.repository.d.ts +0 -16
- package/dist/features/products/repository/sublisting-categories.repository.js +0 -126
- package/dist/features/products/schemas/sublisting-categories.d.ts +0 -45
- package/dist/features/products/schemas/sublisting-categories.js +0 -16
- package/dist/features/promotions/migrations.d.ts +0 -2
- package/dist/features/promotions/migrations.js +0 -10
- package/dist/features/reviews/migrations.d.ts +0 -2
- package/dist/features/reviews/migrations.js +0 -10
- package/dist/features/scams/migrations.d.ts +0 -2
- package/dist/features/scams/migrations.js +0 -10
- package/dist/features/seller/migrations.d.ts +0 -2
- package/dist/features/seller/migrations.js +0 -10
- package/dist/features/stores/migrations.d.ts +0 -2
- package/dist/features/stores/migrations.js +0 -10
- package/dist/features/sublisting/migrations.d.ts +0 -2
- package/dist/features/sublisting/migrations.js +0 -10
- package/dist/features/sublisting/schemas/firestore.d.ts +0 -32
- package/dist/features/sublisting/schemas/firestore.js +0 -19
- package/dist/features/support/migrations.d.ts +0 -2
- package/dist/features/support/migrations.js +0 -10
- package/dist/features/wishlist/migrations.d.ts +0 -2
- package/dist/features/wishlist/migrations.js +0 -10
- package/dist/seed/_bundle-constants.d.ts +0 -14
- package/dist/seed/_bundle-constants.js +0 -14
- package/dist/seed/anime-figures-seed-data.d.ts +0 -8
- package/dist/seed/anime-figures-seed-data.js +0 -1033
- package/dist/seed/beyblade-seed-data.d.ts +0 -7
- package/dist/seed/beyblade-seed-data.js +0 -1129
- package/dist/seed/brands-seed-data.d.ts +0 -7
- package/dist/seed/brands-seed-data.js +0 -410
- package/dist/seed/bundles-seed-data.d.ts +0 -13
- package/dist/seed/bundles-seed-data.js +0 -229
- package/dist/seed/cosplay-accessories-seed-data.d.ts +0 -8
- package/dist/seed/cosplay-accessories-seed-data.js +0 -647
- package/dist/seed/hot-wheels-seed-data.d.ts +0 -7
- package/dist/seed/hot-wheels-seed-data.js +0 -1612
- package/dist/seed/letitrip-official-seed-data.d.ts +0 -8
- package/dist/seed/letitrip-official-seed-data.js +0 -399
- package/dist/seed/pokemon-carousel-slides-seed-data.d.ts +0 -8
- package/dist/seed/pokemon-carousel-slides-seed-data.js +0 -322
- package/dist/seed/pokemon-categories-seed-data.d.ts +0 -24
- package/dist/seed/pokemon-categories-seed-data.js +0 -547
- package/dist/seed/pokemon-coupons-seed-data.d.ts +0 -6
- package/dist/seed/pokemon-coupons-seed-data.js +0 -465
- package/dist/seed/pokemon-homepage-sections-seed-data.d.ts +0 -7
- package/dist/seed/pokemon-homepage-sections-seed-data.js +0 -241
- package/dist/seed/pokemon-products-seed-data.d.ts +0 -9
- package/dist/seed/pokemon-products-seed-data.js +0 -1791
- package/dist/seed/pokemon-seed-bundle.d.ts +0 -47
- package/dist/seed/pokemon-seed-bundle.js +0 -71
- package/dist/seed/pokemon-stores-seed-data.d.ts +0 -6
- package/dist/seed/pokemon-stores-seed-data.js +0 -179
- package/dist/seed/pokemon-users-seed-data.d.ts +0 -6
- package/dist/seed/pokemon-users-seed-data.js +0 -521
- package/dist/seed/products-seed-data.d.ts +0 -6
- package/dist/seed/products-seed-data.js +0 -2530
- package/dist/seed/retro-gaming-seed-data.d.ts +0 -8
- package/dist/seed/retro-gaming-seed-data.js +0 -801
- package/dist/seed/server.d.ts +0 -2
- package/dist/seed/server.js +0 -7
- package/dist/seed/sublisting-categories-seed-data.d.ts +0 -7
- package/dist/seed/sublisting-categories-seed-data.js +0 -159
- package/dist/seed/transformers-seed-data.d.ts +0 -7
- package/dist/seed/transformers-seed-data.js +0 -530
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { categoriesRepository } from "../../../repositories";
|
|
3
|
+
import { Container, Heading, Main, Section, Text } from "../../../ui";
|
|
4
|
+
import { AdSlot } from "../../homepage/components/AdSlot";
|
|
5
|
+
import { CategoryBundlesListing } from "./CategoryBundlesListing";
|
|
6
|
+
const DEFAULT_LIMIT = 200;
|
|
7
|
+
function sp(params, key) {
|
|
8
|
+
const v = params[key];
|
|
9
|
+
return Array.isArray(v) ? v[0] ?? "" : v ?? "";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Public `/bundles` index. Bundles live as `categoryType:"bundle"` rows on
|
|
13
|
+
* the categories collection (SB-UNI-D + V), so this fetches via
|
|
14
|
+
* `categoriesRepository.listByType("bundle", …)` instead of `productRepository`.
|
|
15
|
+
* Optional `?storeId=…` post-filters to a single seller's bundles.
|
|
16
|
+
* Sort UI is owned by `CategoryBundlesListing` (client).
|
|
17
|
+
*/
|
|
18
|
+
export async function BundlesListView({ searchParams = {}, }) {
|
|
19
|
+
const storeId = sp(searchParams, "storeId");
|
|
20
|
+
const all = await categoriesRepository
|
|
21
|
+
.listByType("bundle", { activeOnly: true, limit: DEFAULT_LIMIT })
|
|
22
|
+
.catch(() => []);
|
|
23
|
+
const bundles = storeId
|
|
24
|
+
? all.filter((c) => c.createdByStoreId === storeId)
|
|
25
|
+
: all;
|
|
26
|
+
return (_jsx(Main, { children: _jsx(Section, { className: "py-10", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-2 text-3xl font-semibold text-zinc-900 dark:text-zinc-100", children: "Collectible Bundles" }), _jsx(Text, { className: "mb-6 text-sm text-[var(--appkit-color-text-muted)]", children: "Curated multi-product bundles \u2014 Pok\u00E9mon starter sets, Beyblade tournament packs, Hot Wheels collector boxes and more. One price, one checkout, one shipment." }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(CategoryBundlesListing, { initialBundles: bundles }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
|
|
27
|
+
}
|
|
@@ -26,3 +26,15 @@ export { CategoriesIndexListing } from "./CategoriesIndexListing";
|
|
|
26
26
|
export type { CategoriesIndexListingProps } from "./CategoriesIndexListing";
|
|
27
27
|
export { CategoryDetailTabs } from "./CategoryDetailTabs";
|
|
28
28
|
export type { CategoryDetailTabsProps } from "./CategoryDetailTabs";
|
|
29
|
+
export { BundlesListView } from "./BundlesListView";
|
|
30
|
+
export type { BundlesListViewProps } from "./BundlesListView";
|
|
31
|
+
export { BundleDetailView } from "./BundleDetailView";
|
|
32
|
+
export type { BundleDetailViewProps } from "./BundleDetailView";
|
|
33
|
+
export { BundleBuyNowCta } from "./BundleBuyNowCta";
|
|
34
|
+
export type { BundleBuyNowCtaProps } from "./BundleBuyNowCta";
|
|
35
|
+
export { BundleDynamicRuleEditor } from "./BundleDynamicRuleEditor";
|
|
36
|
+
export type { BundleDynamicRuleEditorProps } from "./BundleDynamicRuleEditor";
|
|
37
|
+
export { BundleItemsPicker, defaultBundleItemsFetch, } from "./BundleItemsPicker";
|
|
38
|
+
export type { BundleItemsPickerProps, BundleItemSearchResult, } from "./BundleItemsPicker";
|
|
39
|
+
export { CategoryBundlesListing } from "./CategoryBundlesListing";
|
|
40
|
+
export type { CategoryBundlesListingProps } from "./CategoryBundlesListing";
|
|
@@ -13,3 +13,12 @@ export { CategorySelectorCreate } from "./CategorySelectorCreate";
|
|
|
13
13
|
export { getCategoryTableColumns } from "./CategoryTableColumns";
|
|
14
14
|
export { CategoriesIndexListing } from "./CategoriesIndexListing";
|
|
15
15
|
export { CategoryDetailTabs } from "./CategoryDetailTabs";
|
|
16
|
+
export { BundlesListView } from "./BundlesListView";
|
|
17
|
+
export { BundleDetailView } from "./BundleDetailView";
|
|
18
|
+
// S-SBUNI-RULES 2026-05-13 — direct-checkout CTA (replaces deleted BundleAddToCartCta).
|
|
19
|
+
export { BundleBuyNowCta } from "./BundleBuyNowCta";
|
|
20
|
+
// S-SBUNI-5 2026-05-13 — admin dynamic-rule editor (paired with BundleItemsPicker).
|
|
21
|
+
export { BundleDynamicRuleEditor } from "./BundleDynamicRuleEditor";
|
|
22
|
+
// S-SBUNI-4 2026-05-13 — multi-select product picker for bundle editor.
|
|
23
|
+
export { BundleItemsPicker, defaultBundleItemsFetch, } from "./BundleItemsPicker";
|
|
24
|
+
export { CategoryBundlesListing } from "./CategoryBundlesListing";
|
|
@@ -10,12 +10,19 @@ import { ValidationError } from "../../../errors";
|
|
|
10
10
|
import { serverLogger } from "../../../monitoring";
|
|
11
11
|
import { sendEmail } from "../../contact/email";
|
|
12
12
|
import { CONSENT_OTP_EXPIRY_MS, CONSENT_OTP_MAX_ATTEMPTS, hashOtp, maskEmail, generateOtpCode, buildConsentOtpEmail, enforceConsentOtpRateLimit, saveConsentOtp, readConsentOtp, patchConsentOtp, } from "../../auth/consent-otp";
|
|
13
|
-
import {
|
|
13
|
+
import { addressesRepository } from "../../addresses/repository/addresses.repository";
|
|
14
|
+
async function findUserAddress(userId, addressId) {
|
|
15
|
+
const address = await addressesRepository.findById(addressId);
|
|
16
|
+
if (!address || address.ownerType !== "user" || address.ownerId !== userId) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return address;
|
|
20
|
+
}
|
|
14
21
|
import { userRepository } from "../../auth/repository/user.repository";
|
|
15
22
|
import { resolveDate } from "../../../utils";
|
|
16
23
|
/** Returns masked email so the UI can confirm which inbox to check. */
|
|
17
24
|
export async function sendCheckoutConsentOtp(userId, userEmail, addressId) {
|
|
18
|
-
const address = await
|
|
25
|
+
const address = await findUserAddress(userId, addressId);
|
|
19
26
|
if (!address)
|
|
20
27
|
throw new ValidationError("Address not found.");
|
|
21
28
|
await enforceConsentOtpRateLimit(userId);
|
|
@@ -69,7 +76,7 @@ export async function verifyCheckoutConsentOtp(userId, addressId, code) {
|
|
|
69
76
|
export async function grantCheckoutConsentViaSms(userId, userPhone, addressId) {
|
|
70
77
|
if (!userPhone)
|
|
71
78
|
throw new ValidationError("No phone number registered on your account.");
|
|
72
|
-
const address = await
|
|
79
|
+
const address = await findUserAddress(userId, addressId);
|
|
73
80
|
if (!address)
|
|
74
81
|
throw new ValidationError("Address not found.");
|
|
75
82
|
const normalizePhone = (s) => s.replace(/[^0-9]/g, "").slice(-10);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
5
5
|
import { Alert, Button, Form, FormActions, Input, RichTextEditor, Select, StackedViewShell, Text, Toggle, } from "../../../ui";
|
|
@@ -34,6 +34,14 @@ const EVENT_TYPE_OPTIONS = [
|
|
|
34
34
|
{ label: "Poll", value: "poll" },
|
|
35
35
|
{ label: "Survey", value: "survey" },
|
|
36
36
|
{ label: "Feedback", value: "feedback" },
|
|
37
|
+
{ label: "Raffle", value: "raffle" },
|
|
38
|
+
{ label: "Spin Wheel", value: "spin_wheel" },
|
|
39
|
+
];
|
|
40
|
+
const RAFFLE_TYPE_OPTIONS = [
|
|
41
|
+
{ label: "Open raffle (all confirmed entries)", value: "open_raffle" },
|
|
42
|
+
{ label: "Top N scorers", value: "top_n_scorers" },
|
|
43
|
+
{ label: "Top N earliest participants", value: "top_n_participants" },
|
|
44
|
+
{ label: "Spin wheel", value: "spin_wheel" },
|
|
37
45
|
];
|
|
38
46
|
const EVENT_STATUS_OPTIONS = [
|
|
39
47
|
{ label: "Draft", value: "draft" },
|
|
@@ -70,7 +78,20 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
|
|
|
70
78
|
const [maxEntriesPerUser, setMaxEntriesPerUser] = React.useState("1");
|
|
71
79
|
const [hasLeaderboard, setHasLeaderboard] = React.useState(false);
|
|
72
80
|
const [anonymous, setAnonymous] = React.useState(false);
|
|
81
|
+
const [hasRaffle, setHasRaffle] = React.useState(false);
|
|
82
|
+
const [raffleType, setRaffleType] = React.useState("open_raffle");
|
|
83
|
+
const [raffleTopN, setRaffleTopN] = React.useState("10");
|
|
84
|
+
const [rafflePrize, setRafflePrize] = React.useState("");
|
|
85
|
+
const [rafflePrizeCouponId, setRafflePrizeCouponId] = React.useState("");
|
|
86
|
+
const [spinPrizes, setSpinPrizes] = React.useState([]);
|
|
87
|
+
const [spinMaxPerUser, setSpinMaxPerUser] = React.useState("1");
|
|
88
|
+
const [spinWindowStart, setSpinWindowStart] = React.useState("");
|
|
89
|
+
const [spinWindowEnd, setSpinWindowEnd] = React.useState("");
|
|
90
|
+
const [raffleWinnerName, setRaffleWinnerName] = React.useState("");
|
|
91
|
+
const [raffleEntryCount, setRaffleEntryCount] = React.useState(null);
|
|
92
|
+
const [raffleGithubUrl, setRaffleGithubUrl] = React.useState("");
|
|
73
93
|
const [saveMessage, setSaveMessage] = React.useState(null);
|
|
94
|
+
const [triggerMessage, setTriggerMessage] = React.useState(null);
|
|
74
95
|
const eventQuery = useQuery({
|
|
75
96
|
queryKey: ["admin-event-by-id", eventId],
|
|
76
97
|
queryFn: () => apiClient.get(`${ADMIN_ENDPOINTS.EVENTS}/${eventId}`),
|
|
@@ -112,6 +133,18 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
|
|
|
112
133
|
if (event.feedbackConfig) {
|
|
113
134
|
setAnonymous(Boolean(event.feedbackConfig.anonymous));
|
|
114
135
|
}
|
|
136
|
+
setHasRaffle(Boolean(event.hasRaffle));
|
|
137
|
+
setRaffleType(event.raffleType || "open_raffle");
|
|
138
|
+
setRaffleTopN(String(event.raffleTopN ?? 10));
|
|
139
|
+
setRafflePrize(event.rafflePrize || "");
|
|
140
|
+
setRafflePrizeCouponId(event.rafflePrizeCouponId || "");
|
|
141
|
+
setSpinPrizes(Array.isArray(event.spinPrizes) ? event.spinPrizes : []);
|
|
142
|
+
setSpinMaxPerUser(String(event.spinMaxPerUser ?? 1));
|
|
143
|
+
setSpinWindowStart(toLocalDatetime(event.spinWindowStart));
|
|
144
|
+
setSpinWindowEnd(toLocalDatetime(event.spinWindowEnd));
|
|
145
|
+
setRaffleWinnerName(event.raffleWinnerDisplayName || "");
|
|
146
|
+
setRaffleEntryCount(typeof event.raffleEntryCount === "number" ? event.raffleEntryCount : null);
|
|
147
|
+
setRaffleGithubUrl(event.raffleGithubFunctionUrl || "");
|
|
115
148
|
}, [eventQuery.data]);
|
|
116
149
|
const saveMutation = useMutation({
|
|
117
150
|
mutationFn: async () => {
|
|
@@ -153,6 +186,29 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
|
|
|
153
186
|
};
|
|
154
187
|
return {};
|
|
155
188
|
};
|
|
189
|
+
const isRaffleType = type === "raffle" || type === "spin_wheel";
|
|
190
|
+
const raffleFields = {};
|
|
191
|
+
if (hasRaffle || isRaffleType) {
|
|
192
|
+
raffleFields.hasRaffle = true;
|
|
193
|
+
raffleFields.raffleType = isRaffleType && type === "spin_wheel"
|
|
194
|
+
? "spin_wheel"
|
|
195
|
+
: raffleType;
|
|
196
|
+
if (Number(raffleTopN) > 0)
|
|
197
|
+
raffleFields.raffleTopN = Number(raffleTopN);
|
|
198
|
+
if (rafflePrize)
|
|
199
|
+
raffleFields.rafflePrize = rafflePrize;
|
|
200
|
+
if (rafflePrizeCouponId)
|
|
201
|
+
raffleFields.rafflePrizeCouponId = rafflePrizeCouponId;
|
|
202
|
+
if (raffleType === "spin_wheel" || type === "spin_wheel") {
|
|
203
|
+
raffleFields.spinPrizes = spinPrizes;
|
|
204
|
+
if (Number(spinMaxPerUser) > 0)
|
|
205
|
+
raffleFields.spinMaxPerUser = Number(spinMaxPerUser);
|
|
206
|
+
if (spinWindowStart)
|
|
207
|
+
raffleFields.spinWindowStart = toISOString(spinWindowStart);
|
|
208
|
+
if (spinWindowEnd)
|
|
209
|
+
raffleFields.spinWindowEnd = toISOString(spinWindowEnd);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
156
212
|
const payload = {
|
|
157
213
|
type,
|
|
158
214
|
title,
|
|
@@ -161,6 +217,7 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
|
|
|
161
217
|
endsAt: toISOString(endsAt),
|
|
162
218
|
coverImageUrl: coverImageUrl || undefined,
|
|
163
219
|
...buildTypeConfig(),
|
|
220
|
+
...raffleFields,
|
|
164
221
|
};
|
|
165
222
|
if (eventId) {
|
|
166
223
|
payload.status = status;
|
|
@@ -179,6 +236,44 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
|
|
|
179
236
|
setSaveMessage(error instanceof Error ? error.message : "Save failed");
|
|
180
237
|
},
|
|
181
238
|
});
|
|
239
|
+
const addSpinPrize = () => {
|
|
240
|
+
setSpinPrizes((prev) => [
|
|
241
|
+
...prev,
|
|
242
|
+
{
|
|
243
|
+
id: `prize-${Date.now()}`,
|
|
244
|
+
label: "",
|
|
245
|
+
couponId: undefined,
|
|
246
|
+
weight: 1,
|
|
247
|
+
isActive: true,
|
|
248
|
+
},
|
|
249
|
+
]);
|
|
250
|
+
};
|
|
251
|
+
const removeSpinPrize = (id) => {
|
|
252
|
+
setSpinPrizes((prev) => prev.filter((p) => p.id !== id));
|
|
253
|
+
};
|
|
254
|
+
const updateSpinPrize = (id, patch) => {
|
|
255
|
+
setSpinPrizes((prev) => prev.map((p) => (p.id === id ? { ...p, ...patch } : p)));
|
|
256
|
+
};
|
|
257
|
+
const triggerRaffleMutation = useMutation({
|
|
258
|
+
mutationFn: async () => {
|
|
259
|
+
if (!eventId)
|
|
260
|
+
throw new Error("Save the event before triggering");
|
|
261
|
+
return apiClient.post(ADMIN_ENDPOINTS.EVENT_TRIGGER_RAFFLE(eventId), {});
|
|
262
|
+
},
|
|
263
|
+
onSuccess: (res) => {
|
|
264
|
+
const data = res.data ?? res;
|
|
265
|
+
setTriggerMessage(data?.raffleWinnerDisplayName
|
|
266
|
+
? `Winner: ${data.raffleWinnerDisplayName}`
|
|
267
|
+
: "Raffle triggered.");
|
|
268
|
+
if (data?.raffleWinnerDisplayName)
|
|
269
|
+
setRaffleWinnerName(data.raffleWinnerDisplayName);
|
|
270
|
+
if (typeof data?.raffleEntryCount === "number")
|
|
271
|
+
setRaffleEntryCount(data.raffleEntryCount);
|
|
272
|
+
},
|
|
273
|
+
onError: (err) => {
|
|
274
|
+
setTriggerMessage(err instanceof Error ? err.message : "Trigger failed");
|
|
275
|
+
},
|
|
276
|
+
});
|
|
182
277
|
const addPollOption = () => {
|
|
183
278
|
setPollOptions((prev) => [...prev, { id: `opt${Date.now()}`, label: "" }]);
|
|
184
279
|
};
|
|
@@ -197,7 +292,18 @@ export function AdminEventEditorView({ eventId, onSaved, embedded, ...rest }) {
|
|
|
197
292
|
const formSection = (_jsxs(Form, { onSubmit: (e) => {
|
|
198
293
|
e.preventDefault();
|
|
199
294
|
saveMutation.mutate();
|
|
200
|
-
}, className: "space-y-6", children: [!eventId && (_jsx(Select, { label: "Event type", value: type, options: EVENT_TYPE_OPTIONS, onChange: (e) => setType(e.target.value) })), _jsx(Input, { label: "Title", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "Charizard Flash Sale 2026", required: true }), _jsxs("div", { children: [_jsx("label", { className: "block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Description" }), _jsx(RichTextEditor, { value: description, onChange: setDescription, minHeightClassName: "min-h-[120px]", placeholder: "Describe this event\u2026" })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Starts at", type: "datetime-local", value: startsAt, onChange: (e) => setStartsAt(e.target.value), required: true }), _jsx(Input, { label: "Ends at", type: "datetime-local", value: endsAt, onChange: (e) => setEndsAt(e.target.value), required: true })] }), _jsx(Input, { label: "Cover image URL (optional)", value: coverImageUrl, onChange: (e) => setCoverImageUrl(e.target.value), placeholder: "https://\u2026" }), eventId && (_jsx(Select, { label: "Status", value: status, options: EVENT_STATUS_OPTIONS, onChange: (e) => setStatus(e.target.value) })), type === "sale" && (_jsxs("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 p-4 space-y-3", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Sale configuration" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Discount %", type: "number", value: discountPercent, onChange: (e) => setDiscountPercent(e.target.value), placeholder: "10" }), _jsx(Input, { label: "Banner text (optional)", value: saleBannerText, onChange: (e) => setSaleBannerText(e.target.value), placeholder: "Limited time \u2014 ends Sunday!" })] })] })), type === "offer" && (_jsxs("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 p-4 space-y-3", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Offer configuration" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Coupon ID", value: couponId, onChange: (e) => setCouponId(e.target.value), placeholder: "Firestore coupon document ID", required: true }), _jsx(Input, { label: "Display code", value: displayCode, onChange: (e) => setDisplayCode(e.target.value), placeholder: "CHARIZARD25", required: true })] }), _jsx(Input, { label: "Banner text (optional)", value: offerBannerText, onChange: (e) => setOfferBannerText(e.target.value), placeholder: "Use CHARIZARD25 at checkout" })] })), type === "poll" && (_jsxs("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 p-4 space-y-4", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Poll configuration" }), _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Options (minimum 2)" }), pollOptions.map((opt, idx) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex-1", children: _jsx(Input, { value: opt.label, onChange: (e) => updatePollOption(opt.id, e.target.value), placeholder: `Option ${idx + 1}` }) }), pollOptions.length > 2 && (_jsx("button", { type: "button", onClick: () => removePollOption(opt.id), className: "text-zinc-400 hover:text-red-500 transition-colors px-2 py-1 text-lg leading-none", "aria-label": "Remove option", children: "\u00D7" }))] }, opt.id))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addPollOption, children: "+ Add option" })] }), _jsx(Select, { label: "Results visibility", value: resultsVisibility, options: POLL_VISIBILITY_OPTIONS, onChange: (e) => setResultsVisibility(e.target.value) }), _jsxs("div", { className: "space-y-3", children: [_jsx(Toggle, { checked: allowMultiSelect, onChange: setAllowMultiSelect, label: "Allow multi-select" }), _jsx(Toggle, { checked: allowComment, onChange: setAllowComment, label: "Allow comment with vote" })] })] })), type === "survey" && (_jsxs("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 p-4 space-y-3", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Survey configuration" }), _jsx(Input, { label: "Max entries per user", type: "number", value: maxEntriesPerUser, onChange: (e) => setMaxEntriesPerUser(e.target.value), placeholder: "1" }), _jsx(Toggle, { checked: requireLogin, onChange: setRequireLogin, label: "Require login to participate" }), _jsx(Toggle, { checked: hasLeaderboard, onChange: setHasLeaderboard, label: "Show leaderboard" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Form fields can be configured from the event detail page after saving." })] })), type === "feedback" && (_jsxs("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 p-4 space-y-3", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Feedback configuration" }), _jsx(Toggle, { checked: anonymous, onChange: setAnonymous, label: "Allow anonymous submissions" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Form fields can be configured from the event detail page after saving." })] })), _jsxs(
|
|
295
|
+
}, className: "space-y-6", children: [!eventId && (_jsx(Select, { label: "Event type", value: type, options: EVENT_TYPE_OPTIONS, onChange: (e) => setType(e.target.value) })), _jsx(Input, { label: "Title", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "Charizard Flash Sale 2026", required: true }), _jsxs("div", { children: [_jsx("label", { className: "block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Description" }), _jsx(RichTextEditor, { value: description, onChange: setDescription, minHeightClassName: "min-h-[120px]", placeholder: "Describe this event\u2026" })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Starts at", type: "datetime-local", value: startsAt, onChange: (e) => setStartsAt(e.target.value), required: true }), _jsx(Input, { label: "Ends at", type: "datetime-local", value: endsAt, onChange: (e) => setEndsAt(e.target.value), required: true })] }), _jsx(Input, { label: "Cover image URL (optional)", value: coverImageUrl, onChange: (e) => setCoverImageUrl(e.target.value), placeholder: "https://\u2026" }), eventId && (_jsx(Select, { label: "Status", value: status, options: EVENT_STATUS_OPTIONS, onChange: (e) => setStatus(e.target.value) })), type === "sale" && (_jsxs("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 p-4 space-y-3", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Sale configuration" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Discount %", type: "number", value: discountPercent, onChange: (e) => setDiscountPercent(e.target.value), placeholder: "10" }), _jsx(Input, { label: "Banner text (optional)", value: saleBannerText, onChange: (e) => setSaleBannerText(e.target.value), placeholder: "Limited time \u2014 ends Sunday!" })] })] })), type === "offer" && (_jsxs("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 p-4 space-y-3", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Offer configuration" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Coupon ID", value: couponId, onChange: (e) => setCouponId(e.target.value), placeholder: "Firestore coupon document ID", required: true }), _jsx(Input, { label: "Display code", value: displayCode, onChange: (e) => setDisplayCode(e.target.value), placeholder: "CHARIZARD25", required: true })] }), _jsx(Input, { label: "Banner text (optional)", value: offerBannerText, onChange: (e) => setOfferBannerText(e.target.value), placeholder: "Use CHARIZARD25 at checkout" })] })), type === "poll" && (_jsxs("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 p-4 space-y-4", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Poll configuration" }), _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Options (minimum 2)" }), pollOptions.map((opt, idx) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex-1", children: _jsx(Input, { value: opt.label, onChange: (e) => updatePollOption(opt.id, e.target.value), placeholder: `Option ${idx + 1}` }) }), pollOptions.length > 2 && (_jsx("button", { type: "button", onClick: () => removePollOption(opt.id), className: "text-zinc-400 hover:text-red-500 transition-colors px-2 py-1 text-lg leading-none", "aria-label": "Remove option", children: "\u00D7" }))] }, opt.id))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addPollOption, children: "+ Add option" })] }), _jsx(Select, { label: "Results visibility", value: resultsVisibility, options: POLL_VISIBILITY_OPTIONS, onChange: (e) => setResultsVisibility(e.target.value) }), _jsxs("div", { className: "space-y-3", children: [_jsx(Toggle, { checked: allowMultiSelect, onChange: setAllowMultiSelect, label: "Allow multi-select" }), _jsx(Toggle, { checked: allowComment, onChange: setAllowComment, label: "Allow comment with vote" })] })] })), type === "survey" && (_jsxs("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 p-4 space-y-3", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Survey configuration" }), _jsx(Input, { label: "Max entries per user", type: "number", value: maxEntriesPerUser, onChange: (e) => setMaxEntriesPerUser(e.target.value), placeholder: "1" }), _jsx(Toggle, { checked: requireLogin, onChange: setRequireLogin, label: "Require login to participate" }), _jsx(Toggle, { checked: hasLeaderboard, onChange: setHasLeaderboard, label: "Show leaderboard" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Form fields can be configured from the event detail page after saving." })] })), type === "feedback" && (_jsxs("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 p-4 space-y-3", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Feedback configuration" }), _jsx(Toggle, { checked: anonymous, onChange: setAnonymous, label: "Allow anonymous submissions" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Form fields can be configured from the event detail page after saving." })] })), _jsxs("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 p-4 space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-700 dark:text-zinc-300", children: "Raffle & spin wheel" }), type !== "raffle" && type !== "spin_wheel" && (_jsx(Toggle, { checked: hasRaffle, onChange: setHasRaffle, label: "Attach raffle" }))] }), (hasRaffle || type === "raffle" || type === "spin_wheel") && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Select, { label: "Raffle type", value: type === "spin_wheel" ? "spin_wheel" : raffleType, options: RAFFLE_TYPE_OPTIONS, onChange: (e) => setRaffleType(e.target.value), disabled: type === "spin_wheel" }), _jsx(Input, { label: "Top N (for top-N raffle types)", type: "number", value: raffleTopN, onChange: (e) => setRaffleTopN(e.target.value), placeholder: "10" })] }), _jsx(Input, { label: "Prize description", value: rafflePrize, onChange: (e) => setRafflePrize(e.target.value), placeholder: "\u20B92,000 store credit + exclusive Pikachu sticker" }), _jsx(Input, { label: "Coupon ID for winner (optional)", value: rafflePrizeCouponId, onChange: (e) => setRafflePrizeCouponId(e.target.value), placeholder: "coupon-vip-winner-2026" }), (raffleType === "spin_wheel" || type === "spin_wheel") && (_jsxs("div", { className: "rounded-lg border border-zinc-200 dark:border-slate-700 p-3 space-y-3", children: [_jsx(Text, { className: "text-xs font-medium text-zinc-600 dark:text-zinc-300", children: "Spin prizes (weighted random)" }), spinPrizes.length === 0 && (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "No spin prizes yet. Add at least one to enable spinning." })), spinPrizes.map((p) => (_jsxs("div", { className: "grid grid-cols-12 gap-2 items-end", children: [_jsx("div", { className: "col-span-5", children: _jsx(Input, { label: "Label", value: p.label, onChange: (e) => updateSpinPrize(p.id, { label: e.target.value }), placeholder: "\u20B9100 off" }) }), _jsx("div", { className: "col-span-3", children: _jsx(Input, { label: "Coupon ID", value: p.couponId ?? "", onChange: (e) => updateSpinPrize(p.id, {
|
|
296
|
+
couponId: e.target.value || undefined,
|
|
297
|
+
}) }) }), _jsx("div", { className: "col-span-2", children: _jsx(Input, { label: "Weight", type: "number", value: String(p.weight), onChange: (e) => updateSpinPrize(p.id, {
|
|
298
|
+
weight: Number(e.target.value) || 0,
|
|
299
|
+
}) }) }), _jsx("div", { className: "col-span-1 flex items-center justify-center pb-2", children: _jsx(Toggle, { checked: p.isActive, onChange: (v) => updateSpinPrize(p.id, { isActive: v }), label: "" }) }), _jsx("div", { className: "col-span-1 flex items-center justify-center pb-2", children: _jsx("button", { type: "button", onClick: () => removeSpinPrize(p.id), className: "text-zinc-400 hover:text-red-500 text-lg leading-none px-2", "aria-label": "Remove prize", children: "\u00D7" }) })] }, p.id))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addSpinPrize, children: "+ Add spin prize" }), _jsx(Input, { label: "Max spins per user", type: "number", value: spinMaxPerUser, onChange: (e) => setSpinMaxPerUser(e.target.value), placeholder: "1" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Input, { label: "Spin window start", type: "datetime-local", value: spinWindowStart, onChange: (e) => setSpinWindowStart(e.target.value) }), _jsx(Input, { label: "Spin window end", type: "datetime-local", value: spinWindowEnd, onChange: (e) => setSpinWindowEnd(e.target.value) })] })] })), eventId && (_jsxs("div", { className: "rounded-lg border border-amber-200 bg-amber-50 dark:border-amber-700 dark:bg-amber-900/20 p-3 space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx(Text, { className: "text-sm font-medium text-amber-900 dark:text-amber-100", children: raffleWinnerName
|
|
300
|
+
? `Winner: ${raffleWinnerName}`
|
|
301
|
+
: "Raffle not yet triggered" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => triggerRaffleMutation.mutate(), disabled: triggerRaffleMutation.isPending ||
|
|
302
|
+
Boolean(raffleWinnerName), children: triggerRaffleMutation.isPending
|
|
303
|
+
? "Triggering…"
|
|
304
|
+
: raffleWinnerName
|
|
305
|
+
? "Already triggered"
|
|
306
|
+
: "Trigger Raffle Now" })] }), raffleEntryCount !== null && (_jsxs(Text, { className: "text-xs text-amber-800 dark:text-amber-200", children: ["Pool size: ", raffleEntryCount] })), raffleGithubUrl && (_jsxs(Text, { className: "text-xs text-amber-800 dark:text-amber-200 break-all", children: ["Fairness proof: ", raffleGithubUrl] })), triggerMessage && (_jsx(Text, { className: "text-xs text-amber-900 dark:text-amber-100", children: triggerMessage }))] }))] }))] }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "outline", onClick: () => { window.location.href = "/admin/events"; }, children: "Cancel" }), _jsx(Button, { type: "submit", disabled: saveMutation.isPending || !isValid, children: saveMutation.isPending ? "Saving…" : eventId ? "Save changes" : "Create event" })] }), saveMessage && (_jsx(Alert, { variant: saveMessage.toLowerCase().includes("fail") ? "error" : "success", title: "Save status", children: saveMessage }))] }));
|
|
201
307
|
if (embedded) {
|
|
202
308
|
return (_jsxs("div", { className: "overflow-y-auto p-4 space-y-4", children: [alertSection, formSection] }));
|
|
203
309
|
}
|
|
@@ -4,8 +4,9 @@ import React, { useState, useCallback } from "react";
|
|
|
4
4
|
import { Plus, X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
7
|
-
import { Button, ListingToolbar, Pagination, ListingViewShell, SideDrawer } from "../../../ui";
|
|
7
|
+
import { Button, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, SideDrawer } from "../../../ui";
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
|
+
import { ADMIN_EVENT_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
9
10
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../../admin/hooks/useAdminListingData";
|
|
10
11
|
import { DataTable } from "../../admin/components/DataTable";
|
|
11
12
|
import { AdminEventEditorView } from "./AdminEventEditorView";
|
|
@@ -18,8 +19,19 @@ const SORT_OPTIONS = [
|
|
|
18
19
|
{ value: "-createdAt", label: "Newest" },
|
|
19
20
|
{ value: "title", label: "Title A–Z" },
|
|
20
21
|
];
|
|
21
|
-
const STATUS_OPTIONS =
|
|
22
|
-
|
|
22
|
+
const STATUS_OPTIONS = ADMIN_EVENT_STATUS_TABS;
|
|
23
|
+
// TODO(events): TYPE_OPTIONS list pre-dates the SB9 EventType union refresh
|
|
24
|
+
// (sale|offer|poll|survey|feedback|raffle|spin_wheel). Keep current values to
|
|
25
|
+
// avoid silently breaking saved-filter URLs — sync in a dedicated follow-up.
|
|
26
|
+
const TYPE_OPTIONS = [
|
|
27
|
+
{ id: "All", label: "All" },
|
|
28
|
+
{ id: "contest", label: "Contest" },
|
|
29
|
+
{ id: "giveaway", label: "Giveaway" },
|
|
30
|
+
{ id: "sale", label: "Sale" },
|
|
31
|
+
{ id: "poll", label: "Poll" },
|
|
32
|
+
{ id: "survey", label: "Survey" },
|
|
33
|
+
{ id: "flash-sale", label: "Flash Sale" },
|
|
34
|
+
];
|
|
23
35
|
export function AdminEventsView({ children, getRowHref, ...props }) {
|
|
24
36
|
const hasChildren = React.Children.count(children) > 0;
|
|
25
37
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
@@ -86,5 +98,5 @@ export function AdminEventsView({ children, getRowHref, ...props }) {
|
|
|
86
98
|
if (hasChildren) {
|
|
87
99
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
88
100
|
}
|
|
89
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search events by title or type", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); table.setPage(1); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add Event"] }) }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No events found", onRowClick: (row) => openEditPanel(row.id) })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [
|
|
101
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search events by title or type", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); table.setPage(1); }, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add Event"] }) }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No events found", onRowClick: (row) => openEditPanel(row.id) })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [_jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Type", tabs: TYPE_OPTIONS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) })] }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Event" : "Edit Event", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminEventEditorView, { eventId: editId ?? undefined, onSaved: closePanel, embedded: true })) })] }));
|
|
90
102
|
}
|
|
@@ -9,6 +9,9 @@ interface EventCardProps {
|
|
|
9
9
|
};
|
|
10
10
|
onParticipate?: (event: EventItem) => void;
|
|
11
11
|
className?: string;
|
|
12
|
+
selectable?: boolean;
|
|
13
|
+
isSelected?: boolean;
|
|
14
|
+
onSelect?: (id: string, selected: boolean) => void;
|
|
12
15
|
}
|
|
13
|
-
export declare function EventCard({ event, labels, onParticipate, className, }: EventCardProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare function EventCard({ event, labels, onParticipate, className, selectable, isSelected, onSelect, }: EventCardProps): import("react/jsx-runtime").JSX.Element;
|
|
14
17
|
export {};
|
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import Link from "next/link";
|
|
3
|
-
import { Article, Button, Div, Heading, RichText, Span, TextLink } from "../../../ui";
|
|
3
|
+
import { Article, BaseListingCard, Button, Div, Heading, RichText, Span, TextLink } from "../../../ui";
|
|
4
4
|
import { THEME_CONSTANTS, LAYOUT } from "../../../tokens";
|
|
5
5
|
import { normalizeRichTextHtml } from "../../../utils/string.formatter";
|
|
6
6
|
import { EVENT_FIELDS } from "../schemas";
|
|
7
7
|
import { EventStatusBadge } from "./EventStatusBadge";
|
|
8
8
|
import { ROUTES } from "../../../next";
|
|
9
|
+
import { useLongPress } from "../../../react/hooks/useLongPress";
|
|
9
10
|
const TYPE_ICONS = {
|
|
10
11
|
sale: "🏷️",
|
|
11
12
|
offer: "🎁",
|
|
12
13
|
poll: "📊",
|
|
13
14
|
survey: "📝",
|
|
14
15
|
feedback: "💬",
|
|
16
|
+
raffle: "🎟️",
|
|
17
|
+
spin_wheel: "🎡",
|
|
15
18
|
};
|
|
16
|
-
export function EventCard({ event, labels = {}, onParticipate, className = "", }) {
|
|
19
|
+
export function EventCard({ event, labels = {}, onParticipate, className = "", selectable = false, isSelected = false, onSelect, }) {
|
|
20
|
+
const longPress = useLongPress(() => onSelect?.(event.id, !isSelected));
|
|
17
21
|
const safeTitle = event.title?.trim() || "Untitled event";
|
|
18
22
|
const now = new Date();
|
|
19
23
|
const endsAt = new Date(event.endsAt);
|
|
20
24
|
const msLeft = endsAt.getTime() - now.getTime();
|
|
21
25
|
const daysLeft = Math.max(0, Math.ceil(msLeft / (1000 * 60 * 60 * 24)));
|
|
22
26
|
const detailHref = String(ROUTES.PUBLIC.EVENT_DETAIL(event.slug ?? event.id));
|
|
23
|
-
return (_jsxs(Article, { className: `group flex h-full ${LAYOUT.cardHeight.event} flex-col overflow-hidden rounded-xl border
|
|
27
|
+
return (_jsxs(Article, { className: `group relative flex h-full ${LAYOUT.cardHeight.event} flex-col overflow-hidden rounded-xl border bg-white shadow-sm transition-shadow hover:shadow-md dark:bg-slate-900 ${isSelected ? "border-primary outline outline-2 outline-primary" : "border-zinc-200 dark:border-slate-700"} ${className}`, onMouseDown: onSelect && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: onSelect && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: onSelect && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: onSelect && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: onSelect && !isSelected ? longPress.onTouchEnd : undefined, children: [onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(event.id, !isSelected); }, label: isSelected ? "Deselect event" : "Select event", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsx(Link, { href: detailHref, className: "block flex-shrink-0", children: event.coverImageUrl ? (_jsx(Div, { className: "aspect-video overflow-hidden", children: _jsx(Div, { role: "img", "aria-label": safeTitle, className: "h-full w-full bg-cover bg-center transition-transform duration-300 group-hover:scale-105", style: { backgroundImage: `url(${event.coverImageUrl})` } }) })) : (_jsx(Div, { className: "aspect-video bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-slate-800 dark:to-slate-700 flex items-center justify-center", children: _jsx("span", { className: "text-5xl opacity-40", "aria-hidden": "true", children: TYPE_ICONS[event.type] }) })) }), _jsxs(Div, { className: "flex flex-1 flex-col p-4", children: [_jsxs(Div, { className: "flex items-start justify-between gap-2 mb-2", children: [_jsx(Span, { className: "text-lg", "aria-hidden": "true", children: TYPE_ICONS[event.type] }), _jsx(EventStatusBadge, { status: event.status })] }), _jsx(Link, { href: detailHref, className: "block", children: _jsx(Heading, { level: 3, className: "font-semibold text-gray-900 dark:text-zinc-100 text-base leading-snug mb-1 group-hover:text-primary transition-colors", children: safeTitle }) }), _jsx(RichText, { html: normalizeRichTextHtml(event.description ?? ""), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: `mb-3 ${THEME_CONSTANTS.utilities.textClamp3} text-sm text-zinc-600 dark:text-zinc-400` }), _jsxs(Div, { className: "mb-3 mt-auto flex items-center justify-between text-xs text-zinc-500 dark:text-zinc-500", children: [event.status === EVENT_FIELDS.STATUS_VALUES.ACTIVE &&
|
|
24
28
|
daysLeft > 0 && _jsxs(Span, { children: ["\u23F1 ", daysLeft, "d remaining"] }), _jsxs(Span, { children: ["\uD83D\uDC65 ", event.stats.totalEntries, " ", labels.entries ?? "entries"] })] }), event.status === EVENT_FIELDS.STATUS_VALUES.ACTIVE && onParticipate ? (_jsx(Button, { type: "button", onClick: () => onParticipate(event), className: "w-full rounded-lg bg-primary py-2 text-sm font-medium text-white transition-colors hover:bg-primary-600", children: labels.participate ?? "Participate" })) : (_jsxs(TextLink, { href: detailHref, className: "inline-flex w-full items-center justify-center gap-1.5 rounded-lg border border-zinc-300 px-3 py-2 text-sm font-medium text-zinc-700 transition-colors hover:bg-zinc-100 dark:border-slate-600 dark:text-zinc-200 dark:hover:bg-slate-800", children: [labels.viewDetails ?? "View details", " \u2192"] }))] })] }));
|
|
25
29
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface RaffleWinnerEvent {
|
|
2
|
+
rafflePrize?: string;
|
|
3
|
+
raffleWinnerUserId?: string;
|
|
4
|
+
raffleWinnerDisplayName?: string;
|
|
5
|
+
raffleEntryCount?: number;
|
|
6
|
+
raffleTriggeredAt?: string | Date;
|
|
7
|
+
raffleGithubFunctionUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface EventRaffleWinnerViewProps {
|
|
10
|
+
event: RaffleWinnerEvent;
|
|
11
|
+
labels?: {
|
|
12
|
+
heading?: string;
|
|
13
|
+
winnerLabel?: string;
|
|
14
|
+
prizeLabel?: string;
|
|
15
|
+
poolLabel?: string;
|
|
16
|
+
fairnessLabel?: string;
|
|
17
|
+
notDrawnYet?: string;
|
|
18
|
+
anonymous?: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export declare function EventRaffleWinnerView({ event, labels }: EventRaffleWinnerViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Article, Div, Heading, Span, Text, TextLink } from "../../../ui";
|
|
3
|
+
const DEFAULT_LABELS = {
|
|
4
|
+
heading: "Raffle Winner",
|
|
5
|
+
winnerLabel: "Winner",
|
|
6
|
+
prizeLabel: "Prize",
|
|
7
|
+
poolLabel: "Entries in pool",
|
|
8
|
+
fairnessLabel: "Verifiable randomness",
|
|
9
|
+
notDrawnYet: "The raffle has not been drawn yet.",
|
|
10
|
+
anonymous: "Anonymous participant",
|
|
11
|
+
};
|
|
12
|
+
export function EventRaffleWinnerView({ event, labels }) {
|
|
13
|
+
const l = { ...DEFAULT_LABELS, ...(labels ?? {}) };
|
|
14
|
+
if (!event.raffleWinnerUserId) {
|
|
15
|
+
return (_jsx(Div, { className: "rounded-xl border border-dashed border-zinc-300 dark:border-zinc-700 px-6 py-10 text-center", children: _jsx(Text, { className: "text-zinc-500 dark:text-zinc-400", children: l.notDrawnYet }) }));
|
|
16
|
+
}
|
|
17
|
+
return (_jsxs(Article, { className: "space-y-4", children: [_jsxs(Heading, { level: 2, className: "text-xl font-semibold text-zinc-900 dark:text-zinc-100", children: ["\uD83C\uDF9F\uFE0F ", l.heading] }), _jsxs(Div, { className: "rounded-2xl border border-amber-200 bg-amber-50/60 dark:border-amber-700 dark:bg-amber-900/20 p-6 space-y-3", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-xs uppercase tracking-wide text-amber-700 dark:text-amber-300", children: l.winnerLabel }), _jsx(Text, { className: "mt-1 text-2xl font-bold text-amber-900 dark:text-amber-100", children: event.raffleWinnerDisplayName?.trim() || l.anonymous })] }), event.rafflePrize ? (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs uppercase tracking-wide text-amber-700 dark:text-amber-300", children: l.prizeLabel }), _jsx(Text, { className: "mt-1 text-base text-zinc-800 dark:text-zinc-100", children: event.rafflePrize })] })) : null, _jsxs(Div, { className: "flex flex-wrap gap-x-6 gap-y-2 pt-2 text-sm text-zinc-600 dark:text-zinc-300", children: [typeof event.raffleEntryCount === "number" ? (_jsxs(Span, { children: [l.poolLabel, ":", " ", _jsx(Span, { className: "font-medium text-zinc-900 dark:text-zinc-100", children: event.raffleEntryCount })] })) : null, event.raffleTriggeredAt ? (_jsx(Span, { children: new Date(event.raffleTriggeredAt).toLocaleString() })) : null] })] }), event.raffleGithubFunctionUrl ? (_jsxs(Div, { className: "rounded-lg border border-zinc-200 dark:border-zinc-700 p-4 text-sm", children: [_jsx(Text, { className: "font-medium text-zinc-700 dark:text-zinc-200", children: l.fairnessLabel }), _jsx(TextLink, { href: event.raffleGithubFunctionUrl, className: "mt-1 block break-all text-primary-600 dark:text-primary-400 hover:underline", children: event.raffleGithubFunctionUrl })] })) : null] }));
|
|
18
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SpinPrize } from "../types";
|
|
2
|
+
export interface SpinWheelViewProps {
|
|
3
|
+
eventId: string;
|
|
4
|
+
prizes: SpinPrize[];
|
|
5
|
+
alreadyUsed?: boolean;
|
|
6
|
+
initialPrizeId?: string;
|
|
7
|
+
initialCouponCode?: string;
|
|
8
|
+
windowStart?: string | Date | null;
|
|
9
|
+
windowEnd?: string | Date | null;
|
|
10
|
+
onSpin: (eventId: string) => Promise<{
|
|
11
|
+
spinPrizeId?: string;
|
|
12
|
+
spinPrizeTitle?: string;
|
|
13
|
+
spinPrizeCouponCode?: string;
|
|
14
|
+
reason?: string;
|
|
15
|
+
}>;
|
|
16
|
+
labels?: {
|
|
17
|
+
heading?: string;
|
|
18
|
+
spinButton?: string;
|
|
19
|
+
spinningButton?: string;
|
|
20
|
+
alreadySpun?: string;
|
|
21
|
+
outsideWindow?: string;
|
|
22
|
+
wonHeadline?: string;
|
|
23
|
+
couponHint?: string;
|
|
24
|
+
errorFallback?: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export declare function SpinWheelView({ eventId, prizes, alreadyUsed, initialPrizeId, initialCouponCode, windowStart, windowEnd, onSpin, labels, }: SpinWheelViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
|
+
import { Button, Div, Heading, Span, Text } from "../../../ui";
|
|
5
|
+
const DEFAULT_LABELS = {
|
|
6
|
+
heading: "Spin the Wheel",
|
|
7
|
+
spinButton: "Spin",
|
|
8
|
+
spinningButton: "Spinning…",
|
|
9
|
+
alreadySpun: "You've already used your spin for this event.",
|
|
10
|
+
outsideWindow: "The spin window is not open.",
|
|
11
|
+
wonHeadline: "You won:",
|
|
12
|
+
couponHint: "Coupon code:",
|
|
13
|
+
errorFallback: "Spin failed. Please try again.",
|
|
14
|
+
};
|
|
15
|
+
const ANIMATION_MS = 3200;
|
|
16
|
+
export function SpinWheelView({ eventId, prizes, alreadyUsed, initialPrizeId, initialCouponCode, windowStart, windowEnd, onSpin, labels, }) {
|
|
17
|
+
const l = { ...DEFAULT_LABELS, ...(labels ?? {}) };
|
|
18
|
+
const activePrizes = useMemo(() => prizes.filter((p) => p.isActive !== false), [prizes]);
|
|
19
|
+
const [spinning, setSpinning] = useState(false);
|
|
20
|
+
const [resultPrizeId, setResultPrizeId] = useState(initialPrizeId);
|
|
21
|
+
const [resultCoupon, setResultCoupon] = useState(initialCouponCode);
|
|
22
|
+
const [error, setError] = useState(null);
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
const startMs = windowStart ? new Date(windowStart).getTime() : null;
|
|
25
|
+
const endMs = windowEnd ? new Date(windowEnd).getTime() : null;
|
|
26
|
+
const inWindow = (startMs === null || now >= startMs) && (endMs === null || now <= endMs);
|
|
27
|
+
const disabled = spinning || alreadyUsed || !inWindow || activePrizes.length === 0;
|
|
28
|
+
const handleSpin = useCallback(async () => {
|
|
29
|
+
if (disabled)
|
|
30
|
+
return;
|
|
31
|
+
setError(null);
|
|
32
|
+
setSpinning(true);
|
|
33
|
+
try {
|
|
34
|
+
const result = await onSpin(eventId);
|
|
35
|
+
window.setTimeout(() => {
|
|
36
|
+
setSpinning(false);
|
|
37
|
+
if (result.spinPrizeId) {
|
|
38
|
+
setResultPrizeId(result.spinPrizeId);
|
|
39
|
+
setResultCoupon(result.spinPrizeCouponCode);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
setError(l.errorFallback);
|
|
43
|
+
}
|
|
44
|
+
}, ANIMATION_MS);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
setSpinning(false);
|
|
48
|
+
setError(l.errorFallback);
|
|
49
|
+
}
|
|
50
|
+
}, [disabled, eventId, l.errorFallback, onSpin]);
|
|
51
|
+
const wonPrize = resultPrizeId
|
|
52
|
+
? activePrizes.find((p) => p.id === resultPrizeId)
|
|
53
|
+
: undefined;
|
|
54
|
+
return (_jsxs(Div, { className: "space-y-4", children: [_jsxs(Heading, { level: 2, className: "text-xl font-semibold text-zinc-900 dark:text-zinc-100", children: ["\uD83C\uDFA1 ", l.heading] }), _jsxs(Div, { className: "relative mx-auto aspect-square w-64 overflow-hidden rounded-full border-4 border-amber-400 bg-gradient-to-br from-amber-100 via-rose-100 to-violet-100 dark:from-amber-900/40 dark:via-rose-900/40 dark:to-violet-900/40", "aria-label": "Spin wheel", children: [_jsx(Div, { className: "absolute inset-0 flex items-center justify-center", style: {
|
|
55
|
+
animation: spinning
|
|
56
|
+
? `lir-spin ${ANIMATION_MS}ms cubic-bezier(0.2, 0.8, 0.2, 1)`
|
|
57
|
+
: undefined,
|
|
58
|
+
}, children: _jsx(Text, { className: "text-4xl", children: "\uD83C\uDF81" }) }), _jsx("style", { children: `
|
|
59
|
+
@keyframes lir-spin {
|
|
60
|
+
0% { transform: rotate(0deg); }
|
|
61
|
+
100% { transform: rotate(2160deg); }
|
|
62
|
+
}
|
|
63
|
+
` })] }), wonPrize ? (_jsxs(Div, { className: "rounded-xl border border-emerald-300 bg-emerald-50 dark:border-emerald-700 dark:bg-emerald-900/30 p-4 text-center", children: [_jsx(Text, { className: "text-sm uppercase tracking-wide text-emerald-700 dark:text-emerald-300", children: l.wonHeadline }), _jsx(Text, { className: "mt-1 text-lg font-bold text-emerald-900 dark:text-emerald-100", children: wonPrize.label }), resultCoupon ? (_jsxs(Text, { className: "mt-2 text-sm text-emerald-800 dark:text-emerald-200", children: [l.couponHint, " ", _jsx(Span, { className: "font-mono font-semibold", children: resultCoupon })] })) : null] })) : null, error ? (_jsx(Text, { className: "text-sm text-rose-600 dark:text-rose-400 text-center", children: error })) : null, _jsx(Div, { className: "flex justify-center", children: _jsx(Button, { variant: "primary", size: "lg", onClick: handleSpin, disabled: disabled, children: spinning ? l.spinningButton : l.spinButton }) }), alreadyUsed ? (_jsx(Text, { className: "text-center text-sm text-zinc-500 dark:text-zinc-400", children: l.alreadySpun })) : !inWindow ? (_jsx(Text, { className: "text-center text-sm text-zinc-500 dark:text-zinc-400", children: l.outsideWindow })) : null] }));
|
|
64
|
+
}
|
|
@@ -25,3 +25,7 @@ export { EventPollWidget } from "./EventPollWidget";
|
|
|
25
25
|
export type {} from "./EventPollWidget";
|
|
26
26
|
export { EventRafflesSection } from "./EventRafflesSection";
|
|
27
27
|
export type { EventRafflesSectionProps } from "./EventRafflesSection";
|
|
28
|
+
export { EventRaffleWinnerView } from "./EventRaffleWinnerView";
|
|
29
|
+
export type { EventRaffleWinnerViewProps } from "./EventRaffleWinnerView";
|
|
30
|
+
export { SpinWheelView } from "./SpinWheelView";
|
|
31
|
+
export type { SpinWheelViewProps } from "./SpinWheelView";
|
|
@@ -13,3 +13,5 @@ export { EventFormDrawer } from "./EventFormDrawer";
|
|
|
13
13
|
export { EventBanner } from "./EventBanner";
|
|
14
14
|
export { EventPollWidget } from "./EventPollWidget";
|
|
15
15
|
export { EventRafflesSection } from "./EventRafflesSection";
|
|
16
|
+
export { EventRaffleWinnerView } from "./EventRaffleWinnerView";
|
|
17
|
+
export { SpinWheelView } from "./SpinWheelView";
|