@mohasinac/appkit 2.7.31 → 2.7.33

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.
Files changed (44) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +1 -1
  2. package/dist/_internal/server/features/faqs/index.d.ts +1 -0
  3. package/dist/_internal/server/features/faqs/index.js +1 -0
  4. package/dist/_internal/server/features/faqs/og.d.ts +9 -0
  5. package/dist/_internal/server/features/faqs/og.js +50 -0
  6. package/dist/_internal/server/features/reviews/index.d.ts +2 -0
  7. package/dist/_internal/server/features/reviews/index.js +2 -0
  8. package/dist/_internal/server/features/reviews/og-data.d.ts +1 -0
  9. package/dist/_internal/server/features/reviews/og-data.js +7 -0
  10. package/dist/_internal/server/features/reviews/og.d.ts +19 -0
  11. package/dist/_internal/server/features/reviews/og.js +70 -0
  12. package/dist/_internal/server/features/scams/index.d.ts +1 -0
  13. package/dist/_internal/server/features/scams/index.js +1 -0
  14. package/dist/_internal/server/features/scams/og.d.ts +18 -0
  15. package/dist/_internal/server/features/scams/og.js +59 -0
  16. package/dist/features/about/components/FAQPageView.js +1 -1
  17. package/dist/features/account/components/UserSidebar.js +1 -1
  18. package/dist/features/admin/components/AdminSidebar.js +1 -1
  19. package/dist/features/blog/components/BlogPostView.js +1 -1
  20. package/dist/features/homepage/components/FAQSection.js +1 -1
  21. package/dist/features/homepage/components/FeaturedProductsSection.js +6 -1
  22. package/dist/features/homepage/components/HeroCarousel.js +21 -5
  23. package/dist/features/homepage/components/SectionCarousel.js +3 -1
  24. package/dist/features/homepage/components/ShopByCategorySection.js +2 -2
  25. package/dist/features/reviews/repository/reviews.repository.d.ts +1 -0
  26. package/dist/features/reviews/repository/reviews.repository.js +14 -3
  27. package/dist/features/seller/components/SellerSidebar.js +1 -1
  28. package/dist/features/stores/components/StoreAboutView.js +1 -1
  29. package/dist/features/stores/components/StoreHeader.js +1 -1
  30. package/dist/seed/bids-seed-data.js +7 -4
  31. package/dist/seed/conversations-seed-data.js +52 -0
  32. package/dist/seed/events-seed-data.js +23 -0
  33. package/dist/seed/history-seed-data.js +13 -0
  34. package/dist/seed/notifications-seed-data.js +10 -0
  35. package/dist/seed/orders-seed-data.js +108 -1
  36. package/dist/seed/sessions-seed-data.js +35 -1
  37. package/dist/seed/users-seed-data.js +1 -1
  38. package/dist/seed/wishlists-seed-data.js +5 -1
  39. package/dist/server-entry.d.ts +3 -1
  40. package/dist/server-entry.js +3 -1
  41. package/dist/server.d.ts +4 -0
  42. package/dist/server.js +4 -0
  43. package/dist/tailwind-utilities.css +1 -1
  44. package/package.json +1 -1
