@mohasinac/appkit 2.3.1 → 2.3.2
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/client.d.ts +12 -8
- package/dist/client.js +7 -4
- package/dist/constants/api-endpoints.d.ts +4 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/core/contact-submissions.repository.d.ts +32 -0
- package/dist/core/contact-submissions.repository.js +49 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +1 -0
- package/dist/features/about/components/HowPayoutsWorkView.js +1 -1
- package/dist/features/account/components/AddressFilters.d.ts +5 -0
- package/dist/features/account/components/AddressFilters.js +20 -0
- package/dist/features/account/components/AddressesIndexListing.d.ts +6 -0
- package/dist/features/account/components/AddressesIndexListing.js +51 -0
- package/dist/features/account/components/UserSidebar.d.ts +7 -3
- package/dist/features/account/components/UserSidebar.js +55 -7
- package/dist/features/account/hooks/useAddresses.d.ts +7 -0
- package/dist/features/account/hooks/useAddresses.js +12 -1
- package/dist/features/admin/actions/admin-read-actions.d.ts +12 -0
- package/dist/features/admin/actions/admin-read-actions.js +18 -0
- package/dist/features/admin/components/AdminBlogView.js +26 -2
- package/dist/features/admin/components/AdminCarouselView.js +18 -2
- package/dist/features/admin/components/AdminCategoriesView.js +27 -2
- package/dist/features/admin/components/AdminContactView.d.ts +4 -0
- package/dist/features/admin/components/AdminContactView.js +50 -0
- package/dist/features/admin/components/AdminFaqsView.js +19 -2
- package/dist/features/admin/components/AdminListingScaffold.d.ts +11 -2
- package/dist/features/admin/components/AdminListingScaffold.js +14 -3
- package/dist/features/admin/components/AdminNewsletterView.d.ts +4 -0
- package/dist/features/admin/components/AdminNewsletterView.js +50 -0
- package/dist/features/admin/components/AdminProductsView.js +30 -2
- package/dist/features/admin/components/AdminReviewsView.js +26 -2
- package/dist/features/admin/components/AdminStoresView.js +17 -2
- package/dist/features/admin/components/AdminUsersView.js +27 -2
- package/dist/features/admin/components/DataTable.d.ts +2 -1
- package/dist/features/admin/components/DataTable.js +18 -4
- package/dist/features/admin/components/index.d.ts +6 -0
- package/dist/features/admin/components/index.js +3 -0
- package/dist/features/admin/hooks/useAdminListingData.d.ts +3 -1
- package/dist/features/admin/hooks/useAdminListingData.js +12 -7
- package/dist/features/admin/server.d.ts +3 -0
- package/dist/features/admin/server.js +2 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +93 -47
- package/dist/features/auctions/components/AuctionFilters.d.ts +8 -0
- package/dist/features/auctions/components/AuctionFilters.js +12 -0
- package/dist/features/auctions/components/AuctionsListView.d.ts +6 -1
- package/dist/features/auctions/components/AuctionsListView.js +37 -5
- package/dist/features/auctions/schemas/index.d.ts +4 -4
- package/dist/features/blog/components/BlogFeaturedCard.d.ts +1 -7
- package/dist/features/blog/components/BlogFeaturedCard.js +4 -5
- package/dist/features/blog/components/BlogFilters.js +2 -1
- package/dist/features/blog/components/BlogIndexListing.js +14 -9
- package/dist/features/blog/components/BlogIndexPageView.d.ts +6 -1
- package/dist/features/blog/components/BlogIndexPageView.js +10 -2
- package/dist/features/blog/components/BlogListView.d.ts +2 -1
- package/dist/features/blog/components/BlogListView.js +10 -3
- package/dist/features/categories/components/CategoriesIndexListing.d.ts +1 -1
- package/dist/features/categories/components/CategoriesIndexListing.js +41 -38
- package/dist/features/categories/components/CategoriesIndexPageView.d.ts +6 -1
- package/dist/features/categories/components/CategoriesIndexPageView.js +41 -2
- package/dist/features/categories/components/CategoryDetailPageView.js +13 -6
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +5 -0
- package/dist/features/categories/components/CategoryDetailTabs.js +17 -0
- package/dist/features/categories/components/CategoryFilters.js +2 -1
- package/dist/features/categories/components/CategoryGrid.d.ts +2 -1
- package/dist/features/categories/components/CategoryGrid.js +8 -6
- package/dist/features/categories/components/CategoryProductsListing.js +22 -11
- package/dist/features/categories/components/index.d.ts +2 -0
- package/dist/features/categories/components/index.js +1 -0
- package/dist/features/categories/hooks/useCategories.d.ts +20 -0
- package/dist/features/categories/hooks/useCategories.js +50 -1
- package/dist/features/categories/hooks/useCategoryTree.d.ts +17 -0
- package/dist/features/categories/hooks/useCategoryTree.js +65 -0
- package/dist/features/events/components/AdminEventEditorView.d.ts +6 -0
- package/dist/features/events/components/AdminEventEditorView.js +203 -0
- package/dist/features/events/components/AdminEventsView.js +28 -2
- package/dist/features/events/components/EventCard.js +4 -2
- package/dist/features/events/components/EventFilters.js +2 -1
- package/dist/features/events/components/EventsIndexListing.js +40 -10
- package/dist/features/events/components/EventsListPageView.d.ts +6 -1
- package/dist/features/events/components/EventsListPageView.js +40 -7
- package/dist/features/events/components/index.d.ts +2 -0
- package/dist/features/events/components/index.js +1 -0
- package/dist/features/events/hooks/useEvents.js +2 -0
- package/dist/features/events/types/index.d.ts +1 -0
- package/dist/features/homepage/components/BlogArticlesSection.js +1 -1
- package/dist/features/homepage/components/EventsSection.js +1 -1
- package/dist/features/homepage/components/FeaturedAuctionsSection.js +1 -1
- package/dist/features/homepage/components/FeaturedPreOrdersSection.js +1 -1
- package/dist/features/homepage/components/FeaturedProductsSection.js +1 -1
- package/dist/features/homepage/components/FeaturedStoresSection.js +1 -1
- package/dist/features/homepage/components/HeroCarousel.js +1 -1
- package/dist/features/homepage/components/MarketplaceHomepageView.js +27 -17
- package/dist/features/homepage/components/SectionCarousel.js +4 -4
- package/dist/features/homepage/components/ShopByCategorySection.js +2 -2
- package/dist/features/homepage/schemas/firestore.d.ts +1 -1
- package/dist/features/homepage/schemas/firestore.js +2 -1
- package/dist/features/layout/AppLayoutShell.d.ts +6 -2
- package/dist/features/layout/AppLayoutShell.js +7 -3
- package/dist/features/pre-orders/components/MarketplacePreorderCard.d.ts +3 -1
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +6 -2
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +80 -12
- package/dist/features/pre-orders/components/PreOrderFilters.d.ts +8 -0
- package/dist/features/pre-orders/components/PreOrderFilters.js +21 -0
- package/dist/features/pre-orders/components/PreOrdersIndexListing.d.ts +2 -1
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +69 -10
- package/dist/features/pre-orders/components/PreOrdersListView.d.ts +6 -1
- package/dist/features/pre-orders/components/PreOrdersListView.js +26 -7
- package/dist/features/pre-orders/components/index.d.ts +2 -0
- package/dist/features/pre-orders/components/index.js +1 -0
- package/dist/features/products/components/AuctionsIndexListing.d.ts +2 -1
- package/dist/features/products/components/AuctionsIndexListing.js +61 -9
- package/dist/features/products/components/InteractiveProductCard.d.ts +2 -4
- package/dist/features/products/components/InteractiveProductCard.js +2 -2
- package/dist/features/products/components/ProductDetailPageView.d.ts +1 -1
- package/dist/features/products/components/ProductDetailPageView.js +116 -25
- package/dist/features/products/components/ProductFilters.d.ts +6 -11
- package/dist/features/products/components/ProductFilters.js +5 -3
- package/dist/features/products/components/ProductGrid.d.ts +8 -2
- package/dist/features/products/components/ProductGrid.js +20 -5
- package/dist/features/products/components/ProductTabsShell.d.ts +3 -11
- package/dist/features/products/components/ProductTabsShell.js +14 -14
- package/dist/features/products/components/ProductsIndexListing.js +73 -9
- package/dist/features/products/components/ProductsIndexPageView.d.ts +6 -1
- package/dist/features/products/components/ProductsIndexPageView.js +39 -6
- package/dist/features/products/components/RelatedProductsCarousel.d.ts +7 -0
- package/dist/features/products/components/RelatedProductsCarousel.js +11 -0
- package/dist/features/products/components/index.d.ts +1 -0
- package/dist/features/products/components/index.js +1 -0
- package/dist/features/products/hooks/useProducts.js +16 -0
- package/dist/features/products/repository/products.repository.d.ts +8 -0
- package/dist/features/products/repository/products.repository.js +2 -0
- package/dist/features/products/schemas/index.d.ts +8 -8
- package/dist/features/products/types/index.d.ts +12 -0
- package/dist/features/promotions/components/CouponsIndexListing.d.ts +9 -0
- package/dist/features/promotions/components/CouponsIndexListing.js +86 -0
- package/dist/features/promotions/components/PromotionsView.d.ts +11 -5
- package/dist/features/promotions/components/PromotionsView.js +6 -1
- package/dist/features/promotions/components/index.d.ts +4 -2
- package/dist/features/promotions/components/index.js +2 -1
- package/dist/features/reviews/components/ReviewDetailPageView.d.ts +4 -0
- package/dist/features/reviews/components/ReviewDetailPageView.js +20 -0
- package/dist/features/reviews/components/ReviewDetailShell.d.ts +7 -0
- package/dist/features/reviews/components/ReviewDetailShell.js +80 -0
- package/dist/features/reviews/components/ReviewFilters.d.ts +3 -3
- package/dist/features/reviews/components/ReviewFilters.js +5 -4
- package/dist/features/reviews/components/ReviewsIndexListing.d.ts +4 -3
- package/dist/features/reviews/components/ReviewsIndexListing.js +35 -51
- package/dist/features/reviews/components/ReviewsIndexPageView.d.ts +6 -1
- package/dist/features/reviews/components/ReviewsIndexPageView.js +49 -3
- package/dist/features/reviews/components/ReviewsList.js +9 -1
- package/dist/features/reviews/components/index.d.ts +1 -0
- package/dist/features/reviews/hooks/useReviews.js +15 -1
- package/dist/features/reviews/types/index.d.ts +6 -1
- package/dist/features/seller/components/SellerSidebar.d.ts +8 -4
- package/dist/features/seller/components/SellerSidebar.js +6 -4
- package/dist/features/seller/components/index.d.ts +30 -0
- package/dist/features/seller/components/index.js +17 -0
- package/dist/features/seller/hooks/useSellerStore.d.ts +2 -0
- package/dist/features/seller/hooks/useSellerStore.js +2 -0
- package/dist/features/seller/permission-map.d.ts +4 -2
- package/dist/features/seller/permission-map.js +16 -14
- package/dist/features/seller/schemas/index.d.ts +2 -2
- package/dist/features/stores/api/[storeSlug]/reviews/route.d.ts +1 -1
- package/dist/features/stores/api/[storeSlug]/reviews/route.js +24 -19
- package/dist/features/stores/components/InteractiveStoreCard.d.ts +0 -5
- package/dist/features/stores/components/InteractiveStoreCard.js +9 -9
- package/dist/features/stores/components/StoreAuctionsListing.js +27 -9
- package/dist/features/stores/components/StoreDetailLayoutView.js +2 -0
- package/dist/features/stores/components/StoreFilters.d.ts +5 -0
- package/dist/features/stores/components/StoreFilters.js +20 -0
- package/dist/features/stores/components/StoreHeader.js +2 -2
- package/dist/features/stores/components/StorePreOrdersListing.d.ts +5 -0
- package/dist/features/stores/components/StorePreOrdersListing.js +40 -0
- package/dist/features/stores/components/StorePreOrdersPageView.d.ts +4 -0
- package/dist/features/stores/components/StorePreOrdersPageView.js +21 -0
- package/dist/features/stores/components/StoreProductsListing.js +21 -11
- package/dist/features/stores/components/StoreReviewsListing.js +2 -7
- package/dist/features/stores/components/StoresIndexListing.js +42 -8
- package/dist/features/stores/components/StoresIndexPageView.d.ts +6 -1
- package/dist/features/stores/components/StoresIndexPageView.js +9 -2
- package/dist/features/stores/components/index.d.ts +3 -0
- package/dist/features/stores/components/index.js +1 -0
- package/dist/features/stores/hooks/useStores.d.ts +7 -1
- package/dist/features/stores/hooks/useStores.js +16 -3
- package/dist/features/stores/schemas/index.d.ts +2 -2
- package/dist/features/stores/types/index.d.ts +3 -0
- package/dist/features/wishlist/hooks/useGuestWishlist.d.ts +20 -0
- package/dist/features/wishlist/hooks/useGuestWishlist.js +49 -0
- package/dist/features/wishlist/hooks/useWishlistCount.d.ts +7 -0
- package/dist/features/wishlist/hooks/useWishlistCount.js +31 -0
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +56 -0
- package/dist/features/wishlist/hooks/useWishlistWithGuest.js +57 -0
- package/dist/features/wishlist/index.d.ts +3 -0
- package/dist/features/wishlist/index.js +3 -0
- package/dist/features/wishlist/utils/guest-wishlist.d.ts +22 -0
- package/dist/features/wishlist/utils/guest-wishlist.js +70 -0
- package/dist/index.d.ts +50 -1
- package/dist/index.js +63 -1
- package/dist/next/routing/route-map.d.ts +70 -36
- package/dist/next/routing/route-map.js +30 -22
- package/dist/seed/addresses-seed-data.js +62 -261
- package/dist/seed/beyblade-seed-data.d.ts +7 -0
- package/dist/seed/beyblade-seed-data.js +947 -0
- package/dist/seed/bids-seed-data.d.ts +10 -2
- package/dist/seed/bids-seed-data.js +220 -1071
- package/dist/seed/blog-posts-seed-data.d.ts +2 -2
- package/dist/seed/blog-posts-seed-data.js +455 -117
- package/dist/seed/cart-seed-data.d.ts +9 -9
- package/dist/seed/cart-seed-data.js +73 -74
- package/dist/seed/coupons-seed-data.d.ts +3 -4
- package/dist/seed/coupons-seed-data.js +3 -509
- package/dist/seed/events-seed-data.d.ts +2 -2
- package/dist/seed/events-seed-data.js +315 -476
- package/dist/seed/faq-seed-data.d.ts +18 -41
- package/dist/seed/faq-seed-data.js +1059 -1172
- package/dist/seed/hot-wheels-seed-data.d.ts +7 -0
- package/dist/seed/hot-wheels-seed-data.js +1365 -0
- package/dist/seed/index.d.ts +6 -1
- package/dist/seed/index.js +6 -1
- package/dist/seed/pokemon-carousel-slides-seed-data.d.ts +4 -2
- package/dist/seed/pokemon-carousel-slides-seed-data.js +152 -268
- package/dist/seed/pokemon-categories-seed-data.d.ts +18 -21
- package/dist/seed/pokemon-categories-seed-data.js +424 -1004
- package/dist/seed/pokemon-coupons-seed-data.d.ts +6 -0
- package/dist/seed/pokemon-coupons-seed-data.js +465 -0
- package/dist/seed/pokemon-homepage-sections-seed-data.d.ts +3 -2
- package/dist/seed/pokemon-homepage-sections-seed-data.js +67 -289
- package/dist/seed/pokemon-products-seed-data.js +662 -0
- package/dist/seed/pokemon-seed-bundle.d.ts +32 -11
- package/dist/seed/pokemon-seed-bundle.js +41 -11
- package/dist/seed/pokemon-stores-seed-data.d.ts +2 -3
- package/dist/seed/pokemon-stores-seed-data.js +56 -31
- package/dist/seed/pokemon-users-seed-data.d.ts +2 -2
- package/dist/seed/pokemon-users-seed-data.js +245 -261
- package/dist/seed/reviews-seed-data.d.ts +17 -2
- package/dist/seed/reviews-seed-data.js +519 -483
- package/dist/seed/site-settings-seed-data.js +14 -14
- package/dist/seed/store-addresses-seed-data.js +68 -50
- package/dist/seed/transformers-seed-data.d.ts +7 -0
- package/dist/seed/transformers-seed-data.js +510 -0
- package/dist/seed/wishlists-seed-data.d.ts +5 -1
- package/dist/seed/wishlists-seed-data.js +82 -4
- package/dist/server.d.ts +1 -0
- package/dist/server.js +2 -0
- package/dist/tokens/index.d.ts +6 -0
- package/dist/tokens/index.js +2 -0
- package/dist/ui/components/BaseListingCard.js +24 -26
- package/dist/ui/components/BaseListingCard.style.css +5 -5
- package/dist/ui/components/HorizontalScroller.d.ts +1 -1
- package/dist/ui/components/HorizontalScroller.js +19 -5
- package/dist/ui/components/SideDrawer.style.css +3 -11
- package/dist/ui/rich-text/RichText.js +19 -1
- package/package.json +1 -1
|
@@ -85,6 +85,13 @@ const DEFAULT_SECURITY_ITEMS = [
|
|
|
85
85
|
},
|
|
86
86
|
];
|
|
87
87
|
// --- Helpers -----------------------------------------------------------------
|
|
88
|
+
/** Strip leading emoji/symbols that admins sometimes prefix onto DB titles. */
|
|
89
|
+
function cleanTitle(raw) {
|
|
90
|
+
if (!raw)
|
|
91
|
+
return raw;
|
|
92
|
+
const cleaned = raw.replace(/^[^\p{L}\p{N}]+/u, "").trim();
|
|
93
|
+
return cleaned || raw;
|
|
94
|
+
}
|
|
88
95
|
function parseWelcomeDescription(description) {
|
|
89
96
|
if (!description)
|
|
90
97
|
return "";
|
|
@@ -105,18 +112,21 @@ function parseWelcomeDescription(description) {
|
|
|
105
112
|
* Render a single homepage section based on its type and config
|
|
106
113
|
*/
|
|
107
114
|
const AD_SLOT_MAP = {
|
|
115
|
+
carousel: "afterHero",
|
|
108
116
|
products: "afterFeaturedProducts",
|
|
109
117
|
reviews: "afterReviews",
|
|
110
118
|
faq: "afterFAQ",
|
|
111
119
|
};
|
|
112
|
-
function renderSection(section, adSlots, newsletterFormSlot, faqItems) {
|
|
120
|
+
function renderSection(section, adSlots, newsletterFormSlot, faqItems, slides) {
|
|
113
121
|
const { type, config } = section;
|
|
114
122
|
const adSlotKey = AD_SLOT_MAP[type];
|
|
115
123
|
const sectionElement = (() => {
|
|
116
124
|
switch (type) {
|
|
125
|
+
case "carousel":
|
|
126
|
+
return _jsx(HeroCarousel, { initialSlides: slides });
|
|
117
127
|
case "welcome": {
|
|
118
128
|
const cfg = config;
|
|
119
|
-
const title = cfg?.h1 || "Discover Amazing Products";
|
|
129
|
+
const title = cleanTitle(cfg?.h1) || "Discover Amazing Products";
|
|
120
130
|
const subtitle = parseWelcomeDescription(cfg?.description);
|
|
121
131
|
const ctaText = cfg?.ctaText || "Shop Now";
|
|
122
132
|
const ctaLink = cfg?.ctaLink || ROUTES.PUBLIC.PRODUCTS;
|
|
@@ -129,7 +139,7 @@ function renderSection(section, adSlots, newsletterFormSlot, faqItems) {
|
|
|
129
139
|
}
|
|
130
140
|
case "categories": {
|
|
131
141
|
const cfg = config;
|
|
132
|
-
return (_jsx(ShopByCategorySection, { title: cfg?.title || "Shop by Category", viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All categories \u2192" }));
|
|
142
|
+
return (_jsx(ShopByCategorySection, { title: cleanTitle(cfg?.title) || "Shop by Category", viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All categories \u2192" }));
|
|
133
143
|
}
|
|
134
144
|
case "stats": {
|
|
135
145
|
const cfg = config;
|
|
@@ -154,27 +164,27 @@ function renderSection(section, adSlots, newsletterFormSlot, faqItems) {
|
|
|
154
164
|
}
|
|
155
165
|
case "products": {
|
|
156
166
|
const cfg = config;
|
|
157
|
-
return (_jsx(FeaturedProductsSection, { title: cfg?.title || "Featured Products", viewMoreHref: ROUTES.PUBLIC.PRODUCTS, viewMoreLabel: "View all products \u2192" }));
|
|
167
|
+
return (_jsx(FeaturedProductsSection, { title: cleanTitle(cfg?.title) || "Featured Products", viewMoreHref: ROUTES.PUBLIC.PRODUCTS, viewMoreLabel: "View all products \u2192" }));
|
|
158
168
|
}
|
|
159
169
|
case "auctions": {
|
|
160
170
|
const cfg = config;
|
|
161
|
-
return (_jsx(FeaturedAuctionsSection, { title: cfg?.title || "Live Auctions", viewMoreHref: ROUTES.PUBLIC.AUCTIONS, viewMoreLabel: "View all auctions \u2192" }));
|
|
171
|
+
return (_jsx(FeaturedAuctionsSection, { title: cleanTitle(cfg?.title) || "Live Auctions", viewMoreHref: ROUTES.PUBLIC.AUCTIONS, viewMoreLabel: "View all auctions \u2192" }));
|
|
162
172
|
}
|
|
163
173
|
case "pre-orders": {
|
|
164
174
|
const cfg = config;
|
|
165
|
-
return (_jsx(FeaturedPreOrdersSection, { title: cfg?.title || "Reserve Before It Ships", viewMoreHref: ROUTES.PUBLIC.PRE_ORDERS, viewMoreLabel: "View all pre-orders \u2192" }));
|
|
175
|
+
return (_jsx(FeaturedPreOrdersSection, { title: cleanTitle(cfg?.title) || "Reserve Before It Ships", viewMoreHref: ROUTES.PUBLIC.PRE_ORDERS, viewMoreLabel: "View all pre-orders \u2192" }));
|
|
166
176
|
}
|
|
167
177
|
case "stores": {
|
|
168
178
|
const cfg = config;
|
|
169
|
-
return (_jsx(FeaturedStoresSection, { title: cfg?.title || "Featured Stores", viewMoreHref: ROUTES.PUBLIC.STORES, viewMoreLabel: "View all stores \u2192" }));
|
|
179
|
+
return (_jsx(FeaturedStoresSection, { title: cleanTitle(cfg?.title) || "Featured Stores", viewMoreHref: ROUTES.PUBLIC.STORES, viewMoreLabel: "View all stores \u2192" }));
|
|
170
180
|
}
|
|
171
181
|
case "events": {
|
|
172
182
|
const cfg = config;
|
|
173
|
-
return (_jsx(EventsSection, { title: cfg?.title || "Events & Offers", viewMoreHref: ROUTES.PUBLIC.EVENTS, viewMoreLabel: "View all events \u2192" }));
|
|
183
|
+
return (_jsx(EventsSection, { title: cleanTitle(cfg?.title) || "Events & Offers", viewMoreHref: ROUTES.PUBLIC.EVENTS, viewMoreLabel: "View all events \u2192" }));
|
|
174
184
|
}
|
|
175
185
|
case "reviews": {
|
|
176
186
|
const cfg = config;
|
|
177
|
-
return (_jsx(HomepageCustomerReviewsSection, { title: cfg?.title || "What Our Customers Say", viewMoreHref: ROUTES.PUBLIC.REVIEWS, viewMoreLabel: "See all reviews \u2192" }));
|
|
187
|
+
return (_jsx(HomepageCustomerReviewsSection, { title: cleanTitle(cfg?.title) || "What Our Customers Say", viewMoreHref: ROUTES.PUBLIC.REVIEWS, viewMoreLabel: "See all reviews \u2192" }));
|
|
178
188
|
}
|
|
179
189
|
case "banner": {
|
|
180
190
|
const cfg = config;
|
|
@@ -182,34 +192,34 @@ function renderSection(section, adSlots, newsletterFormSlot, faqItems) {
|
|
|
182
192
|
}
|
|
183
193
|
case "trust-indicators": {
|
|
184
194
|
const cfg = config;
|
|
185
|
-
return (_jsx(TrustFeaturesSection, { title: cfg?.title || "Why Buyers Trust LetiTrip", items: DEFAULT_TRUST_FEATURES }));
|
|
195
|
+
return (_jsx(TrustFeaturesSection, { title: cleanTitle(cfg?.title) || "Why Buyers Trust LetiTrip", items: DEFAULT_TRUST_FEATURES }));
|
|
186
196
|
}
|
|
187
197
|
case "features": {
|
|
188
198
|
const cfg = config;
|
|
189
|
-
return (_jsx(SecurityHighlightsSection, { title: cfg?.title || "Security You Can Trust", pillLabel: "Built for trust", items: DEFAULT_SECURITY_ITEMS, learnMoreHref: ROUTES.PUBLIC.SECURITY, learnMoreLabel: "Learn about our security \u2192" }));
|
|
199
|
+
return (_jsx(SecurityHighlightsSection, { title: cleanTitle(cfg?.title) || "Security You Can Trust", pillLabel: "Built for trust", items: DEFAULT_SECURITY_ITEMS, learnMoreHref: ROUTES.PUBLIC.SECURITY, learnMoreLabel: "Learn about our security \u2192" }));
|
|
190
200
|
}
|
|
191
201
|
case "whatsapp-community": {
|
|
192
202
|
const cfg = config;
|
|
193
|
-
return (_jsx(WhatsAppCommunitySection, { title: cfg?.title || "Join Our WhatsApp Community", descriptionHtml: cfg?.description || "5,000+ members. Get deal alerts, auction updates, and exclusive drops before anyone else.", memberCount: cfg?.memberCount || 5000, groupLink: cfg?.groupLink || "https://chat.whatsapp.com/" }));
|
|
203
|
+
return (_jsx(WhatsAppCommunitySection, { title: cleanTitle(cfg?.title) || "Join Our WhatsApp Community", descriptionHtml: cfg?.description || "5,000+ members. Get deal alerts, auction updates, and exclusive drops before anyone else.", memberCount: cfg?.memberCount || 5000, groupLink: cfg?.groupLink || "https://chat.whatsapp.com/" }));
|
|
194
204
|
}
|
|
195
205
|
case "faq": {
|
|
196
206
|
const cfg = config;
|
|
197
207
|
if (!cfg?.showOnHomepage) {
|
|
198
208
|
return null;
|
|
199
209
|
}
|
|
200
|
-
return (_jsx(FAQSection, { title: cfg?.title || "Frequently Asked Questions", tabs: [], activeTab: "", items: faqItems, viewMoreHref: ROUTES.PUBLIC.FAQS, viewMoreLabel: "View all FAQs \u2192" }));
|
|
210
|
+
return (_jsx(FAQSection, { title: cleanTitle(cfg?.title) || "Frequently Asked Questions", tabs: [], activeTab: "", items: faqItems, viewMoreHref: ROUTES.PUBLIC.FAQS, viewMoreLabel: "View all FAQs \u2192" }));
|
|
201
211
|
}
|
|
202
212
|
case "blog-articles": {
|
|
203
213
|
const cfg = config;
|
|
204
|
-
return (_jsx(BlogArticlesSection, { title: cfg?.title || "From Our Blog", viewMoreHref: ROUTES.BLOG.LIST, viewMoreLabel: "View all posts \u2192" }));
|
|
214
|
+
return (_jsx(BlogArticlesSection, { title: cleanTitle(cfg?.title) || "From Our Blog", viewMoreHref: ROUTES.BLOG.LIST, viewMoreLabel: "View all posts \u2192" }));
|
|
205
215
|
}
|
|
206
216
|
case "newsletter": {
|
|
207
217
|
const cfg = config;
|
|
208
|
-
return (_jsx(NewsletterSection, { title: cfg?.title || "Stay Updated", subtitle: cfg?.description || "Get the latest drops, auctions, and deals in your inbox.", renderForm: () => newsletterFormSlot ?? null }));
|
|
218
|
+
return (_jsx(NewsletterSection, { title: cleanTitle(cfg?.title) || "Stay Updated", subtitle: cfg?.description || "Get the latest drops, auctions, and deals in your inbox.", renderForm: () => newsletterFormSlot ?? null }));
|
|
209
219
|
}
|
|
210
220
|
case "brands": {
|
|
211
221
|
const cfg = config;
|
|
212
|
-
return (_jsx(BrandsSection, { title: cfg?.title || "Top Brands", subtitle: cfg?.subtitle, limit: cfg?.maxBrands || 12, viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All brands \u2192" }));
|
|
222
|
+
return (_jsx(BrandsSection, { title: cleanTitle(cfg?.title) || "Top Brands", subtitle: cfg?.subtitle, limit: cfg?.maxBrands || 12, viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All brands \u2192" }));
|
|
213
223
|
}
|
|
214
224
|
default:
|
|
215
225
|
return null;
|
|
@@ -248,5 +258,5 @@ export async function MarketplaceHomepageView({ adSlots, newsletterFormSlot, } =
|
|
|
248
258
|
answer: typeof faq.answer === "string" ? faq.answer : faq.answer.text,
|
|
249
259
|
}));
|
|
250
260
|
// Render with dynamic sections from DB
|
|
251
|
-
return (_jsxs(Main, { children: [showAnnouncement ? _jsx(AnnouncementBar, { message: announcementMessage }) : null,
|
|
261
|
+
return (_jsxs(Main, { children: [showAnnouncement ? _jsx(AnnouncementBar, { message: announcementMessage }) : null, orderedSections.map((section) => renderSection(section, adSlots, newsletterFormSlot ?? null, faqItems, slides))] }));
|
|
252
262
|
}
|
|
@@ -8,7 +8,7 @@ function CarouselSkeleton({ count }) {
|
|
|
8
8
|
return (_jsx("div", { className: "flex gap-4 overflow-hidden px-4", "data-section": "sectioncarousel-div-349", children: Array.from({ length: count }).map((_, i) => (_jsxs("div", { className: "flex-none min-w-[clamp(150px,18vw,260px)] max-w-[clamp(240px,36vw,380px)] h-[clamp(180px,26vh,260px)] space-y-2", "data-section": "sectioncarousel-div-350", children: [_jsx("div", { className: `aspect-square rounded-xl ${skeleton.image}` }), _jsx("div", { className: `${skeleton.text} w-3/4` }), _jsx("div", { className: `${skeleton.text} w-1/2` })] }, i))) }));
|
|
9
9
|
}
|
|
10
10
|
// --- Component ---------------------------------------------------------------
|
|
11
|
-
export function SectionCarousel({ title, description, headingVariant = "
|
|
11
|
+
export function SectionCarousel({ title, description, headingVariant = "editorial", pillLabel, backgroundImage, viewMoreHref, viewMoreLabel = "View all →", items, renderItem, perView = THEME_CONSTANTS.carousel.perView.standard, gap = 16, autoScroll = false, autoScrollInterval = 3500, keyExtractor, rows = 1, className = "", isLoading = false, skeletonCount = 4, lightText, showPeek = false, minItemWidth = 220, }) {
|
|
12
12
|
const hasBg = Boolean(backgroundImage);
|
|
13
13
|
const useLightText = lightText ?? hasBg;
|
|
14
14
|
const { themed, flex } = THEME_CONSTANTS;
|
|
@@ -39,7 +39,7 @@ export function SectionCarousel({ title, description, headingVariant = "default"
|
|
|
39
39
|
- show fade edges for visual affordance
|
|
40
40
|
- hide native scrollbar for cleaner appearance
|
|
41
41
|
*/
|
|
42
|
-
showArrows: true, snapToItems: true, showFadeEdges: true, showScrollbar: false, pauseOnHover: true })), viewMoreHref && !isLoading && (_jsx("div", { className: "mt-6
|
|
43
|
-
? "
|
|
44
|
-
: "
|
|
42
|
+
showArrows: true, snapToItems: true, showFadeEdges: true, showScrollbar: false, pauseOnHover: true })), viewMoreHref && !isLoading && (_jsx("div", { className: "mt-6 flex justify-center", "data-section": "sectioncarousel-div-356", children: _jsx(TextLink, { href: viewMoreHref, className: `inline-flex items-center gap-1.5 rounded-lg border px-6 py-2.5 text-sm font-medium transition-colors ${useLightText
|
|
43
|
+
? "border-white/40 text-white hover:bg-white/10"
|
|
44
|
+
: "border-zinc-200 dark:border-zinc-700 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-zinc-800"}`, children: viewMoreLabel }) }))] })] }));
|
|
45
45
|
}
|
|
@@ -10,12 +10,12 @@ function CategoryChip({ category }) {
|
|
|
10
10
|
const coverImage = category.display?.coverImage;
|
|
11
11
|
const initial = category.name[0]?.toUpperCase() ?? "?";
|
|
12
12
|
const productCount = category.metrics?.productCount ?? 0;
|
|
13
|
-
return (_jsxs(Link, { href: ROUTES.PUBLIC.CATEGORY_DETAIL(category.slug), className: "group flex
|
|
13
|
+
return (_jsxs(Link, { href: ROUTES.PUBLIC.CATEGORY_DETAIL(category.slug), className: "group flex w-full min-h-[220px] flex-col overflow-hidden rounded-xl border border-zinc-200 bg-white shadow-sm transition-all hover:border-primary-300 hover:shadow-md dark:border-slate-700 dark:bg-slate-900 dark:hover:border-primary-600", children: [coverImage ? (_jsx("div", { className: "aspect-video w-full overflow-hidden bg-zinc-100 dark:bg-slate-800", "data-section": "shopbycategorysection-div-362", children: _jsx("img", { src: coverImage, alt: category.name, className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" }) })) : (_jsx("div", { className: "aspect-video w-full bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-slate-800 dark:to-slate-700" })), _jsxs("div", { className: "flex flex-1 flex-col p-3 text-left", "data-section": "shopbycategorysection-div-363", children: [_jsx("div", { className: "mb-2 flex h-9 w-9 items-center justify-center rounded-lg bg-primary-100 text-sm font-bold text-primary-700 dark:bg-primary-900 dark:text-primary-300", "data-section": "shopbycategorysection-div-364", children: iconSrc ? (_jsx("img", { src: iconSrc, alt: "", className: "h-6 w-6 rounded object-cover", "aria-hidden": "true" })) : (initial) }), _jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-semibold text-zinc-800 dark:text-zinc-200`, children: category.name }), _jsxs(Text, { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: [productCount.toLocaleString(), " items"] }), _jsx(Text, { className: "mt-auto pt-3 text-xs font-medium text-primary dark:text-primary-400", children: "Browse category \u2192" })] })] }));
|
|
14
14
|
}
|
|
15
15
|
export function ShopByCategorySection({ title = "Shop by Category", subtitle, limit = 12, viewMoreHref, viewMoreLabel = "View all categories →", className = "", }) {
|
|
16
16
|
const { themed } = THEME_CONSTANTS;
|
|
17
17
|
const { data: categories = [], isLoading } = useTopCategories(limit);
|
|
18
18
|
if (!isLoading && categories.length === 0)
|
|
19
19
|
return null;
|
|
20
|
-
return (_jsx(Section, { className: `py-12 px-4 ${themed.bgSecondary} ${className}`, children: _jsxs("div", { className: "mx-auto max-w-7xl", "data-section": "shopbycategorysection-div-365", children: [_jsxs("div", { className: "mb-6 text-center", "data-section": "shopbycategorysection-div-366", children: [_jsx(Heading, { level: 2, className: `text-2xl font-bold md:text-3xl ${themed.textPrimary}`, children: title }), subtitle && (_jsx(Text, { variant: "secondary", className: "mt-1 text-sm", children: subtitle }))] }), isLoading ? (_jsx("div", { className: "flex gap-3 overflow-hidden px-1", "data-section": "shopbycategorysection-div-367", children: Array.from({ length: 6 }).map((_, i) => (_jsx("div", { className: "flex-none h-[104px] w-[108px] animate-pulse rounded-xl bg-zinc-200 dark:bg-slate-700" }, i))) })) : (_jsx(HorizontalScroller, { items: categories, renderItem: (cat) => _jsx(CategoryChip, { category: cat }), perView: THEME_CONSTANTS.carousel.perView.
|
|
20
|
+
return (_jsx(Section, { className: `py-12 px-4 ${themed.bgSecondary} ${className}`, children: _jsxs("div", { className: "mx-auto max-w-7xl", "data-section": "shopbycategorysection-div-365", children: [_jsxs("div", { className: "mb-6 text-center", "data-section": "shopbycategorysection-div-366", children: [_jsx(Heading, { level: 2, className: `text-2xl font-bold md:text-3xl ${themed.textPrimary}`, children: title }), subtitle && (_jsx(Text, { variant: "secondary", className: "mt-1 text-sm", children: subtitle }))] }), isLoading ? (_jsx("div", { className: "flex gap-3 overflow-hidden px-1", "data-section": "shopbycategorysection-div-367", children: Array.from({ length: 6 }).map((_, i) => (_jsx("div", { className: "flex-none h-[104px] w-[108px] animate-pulse rounded-xl bg-zinc-200 dark:bg-slate-700" }, i))) })) : (_jsx(HorizontalScroller, { items: categories, renderItem: (cat) => _jsx(CategoryChip, { category: cat }), perView: THEME_CONSTANTS.carousel.perView.standard, gap: 16, keyExtractor: (cat) => cat.id, autoScroll: true, autoScrollInterval: 3500, showArrows: true, snapToItems: true, showFadeEdges: true, showScrollbar: false, pauseOnHover: true })), viewMoreHref && !isLoading && (_jsx("div", { className: "mt-6 text-center", "data-section": "shopbycategorysection-div-368", children: _jsx(Link, { href: viewMoreHref, className: "inline-flex items-center gap-1 text-sm font-medium text-primary hover:text-primary/80", children: viewMoreLabel }) }))] }) }));
|
|
21
21
|
}
|
|
@@ -245,7 +245,7 @@ export interface BannerSectionConfig {
|
|
|
245
245
|
clickable: boolean;
|
|
246
246
|
clickLink?: string;
|
|
247
247
|
}
|
|
248
|
-
export type SectionType = "welcome" | "stats" | "trust-indicators" | "categories" | "brands" | "products" | "pre-orders" | "auctions" | "banner" | "features" | "reviews" | "whatsapp-community" | "faq" | "blog-articles" | "newsletter" | "stores" | "events";
|
|
248
|
+
export type SectionType = "welcome" | "carousel" | "stats" | "trust-indicators" | "categories" | "brands" | "products" | "pre-orders" | "auctions" | "banner" | "features" | "reviews" | "whatsapp-community" | "faq" | "blog-articles" | "newsletter" | "stores" | "events";
|
|
249
249
|
export type SectionConfig = WelcomeSectionConfig | StatsSectionConfig | TrustIndicatorsSectionConfig | CategoriesSectionConfig | BrandsSectionConfig | ProductsSectionConfig | PreOrdersSectionConfig | AuctionsSectionConfig | BannerSectionConfig | FeaturesSectionConfig | ReviewsSectionConfig | WhatsAppCommunitySectionConfig | FAQSectionConfig | BlogArticlesSectionConfig | NewsletterSectionConfig | StoresSectionConfig | EventsSectionConfig;
|
|
250
250
|
export interface HomepageSectionDocument {
|
|
251
251
|
id: string;
|
|
@@ -51,7 +51,9 @@ export interface AppLayoutShellProps {
|
|
|
51
51
|
onLogout?: () => void;
|
|
52
52
|
/** Href for the admin dashboard — shown in sidebar Dashboard section when user.role is admin. */
|
|
53
53
|
adminHref?: string;
|
|
54
|
-
/** Href for the
|
|
54
|
+
/** Href for the store management dashboard — shown in sidebar Dashboard section when user.role is admin or seller. */
|
|
55
|
+
storeHref?: string;
|
|
56
|
+
/** @deprecated Use storeHref */
|
|
55
57
|
sellerHref?: string;
|
|
56
58
|
/** Href for the user orders page — auto-generates PROFILE section in sidebar. */
|
|
57
59
|
userOrdersHref?: string;
|
|
@@ -72,6 +74,8 @@ export interface AppLayoutShellProps {
|
|
|
72
74
|
settings?: string;
|
|
73
75
|
dashboardSectionTitle?: string;
|
|
74
76
|
adminDashboard?: string;
|
|
77
|
+
storeDashboard?: string;
|
|
78
|
+
/** @deprecated Use storeDashboard */
|
|
75
79
|
sellerDashboard?: string;
|
|
76
80
|
logout?: string;
|
|
77
81
|
};
|
|
@@ -95,4 +99,4 @@ export interface AppLayoutShellProps {
|
|
|
95
99
|
};
|
|
96
100
|
};
|
|
97
101
|
}
|
|
98
|
-
export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, logoHref, promotionsHref, cartHref, profileHref, loginHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, lightBackground, darkBackground, }: AppLayoutShellProps): import("react/jsx-runtime").JSX.Element;
|
|
102
|
+
export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, logoHref, promotionsHref, cartHref, profileHref, loginHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, lightBackground, darkBackground, }: AppLayoutShellProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -33,7 +33,7 @@ function CollapsibleSidebarSection({ section, navItemClass, }) {
|
|
|
33
33
|
}
|
|
34
34
|
return (_jsxs(Div, { className: "space-y-0.5", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-1 py-1 text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-300 transition-colors", children: [_jsx("span", { children: section.title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && (_jsx(Ul, { className: "space-y-0.5", children: section.items.map((item) => (_jsx(Li, { children: _jsxs(TextLink, { href: item.href, variant: "none", className: navItemClass, children: [item.icon && (_jsx("span", { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label] }) }, `${item.href}-${item.label}`))) }))] }));
|
|
35
35
|
}
|
|
36
|
-
export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, logoHref, promotionsHref, cartHref, profileHref, loginHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
|
|
36
|
+
export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, logoHref, promotionsHref, cartHref, profileHref, loginHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
|
|
37
37
|
const [queryClient] = useState(() => new QueryClient());
|
|
38
38
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
39
39
|
const [searchOpen, setSearchOpen] = useState(false);
|
|
@@ -60,6 +60,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
60
60
|
const isAuthenticated = !!user;
|
|
61
61
|
const role = user?.role ?? "user";
|
|
62
62
|
const isAdminOrSeller = role === "admin" || role === "seller";
|
|
63
|
+
const resolvedStoreHref = storeHref ?? sellerHref;
|
|
63
64
|
// Labels with defaults
|
|
64
65
|
const labels = {
|
|
65
66
|
sectionTitle: sidebarProfileLabels?.sectionTitle ?? "Profile",
|
|
@@ -69,7 +70,9 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
69
70
|
settings: sidebarProfileLabels?.settings ?? "Settings",
|
|
70
71
|
dashboardSectionTitle: sidebarProfileLabels?.dashboardSectionTitle ?? "Dashboard",
|
|
71
72
|
adminDashboard: sidebarProfileLabels?.adminDashboard ?? "Admin Dashboard",
|
|
72
|
-
|
|
73
|
+
storeDashboard: sidebarProfileLabels?.storeDashboard ??
|
|
74
|
+
sidebarProfileLabels?.sellerDashboard ??
|
|
75
|
+
"Store Dashboard",
|
|
73
76
|
logout: sidebarProfileLabels?.logout ?? "Logout",
|
|
74
77
|
};
|
|
75
78
|
const sectionLabelClass = "px-1 text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400";
|
|
@@ -86,7 +89,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
86
89
|
action.variant === "outline"
|
|
87
90
|
? "border border-zinc-300 text-zinc-700 hover:bg-zinc-50 dark:border-slate-700 dark:text-zinc-100 dark:hover:bg-slate-800"
|
|
88
91
|
: "bg-primary !text-zinc-900 hover:bg-primary-500 dark:bg-secondary dark:!text-white dark:hover:bg-secondary-600 shadow-lg shadow-primary/20 dark:shadow-secondary/20",
|
|
89
|
-
].join(" "), children: action.label }, `${action.href}-${action.label}`))) })), isAuthenticated && (_jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: sectionLabelClass, children: labels.sectionTitle }), _jsxs(Ul, { className: "space-y-0.5", children: [_jsx(Li, { children: _jsx(TextLink, { href: profileHref, variant: "none", className: navItemClass, children: labels.profile }) }), userOrdersHref && (_jsx(Li, { children: _jsx(TextLink, { href: userOrdersHref, variant: "none", className: navItemClass, children: labels.orders }) })), userWishlistHref && (_jsx(Li, { children: _jsx(TextLink, { href: userWishlistHref, variant: "none", className: navItemClass, children: labels.wishlist }) })), userSettingsHref && (_jsx(Li, { children: _jsx(TextLink, { href: userSettingsHref, variant: "none", className: navItemClass, children: labels.settings }) }))] })] })), isAuthenticated && user?.stats && (_jsxs(Div, { className: "grid grid-cols-2 gap-2", children: [user.stats.totalOrders != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.totalOrders }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Orders" })] })), user.stats.reviewsCount != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.reviewsCount }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Reviews" })] })), user.stats.auctionsWon != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.auctionsWon }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Auctions Won" })] })), user.stats.itemsSold != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.itemsSold }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Items Sold" })] }))] })), isAuthenticated && isAdminOrSeller && (adminHref ||
|
|
92
|
+
].join(" "), children: action.label }, `${action.href}-${action.label}`))) })), isAuthenticated && (_jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: sectionLabelClass, children: labels.sectionTitle }), _jsxs(Ul, { className: "space-y-0.5", children: [_jsx(Li, { children: _jsx(TextLink, { href: profileHref, variant: "none", className: navItemClass, children: labels.profile }) }), userOrdersHref && (_jsx(Li, { children: _jsx(TextLink, { href: userOrdersHref, variant: "none", className: navItemClass, children: labels.orders }) })), userWishlistHref && (_jsx(Li, { children: _jsx(TextLink, { href: userWishlistHref, variant: "none", className: navItemClass, children: labels.wishlist }) })), userSettingsHref && (_jsx(Li, { children: _jsx(TextLink, { href: userSettingsHref, variant: "none", className: navItemClass, children: labels.settings }) }))] })] })), isAuthenticated && user?.stats && (_jsxs(Div, { className: "grid grid-cols-2 gap-2", children: [user.stats.totalOrders != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.totalOrders }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Orders" })] })), user.stats.reviewsCount != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.reviewsCount }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Reviews" })] })), user.stats.auctionsWon != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.auctionsWon }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Auctions Won" })] })), user.stats.itemsSold != null && (_jsxs(Div, { className: "rounded-lg bg-zinc-100 dark:bg-slate-800 px-3 py-2.5 text-center", children: [_jsx(Text, { className: "text-lg font-bold text-zinc-900 dark:text-zinc-100 leading-none", children: user.stats.itemsSold }), _jsx(Text, { className: "mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400", children: "Items Sold" })] }))] })), isAuthenticated && isAdminOrSeller && (adminHref || resolvedStoreHref) && (_jsxs(Div, { className: "space-y-1", children: [_jsx(Text, { className: sectionLabelClass, children: labels.dashboardSectionTitle }), _jsxs(Ul, { className: "space-y-0.5", children: [adminHref && role === "admin" && (_jsx(Li, { children: _jsx(TextLink, { href: adminHref, variant: "none", className: navItemClass, children: labels.adminDashboard }) })), resolvedStoreHref && isAdminOrSeller && (_jsx(Li, { children: _jsx(TextLink, { href: resolvedStoreHref, variant: "none", className: navItemClass, children: labels.storeDashboard }) }))] })] })), normalizedSections.map((section, sectionIndex) => (_jsx(CollapsibleSidebarSection, { section: section, navItemClass: navItemClass }, `sidebar-section-${sectionIndex}`))), (sidebarLocaleSlot ||
|
|
90
93
|
showThemeToggleInSidebar ||
|
|
91
94
|
(isAuthenticated && onLogout)) && (_jsxs(Div, { className: "border-t border-zinc-200 pt-4 space-y-3 dark:border-slate-800", children: [sidebarLocaleSlot, showThemeToggleInSidebar && (_jsxs("button", { type: "button", onClick: toggleTheme, className: "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm text-zinc-700 transition-colors hover:bg-primary-50 hover:text-primary-800 dark:text-zinc-300 dark:hover:bg-slate-800 dark:hover:text-secondary-300", children: [_jsx("span", { "aria-hidden": "true", children: theme === "dark" ? "☀️" : "🌙" }), theme === "dark" ? "Light mode" : "Dark mode"] })), isAuthenticated && onLogout && (_jsx("button", { type: "button", onClick: () => {
|
|
92
95
|
onLogout();
|
|
@@ -102,6 +105,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
102
105
|
userWishlistHref,
|
|
103
106
|
userSettingsHref,
|
|
104
107
|
adminHref,
|
|
108
|
+
storeHref,
|
|
105
109
|
sellerHref,
|
|
106
110
|
sidebarLocaleSlot,
|
|
107
111
|
showThemeToggleInSidebar,
|
|
@@ -5,6 +5,7 @@ export interface MarketplacePreorderCardLabels {
|
|
|
5
5
|
preOrderBadge?: string;
|
|
6
6
|
featuredBadge?: string;
|
|
7
7
|
reserveNow?: string;
|
|
8
|
+
addToCart?: string;
|
|
8
9
|
addToWishlist?: string;
|
|
9
10
|
removeFromWishlist?: string;
|
|
10
11
|
}
|
|
@@ -19,7 +20,8 @@ export interface MarketplacePreorderCardProps {
|
|
|
19
20
|
href?: string;
|
|
20
21
|
hrefBuilder?: (product: MarketplacePreorderCardData) => string;
|
|
21
22
|
onNavigate?: (href: string) => void;
|
|
23
|
+
onAddToCart?: (product: MarketplacePreorderCardData) => void;
|
|
22
24
|
wishlistActions?: WishlistToggleActions;
|
|
23
25
|
labels?: MarketplacePreorderCardLabels;
|
|
24
26
|
}
|
|
25
|
-
export declare function MarketplacePreorderCard({ product, className, variant, selectable, isSelected, onSelect, inWishlist: initialInWishlist, href, hrefBuilder, onNavigate, wishlistActions, labels, }: MarketplacePreorderCardProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
export declare function MarketplacePreorderCard({ product, className, variant, selectable, isSelected, onSelect, inWishlist: initialInWishlist, href, hrefBuilder, onNavigate, onAddToCart, wishlistActions, labels, }: MarketplacePreorderCardProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -15,6 +15,7 @@ const DEFAULT_LABELS = {
|
|
|
15
15
|
preOrderBadge: "Pre-order",
|
|
16
16
|
featuredBadge: "Featured",
|
|
17
17
|
reserveNow: "Reserve now",
|
|
18
|
+
addToCart: "Add to Cart",
|
|
18
19
|
addToWishlist: "Add to wishlist",
|
|
19
20
|
removeFromWishlist: "Remove from wishlist",
|
|
20
21
|
};
|
|
@@ -25,7 +26,7 @@ function resolveHref(product, href, hrefBuilder) {
|
|
|
25
26
|
return hrefBuilder(product);
|
|
26
27
|
return ROUTES.PUBLIC.PRE_ORDER_DETAIL(product.id);
|
|
27
28
|
}
|
|
28
|
-
export function MarketplacePreorderCard({ product, className = "", variant = "grid", selectable = false, isSelected = false, onSelect, inWishlist: initialInWishlist = false, href, hrefBuilder, onNavigate, wishlistActions, labels, }) {
|
|
29
|
+
export function MarketplacePreorderCard({ product, className = "", variant = "grid", selectable = false, isSelected = false, onSelect, inWishlist: initialInWishlist = false, href, hrefBuilder, onNavigate, onAddToCart, wishlistActions, labels, }) {
|
|
29
30
|
const router = useRouter();
|
|
30
31
|
const mergedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
31
32
|
const detailHref = resolveHref(product, href, hrefBuilder);
|
|
@@ -51,7 +52,10 @@ export function MarketplacePreorderCard({ product, className = "", variant = "gr
|
|
|
51
52
|
return (_jsxs(BaseListingCard, { isSelected: isSelected, variant: variant, className: className, children: [_jsxs(BaseListingCard.Hero, { aspect: "square", variant: variant, children: [_jsx(TextLink, { href: detailHref, className: "absolute inset-0 block", children: _jsx(MediaImage, { src: product.mainImage, alt: product.title, size: "card", fallback: "Product image" }) }), _jsxs(Div, { className: "absolute right-2 top-2 flex flex-col items-end gap-1", children: [_jsx(Span, { className: "inline-flex items-center rounded-full bg-cobalt px-2 py-0.5 text-xs font-medium text-white", children: mergedLabels.preOrderBadge }), product.featured && (_jsx(Span, { className: "inline-flex items-center rounded-full bg-amber-500 px-2 py-0.5 text-xs font-medium text-white", children: mergedLabels.featuredBadge }))] }), selectable && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (event) => {
|
|
52
53
|
event.stopPropagation();
|
|
53
54
|
onSelect?.(product.id, !isSelected);
|
|
54
|
-
} }))] }), _jsxs(BaseListingCard.Info, { variant: variant, children: [_jsx(TextLink, { href: detailHref, children: _jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-medium text-zinc-900`, children: product.title }) }), product.description ? (_jsx(RichText, { html: normalizeRichTextHtml(product.description), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: `${THEME_CONSTANTS.utilities.textClamp2} text-xs text-zinc-500` })) : null, _jsxs(Row, { justify: "between", className: "mt-1 gap-2", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-900", children: formatCurrency(product.price, getDefaultCurrency()) }), shipDate && _jsx(PreorderBadge, { shipDate: shipDate })] }), _jsxs(Row, { justify: "between", className: "mt-2 gap-2", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", className: "flex-1 text-xs", onClick: handleNavigate, children: mergedLabels.reserveNow }),
|
|
55
|
+
} }))] }), _jsxs(BaseListingCard.Info, { variant: variant, children: [_jsx(TextLink, { href: detailHref, children: _jsx(Text, { className: `${THEME_CONSTANTS.utilities.textClamp2} text-sm font-medium text-zinc-900`, children: product.title }) }), product.description ? (_jsx(RichText, { html: normalizeRichTextHtml(product.description), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: `${THEME_CONSTANTS.utilities.textClamp2} text-xs text-zinc-500` })) : null, _jsxs(Row, { justify: "between", className: "mt-1 gap-2", children: [_jsx(Text, { className: "text-sm font-semibold text-zinc-900", children: formatCurrency(product.price, getDefaultCurrency()) }), shipDate && _jsx(PreorderBadge, { shipDate: shipDate })] }), _jsxs(Row, { justify: "between", className: "mt-2 gap-2", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", className: "flex-1 text-xs", onClick: handleNavigate, children: mergedLabels.reserveNow }), onAddToCart ? (_jsx(Button, { type: "button", variant: "outline", size: "sm", className: "flex-1 text-xs", onClick: (e) => {
|
|
56
|
+
e.stopPropagation();
|
|
57
|
+
onAddToCart(product);
|
|
58
|
+
}, children: mergedLabels.addToCart })) : null, wishlistActions ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: `text-base ${inWishlist ? "text-primary" : "text-zinc-500"}`, onClick: handleWishlist, "aria-label": inWishlist
|
|
55
59
|
? mergedLabels.removeFromWishlist
|
|
56
60
|
: mergedLabels.addToWishlist, children: inWishlist ? "♥" : "♡" })) : null] })] })] }));
|
|
57
61
|
}
|
|
@@ -3,8 +3,25 @@ import Link from "next/link";
|
|
|
3
3
|
import { productRepository } from "../../../repositories";
|
|
4
4
|
import { ROUTES } from "../../../next";
|
|
5
5
|
import { getDefaultCurrency } from "../../../core/baseline-resolver";
|
|
6
|
-
import {
|
|
6
|
+
import { formatCurrency } from "../../../utils/number.formatter";
|
|
7
|
+
import { normalizeRichTextHtml } from "../../../utils/string.formatter";
|
|
8
|
+
import { safeDisplayName } from "../../../security";
|
|
9
|
+
import { Button, Container, Div, Heading, Main, RichText, Row, Section, Span, Stack, Text, } from "../../../ui";
|
|
7
10
|
import { PreOrderDetailView } from "../../products/components/PreOrderDetailView";
|
|
11
|
+
import { BuyBar } from "../../products/components/BuyBar";
|
|
12
|
+
import { ProductGalleryClient } from "../../products/components/ProductGalleryClient";
|
|
13
|
+
import { ProductFeatureBadges } from "../../products/components/ProductFeatureBadges";
|
|
14
|
+
function toDescriptionHtml(raw) {
|
|
15
|
+
if (!raw)
|
|
16
|
+
return "";
|
|
17
|
+
const s = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
18
|
+
return normalizeRichTextHtml(s);
|
|
19
|
+
}
|
|
20
|
+
const PRODUCTION_STATUS_LABELS = {
|
|
21
|
+
upcoming: "Coming Soon",
|
|
22
|
+
in_production: "In Production",
|
|
23
|
+
ready_to_ship: "Ready to Ship",
|
|
24
|
+
};
|
|
8
25
|
export async function PreOrderDetailPageView({ id }) {
|
|
9
26
|
const product = await productRepository.findByIdOrSlug(id).catch(() => undefined);
|
|
10
27
|
if (!product) {
|
|
@@ -12,16 +29,67 @@ export async function PreOrderDetailPageView({ id }) {
|
|
|
12
29
|
}
|
|
13
30
|
const p = product;
|
|
14
31
|
const currency = p.currency || getDefaultCurrency();
|
|
15
|
-
const
|
|
16
|
-
|
|
32
|
+
const title = String(p.title ?? p.name ?? "Pre-Order Item");
|
|
33
|
+
const price = typeof p.price === "number" ? p.price : null;
|
|
34
|
+
const images = Array.isArray(p.images)
|
|
35
|
+
? p.images
|
|
36
|
+
: typeof p.mainImage === "string"
|
|
37
|
+
? [p.mainImage]
|
|
38
|
+
: [];
|
|
39
|
+
const reservedCount = typeof p.preOrderCurrentCount === "number"
|
|
40
|
+
? p.preOrderCurrentCount
|
|
41
|
+
: typeof p.reservedCount === "number"
|
|
42
|
+
? p.reservedCount
|
|
43
|
+
: 0;
|
|
44
|
+
const reserveTarget = typeof p.preOrderMaxQuantity === "number"
|
|
45
|
+
? p.preOrderMaxQuantity
|
|
46
|
+
: typeof p.preOrderTarget === "number"
|
|
47
|
+
? p.preOrderTarget
|
|
48
|
+
: typeof p.reserveTarget === "number"
|
|
49
|
+
? p.reserveTarget
|
|
50
|
+
: 0;
|
|
51
|
+
const progressPct = reserveTarget > 0
|
|
52
|
+
? Math.min(100, Math.round((reservedCount / reserveTarget) * 100))
|
|
53
|
+
: 0;
|
|
54
|
+
const depositPercent = typeof p.preOrderDepositPercent === "number" ? p.preOrderDepositPercent : null;
|
|
55
|
+
const depositAmount = typeof p.preOrderDepositAmount === "number"
|
|
56
|
+
? p.preOrderDepositAmount
|
|
57
|
+
: price !== null && depositPercent !== null
|
|
58
|
+
? Math.round((price * depositPercent) / 100)
|
|
59
|
+
: null;
|
|
60
|
+
const deliveryDate = p.preOrderDeliveryDate
|
|
61
|
+
? new Date(p.preOrderDeliveryDate)
|
|
62
|
+
: null;
|
|
63
|
+
const productionStatus = typeof p.preOrderProductionStatus === "string"
|
|
64
|
+
? p.preOrderProductionStatus
|
|
17
65
|
: null;
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
66
|
+
const isCancellable = p.preOrderCancellable === true;
|
|
67
|
+
const condition = typeof p.condition === "string" ? p.condition : null;
|
|
68
|
+
const featured = p.featured === true;
|
|
69
|
+
const shippingPaidBy = p.shippingPaidBy;
|
|
70
|
+
const freeShipping = shippingPaidBy === "seller";
|
|
71
|
+
const sellerName = typeof p.sellerName === "string" ? p.sellerName : null;
|
|
72
|
+
const safeSeller = sellerName ? safeDisplayName(sellerName, "") : null;
|
|
73
|
+
const features = Array.isArray(p.features) ? p.features : [];
|
|
74
|
+
const tags = Array.isArray(p.tags) ? p.tags : [];
|
|
75
|
+
const descriptionHtml = toDescriptionHtml(p.description);
|
|
76
|
+
return (_jsx(Main, { children: _jsxs(Container, { size: "xl", className: "px-4 py-6", children: [_jsxs("nav", { "aria-label": "Breadcrumb", className: "mb-4 flex items-center gap-1.5 text-xs text-zinc-500 dark:text-zinc-400 flex-wrap", children: [_jsx(Link, { href: String(ROUTES.HOME), className: "hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.PRE_ORDERS), className: "hover:text-primary-600 transition-colors", children: "Pre-Orders" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "text-zinc-700 dark:text-zinc-300 truncate max-w-[200px]", children: title })] }), _jsx(PreOrderDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, productName: title })), renderInfo: () => (_jsxs(Stack, { gap: "sm", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", className: "mb-1.5 flex-wrap", children: [_jsx(Span, { className: "inline-block rounded-full bg-indigo-100 dark:bg-indigo-900/30 px-2.5 py-0.5 text-xs font-semibold text-indigo-700 dark:text-indigo-300", children: "Pre-Order" }), productionStatus && (_jsx(Span, { className: "inline-block rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-0.5 text-xs font-medium text-zinc-600 dark:text-zinc-300", children: PRODUCTION_STATUS_LABELS[productionStatus] ?? productionStatus }))] }), _jsx(Heading, { level: 1, className: "text-xl font-bold leading-snug text-zinc-900 dark:text-zinc-50 sm:text-2xl", children: title })] }), price !== null && (_jsx(Span, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: formatCurrency(price, currency) })), deliveryDate && (_jsxs(Row, { align: "center", gap: "xs", className: "text-sm text-zinc-600 dark:text-zinc-400", children: [_jsx(Span, { children: "\uD83D\uDCC5" }), _jsx(Span, { children: "Estimated delivery:" }), _jsx(Span, { className: "font-medium", children: deliveryDate.toLocaleDateString(undefined, { year: "numeric", month: "long" }) })] })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, condition: condition ?? undefined, returnable: isCancellable, labels: {
|
|
77
|
+
featured: "Featured",
|
|
78
|
+
fasterDelivery: "Faster Delivery",
|
|
79
|
+
ratedSeller: "Rated Seller",
|
|
80
|
+
condition: "Condition",
|
|
81
|
+
conditionNew: "New",
|
|
82
|
+
conditionUsed: "Used",
|
|
83
|
+
conditionBroken: "For Parts",
|
|
84
|
+
conditionRefurbished: "Refurbished",
|
|
85
|
+
returnable: "Cancellable",
|
|
86
|
+
freeShipping: "Free Shipping",
|
|
87
|
+
codAvailable: "Cash on Delivery",
|
|
88
|
+
wishlistCount: (n) => `${n} wishlisted`,
|
|
89
|
+
categoryProductCount: (n, cat) => `${n} in ${cat}`,
|
|
90
|
+
} }), features.length > 0 && (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 px-4 py-3", children: [_jsx(Text, { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "About this product" }), _jsx("ul", { className: "space-y-1.5", children: features.map((f, i) => (_jsxs("li", { className: "flex items-start gap-2 text-sm text-zinc-700 dark:text-zinc-300", 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-sm leading-relaxed text-zinc-600 dark:text-zinc-400 line-clamp-4" })), safeSeller && (_jsxs(Row, { align: "center", gap: "xs", className: "border-t border-zinc-100 dark:border-zinc-800 pt-3", children: [_jsx(Span, { className: "text-xs text-zinc-500", children: "Sold by" }), _jsx(Span, { className: "text-xs font-medium text-zinc-700 dark:text-zinc-300", children: safeSeller })] }))] })), renderBuyBar: () => (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-5 space-y-4", children: [reserveTarget > 0 && (_jsxs(Div, { className: "space-y-2", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Text, { className: "text-xs text-zinc-500", children: [reservedCount, " of ", reserveTarget, " reserved"] }), _jsxs(Span, { className: "text-xs font-semibold text-primary-600 dark:text-primary-400", children: [progressPct, "%"] })] }), _jsx(Div, { className: "h-2 w-full overflow-hidden rounded-full bg-zinc-200 dark:bg-zinc-700", children: _jsx(Div, { className: "h-full rounded-full bg-primary transition-all", style: { width: `${progressPct}%` } }) })] })), price !== null && (_jsxs(Div, { children: [_jsx(Text, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: formatCurrency(price, currency) }), depositAmount !== null && (_jsxs(Text, { className: "mt-0.5 text-xs text-zinc-500", children: ["Reserve with ", formatCurrency(depositAmount, currency), depositPercent !== null ? ` (${depositPercent}% deposit)` : ""] }))] })), _jsxs(Stack, { gap: "sm", children: [_jsx(Button, { variant: "primary", size: "md", className: "w-full", children: "Reserve Now" }), isCancellable && (_jsx(Text, { className: "text-center text-xs text-zinc-500 dark:text-zinc-400", children: "\u2713 Free cancellation before production" }))] }), tags.length > 0 && (_jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { className: "rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-1 text-xs text-zinc-600 dark:text-zinc-300", children: tag }, tag))) }) })), _jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "sm", className: "justify-center text-center", children: [
|
|
91
|
+
{ icon: "🔒", label: "Secure\nPayment" },
|
|
92
|
+
{ icon: "📅", label: "Guaranteed\nDelivery" },
|
|
93
|
+
{ icon: "↩", label: "Free\nCancellation" },
|
|
94
|
+
].map(({ icon, label }) => (_jsxs(Div, { className: "flex flex-col items-center gap-1 text-xs text-zinc-500 dark:text-zinc-400 min-w-[60px]", children: [_jsx(Span, { className: "text-base", children: icon }), _jsx(Span, { className: "whitespace-pre-line leading-tight", children: label })] }, label))) }) })] })) }), _jsxs(BuyBar, { children: [price !== null && (_jsx(Span, { className: "mr-auto text-sm font-bold text-zinc-900 dark:text-zinc-50", children: formatCurrency(price, currency) })), _jsx(Button, { variant: "primary", size: "sm", className: "flex-1", children: "Reserve Now" })] })] }) }));
|
|
27
95
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FacetOption } from "../../filters/FilterFacetSection";
|
|
2
|
+
import type { UrlTable } from "../../filters/FilterPanel";
|
|
3
|
+
export interface PreOrderFiltersProps {
|
|
4
|
+
table: UrlTable;
|
|
5
|
+
storeOptions?: FacetOption[];
|
|
6
|
+
currencyPrefix?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function PreOrderFilters({ table, storeOptions, currencyPrefix, }: PreOrderFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslations } from "next-intl";
|
|
3
|
+
import { FilterFacetSection } from "../../filters/FilterFacetSection";
|
|
4
|
+
import { RangeFilter } from "../../filters/RangeFilter";
|
|
5
|
+
import { Div } from "../../../ui";
|
|
6
|
+
export function PreOrderFilters({ table, storeOptions = [], currencyPrefix = "", }) {
|
|
7
|
+
const t = useTranslations("filters");
|
|
8
|
+
const statusOptions = [
|
|
9
|
+
{ value: "upcoming", label: "Upcoming" },
|
|
10
|
+
{ value: "in_production", label: "In Production" },
|
|
11
|
+
{ value: "ready_to_ship", label: "Ready to Ship" },
|
|
12
|
+
{ value: "shipped", label: "Shipped" },
|
|
13
|
+
];
|
|
14
|
+
const selectedStatuses = table.get("preOrderStatus")
|
|
15
|
+
? table.get("preOrderStatus").split("|").filter(Boolean)
|
|
16
|
+
: [];
|
|
17
|
+
const selectedStores = table.get("storeId")
|
|
18
|
+
? table.get("storeId").split("|").filter(Boolean)
|
|
19
|
+
: [];
|
|
20
|
+
return (_jsxs(Div, { children: [_jsx(FilterFacetSection, { title: t("preOrderStatus"), options: statusOptions, selected: selectedStatuses, onChange: (vals) => table.set("preOrderStatus", vals[0] ?? ""), searchable: false, selectionMode: "single", defaultCollapsed: false }), _jsx(RangeFilter, { title: t("priceRange"), minValue: table.get("minPrice"), maxValue: table.get("maxPrice"), onMinChange: (v) => table.set("minPrice", v), onMaxChange: (v) => table.set("maxPrice", v), prefix: currencyPrefix, showSlider: true, minBound: 0, maxBound: 500000, step: 100, minPlaceholder: t("minPrice"), maxPlaceholder: t("maxPrice"), defaultCollapsed: false }), storeOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("store"), options: storeOptions, selected: selectedStores, onChange: (vals) => table.set("storeId", vals[0] ?? ""), searchable: storeOptions.length > 6, selectionMode: "single", defaultCollapsed: true })), _jsx(RangeFilter, { title: "Delivery Date Range", type: "date", minValue: table.get("dateFrom"), maxValue: table.get("dateTo"), onMinChange: (v) => table.set("dateFrom", v), onMaxChange: (v) => table.set("dateTo", v), minPlaceholder: t("minDate"), maxPlaceholder: t("maxDate"), defaultCollapsed: true })] }));
|
|
21
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export interface PreOrdersIndexListingProps {
|
|
2
2
|
initialData?: any;
|
|
3
|
+
categorySlug?: string;
|
|
3
4
|
}
|
|
4
|
-
export declare function PreOrdersIndexListing({ initialData }: PreOrdersIndexListingProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare function PreOrdersIndexListing({ initialData, categorySlug }: PreOrdersIndexListingProps): import("react/jsx-runtime").JSX.Element;
|