@mohasinac/appkit 4.1.3 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/server/features/auth/actions.d.ts +0 -1
- package/dist/_internal/server/features/auth/actions.js +0 -1
- package/dist/_internal/server/features/bundles/data.d.ts +22 -4
- package/dist/_internal/server/features/bundles/data.js +41 -4
- package/dist/_internal/server/features/bundles/index.d.ts +1 -1
- package/dist/_internal/server/features/bundles/index.js +1 -1
- package/dist/_internal/server/features/checkout/actions.d.ts +15 -0
- package/dist/_internal/server/features/checkout/actions.js +249 -257
- package/dist/_internal/server/features/checkout/prize-bundle-gates.d.ts +5 -16
- package/dist/_internal/server/features/checkout/prize-bundle-gates.js +5 -30
- package/dist/_internal/server/features/orders/adapters.js +1 -14
- package/dist/_internal/server/features/products/actions.js +8 -3
- package/dist/_internal/server/features/products/service.d.ts +15 -0
- package/dist/_internal/server/features/products/service.js +41 -0
- package/dist/_internal/server/functions/firestore.d.ts +3 -1
- package/dist/_internal/server/functions/firestore.js +17 -1
- package/dist/_internal/server/functions/scheduled.d.ts +3 -5
- package/dist/_internal/server/functions/scheduled.js +14 -30
- package/dist/_internal/server/jobs/core/bundleStockSync.js +25 -7
- package/dist/_internal/server/jobs/core/countersReconcile.js +7 -1
- package/dist/_internal/server/jobs/core/index.d.ts +4 -4
- package/dist/_internal/server/jobs/core/index.js +4 -4
- package/dist/_internal/server/jobs/core/jobRunners.js +10 -0
- package/dist/_internal/server/jobs/core/newsletterExport.d.ts +15 -0
- package/dist/_internal/server/jobs/core/newsletterExport.js +43 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +2 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +1 -0
- package/dist/_internal/server/jobs/core/onPrizeDrawPaymentConfirmed.d.ts +19 -0
- package/dist/_internal/server/jobs/core/onPrizeDrawPaymentConfirmed.js +30 -0
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.d.ts +2 -2
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +3 -2
- package/dist/_internal/server/jobs/core/pendingOrderTimeout.js +1 -0
- package/dist/_internal/server/jobs/core/prizeDrawAssignWinner.d.ts +20 -0
- package/dist/_internal/server/jobs/core/prizeDrawAssignWinner.js +113 -0
- package/dist/_internal/server/jobs/core/prizeDrawExpiryReveal.d.ts +14 -0
- package/dist/_internal/server/jobs/core/prizeDrawExpiryReveal.js +74 -0
- package/dist/_internal/server/jobs/core/prizeDrawSoldOutReveal.d.ts +22 -0
- package/dist/_internal/server/jobs/core/prizeDrawSoldOutReveal.js +68 -0
- package/dist/_internal/server/jobs/core/revenueRollup.d.ts +13 -0
- package/dist/_internal/server/jobs/core/revenueRollup.js +22 -0
- package/dist/_internal/server/jobs/core/whatsappNotify.d.ts +35 -0
- package/dist/_internal/server/jobs/core/whatsappNotify.js +82 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +4 -4
- package/dist/_internal/server/jobs/handlers/index.js +9 -6
- package/dist/_internal/server/jobs/handlers/onPrizeDrawPaymentConfirmed.d.ts +3 -0
- package/dist/_internal/server/jobs/handlers/onPrizeDrawPaymentConfirmed.js +11 -0
- package/dist/_internal/server/jobs/handlers/prizeDrawExpiryReveal.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/prizeDrawExpiryReveal.js +2 -0
- package/dist/_internal/server/jobs/handlers/prizeDrawSoldOutReveal.d.ts +3 -0
- package/dist/_internal/server/jobs/handlers/prizeDrawSoldOutReveal.js +4 -0
- package/dist/_internal/server/jobs/handlers/revenueRollup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/revenueRollup.js +2 -0
- package/dist/_internal/shared/actions/action-registry.js +40 -10
- package/dist/_internal/shared/checkout/rules/_defaults.js +1 -2
- package/dist/_internal/shared/checkout/rules/_limits.d.ts +3 -0
- package/dist/_internal/shared/checkout/rules/_limits.js +3 -0
- package/dist/_internal/shared/checkout/rules/_registry.d.ts +2 -1
- package/dist/_internal/shared/checkout/rules/_registry.js +2 -2
- package/dist/_internal/shared/checkout/rules/classified.rule.js +0 -1
- package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
- package/dist/_internal/shared/checkout/rules/index.js +1 -1
- package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +20 -1
- package/dist/_internal/shared/checkout/rules/types.d.ts +17 -7
- package/dist/_internal/shared/features/categories/bundle-copy.d.ts +2 -0
- package/dist/_internal/shared/features/categories/bundle-copy.js +2 -0
- package/dist/_internal/shared/features/categories/bundle-pricing.d.ts +23 -0
- package/dist/_internal/shared/features/categories/bundle-pricing.js +25 -0
- package/dist/_internal/shared/features/checkout/config.d.ts +7 -0
- package/dist/_internal/shared/features/checkout/config.js +7 -0
- package/dist/_internal/shared/fees/calculator.d.ts +26 -0
- package/dist/_internal/shared/fees/calculator.js +25 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -1
- package/dist/constants/api-endpoints.d.ts +6 -9
- package/dist/constants/api-endpoints.js +5 -5
- package/dist/constants/field-names.d.ts +17 -1
- package/dist/constants/field-names.js +17 -1
- package/dist/features/about/components/FeesView.js +18 -0
- package/dist/features/account/hooks/useNotifications.js +5 -0
- package/dist/features/admin/actions/admin-actions.js +6 -0
- package/dist/features/admin/actions/notification-actions.d.ts +4 -1
- package/dist/features/admin/actions/notification-actions.js +57 -12
- package/dist/features/admin/components/AdminAdsView.js +1 -1
- package/dist/features/admin/components/AdminBlogEditorView.js +1 -1
- package/dist/features/admin/components/AdminNewsletterView.js +42 -11
- package/dist/features/admin/components/AdminPrizeDrawsView.js +3 -3
- package/dist/features/admin/components/AdminSiteSettingsView.js +115 -11
- package/dist/features/admin/components/AdminTesterChecklistView.js +74 -22
- package/dist/features/admin/hooks/useAdminListing.d.ts +4 -0
- package/dist/features/admin/hooks/useAdminListing.js +4 -2
- package/dist/features/admin/repository/analytics-rollup.repository.d.ts +25 -0
- package/dist/features/admin/repository/analytics-rollup.repository.js +31 -0
- package/dist/features/admin/repository/site-settings.repository.js +8 -1
- package/dist/features/admin/schemas/firestore.d.ts +54 -5
- package/dist/features/admin/schemas/firestore.js +63 -26
- package/dist/features/auctions/actions/bid-actions.js +3 -1
- package/dist/features/auctions/components/AuctionBidsTable.d.ts +6 -1
- package/dist/features/auctions/components/AuctionBidsTable.js +8 -4
- package/dist/features/auctions/components/AuctionDetailPageView.js +10 -13
- package/dist/features/auctions/components/CollapsibleBidHistory.d.ts +23 -2
- package/dist/features/auctions/components/CollapsibleBidHistory.js +21 -3
- package/dist/features/auctions/components/LiveBidPrice.d.ts +19 -0
- package/dist/features/auctions/components/LiveBidPrice.js +17 -0
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -1
- package/dist/features/auctions/components/PlaceBidFormClient.js +16 -1
- package/dist/features/auctions/hooks/useBids.d.ts +33 -0
- package/dist/features/auctions/hooks/useBids.js +32 -0
- package/dist/features/auctions/hooks/useLiveAuctionBid.d.ts +20 -0
- package/dist/features/auctions/hooks/useLiveAuctionBid.js +23 -0
- package/dist/features/auth/checkout-value-otp.d.ts +7 -10
- package/dist/features/auth/checkout-value-otp.js +14 -10
- package/dist/features/auth/server.d.ts +0 -1
- package/dist/features/auth/server.js +0 -1
- package/dist/features/blog/api/[slug]/route.d.ts +5 -0
- package/dist/features/blog/api/[slug]/route.js +12 -3
- package/dist/features/blog/components/BlogPostView.d.ts +2 -0
- package/dist/features/blog/components/BlogPostView.js +5 -4
- package/dist/features/blog/hooks/useBlog.d.ts +2 -0
- package/dist/features/blog/hooks/useBlog.js +2 -0
- package/dist/features/blog/repository/blog.repository.d.ts +7 -0
- package/dist/features/blog/repository/blog.repository.js +44 -0
- package/dist/features/blog/schemas/firestore.d.ts +3 -0
- package/dist/features/blog/schemas/firestore.js +1 -0
- package/dist/features/blog/types/index.d.ts +2 -0
- package/dist/features/cart/components/index.d.ts +0 -2
- package/dist/features/cart/components/index.js +0 -1
- package/dist/features/cart/hooks/useCartCount.js +22 -5
- package/dist/features/cart/hooks/useGuestCart.js +11 -1
- package/dist/features/cart/utils/guest-cart.d.ts +4 -0
- package/dist/features/cart/utils/guest-cart.js +10 -0
- package/dist/features/cart/utils/pending-ops.d.ts +17 -0
- package/dist/features/cart/utils/pending-ops.js +46 -5
- package/dist/features/categories/components/BrandDetailPageView.js +6 -2
- package/dist/features/categories/components/BundleDetailView.js +4 -2
- package/dist/features/categories/components/CategoryDetailPageView.js +10 -2
- package/dist/features/categories/schemas/firestore.d.ts +7 -0
- package/dist/features/checkout/actions/index.d.ts +0 -1
- package/dist/features/checkout/actions/index.js +0 -1
- package/dist/features/checkout/schemas/firestore.d.ts +2 -2
- package/dist/features/events/actions/event-actions.d.ts +2 -0
- package/dist/features/events/actions/event-actions.js +5 -0
- package/dist/features/events/components/EventCard.js +1 -2
- package/dist/features/events/components/RelatedEventsCarousel.d.ts +15 -0
- package/dist/features/events/components/RelatedEventsCarousel.js +13 -0
- package/dist/features/events/components/index.d.ts +2 -0
- package/dist/features/events/components/index.js +1 -0
- package/dist/features/events/repository/events.repository.d.ts +6 -0
- package/dist/features/events/repository/events.repository.js +21 -0
- package/dist/features/history/repository/user-history.repository.d.ts +1 -1
- package/dist/features/history/utils/guest-history.d.ts +1 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +4 -2
- package/dist/features/homepage/components/FeaturedProductsSection.js +2 -2
- package/dist/features/homepage/components/SectionCarousel.js +1 -1
- package/dist/features/homepage/components/WelcomeSection.js +1 -1
- package/dist/features/layout/AppLayoutShell.js +1 -1
- package/dist/features/layout/BackToTop.d.ts +9 -5
- package/dist/features/layout/BackToTop.js +17 -21
- package/dist/features/layout/NavbarLayout.js +1 -1
- package/dist/features/layout/TitleBarLayout.js +2 -2
- package/dist/features/media/MediaVideo.d.ts +7 -0
- package/dist/features/media/MediaVideo.js +36 -5
- package/dist/features/orders/actions/refund-actions.js +1 -0
- package/dist/features/orders/components/OrdersList.js +1 -5
- package/dist/features/orders/schemas/firestore.d.ts +16 -8
- package/dist/features/orders/schemas/index.d.ts +3 -32
- package/dist/features/orders/schemas/index.js +1 -4
- package/dist/features/orders/types/index.d.ts +6 -9
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +2 -1
- package/dist/features/products/actions/product-actions.js +13 -10
- package/dist/features/products/components/MarketplaceBundleCard.d.ts +1 -1
- package/dist/features/products/components/MarketplaceBundleCard.js +7 -4
- package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
- package/dist/features/products/components/PrizeDrawItemsEditor.js +2 -2
- package/dist/features/products/components/PrizeDrawWinnerMappingView.d.ts +14 -0
- package/dist/features/products/components/PrizeDrawWinnerMappingView.js +37 -0
- package/dist/features/products/components/PrizeRevealModal.d.ts +10 -23
- package/dist/features/products/components/PrizeRevealModal.js +18 -147
- package/dist/features/products/components/ProductDetailPageView.js +72 -30
- package/dist/features/products/components/ProductForm.js +7 -14
- package/dist/features/products/components/ProductGalleryClient.d.ts +10 -1
- package/dist/features/products/components/ProductGalleryClient.js +29 -10
- package/dist/features/products/components/ProductsIndexListing.js +2 -1
- package/dist/features/products/components/RelatedProductsCarousel.js +2 -2
- package/dist/features/products/components/ShowGroupSection.js +2 -2
- package/dist/features/products/components/SublistingCarouselSection.js +1 -1
- package/dist/features/products/components/index.d.ts +4 -2
- package/dist/features/products/components/index.js +1 -0
- package/dist/features/products/constants/action-defs.d.ts +28 -4
- package/dist/features/products/constants/action-defs.js +76 -3
- package/dist/features/products/repository/products.repository.d.ts +6 -0
- package/dist/features/products/repository/products.repository.js +20 -1
- package/dist/features/products/schemas/firestore.d.ts +26 -2
- package/dist/features/products/schemas/index.d.ts +11 -3
- package/dist/features/products/schemas/index.js +10 -2
- package/dist/features/products/types/index.d.ts +2 -1
- package/dist/features/reviews/actions/review-actions.js +1 -0
- package/dist/features/reviews/components/ReviewDetailPageView.js +8 -1
- package/dist/features/reviews/components/ReviewDetailShell.js +2 -1
- package/dist/features/reviews/components/ReviewsList.js +2 -1
- package/dist/features/reviews/schemas/firestore.d.ts +3 -0
- package/dist/features/reviews/schemas/index.d.ts +6 -0
- package/dist/features/reviews/schemas/index.js +2 -0
- package/dist/features/reviews/types/index.d.ts +3 -0
- package/dist/features/scams/actions/scam-actions.d.ts +3 -0
- package/dist/features/scams/actions/scam-actions.js +3 -2
- package/dist/features/scams/components/ScamProfileView.d.ts +2 -1
- package/dist/features/scams/components/ScamProfileView.js +2 -2
- package/dist/features/scams/repository/scammer.repository.d.ts +6 -0
- package/dist/features/scams/repository/scammer.repository.js +24 -0
- package/dist/features/seller/actions/seller-actions.js +8 -0
- package/dist/features/seller/components/SellerOrdersView.js +1 -1
- package/dist/features/seller/components/SellerPrizeDrawsView.js +4 -0
- package/dist/features/stores/components/StoreDetailLayoutView.js +6 -6
- package/dist/features/stores/components/StoreNavTabs.d.ts +10 -1
- package/dist/features/stores/components/StoreNavTabs.js +21 -9
- package/dist/features/tester/actions/index.d.ts +1 -0
- package/dist/features/tester/actions/index.js +1 -0
- package/dist/features/tester/actions/leaderboard-actions.d.ts +3 -0
- package/dist/features/tester/actions/leaderboard-actions.js +5 -0
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +26 -4
- package/dist/features/tester/components/AdminTesterFeedbackListView.js +13 -0
- package/dist/features/tester/components/BugHunterLeaderboardView.d.ts +23 -0
- package/dist/features/tester/components/BugHunterLeaderboardView.js +18 -0
- package/dist/features/tester/components/index.d.ts +2 -0
- package/dist/features/tester/components/index.js +1 -0
- package/dist/features/tester/repository/tester-checklist-item.repository.d.ts +12 -1
- package/dist/features/tester/repository/tester-checklist-item.repository.js +80 -0
- package/dist/features/tester/repository/tester-checklist-response.repository.d.ts +4 -0
- package/dist/features/tester/repository/tester-checklist-response.repository.js +4 -0
- package/dist/features/tester/schemas/firestore.d.ts +22 -1
- package/dist/features/tester/schemas/firestore.js +8 -0
- package/dist/features/tester/seed-data/categories-tester-seed-data.js +42 -7
- package/dist/features/tester/seed-data/events-tester-seed-data.js +23 -0
- package/dist/features/tester/seed-data/orders-tester-seed-data.js +69 -31
- package/dist/features/tester/seed-data/products-tester-seed-data.js +152 -8
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +249 -14
- package/dist/features/whatsapp-bot/helpers/whatsapp.d.ts +10 -1
- package/dist/features/whatsapp-bot/helpers/whatsapp.js +47 -0
- package/dist/features/whatsapp-bot/types/index.d.ts +19 -0
- package/dist/features/wishlist/hooks/useGuestWishlist.js +11 -1
- package/dist/features/wishlist/hooks/useWishlistCount.js +16 -2
- package/dist/features/wishlist/utils/guest-wishlist.d.ts +5 -0
- package/dist/features/wishlist/utils/guest-wishlist.js +11 -0
- package/dist/index.d.ts +23 -24
- package/dist/index.js +29 -60
- package/dist/jobs.d.ts +1 -1
- package/dist/jobs.js +2 -2
- package/dist/next/routing/route-map.d.ts +4 -0
- package/dist/next/routing/route-map.js +2 -0
- package/dist/repositories/index.d.ts +2 -0
- package/dist/repositories/index.js +1 -0
- package/dist/schemas/registry.d.ts +6 -26
- package/dist/seed/bids-seed-data.js +14 -2
- package/dist/seed/blog-posts-seed-data.js +2 -1
- package/dist/seed/categories-seed-data.js +11 -2
- package/dist/seed/events-seed-data.js +11 -11
- package/dist/seed/faq-seed-data.js +5 -1
- package/dist/seed/products-auctions-seed-data.js +2 -2
- package/dist/seed/products-preorders-seed-data.d.ts +2 -1
- package/dist/seed/products-prize-draws-seed-data.js +56 -10
- package/dist/seed/products-standard-seed-data.js +82 -3
- package/dist/seed/scammers-seed-data.js +75 -2
- package/dist/seed/site-settings-seed-data.js +12 -2
- package/dist/server-entry.d.ts +0 -1
- package/dist/server-entry.js +0 -1
- package/dist/server.d.ts +6 -22
- package/dist/server.js +12 -59
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/BaseListingCard.js +1 -1
- package/dist/ui/components/ImageLightbox.d.ts +9 -0
- package/dist/ui/components/ImageLightbox.js +6 -3
- package/dist/ui/components/ListingToolbar.d.ts +18 -1
- package/dist/ui/components/ListingToolbar.js +5 -2
- package/dist/ui/components/StickyToolbar.js +13 -8
- package/package.json +1 -1
|
@@ -75,7 +75,11 @@ export const PRODUCT_FIELDS = {
|
|
|
75
75
|
PRIZE_REVEAL_STATUS: "prizeRevealStatus",
|
|
76
76
|
PRIZE_REVEAL_WINDOW_START: "prizeRevealWindowStart",
|
|
77
77
|
PRIZE_REVEAL_WINDOW_END: "prizeRevealWindowEnd",
|
|
78
|
-
|
|
78
|
+
PRIZE_DRAW_DURATION_DAYS: "prizeDrawDurationDays",
|
|
79
|
+
PRIZE_REVEAL_MODE: "prizeRevealMode",
|
|
80
|
+
PRIZE_CURRENT_ENTRIES: "prizeCurrentEntries",
|
|
81
|
+
PRIZE_MAX_ENTRIES: "prizeMaxEntries",
|
|
82
|
+
PRIZE_DRAW_MODE: "prizeDrawMode",
|
|
79
83
|
IS_PART_OF_BUNDLE: "isPartOfBundle",
|
|
80
84
|
SEARCH_TOKENS: "searchTokens",
|
|
81
85
|
CREATED_AT: "createdAt",
|
|
@@ -116,6 +120,14 @@ export const PRODUCT_FIELDS = {
|
|
|
116
120
|
OPEN: "open",
|
|
117
121
|
CLOSED: "closed",
|
|
118
122
|
},
|
|
123
|
+
PRIZE_REVEAL_MODE_VALUES: {
|
|
124
|
+
INSTANT: "instant",
|
|
125
|
+
SCHEDULED: "scheduled",
|
|
126
|
+
},
|
|
127
|
+
PRIZE_DRAW_MODE_VALUES: {
|
|
128
|
+
REVEAL: "reveal",
|
|
129
|
+
LOTTERY: "lottery",
|
|
130
|
+
},
|
|
119
131
|
IS_ACTIVE: "isActive",
|
|
120
132
|
DISPLAY_ORDER: "displayOrder",
|
|
121
133
|
LISTING_TYPE_VALUES: {
|
|
@@ -185,6 +197,9 @@ export const ORDER_FIELDS = {
|
|
|
185
197
|
PAYMENT_PROOF_URL: "paymentProofUrl",
|
|
186
198
|
PAYMENT_PROOF_UPLOADED_AT: "paymentProofUploadedAt",
|
|
187
199
|
PAYMENT_REVIEW_OUTCOME: "paymentReviewOutcome",
|
|
200
|
+
PRIZE_WON: "prizeWon",
|
|
201
|
+
PRIZE_DRAW_PRODUCT_ID: "prizeDrawProductId",
|
|
202
|
+
PRIZE_REVEAL_MODE: "prizeRevealMode",
|
|
188
203
|
STATUS_VALUES: {
|
|
189
204
|
PENDING: "pending",
|
|
190
205
|
CONFIRMED: "confirmed",
|
|
@@ -466,6 +481,7 @@ export const CATEGORY_FIELDS = {
|
|
|
466
481
|
BUNDLE_ITEM_DETAILS: "bundleItemDetails",
|
|
467
482
|
BUNDLE_STOCK_STATUS: "bundleStockStatus",
|
|
468
483
|
BUNDLE_QUERY_RESOLVED_AT: "bundleQueryResolvedAt",
|
|
484
|
+
BUNDLE_ORIGINAL_TOTAL: "bundleOriginalTotal",
|
|
469
485
|
DISPLAY: "display",
|
|
470
486
|
VIEW_COUNT: "viewCount",
|
|
471
487
|
CREATED_AT: "createdAt",
|
|
@@ -65,6 +65,24 @@ export async function FeesView({} = {}) {
|
|
|
65
65
|
who: t("paidByBuyer"),
|
|
66
66
|
note: t("codHandlingFeeNote"),
|
|
67
67
|
},
|
|
68
|
+
{
|
|
69
|
+
category: t("whatsappNotifyFeeTitle"),
|
|
70
|
+
rate: t("whatsappNotifyFeeRate"),
|
|
71
|
+
who: t("paidByBuyer"),
|
|
72
|
+
note: t("whatsappNotifyFeeNote"),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
category: t("giftWrapFeeTitle"),
|
|
76
|
+
rate: t("giftWrapFeeRate"),
|
|
77
|
+
who: t("paidByBuyer"),
|
|
78
|
+
note: t("giftWrapFeeNote"),
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
category: t("shipmentProtectionFeeTitle"),
|
|
82
|
+
rate: t("shipmentProtectionFeeRate"),
|
|
83
|
+
who: t("paidByBuyer"),
|
|
84
|
+
note: t("shipmentProtectionFeeNote"),
|
|
85
|
+
},
|
|
68
86
|
];
|
|
69
87
|
const OFFER_PAYOUT_ROWS = [
|
|
70
88
|
{ label: t("grossSale"), example: "₹1,000" },
|
|
@@ -9,6 +9,11 @@ export function useNotifications(limit = 10, options) {
|
|
|
9
9
|
queryKey: ["notifications", "list", String(limit)],
|
|
10
10
|
queryFn: () => apiClient.get(`${notificationsEndpoint}?limit=${limit}`),
|
|
11
11
|
staleTime: 30000,
|
|
12
|
+
// Notifications originate server-side (order/bid/moderation events) with
|
|
13
|
+
// no client-driven local-first path to short-circuit, unlike cart/
|
|
14
|
+
// wishlist. Poll on the same ~30s cadence as useSyncManager so the bell
|
|
15
|
+
// badge picks up new notifications without requiring a window refocus.
|
|
16
|
+
refetchInterval: 30000,
|
|
12
17
|
});
|
|
13
18
|
const { mutate: markRead } = useMutation({
|
|
14
19
|
mutationFn: (id) => apiClient.patch(readEndpoint(id), {}),
|
|
@@ -14,6 +14,7 @@ import { productRepository } from "../../products/repository/products.repository
|
|
|
14
14
|
import { NotFoundError, ValidationError } from "../../../errors";
|
|
15
15
|
import { finalizeStagedMediaUrl, finalizeStagedMediaField, finalizeStagedMediaArray, } from "../../media/finalize";
|
|
16
16
|
import { getProviders } from "../../../contracts";
|
|
17
|
+
import { assertPrizeDrawNotLocked, assertPrizeDrawWonItemsImmutable, } from "../../../_internal/server/features/products/service";
|
|
17
18
|
const ERR_UID_REQUIRED = "uid is required";
|
|
18
19
|
export async function revokeSession(adminId, sessionId) {
|
|
19
20
|
if (!sessionId?.trim()) {
|
|
@@ -153,6 +154,10 @@ export async function adminUpdateProduct(adminId, id, input) {
|
|
|
153
154
|
if (!existing) {
|
|
154
155
|
throw new NotFoundError("Product not found");
|
|
155
156
|
}
|
|
157
|
+
if (input.status && input.status !== "published" && existing.status === "published") {
|
|
158
|
+
assertPrizeDrawNotLocked(existing, "unpublished or archived");
|
|
159
|
+
}
|
|
160
|
+
assertPrizeDrawWonItemsImmutable(existing, input.prizeDrawItems);
|
|
156
161
|
const finalized = { ...input };
|
|
157
162
|
if (typeof finalized.mainImage === "string" && finalized.mainImage) {
|
|
158
163
|
finalized.mainImage = await finalizeStagedMediaUrl(finalized.mainImage);
|
|
@@ -194,6 +199,7 @@ export async function adminDeleteProduct(adminId, id) {
|
|
|
194
199
|
if (!existing) {
|
|
195
200
|
throw new NotFoundError("Product not found");
|
|
196
201
|
}
|
|
202
|
+
assertPrizeDrawNotLocked(existing, "deleted");
|
|
197
203
|
await productRepository.delete(id);
|
|
198
204
|
serverLogger.info("adminDeleteProduct", {
|
|
199
205
|
adminId,
|
|
@@ -14,11 +14,14 @@ export interface SendNotificationInput extends NotificationCreateInput {
|
|
|
14
14
|
userPhone?: string;
|
|
15
15
|
/** Pre-rendered email HTML — falls back to a plain-text <p> when absent. */
|
|
16
16
|
emailHtml?: string;
|
|
17
|
+
/** Buyer paid the ₹10 WhatsApp order-updates addon — enables WhatsApp channel for this notification even when the user has no standing WhatsApp subscription. */
|
|
18
|
+
orderWhatsappAddonPaid?: boolean;
|
|
17
19
|
}
|
|
18
20
|
export interface SendNotificationResult {
|
|
19
21
|
notification: NotificationDocument;
|
|
20
22
|
email: "sent" | "skipped" | "failed";
|
|
21
|
-
|
|
23
|
+
/** WhatsApp dispatch is now async (enqueued as a `whatsappNotify` job) — "queued" replaces "sent" as the immediate outcome; the real delivery result lands on `notification.whatsappStatus`. */
|
|
24
|
+
whatsapp: "queued" | "skipped" | "failed";
|
|
22
25
|
}
|
|
23
26
|
/**
|
|
24
27
|
* Create an in-app notification and fan out to enabled external channels.
|
|
@@ -3,10 +3,39 @@ import { notificationRepository } from "../repository/notification.repository";
|
|
|
3
3
|
import { siteSettingsRepository } from "../repository/site-settings.repository";
|
|
4
4
|
import { userRepository } from "../../auth/repository/user.repository";
|
|
5
5
|
import { createResendProvider } from "../../../providers/email-resend/provider";
|
|
6
|
-
import {
|
|
6
|
+
import { enqueueJob } from "../../jobs/actions/enqueue-job";
|
|
7
7
|
import { serverLogger } from "../../../monitoring";
|
|
8
8
|
import { decryptPii } from "../../../security/index";
|
|
9
9
|
import { DEFAULT_NOTIFICATION_CHANNELS, meetsMinPriority, } from "../schemas/firestore";
|
|
10
|
+
/**
|
|
11
|
+
* Notification types whose WhatsApp send is gated behind the buyer's
|
|
12
|
+
* per-order ₹10 WhatsApp-updates addon (`OrderDocument.whatsappNotifyAddon`)
|
|
13
|
+
* — see `orderWhatsappAddonPaid` on `SendNotificationInput`. Every other
|
|
14
|
+
* type (bid_won, offers, scam reports, support tickets, admin alerts, …)
|
|
15
|
+
* keeps the original two-gate behavior (admin channel enabled + user hasn't
|
|
16
|
+
* opted out) unchanged.
|
|
17
|
+
*/
|
|
18
|
+
const ORDER_GATED_TYPES = new Set([
|
|
19
|
+
"order_placed",
|
|
20
|
+
"order_confirmed",
|
|
21
|
+
"order_shipped",
|
|
22
|
+
"order_delivered",
|
|
23
|
+
"order_cancelled",
|
|
24
|
+
"refund_initiated",
|
|
25
|
+
]);
|
|
26
|
+
/** Notification type → the flat `SiteSettingsCredentials` field holding its approved Meta template name. */
|
|
27
|
+
const WHATSAPP_TEMPLATE_FIELD = {
|
|
28
|
+
order_placed: "whatsappTemplateOrderPlaced",
|
|
29
|
+
order_confirmed: "whatsappTemplateOrderConfirmed",
|
|
30
|
+
order_shipped: "whatsappTemplateOrderShipped",
|
|
31
|
+
order_delivered: "whatsappTemplateOrderDelivered",
|
|
32
|
+
order_cancelled: "whatsappTemplateOrderCancelled",
|
|
33
|
+
refund_initiated: "whatsappTemplateRefundInitiated",
|
|
34
|
+
};
|
|
35
|
+
function resolveWhatsAppTemplateName(type, creds) {
|
|
36
|
+
const field = WHATSAPP_TEMPLATE_FIELD[type];
|
|
37
|
+
return (field ? creds[field] : undefined)?.trim() ?? "";
|
|
38
|
+
}
|
|
10
39
|
export async function markNotificationRead(id) {
|
|
11
40
|
await notificationRepository.markAsRead(id);
|
|
12
41
|
}
|
|
@@ -84,9 +113,8 @@ export async function sendNotification(input) {
|
|
|
84
113
|
offer_expired: "offers", offer_counter_accepted: "offers",
|
|
85
114
|
refund_initiated: "orderUpdates",
|
|
86
115
|
product_available: "system",
|
|
87
|
-
|
|
116
|
+
prize_won: "orderUpdates",
|
|
88
117
|
prize_reveal_expired: "orderUpdates",
|
|
89
|
-
prize_reveal_reminder: "orderUpdates",
|
|
90
118
|
};
|
|
91
119
|
const typeKey = typeToPrefsKey[type];
|
|
92
120
|
// If the user has explicitly disabled this notification type, skip all external channels.
|
|
@@ -121,28 +149,45 @@ export async function sendNotification(input) {
|
|
|
121
149
|
}
|
|
122
150
|
}
|
|
123
151
|
}
|
|
124
|
-
// WhatsApp channel — admin enabled AND user hasn't opted out
|
|
152
|
+
// WhatsApp channel — admin enabled AND user hasn't opted out AND (for
|
|
153
|
+
// order-lifecycle types) the buyer paid the per-order WhatsApp addon.
|
|
125
154
|
let whatsappStatus = "skipped";
|
|
126
155
|
if (channels.whatsapp.enabled &&
|
|
127
156
|
userChannelPrefs.whatsapp !== false &&
|
|
128
157
|
resolvedPhone &&
|
|
129
158
|
meetsMinPriority(priority, channels.whatsapp.minPriority) &&
|
|
130
|
-
(!channels.whatsapp.types?.length || channels.whatsapp.types.includes(type))
|
|
159
|
+
(!channels.whatsapp.types?.length || channels.whatsapp.types.includes(type)) &&
|
|
160
|
+
(!ORDER_GATED_TYPES.has(type) || input.orderWhatsappAddonPaid === true)) {
|
|
131
161
|
const phoneNumberId = creds.whatsappPhoneNumberId?.trim() ?? "";
|
|
132
162
|
const accessToken = creds.whatsappCloudApiToken?.trim() ?? "";
|
|
133
163
|
if (phoneNumberId && accessToken) {
|
|
134
164
|
try {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
165
|
+
const { jobId } = await enqueueJob({
|
|
166
|
+
jobType: "whatsappNotify",
|
|
167
|
+
payload: {
|
|
168
|
+
toPhone: resolvedPhone,
|
|
169
|
+
title,
|
|
170
|
+
message,
|
|
171
|
+
type,
|
|
172
|
+
templateName: resolveWhatsAppTemplateName(type, creds),
|
|
173
|
+
templateLanguage: creds.whatsappTemplateLanguage ?? "en",
|
|
174
|
+
notificationId: notification.id,
|
|
175
|
+
phoneNumberId,
|
|
176
|
+
accessToken,
|
|
177
|
+
},
|
|
178
|
+
requestedBy: input.userId,
|
|
179
|
+
});
|
|
180
|
+
whatsappStatus = "queued";
|
|
181
|
+
await notificationRepository
|
|
182
|
+
.update(notification.id, { whatsappStatus: "queued", whatsappJobId: jobId })
|
|
183
|
+
.catch((err) => {
|
|
184
|
+
void normalizeError(err);
|
|
185
|
+
serverLogger.warn("sendNotification: failed to write whatsappJobId onto notification doc", { notificationId: notification.id });
|
|
140
186
|
});
|
|
141
|
-
whatsappStatus = ok ? "sent" : "failed";
|
|
142
187
|
}
|
|
143
188
|
catch (err) {
|
|
144
189
|
void normalizeError(err);
|
|
145
|
-
serverLogger.error("sendNotification: WhatsApp
|
|
190
|
+
serverLogger.error("sendNotification: WhatsApp job enqueue failed", { userId: input.userId, err });
|
|
146
191
|
whatsappStatus = "failed";
|
|
147
192
|
}
|
|
148
193
|
}
|
|
@@ -156,7 +156,7 @@ export function AdminAdsView({ endpoint = ADMIN_ENDPOINTS.ADS, labels = {}, crea
|
|
|
156
156
|
settingsMutation.mutate(payload);
|
|
157
157
|
};
|
|
158
158
|
return (_jsx(StackedViewShell, { portal: "admin", ...rest, title: labels.title ?? "Ad Inventory", sections: [
|
|
159
|
-
_jsxs(Row, { align: "center", justify: "between", gap: "3", children: [_jsx(Text, { variant: "secondary", children: "Manage ad inventory, placement mapping, and publishing state." }), _jsx(TextLink, { variant: "bare", href: createHref, rounded: "md", paddingX: "sm", size: "sm", weight: "medium", layout: "inline-flex", align: "center", className: "h-9 bg-
|
|
159
|
+
_jsxs(Row, { align: "center", justify: "between", gap: "3", children: [_jsx(Text, { variant: "secondary", children: "Manage ad inventory, placement mapping, and publishing state." }), _jsx(TextLink, { variant: "bare", href: createHref, rounded: "md", paddingX: "sm", size: "sm", weight: "medium", layout: "inline-flex", align: "center", className: "h-9 bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text)]", children: "New ad" })] }),
|
|
160
160
|
adsQuery.error ? (_jsx(Alert, { variant: "error", title: "Could not load ads", children: adsQuery.error instanceof Error ? adsQuery.error.message : "Unknown error" })) : null,
|
|
161
161
|
_jsx(AdsSettingsPanel, { adsenseClientId: adsenseClientId, setAdsenseClientId: setAdsenseClientId, thirdPartyScriptUrl: thirdPartyScriptUrl, setThirdPartyScriptUrl: setThirdPartyScriptUrl, consentRequired: consentRequired, setConsentRequired: setConsentRequired, serverCredentialIssues: serverCredentialIssues, localCredentialIssues: localCredentialIssues, credentialStatus: credentialStatus, providerCredentialsMasked: adsQuery.data?.providerCredentialsMasked, settingsMutation: settingsMutation, hasPendingCredentialInput: hasPendingCredentialInput, currentConsentRequired: Boolean(adsQuery.data?.consentRequired), settingsMessage: settingsMessage, onSave: saveSettings }),
|
|
162
162
|
_jsx(AdsFilterRow, { q: q, setQ: setQ, status: status, setStatus: setStatus, provider: provider, setProvider: setProvider, placement: placement, setPlacement: setPlacement, placements: placements, onPageReset: () => setPage(1) }),
|
|
@@ -114,7 +114,7 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
|
|
|
114
114
|
slug: draft.slug || toSlug(draft.title),
|
|
115
115
|
excerpt: draft.excerpt,
|
|
116
116
|
content: draft.content,
|
|
117
|
-
coverImage: draft.coverImage
|
|
117
|
+
coverImage: draft.coverImage ? { type: "image", url: draft.coverImage } : undefined,
|
|
118
118
|
youtubeId: draft.youtubeId || undefined,
|
|
119
119
|
category: draft.category,
|
|
120
120
|
tags: draft.tags,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
|
-
import { useApiMutation } from "@mohasinac/appkit/client";
|
|
4
|
+
import { useApiMutation, useBulkEvent, RTDB_PATHS } from "@mohasinac/appkit/client";
|
|
5
5
|
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
6
6
|
import { sortBy } from "@mohasinac/appkit";
|
|
7
|
-
import React, { useCallback, useState } from "react";
|
|
7
|
+
import React, { useCallback, useEffect, useState } from "react";
|
|
8
8
|
import { useQueryClient } from "@tanstack/react-query";
|
|
9
9
|
import { Button, ConfirmDeleteModal, FilterChipGroup, ListingLayout, RowActionMenu, useToast } from "../../../ui";
|
|
10
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
@@ -33,24 +33,55 @@ export function AdminNewsletterView({ children, onBulkUnsubscribe, ...props }) {
|
|
|
33
33
|
showToast(err?.message ?? "Failed to unsubscribe.", "error");
|
|
34
34
|
},
|
|
35
35
|
});
|
|
36
|
+
// Export runs as an async `newsletterExport` job (Async Job Primitive) —
|
|
37
|
+
// the route only enqueues; this subscribes to the same RTDB bulk-event
|
|
38
|
+
// channel every other async job in the admin panel uses.
|
|
39
|
+
const [exporting, setExporting] = useState(false);
|
|
40
|
+
const exportEvent = useBulkEvent({ rtdbPath: RTDB_PATHS.BULK_EVENTS });
|
|
36
41
|
const handleExportCsv = useCallback(async () => {
|
|
42
|
+
setExporting(true);
|
|
37
43
|
try {
|
|
38
44
|
const response = await fetch(ADMIN_ENDPOINTS.NEWSLETTER_EXPORT);
|
|
39
45
|
if (!response.ok)
|
|
40
46
|
throw new Error("Export failed");
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
const body = (await response.json());
|
|
48
|
+
const { jobId, customToken } = body.data ?? {};
|
|
49
|
+
if (jobId && customToken) {
|
|
50
|
+
exportEvent.subscribe(jobId, customToken);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
throw new Error("Export failed to start");
|
|
54
|
+
}
|
|
48
55
|
}
|
|
49
56
|
catch (_err) {
|
|
50
57
|
void normalizeError(_err);
|
|
58
|
+
setExporting(false);
|
|
51
59
|
showToast("Failed to export CSV.", "error");
|
|
52
60
|
}
|
|
53
|
-
}, [showToast]);
|
|
61
|
+
}, [showToast, exportEvent]);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (exportEvent.status === "success") {
|
|
64
|
+
setExporting(false);
|
|
65
|
+
const csv = exportEvent.result?.data?.csv;
|
|
66
|
+
if (csv) {
|
|
67
|
+
const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
|
|
68
|
+
const url = URL.createObjectURL(blob);
|
|
69
|
+
const a = document.createElement("a");
|
|
70
|
+
a.href = url;
|
|
71
|
+
a.download = `newsletter-subscribers-${new Date().toISOString().slice(0, 10)}.csv`;
|
|
72
|
+
a.click();
|
|
73
|
+
URL.revokeObjectURL(url);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
showToast("Export finished with no data.", "error");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else if (exportEvent.status === "failed" || exportEvent.status === "timeout") {
|
|
80
|
+
setExporting(false);
|
|
81
|
+
showToast(exportEvent.error ?? "Failed to export CSV.", "error");
|
|
82
|
+
}
|
|
83
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
84
|
+
}, [exportEvent.status]);
|
|
54
85
|
if (React.Children.count(children) > 0) {
|
|
55
86
|
return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
|
|
56
87
|
}
|
|
@@ -85,7 +116,7 @@ export function AdminNewsletterView({ children, onBulkUnsubscribe, ...props }) {
|
|
|
85
116
|
return mappedRows.length;
|
|
86
117
|
},
|
|
87
118
|
buildFilters: (state) => state.status && state.status !== "All" ? sieveFilter("status", SIEVE_OP.EQ, state.status) : undefined,
|
|
88
|
-
toolbarExtra: (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleExportCsv, children: ACTIONS.ADMIN["export-csv"].label })),
|
|
119
|
+
toolbarExtra: (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleExportCsv, isLoading: exporting, disabled: exporting, children: ACTIONS.ADMIN["export-csv"].label })),
|
|
89
120
|
buildBulkActions: onBulkUnsubscribe
|
|
90
121
|
? (selection) => [
|
|
91
122
|
buildBulkAction(ACTIONS.ADMIN["unsubscribe-newsletter"], async () => {
|
|
@@ -3,8 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
|
|
4
4
|
import { sortBy } from "@mohasinac/appkit";
|
|
5
5
|
import React from "react";
|
|
6
|
-
import { Pencil } from "lucide-react";
|
|
7
|
-
import { Badge, Button, FilterChipGroup, ListingLayout, Span, Text, TextLink } from "../../../ui";
|
|
6
|
+
import { Pencil, ListChecks } from "lucide-react";
|
|
7
|
+
import { Badge, Button, FilterChipGroup, ListingLayout, Row, Span, Text, TextLink } from "../../../ui";
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
9
|
import { ADMIN_BULK_ACTIONS, ROW_ACTION_META } from "../../products/constants/action-defs";
|
|
10
10
|
import { ADMIN_PRODUCT_STATUS_TABS } from "../constants/filter-tabs";
|
|
@@ -101,7 +101,7 @@ export function AdminPrizeDrawsView({ children, ...props }) {
|
|
|
101
101
|
destructive: ROW_ACTION_META[id].destructive,
|
|
102
102
|
onClick: () => selection.clearSelection(),
|
|
103
103
|
})),
|
|
104
|
-
renderRowActions: (row) => (_jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: _jsx(TextLink, { href: String(ROUTES.ADMIN.PRIZE_DRAWS_EDIT(row.id)), "aria-label": "Edit", children: _jsx(Pencil, { className: "w-4 h-4" }) }) })),
|
|
104
|
+
renderRowActions: (row) => (_jsxs(Row, { gap: "xs", children: [_jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: _jsx(TextLink, { href: String(ROUTES.ADMIN.PRIZE_DRAWS_EDIT(row.id)), "aria-label": "Edit", children: _jsx(Pencil, { className: "w-4 h-4" }) }) }), _jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: _jsx(TextLink, { href: String(ROUTES.ADMIN.PRIZE_DRAWS_ENTRIES(row.id)), "aria-label": "View entries", children: _jsx(ListChecks, { className: "w-4 h-4" }) }) })] })),
|
|
105
105
|
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_PRODUCT_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) })),
|
|
106
106
|
};
|
|
107
107
|
return _jsx(DataListingView, { config: config });
|