@@ -91,5 +91,5 @@ export function DashboardLayoutClient({ variant, groups, permissions, activeHref
91
91
  const adminGroups = variant === "admin"
92
92
  ? filteredGroups
93
93
  : groups;
94
- return (_jsxs(_Fragment, { children: [variant === "admin" && (_jsx(AdminSidebar, { variant: "sidebar", desktopOpen: desktopOpen, mobileOpen: mobileOpen, activePath: activeHref, groups: adminGroups, onCloseMobile: close, onToggle: toggle, className: className })), variant === "store" && (_jsx(StoreSidebar, { variant: "sidebar", desktopOpen: desktopOpen, mobileOpen: mobileOpen, activeHref: activeHref, items: [], groups: filteredGroups, onCloseMobile: close, onToggle: toggle, className: className })), variant === "user" && (_jsx(UserSidebar, { variant: "sidebar", desktopOpen: desktopOpen, mobileOpen: mobileOpen, items: filteredGroups.flatMap((g) => g.items), groups: filteredGroups, onCloseMobile: close, onToggle: toggle, className: className })), _jsx("div", { className: "w-full px-4 py-6 md:pl-5 md:pr-6 lg:pr-8 min-h-[calc(100dvh-var(--header-height,3.5rem))]", children: children })] }));
94
+ return (_jsxs(_Fragment, { children: [variant === "admin" && (_jsx(AdminSidebar, { variant: "sidebar", desktopOpen: desktopOpen, mobileOpen: mobileOpen, activePath: activeHref, groups: adminGroups, onCloseMobile: close, onToggle: toggle, className: className })), variant === "store" && (_jsx(StoreSidebar, { variant: "sidebar", desktopOpen: desktopOpen, mobileOpen: mobileOpen, activeHref: activeHref, items: [], groups: filteredGroups, onCloseMobile: close, onToggle: toggle, className: className })), variant === "user" && (_jsx(UserSidebar, { variant: "sidebar", desktopOpen: desktopOpen, mobileOpen: mobileOpen, items: filteredGroups.flatMap((g) => g.items), groups: filteredGroups, onCloseMobile: close, onToggle: toggle, className: className })), _jsx("div", { className: "w-full px-4 py-8 md:pl-7 md:pr-6 lg:pl-8 lg:pr-8 min-h-[calc(100dvh-var(--header-height,3.5rem))]", children: children })] }));
95
95
  }
@@ -0,0 +1 @@
1
+ export { renderFaqOg, renderFaqOgImage, type FaqOgData } from "./og";
@@ -0,0 +1 @@
1
+ export { renderFaqOg, renderFaqOgImage } from "./og";
@@ -0,0 +1,9 @@
1
+ import type { ReactElement } from "react";
2
+ export interface FaqOgData {
3
+ categoryLabel: string;
4
+ siteName: string;
5
+ }
6
+ export declare function renderFaqOg(category: string, opts: {
7
+ siteName: string;
8
+ }): ReactElement;
9
+ export declare function renderFaqOgImage(data: FaqOgData): ReactElement;
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function renderFaqOg(category, opts) {
3
+ const label = category
4
+ .replace(/-/g, " ")
5
+ .replace(/\b\w/g, (c) => c.toUpperCase());
6
+ return renderFaqOgImage({ categoryLabel: label, siteName: opts.siteName });
7
+ }
8
+ export function renderFaqOgImage(data) {
9
+ const { categoryLabel, siteName } = data;
10
+ return (_jsxs("div", { style: {
11
+ display: "flex",
12
+ width: "100%",
13
+ height: "100%",
14
+ background: "linear-gradient(135deg, #3570fc 0%, #1343de 100%)",
15
+ fontFamily: "sans-serif",
16
+ alignItems: "center",
17
+ justifyContent: "center",
18
+ padding: "80px",
19
+ position: "relative",
20
+ overflow: "hidden",
21
+ }, children: [_jsx("div", { style: {
22
+ position: "absolute",
23
+ inset: 0,
24
+ opacity: 0.06,
25
+ backgroundImage: "radial-gradient(circle at 25% 25%, white 1px, transparent 0), radial-gradient(circle at 75% 75%, white 1px, transparent 0)",
26
+ backgroundSize: "64px 64px",
27
+ } }), _jsxs("div", { style: {
28
+ position: "relative",
29
+ display: "flex",
30
+ flexDirection: "column",
31
+ alignItems: "center",
32
+ gap: 24,
33
+ textAlign: "center",
34
+ }, children: [_jsxs("div", { style: {
35
+ fontSize: 20,
36
+ color: "rgba(255,255,255,0.7)",
37
+ letterSpacing: 3,
38
+ textTransform: "uppercase",
39
+ }, children: [siteName, " \u00B7 Help Centre"] }), _jsx("div", { style: {
40
+ fontSize: 72,
41
+ fontWeight: 800,
42
+ color: "white",
43
+ lineHeight: 1.1,
44
+ letterSpacing: "-1px",
45
+ }, children: categoryLabel }), _jsx("div", { style: {
46
+ fontSize: 32,
47
+ color: "rgba(255,255,255,0.8)",
48
+ fontWeight: 400,
49
+ }, children: "Frequently Asked Questions" })] })] }));
50
+ }
@@ -1,3 +1,5 @@
1
1
  export { getReviewsForProduct, getReviewsForStore, hasUserPurchasedProduct } from "./data";
2
+ export { getReviewById } from "./og-data";
3
+ export { renderReviewOg, renderReviewOgImage, type ReviewOgData } from "./og";
2
4
  export { createReviewAction, replyToReviewAction, deleteReviewAction, markReviewHelpfulAction, } from "./actions";
3
5
  export { REVIEWS_PAGE_SIZE, REVIEW_MAX_RATING, REVIEW_MIN_RATING, REVIEW_IMAGES_MAX } from "../../../shared/features/reviews/config";
@@ -1,3 +1,5 @@
1
1
  export { getReviewsForProduct, getReviewsForStore, hasUserPurchasedProduct } from "./data";
2
+ export { getReviewById } from "./og-data";
3
+ export { renderReviewOg, renderReviewOgImage } from "./og";
2
4
  export { createReviewAction, replyToReviewAction, deleteReviewAction, markReviewHelpfulAction, } from "./actions";
3
5
  export { REVIEWS_PAGE_SIZE, REVIEW_MAX_RATING, REVIEW_MIN_RATING, REVIEW_IMAGES_MAX } from "../../../shared/features/reviews/config";
@@ -0,0 +1 @@
1
+ export declare const getReviewById: (id: string) => Promise<import("../../../..").ReviewDocument | null>;
@@ -0,0 +1,7 @@
1
+ import { cache } from "react";
2
+ import { reviewRepository } from "../../../../repositories";
3
+ export const getReviewById = cache(async (id) => {
4
+ if (!id)
5
+ return null;
6
+ return reviewRepository.findById(id).catch(() => null);
7
+ });
@@ -0,0 +1,19 @@
1
+ import type { ReactElement } from "react";
2
+ export interface ReviewOgData {
3
+ productTitle: string;
4
+ rating: number;
5
+ reviewerInitial: string;
6
+ reviewExcerpt?: string | null;
7
+ }
8
+ interface ReviewDocLike {
9
+ title?: string | null;
10
+ productName?: string | null;
11
+ rating?: number | null;
12
+ userName?: string | null;
13
+ body?: string | null;
14
+ }
15
+ export declare function renderReviewOg(doc: ReviewDocLike | null | undefined, opts: {
16
+ siteName: string;
17
+ }): ReactElement;
18
+ export declare function renderReviewOgImage(data: ReviewOgData, siteName: string): ReactElement;
19
+ export {};
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ function stars(n) {
3
+ return "★".repeat(Math.min(5, Math.max(1, Math.round(n))));
4
+ }
5
+ export function renderReviewOg(doc, opts) {
6
+ return renderReviewOgImage({
7
+ productTitle: doc?.productName ?? doc?.title ?? "Collectible",
8
+ rating: doc?.rating ?? 5,
9
+ reviewerInitial: (doc?.userName?.[0] ?? "?").toUpperCase(),
10
+ reviewExcerpt: doc?.body ? doc.body.slice(0, 120) : null,
11
+ }, opts.siteName);
12
+ }
13
+ export function renderReviewOgImage(data, siteName) {
14
+ const { productTitle, rating, reviewerInitial, reviewExcerpt } = data;
15
+ return (_jsxs("div", { style: {
16
+ display: "flex",
17
+ width: "100%",
18
+ height: "100%",
19
+ background: "#0f172a",
20
+ fontFamily: "sans-serif",
21
+ alignItems: "center",
22
+ justifyContent: "center",
23
+ padding: "60px",
24
+ position: "relative",
25
+ }, children: [_jsx("div", { style: {
26
+ position: "absolute",
27
+ inset: 0,
28
+ background: "linear-gradient(135deg, #0f172a 0%, #1e293b 100%)",
29
+ } }), _jsxs("div", { style: {
30
+ position: "relative",
31
+ display: "flex",
32
+ flexDirection: "column",
33
+ width: "100%",
34
+ maxWidth: 1080,
35
+ gap: 32,
36
+ }, children: [_jsxs("div", { style: { fontSize: 18, color: "#94a3b8", letterSpacing: 2, textTransform: "uppercase" }, children: [siteName, " \u00B7 Customer Review"] }), _jsxs("div", { style: {
37
+ display: "flex",
38
+ alignItems: "center",
39
+ gap: 24,
40
+ }, children: [_jsx("div", { style: {
41
+ width: 80,
42
+ height: 80,
43
+ borderRadius: 40,
44
+ background: "linear-gradient(135deg, #3570fc, #e91e8c)",
45
+ display: "flex",
46
+ alignItems: "center",
47
+ justifyContent: "center",
48
+ fontSize: 36,
49
+ fontWeight: 700,
50
+ color: "white",
51
+ flexShrink: 0,
52
+ }, children: reviewerInitial }), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [_jsx("div", { style: { fontSize: 32, color: "#f59e0b", letterSpacing: 2 }, children: stars(rating) }), _jsxs("div", { style: { fontSize: 20, color: "#64748b" }, children: [rating, "/5"] })] })] }), _jsx("div", { style: {
53
+ fontSize: 48,
54
+ fontWeight: 700,
55
+ color: "#f1f5f9",
56
+ lineHeight: 1.2,
57
+ display: "-webkit-box",
58
+ WebkitLineClamp: 2,
59
+ WebkitBoxOrient: "vertical",
60
+ overflow: "hidden",
61
+ }, children: productTitle }), reviewExcerpt && (_jsxs("div", { style: {
62
+ fontSize: 24,
63
+ color: "#94a3b8",
64
+ lineHeight: 1.5,
65
+ display: "-webkit-box",
66
+ WebkitLineClamp: 2,
67
+ WebkitBoxOrient: "vertical",
68
+ overflow: "hidden",
69
+ }, children: ["\u201C", reviewExcerpt, "\u201D"] }))] })] }));
70
+ }
@@ -1 +1,2 @@
1
1
  export * from "./data";
2
+ export { renderScamOg, renderScamOgImage, type ScamOgData } from "./og";
@@ -1 +1,2 @@
1
1
  export * from "./data";
