@mohasinac/appkit 3.9.0 → 3.9.1
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/features/admin/components/AdminSectionsView.js +4 -4
- package/dist/features/admin/components/sections/adminSectionsBuildParse.js +13 -5
- package/dist/features/admin/components/sections/adminSectionsTypes.d.ts +4 -0
- package/dist/features/admin/components/sections/adminSectionsTypes.js +9 -5
- package/dist/features/homepage/components/BrandsSection.d.ts +4 -1
- package/dist/features/homepage/components/BrandsSection.js +2 -2
- package/dist/features/homepage/components/CustomCardsSection.js +3 -3
- package/dist/features/homepage/components/CustomerReviewsSection.d.ts +4 -1
- package/dist/features/homepage/components/CustomerReviewsSection.js +2 -2
- package/dist/features/homepage/components/EventsSection.d.ts +4 -1
- package/dist/features/homepage/components/EventsSection.js +2 -2
- package/dist/features/homepage/components/FeaturedAuctionsSection.d.ts +2 -1
- package/dist/features/homepage/components/FeaturedAuctionsSection.js +2 -2
- package/dist/features/homepage/components/FeaturedPreOrdersSection.d.ts +2 -1
- package/dist/features/homepage/components/FeaturedPreOrdersSection.js +2 -2
- package/dist/features/homepage/components/FeaturedProductsSection.d.ts +2 -1
- package/dist/features/homepage/components/FeaturedProductsSection.js +2 -2
- package/dist/features/homepage/components/FeaturedStoresSection.d.ts +4 -1
- package/dist/features/homepage/components/FeaturedStoresSection.js +2 -2
- package/dist/features/homepage/components/HomepageCustomerReviewsSection.d.ts +4 -1
- package/dist/features/homepage/components/HomepageCustomerReviewsSection.js +2 -2
- package/dist/features/homepage/components/SectionCarousel.d.ts +3 -1
- package/dist/features/homepage/components/SectionCarousel.js +2 -2
- package/dist/features/homepage/components/ShopByCategorySection.d.ts +4 -1
- package/dist/features/homepage/components/ShopByCategorySection.js +2 -2
- package/dist/features/homepage/lib/section-renderer.js +9 -9
- package/dist/features/homepage/schemas/firestore.d.ts +8 -0
- package/dist/seed/homepage-sections-seed-data.js +10 -0
- package/dist/ui/components/HorizontalScroller.js +3 -0
- package/package.json +1 -1
|
@@ -703,7 +703,7 @@ export function AdminSectionsView({ children }) {
|
|
|
703
703
|
return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Reviews Section Builder" }), _jsx(Input, { label: "Section title", value: reviewsBuilder.title, onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, title: e.target.value })) }), _jsx(Select, { label: "Reviews source", value: reviewsBuilder.source, onValueChange: (v) => setReviewsBuilder((prev) => ({ ...prev, source: v })), options: [
|
|
704
704
|
{ label: "Platform reviews (LetItRip)", value: "platform" },
|
|
705
705
|
{ label: "Google Business Reviews", value: "google" },
|
|
706
|
-
] }), reviewsBuilder.source === "google" ? (_jsx(Input, { label: "Google Place ID", value: reviewsBuilder.placeId, onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, placeId: e.target.value })), placeholder: "ChIJ...", helperText: "Find your Place ID at maps.google.com/placesinventory" })) : null, _jsx(Select, { label: "Max reviews", value: String(reviewsBuilder.maxReviews), onValueChange: (v) => setReviewsBuilder((prev) => ({ ...prev, maxReviews: Number(v) })), options: [{ label: "5", value: "5" }, { label: "10", value: "10" }, { label: "20", value: "20" }] }), _jsx(Select, { label: "Items per view", value: String(reviewsBuilder.itemsPerView), onValueChange: (v) => setReviewsBuilder((prev) => ({ ...prev, itemsPerView: Number(v) })), options: [{ label: "1", value: "1" }, { label: "2", value: "2" }, { label: "3", value: "3" }] }), _jsx(Checkbox, { checked: reviewsBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(reviewsBuilder.scrollInterval), onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, scrollInterval: Math.max(1000, Number(e.target.value) || 1000) })) })] }));
|
|
706
|
+
] }), reviewsBuilder.source === "google" ? (_jsx(Input, { label: "Google Place ID", value: reviewsBuilder.placeId, onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, placeId: e.target.value })), placeholder: "ChIJ...", helperText: "Find your Place ID at maps.google.com/placesinventory" })) : null, _jsx(Select, { label: "Max reviews", value: String(reviewsBuilder.maxReviews), onValueChange: (v) => setReviewsBuilder((prev) => ({ ...prev, maxReviews: Number(v) })), options: [{ label: "5", value: "5" }, { label: "10", value: "10" }, { label: "20", value: "20" }] }), _jsx(Select, { label: "Items per view", value: String(reviewsBuilder.itemsPerView), onValueChange: (v) => setReviewsBuilder((prev) => ({ ...prev, itemsPerView: Number(v) })), options: [{ label: "1", value: "1" }, { label: "2", value: "2" }, { label: "3", value: "3" }] }), _jsx(Checkbox, { checked: reviewsBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(reviewsBuilder.scrollInterval), onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, scrollInterval: Math.max(1000, Number(e.target.value) || 1000) })) }), _jsx(Checkbox, { checked: reviewsBuilder.loop, label: "Loop carousel", onChange: (e) => setReviewsBuilder((prev) => ({ ...prev, loop: e.target.checked })) })] }));
|
|
707
707
|
}
|
|
708
708
|
function renderWhatsAppBuilder() {
|
|
709
709
|
return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "WhatsApp Community Builder" }), _jsx(Input, { label: "Title", value: whatsappBuilder.title, onChange: (e) => setWhatsappBuilder((prev) => ({ ...prev, title: e.target.value })) }), _jsx(Textarea, { label: "Description", value: whatsappBuilder.description, onChange: (e) => setWhatsappBuilder((prev) => ({ ...prev, description: e.target.value })), rows: 2 }), _jsx(Input, { label: "WhatsApp group link", type: "url", value: whatsappBuilder.groupLink, onChange: (e) => setWhatsappBuilder((prev) => ({ ...prev, groupLink: e.target.value })), placeholder: "https://chat.whatsapp.com/..." }), _jsx(Input, { label: "Member count", type: "number", min: 0, value: String(whatsappBuilder.memberCount), onChange: (e) => setWhatsappBuilder((prev) => ({ ...prev, memberCount: Math.max(0, Number(e.target.value) || 0) })) }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Benefits" }), whatsappBuilder.benefits.map((benefit, index) => (_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Input, { value: benefit, onChange: (e) => setWhatsappBuilder((prev) => { const next = [...prev.benefits]; next[index] = e.target.value; return { ...prev, benefits: next }; }), className: "flex-1" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setWhatsappBuilder((prev) => ({ ...prev, benefits: prev.benefits.filter((_, i) => i !== index) })), children: "\u2715" })] }, `wb-${index}`))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setWhatsappBuilder((prev) => ({ ...prev, benefits: [...prev.benefits, ""] })), children: "+ Add benefit" })] }), _jsx(Input, { label: "Button text", value: whatsappBuilder.buttonText, onChange: (e) => setWhatsappBuilder((prev) => ({ ...prev, buttonText: e.target.value })) }), _jsx(Input, { label: "Testimonial (optional)", value: whatsappBuilder.testimonial, onChange: (e) => setWhatsappBuilder((prev) => ({ ...prev, testimonial: e.target.value })) })] }));
|
|
@@ -715,10 +715,10 @@ export function AdminSectionsView({ children }) {
|
|
|
715
715
|
return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Trust Indicators Builder" }), _jsx(Input, { label: "Section title", value: trustIndicatorsBuilder.title, onChange: (e) => setTrustIndicatorsBuilder((prev) => ({ ...prev, title: e.target.value })) }), trustIndicatorsBuilder.indicators.map((ind, index) => (_jsxs(Stack, { className: `${__P.p3}`, gap: "sm", rounded: "md", border: "default", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: ["Indicator ", index + 1] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setTrustIndicatorsBuilder((prev) => ({ ...prev, indicators: prev.indicators.filter((_, i) => i !== index) })), children: "Remove" })] }), _jsx(Input, { label: "Icon (emoji or text)", value: ind.icon, onChange: (e) => setTrustIndicatorsBuilder((prev) => { const next = [...prev.indicators]; next[index] = { ...next[index], icon: e.target.value }; return { ...prev, indicators: next }; }) }), _jsx(Input, { label: "Title", value: ind.title, onChange: (e) => setTrustIndicatorsBuilder((prev) => { const next = [...prev.indicators]; next[index] = { ...next[index], title: e.target.value }; return { ...prev, indicators: next }; }) }), _jsx(Input, { label: "Description", value: ind.description, onChange: (e) => setTrustIndicatorsBuilder((prev) => { const next = [...prev.indicators]; next[index] = { ...next[index], description: e.target.value }; return { ...prev, indicators: next }; }) })] }, ind.id))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setTrustIndicatorsBuilder((prev) => ({ ...prev, indicators: [...prev.indicators, { id: `ti-${Date.now()}`, icon: "✨", title: "", description: "" }] })), children: "+ Add indicator" })] }));
|
|
716
716
|
}
|
|
717
717
|
function renderCategoriesBuilder() {
|
|
718
|
-
return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Categories Section Builder" }), _jsx(Input, { label: "Section title", value: categoriesBuilder.title, onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, title: e.target.value })) }), _jsx(Input, { label: "Max categories (4\u201312)", type: "number", min: 4, max: 12, value: String(categoriesBuilder.maxCategories), onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, maxCategories: Math.min(12, Math.max(4, Number(e.target.value) || 4)) })) }), _jsx(Checkbox, { checked: categoriesBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(categoriesBuilder.scrollInterval), onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, scrollInterval: Math.max(1000, Number(e.target.value) || 1000) })) })] }));
|
|
718
|
+
return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Categories Section Builder" }), _jsx(Input, { label: "Section title", value: categoriesBuilder.title, onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, title: e.target.value })) }), _jsx(Input, { label: "Max categories (4\u201312)", type: "number", min: 4, max: 12, value: String(categoriesBuilder.maxCategories), onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, maxCategories: Math.min(12, Math.max(4, Number(e.target.value) || 4)) })) }), _jsx(Checkbox, { checked: categoriesBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(categoriesBuilder.scrollInterval), onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, scrollInterval: Math.max(1000, Number(e.target.value) || 1000) })) }), _jsx(Checkbox, { checked: categoriesBuilder.loop, label: "Loop carousel", onChange: (e) => setCategoriesBuilder((prev) => ({ ...prev, loop: e.target.checked })) })] }));
|
|
719
719
|
}
|
|
720
720
|
function renderBrandsBuilder() {
|
|
721
|
-
return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Brands Section Builder" }), _jsx(Input, { label: "Section title", value: brandsBuilder.title, onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, title: e.target.value })) }), _jsx(Input, { label: "Subtitle", value: brandsBuilder.subtitle, onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, subtitle: e.target.value })) }), _jsx(Input, { label: "Max brands", type: "number", min: 1, max: 30, value: String(brandsBuilder.maxBrands), onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, maxBrands: Math.max(1, Number(e.target.value) || 1) })) }), _jsx(Checkbox, { checked: brandsBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(brandsBuilder.scrollInterval), onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, scrollInterval: Math.max(1000, Number(e.target.value) || 1000) })) })] }));
|
|
721
|
+
return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Brands Section Builder" }), _jsx(Input, { label: "Section title", value: brandsBuilder.title, onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, title: e.target.value })) }), _jsx(Input, { label: "Subtitle", value: brandsBuilder.subtitle, onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, subtitle: e.target.value })) }), _jsx(Input, { label: "Max brands", type: "number", min: 1, max: 30, value: String(brandsBuilder.maxBrands), onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, maxBrands: Math.max(1, Number(e.target.value) || 1) })) }), _jsx(Checkbox, { checked: brandsBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(brandsBuilder.scrollInterval), onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, scrollInterval: Math.max(1000, Number(e.target.value) || 1000) })) }), _jsx(Checkbox, { checked: brandsBuilder.loop, label: "Loop carousel", onChange: (e) => setBrandsBuilder((prev) => ({ ...prev, loop: e.target.checked })) })] }));
|
|
722
722
|
}
|
|
723
723
|
function renderCarouselBuilder() {
|
|
724
724
|
return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Carousel Section Builder" }), _jsx(Input, { label: "Section title", value: carouselBuilder.title, onChange: (e) => setCarouselBuilder((prev) => ({ ...prev, title: e.target.value })), placeholder: "Hero Carousel" }), _jsx(Select, { label: "Slide height", value: carouselBuilder.height, onValueChange: (value) => setCarouselBuilder((prev) => ({ ...prev, height: value })), options: [
|
|
@@ -786,7 +786,7 @@ export function AdminSectionsView({ children }) {
|
|
|
786
786
|
] }), _jsx(Checkbox, { checked: customCardsBuilder.autoScroll, label: "Auto-scroll", onChange: (e) => setCustomCardsBuilder((prev) => ({ ...prev, autoScroll: e.target.checked })) }), _jsx(Input, { label: LBL_SCROLL_INTERVAL, type: "number", min: 1000, step: 500, value: String(customCardsBuilder.scrollIntervalMs), onChange: (e) => setCustomCardsBuilder((prev) => ({
|
|
787
787
|
...prev,
|
|
788
788
|
scrollIntervalMs: Math.max(1000, Number(e.target.value) || 4000),
|
|
789
|
-
})) }), _jsxs(Stack, { gap: "3", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: ["Cards (", customCardsBuilder.cards.length, ")"] }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addCard, children: "Add card" })] }), customCardsBuilder.cards.map((card, index) => renderCardFields(card, index)), customCardsBuilder.cards.length === 0 ? (_jsx(Text, { size: "sm", color: "muted", children: "No cards added yet. Click \"Add card\" to start." })) : null] })] }));
|
|
789
|
+
})) }), _jsx(Checkbox, { checked: customCardsBuilder.loop, label: "Loop carousel", onChange: (e) => setCustomCardsBuilder((prev) => ({ ...prev, loop: e.target.checked })) }), _jsxs(Stack, { gap: "3", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsxs(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: ["Cards (", customCardsBuilder.cards.length, ")"] }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addCard, children: "Add card" })] }), customCardsBuilder.cards.map((card, index) => renderCardFields(card, index)), customCardsBuilder.cards.length === 0 ? (_jsx(Text, { size: "sm", color: "muted", children: "No cards added yet. Click \"Add card\" to start." })) : null] })] }));
|
|
790
790
|
}
|
|
791
791
|
function renderGoogleReviewsBuilder() {
|
|
792
792
|
return (_jsxs(Div, { className: CLS_SECTION_PANEL, children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Google Reviews Builder" }), _jsx(Input, { label: "Google Place ID", value: googleReviewsBuilder.placeId, onChange: (e) => setGoogleReviewsBuilder((prev) => ({ ...prev, placeId: e.target.value })), placeholder: "ChIJ...", helperText: "Find your Place ID at developers.google.com/maps/documentation/places/web-service/place-id" }), _jsx(Input, { label: "Max reviews to show", type: "number", min: 1, max: 20, value: String(googleReviewsBuilder.maxReviews), onChange: (e) => setGoogleReviewsBuilder((prev) => ({
|
|
@@ -190,7 +190,7 @@ export function parseProductsBuilder(config) {
|
|
|
190
190
|
manualResourceIds: toStringArray(resources.ids).join(", "),
|
|
191
191
|
filterByCategory: toStringValue(config.filterByCategory),
|
|
192
192
|
maxCount: ([5, 10, 20].includes(maxCount) ? maxCount : 10),
|
|
193
|
-
loop: toBooleanValue(config.loop,
|
|
193
|
+
loop: toBooleanValue(config.loop, DEFAULT_PRODUCTS_BUILDER.loop),
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
export function parseAuctionsBuilder(config) {
|
|
@@ -210,7 +210,7 @@ export function parseAuctionsBuilder(config) {
|
|
|
210
210
|
manualResourceIds: toStringArray(resources.ids).join(", "),
|
|
211
211
|
filterByCategory: toStringValue(config.filterByCategory),
|
|
212
212
|
maxCount: ([5, 10, 20].includes(maxCount) ? maxCount : 10),
|
|
213
|
-
loop: toBooleanValue(config.loop,
|
|
213
|
+
loop: toBooleanValue(config.loop, DEFAULT_AUCTIONS_BUILDER.loop),
|
|
214
214
|
};
|
|
215
215
|
}
|
|
216
216
|
export function parseStatsBuilder(config) {
|
|
@@ -271,7 +271,7 @@ export function parsePreOrdersBuilder(config) {
|
|
|
271
271
|
manualResourceIds: toStringArray(resources.ids).join(", "),
|
|
272
272
|
filterByCategory: toStringValue(config.filterByCategory),
|
|
273
273
|
maxCount: ([5, 10, 20].includes(maxCount) ? maxCount : 10),
|
|
274
|
-
loop: toBooleanValue(config.loop,
|
|
274
|
+
loop: toBooleanValue(config.loop, DEFAULT_PRE_ORDERS_BUILDER.loop),
|
|
275
275
|
};
|
|
276
276
|
}
|
|
277
277
|
export function parseStoresBuilder(config) {
|
|
@@ -292,7 +292,7 @@ export function parseStoresBuilder(config) {
|
|
|
292
292
|
manualResourceIds: toStringArray(resources.ids).join(", "),
|
|
293
293
|
filterByCategory: toStringValue(config.filterByCategory),
|
|
294
294
|
maxCount: ([5, 10, 20].includes(maxCount) ? maxCount : 10),
|
|
295
|
-
loop: toBooleanValue(config.loop,
|
|
295
|
+
loop: toBooleanValue(config.loop, DEFAULT_STORES_BUILDER.loop),
|
|
296
296
|
};
|
|
297
297
|
}
|
|
298
298
|
export function parseEventsBuilder(config) {
|
|
@@ -313,7 +313,7 @@ export function parseEventsBuilder(config) {
|
|
|
313
313
|
manualResourceIds: toStringArray(resources.ids).join(", "),
|
|
314
314
|
filterByCategory: toStringValue(config.filterByCategory),
|
|
315
315
|
maxCount: ([5, 10, 20].includes(maxCount) ? maxCount : 10),
|
|
316
|
-
loop: toBooleanValue(config.loop,
|
|
316
|
+
loop: toBooleanValue(config.loop, DEFAULT_EVENTS_BUILDER.loop),
|
|
317
317
|
};
|
|
318
318
|
}
|
|
319
319
|
export function buildSocialFeedConfig(builder) {
|
|
@@ -395,6 +395,7 @@ export function buildCategoriesConfig(builder) {
|
|
|
395
395
|
maxCategories: builder.maxCategories,
|
|
396
396
|
autoScroll: builder.autoScroll,
|
|
397
397
|
scrollInterval: builder.scrollInterval,
|
|
398
|
+
loop: builder.loop,
|
|
398
399
|
};
|
|
399
400
|
}
|
|
400
401
|
export function parseCategoriesBuilder(config) {
|
|
@@ -403,6 +404,7 @@ export function parseCategoriesBuilder(config) {
|
|
|
403
404
|
maxCategories: toNumberValue(config.maxCategories, DEFAULT_CATEGORIES_BUILDER.maxCategories),
|
|
404
405
|
autoScroll: toBooleanValue(config.autoScroll, DEFAULT_CATEGORIES_BUILDER.autoScroll),
|
|
405
406
|
scrollInterval: toNumberValue(config.scrollInterval, DEFAULT_CATEGORIES_BUILDER.scrollInterval),
|
|
407
|
+
loop: toBooleanValue(config.loop, DEFAULT_CATEGORIES_BUILDER.loop),
|
|
406
408
|
};
|
|
407
409
|
}
|
|
408
410
|
export function buildBrandsConfig(builder) {
|
|
@@ -412,6 +414,7 @@ export function buildBrandsConfig(builder) {
|
|
|
412
414
|
maxBrands: builder.maxBrands,
|
|
413
415
|
autoScroll: builder.autoScroll,
|
|
414
416
|
scrollInterval: builder.scrollInterval,
|
|
417
|
+
loop: builder.loop,
|
|
415
418
|
};
|
|
416
419
|
}
|
|
417
420
|
export function parseBrandsBuilder(config) {
|
|
@@ -421,6 +424,7 @@ export function parseBrandsBuilder(config) {
|
|
|
421
424
|
maxBrands: toNumberValue(config.maxBrands, DEFAULT_BRANDS_BUILDER.maxBrands),
|
|
422
425
|
autoScroll: toBooleanValue(config.autoScroll, DEFAULT_BRANDS_BUILDER.autoScroll),
|
|
423
426
|
scrollInterval: toNumberValue(config.scrollInterval, DEFAULT_BRANDS_BUILDER.scrollInterval),
|
|
427
|
+
loop: toBooleanValue(config.loop, DEFAULT_BRANDS_BUILDER.loop),
|
|
424
428
|
};
|
|
425
429
|
}
|
|
426
430
|
export function buildBannerConfig(builder) {
|
|
@@ -486,6 +490,7 @@ export function buildReviewsConfig(builder) {
|
|
|
486
490
|
mobileItemsPerView: 1,
|
|
487
491
|
autoScroll: builder.autoScroll,
|
|
488
492
|
scrollInterval: builder.scrollInterval,
|
|
493
|
+
loop: builder.loop,
|
|
489
494
|
source: builder.source,
|
|
490
495
|
placeId: builder.source === "google" ? builder.placeId || undefined : undefined,
|
|
491
496
|
};
|
|
@@ -497,6 +502,7 @@ export function parseReviewsBuilder(config) {
|
|
|
497
502
|
itemsPerView: toNumberValue(config.itemsPerView, DEFAULT_REVIEWS_BUILDER.itemsPerView),
|
|
498
503
|
autoScroll: toBooleanValue(config.autoScroll, DEFAULT_REVIEWS_BUILDER.autoScroll),
|
|
499
504
|
scrollInterval: toNumberValue(config.scrollInterval, DEFAULT_REVIEWS_BUILDER.scrollInterval),
|
|
505
|
+
loop: toBooleanValue(config.loop, DEFAULT_REVIEWS_BUILDER.loop),
|
|
500
506
|
source: toStringValue(config.source, "platform"),
|
|
501
507
|
placeId: toStringValue(config.placeId),
|
|
502
508
|
};
|
|
@@ -613,6 +619,7 @@ export function buildCustomCardsConfig(builder) {
|
|
|
613
619
|
columns: builder.columns,
|
|
614
620
|
autoScroll: builder.autoScroll,
|
|
615
621
|
scrollIntervalMs: builder.scrollIntervalMs,
|
|
622
|
+
loop: builder.loop,
|
|
616
623
|
cards: builder.cards.map((card, index) => ({
|
|
617
624
|
id: card.id || `card-${index + 1}`,
|
|
618
625
|
image: card.image || undefined,
|
|
@@ -639,6 +646,7 @@ export function parseCustomCardsBuilder(config) {
|
|
|
639
646
|
columns: ([1, 2, 3, 4].includes(cols) ? cols : DEFAULT_CUSTOM_CARDS_BUILDER.columns),
|
|
640
647
|
autoScroll: toBooleanValue(config.autoScroll, DEFAULT_CUSTOM_CARDS_BUILDER.autoScroll),
|
|
641
648
|
scrollIntervalMs: toNumberValue(config.scrollIntervalMs, DEFAULT_CUSTOM_CARDS_BUILDER.scrollIntervalMs),
|
|
649
|
+
loop: toBooleanValue(config.loop, DEFAULT_CUSTOM_CARDS_BUILDER.loop),
|
|
642
650
|
cards: cardsRaw.map((item, index) => {
|
|
643
651
|
const c = (item ?? {});
|
|
644
652
|
const br = toStringValue(c.borderRadius, "none");
|
|
@@ -150,6 +150,7 @@ export interface CategoriesBuilderState {
|
|
|
150
150
|
maxCategories: number;
|
|
151
151
|
autoScroll: boolean;
|
|
152
152
|
scrollInterval: number;
|
|
153
|
+
loop: boolean;
|
|
153
154
|
}
|
|
154
155
|
export interface BrandsBuilderState {
|
|
155
156
|
title: string;
|
|
@@ -157,6 +158,7 @@ export interface BrandsBuilderState {
|
|
|
157
158
|
maxBrands: number;
|
|
158
159
|
autoScroll: boolean;
|
|
159
160
|
scrollInterval: number;
|
|
161
|
+
loop: boolean;
|
|
160
162
|
}
|
|
161
163
|
export interface BannerBuilderState {
|
|
162
164
|
height: "sm" | "md" | "lg" | "xl";
|
|
@@ -185,6 +187,7 @@ export interface ReviewsBuilderState {
|
|
|
185
187
|
itemsPerView: number;
|
|
186
188
|
autoScroll: boolean;
|
|
187
189
|
scrollInterval: number;
|
|
190
|
+
loop: boolean;
|
|
188
191
|
source: "platform" | "google";
|
|
189
192
|
placeId: string;
|
|
190
193
|
}
|
|
@@ -249,6 +252,7 @@ export interface CustomCardsBuilderState {
|
|
|
249
252
|
columns: 1 | 2 | 3 | 4;
|
|
250
253
|
autoScroll: boolean;
|
|
251
254
|
scrollIntervalMs: number;
|
|
255
|
+
loop: boolean;
|
|
252
256
|
cards: CustomCardsCardBuilderEntry[];
|
|
253
257
|
}
|
|
254
258
|
export interface GoogleReviewsBuilderState {
|
|
@@ -42,7 +42,7 @@ export const DEFAULT_PRODUCTS_BUILDER = {
|
|
|
42
42
|
manualResourceIds: "",
|
|
43
43
|
filterByCategory: "",
|
|
44
44
|
maxCount: 10,
|
|
45
|
-
loop:
|
|
45
|
+
loop: true,
|
|
46
46
|
};
|
|
47
47
|
export const DEFAULT_AUCTIONS_BUILDER = {
|
|
48
48
|
title: "Live Auctions",
|
|
@@ -57,7 +57,7 @@ export const DEFAULT_AUCTIONS_BUILDER = {
|
|
|
57
57
|
manualResourceIds: "",
|
|
58
58
|
filterByCategory: "",
|
|
59
59
|
maxCount: 10,
|
|
60
|
-
loop:
|
|
60
|
+
loop: true,
|
|
61
61
|
};
|
|
62
62
|
const DEFAULT_STAT_ROW = {
|
|
63
63
|
source: "static",
|
|
@@ -89,7 +89,7 @@ export const DEFAULT_PRE_ORDERS_BUILDER = {
|
|
|
89
89
|
manualResourceIds: "",
|
|
90
90
|
filterByCategory: "",
|
|
91
91
|
maxCount: 10,
|
|
92
|
-
loop:
|
|
92
|
+
loop: true,
|
|
93
93
|
};
|
|
94
94
|
export const DEFAULT_STORES_BUILDER = {
|
|
95
95
|
title: "Featured Stores",
|
|
@@ -105,7 +105,7 @@ export const DEFAULT_STORES_BUILDER = {
|
|
|
105
105
|
manualResourceIds: "",
|
|
106
106
|
filterByCategory: "",
|
|
107
107
|
maxCount: 10,
|
|
108
|
-
loop:
|
|
108
|
+
loop: true,
|
|
109
109
|
};
|
|
110
110
|
export const DEFAULT_EVENTS_BUILDER = {
|
|
111
111
|
title: "Events & Offers",
|
|
@@ -121,7 +121,7 @@ export const DEFAULT_EVENTS_BUILDER = {
|
|
|
121
121
|
manualResourceIds: "",
|
|
122
122
|
filterByCategory: "",
|
|
123
123
|
maxCount: 10,
|
|
124
|
-
loop:
|
|
124
|
+
loop: true,
|
|
125
125
|
};
|
|
126
126
|
export const DEFAULT_SOCIAL_FEED_BUILDER = {
|
|
127
127
|
title: "",
|
|
@@ -156,6 +156,7 @@ export const DEFAULT_CATEGORIES_BUILDER = {
|
|
|
156
156
|
maxCategories: 8,
|
|
157
157
|
autoScroll: false,
|
|
158
158
|
scrollInterval: 5000,
|
|
159
|
+
loop: true,
|
|
159
160
|
};
|
|
160
161
|
export const DEFAULT_BRANDS_BUILDER = {
|
|
161
162
|
title: "Top Brands",
|
|
@@ -163,6 +164,7 @@ export const DEFAULT_BRANDS_BUILDER = {
|
|
|
163
164
|
maxBrands: 13,
|
|
164
165
|
autoScroll: true,
|
|
165
166
|
scrollInterval: 4000,
|
|
167
|
+
loop: true,
|
|
166
168
|
};
|
|
167
169
|
export const DEFAULT_BANNER_BUILDER = {
|
|
168
170
|
height: "lg",
|
|
@@ -187,6 +189,7 @@ export const DEFAULT_REVIEWS_BUILDER = {
|
|
|
187
189
|
itemsPerView: 3,
|
|
188
190
|
autoScroll: true,
|
|
189
191
|
scrollInterval: 5000,
|
|
192
|
+
loop: true,
|
|
190
193
|
source: "platform",
|
|
191
194
|
placeId: "",
|
|
192
195
|
};
|
|
@@ -237,6 +240,7 @@ export const DEFAULT_CUSTOM_CARDS_BUILDER = {
|
|
|
237
240
|
columns: 3,
|
|
238
241
|
autoScroll: false,
|
|
239
242
|
scrollIntervalMs: 4000,
|
|
243
|
+
loop: true,
|
|
240
244
|
cards: [],
|
|
241
245
|
};
|
|
242
246
|
export const DEFAULT_GOOGLE_REVIEWS_BUILDER = {
|
|
@@ -13,5 +13,8 @@ export interface BrandsSectionProps {
|
|
|
13
13
|
featuredOnly?: boolean;
|
|
14
14
|
byCountry?: string;
|
|
15
15
|
};
|
|
16
|
+
autoScroll?: boolean;
|
|
17
|
+
scrollInterval?: number;
|
|
18
|
+
loop?: boolean;
|
|
16
19
|
}
|
|
17
|
-
export declare function BrandsSection({ title, subtitle, limit, viewMoreHref, viewMoreLabel, className, initialItems, cta, filters, }: BrandsSectionProps): import("react").JSX.Element | null;
|
|
20
|
+
export declare function BrandsSection({ title, subtitle, limit, viewMoreHref, viewMoreLabel, className, initialItems, cta, filters, autoScroll, scrollInterval, loop, }: BrandsSectionProps): import("react").JSX.Element | null;
|
|
@@ -29,7 +29,7 @@ function BrandLogo({ brand }) {
|
|
|
29
29
|
const initial = brand.name[0]?.toUpperCase() ?? "?";
|
|
30
30
|
return (_jsxs(Link, { href: ROUTES.PUBLIC.CATEGORY_DETAIL(brand.slug), className: "flex h-32 w-36 flex-col items-center justify-center gap-[var(--appkit-space-2)] rounded-xl border border-zinc-200 bg-[var(--appkit-color-surface)] p-[var(--appkit-space-3)] shadow-sm transition-all hover:border-primary-300 hover:shadow-md border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] md:h-40", children: [iconSrc || coverImage ? (_jsx(Image, { src: iconSrc ?? coverImage, alt: brand.name, width: 64, height: 64, className: "h-16 w-16 rounded object-contain" })) : (_jsx(Row, { textWeight: "bold", textSize: "sm", className: "h-16 w-16 bg-primary-100 text-primary-700 dark:bg-primary-900 dark:text-primary-300", align: "center", justify: "center", rounded: "lg", children: initial })), _jsx(Text, { className: "w-full truncate", color: "muted", size: "xs", weight: "medium", align: "start", children: brand.name })] }));
|
|
31
31
|
}
|
|
32
|
-
export function BrandsSection({ title = "Top Brands", subtitle, limit = 12, viewMoreHref, viewMoreLabel = "All brands →", className = "", initialItems, cta, filters, }) {
|
|
32
|
+
export function BrandsSection({ title = "Top Brands", subtitle, limit = 12, viewMoreHref, viewMoreLabel = "All brands →", className = "", initialItems, cta, filters, autoScroll = true, scrollInterval = 4000, loop = true, }) {
|
|
33
33
|
const themed = { textPrimary: THEMED_TEXT_PRIMARY };
|
|
34
34
|
const { data: allBrands = [], isLoading } = useTopBrands(limit, { initialData: initialItems });
|
|
35
35
|
const [activeFilter, setActiveFilter] = useState("all");
|
|
@@ -42,5 +42,5 @@ export function BrandsSection({ title = "Top Brands", subtitle, limit = 12, view
|
|
|
42
42
|
});
|
|
43
43
|
if (!isLoading && allBrands.length === 0)
|
|
44
44
|
return null;
|
|
45
|
-
return (_jsx(Section, { className: `${className}`, surface: "subtle", paddingY: "y-3xl", paddingX: "x-md", children: _jsxs(Div, { className: "mx-auto max-w-7xl", children: [_jsxs(Row, { className: "mb-6", align: "center", justify: "between", children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 2, className: themed.textPrimary, size: "2xl", mdSize: "3xl", weight: "bold", children: title }), subtitle && (_jsx(Text, { variant: "secondary", className: "mt-1", size: "sm", children: subtitle }))] }), !cta && viewMoreHref && (_jsx(Link, { href: viewMoreHref, className: "text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-primary)] hover:underline", children: viewMoreLabel }))] }), showFeaturedChip && !isLoading && (_jsxs(Row, { wrap: true, gap: "sm", className: "mb-4", children: [_jsx(BrandFilterChip, { label: "All", active: activeFilter === "all", onClick: () => setActiveFilter("all") }), _jsx(BrandFilterChip, { label: "Featured", active: activeFilter === "featured", onClick: () => setActiveFilter(activeFilter === "featured" ? "all" : "featured") })] })), isLoading ? (_jsx(Div, { layout: "flex", gap: "3", className: `${__O.hidden}`, children: Array.from({ length: 8 }).map((_, i) => (_jsx(Div, { className: "h-32 w-36 flex-none animate-pulse md:h-40", surface: "subtle", rounded: "xl" }, i))) })) : (_jsx(HorizontalScroller, { items: brands, renderItem: (brand) => _jsx(BrandLogo, { brand: brand }), keyExtractor: (brand) => brand.id, gap: 12, showArrows: true, showScrollbar: false,
|
|
45
|
+
return (_jsx(Section, { className: `${className}`, surface: "subtle", paddingY: "y-3xl", paddingX: "x-md", children: _jsxs(Div, { className: "mx-auto max-w-7xl", children: [_jsxs(Row, { className: "mb-6", align: "center", justify: "between", children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 2, className: themed.textPrimary, size: "2xl", mdSize: "3xl", weight: "bold", children: title }), subtitle && (_jsx(Text, { variant: "secondary", className: "mt-1", size: "sm", children: subtitle }))] }), !cta && viewMoreHref && (_jsx(Link, { href: viewMoreHref, className: "text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-primary)] hover:underline", children: viewMoreLabel }))] }), showFeaturedChip && !isLoading && (_jsxs(Row, { wrap: true, gap: "sm", className: "mb-4", children: [_jsx(BrandFilterChip, { label: "All", active: activeFilter === "all", onClick: () => setActiveFilter("all") }), _jsx(BrandFilterChip, { label: "Featured", active: activeFilter === "featured", onClick: () => setActiveFilter(activeFilter === "featured" ? "all" : "featured") })] })), isLoading ? (_jsx(Div, { layout: "flex", gap: "3", className: `${__O.hidden}`, children: Array.from({ length: 8 }).map((_, i) => (_jsx(Div, { className: "h-32 w-36 flex-none animate-pulse md:h-40", surface: "subtle", rounded: "xl" }, i))) })) : (_jsx(HorizontalScroller, { items: brands, renderItem: (brand) => _jsx(BrandLogo, { brand: brand }), keyExtractor: (brand) => brand.id, gap: 12, showArrows: true, showScrollbar: false, autoScroll: autoScroll, autoScrollInterval: scrollInterval, pauseOnHover: true, loop: loop })), cta && !isLoading && (_jsx(Div, { className: "mt-6 text-left", children: _jsx(Link, { href: cta.href, className: CTA_CLASSES[cta.variant], children: cta.label }) }))] }) }));
|
|
46
46
|
}
|
|
@@ -55,16 +55,16 @@ function SectionHeader({ title }) {
|
|
|
55
55
|
return (_jsx(Div, { className: "mb-6", children: _jsx(Heading, { level: 2, className: themed.textPrimary, children: title }) }));
|
|
56
56
|
}
|
|
57
57
|
export function CustomCardsSection(config) {
|
|
58
|
-
const { title, layout, columns = 3, cards, autoScroll, scrollIntervalMs } = config;
|
|
58
|
+
const { title, layout, columns = 3, cards, autoScroll, scrollIntervalMs, loop = true } = config;
|
|
59
59
|
if (!cards?.length)
|
|
60
60
|
return null;
|
|
61
61
|
// autoScroll: wrap all cards in SectionCarousel (client carousel)
|
|
62
62
|
if (autoScroll) {
|
|
63
|
-
return (_jsx(Section, { padding: "y-3xl", surface: "muted", children: _jsx(Div, { className: CLS_CONTAINER, children: _jsx(SectionCarousel, { title: title ?? "", items: cards, renderItem: (card) => _jsx(CardItem, { card: card }), keyExtractor: (card) => card.id, autoScroll: true, autoScrollInterval: scrollIntervalMs ?? 3500, perView: { base: 1, sm: 2, md: columns } }) }) }));
|
|
63
|
+
return (_jsx(Section, { padding: "y-3xl", surface: "muted", children: _jsx(Div, { className: CLS_CONTAINER, children: _jsx(SectionCarousel, { title: title ?? "", items: cards, renderItem: (card) => _jsx(CardItem, { card: card }), keyExtractor: (card) => card.id, autoScroll: true, autoScrollInterval: scrollIntervalMs ?? 3500, loop: loop, perView: { base: 1, sm: 2, md: columns } }) }) }));
|
|
64
64
|
}
|
|
65
65
|
// Row layout: horizontal scroller
|
|
66
66
|
if (layout === "row") {
|
|
67
|
-
return (_jsx(Section, { padding: "y-3xl", surface: "muted", children: _jsxs(Div, { className: CLS_CONTAINER, children: [_jsx(SectionHeader, { title: title }), _jsx(HorizontalScroller, { gap: 16, showArrows: true, snapToItems: true, loop:
|
|
67
|
+
return (_jsx(Section, { padding: "y-3xl", surface: "muted", children: _jsxs(Div, { className: CLS_CONTAINER, children: [_jsx(SectionHeader, { title: title }), _jsx(HorizontalScroller, { gap: 16, showArrows: true, snapToItems: true, loop: loop, items: cards, keyExtractor: (card) => card.id, renderItem: (card) => (_jsx(Div, { className: "w-72 flex-shrink-0", children: _jsx(CardItem, { card: card }) })) })] }) }));
|
|
68
68
|
}
|
|
69
69
|
// Masonry layout: CSS columns
|
|
70
70
|
if (layout === "masonry") {
|
|
@@ -9,5 +9,8 @@ export interface CustomerReviewsSectionProps<T = unknown> {
|
|
|
9
9
|
isLoading?: boolean;
|
|
10
10
|
keyExtractor?: (item: T) => string;
|
|
11
11
|
className?: string;
|
|
12
|
+
autoScroll?: boolean;
|
|
13
|
+
scrollInterval?: number;
|
|
14
|
+
loop?: boolean;
|
|
12
15
|
}
|
|
13
|
-
export declare function CustomerReviewsSection<T = unknown>({ title, subtitle, items, renderItem, viewMoreHref, viewMoreLabel, isLoading, keyExtractor, className, }: CustomerReviewsSectionProps<T>): React.JSX.Element | null;
|
|
16
|
+
export declare function CustomerReviewsSection<T = unknown>({ title, subtitle, items, renderItem, viewMoreHref, viewMoreLabel, isLoading, keyExtractor, className, autoScroll, scrollInterval, loop, }: CustomerReviewsSectionProps<T>): React.JSX.Element | null;
|
|
@@ -10,12 +10,12 @@ const __O = {
|
|
|
10
10
|
hidden: "overflow-hidden",
|
|
11
11
|
};
|
|
12
12
|
// --- Section -----------------------------------------------------------------
|
|
13
|
-
export function CustomerReviewsSection({ title, subtitle, items, renderItem, viewMoreHref, viewMoreLabel = "See all reviews", isLoading = false, keyExtractor, className = "", }) {
|
|
13
|
+
export function CustomerReviewsSection({ title, subtitle, items, renderItem, viewMoreHref, viewMoreLabel = "See all reviews", isLoading = false, keyExtractor, className = "", autoScroll = true, scrollInterval = 4500, loop = true, }) {
|
|
14
14
|
const themed = { textPrimary: THEMED_TEXT_PRIMARY, textSecondary: THEMED_TEXT_SECONDARY };
|
|
15
15
|
if (isLoading) {
|
|
16
16
|
return (_jsx(Section, { className: `${__P.p8} ${className}`, surface: "muted", children: _jsxs(Div, { className: "w-full max-w-7xl mx-auto", children: [_jsx(Div, { className: `h-8 ${skeleton.base} mb-8 max-w-xs mx-auto` }), _jsx(Div, { layout: "flex", gap: "6", className: `${__O.hidden}`, children: [...Array(3)].map((_, i) => (_jsx(Div, { className: `${skeleton.card} h-[clamp(180px,26vh,260px)] min-w-[clamp(150px,18vw,260px)] max-w-[clamp(240px,36vw,380px)] flex-none` }, i))) })] }) }));
|
|
17
17
|
}
|
|
18
18
|
if (items.length === 0)
|
|
19
19
|
return null;
|
|
20
|
-
return (_jsx(Section, { className: `${className}`, surface: "muted", padding: "y-3xl", children: _jsxs(Div, { className: "w-full max-w-7xl mx-auto md:px-[2rem]", padding: "x-md", children: [_jsxs(Div, { className: "text-left mb-10", children: [_jsx(Heading, { level: 2, className: `${themed.textPrimary} mb-3`, size: "3xl", mdSize: "4xl", weight: "bold", children: title }), subtitle && (_jsx(Text, { className: `${themed.textSecondary}`, size: "base", children: subtitle }))] }), _jsx(HorizontalScroller, { items: items, renderItem: (item) => renderItem(item), perView: CAROUSEL_PER_VIEW.reviews, gap: 24, autoScroll:
|
|
20
|
+
return (_jsx(Section, { className: `${className}`, surface: "muted", padding: "y-3xl", children: _jsxs(Div, { className: "w-full max-w-7xl mx-auto md:px-[2rem]", padding: "x-md", children: [_jsxs(Div, { className: "text-left mb-10", children: [_jsx(Heading, { level: 2, className: `${themed.textPrimary} mb-3`, size: "3xl", mdSize: "4xl", weight: "bold", children: title }), subtitle && (_jsx(Text, { className: `${themed.textSecondary}`, size: "base", children: subtitle }))] }), _jsx(HorizontalScroller, { items: items, renderItem: (item) => renderItem(item), perView: CAROUSEL_PER_VIEW.reviews, gap: 24, autoScroll: autoScroll, autoScrollInterval: scrollInterval, pauseOnHover: true, snapToItems: true, showArrows: true, keyExtractor: keyExtractor, loop: loop }), viewMoreHref && (_jsx(Div, { className: "text-left mt-8", children: _jsx(TextLink, { href: viewMoreHref, className: "text-primary hover:text-primary/80", size: "sm", weight: "medium", children: viewMoreLabel }) }))] }) }));
|
|
21
21
|
}
|
|
@@ -8,5 +8,8 @@ export interface EventsSectionProps {
|
|
|
8
8
|
limit?: number;
|
|
9
9
|
className?: string;
|
|
10
10
|
initialItems?: EventItem[];
|
|
11
|
+
autoScroll?: boolean;
|
|
12
|
+
scrollInterval?: number;
|
|
13
|
+
loop?: boolean;
|
|
11
14
|
}
|
|
12
|
-
export declare function EventsSection({ title, description, viewMoreHref, viewMoreLabel, limit, className, initialItems, }: EventsSectionProps): React.JSX.Element;
|
|
15
|
+
export declare function EventsSection({ title, description, viewMoreHref, viewMoreLabel, limit, className, initialItems, autoScroll, scrollInterval, loop, }: EventsSectionProps): React.JSX.Element;
|
|
@@ -4,7 +4,7 @@ import { SectionCarousel } from "./SectionCarousel";
|
|
|
4
4
|
import { useHomepageEvents } from "../hooks/useHomepageEvents";
|
|
5
5
|
import { EventCard } from "../../events/components/EventCard";
|
|
6
6
|
import { CAROUSEL_PER_VIEW } from "../constants/carousel-per-view";
|
|
7
|
-
export function EventsSection({ title = "Events & Offers", description, viewMoreHref, viewMoreLabel = "View all events →", limit = 6, className = "", initialItems, }) {
|
|
7
|
+
export function EventsSection({ title = "Events & Offers", description, viewMoreHref, viewMoreLabel = "View all events →", limit = 6, className = "", initialItems, autoScroll = false, scrollInterval = 5000, loop = true, }) {
|
|
8
8
|
const { data: items = [], isLoading } = useHomepageEvents(limit, { initialData: initialItems });
|
|
9
|
-
return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Events & Offers", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 3, perView: CAROUSEL_PER_VIEW.events, gap: 16, keyExtractor: (event) => event.id, renderItem: (event) => (_jsx(EventCard, { event: event })), className: className }));
|
|
9
|
+
return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Events & Offers", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 3, perView: CAROUSEL_PER_VIEW.events, gap: 16, autoScroll: autoScroll, autoScrollInterval: scrollInterval, loop: loop, keyExtractor: (event) => event.id, renderItem: (event) => (_jsx(EventCard, { event: event })), className: className }));
|
|
10
10
|
}
|
|
@@ -11,5 +11,6 @@ export interface FeaturedAuctionsSectionProps {
|
|
|
11
11
|
rows?: number;
|
|
12
12
|
autoScroll?: boolean;
|
|
13
13
|
scrollInterval?: number;
|
|
14
|
+
loop?: boolean;
|
|
14
15
|
}
|
|
15
|
-
export declare function FeaturedAuctionsSection({ title, description, viewMoreHref, viewMoreLabel, className, filterByBrand, initialItems, rows, autoScroll, scrollInterval, }: FeaturedAuctionsSectionProps): React.JSX.Element;
|
|
16
|
+
export declare function FeaturedAuctionsSection({ title, description, viewMoreHref, viewMoreLabel, className, filterByBrand, initialItems, rows, autoScroll, scrollInterval, loop, }: FeaturedAuctionsSectionProps): React.JSX.Element;
|
|
@@ -4,7 +4,7 @@ import { SectionCarousel } from "./SectionCarousel";
|
|
|
4
4
|
import { useFeaturedAuctions } from "../hooks/useFeaturedAuctions";
|
|
5
5
|
import { MarketplaceAuctionCard } from "../../auctions/components/MarketplaceAuctionCard";
|
|
6
6
|
import { CAROUSEL_PER_VIEW } from "../constants/carousel-per-view";
|
|
7
|
-
export function FeaturedAuctionsSection({ title = "Live Auctions", description, viewMoreHref, viewMoreLabel = "View all auctions →", className = "", filterByBrand, initialItems, rows = 1, autoScroll = false, scrollInterval = 5000, }) {
|
|
7
|
+
export function FeaturedAuctionsSection({ title = "Live Auctions", description, viewMoreHref, viewMoreLabel = "View all auctions →", className = "", filterByBrand, initialItems, rows = 1, autoScroll = false, scrollInterval = 5000, loop = true, }) {
|
|
8
8
|
const { data: items = [], isLoading } = useFeaturedAuctions({ filterByBrand, initialData: initialItems });
|
|
9
|
-
return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Live Auctions", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, rows: Math.min(Math.max(rows, 1), 4), autoScroll: autoScroll, autoScrollInterval: scrollInterval, keyExtractor: (product) => product.id, renderItem: (product) => (_jsx(MarketplaceAuctionCard, { product: product })), className: className }));
|
|
9
|
+
return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Live Auctions", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, rows: Math.min(Math.max(rows, 1), 4), autoScroll: autoScroll, autoScrollInterval: scrollInterval, loop: loop, keyExtractor: (product) => product.id, renderItem: (product) => (_jsx(MarketplaceAuctionCard, { product: product })), className: className }));
|
|
10
10
|
}
|
|
@@ -11,5 +11,6 @@ export interface FeaturedPreOrdersSectionProps {
|
|
|
11
11
|
rows?: number;
|
|
12
12
|
autoScroll?: boolean;
|
|
13
13
|
scrollInterval?: number;
|
|
14
|
+
loop?: boolean;
|
|
14
15
|
}
|
|
15
|
-
export declare function FeaturedPreOrdersSection({ title, description, viewMoreHref, viewMoreLabel, className, filterByBrand, initialItems, rows, autoScroll, scrollInterval, }: FeaturedPreOrdersSectionProps): React.JSX.Element;
|
|
16
|
+
export declare function FeaturedPreOrdersSection({ title, description, viewMoreHref, viewMoreLabel, className, filterByBrand, initialItems, rows, autoScroll, scrollInterval, loop, }: FeaturedPreOrdersSectionProps): React.JSX.Element;
|
|
@@ -4,7 +4,7 @@ import { SectionCarousel } from "./SectionCarousel";
|
|
|
4
4
|
import { useFeaturedPreOrders } from "../hooks/useFeaturedPreOrders";
|
|
5
5
|
import { MarketplacePreorderCard } from "../../pre-orders/components/MarketplacePreorderCard";
|
|
6
6
|
import { CAROUSEL_PER_VIEW } from "../constants/carousel-per-view";
|
|
7
|
-
export function FeaturedPreOrdersSection({ title = "Reserve Before It Ships", description, viewMoreHref, viewMoreLabel = "View all pre-orders →", className = "", filterByBrand, initialItems, rows = 1, autoScroll = false, scrollInterval = 5000, }) {
|
|
7
|
+
export function FeaturedPreOrdersSection({ title = "Reserve Before It Ships", description, viewMoreHref, viewMoreLabel = "View all pre-orders →", className = "", filterByBrand, initialItems, rows = 1, autoScroll = false, scrollInterval = 5000, loop = true, }) {
|
|
8
8
|
const { data: items = [], isLoading } = useFeaturedPreOrders({ filterByBrand, initialData: initialItems });
|
|
9
|
-
return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Pre-Order Incoming", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, rows: Math.min(Math.max(rows, 1), 4), autoScroll: autoScroll, autoScrollInterval: scrollInterval, keyExtractor: (product) => product.id, renderItem: (product) => (_jsx(MarketplacePreorderCard, { product: product })), className: className }));
|
|
9
|
+
return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Pre-Order Incoming", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, rows: Math.min(Math.max(rows, 1), 4), autoScroll: autoScroll, autoScrollInterval: scrollInterval, loop: loop, keyExtractor: (product) => product.id, renderItem: (product) => (_jsx(MarketplacePreorderCard, { product: product })), className: className }));
|
|
10
10
|
}
|
|
@@ -11,5 +11,6 @@ export interface FeaturedProductsSectionProps {
|
|
|
11
11
|
maxItems?: number;
|
|
12
12
|
autoScroll?: boolean;
|
|
13
13
|
scrollInterval?: number;
|
|
14
|
+
loop?: boolean;
|
|
14
15
|
}
|
|
15
|
-
export declare function FeaturedProductsSection({ title, description, viewMoreHref, viewMoreLabel, className, filterByBrand, initialItems, rows, autoScroll, scrollInterval, }: FeaturedProductsSectionProps): import("react").JSX.Element;
|
|
16
|
+
export declare function FeaturedProductsSection({ title, description, viewMoreHref, viewMoreLabel, className, filterByBrand, initialItems, rows, autoScroll, scrollInterval, loop, }: FeaturedProductsSectionProps): import("react").JSX.Element;
|
|
@@ -5,7 +5,7 @@ import { useFeaturedProducts } from "../hooks/useFeaturedProducts";
|
|
|
5
5
|
import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
|
|
6
6
|
import { ROUTES } from "../../../next";
|
|
7
7
|
import { CAROUSEL_PER_VIEW } from "../constants/carousel-per-view";
|
|
8
|
-
export function FeaturedProductsSection({ title = "Featured Products", description, viewMoreHref, viewMoreLabel = "View all products →", className = "", filterByBrand, initialItems, rows = 1, autoScroll = false, scrollInterval = 5000, }) {
|
|
8
|
+
export function FeaturedProductsSection({ title = "Featured Products", description, viewMoreHref, viewMoreLabel = "View all products →", className = "", filterByBrand, initialItems, rows = 1, autoScroll = false, scrollInterval = 5000, loop = true, }) {
|
|
9
9
|
const { data, isLoading } = useFeaturedProducts({
|
|
10
10
|
filterByBrand,
|
|
11
11
|
initialData: initialItems?.length
|
|
@@ -13,5 +13,5 @@ export function FeaturedProductsSection({ title = "Featured Products", descripti
|
|
|
13
13
|
: undefined,
|
|
14
14
|
});
|
|
15
15
|
const items = data?.items ?? [];
|
|
16
|
-
return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Featured Products", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, rows: Math.min(Math.max(rows, 1), 4), autoScroll: autoScroll, autoScrollInterval: scrollInterval, keyExtractor: (product) => product.id, renderItem: (product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.PRODUCT_DETAIL(product.slug ?? product.id ?? "")) })), className: className }));
|
|
16
|
+
return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Featured Products", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, rows: Math.min(Math.max(rows, 1), 4), autoScroll: autoScroll, autoScrollInterval: scrollInterval, loop: loop, keyExtractor: (product) => product.id, renderItem: (product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.PRODUCT_DETAIL(product.slug ?? product.id ?? "")) })), className: className }));
|
|
17
17
|
}
|
|
@@ -8,5 +8,8 @@ export interface FeaturedStoresSectionProps {
|
|
|
8
8
|
limit?: number;
|
|
9
9
|
className?: string;
|
|
10
10
|
initialItems?: StoreListItem[];
|
|
11
|
+
autoScroll?: boolean;
|
|
12
|
+
scrollInterval?: number;
|
|
13
|
+
loop?: boolean;
|
|
11
14
|
}
|
|
12
|
-
export declare function FeaturedStoresSection({ title, description, viewMoreHref, viewMoreLabel, limit, className, initialItems, }: FeaturedStoresSectionProps): React.JSX.Element;
|
|
15
|
+
export declare function FeaturedStoresSection({ title, description, viewMoreHref, viewMoreLabel, limit, className, initialItems, autoScroll, scrollInterval, loop, }: FeaturedStoresSectionProps): React.JSX.Element;
|
|
@@ -5,7 +5,7 @@ import { useFeaturedStores } from "../hooks/useFeaturedStores";
|
|
|
5
5
|
import { InteractiveStoreCard } from "../../stores/components/InteractiveStoreCard";
|
|
6
6
|
import { ROUTES } from "../../../next";
|
|
7
7
|
import { CAROUSEL_PER_VIEW } from "../constants/carousel-per-view";
|
|
8
|
-
export function FeaturedStoresSection({ title = "Featured Stores", description, viewMoreHref, viewMoreLabel = "View all stores →", limit = 8, className = "", initialItems, }) {
|
|
8
|
+
export function FeaturedStoresSection({ title = "Featured Stores", description, viewMoreHref, viewMoreLabel = "View all stores →", limit = 8, className = "", initialItems, autoScroll = false, scrollInterval = 5000, loop = true, }) {
|
|
9
9
|
const { data: items = [], isLoading } = useFeaturedStores(limit, { initialData: initialItems });
|
|
10
|
-
return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Top Stores", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, keyExtractor: (store) => store.id, renderItem: (store) => (_jsx(InteractiveStoreCard, { store: store, href: ROUTES.PUBLIC.STORE_DETAIL(store.storeSlug) })), className: className }));
|
|
10
|
+
return (_jsx(SectionCarousel, { title: title, description: description, pillLabel: "Top Stores", headingVariant: "editorial", viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, items: items, isLoading: isLoading, skeletonCount: 4, perView: CAROUSEL_PER_VIEW.standard, gap: 16, autoScroll: autoScroll, autoScrollInterval: scrollInterval, loop: loop, keyExtractor: (store) => store.id, renderItem: (store) => (_jsx(InteractiveStoreCard, { store: store, href: ROUTES.PUBLIC.STORE_DETAIL(store.storeSlug) })), className: className }));
|
|
11
11
|
}
|
|
@@ -5,5 +5,8 @@ export interface HomepageCustomerReviewsSectionProps {
|
|
|
5
5
|
viewMoreHref?: string;
|
|
6
6
|
viewMoreLabel?: string;
|
|
7
7
|
className?: string;
|
|
8
|
+
autoScroll?: boolean;
|
|
9
|
+
scrollInterval?: number;
|
|
10
|
+
loop?: boolean;
|
|
8
11
|
}
|
|
9
|
-
export declare function HomepageCustomerReviewsSection({ title, subtitle, viewMoreHref, viewMoreLabel, className, }: HomepageCustomerReviewsSectionProps): React.JSX.Element;
|
|
12
|
+
export declare function HomepageCustomerReviewsSection({ title, subtitle, viewMoreHref, viewMoreLabel, className, autoScroll, scrollInterval, loop, }: HomepageCustomerReviewsSectionProps): React.JSX.Element;
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { CustomerReviewsSection } from "./CustomerReviewsSection";
|
|
4
4
|
import { useHomepageReviews } from "../hooks/useHomepageReviews";
|
|
5
5
|
import { ReviewCard } from "../../reviews/components";
|
|
6
|
-
export function HomepageCustomerReviewsSection({ title = "What Our Customers Say", subtitle, viewMoreHref, viewMoreLabel = "See all reviews →", className = "", }) {
|
|
6
|
+
export function HomepageCustomerReviewsSection({ title = "What Our Customers Say", subtitle, viewMoreHref, viewMoreLabel = "See all reviews →", className = "", autoScroll = true, scrollInterval = 4500, loop = true, }) {
|
|
7
7
|
const { data: reviews = [], isLoading } = useHomepageReviews();
|
|
8
|
-
return (_jsx(CustomerReviewsSection, { title: title, subtitle: subtitle, items: reviews, renderItem: (review) => (_jsx(ReviewCard, { review: review, className: "h-full border-zinc-200 shadow-sm border-[var(--appkit-color-border)]" })), viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, isLoading: isLoading, keyExtractor: (review) => review.id, className: className }));
|
|
8
|
+
return (_jsx(CustomerReviewsSection, { title: title, subtitle: subtitle, items: reviews, renderItem: (review) => (_jsx(ReviewCard, { review: review, className: "h-full border-zinc-200 shadow-sm border-[var(--appkit-color-border)]" })), viewMoreHref: viewMoreHref, viewMoreLabel: viewMoreLabel, isLoading: isLoading, keyExtractor: (review) => review.id, className: className, autoScroll: autoScroll, scrollInterval: scrollInterval, loop: loop }));
|
|
9
9
|
}
|
|
@@ -41,6 +41,8 @@ export interface SectionCarouselProps<T = unknown> {
|
|
|
41
41
|
autoScroll?: boolean;
|
|
42
42
|
/** Interval between auto-scroll steps in ms. Default: 3500 */
|
|
43
43
|
autoScrollInterval?: number;
|
|
44
|
+
/** Circular auto-rotate at the carousel edges. Default: true */
|
|
45
|
+
loop?: boolean;
|
|
44
46
|
/** Key extractor for the HorizontalScroller. */
|
|
45
47
|
keyExtractor?: (item: T, index: number) => string;
|
|
46
48
|
/**
|
|
@@ -72,4 +74,4 @@ export interface SectionCarouselProps<T = unknown> {
|
|
|
72
74
|
*/
|
|
73
75
|
minItemWidth?: number;
|
|
74
76
|
}
|
|
75
|
-
export declare function SectionCarousel<T = unknown>({ title, description, headingVariant, pillLabel, backgroundImage, viewMoreHref, viewMoreLabel, items, renderItem, perView, gap, autoScroll, autoScrollInterval, keyExtractor, rows, className, isLoading, skeletonCount, lightText, showPeek: _showPeek, minItemWidth, }: SectionCarouselProps<T>): React.JSX.Element | null;
|
|
77
|
+
export declare function SectionCarousel<T = unknown>({ title, description, headingVariant, pillLabel, backgroundImage, viewMoreHref, viewMoreLabel, items, renderItem, perView, gap, autoScroll, autoScrollInterval, loop, keyExtractor, rows, className, isLoading, skeletonCount, lightText, showPeek: _showPeek, minItemWidth, }: SectionCarouselProps<T>): React.JSX.Element | null;
|
|
@@ -12,7 +12,7 @@ function CarouselSkeleton({ count }) {
|
|
|
12
12
|
return (_jsx(Div, { layout: "flex", gap: "4", className: `${__O.hidden}`, padding: "x-md", children: Array.from({ length: count }).map((_, i) => (_jsxs(Stack, { className: "flex-none min-w-[clamp(150px,18vw,260px)] max-w-[clamp(240px,36vw,380px)] h-[clamp(180px,26vh,260px)]", gap: "sm", children: [_jsx(Div, { className: `aspect-square ${skeleton.image}`, rounded: "xl" }), _jsx(Div, { className: `${skeleton.text} w-3/4` }), _jsx(Div, { className: `${skeleton.text} w-1/2` })] }, i))) }));
|
|
13
13
|
}
|
|
14
14
|
// --- Component ---------------------------------------------------------------
|
|
15
|
-
export function SectionCarousel({ title, description, headingVariant = "editorial", pillLabel, backgroundImage, viewMoreHref, viewMoreLabel = "View all →", items, renderItem, perView = CAROUSEL_PER_VIEW.standard, gap = 16, autoScroll = false, autoScrollInterval = 3500, keyExtractor, rows = 1, className = "", isLoading = false, skeletonCount = 4, lightText, showPeek: _showPeek = false, minItemWidth = 220, }) {
|
|
15
|
+
export function SectionCarousel({ title, description, headingVariant = "editorial", pillLabel, backgroundImage, viewMoreHref, viewMoreLabel = "View all →", items, renderItem, perView = CAROUSEL_PER_VIEW.standard, gap = 16, autoScroll = false, autoScrollInterval = 3500, loop = true, keyExtractor, rows = 1, className = "", isLoading = false, skeletonCount = 4, lightText, showPeek: _showPeek = false, minItemWidth = 220, }) {
|
|
16
16
|
if (!isLoading && items.length === 0)
|
|
17
17
|
return null;
|
|
18
18
|
const hasBg = Boolean(backgroundImage);
|
|
@@ -41,5 +41,5 @@ export function SectionCarousel({ title, description, headingVariant = "editoria
|
|
|
41
41
|
headingClass,
|
|
42
42
|
]
|
|
43
43
|
.filter(Boolean)
|
|
44
|
-
.join(" "), children: title }), headingVariant === "editorial" && (_jsxs(Row, { align: "center", justify: "center", gap: "sm", textSize: "xs", color: "faint", className: "mt-1 select-none", "aria-hidden": "true", children: [_jsx(Span, { className: "h-px w-6 bg-current" }), _jsx(Span, { size: "xs", children: "\u2736" }), _jsx(Span, { className: "h-px w-6 bg-current" })] })), description && (_jsx(Text, { className: `${descVariant} mt-2`, size: "base", children: description }))] }), isLoading ? (_jsx(CarouselSkeleton, { count: skeletonCount })) : (_jsx(HorizontalScroller, { items: items, renderItem: renderItem, perView: perView, gap: gap, autoScroll: autoScroll, autoScrollInterval: autoScrollInterval, loop:
|
|
44
|
+
.join(" "), children: title }), headingVariant === "editorial" && (_jsxs(Row, { align: "center", justify: "center", gap: "sm", textSize: "xs", color: "faint", className: "mt-1 select-none", "aria-hidden": "true", children: [_jsx(Span, { className: "h-px w-6 bg-current" }), _jsx(Span, { size: "xs", children: "\u2736" }), _jsx(Span, { className: "h-px w-6 bg-current" })] })), description && (_jsx(Text, { className: `${descVariant} mt-2`, size: "base", children: description }))] }), isLoading ? (_jsx(CarouselSkeleton, { count: skeletonCount })) : (_jsx(HorizontalScroller, { items: items, renderItem: renderItem, perView: perView, gap: gap, autoScroll: autoScroll, autoScrollInterval: autoScrollInterval, loop: loop, keyExtractor: keyExtractor, rows: rows, minItemWidth: minItemWidth, showArrows: true, snapToItems: true, showFadeEdges: true, showScrollbar: false, pauseOnHover: true })), viewMoreHref && !isLoading && (_jsx(Row, { className: "mt-6", justify: "start", children: _jsx(TextLink, { rounded: "lg", paddingX: "xl", paddingY: "sm", href: viewMoreHref, className: `inline-flex items-[center] gap-[0.375rem] border transition-colors ${useLightText ? "border-white/40 text-white hover:bg-[rgba(255,255,255,0.1)]" : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)]"}`, size: "sm", weight: "medium", children: viewMoreLabel }) }))] })] }));
|
|
45
45
|
}
|
|
@@ -14,5 +14,8 @@ export interface ShopByCategorySectionProps {
|
|
|
14
14
|
rootOnly?: boolean;
|
|
15
15
|
rootCategoryId?: string;
|
|
16
16
|
};
|
|
17
|
+
autoScroll?: boolean;
|
|
18
|
+
scrollInterval?: number;
|
|
19
|
+
loop?: boolean;
|
|
17
20
|
}
|
|
18
|
-
export declare function ShopByCategorySection({ title, subtitle, limit, viewMoreHref, viewMoreLabel, className, initialItems, cta, filters, }: ShopByCategorySectionProps): import("react").JSX.Element | null;
|
|
21
|
+
export declare function ShopByCategorySection({ title, subtitle, limit, viewMoreHref, viewMoreLabel, className, initialItems, cta, filters, autoScroll, scrollInterval, loop, }: ShopByCategorySectionProps): import("react").JSX.Element | null;
|
|
@@ -37,7 +37,7 @@ function FilterChip({ label, active, onClick, }) {
|
|
|
37
37
|
: "border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text-muted)] hover:border-[var(--appkit-color-primary)]",
|
|
38
38
|
].join(" "), children: label }));
|
|
39
39
|
}
|
|
40
|
-
export function ShopByCategorySection({ title = "Shop by Category", subtitle, limit = 12, viewMoreHref, viewMoreLabel = "View all categories →", className = "", initialItems, cta, filters, }) {
|
|
40
|
+
export function ShopByCategorySection({ title = "Shop by Category", subtitle, limit = 12, viewMoreHref, viewMoreLabel = "View all categories →", className = "", initialItems, cta, filters, autoScroll = true, scrollInterval = 3500, loop = true, }) {
|
|
41
41
|
const themed = { textPrimary: THEMED_TEXT_PRIMARY };
|
|
42
42
|
const { data: allCategories = [], isLoading } = useTopCategories(limit, { initialData: initialItems });
|
|
43
43
|
const [activeFilter, setActiveFilter] = useState("all");
|
|
@@ -68,5 +68,5 @@ export function ShopByCategorySection({ title = "Shop by Category", subtitle, li
|
|
|
68
68
|
if (!cat)
|
|
69
69
|
return null;
|
|
70
70
|
return (_jsx(FilterChip, { label: cat.name, active: activeFilter === id, onClick: () => setActiveFilter(activeFilter === id ? "all" : id) }, id));
|
|
71
|
-
})] })), isLoading ? (_jsx(Div, { layout: "flex", gap: "3", className: `${__O.hidden} px-[0.25rem]`, children: Array.from({ length: 6 }).map((_, i) => (_jsx(Div, { className: "flex-none h-[104px] w-[108px] animate-pulse", surface: "subtle", rounded: "xl" }, i))) })) : (_jsx(HorizontalScroller, { items: categories, renderItem: (cat) => _jsx(CategoryChip, { category: cat }), perView: CAROUSEL_PER_VIEW.standard, gap: 16, keyExtractor: (cat) => cat.id, autoScroll:
|
|
71
|
+
})] })), isLoading ? (_jsx(Div, { layout: "flex", gap: "3", className: `${__O.hidden} px-[0.25rem]`, children: Array.from({ length: 6 }).map((_, i) => (_jsx(Div, { className: "flex-none h-[104px] w-[108px] animate-pulse", surface: "subtle", rounded: "xl" }, i))) })) : (_jsx(HorizontalScroller, { items: categories, renderItem: (cat) => _jsx(CategoryChip, { category: cat }), perView: CAROUSEL_PER_VIEW.standard, gap: 16, keyExtractor: (cat) => cat.id, autoScroll: autoScroll, autoScrollInterval: scrollInterval, showArrows: true, snapToItems: true, showFadeEdges: true, showScrollbar: false, loop: loop, pauseOnHover: true })), cta && !isLoading && (_jsx(Div, { className: "mt-6 text-left", children: _jsx(Link, { href: cta.href, className: CTA_CLASSES[cta.variant], children: cta.label }) })), !cta && viewMoreHref && !isLoading && (_jsx(Div, { className: "mt-6 text-left", children: _jsx(Link, { href: viewMoreHref, className: "inline-flex items-center gap-[var(--appkit-space-1)] text-[length:var(--appkit-text-sm)] font-medium text-[var(--appkit-color-primary)] hover:opacity-80", children: viewMoreLabel }) }))] }) }));
|
|
72
72
|
}
|
|
@@ -53,7 +53,7 @@ function renderSectionElement(section, newsletterFormSlot, faqItems, slides, liv
|
|
|
53
53
|
}
|
|
54
54
|
case "categories": {
|
|
55
55
|
const cfg = config;
|
|
56
|
-
return (_jsx(ShopByCategorySection, { title: cleanTitle(cfg?.title) || "Shop by Category", viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All categories \u2192", initialItems: sectionData.categories, cta: cfg?.cta, filters: cfg?.filters }));
|
|
56
|
+
return (_jsx(ShopByCategorySection, { title: cleanTitle(cfg?.title) || "Shop by Category", viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All categories \u2192", initialItems: sectionData.categories, cta: cfg?.cta, filters: cfg?.filters, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
|
|
57
57
|
}
|
|
58
58
|
case "stats": {
|
|
59
59
|
const cfg = config;
|
|
@@ -84,27 +84,27 @@ function renderSectionElement(section, newsletterFormSlot, faqItems, slides, liv
|
|
|
84
84
|
}
|
|
85
85
|
case "products": {
|
|
86
86
|
const cfg = config;
|
|
87
|
-
return (_jsx(FeaturedProductsSection, { title: cleanTitle(cfg?.title) || "Featured Products", viewMoreHref: ROUTES.PUBLIC.PRODUCTS, viewMoreLabel: "View all products \u2192", filterByBrand: cfg?.filterByBrand, initialItems: sectionData.products, rows: cfg?.rows, maxItems: cfg?.maxItems, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval }));
|
|
87
|
+
return (_jsx(FeaturedProductsSection, { title: cleanTitle(cfg?.title) || "Featured Products", viewMoreHref: ROUTES.PUBLIC.PRODUCTS, viewMoreLabel: "View all products \u2192", filterByBrand: cfg?.filterByBrand, initialItems: sectionData.products, rows: cfg?.rows, maxItems: cfg?.maxItems, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
|
|
88
88
|
}
|
|
89
89
|
case "auctions": {
|
|
90
90
|
const cfg = config;
|
|
91
|
-
return (_jsx(FeaturedAuctionsSection, { title: cleanTitle(cfg?.title) || "Live Auctions", viewMoreHref: ROUTES.PUBLIC.AUCTIONS, viewMoreLabel: "View all auctions \u2192", filterByBrand: cfg?.filterByBrand, initialItems: sectionData.auctions, rows: cfg?.rows, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval }));
|
|
91
|
+
return (_jsx(FeaturedAuctionsSection, { title: cleanTitle(cfg?.title) || "Live Auctions", viewMoreHref: ROUTES.PUBLIC.AUCTIONS, viewMoreLabel: "View all auctions \u2192", filterByBrand: cfg?.filterByBrand, initialItems: sectionData.auctions, rows: cfg?.rows, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
|
|
92
92
|
}
|
|
93
93
|
case "pre-orders": {
|
|
94
94
|
const cfg = config;
|
|
95
|
-
return (_jsx(FeaturedPreOrdersSection, { title: cleanTitle(cfg?.title) || "Reserve Before It Ships", viewMoreHref: ROUTES.PUBLIC.PRE_ORDERS, viewMoreLabel: "View all pre-orders \u2192", filterByBrand: cfg?.filterByBrand, initialItems: sectionData.preOrders, rows: cfg?.rows, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval }));
|
|
95
|
+
return (_jsx(FeaturedPreOrdersSection, { title: cleanTitle(cfg?.title) || "Reserve Before It Ships", viewMoreHref: ROUTES.PUBLIC.PRE_ORDERS, viewMoreLabel: "View all pre-orders \u2192", filterByBrand: cfg?.filterByBrand, initialItems: sectionData.preOrders, rows: cfg?.rows, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
|
|
96
96
|
}
|
|
97
97
|
case "stores": {
|
|
98
98
|
const cfg = config;
|
|
99
|
-
return (_jsx(FeaturedStoresSection, { title: cleanTitle(cfg?.title) || "Featured Stores", viewMoreHref: ROUTES.PUBLIC.STORES, viewMoreLabel: "View all stores \u2192", initialItems: sectionData.stores }));
|
|
99
|
+
return (_jsx(FeaturedStoresSection, { title: cleanTitle(cfg?.title) || "Featured Stores", viewMoreHref: ROUTES.PUBLIC.STORES, viewMoreLabel: "View all stores \u2192", initialItems: sectionData.stores, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
|
|
100
100
|
}
|
|
101
101
|
case "events": {
|
|
102
102
|
const cfg = config;
|
|
103
|
-
return (_jsx(EventsSection, { title: cleanTitle(cfg?.title) || "Events & Offers", viewMoreHref: ROUTES.PUBLIC.EVENTS, viewMoreLabel: "View all events \u2192", initialItems: sectionData.events }));
|
|
103
|
+
return (_jsx(EventsSection, { title: cleanTitle(cfg?.title) || "Events & Offers", viewMoreHref: ROUTES.PUBLIC.EVENTS, viewMoreLabel: "View all events \u2192", initialItems: sectionData.events, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
|
|
104
104
|
}
|
|
105
105
|
case "reviews": {
|
|
106
106
|
const cfg = config;
|
|
107
|
-
return (_jsx(HomepageCustomerReviewsSection, { title: cleanTitle(cfg?.title) || "What Our Customers Say", viewMoreHref: ROUTES.PUBLIC.REVIEWS, viewMoreLabel: "See all reviews \u2192" }));
|
|
107
|
+
return (_jsx(HomepageCustomerReviewsSection, { title: cleanTitle(cfg?.title) || "What Our Customers Say", viewMoreHref: ROUTES.PUBLIC.REVIEWS, viewMoreLabel: "See all reviews \u2192", autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
|
|
108
108
|
}
|
|
109
109
|
case "banner": {
|
|
110
110
|
const cfg = config;
|
|
@@ -156,7 +156,7 @@ function renderSectionElement(section, newsletterFormSlot, faqItems, slides, liv
|
|
|
156
156
|
}
|
|
157
157
|
case "brands": {
|
|
158
158
|
const cfg = config;
|
|
159
|
-
return (_jsx(BrandsSection, { title: cleanTitle(cfg?.title) || "Top Brands", subtitle: cfg?.subtitle, limit: cfg?.maxBrands || 12, viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All brands \u2192", initialItems: sectionData.brands, cta: cfg?.cta, filters: cfg?.filters }));
|
|
159
|
+
return (_jsx(BrandsSection, { title: cleanTitle(cfg?.title) || "Top Brands", subtitle: cfg?.subtitle, limit: cfg?.maxBrands || 12, viewMoreHref: ROUTES.PUBLIC.CATEGORIES, viewMoreLabel: "All brands \u2192", initialItems: sectionData.brands, cta: cfg?.cta, filters: cfg?.filters, autoScroll: cfg?.autoScroll, scrollInterval: cfg?.scrollInterval, loop: cfg?.loop }));
|
|
160
160
|
}
|
|
161
161
|
case "social-feed": {
|
|
162
162
|
const cfg = config;
|
|
@@ -169,7 +169,7 @@ function renderSectionElement(section, newsletterFormSlot, faqItems, slides, liv
|
|
|
169
169
|
const cfg = config;
|
|
170
170
|
if (!cfg?.cards?.length)
|
|
171
171
|
return null;
|
|
172
|
-
return (_jsx(CustomCardsSection, { title: cleanTitle(cfg.title), layout: cfg.layout ?? "grid", columns: cfg.columns ?? 3, cards: cfg.cards, autoScroll: cfg.autoScroll, scrollIntervalMs: cfg.scrollIntervalMs }));
|
|
172
|
+
return (_jsx(CustomCardsSection, { title: cleanTitle(cfg.title), layout: cfg.layout ?? "grid", columns: cfg.columns ?? 3, cards: cfg.cards, autoScroll: cfg.autoScroll, scrollIntervalMs: cfg.scrollIntervalMs, loop: cfg.loop }));
|
|
173
173
|
}
|
|
174
174
|
case "google-reviews": {
|
|
175
175
|
const cfg = config;
|
|
@@ -196,6 +196,8 @@ export interface CategoriesSectionConfig {
|
|
|
196
196
|
maxCategories: 4;
|
|
197
197
|
autoScroll: boolean;
|
|
198
198
|
scrollInterval: number;
|
|
199
|
+
/** Circular auto-rotate at the carousel edges. Default: true. */
|
|
200
|
+
loop?: boolean;
|
|
199
201
|
/** Optional CTA button rendered below the scroller. */
|
|
200
202
|
cta?: SectionCTA;
|
|
201
203
|
/** Optional client-side filter chips above the scroller. */
|
|
@@ -213,6 +215,8 @@ export interface BrandsSectionConfig {
|
|
|
213
215
|
maxBrands: number;
|
|
214
216
|
autoScroll: boolean;
|
|
215
217
|
scrollInterval: number;
|
|
218
|
+
/** Circular auto-rotate at the carousel edges. Default: true. */
|
|
219
|
+
loop?: boolean;
|
|
216
220
|
/** Optional CTA button rendered below the scroller. */
|
|
217
221
|
cta?: SectionCTA;
|
|
218
222
|
/** Optional client-side filter chips above the scroller. */
|
|
@@ -306,6 +310,8 @@ export interface ReviewsSectionConfig {
|
|
|
306
310
|
mobileItemsPerView: 1;
|
|
307
311
|
autoScroll: boolean;
|
|
308
312
|
scrollInterval: number;
|
|
313
|
+
/** Circular auto-rotate at the carousel edges. Default: true. */
|
|
314
|
+
loop?: boolean;
|
|
309
315
|
source?: "platform" | "google";
|
|
310
316
|
placeId?: string;
|
|
311
317
|
}
|
|
@@ -496,6 +502,8 @@ export interface CustomCardsSectionConfig {
|
|
|
496
502
|
cards: CustomCardsCard[];
|
|
497
503
|
autoScroll?: boolean;
|
|
498
504
|
scrollIntervalMs?: number;
|
|
505
|
+
/** Circular auto-rotate at the carousel edges. Default: true. */
|
|
506
|
+
loop?: boolean;
|
|
499
507
|
}
|
|
500
508
|
export type CollectionCardType = "products" | "auctions" | "pre-orders" | "stores" | "events" | "blog-posts" | "reviews" | "brands" | "categories";
|
|
501
509
|
export interface CollectionCardsEntry {
|
|
@@ -86,6 +86,7 @@ export const homepageSectionsSeedData = [
|
|
|
86
86
|
maxCategories: 4,
|
|
87
87
|
autoScroll: true,
|
|
88
88
|
scrollInterval: 4000,
|
|
89
|
+
loop: true,
|
|
89
90
|
},
|
|
90
91
|
createdAt: daysAgo(90),
|
|
91
92
|
updatedAt: daysAgo(2),
|
|
@@ -101,6 +102,7 @@ export const homepageSectionsSeedData = [
|
|
|
101
102
|
maxBrands: 13,
|
|
102
103
|
autoScroll: true,
|
|
103
104
|
scrollInterval: 3000,
|
|
105
|
+
loop: true,
|
|
104
106
|
},
|
|
105
107
|
createdAt: daysAgo(90),
|
|
106
108
|
updatedAt: daysAgo(2),
|
|
@@ -119,6 +121,7 @@ export const homepageSectionsSeedData = [
|
|
|
119
121
|
mobileItemsPerRow: 2,
|
|
120
122
|
autoScroll: true,
|
|
121
123
|
scrollInterval: 5000,
|
|
124
|
+
loop: true,
|
|
122
125
|
},
|
|
123
126
|
createdAt: daysAgo(90),
|
|
124
127
|
updatedAt: daysAgo(2),
|
|
@@ -137,6 +140,7 @@ export const homepageSectionsSeedData = [
|
|
|
137
140
|
mobileItemsPerRow: 1,
|
|
138
141
|
autoScroll: true,
|
|
139
142
|
scrollInterval: 5000,
|
|
143
|
+
loop: true,
|
|
140
144
|
},
|
|
141
145
|
createdAt: daysAgo(90),
|
|
142
146
|
updatedAt: daysAgo(2),
|
|
@@ -155,6 +159,7 @@ export const homepageSectionsSeedData = [
|
|
|
155
159
|
mobileItemsPerRow: 2,
|
|
156
160
|
autoScroll: true,
|
|
157
161
|
scrollInterval: 5000,
|
|
162
|
+
loop: true,
|
|
158
163
|
},
|
|
159
164
|
createdAt: daysAgo(90),
|
|
160
165
|
updatedAt: daysAgo(2),
|
|
@@ -214,6 +219,7 @@ export const homepageSectionsSeedData = [
|
|
|
214
219
|
mobileItemsPerView: 1,
|
|
215
220
|
autoScroll: true,
|
|
216
221
|
scrollInterval: 4000,
|
|
222
|
+
loop: true,
|
|
217
223
|
},
|
|
218
224
|
createdAt: daysAgo(90),
|
|
219
225
|
updatedAt: daysAgo(2),
|
|
@@ -229,6 +235,7 @@ export const homepageSectionsSeedData = [
|
|
|
229
235
|
maxStores: 8,
|
|
230
236
|
autoScroll: true,
|
|
231
237
|
scrollInterval: 4000,
|
|
238
|
+
loop: true,
|
|
232
239
|
},
|
|
233
240
|
createdAt: daysAgo(90),
|
|
234
241
|
updatedAt: daysAgo(2),
|
|
@@ -244,6 +251,7 @@ export const homepageSectionsSeedData = [
|
|
|
244
251
|
maxEvents: 6,
|
|
245
252
|
autoScroll: true,
|
|
246
253
|
scrollInterval: 6000,
|
|
254
|
+
loop: true,
|
|
247
255
|
},
|
|
248
256
|
createdAt: daysAgo(60),
|
|
249
257
|
updatedAt: daysAgo(2),
|
|
@@ -374,6 +382,7 @@ export const homepageSectionsSeedData = [
|
|
|
374
382
|
mobileItemsPerRow: 2,
|
|
375
383
|
autoScroll: true,
|
|
376
384
|
scrollInterval: 5000,
|
|
385
|
+
loop: true,
|
|
377
386
|
filterByBrand: "brand-konami",
|
|
378
387
|
sortBy: "featured",
|
|
379
388
|
},
|
|
@@ -394,6 +403,7 @@ export const homepageSectionsSeedData = [
|
|
|
394
403
|
mobileItemsPerRow: 2,
|
|
395
404
|
autoScroll: true,
|
|
396
405
|
scrollInterval: 5000,
|
|
406
|
+
loop: true,
|
|
397
407
|
filterByBrand: "brand-upper-deck",
|
|
398
408
|
sortBy: "featured",
|
|
399
409
|
},
|
|
@@ -294,6 +294,9 @@ export function HorizontalScroller({ children, className = "", gap = 16, snapToI
|
|
|
294
294
|
? {
|
|
295
295
|
onMouseEnter: () => setIsPaused(true),
|
|
296
296
|
onMouseLeave: () => setIsPaused(false),
|
|
297
|
+
onTouchStart: () => setIsPaused(true),
|
|
298
|
+
onTouchEnd: () => setIsPaused(false),
|
|
299
|
+
onTouchCancel: () => setIsPaused(false),
|
|
297
300
|
}
|
|
298
301
|
: {};
|
|
299
302
|
const combinedOnScroll = () => {
|