@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
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Rollup Repository
|
|
3
|
+
*
|
|
4
|
+
* Singleton `analytics/dashboardRollup` doc — a pre-computed aggregate that
|
|
5
|
+
* replaces an unbounded `orderRepository.findByStatus("delivered")` scan on
|
|
6
|
+
* every admin dashboard page load. Written daily by the `revenueRollup`
|
|
7
|
+
* scheduled Firebase Function (`appkit/src/_internal/server/jobs/core/revenueRollup.ts`),
|
|
8
|
+
* read by `GET /api/admin/dashboard`.
|
|
9
|
+
*/
|
|
10
|
+
import { BaseRepository } from "../../../providers/db-firebase";
|
|
11
|
+
export interface AnalyticsRollupDocument {
|
|
12
|
+
id: string;
|
|
13
|
+
totalRevenue: number;
|
|
14
|
+
deliveredOrderCount: number;
|
|
15
|
+
computedAt: Date;
|
|
16
|
+
}
|
|
17
|
+
export declare class AnalyticsRollupRepository extends BaseRepository<AnalyticsRollupDocument> {
|
|
18
|
+
constructor();
|
|
19
|
+
getDashboardRollup(): Promise<AnalyticsRollupDocument | null>;
|
|
20
|
+
setDashboardRollup(data: {
|
|
21
|
+
totalRevenue: number;
|
|
22
|
+
deliveredOrderCount: number;
|
|
23
|
+
}): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export declare const analyticsRollupRepository: AnalyticsRollupRepository;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Rollup Repository
|
|
3
|
+
*
|
|
4
|
+
* Singleton `analytics/dashboardRollup` doc — a pre-computed aggregate that
|
|
5
|
+
* replaces an unbounded `orderRepository.findByStatus("delivered")` scan on
|
|
6
|
+
* every admin dashboard page load. Written daily by the `revenueRollup`
|
|
7
|
+
* scheduled Firebase Function (`appkit/src/_internal/server/jobs/core/revenueRollup.ts`),
|
|
8
|
+
* read by `GET /api/admin/dashboard`.
|
|
9
|
+
*/
|
|
10
|
+
import { BaseRepository } from "../../../providers/db-firebase";
|
|
11
|
+
const ANALYTICS_COLLECTION = "analytics";
|
|
12
|
+
const DASHBOARD_ROLLUP_ID = "dashboardRollup";
|
|
13
|
+
export class AnalyticsRollupRepository extends BaseRepository {
|
|
14
|
+
constructor() {
|
|
15
|
+
super(ANALYTICS_COLLECTION);
|
|
16
|
+
}
|
|
17
|
+
async getDashboardRollup() {
|
|
18
|
+
return this.findById(DASHBOARD_ROLLUP_ID);
|
|
19
|
+
}
|
|
20
|
+
async setDashboardRollup(data) {
|
|
21
|
+
const existing = await this.findById(DASHBOARD_ROLLUP_ID);
|
|
22
|
+
const payload = { ...data, computedAt: new Date() };
|
|
23
|
+
if (existing) {
|
|
24
|
+
await this.update(DASHBOARD_ROLLUP_ID, payload);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
await this.createWithId(DASHBOARD_ROLLUP_ID, payload);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export const analyticsRollupRepository = new AnalyticsRollupRepository();
|
|
@@ -72,9 +72,16 @@ export class SiteSettingsRepository extends BaseRepository {
|
|
|
72
72
|
mergeEncryptedCredentials(existing, updates) {
|
|
73
73
|
const encrypted = { ...existing };
|
|
74
74
|
for (const [key, value] of Object.entries(updates)) {
|
|
75
|
-
if (value && value.trim()) {
|
|
75
|
+
if (typeof value === "string" && value.trim()) {
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
77
|
encrypted[key] = encryptSecret(value.trim());
|
|
77
78
|
}
|
|
79
|
+
else if (value !== undefined && typeof value !== "string") {
|
|
80
|
+
// Non-string credential fields (e.g. whatsappTemplates: Record<string,string>)
|
|
81
|
+
// are stored as-is — they are not secret values to encrypt.
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
83
|
+
encrypted[key] = value;
|
|
84
|
+
}
|
|
78
85
|
// Empty / undefined → keep whatever was already stored
|
|
79
86
|
}
|
|
80
87
|
return encrypted;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { AboutContentDocument } from "../../about/schemas/firestore";
|
|
6
6
|
export declare const ADMIN_CHECKOUT_BYPASS_FLAG_KEY: "adminCheckoutBypass";
|
|
7
|
-
export type NotificationType = "order_placed" | "order_confirmed" | "order_shipped" | "order_delivered" | "order_cancelled" | "bid_placed" | "bid_outbid" | "bid_won" | "bid_lost" | "review_approved" | "review_replied" | "product_available" | "promotion" | "system" | "welcome" | "account_action" | "offer_received" | "offer_responded" | "offer_expired" | "offer_counter_accepted" | "refund_initiated" | "
|
|
7
|
+
export type NotificationType = "order_placed" | "order_confirmed" | "order_shipped" | "order_delivered" | "order_cancelled" | "bid_placed" | "bid_outbid" | "bid_won" | "bid_lost" | "review_approved" | "review_replied" | "product_available" | "promotion" | "system" | "welcome" | "account_action" | "offer_received" | "offer_responded" | "offer_expired" | "offer_counter_accepted" | "refund_initiated" | "prize_won" | "prize_reveal_expired" | "emi_installment_due_soon" | "emi_installment_overdue" | "payment_review";
|
|
8
8
|
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
9
9
|
export type NotificationPriority = "low" | "normal" | "high";
|
|
10
10
|
export interface NotificationDocument extends BaseDocument {
|
|
@@ -20,6 +20,10 @@ export interface NotificationDocument extends BaseDocument {
|
|
|
20
20
|
readAt?: Date;
|
|
21
21
|
relatedId?: string;
|
|
22
22
|
relatedType?: "order" | "product" | "bid" | "review" | "blog" | "user" | "offer" | "support_ticket" | "scammer";
|
|
23
|
+
/** Outcome of the async whatsappNotify job for this notification's WhatsApp send, if one was enqueued. */
|
|
24
|
+
whatsappStatus?: "queued" | "sent" | "failed" | "skipped";
|
|
25
|
+
/** jobs/{id} doc that ran (or is running) the WhatsApp send for this notification. */
|
|
26
|
+
whatsappJobId?: string;
|
|
23
27
|
}
|
|
24
28
|
export declare const NOTIFICATIONS_COLLECTION: "notifications";
|
|
25
29
|
export declare const NOTIFICATIONS_INDEXED_FIELDS: readonly ["userId", "isRead", "createdAt", "type"];
|
|
@@ -61,9 +65,8 @@ export declare const NOTIFICATION_FIELDS: {
|
|
|
61
65
|
readonly OFFER_COUNTER_ACCEPTED: NotificationType;
|
|
62
66
|
readonly REFUND_INITIATED: NotificationType;
|
|
63
67
|
readonly ACCOUNT_ACTION: NotificationType;
|
|
64
|
-
readonly
|
|
68
|
+
readonly PRIZE_WON: NotificationType;
|
|
65
69
|
readonly PRIZE_REVEAL_EXPIRED: NotificationType;
|
|
66
|
-
readonly PRIZE_REVEAL_REMINDER: NotificationType;
|
|
67
70
|
};
|
|
68
71
|
readonly PRIORITY_VALUES: {
|
|
69
72
|
readonly LOW: NotificationPriority;
|
|
@@ -163,6 +166,23 @@ export interface SiteSettingsCredentials {
|
|
|
163
166
|
whatsappCloudApiToken?: string;
|
|
164
167
|
/** Comma-separated digits-only phone numbers that receive order announcements */
|
|
165
168
|
whatsappAdminNotifyNumbers?: string;
|
|
169
|
+
/**
|
|
170
|
+
* Approved Meta message-template names, one flat string field per
|
|
171
|
+
* order-lifecycle notification type — every `SiteSettingsCredentials`
|
|
172
|
+
* field is stored as an individually-encrypted string (see
|
|
173
|
+
* `getDecryptedCredentials`/`getCredentialsMasked`, which iterate + mask
|
|
174
|
+
* every field as a string), so this can't be a `Record<string,string>`
|
|
175
|
+
* map. Business-initiated WhatsApp sends outside the 24h customer-service
|
|
176
|
+
* window require a pre-approved template.
|
|
177
|
+
*/
|
|
178
|
+
whatsappTemplateOrderPlaced?: string;
|
|
179
|
+
whatsappTemplateOrderConfirmed?: string;
|
|
180
|
+
whatsappTemplateOrderShipped?: string;
|
|
181
|
+
whatsappTemplateOrderDelivered?: string;
|
|
182
|
+
whatsappTemplateOrderCancelled?: string;
|
|
183
|
+
whatsappTemplateRefundInitiated?: string;
|
|
184
|
+
/** BCP-47 language code the templates above were approved in, e.g. "en" or "en_US". */
|
|
185
|
+
whatsappTemplateLanguage?: string;
|
|
166
186
|
}
|
|
167
187
|
export interface SiteSettingsCredentialsMasked {
|
|
168
188
|
razorpayKeyId?: string;
|
|
@@ -184,6 +204,13 @@ export interface SiteSettingsCredentialsMasked {
|
|
|
184
204
|
whatsappPhoneNumberId?: string;
|
|
185
205
|
whatsappCloudApiToken?: string;
|
|
186
206
|
whatsappAdminNotifyNumbers?: string;
|
|
207
|
+
whatsappTemplateOrderPlaced?: string;
|
|
208
|
+
whatsappTemplateOrderConfirmed?: string;
|
|
209
|
+
whatsappTemplateOrderShipped?: string;
|
|
210
|
+
whatsappTemplateOrderDelivered?: string;
|
|
211
|
+
whatsappTemplateOrderCancelled?: string;
|
|
212
|
+
whatsappTemplateRefundInitiated?: string;
|
|
213
|
+
whatsappTemplateLanguage?: string;
|
|
187
214
|
}
|
|
188
215
|
export interface NotificationChannelConfig {
|
|
189
216
|
inApp: {
|
|
@@ -320,6 +347,19 @@ export interface SiteSettingsDocument extends BaseDocument {
|
|
|
320
347
|
/** COD handling fee charged to the buyer: max(codHandlingFeeMin, subtotal × codHandlingFeePercent / 100). */
|
|
321
348
|
codHandlingFeeMin: number;
|
|
322
349
|
codHandlingFeePercent: number;
|
|
350
|
+
/** Admin master toggle — whether the WhatsApp order-updates addon is offered at checkout at all. */
|
|
351
|
+
whatsappNotifyFeeEnabled: boolean;
|
|
352
|
+
/** Flat rupee fee charged to the buyer when they opt into the WhatsApp order-updates addon. */
|
|
353
|
+
whatsappNotifyFee: number;
|
|
354
|
+
/** Admin master toggle — whether the gift-wrap addon is offered at checkout at all. */
|
|
355
|
+
giftWrapFeeEnabled: boolean;
|
|
356
|
+
/** Flat rupee fee charged to the buyer when they opt into gift wrap. */
|
|
357
|
+
giftWrapFee: number;
|
|
358
|
+
/** Admin master toggle — whether the shipment-protection addon is offered at checkout at all. */
|
|
359
|
+
shipmentProtectionFeeEnabled: boolean;
|
|
360
|
+
/** Shipment protection fee: max(shipmentProtectionFeeMin, subtotal × shipmentProtectionFeePercent / 100). */
|
|
361
|
+
shipmentProtectionFeePercent: number;
|
|
362
|
+
shipmentProtectionFeeMin: number;
|
|
323
363
|
sellerShippingFixed: number;
|
|
324
364
|
platformShippingPercent: number;
|
|
325
365
|
platformShippingFixedMin: number;
|
|
@@ -448,7 +488,7 @@ export interface SiteSettingsDocument extends BaseDocument {
|
|
|
448
488
|
/**
|
|
449
489
|
* Image watermark configuration applied by the `/api/media/[...slug]` CDN
|
|
450
490
|
* proxy. When absent the proxy falls back to text watermark "letitrip.in" at
|
|
451
|
-
*
|
|
491
|
+
* 10% width with 10% opacity, centered.
|
|
452
492
|
*/
|
|
453
493
|
watermark?: {
|
|
454
494
|
/** `"text"` renders the `text` field; `"image"` overlays `imageUrl`. */
|
|
@@ -463,8 +503,17 @@ export interface SiteSettingsDocument extends BaseDocument {
|
|
|
463
503
|
imageUrl?: string;
|
|
464
504
|
/** Percentage of target image width (0–100). 0 disables the watermark. */
|
|
465
505
|
size?: number;
|
|
466
|
-
/** Percentage opacity (0–100). Default
|
|
506
|
+
/** Percentage opacity (0–100). Default 10. */
|
|
467
507
|
opacity?: number;
|
|
508
|
+
/**
|
|
509
|
+
* Anchor preset: 4 corners + center, or `"custom"` to use `offsetX`/
|
|
510
|
+
* `offsetY` instead. Default `"center"`.
|
|
511
|
+
*/
|
|
512
|
+
position?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "center" | "custom";
|
|
513
|
+
/** `position: "custom"` only — % of image width from center. +right / -left. Default 0. */
|
|
514
|
+
offsetX?: number;
|
|
515
|
+
/** `position: "custom"` only — % of image height from center. +down / -up. Default 0. */
|
|
516
|
+
offsetY?: number;
|
|
468
517
|
};
|
|
469
518
|
/** Encrypted provider credentials � never return raw to the client. */
|
|
470
519
|
credentials?: SiteSettingsCredentials;
|
|
@@ -52,9 +52,8 @@ export const NOTIFICATION_FIELDS = {
|
|
|
52
52
|
OFFER_COUNTER_ACCEPTED: "offer_counter_accepted",
|
|
53
53
|
REFUND_INITIATED: "refund_initiated",
|
|
54
54
|
ACCOUNT_ACTION: "account_action",
|
|
55
|
-
|
|
55
|
+
PRIZE_WON: "prize_won",
|
|
56
56
|
PRIZE_REVEAL_EXPIRED: "prize_reveal_expired",
|
|
57
|
-
PRIZE_REVEAL_REMINDER: "prize_reveal_reminder",
|
|
58
57
|
},
|
|
59
58
|
PRIORITY_VALUES: {
|
|
60
59
|
LOW: "low",
|
|
@@ -189,6 +188,13 @@ export const DEFAULT_SITE_SETTINGS_DATA = {
|
|
|
189
188
|
codDepositPercent: 10,
|
|
190
189
|
codHandlingFeeMin: 200,
|
|
191
190
|
codHandlingFeePercent: 10,
|
|
191
|
+
whatsappNotifyFeeEnabled: false,
|
|
192
|
+
whatsappNotifyFee: 10,
|
|
193
|
+
giftWrapFeeEnabled: false,
|
|
194
|
+
giftWrapFee: 49,
|
|
195
|
+
shipmentProtectionFeeEnabled: false,
|
|
196
|
+
shipmentProtectionFeePercent: 2,
|
|
197
|
+
shipmentProtectionFeeMin: 30,
|
|
192
198
|
sellerShippingFixed: 0,
|
|
193
199
|
platformShippingPercent: 10,
|
|
194
200
|
platformShippingFixedMin: 0,
|
|
@@ -284,61 +290,92 @@ export const DEFAULT_TRUST_BAR_ITEMS = [
|
|
|
284
290
|
{ icon: "??", label: "24/7 Support", visible: true },
|
|
285
291
|
{ icon: "?", label: "Authentic Sellers", visible: true },
|
|
286
292
|
];
|
|
293
|
+
// NOTE: labelKey/descKey hold plain display text (not i18n message keys) —
|
|
294
|
+
// this feature-flag list has no matching next-intl namespace, so the admin
|
|
295
|
+
// UI reads these two fields directly rather than through getTranslations().
|
|
287
296
|
export const FEATURE_FLAG_META = [
|
|
288
297
|
{
|
|
289
298
|
key: "chats",
|
|
290
|
-
labelKey: "chats",
|
|
291
|
-
descKey: "
|
|
292
|
-
icon: "
|
|
299
|
+
labelKey: "In-app chats",
|
|
300
|
+
descKey: "Buyer/seller direct messaging.",
|
|
301
|
+
icon: "\u{1F4AC}",
|
|
293
302
|
category: "platform",
|
|
294
303
|
},
|
|
295
304
|
{
|
|
296
305
|
key: "smsVerification",
|
|
297
|
-
labelKey: "
|
|
298
|
-
descKey: "
|
|
299
|
-
icon: "
|
|
306
|
+
labelKey: "SMS verification",
|
|
307
|
+
descKey: "Phone-number OTP verification during signup/checkout.",
|
|
308
|
+
icon: "\u{1F4F1}",
|
|
300
309
|
category: "platform",
|
|
301
310
|
},
|
|
302
311
|
{
|
|
303
312
|
key: "translations",
|
|
304
|
-
labelKey: "
|
|
305
|
-
descKey: "
|
|
306
|
-
icon: "
|
|
313
|
+
labelKey: "Translations",
|
|
314
|
+
descKey: "Locale switcher and translated UI strings.",
|
|
315
|
+
icon: "\u{1F310}",
|
|
307
316
|
category: "platform",
|
|
308
317
|
},
|
|
309
318
|
{
|
|
310
319
|
key: "auctions",
|
|
311
|
-
labelKey: "
|
|
312
|
-
descKey: "
|
|
313
|
-
icon: "
|
|
320
|
+
labelKey: "Auctions",
|
|
321
|
+
descKey: "Auction listings and bidding.",
|
|
322
|
+
icon: "\u{1F528}",
|
|
314
323
|
category: "platform",
|
|
315
324
|
},
|
|
316
325
|
{
|
|
317
326
|
key: "reviews",
|
|
318
|
-
labelKey: "
|
|
319
|
-
descKey: "
|
|
320
|
-
icon: "
|
|
327
|
+
labelKey: "Reviews",
|
|
328
|
+
descKey: "Product and store reviews.",
|
|
329
|
+
icon: "⭐",
|
|
321
330
|
category: "platform",
|
|
322
331
|
},
|
|
323
332
|
{
|
|
324
333
|
key: "notifications",
|
|
325
|
-
labelKey: "
|
|
326
|
-
descKey: "
|
|
327
|
-
icon: "
|
|
334
|
+
labelKey: "Notifications",
|
|
335
|
+
descKey: "In-app notification bell and inbox.",
|
|
336
|
+
icon: "\u{1F514}",
|
|
328
337
|
category: "platform",
|
|
329
338
|
},
|
|
330
339
|
{
|
|
331
340
|
key: "sellerRegistration",
|
|
332
|
-
labelKey: "
|
|
333
|
-
descKey: "
|
|
334
|
-
icon: "
|
|
341
|
+
labelKey: "Seller registration",
|
|
342
|
+
descKey: "\"Become a seller\" signup flow.",
|
|
343
|
+
icon: "\u{1F3EA}",
|
|
335
344
|
category: "platform",
|
|
336
345
|
},
|
|
337
346
|
{
|
|
338
347
|
key: "preOrders",
|
|
339
|
-
labelKey: "
|
|
340
|
-
descKey: "
|
|
341
|
-
icon: "
|
|
348
|
+
labelKey: "Pre-orders",
|
|
349
|
+
descKey: "Pre-order listings and deposit checkout.",
|
|
350
|
+
icon: "\u{1F4E6}",
|
|
351
|
+
category: "platform",
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
key: "wishlists",
|
|
355
|
+
labelKey: "Wishlists",
|
|
356
|
+
descKey: "Save-for-later wishlist feature.",
|
|
357
|
+
icon: "❤️",
|
|
358
|
+
category: "platform",
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
key: "events",
|
|
362
|
+
labelKey: "Events",
|
|
363
|
+
descKey: "Sales, raffles, spin-wheel, and lottery events.",
|
|
364
|
+
icon: "\u{1F389}",
|
|
365
|
+
category: "platform",
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
key: "blog",
|
|
369
|
+
labelKey: "Blog",
|
|
370
|
+
descKey: "Blog posts section.",
|
|
371
|
+
icon: "\u{1F4DD}",
|
|
372
|
+
category: "platform",
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
key: "coupons",
|
|
376
|
+
labelKey: "Coupons",
|
|
377
|
+
descKey: "Coupon codes and discounts.",
|
|
378
|
+
icon: "\u{1F3AB}",
|
|
342
379
|
category: "platform",
|
|
343
380
|
},
|
|
344
381
|
];
|
|
@@ -130,13 +130,15 @@ export async function placeBid(userId, userEmail, input) {
|
|
|
130
130
|
});
|
|
131
131
|
});
|
|
132
132
|
const finalVisibleForRtdb = newBidWins ? visibleBid : (bumpedPreviousVisible ?? prevVisible);
|
|
133
|
+
const finalBidCountForRtdb = (product.bidCount ?? 0) + 1;
|
|
133
134
|
try {
|
|
134
135
|
const rtdb = getAdminRealtimeDb();
|
|
135
136
|
await rtdb.ref(`/auction-bids/${productId}`).set({
|
|
136
137
|
currentBid: finalVisibleForRtdb,
|
|
138
|
+
bidCount: finalBidCountForRtdb,
|
|
137
139
|
lastBid: {
|
|
138
140
|
amount: finalVisibleForRtdb,
|
|
139
|
-
bidderName:
|
|
141
|
+
bidderName: bid.userName,
|
|
140
142
|
timestamp: Date.now(),
|
|
141
143
|
},
|
|
142
144
|
updatedAt: Date.now(),
|
|
@@ -9,5 +9,10 @@ export interface AuctionBidsTableProps {
|
|
|
9
9
|
bids: BidDocument[];
|
|
10
10
|
portal?: "buyer" | "store" | "admin";
|
|
11
11
|
emptyLabel?: React.ReactNode;
|
|
12
|
+
/** Server-side pagination — the auction groups shown are already just the
|
|
13
|
+
* current page's worth of bids grouped by auction, not the full history. */
|
|
14
|
+
totalPages?: number;
|
|
15
|
+
currentPage?: number;
|
|
16
|
+
onPageChange?: (page: number) => void;
|
|
12
17
|
}
|
|
13
|
-
export declare function AuctionBidsTable({ bids, portal, emptyLabel, }: AuctionBidsTableProps): React.JSX.Element;
|
|
18
|
+
export declare function AuctionBidsTable({ bids, portal, emptyLabel, totalPages, currentPage, onPageChange, }: AuctionBidsTableProps): React.JSX.Element;
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useMemo, useState } from "react";
|
|
4
4
|
import { ChevronRight } from "lucide-react";
|
|
5
5
|
import { Badge, Button, Div, Row, Span, Stack, Text } from "../../../ui";
|
|
6
|
+
import { Pagination } from "../../../ui/components/Pagination";
|
|
6
7
|
const __O = {
|
|
7
8
|
hidden: "overflow-hidden",
|
|
8
9
|
};
|
|
@@ -51,15 +52,18 @@ function groupByAuction(bids) {
|
|
|
51
52
|
}
|
|
52
53
|
function AuctionRow({ auction, portal, }) {
|
|
53
54
|
const [expanded, setExpanded] = useState(false);
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
// Most-recent bid first — matches the caller's own newest-first ordering
|
|
56
|
+
// (see UserBidsPage) instead of re-sorting by amount, which silently threw
|
|
57
|
+
// that ordering away for the expanded per-auction row list.
|
|
58
|
+
const sorted = useMemo(() => [...auction.bids].sort((a, b) => +new Date(b.bidDate) - +new Date(a.bidDate)), [auction.bids]);
|
|
59
|
+
const highest = auction.bids.reduce((max, b) => Math.max(max, b.bidAmount), 0);
|
|
56
60
|
const isWinning = auction.bids.some((b) => b.isWinning);
|
|
57
61
|
return (_jsxs(Div, { className: `border border-[var(--appkit-color-border)] ${__O.hidden} bg-[var(--appkit-color-surface)]`, rounded: "xl", shadow: "sm", children: [_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setExpanded((v) => !v), gap: "lg", paddingX: "md", paddingY: "md", className: "w-full text-left", "aria-expanded": expanded, children: [_jsx(ChevronRight, { className: `shrink-0 text-[var(--appkit-color-text-muted)] transition-transform ${expanded ? "rotate-90" : ""}`, size: 16 }), _jsx(Div, { className: "flex-1 min-w-0", children: _jsx(Text, { className: "text-[var(--appkit-color-text)] line-clamp-1", size: "sm", weight: "semibold", children: auction.productTitle }) }), _jsxs(Row, { gap: "sm", className: "shrink-0", children: [isWinning && (_jsx(Badge, { variant: "active", children: "Winning" })), _jsxs(Text, { variant: "secondary", size: "xs", children: [auction.bids.length, " bid", auction.bids.length !== 1 ? "s" : ""] }), _jsx(Text, { className: "text-[var(--appkit-color-text)]", size: "sm", weight: "semibold", children: formatBidAmount(highest) })] })] }), expanded && (_jsxs(Div, { className: "border-t border-[var(--appkit-color-border)]", children: [_jsxs(Div, { textSize: "xs", textWeight: "medium", paddingX: "x-md", paddingY: "y-xs", className: `grid text-[var(--appkit-color-text-muted)] uppercase tracking-wide border-b border-[var(--appkit-color-border-subtle)] ${portal === "buyer" ? "[grid-template-columns:1fr_auto_auto]" : "[grid-template-columns:1fr_1fr_auto_auto]"}`, children: [_jsx(Span, { children: portal === "buyer" ? "Amount" : "Bidder" }), portal !== "buyer" && _jsx(Span, { children: "Amount" }), _jsx(Span, { children: "Status" }), _jsx(Span, { className: "text-right", children: "Time" })] }), sorted.map((bid) => (_jsxs(Div, { layout: "grid", align: "center", paddingX: "x-md", paddingY: "y-xs-tall", className: `border-b border-[var(--appkit-color-border-subtle)] last:border-0 hover:bg-[var(--appkit-color-border-subtle)] transition-colors ${portal === "buyer" ? "[grid-template-columns:1fr_auto_auto]" : "[grid-template-columns:1fr_1fr_auto_auto]"}`, children: [portal !== "buyer" && (_jsx(Text, { className: "text-[var(--appkit-color-text)] truncate pr-[0.75rem]", size: "sm", children: bid.userName || bid.userId })), _jsx(Text, { className: "text-[var(--appkit-color-text)]", size: "sm", weight: "medium", children: formatBidAmount(bid.bidAmount) }), _jsx(Badge, { variant: STATUS_VARIANT[bid.status] ?? "pending", className: "capitalize", children: bid.status }), _jsx(Text, { variant: "secondary", size: "xs", align: "end", children: relDate(bid.bidDate) })] }, bid.id)))] }))] }));
|
|
58
62
|
}
|
|
59
|
-
export function AuctionBidsTable({ bids, portal = "buyer", emptyLabel = "No bids found.", }) {
|
|
63
|
+
export function AuctionBidsTable({ bids, portal = "buyer", emptyLabel = "No bids found.", totalPages, currentPage, onPageChange, }) {
|
|
60
64
|
const auctions = useMemo(() => groupByAuction(bids), [bids]);
|
|
61
65
|
if (auctions.length === 0) {
|
|
62
66
|
return (_jsx(Div, { padding: "y-6xl", className: "text-left", children: _jsx(Text, { variant: "secondary", children: emptyLabel }) }));
|
|
63
67
|
}
|
|
64
|
-
return (
|
|
68
|
+
return (_jsxs(Stack, { gap: "3", children: [auctions.map((auction) => (_jsx(AuctionRow, { auction: auction, portal: portal }, auction.productId))), totalPages !== undefined && totalPages > 1 && currentPage !== undefined && onPageChange && (_jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: onPageChange }) }))] }));
|
|
65
69
|
}
|
|
@@ -31,6 +31,7 @@ import { MarketplaceAuctionGrid } from "./MarketplaceAuctionGrid";
|
|
|
31
31
|
import { listReviewsBySeller } from "../../reviews/actions/review-actions";
|
|
32
32
|
import { PlaceBidModalButton } from "./PlaceBidFormClient";
|
|
33
33
|
import { CollapsibleBidHistory } from "./CollapsibleBidHistory";
|
|
34
|
+
import { LiveBidPrice } from "./LiveBidPrice";
|
|
34
35
|
import { SublistingCarouselSection } from "../../products/components/SublistingCarouselSection";
|
|
35
36
|
function toDescriptionHtml(raw) {
|
|
36
37
|
if (!raw)
|
|
@@ -39,8 +40,8 @@ function toDescriptionHtml(raw) {
|
|
|
39
40
|
return normalizeRichTextHtml(s);
|
|
40
41
|
}
|
|
41
42
|
function renderAuctionInfoPanel(props) {
|
|
42
|
-
const { title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, featured, freeShipping, condition, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref } = props;
|
|
43
|
-
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_LIVE_BADGE, children: "\uD83C\uDFF7\uFE0F Live Auction" }), isEnded ? (_jsx(Span, { color: "error", surface: "danger-surface", size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", children: "Ended" })) : (_jsx(Span, { color: "success", surface: "success-surface", size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", children: "Active" }))] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), _jsxs(Div, { children: [_jsx(Text, { className: "mb-0.5", color: "muted", size: "xs", children: "Current bid" }),
|
|
43
|
+
const { productId, title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, featured, freeShipping, condition, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref } = props;
|
|
44
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_LIVE_BADGE, children: "\uD83C\uDFF7\uFE0F Live Auction" }), isEnded ? (_jsx(Span, { color: "error", surface: "danger-surface", size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", children: "Ended" })) : (_jsx(Span, { color: "success", surface: "success-surface", size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", children: "Active" }))] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), _jsxs(Div, { children: [_jsx(Text, { className: "mb-0.5", color: "muted", size: "xs", children: "Current bid" }), _jsx(LiveBidPrice, { productId: productId, currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "2xl", badgeSize: "sm" }), endDate && _jsxs(Text, { className: "mt-1.5", color: "muted", size: "sm", children: [isEnded ? "Ended" : "Ends", " ", _jsx(Span, { weight: "medium", color: "muted", children: endDate.toLocaleString() })] })] }), buyNowPrice !== null && !isEnded && (_jsxs(Row, { align: "center", gap: "sm", className: "border border-[var(--appkit-color-primary-200)] dark:border-[var(--appkit-color-primary-800)] bg-primary-50 dark:bg-primary-900/20", padding: "inlineSm", rounded: "lg", children: [_jsx(Span, { size: "xs", color: "muted", children: "Buy Now:" }), _jsx(Span, { size: "base", weight: "bold", className: "text-primary-700 dark:text-primary-300", children: formatCurrency(buyNowPrice, currency) })] })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, condition: condition ?? undefined, labels: { featured: "Featured", fasterDelivery: "Faster Delivery", ratedSeller: "Rated Seller", condition: "Condition", conditionNew: "New", conditionUsed: "Used", conditionBroken: "For Parts", conditionRefurbished: "Refurbished", returnable: "Returnable", freeShipping: "Free Shipping", codAvailable: "Cash on Delivery", emiAvailable: "EMI Available", wishlistCount: (n) => `${n} wishlisted`, categoryProductCount: (n, cat) => `${n} in ${cat}` } }), (categoryName || category || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [category && _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: categoryName || category }), !category && categoryName && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: categoryName }), brand && brandSlug && _jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: brand }), brand && !brandSlug && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: brand })] })), productFeatures && features.length > 0 && _jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures }), !productFeatures && features.length > 0 && (_jsxs(Div, { border: "subtle", surface: "muted", padding: "inline", rounded: "xl", children: [_jsx(Text, { className: "mb-2 tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "About this item" }), _jsx(Ul, { spacing: "comfortable", size: "sm", color: "primary", children: features.map((f, i) => _jsxs(Li, { layout: "flex-start", gap: "2", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i)) })] })), descriptionHtml && _jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" }), safeSeller && (_jsx(Div, { className: `${__P.p3}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Listed by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && _jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" })] }) }))] }));
|
|
44
45
|
}
|
|
45
46
|
function renderAuctionStoreReviews(storeReviews) {
|
|
46
47
|
if (storeReviews.length === 0)
|
|
@@ -53,7 +54,7 @@ export async function AuctionDetailPageView({ id, initialAuction, onPlaceBid, on
|
|
|
53
54
|
? (initialAuction ?? undefined)
|
|
54
55
|
: await productRepository.findByIdOrSlug(id).catch(() => undefined);
|
|
55
56
|
const bidsResult = product
|
|
56
|
-
? await listBidsByProduct(String(product.id), { pageSize:
|
|
57
|
+
? await listBidsByProduct(String(product.id), { pageSize: 5 }).catch(() => null)
|
|
57
58
|
: null;
|
|
58
59
|
const storeId = product?.storeId;
|
|
59
60
|
const storeReviews = storeId
|
|
@@ -83,6 +84,7 @@ export async function AuctionDetailPageView({ id, initialAuction, onPlaceBid, on
|
|
|
83
84
|
: typeof p.mainImage === "string"
|
|
84
85
|
? [p.mainImage]
|
|
85
86
|
: [];
|
|
87
|
+
const productVideo = p.video;
|
|
86
88
|
const endDate = p.auctionEndDate ? new Date(p.auctionEndDate) : null;
|
|
87
89
|
const isEnded = endDate ? endDate < new Date() : false;
|
|
88
90
|
const bidCount = typeof p.bidCount === "number" ? p.bidCount : 0;
|
|
@@ -125,23 +127,18 @@ export async function AuctionDetailPageView({ id, initialAuction, onPlaceBid, on
|
|
|
125
127
|
price: typeof p.price === "number" ? p.price : undefined,
|
|
126
128
|
storeId: typeof p.storeId === "string" ? p.storeId : undefined,
|
|
127
129
|
storeName: typeof p.storeName === "string" ? p.storeName : undefined,
|
|
128
|
-
} }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.AUCTIONS), className: CLS_BREADCRUMB_LINK, children: "Auctions" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(AuctionDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, productName: title })), renderInfo: () => renderAuctionInfoPanel({ title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, featured, freeShipping, condition, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref }), renderBidForm: () => onPlaceBid ? (_jsx(Stack, { id: "auction-bid-form", gap: "3", children: _jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [_jsxs(Row, { justify: "between", align: "baseline", children: [_jsx(
|
|
130
|
+
} }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.AUCTIONS), className: CLS_BREADCRUMB_LINK, children: "Auctions" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(AuctionDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, video: productVideo, productName: title })), renderInfo: () => renderAuctionInfoPanel({ productId: String(product.id), title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, featured, freeShipping, condition, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref }), renderBidForm: () => onPlaceBid ? (_jsx(Stack, { id: "auction-bid-form", gap: "3", children: _jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [_jsxs(Row, { justify: "between", align: "baseline", children: [_jsx(LiveBidPrice, { productId: String(product.id), currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "xl", badgeSize: "xs" }), !isEnded && (_jsxs(Span, { size: "xs", color: "muted", children: ["min increment ", formatCurrency(minBidIncrement, currency)] }))] }), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, minBidIncrement: minBidIncrement, currency: currency, isEnded: isEnded, buyNowPrice: buyNowPrice, bidsHaveStarted: bidsHaveStarted, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] }) })) : (
|
|
129
131
|
/* Read-only bid panel — shown when no bid action is wired (preview/demo) */
|
|
130
|
-
_jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "md", rounded: "xl", surface: "muted", children: [_jsx(Stack, { gap: "xs", children: _jsxs(Text, { size: "xs", color: "muted", children: ["Starting bid: ", formatCurrency(startingBid, currency), !isEnded && _jsxs(_Fragment, { children: [" \u00B7 min increment ", formatCurrency(minBidIncrement, currency)] })] }) }), _jsxs(Stack, { gap: "sm", children: [_jsx(Input, { type: "number", placeholder: `At least ${formatCurrency(currentBid + minBidIncrement, currency)}`, min: currentBid + minBidIncrement, "aria-label": "Your bid amount", disabled: isEnded }), _jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: isEnded, children: isEnded ? "Auction Ended" : "Place Bid" }), buyNowPrice !== null && !isEnded && !bidsHaveStarted && (_jsxs(Button, { variant: "secondary", size: "md", className: "w-full", disabled: true, children: ["Buy Now \u2014 ", formatCurrency(buyNowPrice, currency)] }))] }), tags.length > 0 && (_jsx(Div, { border: "default", className: "border-t", padding: "t-md", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { size: "xs", rounded: "full", padding: "pill-sm-tall", surface: "subtle", color: "muted", children: tag }, tag))) }) }))] })), renderMobileBidForm: () => !isEnded && onPlaceBid ? (_jsxs(Stack, { className: `lg:hidden ${__P.p4}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [
|
|
132
|
+
_jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "md", rounded: "xl", surface: "muted", children: [_jsx(Stack, { gap: "xs", children: _jsxs(Text, { size: "xs", color: "muted", children: ["Starting bid: ", formatCurrency(startingBid, currency), !isEnded && _jsxs(_Fragment, { children: [" \u00B7 min increment ", formatCurrency(minBidIncrement, currency)] })] }) }), _jsxs(Stack, { gap: "sm", children: [_jsx(Input, { type: "number", placeholder: `At least ${formatCurrency(currentBid + minBidIncrement, currency)}`, min: currentBid + minBidIncrement, "aria-label": "Your bid amount", disabled: isEnded }), _jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: isEnded, children: isEnded ? "Auction Ended" : "Place Bid" }), buyNowPrice !== null && !isEnded && !bidsHaveStarted && (_jsxs(Button, { variant: "secondary", size: "md", className: "w-full", disabled: true, children: ["Buy Now \u2014 ", formatCurrency(buyNowPrice, currency)] }))] }), tags.length > 0 && (_jsx(Div, { border: "default", className: "border-t", padding: "t-md", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { size: "xs", rounded: "full", padding: "pill-sm-tall", surface: "subtle", color: "muted", children: tag }, tag))) }) }))] })), renderMobileBidForm: () => !isEnded && onPlaceBid ? (_jsxs(Stack, { className: `lg:hidden ${__P.p4}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [_jsx(LiveBidPrice, { productId: String(product.id), currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "base", badgeSize: "xs" }), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, minBidIncrement: minBidIncrement, currency: currency, isEnded: isEnded, buyNowPrice: buyNowPrice, bidsHaveStarted: bidsHaveStarted, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] })) : !isEnded ? (_jsxs(Div, { className: `${__P.p4} lg:hidden`, border: "subtle", rounded: "xl", surface: "muted", children: [_jsxs(Row, { align: "center", gap: "sm", className: "mb-3", children: [_jsx(Span, { size: "base", weight: "bold", className: "text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { size: "xs", color: "muted", children: [bidCount, " bids"] })] }), _jsx(Button, { variant: "primary", size: "md", className: "w-full", children: "Place Bid" })] })) : null, renderSublistingSection: sublistingCategoryId
|
|
131
133
|
? () => (_jsx(SublistingCarouselSection, { sublistingCategoryId: sublistingCategoryId, currentListingId: String(product.id) }))
|
|
132
134
|
: undefined, renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })) : undefined, specsContent: specs.length > 0 ? (_jsx(Dl, { divide: "subtle", rounded: "xl", border: "subtle", className: "overflow-hidden", children: specs.map((s, i) => (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-36 flex-shrink-0", color: "primary", weight: "medium", children: s.name }), _jsxs(Dd, { className: "flex-1", color: "muted", children: [s.value, s.unit ? ` ${s.unit}` : ""] })] }, i))) })) : undefined, customTabs: customSections.map((s) => ({
|
|
133
135
|
id: s.id,
|
|
134
136
|
label: s.title,
|
|
135
137
|
content: _jsx(CustomSectionTabContent, { section: s }),
|
|
136
138
|
})) })), renderBidHistory: () => {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
bidderName: (b.bidderName ?? b.userName),
|
|
141
|
-
amount: (typeof b.bidAmount === "number" ? b.bidAmount : typeof b.amount === "number" ? b.amount : 0),
|
|
142
|
-
placedAt: (b.bidDate ?? b.createdAt ?? b.bidAt ?? ""),
|
|
143
|
-
}));
|
|
144
|
-
return _jsx(CollapsibleBidHistory, { bids: bids, currency: currency });
|
|
139
|
+
if (!bidsResult)
|
|
140
|
+
return null;
|
|
141
|
+
return (_jsx(CollapsibleBidHistory, { productId: String(product.id), initialData: bidsResult, currency: currency }));
|
|
145
142
|
}, renderRelated: () => {
|
|
146
143
|
const now = new Date();
|
|
147
144
|
const related = relatedDocs
|
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { BidHistoryEntry } from "../../products/components/BidHistory";
|
|
3
|
+
import { type BidListResponse } from "../hooks/useBids";
|
|
4
|
+
interface RawBidLike {
|
|
5
|
+
id?: unknown;
|
|
6
|
+
userId?: unknown;
|
|
7
|
+
bidderId?: unknown;
|
|
8
|
+
userName?: unknown;
|
|
9
|
+
bidderName?: unknown;
|
|
10
|
+
bidAmount?: unknown;
|
|
11
|
+
amount?: unknown;
|
|
12
|
+
bidDate?: unknown;
|
|
13
|
+
createdAt?: unknown;
|
|
14
|
+
bidAt?: unknown;
|
|
15
|
+
}
|
|
16
|
+
/** Shared shape mapper — SSR's raw bid docs and useBids()'s client-fetched
|
|
17
|
+
* pages both go through this so every page (SSR page 1, client pages 2+)
|
|
18
|
+
* renders identically. */
|
|
19
|
+
export declare function toBidHistoryEntry(b: RawBidLike): BidHistoryEntry;
|
|
3
20
|
interface CollapsibleBidHistoryProps {
|
|
4
|
-
|
|
21
|
+
productId: string;
|
|
22
|
+
/** SSR page-1 result — seeds `useBids()`'s cache so opening the section
|
|
23
|
+
* doesn't trigger a redundant fetch for the page already rendered. */
|
|
24
|
+
initialData: BidListResponse;
|
|
5
25
|
currency: string;
|
|
26
|
+
pageSize?: number;
|
|
6
27
|
}
|
|
7
|
-
export declare function CollapsibleBidHistory({
|
|
28
|
+
export declare function CollapsibleBidHistory({ productId, initialData, currency, pageSize }: CollapsibleBidHistoryProps): React.JSX.Element;
|
|
8
29
|
export {};
|
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from "react";
|
|
4
|
-
import { Button, Div, Span } from "../../../ui";
|
|
4
|
+
import { Button, Div, Row, Span } from "../../../ui";
|
|
5
|
+
import { Pagination } from "../../../ui/components/Pagination";
|
|
5
6
|
import { BidHistory } from "../../products/components/BidHistory";
|
|
7
|
+
import { useBids } from "../hooks/useBids";
|
|
6
8
|
const __O = {
|
|
7
9
|
hidden: "overflow-hidden",
|
|
8
10
|
};
|
|
9
|
-
|
|
11
|
+
/** Shared shape mapper — SSR's raw bid docs and useBids()'s client-fetched
|
|
12
|
+
* pages both go through this so every page (SSR page 1, client pages 2+)
|
|
13
|
+
* renders identically. */
|
|
14
|
+
export function toBidHistoryEntry(b) {
|
|
15
|
+
return {
|
|
16
|
+
id: String(b.id ?? ""),
|
|
17
|
+
bidderId: String(b.userId ?? b.bidderId ?? ""),
|
|
18
|
+
bidderName: (b.bidderName ?? b.userName),
|
|
19
|
+
amount: typeof b.bidAmount === "number" ? b.bidAmount : typeof b.amount === "number" ? b.amount : 0,
|
|
20
|
+
placedAt: (b.bidDate ?? b.createdAt ?? b.bidAt ?? ""),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function CollapsibleBidHistory({ productId, initialData, currency, pageSize = 5 }) {
|
|
10
24
|
const [open, setOpen] = useState(false);
|
|
11
|
-
|
|
25
|
+
const [page, setPage] = useState(1);
|
|
26
|
+
const { bids: rawBids, totalPages, isLoading } = useBids({ productId, page, pageSize }, { initialData, enabled: open });
|
|
27
|
+
const bids = rawBids.map(toBidHistoryEntry);
|
|
28
|
+
const total = initialData.total;
|
|
29
|
+
return (_jsxs(Div, { className: `mt-6 ${__O.hidden}`, border: "subtle", rounded: "xl", children: [_jsxs(Button, { type: "button", variant: "ghost", onClick: () => setOpen((v) => !v), paddingX: "md", paddingY: "md", rounded: "none", className: "w-full justify-between bg-[var(--appkit-color-surface)]/60 hover:bg-[var(--appkit-color-surface-elevated)]", "aria-expanded": open, children: [_jsxs(Span, { size: "sm", weight: "semibold", className: "tracking-wide", color: "muted", transform: "uppercase", children: ["Bid History", total > 0 && (_jsx(Span, { size: "xs", weight: "medium", className: "ml-2 normal-case tracking-normal", rounded: "full", padding: "pill-xs", surface: "subtle", color: "muted", children: total }))] }), _jsx("svg", { className: `h-4 w-4 text-zinc-400 transition-transform duration-200 ${open ? "rotate-180" : ""}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), open && (_jsxs(Div, { surface: "default", padding: "md", children: [_jsx(BidHistory, { bids: bids, isLoading: isLoading && bids.length === 0, isEmpty: !isLoading && bids.length === 0, currency: currency }), totalPages > 1 && (_jsx(Row, { justify: "center", className: "mt-4", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: setPage, size: "sm" }) }))] }))] }));
|
|
12
30
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface LiveBidPriceProps {
|
|
2
|
+
productId: string;
|
|
3
|
+
currentBid: number;
|
|
4
|
+
bidCount: number;
|
|
5
|
+
currency: string;
|
|
6
|
+
isEnded: boolean;
|
|
7
|
+
/** Matches the appkit `<Span size>` scale — pick to match the call site. */
|
|
8
|
+
priceSize?: "base" | "lg" | "xl" | "2xl";
|
|
9
|
+
badgeSize?: "xs" | "sm";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* LiveBidPrice — the current-bid + bid-count pill, kept live via the
|
|
13
|
+
* `/auction-bids/{id}` SSE channel. Replaces 3 near-identical static
|
|
14
|
+
* SSR-only displays on the auction detail page (info panel, desktop and
|
|
15
|
+
* mobile compact bid-summary cards) so every viewer watching the page sees
|
|
16
|
+
* a new bid the instant it's placed, not just after their own bid or a
|
|
17
|
+
* page reload.
|
|
18
|
+
*/
|
|
19
|
+
export declare function LiveBidPrice({ productId, currentBid, bidCount, currency, isEnded, priceSize, badgeSize, }: LiveBidPriceProps): import("react").JSX.Element;
|