2
+ export { renderScamOg, renderScamOgImage } from "./og";
@@ -0,0 +1,18 @@
1
+ import type { ReactElement } from "react";
2
+ export interface ScamOgData {
3
+ displayName: string;
4
+ scamTypeLabel: string;
5
+ reportCount: number;
6
+ }
7
+ interface ScammerDocLike {
8
+ displayNames?: string[];
9
+ scamType?: string;
10
+ incidents?: unknown[];
11
+ totalReports?: number;
12
+ }
13
+ export declare function renderScamOg(doc: ScammerDocLike | null | undefined, opts: {
14
+ siteName: string;
15
+ scamTypeLabel?: string;
16
+ }): ReactElement;
17
+ export declare function renderScamOgImage(data: ScamOgData, siteName: string): ReactElement;
18
+ export {};
@@ -0,0 +1,59 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function renderScamOg(doc, opts) {
3
+ return renderScamOgImage({
4
+ displayName: doc?.displayNames?.[0] ?? "Unknown",
5
+ scamTypeLabel: opts.scamTypeLabel ?? doc?.scamType ?? "Scammer",
6
+ reportCount: doc?.totalReports ?? 0,
7
+ }, opts.siteName);
8
+ }
9
+ export function renderScamOgImage(data, siteName) {
10
+ const { displayName, scamTypeLabel, reportCount } = data;
11
+ return (_jsxs("div", { style: {
12
+ display: "flex",
13
+ width: "100%",
14
+ height: "100%",
15
+ background: "#0f172a",
16
+ fontFamily: "sans-serif",
17
+ alignItems: "center",
18
+ padding: "60px 80px",
19
+ position: "relative",
20
+ overflow: "hidden",
21
+ }, children: [_jsx("div", { style: {
22
+ position: "absolute",
23
+ inset: 0,
24
+ background: "linear-gradient(135deg, #1e0505 0%, #0f172a 60%)",
25
+ } }), _jsxs("div", { style: {
26
+ position: "relative",
27
+ display: "flex",
28
+ flexDirection: "column",
29
+ gap: 28,
30
+ width: "100%",
31
+ }, children: [_jsxs("div", { style: {
32
+ display: "flex",
33
+ alignItems: "center",
34
+ gap: 12,
35
+ }, children: [_jsx("div", { style: {
36
+ background: "#dc2626",
37
+ color: "white",
38
+ padding: "6px 18px",
39
+ borderRadius: 6,
40
+ fontSize: 16,
41
+ fontWeight: 700,
42
+ letterSpacing: 2,
43
+ textTransform: "uppercase",
44
+ }, children: "\u26A0 Verified Scammer" }), _jsxs("div", { style: { fontSize: 18, color: "#94a3b8", letterSpacing: 1 }, children: [siteName, " \u00B7 Scam Registry"] })] }), _jsx("div", { style: {
45
+ fontSize: 72,
46
+ fontWeight: 800,
47
+ color: "#f1f5f9",
48
+ lineHeight: 1.1,
49
+ letterSpacing: "-1px",
50
+ }, children: displayName }), _jsxs("div", { style: { display: "flex", gap: 24, alignItems: "center" }, children: [_jsx("div", { style: {
51
+ background: "#1e293b",
52
+ border: "1px solid #334155",
53
+ borderRadius: 8,
54
+ padding: "10px 20px",
55
+ fontSize: 22,
56
+ color: "#f87171",
57
+ fontWeight: 600,
58
+ }, children: scamTypeLabel }), reportCount > 0 && (_jsxs("div", { style: { fontSize: 22, color: "#64748b" }, children: [reportCount, " ", reportCount === 1 ? "report" : "reports"] }))] }), _jsx("div", { style: { fontSize: 20, color: "#64748b", marginTop: 8 }, children: "Do not transact with this individual. Verified by the LetItRip community." })] })] }));
59
+ }
@@ -23,5 +23,5 @@ export async function FAQPageView({ category, heroBannerClass = DEFAULT_HERO_CLA
23
23
  ? "bg-primary/10 text-primary font-semibold"
24
24
  : "hover:bg-neutral-100 dark:hover:bg-neutral-800"}`, children: [_jsxs("span", { className: `${flex.row} gap-2`, children: [_jsx(HelpCircle, { className: "w-4 h-4" }), t("allCategories")] }), _jsx(ChevronRight, { className: "w-3.5 h-3.5 opacity-50" })] }), categories.map((cat) => (_jsxs(TextLink, { href: String(ROUTES.PUBLIC.FAQ_CATEGORY(cat.slug)), className: `flex items-center justify-between px-3 py-2 rounded-lg text-sm transition-colors ${category === cat.slug
25
25
  ? "bg-primary/10 text-primary font-semibold"
26
- : "hover:bg-neutral-100 dark:hover:bg-neutral-800"}`, children: [_jsxs("span", { className: `${flex.row} gap-2`, children: [_jsx("span", { children: cat.icon }), cat.label] }), _jsx(ChevronRight, { className: "w-3.5 h-3.5 opacity-50" })] }, cat.slug)))] })] })), _jsxs("div", { className: "flex-1 min-w-0", "data-section": "faqpageview-div-97", children: [visibleItems.length === 0 ? (_jsxs(Section, { className: `text-center py-16 rounded-2xl border ${themed.border} ${themed.bgSecondary}`, children: [_jsx(HelpCircle, { className: "w-10 h-10 mx-auto mb-3 text-neutral-300 dark:text-neutral-600" }), _jsx(Heading, { level: 3, className: "mb-2 text-base", children: t("emptyTitle") }), _jsx(Text, { variant: "secondary", className: "text-sm", children: t("emptyText") })] })) : (_jsx(Stack, { gap: "sm", className: "space-y-3", children: visibleItems.map((item, i) => (_jsxs("details", { className: `group rounded-xl border ${themed.border} ${themed.bgPrimary} overflow-hidden`, children: [_jsxs("summary", { className: `flex items-center justify-between px-5 py-4 cursor-pointer list-none select-none font-medium text-sm hover:bg-neutral-50 dark:hover:bg-neutral-800/50 transition-colors`, children: [_jsx("span", { children: item.question }), _jsx(ChevronRight, { className: "w-4 h-4 flex-shrink-0 ml-3 transition-transform group-open:rotate-90" })] }), _jsx("div", { className: "px-5 pb-5 pt-1", "data-section": "faqpageview-div-98", children: _jsx(RichTextRenderer, { html: item.answer, proseClass: "prose prose-sm max-w-none dark:prose-invert", className: "text-neutral-600 dark:text-neutral-300" }) })] }, i))) })), _jsxs(Section, { className: `mt-10 rounded-2xl p-6 text-center border ${themed.border} ${themed.bgSecondary}`, children: [_jsx(Heading, { level: 3, className: "mb-2 text-base", children: t("stillNeedHelpTitle") }), _jsx(Text, { variant: "secondary", className: "text-sm mb-4", children: t("stillNeedHelpText") }), _jsxs("div", { className: `${flex.center} gap-4 flex-wrap`, "data-section": "faqpageview-div-99", children: [_jsx(TextLink, { href: String(ROUTES.PUBLIC.HELP), children: t("helpCenter") }), _jsx(TextLink, { href: String(ROUTES.PUBLIC.CONTACT), variant: "muted", children: t("contactUs") })] })] })] })] }) })] }));
26
+ : "hover:bg-neutral-100 dark:hover:bg-neutral-800"}`, children: [_jsxs("span", { className: `${flex.row} gap-2`, children: [_jsx("span", { children: cat.icon }), cat.label] }), _jsx(ChevronRight, { className: "w-3.5 h-3.5 opacity-50" })] }, cat.slug)))] })] })), _jsxs("div", { className: "flex-1 min-w-0", "data-section": "faqpageview-div-97", children: [visibleItems.length === 0 ? (_jsxs(Section, { className: `text-center py-16 rounded-2xl border ${themed.border} ${themed.bgSecondary}`, children: [_jsx(HelpCircle, { className: "w-10 h-10 mx-auto mb-3 text-neutral-300 dark:text-neutral-600" }), _jsx(Heading, { level: 3, className: "mb-2 text-base", children: t("emptyTitle") }), _jsx(Text, { variant: "secondary", className: "text-sm", children: t("emptyText") })] })) : (_jsx(Stack, { gap: "sm", className: "space-y-3", children: visibleItems.map((item, i) => (_jsxs("details", { className: `group rounded-xl border ${themed.border} ${themed.bgPrimary} overflow-hidden`, children: [_jsxs("summary", { className: `flex items-center justify-between px-5 py-4 cursor-pointer list-none select-none font-medium text-sm hover:bg-neutral-50 dark:hover:bg-neutral-800/50 transition-colors`, children: [_jsx("span", { children: item.question }), _jsx(ChevronRight, { className: "w-4 h-4 flex-shrink-0 ml-3 transition-transform group-open:rotate-90" })] }), _jsx("div", { className: "px-5 pb-5 pt-1", "data-section": "faqpageview-div-98", children: _jsx(RichTextRenderer, { html: item.answer, proseClass: "prose prose-sm max-w-none dark:prose-invert" }) })] }, i))) })), _jsxs(Section, { className: `mt-10 rounded-2xl p-6 text-center border ${themed.border} ${themed.bgSecondary}`, children: [_jsx(Heading, { level: 3, className: "mb-2 text-base", children: t("stillNeedHelpTitle") }), _jsx(Text, { variant: "secondary", className: "text-sm mb-4", children: t("stillNeedHelpText") }), _jsxs("div", { className: `${flex.center} gap-4 flex-wrap`, "data-section": "faqpageview-div-99", children: [_jsx(TextLink, { href: String(ROUTES.PUBLIC.HELP), children: t("helpCenter") }), _jsx(TextLink, { href: String(ROUTES.PUBLIC.CONTACT), variant: "muted", children: t("contactUs") })] })] })] })] }) })] }));
27
27
  }
@@ -63,7 +63,7 @@ export function UserSidebar({ items, groups, mobileOpen = false, onCloseMobile,
63
63
  height: "calc(100vh - var(--header-height, 3.5rem))",
64
64
  width: "13rem",
65
65
  transform: desktopOpen ? "translateX(0)" : "translateX(calc(-100% + 1.25rem))",
66
- }, children: [_jsxs("div", { className: "flex-1 bg-white dark:bg-slate-900 border-r border-zinc-200 dark:border-slate-800 flex flex-col overflow-hidden shadow-xl", children: [_jsx("div", { className: "px-4 py-3.5 border-b border-zinc-100 dark:border-slate-800 shrink-0", children: _jsx("span", { className: "text-xs font-semibold uppercase tracking-widest text-zinc-400 dark:text-zinc-500", children: "My Account" }) }), _jsx("div", { className: "flex-1 overflow-y-auto", children: _jsx(DrawerContent, { groups: groups, items: items, activeHref: pathname }) })] }), _jsx("button", { type: "button", onClick: handleToggle, "aria-label": desktopOpen ? "Collapse sidebar" : "Expand sidebar", className: "w-5 shrink-0 bg-primary dark:bg-secondary hover:bg-primary-600 dark:hover:bg-secondary-600 flex items-center justify-center transition-colors rounded-r-md shadow-md", children: _jsx("svg", { className: "w-3 h-3 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: desktopOpen ? (_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M11 19l-7-7 7-7m8 14l-7-7 7-7" })) : (_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M13 5l7 7-7 7M5 5l7 7-7 7" })) }) })] }), _jsx("div", { className: "md:hidden", children: _jsx(BottomSheet, { open: mobileOpen, onClose: close, title: "My Account", children: _jsx(DrawerContent, { groups: groups, items: items, activeHref: pathname, onItemClick: close }) }) })] }));
66
+ }, children: [_jsxs("div", { className: "flex-1 bg-white dark:bg-slate-900 border-r border-zinc-200 dark:border-slate-800 flex flex-col overflow-hidden shadow-xl", children: [_jsx("div", { className: "px-4 py-3.5 border-b border-zinc-100 dark:border-slate-800 shrink-0", children: _jsx("span", { className: "text-xs font-semibold uppercase tracking-widest text-zinc-400 dark:text-zinc-500", children: "My Account" }) }), _jsx("div", { className: "flex-1 overflow-y-auto", children: _jsx(DrawerContent, { groups: groups, items: items, activeHref: pathname }) })] }), _jsx("button", { type: "button", onClick: handleToggle, "aria-label": desktopOpen ? "Collapse sidebar" : "Expand sidebar", className: "w-5 shrink-0 flex items-center justify-center transition-opacity rounded-r-md shadow-md hover:opacity-85", style: { background: "linear-gradient(to bottom, var(--appkit-color-cobalt-700), var(--appkit-color-cobalt))" }, children: _jsx("svg", { className: "w-3 h-3 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: desktopOpen ? (_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M11 19l-7-7 7-7m8 14l-7-7 7-7" })) : (_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M13 5l7 7-7 7M5 5l7 7-7 7" })) }) })] }), _jsx("div", { className: "md:hidden", children: _jsx(BottomSheet, { open: mobileOpen, onClose: close, title: "My Account", children: _jsx(DrawerContent, { groups: groups, items: items, activeHref: pathname, onItemClick: close }) }) })] }));
67
67
  }
68
68
  // ── Overlay variant (default) — portal on desktop, bottom-sheet on mobile ─
69
69
  const content = (_jsx(DrawerContent, { groups: groups, items: items, activeHref: pathname, onItemClick: close }));
@@ -47,7 +47,7 @@ export function AdminSidebar({ renderNavItems, renderHeader, renderFooter, activ
47
47
  height: "calc(100vh - var(--header-height, 3.5rem))",
48
48
  width: "18rem",
49
49
  transform: desktopOpen ? "translateX(0)" : "translateX(calc(-100% + 1.25rem))",
50
- }, children: [_jsxs("div", { className: "flex-1 bg-white dark:bg-slate-950 border-r border-zinc-200 dark:border-slate-800 flex flex-col overflow-hidden shadow-xl", children: [_jsx("div", { className: "px-4 py-3.5 border-b border-zinc-100 dark:border-slate-800 shrink-0", children: renderHeader ? renderHeader() : _jsx("span", { className: "text-xs font-semibold uppercase tracking-widest text-zinc-400 dark:text-zinc-500", children: "Admin Panel" }) }), _jsx("div", { className: "flex-1 overflow-y-auto", children: navContent }), renderFooter && _jsx("div", { className: "px-4 py-3 border-t border-zinc-200 dark:border-slate-800", children: renderFooter() })] }), _jsx("button", { type: "button", onClick: handleToggle, "aria-label": desktopOpen ? "Collapse sidebar" : "Expand sidebar", className: "w-5 shrink-0 bg-primary dark:bg-secondary hover:bg-primary-600 dark:hover:bg-secondary-600 flex items-center justify-center transition-colors rounded-r-md shadow-md", children: _jsx("svg", { className: "w-3 h-3 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: desktopOpen ? (_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M11 19l-7-7 7-7m8 14l-7-7 7-7" })) : (_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M13 5l7 7-7 7M5 5l7 7-7 7" })) }) })] }), _jsx("div", { className: "md:hidden", children: _jsx(BottomSheet, { open: mobileOpen, onClose: close, title: "Admin Panel", children: mobileNavContent }) })] }));
50
+ }, children: [_jsxs("div", { className: "flex-1 bg-white dark:bg-slate-950 border-r border-zinc-200 dark:border-slate-800 flex flex-col overflow-hidden shadow-xl", children: [_jsx("div", { className: "px-4 py-3.5 border-b border-zinc-100 dark:border-slate-800 shrink-0", children: renderHeader ? renderHeader() : _jsx("span", { className: "text-xs font-semibold uppercase tracking-widest text-zinc-400 dark:text-zinc-500", children: "Admin Panel" }) }), _jsx("div", { className: "flex-1 overflow-y-auto", children: navContent }), renderFooter && _jsx("div", { className: "px-4 py-3 border-t border-zinc-200 dark:border-slate-800", children: renderFooter() })] }), _jsx("button", { type: "button", onClick: handleToggle, "aria-label": desktopOpen ? "Collapse sidebar" : "Expand sidebar", className: "w-5 shrink-0 flex items-center justify-center transition-opacity rounded-r-md shadow-md hover:opacity-85", style: { background: "linear-gradient(to bottom, var(--appkit-color-cobalt-700), var(--appkit-color-cobalt))" }, children: _jsx("svg", { className: "w-3 h-3 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: desktopOpen ? (_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M11 19l-7-7 7-7m8 14l-7-7 7-7" })) : (_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M13 5l7 7-7 7M5 5l7 7-7 7" })) }) })] }), _jsx("div", { className: "md:hidden", children: _jsx(BottomSheet, { open: mobileOpen, onClose: close, title: "Admin Panel", children: mobileNavContent }) })] }));
51
51
  }
52
52
  return (_jsxs(_Fragment, { children: [mounted && mobileOpen &&
53
53
  createPortal(_jsx(DrawerPanel, { title: "Admin Panel", onClose: close, children: navContent }), document.body), _jsx("div", { className: "md:hidden", children: _jsx(BottomSheet, { open: mobileOpen, onClose: close, title: "Admin Panel", children: mobileNavContent }) })] }));
@@ -45,5 +45,5 @@ export function BlogPostView({ slug, initialData, labels = {}, renderImage, rend
45
45
  })
46
46
  : "";
47
47
  const coverImageUrl = getMediaUrl(post.coverImage);
48
- return (_jsxs(Div, { className: `min-h-screen ${className}`, children: [coverImageUrl && (_jsxs(Div, { className: "relative h-72 md:h-96 overflow-hidden", children: [renderImage ? (renderImage(post)) : (_jsx(Div, { role: "img", "aria-label": post.title, className: "h-full w-full bg-cover bg-center", style: { backgroundImage: `url(${coverImageUrl})` } })), _jsx(Div, { className: "absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" })] })), _jsxs(Div, { className: "max-w-3xl mx-auto px-4 py-12", children: [renderBlogPostHeader(post, date, labels), post.tags && post.tags.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mb-8", children: post.tags.map((tag) => (_jsxs(Span, { className: "inline-block px-3 py-1 rounded-full bg-neutral-100 text-neutral-600 text-xs font-medium", children: ["#", tag] }, tag))) })), renderAuthorBio && _jsx(Div, { className: "mb-6", children: renderAuthorBio(post) }), _jsx(Div, { className: "bg-white dark:bg-slate-900 rounded-xl border border-neutral-200 dark:border-slate-700 p-8 mb-12", children: renderContent ? renderContent(post) : (_jsx(RichText, { html: normalizeRichTextHtml(post.content ?? ""), proseClass: "prose max-w-none dark:prose-invert", className: "text-neutral-800 dark:text-neutral-100" })) }), renderBlogPostRelated(related, labels, renderRelatedCard), renderBackButton && _jsx(Div, { className: "mt-10 pt-8 border-t border-neutral-200", children: renderBackButton() })] })] }));
48
+ return (_jsxs(Div, { className: `min-h-screen ${className}`, children: [coverImageUrl && (_jsxs(Div, { className: "relative h-72 md:h-96 overflow-hidden", children: [renderImage ? (renderImage(post)) : (_jsx(Div, { role: "img", "aria-label": post.title, className: "h-full w-full bg-cover bg-center", style: { backgroundImage: `url(${coverImageUrl})` } })), _jsx(Div, { className: "absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" })] })), _jsxs(Div, { className: "max-w-3xl mx-auto px-4 py-12", children: [renderBlogPostHeader(post, date, labels), post.tags && post.tags.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mb-8", children: post.tags.map((tag) => (_jsxs(Span, { className: "inline-block px-3 py-1 rounded-full bg-neutral-100 text-neutral-600 text-xs font-medium", children: ["#", tag] }, tag))) })), renderAuthorBio && _jsx(Div, { className: "mb-6", children: renderAuthorBio(post) }), _jsx(Div, { className: "bg-white dark:bg-slate-900 rounded-xl border border-neutral-200 dark:border-slate-700 p-8 mb-12", children: renderContent ? renderContent(post) : (_jsx(RichText, { html: normalizeRichTextHtml(post.content ?? ""), proseClass: "prose max-w-none dark:prose-invert" })) }), renderBlogPostRelated(related, labels, renderRelatedCard), renderBackButton && _jsx(Div, { className: "mt-10 pt-8 border-t border-neutral-200", children: renderBackButton() })] })] }));
49
49
  }
@@ -65,6 +65,6 @@ export function FAQSection({ title, subtitle, items, tabs: tabsProp, showCategor
65
65
  }, [items, showTabs, activeTab]);
66
66
  return (_jsx(Section, { className: `py-16 px-4 sm:px-8 ${themed.bgSecondary} ${className}`, children: _jsxs("div", { className: "w-full max-w-4xl mx-auto", "data-section": "faqsection-div-320", children: [_jsxs("div", { className: "text-center mb-10", "data-section": "faqsection-div-321", children: [_jsx(Heading, { level: 2, variant: "none", className: "bg-gradient-to-r from-primary to-cobalt bg-clip-text text-transparent text-3xl md:text-4xl font-bold mb-3", children: title }), subtitle && (_jsx(Text, { className: `text-base max-w-xl mx-auto ${themed.textSecondary}`, children: subtitle }))] }), showTabs && (_jsxs("div", { className: `${flex.center} flex-wrap gap-2 mb-8`, "data-section": "faqsection-div-322", children: [_jsx(Button, { variant: activeTab === "all" ? "primary" : "ghost", size: "sm", onClick: () => setActiveTab("all"), className: "rounded-full", children: "All" }), derivedTabs.map((tab) => (_jsx(Button, { variant: activeTab === tab.value ? "primary" : "ghost", size: "sm", onClick: () => setActiveTab(tab.value), className: "rounded-full", children: tab.label }, tab.value)))] })), _jsxs("div", { className: "space-y-2", "data-section": "faqsection-div-323", children: [visibleItems.length === 0 && (_jsx(Text, { className: `text-center py-12 ${themed.textSecondary}`, children: "No FAQs in this category yet." })), visibleItems.map((faq) => {
67
67
  const isOpen = openIds.has(faq.id);
68
- return (_jsxs("div", { className: `${themed.bgPrimary} rounded-xl overflow-hidden border transition-all duration-200 ${isOpen ? "border-primary/40 shadow-sm" : `${themed.border}`}`, "data-section": "faqsection-div-324", children: [_jsxs(Button, { variant: "ghost", className: `w-full text-left px-5 py-4 ${flex.between} gap-4 hover:${themed.bgTertiary} transition-colors rounded-none`, onClick: () => toggle(faq.id), "aria-expanded": isOpen, children: [_jsx(Span, { className: `text-sm font-medium flex-1 leading-snug ${isOpen ? "text-primary" : themed.textPrimary}`, children: faq.question }), _jsx(ChevronDown, { className: `w-4 h-4 flex-shrink-0 transition-transform duration-250 ${isOpen ? "rotate-180 text-primary" : themed.textSecondary}` })] }), _jsx("div", { className: `grid transition-all duration-300 ease-out ${isOpen ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"}`, "data-section": "faqsection-div-325", children: _jsx("div", { className: "overflow-hidden", "data-section": "faqsection-div-326", children: _jsx("div", { className: "px-5 pb-5 pt-0", children: isHtml(faq.answer) ? (_jsx(RichText, { html: faq.answer, proseClass: "prose prose-sm max-w-none dark:prose-invert", className: themed.textSecondary })) : (_jsx(Text, { className: `text-sm leading-relaxed ${themed.textSecondary}`, children: faq.answer })) }) }) })] }, faq.id));
68
+ return (_jsxs("div", { className: `${themed.bgPrimary} rounded-xl overflow-hidden border transition-all duration-200 ${isOpen ? "border-primary/40 shadow-sm" : `${themed.border}`}`, "data-section": "faqsection-div-324", children: [_jsxs(Button, { variant: "ghost", className: `w-full text-left px-5 py-4 ${flex.between} gap-4 hover:${themed.bgTertiary} transition-colors rounded-none`, onClick: () => toggle(faq.id), "aria-expanded": isOpen, children: [_jsx(Span, { className: `text-sm font-medium flex-1 leading-snug ${isOpen ? "text-primary" : themed.textPrimary}`, children: faq.question }), _jsx(ChevronDown, { className: `w-4 h-4 flex-shrink-0 transition-transform duration-250 ${isOpen ? "rotate-180 text-primary" : themed.textSecondary}` })] }), _jsx("div", { className: `grid transition-all duration-300 ease-out ${isOpen ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"}`, "data-section": "faqsection-div-325", children: _jsx("div", { className: "overflow-hidden", "data-section": "faqsection-div-326", children: _jsx("div", { className: "px-5 pb-5 pt-0", children: isHtml(faq.answer) ? (_jsx(RichText, { html: faq.answer, proseClass: "prose prose-sm max-w-none dark:prose-invert" })) : (_jsx(Text, { className: `text-sm leading-relaxed ${themed.textSecondary}`, children: faq.answer })) }) }) })] }, faq.id));
69
69
  })] }), viewMoreHref && (_jsx("div", { className: "text-center mt-10", "data-section": "faqsection-div-327", children: _jsxs(TextLink, { href: viewMoreHref, className: "inline-flex items-center gap-2 px-6 py-2.5 rounded-full text-sm font-medium bg-primary/10 text-primary hover:bg-primary/20 dark:bg-primary/15 transition-colors", children: [viewMoreLabel, hasMore && moreCount > 0 && (_jsxs(Span, { className: "bg-primary text-white text-xs px-2 py-0.5 rounded-full", children: ["+", moreCount] }))] }) }))] }) }));
70
70
  }
@@ -4,6 +4,7 @@ import { useState, useEffect, useRef } from "react";
4
4
  import Link from "next/link";
5
5
  import { SectionCarousel } from "./SectionCarousel";
6
6
  import { useFeaturedProducts } from "../hooks/useFeaturedProducts";
7
+ import { useMediaQuery } from "../../../react";
7
8
  import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
8
9
  import { ROUTES } from "../../../next";
9
10
  import { Section, Heading, Text } from "../../../ui";
@@ -11,7 +12,11 @@ import { THEME_CONSTANTS } from "../../../tokens";
11
12
  // --- Multi-row grid + pagination -----------------------------------------------
12
13
  function ProductGrid({ items, rows, maxItems, pagination, viewMoreHref, viewMoreLabel, title, description, }) {
13
14
  const { themed } = THEME_CONSTANTS;
14
- const pageSize = Math.min(rows * 5, maxItems);
15
+ // On mobile (<sm, 2-col grid) a second row doubles the section height to ~2 screens.
16
+ // Collapse to 1 row automatically so the section fits within one viewport.
17
+ const isMobile = useMediaQuery("(max-width: 639px)");
18
+ const effectiveRows = isMobile ? 1 : rows;
19
+ const pageSize = Math.min(effectiveRows * 5, maxItems);
15
20
  const [offset, setOffset] = useState(0);
16
21
  const timerRef = useRef(null);
17
22
  const totalItems = Math.min(items.length, maxItems);
@@ -43,11 +43,27 @@ function getHoverClass(effect) {
43
43
  return "";
44
44
  }
45
45
  function getSlideHeightClass(height) {
46
- if (height === "viewport")
47
- return "min-h-screen";
48
- if (height === "tall")
49
- return "min-h-[80vh]";
50
- return "min-h-[60vh] md:min-h-[clamp(420px,72vh,680px)]";
46
+ // BN-1 (fixed bottom nav) = h-16 = 4rem, shown only below lg — must be subtracted on mobile.
47
+ // --header-height is the measured sticky header height set by AppLayoutShell.
48
+ if (height === "viewport") {
49
+ return [
50
+ // mobile: full dvh minus header minus BN-1
51
+ "min-h-[calc(100dvh-var(--header-height,3.5rem)-4rem)]",
52
+ // desktop: full dvh minus header only (no BN-1)
53
+ "lg:min-h-[calc(100dvh-var(--header-height,3.5rem))]",
54
+ ].join(" ");
55
+ }
56
+ if (height === "tall") {
57
+ return [
58
+ "min-h-[calc(72dvh-var(--header-height,3.5rem)-4rem)]",
59
+ "lg:min-h-[clamp(480px,76dvh,840px)]",
60
+ ].join(" ");
61
+ }
62
+ // medium (default) — comfortable hero without forcing full-screen
63
+ return [
64
+ "min-h-[calc(52dvh-var(--header-height,3.5rem)-4rem)]",
65
+ "lg:min-h-[clamp(420px,65dvh,680px)]",
66
+ ].join(" ");
51
67
  }
52
68
  /** Renders the slide background — image, video, solid colour, or gradient. */
53
69
  function SlideBackground({ bg, legacy, mobileLegacy, isMobile, priority, }) {
@@ -24,7 +24,9 @@ export function SectionCarousel({ title, description, headingVariant = "editoria
24
24
  return (_jsxs(Section, { className: [
25
25
  "relative overflow-hidden w-full",
26
26
  hasBg ? "" : themed.bgSecondary,
27
- "p-8",
27
+ // Vertical padding: 5rem on desktop, 3rem on mobile to leave room for adjacent sections.
28
+ // Horizontal: 1rem mobile → 2rem desktop; handled via inner max-w container.
29
+ "py-10 px-4 md:py-12 md:px-8",
28
30
  className,
29
31
  ]
30
32
  .filter(Boolean)
@@ -15,7 +15,7 @@ function CategoryChip({ category }) {
15
15
  const coverImage = category.display?.coverImage;
16
16
  const initial = category.name[0]?.toUpperCase() ?? "?";
17
17
  const productCount = category.metrics?.productCount ?? 0;
18
- 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 && isImageUrl(coverImage) ? (_jsx("div", { className: "aspect-video w-full overflow-hidden bg-zinc-100 dark:bg-slate-800", children: _jsx(Image, { src: coverImage, alt: category.name, width: 320, height: 180, 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", 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", children: iconSrc && isImageUrl(iconSrc) ? (_jsx(Image, { src: iconSrc, alt: "", width: 24, height: 24, className: "h-6 w-6 rounded object-cover", "aria-hidden": true })) : iconSrc ? (_jsx("span", { "aria-hidden": "true", className: "text-lg leading-none", children: iconSrc })) : (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" })] })] }));
18
+ return (_jsxs(Link, { href: ROUTES.PUBLIC.CATEGORY_DETAIL(category.slug), className: "group flex w-full min-h-[180px] sm: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 && isImageUrl(coverImage) ? (_jsx("div", { className: "aspect-video w-full overflow-hidden bg-zinc-100 dark:bg-slate-800", children: _jsx(Image, { src: coverImage, alt: category.name, width: 320, height: 180, 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", 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", children: iconSrc && isImageUrl(iconSrc) ? (_jsx(Image, { src: iconSrc, alt: "", width: 24, height: 24, className: "h-6 w-6 rounded object-cover", "aria-hidden": true })) : iconSrc ? (_jsx("span", { "aria-hidden": "true", className: "text-lg leading-none", children: iconSrc })) : (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" })] })] }));
19
19
  }
20
20
  const CTA_CLASSES = {
21
21
  filled: "rounded-lg bg-[var(--appkit-color-primary)] px-5 py-2 text-sm font-semibold text-white hover:opacity-90",
@@ -56,7 +56,7 @@ export function ShopByCategorySection({ title = "Shop by Category", subtitle, li
56
56
  const categories = filtered.length > 0 ? filtered : allCategories;
57
57
  if (!isLoading && allCategories.length === 0)
58
58
  return null;
59
- return (_jsx(Section, { className: `py-12 px-4 ${themed.bgSecondary} ${className}`, children: _jsxs("div", { className: "mx-auto max-w-7xl", children: [_jsxs("div", { className: "mb-6 text-center", 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 }))] }), hasFilters && !isLoading && (_jsxs("div", { className: "mb-4 flex flex-wrap gap-2", children: [_jsx(FilterChip, { label: "All", active: activeFilter === "all", onClick: () => setActiveFilter("all") }), rootIds.slice(0, 5).map((id) => {
59
+ return (_jsx(Section, { className: `py-10 px-4 md:py-12 ${themed.bgSecondary} ${className}`, children: _jsxs("div", { className: "mx-auto max-w-7xl", children: [_jsxs("div", { className: "mb-6 text-center", 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 }))] }), hasFilters && !isLoading && (_jsxs("div", { className: "mb-4 flex flex-wrap gap-2", children: [_jsx(FilterChip, { label: "All", active: activeFilter === "all", onClick: () => setActiveFilter("all") }), rootIds.slice(0, 5).map((id) => {
60
60
  const cat = allCategories.find((c) => c.id === id);
61
61
  if (!cat)
62
62
  return null;
@@ -8,6 +8,7 @@ export interface ReviewRatingAggregate {
8
8
  }
9
9
  declare class ReviewRepository extends BaseRepository<ReviewDocument> {
10
10
  constructor();
11
+ private decryptSieveResult;
11
12
  private encryptReviewData;
12
13
  protected mapDoc<D = ReviewDocument>(snap: import("../../../providers/db-firebase").DocumentSnapshot): D;
13
14
  create(input: ReviewCreateInput): Promise<ReviewDocument>;
@@ -13,6 +13,14 @@ class ReviewRepository extends BaseRepository {
13
13
  constructor() {
14
14
  super(REVIEW_COLLECTION);
15
15
  }
16
+ decryptSieveResult(result) {
17
+ return {
18
+ ...result,
19
+ items: result.items.map((item) => decryptPiiFields(item, [
20
+ ...REVIEW_PII_FIELDS,
21
+ ])),
22
+ };
23
+ }
16
24
  encryptReviewData(data) {
17
25
  let encrypted = encryptPiiFields(data, [...REVIEW_PII_FIELDS]);
18
26
  encrypted = addPiiIndices(data, REVIEW_PII_INDEX_MAP);
@@ -181,25 +189,28 @@ class ReviewRepository extends BaseRepository {
181
189
  }
182
190
  async listForProduct(productId, model) {
183
191
  const baseQuery = this.getCollection().where(REVIEW_FIELDS.PRODUCT_ID, "==", productId);
184
- return this.sieveQuery(model, ReviewRepository.SIEVE_FIELDS, {
192
+ const result = await this.sieveQuery(model, ReviewRepository.SIEVE_FIELDS, {
185
193
  baseQuery,
186
194
  defaultPageSize: 10,
187
195
  maxPageSize: 50,
188
196
  });
197
+ return this.decryptSieveResult(result);
189
198
  }
190
199
  async listForStore(storeId, model) {
191
200
  const baseQuery = this.getCollection().where(REVIEW_FIELDS.STORE_ID, "==", storeId);
192
- return this.sieveQuery(model, ReviewRepository.SIEVE_FIELDS, {
201
+ const result = await this.sieveQuery(model, ReviewRepository.SIEVE_FIELDS, {
193
202
  baseQuery,
194
203
  defaultPageSize: 10,
195
204
  maxPageSize: 100,
196
205
  });
206
+ return this.decryptSieveResult(result);
197
207
  }
198
208
  async listAll(model) {
199
- return this.sieveQuery(model, ReviewRepository.SIEVE_FIELDS, {
209
+ const result = await this.sieveQuery(model, ReviewRepository.SIEVE_FIELDS, {
200
210
  defaultPageSize: 50,
201
211
  maxPageSize: 200,
202
212
  });
213
+ return this.decryptSieveResult(result);
203
214
  }
204
215
  }
205
216
  ReviewRepository.SIEVE_FIELDS = {
@@ -56,7 +56,7 @@ export function StoreSidebar({ items, groups, activeHref, storeName, storeLogoUR
56
56
  height: "calc(100vh - var(--header-height, 3.5rem))",
57
57
  width: "18rem",
58
58
  transform: desktopOpen ? "translateX(0)" : "translateX(calc(-100% + 1.25rem))",
59
- }, children: [_jsxs("div", { className: "flex-1 bg-white dark:bg-slate-950 border-r border-zinc-200 dark:border-slate-700 flex flex-col overflow-hidden shadow-xl", children: [_jsx("div", { className: "px-4 py-3.5 border-b border-zinc-100 dark:border-slate-800 shrink-0", children: _jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [storeLogoURL ? (_jsx(Div, { role: "img", "aria-label": storeName, className: CLS_STORE_AVATAR, style: { backgroundImage: `url(${storeLogoURL})` } })) : (_jsx(Div, { className: CLS_STORE_FALLBACK, children: storeName?.[0]?.toUpperCase() })), _jsx(Text, { className: CLS_STORE_NAME, children: storeName || panelTitle })] }) }), _jsx("div", { className: "flex-1 overflow-y-auto", children: navContent })] }), _jsx("button", { type: "button", onClick: handleToggle, "aria-label": desktopOpen ? "Collapse sidebar" : "Expand sidebar", className: "w-5 shrink-0 bg-primary dark:bg-secondary hover:bg-primary-600 dark:hover:bg-secondary-600 flex items-center justify-center transition-colors rounded-r-md shadow-md", children: _jsx("svg", { className: "w-3 h-3 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: desktopOpen ? (_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M11 19l-7-7 7-7m8 14l-7-7 7-7" })) : (_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M13 5l7 7-7 7M5 5l7 7-7 7" })) }) })] }), _jsx("div", { className: "md:hidden", children: _jsx(BottomSheet, { open: mobileOpen, onClose: close, title: panelTitle, children: groups ? (_jsx(GroupsContent, { groups: groups, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close })) : (_jsx(FlatContent, { items: items, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close })) }) })] }));
59
+ }, children: [_jsxs("div", { className: "flex-1 bg-white dark:bg-slate-950 border-r border-zinc-200 dark:border-slate-700 flex flex-col overflow-hidden shadow-xl", children: [_jsx("div", { className: "px-4 py-3.5 border-b border-zinc-100 dark:border-slate-800 shrink-0", children: _jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [storeLogoURL ? (_jsx(Div, { role: "img", "aria-label": storeName, className: CLS_STORE_AVATAR, style: { backgroundImage: `url(${storeLogoURL})` } })) : (_jsx(Div, { className: CLS_STORE_FALLBACK, children: storeName?.[0]?.toUpperCase() })), _jsx(Text, { className: CLS_STORE_NAME, children: storeName || panelTitle })] }) }), _jsx("div", { className: "flex-1 overflow-y-auto", children: navContent })] }), _jsx("button", { type: "button", onClick: handleToggle, "aria-label": desktopOpen ? "Collapse sidebar" : "Expand sidebar", className: "w-5 shrink-0 flex items-center justify-center transition-opacity rounded-r-md shadow-md hover:opacity-85", style: { background: "linear-gradient(to bottom, var(--appkit-color-cobalt-700), var(--appkit-color-cobalt))" }, children: _jsx("svg", { className: "w-3 h-3 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: desktopOpen ? (_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M11 19l-7-7 7-7m8 14l-7-7 7-7" })) : (_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M13 5l7 7-7 7M5 5l7 7-7 7" })) }) })] }), _jsx("div", { className: "md:hidden", children: _jsx(BottomSheet, { open: mobileOpen, onClose: close, title: panelTitle, children: groups ? (_jsx(GroupsContent, { groups: groups, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close })) : (_jsx(FlatContent, { items: items, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close })) }) })] }));
60
60
  }
61
61
  return (_jsxs(_Fragment, { children: [mounted && mobileOpen &&
62
62
  createPortal(_jsx(DrawerPanel, { title: panelTitle, onClose: close, children: navContent }), document.body), _jsx("div", { className: "md:hidden", children: _jsx(BottomSheet, { open: mobileOpen, onClose: close, title: panelTitle, children: groups ? (_jsx(GroupsContent, { groups: groups, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close })) : (_jsx(FlatContent, { items: items, activeHref: activeHref, storeName: storeName, storeLogoURL: storeLogoURL, onItemClick: close })) }) })] }));
@@ -10,5 +10,5 @@ export function StoreAboutView({ store, labels = {}, renderSocialLinks, renderSt
10
10
  month: "long",
11
11
  })
12
12
  : null;
13
- return (_jsxs(Div, { className: `space-y-6 py-6 ${className}`, children: [store.bio && (_jsxs(Div, { children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold mb-2", children: labels.aboutTitle ?? "About" }), _jsx(RichText, { html: normalizeRichTextHtml(store.bio), className: "text-neutral-600" })] })), renderStats?.(store), _jsxs(Div, { className: "space-y-3 text-sm", children: [store.location && (_jsxs(Div, { className: "flex gap-2", children: [_jsx(Text, { className: CLS_LABEL, children: labels.locationLabel ?? "Location" }), _jsx(Text, { className: "text-neutral-600", children: store.location })] })), joinDate && (_jsxs(Div, { className: "flex gap-2", children: [_jsx(Text, { className: CLS_LABEL, children: labels.memberSinceLabel ?? "Member since" }), _jsx(Text, { className: "text-neutral-600", children: joinDate })] })), store.website && (_jsxs(Div, { className: "flex gap-2", children: [_jsx(Text, { className: CLS_LABEL, children: labels.websiteLabel ?? "Website" }), _jsx("a", { href: store.website, target: "_blank", rel: "noopener noreferrer", className: "text-primary hover:underline break-all", children: store.website })] }))] }), store.socialLinks && renderSocialLinks?.(store.socialLinks), store.returnPolicy && (_jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "text-base font-semibold mb-2", children: labels.returnPolicyLabel ?? "Return Policy" }), _jsx(RichText, { html: normalizeRichTextHtml(store.returnPolicy), className: "text-neutral-600" })] })), store.shippingPolicy && (_jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "text-base font-semibold mb-2", children: labels.shippingPolicyLabel ?? "Shipping Policy" }), _jsx(RichText, { html: normalizeRichTextHtml(store.shippingPolicy), className: "text-neutral-600" })] })), store.isVacationMode && (_jsxs(Div, { className: "bg-yellow-50 border border-yellow-200 rounded-lg p-4", children: [_jsx(Text, { className: "text-yellow-800 font-medium", children: labels.vacationModeLabel ?? "This store is on vacation mode" }), store.vacationMessage && (_jsx(Text, { className: "text-yellow-700 mt-1", children: store.vacationMessage }))] }))] }));
13
+ return (_jsxs(Div, { className: `space-y-6 py-6 ${className}`, children: [store.bio && (_jsxs(Div, { children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold mb-2", children: labels.aboutTitle ?? "About" }), _jsx(RichText, { html: normalizeRichTextHtml(store.bio) })] })), renderStats?.(store), _jsxs(Div, { className: "space-y-3 text-sm", children: [store.location && (_jsxs(Div, { className: "flex gap-2", children: [_jsx(Text, { className: CLS_LABEL, children: labels.locationLabel ?? "Location" }), _jsx(Text, { className: "", children: store.location })] })), joinDate && (_jsxs(Div, { className: "flex gap-2", children: [_jsx(Text, { className: CLS_LABEL, children: labels.memberSinceLabel ?? "Member since" }), _jsx(Text, { className: "", children: joinDate })] })), store.website && (_jsxs(Div, { className: "flex gap-2", children: [_jsx(Text, { className: CLS_LABEL, children: labels.websiteLabel ?? "Website" }), _jsx("a", { href: store.website, target: "_blank", rel: "noopener noreferrer", className: "text-primary hover:underline break-all", children: store.website })] }))] }), store.socialLinks && renderSocialLinks?.(store.socialLinks), store.returnPolicy && (_jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "text-base font-semibold mb-2", children: labels.returnPolicyLabel ?? "Return Policy" }), _jsx(RichText, { html: normalizeRichTextHtml(store.returnPolicy) })] })), store.shippingPolicy && (_jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "text-base font-semibold mb-2", children: labels.shippingPolicyLabel ?? "Shipping Policy" }), _jsx(RichText, { html: normalizeRichTextHtml(store.shippingPolicy) })] })), store.isVacationMode && (_jsxs(Div, { className: "bg-yellow-50 border border-yellow-200 rounded-lg p-4", children: [_jsx(Text, { className: "text-yellow-800 font-medium", children: labels.vacationModeLabel ?? "This store is on vacation mode" }), store.vacationMessage && (_jsx(Text, { className: "text-yellow-700 mt-1", children: store.vacationMessage }))] }))] }));
14
14
  }
@@ -3,7 +3,7 @@ import { Button, Div, Heading, RichText, Row, Section, Span, Text, } from "../..
3
3
  import { normalizeRichTextHtml } from "../../../utils/string.formatter";
4
4
  import { ShareButton } from "../../products/components/ShareButton";
5
5
  export function StoreHeader({ store, labels = {}, onFollow, className = "", }) {
6
- return (_jsxs(Section, { className: `bg-white dark:bg-slate-900 border-b border-gray-200 dark:border-slate-700 ${className}`, children: [store.storeBannerURL && (_jsx(Div, { className: "h-40 md:h-56 overflow-hidden bg-gray-100 dark:bg-slate-800", children: _jsx(Div, { role: "img", "aria-label": `${store.storeName} banner`, className: "h-full w-full bg-center bg-cover", style: { backgroundImage: `url(${store.storeBannerURL})` } }) })), _jsxs(Div, { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4", children: [_jsxs(Div, { className: "flex items-end gap-4", children: [store.storeLogoURL ? (_jsx(Div, { role: "img", "aria-label": store.storeName, className: "-mt-8 h-16 w-16 rounded-xl border-2 border-white dark:border-slate-800 bg-center bg-cover shadow-sm", style: { backgroundImage: `url(${store.storeLogoURL})` } })) : (_jsx(Div, { className: "-mt-8 h-16 w-16 rounded-xl border-2 border-white dark:border-slate-800 bg-orange-100 dark:bg-orange-900/30 flex items-center justify-center text-orange-600 dark:text-orange-400 font-bold text-2xl shadow-sm", children: store.storeName[0]?.toUpperCase() })), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Div, { className: "flex flex-wrap items-center gap-2 mb-0.5", children: [_jsx(Heading, { level: 1, className: "text-xl font-bold text-gray-900 dark:text-zinc-100", children: store.storeName }), store.averageRating != null && store.averageRating > 0 && (_jsxs(Span, { className: "inline-flex items-center gap-1 text-sm font-medium text-amber-500", children: ["\u2605 ", store.averageRating.toFixed(1)] }))] }), _jsxs(Row, { className: "gap-3 text-xs text-gray-500 dark:text-zinc-400 mb-0.5", children: [store.category && _jsx(Span, { className: "capitalize", children: store.category }), store.totalProducts != null && store.totalProducts > 0 && (_jsxs(Span, { children: [store.totalProducts, " ", labels.products ?? "products"] })), store.totalReviews != null && store.totalReviews > 0 && (_jsxs(Span, { children: [store.totalReviews, " ", labels.reviews ?? "reviews"] })), store.itemsSold != null && store.itemsSold > 0 && (_jsxs(Span, { children: [store.itemsSold, " ", labels.sold ?? "sold"] }))] }), store.storeDescription && (_jsx(RichText, { html: normalizeRichTextHtml(store.storeDescription), copyableCode: true, className: "text-sm text-gray-500 dark:text-zinc-400 mt-0.5" }))] }), _jsxs(Row, { gap: "sm", align: "center", className: "shrink-0", children: [_jsx(ShareButton, { title: store.storeName }), onFollow && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onFollow(store.storeSlug), className: "rounded-lg border border-orange-500 px-4 py-2 text-sm font-medium text-orange-500 hover:bg-orange-50 transition-colors", children: labels.follow ?? "Follow" }))] })] }), store.isVacationMode && (_jsx(Text, { className: "mt-3 rounded-lg bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 px-3 py-2 text-sm text-yellow-700 dark:text-yellow-300", children: store.vacationMessage ??
6
+ return (_jsxs(Section, { className: `bg-white dark:bg-slate-900 border-b border-gray-200 dark:border-slate-700 ${className}`, children: [store.storeBannerURL && (_jsx(Div, { className: "h-40 md:h-56 overflow-hidden bg-gray-100 dark:bg-slate-800", children: _jsx(Div, { role: "img", "aria-label": `${store.storeName} banner`, className: "h-full w-full bg-center bg-cover", style: { backgroundImage: `url(${store.storeBannerURL})` } }) })), _jsxs(Div, { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4", children: [_jsxs(Div, { className: "flex items-end gap-4", children: [store.storeLogoURL ? (_jsx(Div, { role: "img", "aria-label": store.storeName, className: "-mt-8 h-16 w-16 rounded-xl border-2 border-white dark:border-slate-800 bg-center bg-cover shadow-sm", style: { backgroundImage: `url(${store.storeLogoURL})` } })) : (_jsx(Div, { className: "-mt-8 h-16 w-16 rounded-xl border-2 border-white dark:border-slate-800 bg-orange-100 dark:bg-orange-900/30 flex items-center justify-center text-orange-600 dark:text-orange-400 font-bold text-2xl shadow-sm", children: store.storeName[0]?.toUpperCase() })), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Div, { className: "flex flex-wrap items-center gap-2 mb-0.5", children: [_jsx(Heading, { level: 1, className: "text-xl font-bold text-gray-900 dark:text-zinc-100", children: store.storeName }), store.averageRating != null && store.averageRating > 0 && (_jsxs(Span, { className: "inline-flex items-center gap-1 text-sm font-medium text-amber-500", children: ["\u2605 ", store.averageRating.toFixed(1)] }))] }), _jsxs(Row, { className: "gap-3 text-xs text-gray-500 dark:text-zinc-400 mb-0.5", children: [store.category && _jsx(Span, { className: "capitalize", children: store.category }), store.totalProducts != null && store.totalProducts > 0 && (_jsxs(Span, { children: [store.totalProducts, " ", labels.products ?? "products"] })), store.totalReviews != null && store.totalReviews > 0 && (_jsxs(Span, { children: [store.totalReviews, " ", labels.reviews ?? "reviews"] })), store.itemsSold != null && store.itemsSold > 0 && (_jsxs(Span, { children: [store.itemsSold, " ", labels.sold ?? "sold"] }))] }), store.storeDescription && (_jsx(RichText, { html: normalizeRichTextHtml(store.storeDescription), copyableCode: true, className: "mt-0.5" }))] }), _jsxs(Row, { gap: "sm", align: "center", className: "shrink-0", children: [_jsx(ShareButton, { title: store.storeName }), onFollow && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onFollow(store.storeSlug), className: "rounded-lg border border-orange-500 px-4 py-2 text-sm font-medium text-orange-500 hover:bg-orange-50 transition-colors", children: labels.follow ?? "Follow" }))] })] }), store.isVacationMode && (_jsx(Text, { className: "mt-3 rounded-lg bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 px-3 py-2 text-sm text-yellow-700 dark:text-yellow-300", children: store.vacationMessage ??
7
7
  labels.vacationMode ??
8
8
  "Store is on vacation mode" }))] })] }));
9
9
  }