@mohasinac/appkit 2.8.4 → 2.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/SidebarCollapseToggle.js +2 -0
- package/dist/_internal/shared/listing-types/action-tracker.js +0 -2
- package/dist/core/unit-of-work.js +0 -1
- package/dist/features/account/components/NotificationBell.js +1 -0
- package/dist/features/account/components/UserOffersPanel.js +1 -0
- package/dist/features/account/components/UserSidebar.js +3 -1
- package/dist/features/admin/components/AdminCarouselEditorView.js +3 -1
- package/dist/features/admin/components/AdminSidebar.js +3 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +3 -1
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +0 -3
- package/dist/features/admin/components/DashboardStats.js +5 -1
- package/dist/features/admin/components/analytics/AdminAnalyticsCharts.js +3 -1
- package/dist/features/admin/components/analytics/AdminStatCard.js +5 -1
- package/dist/features/admin/hooks/useChat.js +1 -0
- package/dist/features/auctions/components/AuctionBidsTable.js +5 -1
- package/dist/features/auth/hooks/useAuth.js +1 -0
- package/dist/features/blog/components/BlogFeaturedCard.js +3 -1
- package/dist/features/blog/components/BlogListView.js +5 -1
- package/dist/features/blog/components/BlogPostView.js +3 -1
- package/dist/features/cart/components/CartDrawer.js +3 -1
- package/dist/features/categories/components/BrandDetailPageView.js +5 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +3 -1
- package/dist/features/categories/components/CategoryGrid.js +5 -1
- package/dist/features/categories/components/CategoryTableColumns.js +6 -2
- package/dist/features/categories/components/CategoryTree.js +3 -1
- package/dist/features/categories/components/ConcernCard.js +3 -1
- package/dist/features/collections/components/CollectionCard.js +3 -1
- package/dist/features/events/components/EventCard.js +3 -1
- package/dist/features/forms/Slider.js +3 -1
- package/dist/features/history/hooks/useHistory.js +1 -0
- package/dist/features/homepage/components/AdSlot.js +6 -2
- package/dist/features/homepage/components/AdvertisementBanner.js +3 -1
- package/dist/features/homepage/components/BeforeAfterCard.js +5 -1
- package/dist/features/homepage/components/CustomCardsSection.js +5 -1
- package/dist/features/homepage/components/HowItWorksSection.js +3 -1
- package/dist/features/homepage/components/NewsletterBanner.js +6 -2
- package/dist/features/homepage/components/SecurityHighlightsSection.js +3 -1
- package/dist/features/homepage/components/StatsCounterSection.js +3 -1
- package/dist/features/homepage/components/TrustFeaturesSection.js +3 -1
- package/dist/features/layout/AppLayoutShell.js +3 -1
- package/dist/features/layout/BottomActionsContext.js +1 -3
- package/dist/features/layout/BottomNavbar.js +1 -1
- package/dist/features/media/MediaLightbox.js +9 -3
- package/dist/features/media/modals/ImageEditor.js +6 -2
- package/dist/features/media/upload/MediaUploadField.js +1 -1
- package/dist/features/messages/hooks/useConversation.js +2 -0
- package/dist/features/messages/hooks/useConversations.js +1 -0
- package/dist/features/orders/components/OrdersList.js +3 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
- package/dist/features/products/components/CompareOverlay.js +5 -1
- package/dist/features/products/components/FeatureBadge.js +3 -1
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +1 -1
- package/dist/features/products/components/ProductGrid.js +3 -1
- package/dist/features/reviews/components/ReviewDetailShell.js +8 -2
- package/dist/features/reviews/components/ReviewModal.js +5 -1
- package/dist/features/reviews/components/ReviewSummary.js +3 -1
- package/dist/features/reviews/components/ReviewsList.js +5 -1
- package/dist/features/search/components/Search.js +3 -1
- package/dist/features/seller/components/PhysicalLocationModal.js +1 -1
- package/dist/features/seller/components/SellerAddressesView.js +2 -1
- package/dist/features/seller/components/SellerBidsView.js +3 -1
- package/dist/features/seller/components/SellerCouponEditorView.js +3 -1
- package/dist/features/seller/components/SellerOffersPanel.js +1 -0
- package/dist/features/seller/components/SellerPayoutMethodsView.js +1 -0
- package/dist/features/seller/components/SellerProductsView.js +5 -1
- package/dist/features/seller/components/SellerReviewsView.js +1 -0
- package/dist/features/seller/components/SellerShippingConfigsView.js +1 -0
- package/dist/features/seller/components/SellerSidebar.js +12 -4
- package/dist/features/seller/components/SellerStoreCategoriesView.js +1 -0
- package/dist/features/seller/components/SellerTemplatesView.js +1 -0
- package/dist/features/shell/FormShell.js +10 -2
- package/dist/features/shell/QuickFormDrawer.js +5 -2
- package/dist/features/stores/components/StoreHeader.js +5 -1
- package/dist/features/wishlist/components/WishlistPage.js +3 -1
- package/dist/providers/db-firebase/admin.js +0 -1
- package/dist/ui/DataTable.js +6 -2
- package/dist/ui/components/AvatarDisplay.js +3 -1
- package/dist/ui/components/BackgroundRenderer.js +4 -2
- package/dist/ui/components/Card.js +6 -2
- package/dist/ui/components/ImageGallery.js +3 -1
- package/dist/ui/components/ImageLightbox.js +3 -1
- package/dist/ui/components/PasswordStrengthIndicator.js +3 -1
- package/dist/ui/components/Progress.js +6 -2
- package/dist/ui/components/RowActionMenu.js +3 -1
- package/dist/ui/components/SiteLogo.js +7 -1
- package/dist/ui/components/Skeleton.js +3 -1
- package/dist/ui/components/Slider.js +3 -1
- package/dist/ui/components/TabStrip.js +3 -1
- package/package.json +1 -1
|
@@ -19,7 +19,9 @@ function CategoryTreeNode({ item, children = [], activeId, onSelect, depth = 0,
|
|
|
19
19
|
setExpanded((p) => !p);
|
|
20
20
|
onSelect?.(item);
|
|
21
21
|
}, className: `flex cursor-pointer items-center gap-1 rounded-md px-2 py-1.5 text-sm transition select-none
|
|
22
|
-
${isActive ? "bg-primary-50 font-semibold text-primary-700 dark:bg-primary-900/30 dark:text-primary-400" : "text-neutral-700 hover:bg-neutral-50 dark:text-zinc-300 dark:hover:bg-zinc-800"}`,
|
|
22
|
+
${isActive ? "bg-primary-50 font-semibold text-primary-700 dark:bg-primary-900/30 dark:text-primary-400" : "text-neutral-700 hover:bg-neutral-50 dark:text-zinc-300 dark:hover:bg-zinc-800"}`,
|
|
23
|
+
// audit-inline-style-ok: computed pixel offset
|
|
24
|
+
style: { paddingLeft: `${8 + indent}px` }, children: [hasChildren && (_jsx(Span, { size: "xs", "aria-hidden": "true", className: `text-zinc-400 dark:text-zinc-400 transition-transform duration-150 ${expanded ? "rotate-90" : ""}`, children: "\u25B6" })), !hasChildren && _jsx(Span, { className: "w-3" }), item.display?.icon && (_jsx(Span, { size: "base", "aria-hidden": "true", className: "flex-shrink-0", children: item.display.icon })), _jsx(Text, { className: "flex-1 truncate", children: item.name }), item.metrics && (_jsx(Span, { size: "xs", className: "ml-auto flex-shrink-0 text-zinc-400 dark:text-zinc-400 tabular-nums", children: item.metrics.productCount }))] }), hasChildren && expanded && (_jsx(Div, { role: "group", children: children.map((child) => (_jsx(CategoryTreeNode, { item: child, activeId: activeId, onSelect: onSelect, depth: depth + 1 }, child.id))) }))] }));
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* CategoryTree — hierarchical expandable navigation for categories.
|
|
@@ -2,5 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Button, Div, Heading, Text } from "../../../ui";
|
|
3
3
|
import { THEME_CONSTANTS } from "../../../tokens";
|
|
4
4
|
export function ConcernCard({ concern, onClick, className = "", }) {
|
|
5
|
-
return (_jsxs(Button, { type: "button", variant: "outline", onClick: () => onClick?.(concern), className: `group flex flex-col items-center gap-3 rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 p-4 text-center shadow-sm transition hover:border-primary dark:hover:border-primary-400 hover:shadow-md ${className}`, children: [concern.display?.coverImage && (_jsx(Div, { role: "img", "aria-label": concern.name, className: "h-16 w-16 rounded-full bg-center bg-cover",
|
|
5
|
+
return (_jsxs(Button, { type: "button", variant: "outline", onClick: () => onClick?.(concern), className: `group flex flex-col items-center gap-3 rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 p-4 text-center shadow-sm transition hover:border-primary dark:hover:border-primary-400 hover:shadow-md ${className}`, children: [concern.display?.coverImage && (_jsx(Div, { role: "img", "aria-label": concern.name, className: "h-16 w-16 rounded-full bg-center bg-cover",
|
|
6
|
+
// audit-inline-style-ok: dynamic image URL
|
|
7
|
+
style: { backgroundImage: `url(${concern.display.coverImage})` } })), _jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "text-sm font-semibold text-neutral-900 dark:text-zinc-100 group-hover:text-primary dark:group-hover:text-primary-400", children: concern.name }), concern.description && (_jsx(Text, { className: `mt-1 text-xs ${THEME_CONSTANTS.utilities.textClamp2} text-neutral-500 dark:text-zinc-400`, children: concern.description }))] })] }));
|
|
6
8
|
}
|
|
@@ -3,7 +3,9 @@ import { Div, Heading, Text, TextLink } from "../../../ui";
|
|
|
3
3
|
import { LAYOUT } from "../../../tokens";
|
|
4
4
|
const CLS_PLACEHOLDER = "w-full bg-gradient-to-br from-indigo-100 to-purple-100 dark:from-indigo-900/30 dark:to-purple-900/30";
|
|
5
5
|
export function CollectionCard({ collection, href }) {
|
|
6
|
-
return (_jsxs(TextLink, { href: href, className: "group relative block overflow-hidden rounded-xl bg-gray-100 dark:bg-slate-800 transition-shadow hover:shadow-lg", children: [collection.image ? (_jsx(Div, { role: "img", "aria-label": collection.title, className: `${LAYOUT.cardHeight.md} w-full bg-cover bg-center transition-transform duration-300 group-hover:scale-105`,
|
|
6
|
+
return (_jsxs(TextLink, { href: href, className: "group relative block overflow-hidden rounded-xl bg-gray-100 dark:bg-slate-800 transition-shadow hover:shadow-lg", children: [collection.image ? (_jsx(Div, { role: "img", "aria-label": collection.title, className: `${LAYOUT.cardHeight.md} w-full bg-cover bg-center transition-transform duration-300 group-hover:scale-105`,
|
|
7
|
+
// audit-inline-style-ok: dynamic image URL
|
|
8
|
+
style: { backgroundImage: `url(${collection.image})` } })) : (_jsx(Div, { className: `${LAYOUT.cardHeight.md} ${CLS_PLACEHOLDER}` })), _jsxs(Div, { surface: "default", padding: "sm", children: [_jsx(Heading, { level: 3, className: "font-semibold text-gray-900 dark:text-zinc-100", children: collection.title }), collection.subtitle && (_jsx(Text, { className: "mt-1 text-sm text-gray-500 dark:text-zinc-400", children: collection.subtitle })), collection.productCount !== undefined && (_jsxs(Text, { className: "mt-2 text-xs text-gray-400 dark:text-zinc-500", children: [collection.productCount, " ", collection.productCount === 1 ? "item" : "items"] }))] })] }));
|
|
7
9
|
}
|
|
8
10
|
export function CollectionGrid({ collections, getHref }) {
|
|
9
11
|
return (_jsx(Div, { className: "grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3", children: collections.map((c) => (_jsx(CollectionCard, { collection: c, href: getHref(c.slug) }, c.slug))) }));
|
|
@@ -31,6 +31,8 @@ export function EventCard({ event, labels = {}, onParticipate, className = "", s
|
|
|
31
31
|
const msLeft = endsAt.getTime() - now.getTime();
|
|
32
32
|
const daysLeft = Math.max(0, Math.ceil(msLeft / (1000 * 60 * 60 * 24)));
|
|
33
33
|
const detailHref = String(ROUTES.PUBLIC.EVENT_DETAIL(event.slug ?? event.id));
|
|
34
|
-
return (_jsxs(Article, { className: `group relative flex h-full ${LAYOUT.cardHeight.event} flex-col overflow-hidden rounded-xl border bg-white shadow-sm transition-shadow hover:shadow-md dark:bg-slate-900 ${isSelected ? "border-primary outline outline-2 outline-primary" : "border-zinc-200 dark:border-slate-700"} ${className}`, onMouseDown: onSelect && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: onSelect && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: onSelect && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: onSelect && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: onSelect && !isSelected ? longPress.onTouchEnd : undefined, children: [onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(event.id, !isSelected); }, label: isSelected ? "Deselect event" : "Select event", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsx(Link, { href: detailHref, className: "block flex-shrink-0", children: event.coverImageUrl ? (_jsx(Div, { className: `aspect-video ${__O.hidden}`, children: _jsx(Div, { role: "img", "aria-label": safeTitle, className: "h-full w-full bg-cover bg-center transition-transform duration-300 group-hover:scale-105",
|
|
34
|
+
return (_jsxs(Article, { className: `group relative flex h-full ${LAYOUT.cardHeight.event} flex-col overflow-hidden rounded-xl border bg-white shadow-sm transition-shadow hover:shadow-md dark:bg-slate-900 ${isSelected ? "border-primary outline outline-2 outline-primary" : "border-zinc-200 dark:border-slate-700"} ${className}`, onMouseDown: onSelect && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: onSelect && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: onSelect && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: onSelect && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: onSelect && !isSelected ? longPress.onTouchEnd : undefined, children: [onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(event.id, !isSelected); }, label: isSelected ? "Deselect event" : "Select event", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsx(Link, { href: detailHref, className: "block flex-shrink-0", children: event.coverImageUrl ? (_jsx(Div, { className: `aspect-video ${__O.hidden}`, children: _jsx(Div, { role: "img", "aria-label": safeTitle, className: "h-full w-full bg-cover bg-center transition-transform duration-300 group-hover:scale-105",
|
|
35
|
+
// audit-inline-style-ok: dynamic image URL
|
|
36
|
+
style: { backgroundImage: `url(${event.coverImageUrl})` } }) })) : (_jsx(Div, { className: "aspect-video bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-slate-800 dark:to-slate-700 flex items-center justify-center", children: _jsx(Span, { className: "text-5xl opacity-40", "aria-hidden": "true", children: TYPE_ICONS[event.type] }) })) }), _jsxs(Div, { className: `flex flex-1 flex-col ${__P.p4}`, children: [_jsxs(Div, { className: "flex items-start justify-between gap-2 mb-2", children: [_jsx(Span, { size: "lg", "aria-hidden": "true", children: TYPE_ICONS[event.type] }), _jsx(EventStatusBadge, { status: event.status })] }), _jsx(Link, { href: detailHref, className: "block", children: _jsx(Heading, { level: 3, className: "font-semibold text-gray-900 dark:text-zinc-100 text-base leading-snug mb-1 group-hover:text-primary transition-colors", children: safeTitle }) }), _jsx(RichText, { html: normalizeRichTextHtml(event.description ?? ""), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: `mb-3 ${THEME_CONSTANTS.utilities.textClamp3} text-sm text-zinc-600 dark:text-zinc-400` }), _jsxs(Div, { className: "mb-3 mt-auto flex items-center justify-between text-xs text-zinc-500 dark:text-zinc-400", children: [event.status === EVENT_FIELDS.STATUS_VALUES.ACTIVE &&
|
|
35
37
|
daysLeft > 0 && _jsxs(Span, { children: ["\u23F1 ", daysLeft, "d remaining"] }), _jsxs(Span, { children: ["\uD83D\uDC65 ", event.stats.totalEntries, " ", labels.entries ?? "entries"] })] }), event.status === EVENT_FIELDS.STATUS_VALUES.ACTIVE && onParticipate ? (_jsx(Button, { type: "button", onClick: () => onParticipate(event), className: "w-full rounded-lg bg-primary py-2 text-sm font-medium text-white transition-colors hover:bg-primary-600", children: labels.participate ?? "Participate" })) : (_jsxs(TextLink, { href: detailHref, className: "inline-flex w-full items-center justify-center gap-1.5 rounded-lg border border-zinc-300 px-3 py-2 text-sm font-medium text-zinc-700 transition-colors hover:bg-zinc-100 dark:border-slate-600 dark:text-zinc-200 dark:hover:bg-slate-800", children: [labels.viewDetails ?? "View details", " \u2192"] }))] })] }));
|
|
36
38
|
}
|
|
@@ -18,5 +18,7 @@ export function Slider({ value: controlledValue, defaultValue = 0, min = 0, max
|
|
|
18
18
|
onChange?.(newVal);
|
|
19
19
|
};
|
|
20
20
|
const displayValue = formatValue ? formatValue(value) : String(value);
|
|
21
|
-
return (_jsxs(Div, { className: cn("w-full space-y-2", className), children: [(label || showValue) && (_jsxs(Row, { justify: "between", children: [label && (_jsx("label", { htmlFor: sliderId, className: LABEL_BASE, children: label })), showValue && (_jsx(Span, { size: "sm", weight: "semibold", className: "tabular-nums text-zinc-900 dark:text-zinc-100", children: displayValue }))] })), _jsxs(Row, { className: "relative h-5", children: [_jsx(Div, { className: "absolute inset-y-0 flex items-center w-full", children: _jsx(Div, { className: "w-full h-1.5 rounded-full bg-zinc-200 dark:bg-slate-700", children: _jsx(Div, { className: "h-full rounded-full bg-primary-500 dark:bg-secondary-500 transition-all",
|
|
21
|
+
return (_jsxs(Div, { className: cn("w-full space-y-2", className), children: [(label || showValue) && (_jsxs(Row, { justify: "between", children: [label && (_jsx("label", { htmlFor: sliderId, className: LABEL_BASE, children: label })), showValue && (_jsx(Span, { size: "sm", weight: "semibold", className: "tabular-nums text-zinc-900 dark:text-zinc-100", children: displayValue }))] })), _jsxs(Row, { className: "relative h-5", children: [_jsx(Div, { className: "absolute inset-y-0 flex items-center w-full", children: _jsx(Div, { className: "w-full h-1.5 rounded-full bg-zinc-200 dark:bg-slate-700", children: _jsx(Div, { className: "h-full rounded-full bg-primary-500 dark:bg-secondary-500 transition-all",
|
|
22
|
+
// audit-inline-style-ok: computed percentage
|
|
23
|
+
style: { width: `${pct}%` } }) }) }), _jsx("input", { type: "range", id: sliderId, min: min, max: max, step: step, value: value, disabled: disabled, onChange: handleChange, className: cn("relative w-full appearance-none bg-transparent cursor-pointer", "focus:outline-none", "[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4", "[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white", "[&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-primary-500 dark:[&::-webkit-slider-thumb]:border-secondary-500", "[&::-webkit-slider-thumb]:shadow-sm", "[&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4", "[&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:bg-white", "[&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-primary-500 dark:[&::-moz-range-thumb]:border-secondary-500", disabled && "opacity-50 cursor-not-allowed") })] }), _jsxs(Row, { justify: "between", className: "text-xs text-zinc-400 dark:text-zinc-400", children: [_jsx(Span, { children: formatValue ? formatValue(min) : min }), _jsx(Span, { children: formatValue ? formatValue(max) : max })] }), error && (_jsx(Text, { className: ERROR_BASE, role: "alert", children: error }))] }));
|
|
22
24
|
}
|
|
@@ -69,6 +69,7 @@ export function useHistory(userId) {
|
|
|
69
69
|
const [items, setItems] = useState([]);
|
|
70
70
|
const trackTimers = useRef(new Map());
|
|
71
71
|
const loadItems = useCallback(async () => {
|
|
72
|
+
// toast-intentionally-silent — silent fallback to empty list on error
|
|
72
73
|
try {
|
|
73
74
|
if (isAuth) {
|
|
74
75
|
setItems(await fetchAuthHistory());
|
|
@@ -20,11 +20,15 @@ function AdsenseAd({ config }) {
|
|
|
20
20
|
// AdSense not loaded yet — no-op
|
|
21
21
|
}
|
|
22
22
|
}, []);
|
|
23
|
-
return (_jsx(Div, { ref: ref, children: _jsx("ins", { className: "adsbygoogle",
|
|
23
|
+
return (_jsx(Div, { ref: ref, children: _jsx("ins", { className: "adsbygoogle",
|
|
24
|
+
// audit-inline-style-ok: dynamic CSS
|
|
25
|
+
style: { display: "block" }, "data-ad-client": config.adsenseClient ?? "", "data-ad-slot": config.adsenseSlot ?? "", "data-ad-format": config.adsenseFormat ?? "auto", "data-full-width-responsive": "true" }) }));
|
|
24
26
|
}
|
|
25
27
|
function ThirdPartyAd({ config }) {
|
|
26
28
|
if (config.thirdPartyUrl) {
|
|
27
|
-
return (_jsx("iframe", { src: config.thirdPartyUrl, width: config.thirdPartyWidth ?? "100%", height: config.thirdPartyHeight ?? "90", scrolling: "no", frameBorder: "0", title: "Advertisement",
|
|
29
|
+
return (_jsx("iframe", { src: config.thirdPartyUrl, width: config.thirdPartyWidth ?? "100%", height: config.thirdPartyHeight ?? "90", scrolling: "no", frameBorder: "0", title: "Advertisement",
|
|
30
|
+
// audit-inline-style-ok: dynamic CSS
|
|
31
|
+
style: { border: 0, overflow: "hidden" }, loading: "lazy" }));
|
|
28
32
|
}
|
|
29
33
|
return null;
|
|
30
34
|
}
|
|
@@ -30,5 +30,7 @@ export function AdvertisementBanner({ title, subtitle, ctaLabel = "Shop now", on
|
|
|
30
30
|
compact
|
|
31
31
|
? "h-[clamp(112px,16vh,160px)]"
|
|
32
32
|
: "min-h-[clamp(240px,34vh,360px)]",
|
|
33
|
-
].join(" "),
|
|
33
|
+
].join(" "),
|
|
34
|
+
// audit-inline-style-ok: optional inline color
|
|
35
|
+
style: backgroundColor ? { backgroundColor } : undefined, children: [!backgroundColor && (_jsx(Div, { className: CLS_GRADIENT_BG, "aria-hidden": true })), _jsxs(Div, { className: `absolute inset-0 ${__O.hidden}`, "aria-hidden": true, children: [_jsx(Div, { className: "absolute -top-16 -left-16 w-64 h-64 rounded-full bg-primary/20 blur-3xl animate-pulse" }), _jsx(Div, { className: "absolute -bottom-16 right-0 w-80 h-80 rounded-full bg-cobalt/20 blur-3xl animate-pulse" })] }), compact ? (_jsxs(Row, { justify: "between", className: "relative z-10 w-full px-6 py-4 gap-4 flex-wrap", children: [_jsxs(Span, { className: "inline-flex items-center gap-2", children: [_jsx(Sparkles, { className: "w-4 h-4 text-white/80" }), _jsx(Span, { size: "sm", weight: "semibold", className: "text-white", children: title })] }), ctaLabel && onCtaClick && (_jsxs(Button, { variant: "secondary", size: "sm", onClick: onCtaClick, className: CLS_CTA_BTN_SM, children: [ctaLabel, _jsx(ArrowRight, { className: "w-3.5 h-3.5" })] }))] })) : (_jsxs(Div, { className: "relative z-10 w-full max-w-4xl mx-auto px-6 py-12 md:py-16 text-center", children: [tagLabel && (_jsxs(Span, { size: "xs", weight: "semibold", className: "inline-flex items-center gap-1.5 bg-white/15 text-white/90 uppercase tracking-widest px-4 py-1.5 rounded-full mb-5 backdrop-blur-sm shadow-sm", children: [_jsx(Sparkles, { className: "w-3.5 h-3.5" }), tagLabel] })), _jsx(Heading, { level: 2, variant: "none", className: "text-3xl md:text-5xl lg:text-6xl font-extrabold text-white mb-4 drop-shadow-lg leading-tight", children: title }), subtitle && (_jsx(Text, { variant: "none", className: "text-white text-base md:text-xl mb-10 max-w-2xl mx-auto", children: subtitle })), ctaLabel && onCtaClick && (_jsxs(Button, { variant: "secondary", size: "lg", onClick: onCtaClick, className: CLS_CTA_BTN_LG, children: [ctaLabel, _jsx(ArrowRight, { className: "w-4 h-4" })] }))] }))] }) }) }));
|
|
34
36
|
}
|
|
@@ -55,5 +55,9 @@ export function BeforeAfterCard({ item, beforeLabel = "Before", afterLabel = "Af
|
|
|
55
55
|
setPosition((p) => Math.max(0, p - 2));
|
|
56
56
|
if (e.key === "ArrowRight")
|
|
57
57
|
setPosition((p) => Math.min(100, p + 2));
|
|
58
|
-
}, children: [_jsx(Image, { src: item.afterImage, alt: afterLabel, fill: true, sizes: "(max-width: 640px) 100vw, 50vw", className: "object-cover" }), _jsx(Div, { className: "absolute inset-0 overflow-hidden",
|
|
58
|
+
}, children: [_jsx(Image, { src: item.afterImage, alt: afterLabel, fill: true, sizes: "(max-width: 640px) 100vw, 50vw", className: "object-cover" }), _jsx(Div, { className: "absolute inset-0 overflow-hidden",
|
|
59
|
+
// audit-inline-style-ok: computed percentage
|
|
60
|
+
style: { width: `${position}%` }, children: _jsx(Image, { src: item.beforeImage, alt: beforeLabel, fill: true, sizes: "(max-width: 640px) 100vw, 50vw", className: "object-cover" }) }), _jsx(Div, { className: "absolute top-0 bottom-0 z-10 w-0.5 bg-card shadow-md",
|
|
61
|
+
// audit-inline-style-ok: computed percentage
|
|
62
|
+
style: { left: `${position}%` }, children: _jsx(Div, { className: "absolute top-1/2 left-1/2 flex h-8 w-8 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full bg-card shadow-lg", children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M4 8L1 5M4 8L1 11M4 8H12M12 8L15 5M12 8L15 11", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) }) }), _jsx(Span, { size: "xs", weight: "medium", className: "absolute top-3 left-3 z-10 rounded-full bg-black/50 px-2.5 py-0.5 text-white", children: beforeLabel }), _jsx(Span, { size: "xs", weight: "medium", className: "absolute top-3 right-3 z-10 rounded-full bg-black/50 px-2.5 py-0.5 text-white", children: afterLabel })] }), _jsx(Div, { className: `${__P.p4}`, children: _jsx(Text, { className: "text-foreground text-sm font-medium", children: item.caption }) })] }));
|
|
59
63
|
}
|
|
@@ -47,7 +47,11 @@ function CardItem({ card }) {
|
|
|
47
47
|
cardStyle.backgroundColor = card.backgroundColor;
|
|
48
48
|
if (card.textColor)
|
|
49
49
|
cardStyle.color = card.textColor;
|
|
50
|
-
return (_jsxs(Div, { className: `flex flex-col overflow-hidden ${radiusClass} ${shadowClass} bg-[var(--appkit-color-surface)]`,
|
|
50
|
+
return (_jsxs(Div, { className: `flex flex-col overflow-hidden ${radiusClass} ${shadowClass} bg-[var(--appkit-color-surface)]`,
|
|
51
|
+
// audit-inline-style-ok: pass-through style prop
|
|
52
|
+
style: cardStyle, children: [card.image && (_jsx(Div, { className: `relative w-full aspect-video ${__O.hidden}`, children: _jsx(MediaImage, { src: card.image, alt: card.imageAlt ?? "", size: "card" }) })), _jsxs(Div, { className: `flex flex-col gap-2 ${__P.p4} flex-1`, children: [card.eyebrow && (_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest opacity-70", children: card.eyebrow })), card.title && (_jsx(Text, { className: "text-base font-bold leading-snug", children: card.title })), card.body && (_jsx(Text, { className: "text-sm opacity-80 leading-relaxed flex-1", children: card.body })), card.buttons && card.buttons.length > 0 && (_jsx(Div, { className: "flex flex-wrap gap-2 mt-2", children: card.buttons.map((btn, i) => (_jsx("a", { href: btn.href, target: btn.target ?? "_self", rel: btn.target === "_blank" ? "noopener noreferrer" : undefined, className: VARIANT_CLASS[btn.variant] ?? VARIANT_CLASS.primary, children: btn.label }, i))) })), card.formEmbed && (_jsx("iframe", { src: card.formEmbed, sandbox: "allow-forms allow-scripts", loading: "lazy", className: "w-full mt-3 border-0 rounded-md",
|
|
53
|
+
// audit-inline-style-ok: dynamic CSS
|
|
54
|
+
style: { minHeight: "200px" }, title: "Embedded form" }))] })] }));
|
|
51
55
|
}
|
|
52
56
|
// --- Section Header ----------------------------------------------------------
|
|
53
57
|
function SectionHeader({ title }) {
|
|
@@ -14,7 +14,9 @@ function StepCard({ step, visible, delay, }) {
|
|
|
14
14
|
"shadow-md group hover:-translate-y-2 hover:shadow-xl",
|
|
15
15
|
"transition-all duration-300",
|
|
16
16
|
visible ? CLS_VISIBLE : "opacity-0 translate-y-8",
|
|
17
|
-
].join(" "),
|
|
17
|
+
].join(" "),
|
|
18
|
+
// audit-inline-style-ok: computed timing
|
|
19
|
+
style: { transitionDelay: `${delay}ms` }, "data-section": "howitworkssection-div-340", children: [_jsx(Div, { className: "absolute top-4 right-5 font-display text-7xl bg-gradient-to-br from-primary to-cobalt opacity-10 bg-clip-text text-transparent select-none pointer-events-none leading-none", "aria-hidden": "true", children: step.number }), _jsx(Div, { className: `relative z-10 w-10 h-10 rounded-full ${badgeBg} text-white font-bold text-sm flex items-center justify-center mb-5 shadow-md`, children: step.number }), step.renderIcon && (_jsx(Div, { className: `relative z-10 w-14 h-14 rounded-2xl ${iconBg} flex items-center justify-center mb-4 border border-white/80 dark:border-slate-700/50`, children: _jsx("span", { className: `${iconColor}`, "aria-hidden": "true", children: step.renderIcon({ className: "w-6 h-6" }) }) })), _jsx(Heading, { level: 3, className: `relative z-10 text-base font-semibold ${THEME_CONSTANTS.themed.textPrimary} mb-2`, children: step.title }), _jsx(Text, { className: `relative z-10 text-sm ${THEME_CONSTANTS.themed.textSecondary} leading-relaxed`, children: step.desc })] }));
|
|
18
20
|
}
|
|
19
21
|
// --- Section -----------------------------------------------------------------
|
|
20
22
|
export function HowItWorksSection({ title, subtitle, pillLabel, ctaLabel, onCtaClick, steps, className = "", }) {
|
|
@@ -26,9 +26,13 @@ export function NewsletterBanner({ onSubscribe, eyebrow = "STAY CONNECTED", head
|
|
|
26
26
|
setLoading(false);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
return (_jsxs(Section, { className: "relative overflow-hidden py-24",
|
|
29
|
+
return (_jsxs(Section, { className: "relative overflow-hidden py-24",
|
|
30
|
+
// audit-inline-style-ok: dynamic CSS
|
|
31
|
+
style: {
|
|
30
32
|
background: "var(--newsletter-bg, linear-gradient(135deg, var(--color-primary, #1e40af) 0%, var(--color-secondary, #7c3aed) 100%))",
|
|
31
|
-
}, children: [_jsx(Div, { className: "pointer-events-none absolute -top-20 -right-20 h-60 w-60 rounded-full border border-white/[0.06]" }), _jsx(Div, { className: "pointer-events-none absolute -bottom-16 -left-16 h-48 w-48 rounded-full border border-dashed border-white/[0.08]" }), _jsxs(Div, { className: "relative mx-auto max-w-2xl px-4 text-center sm:px-6", children: [_jsx(Span, { size: "xs", weight: "semibold", className: "mb-4 inline-flex rounded-full border border-white/20 px-3 py-1 uppercase tracking-widest text-white/70", children: eyebrow }), _jsx(Heading, { level: 2, className: "text-3xl font-bold text-white sm:text-4xl", children: heading }), _jsx(Text, { className: "mt-4 text-white/60", children: subheading }), success ? (_jsx(Text, { className: "mt-8 rounded-xl border border-white/20 bg-white/10 px-6 py-4 text-sm font-semibold text-white", children: successMessage })) : (_jsxs("form", { onSubmit: handleSubmit, className: "mt-8 flex gap-3", children: [_jsx(Input, { type: "email", value: email, onChange: (e) => setEmail(e.target.value), placeholder: placeholder, required: true, className: "flex-1 rounded-xl border border-white/20 bg-white/10 px-4 py-3 text-sm text-white placeholder:text-white/40 outline-none focus:border-white/40" }), _jsx(Button, { type: "submit", disabled: loading, variant: "primary", className: "shrink-0 rounded-xl px-5 py-3 text-sm font-bold transition-opacity disabled:opacity-60",
|
|
33
|
+
}, children: [_jsx(Div, { className: "pointer-events-none absolute -top-20 -right-20 h-60 w-60 rounded-full border border-white/[0.06]" }), _jsx(Div, { className: "pointer-events-none absolute -bottom-16 -left-16 h-48 w-48 rounded-full border border-dashed border-white/[0.08]" }), _jsxs(Div, { className: "relative mx-auto max-w-2xl px-4 text-center sm:px-6", children: [_jsx(Span, { size: "xs", weight: "semibold", className: "mb-4 inline-flex rounded-full border border-white/20 px-3 py-1 uppercase tracking-widest text-white/70", children: eyebrow }), _jsx(Heading, { level: 2, className: "text-3xl font-bold text-white sm:text-4xl", children: heading }), _jsx(Text, { className: "mt-4 text-white/60", children: subheading }), success ? (_jsx(Text, { className: "mt-8 rounded-xl border border-white/20 bg-white/10 px-6 py-4 text-sm font-semibold text-white", children: successMessage })) : (_jsxs("form", { onSubmit: handleSubmit, className: "mt-8 flex gap-3", children: [_jsx(Input, { type: "email", value: email, onChange: (e) => setEmail(e.target.value), placeholder: placeholder, required: true, className: "flex-1 rounded-xl border border-white/20 bg-white/10 px-4 py-3 text-sm text-white placeholder:text-white/40 outline-none focus:border-white/40" }), _jsx(Button, { type: "submit", disabled: loading, variant: "primary", className: "shrink-0 rounded-xl px-5 py-3 text-sm font-bold transition-opacity disabled:opacity-60",
|
|
34
|
+
// audit-inline-style-ok: dynamic CSS
|
|
35
|
+
style: {
|
|
32
36
|
background: "var(--color-yellow, #FFE500)",
|
|
33
37
|
color: "#0D0D0D",
|
|
34
38
|
}, children: loading ? "…" : ctaLabel })] })), error && (_jsx(Text, { className: CLS_ERROR_TEXT, children: errorMessage })), _jsx(Text, { className: "mt-4 text-xs text-white/40", children: "No spam, ever. Unsubscribe anytime." })] })] }));
|
|
@@ -12,7 +12,9 @@ function SecurityCard({ item, visible, delay, }) {
|
|
|
12
12
|
"rounded-xl border p-5 transition-all duration-500",
|
|
13
13
|
colorClass,
|
|
14
14
|
visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4",
|
|
15
|
-
].join(" "),
|
|
15
|
+
].join(" "),
|
|
16
|
+
// audit-inline-style-ok: computed timing
|
|
17
|
+
style: { transitionDelay: `${delay}ms` }, "data-section": "securityhighlightssection-div-357", children: [item.renderIcon && (_jsx(Div, { className: "w-10 h-10 rounded-lg bg-white/60 dark:bg-white/10 flex items-center justify-center mb-3", children: _jsx("span", { className: iconColorClass, "aria-hidden": "true", children: item.renderIcon({ className: "w-6 h-6" }) }) })), _jsx(Text, { className: "font-semibold mb-1", children: item.title }), _jsx(Text, { variant: "secondary", className: "text-sm leading-relaxed", children: item.description })] }));
|
|
16
18
|
}
|
|
17
19
|
// --- Section -----------------------------------------------------------------
|
|
18
20
|
export function SecurityHighlightsSection({ title, subtitle, pillLabel, items, learnMoreHref, learnMoreLabel = "Learn more →", className = "", }) {
|
|
@@ -9,7 +9,9 @@ function StatCard({ stat, visible, delay, isLast, }) {
|
|
|
9
9
|
"transition-all duration-700",
|
|
10
10
|
visible ? "translate-y-0 opacity-100" : "translate-y-6 opacity-0",
|
|
11
11
|
!isLast ? "border-r border-zinc-200 dark:border-slate-700" : "",
|
|
12
|
-
].join(" "),
|
|
12
|
+
].join(" "),
|
|
13
|
+
// audit-inline-style-ok: computed timing
|
|
14
|
+
style: { transitionDelay: `${delay}ms` }, children: [stat.renderIcon && (_jsx(Div, { className: "mb-4 flex h-14 w-14 items-center justify-center rounded-2xl bg-primary-50 dark:bg-primary-950/40 border border-primary-100 dark:border-primary-900/30", children: stat.renderIcon({ className: "w-7 h-7" }) })), _jsx(Heading, { level: 2, variant: "none", className: "mb-1 font-display text-4xl font-black bg-gradient-to-r from-primary to-secondary dark:from-primary dark:to-cobalt-400 bg-clip-text text-transparent md:text-5xl", children: stat.value }), _jsx(Text, { variant: "none", className: "text-xs uppercase tracking-widest text-zinc-500 dark:text-zinc-400 font-medium", children: stat.label })] }));
|
|
13
15
|
}
|
|
14
16
|
// --- Sectmon -----------------------------------------------------------------
|
|
15
17
|
export function StatsCounterSection({ stats, className = "", }) {
|
|
@@ -9,7 +9,9 @@ function TrustFeatureCard({ item, visible, delay, }) {
|
|
|
9
9
|
"flex flex-col items-center text-center p-4",
|
|
10
10
|
"transition-all duration-500",
|
|
11
11
|
visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4",
|
|
12
|
-
].join(" "),
|
|
12
|
+
].join(" "),
|
|
13
|
+
// audit-inline-style-ok: computed timing
|
|
14
|
+
style: { transitionDelay: `${delay}ms` }, "data-section": "trustfeaturessection-div-373", children: [_jsx(Div, { className: "flex h-14 w-14 items-center justify-center rounded-2xl bg-primary-50 dark:bg-primary-950/40 border border-primary-100 dark:border-primary-900/30 flex-shrink-0", children: item.renderIcon ? (item.renderIcon({ className: "w-7 h-7" })) : (_jsx(Span, { className: "text-primary-600 dark:text-secondary-400 text-2xl", "aria-hidden": "true", children: "\u2713" })) }), _jsx(Heading, { level: 3, className: "mt-3 mb-1 text-sm font-bold tracking-wide uppercase text-zinc-900 dark:text-zinc-100", children: item.title }), _jsx(Text, { className: "text-xs leading-relaxed text-zinc-500 dark:text-zinc-400", children: item.description })] }));
|
|
13
15
|
}
|
|
14
16
|
// --- Single strip-variant item -----------------------------------------------
|
|
15
17
|
function TrustStripItem({ item }) {
|
|
@@ -57,7 +57,9 @@ function SidebarUserHeader({ user, onClose, }) {
|
|
|
57
57
|
}
|
|
58
58
|
: user.photoURL
|
|
59
59
|
? { url: user.photoURL, position: { x: 50, y: 50 }, zoom: 1 }
|
|
60
|
-
: null, size: "md", alt: user.displayName || "User", displayName: user.displayName, email: user.email }), user.role && user.role !== "user" && (_jsx(Div, { className: "absolute -bottom-0.5 -right-0.5 flex items-center justify-center w-4 h-4 rounded-full border-2 border-white dark:border-slate-900 text-white text-[9px] font-bold leading-none select-none",
|
|
60
|
+
: null, size: "md", alt: user.displayName || "User", displayName: user.displayName, email: user.email }), user.role && user.role !== "user" && (_jsx(Div, { className: "absolute -bottom-0.5 -right-0.5 flex items-center justify-center w-4 h-4 rounded-full border-2 border-white dark:border-slate-900 text-white text-[9px] font-bold leading-none select-none",
|
|
61
|
+
// audit-inline-style-ok: runtime brand gradient
|
|
62
|
+
style: { background: ROLE_DOT_COLORS[user.role] ?? "#6b7280" }, title: user.role.charAt(0).toUpperCase() + user.role.slice(1), "aria-label": user.role, children: user.role.charAt(0).toUpperCase() }))] }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100 truncate", children: user.displayName || "User" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 truncate", children: user.email || "" })] })] }), _jsx("button", { type: "button", "aria-label": "Close menu", onClick: onClose, className: "flex-shrink-0 rounded-full p-2 text-zinc-600 hover:bg-zinc-200 hover:text-zinc-900 dark:text-zinc-300 dark:hover:bg-slate-800 dark:hover:text-zinc-100 transition-all hover:rotate-90", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }));
|
|
61
63
|
}
|
|
62
64
|
/** Sidebar header when no user is logged in — title + close button. */
|
|
63
65
|
function SidebarGuestHeader({ sidebarTitle, onClose, }) {
|
|
@@ -63,9 +63,7 @@ export function BottomActionsProvider({ children, }) {
|
|
|
63
63
|
bulkClearRef,
|
|
64
64
|
setInfoLabel,
|
|
65
65
|
clearAll,
|
|
66
|
-
}),
|
|
67
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
68
|
-
[state, setActions, setBulkConfig, setInfoLabel, clearAll]);
|
|
66
|
+
}), [state, setActions, setBulkConfig, setInfoLabel, clearAll]);
|
|
69
67
|
return (_jsx(BottomActionsContext.Provider, { value: value, children: children }));
|
|
70
68
|
}
|
|
71
69
|
// --- Hook ---------------------------------------------------------------------
|
|
@@ -27,6 +27,6 @@ export function BottomNavbar({ user, homeHref, shopHref, cartHref, profileHref,
|
|
|
27
27
|
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [visibleItems.map((item) => (_jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: item.href, label: item.label, icon: item.icon ?? defaultIcon, isActive: pathname === item.href || (item.href !== "/" && pathname.startsWith(item.href + "/")), variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }, item.key))), _jsx(Li, { className: "flex-1", children: _jsxs("button", { type: "button", onClick: onMoreToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.more, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 12h16M4 18h16" }) }), _jsx(Span, { className: labelClassName, children: labels.more })] }) })] }));
|
|
28
28
|
}
|
|
29
29
|
const itemStyle = { width: "20%" };
|
|
30
|
-
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [_jsx(Li, { className: "flex-1", style: itemStyle, children: _jsx(NavItem, { href: homeHref, label: labels.home, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), isActive: pathname === homeHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", style: itemStyle, children: _jsx(NavItem, { href: shopHref, label: labels.products, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" }) }), isActive: pathname === shopHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", style: itemStyle, children: _jsxs("button", { type: "button", onClick: onSearchToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.search, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.search })] }) }), _jsx(Li, { className: "flex-1", style: itemStyle, children: _jsxs(TextLink, { href: cartHref, variant: "none", className: `${slotClassName} ${pathname === cartHref ? activeClassName : inactiveClassName}`, "aria-label": labels.cart, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.cart })] }) }), _jsx(Li, { className: "flex-1", style: itemStyle, children: user ? (_jsxs(TextLink, { href: profileHref, variant: "none", className: `${slotClassName} ${pathname === profileHref ? activeClassName : inactiveClassName}`, "aria-label": labels.profile, children: [_jsx(AvatarDisplay, { cropData: user.avatarMetadata ||
|
|
30
|
+
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: ["// audit-inline-style-ok: pass-through style prop", _jsx(Li, { className: "flex-1", style: itemStyle, children: _jsx(NavItem, { href: homeHref, label: labels.home, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), isActive: pathname === homeHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), "// audit-inline-style-ok: pass-through style prop", _jsx(Li, { className: "flex-1", style: itemStyle, children: _jsx(NavItem, { href: shopHref, label: labels.products, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" }) }), isActive: pathname === shopHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), "// audit-inline-style-ok: pass-through style prop", _jsx(Li, { className: "flex-1", style: itemStyle, children: _jsxs("button", { type: "button", onClick: onSearchToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.search, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.search })] }) }), "// audit-inline-style-ok: pass-through style prop", _jsx(Li, { className: "flex-1", style: itemStyle, children: _jsxs(TextLink, { href: cartHref, variant: "none", className: `${slotClassName} ${pathname === cartHref ? activeClassName : inactiveClassName}`, "aria-label": labels.cart, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.cart })] }) }), "// audit-inline-style-ok: pass-through style prop", _jsx(Li, { className: "flex-1", style: itemStyle, children: user ? (_jsxs(TextLink, { href: profileHref, variant: "none", className: `${slotClassName} ${pathname === profileHref ? activeClassName : inactiveClassName}`, "aria-label": labels.profile, children: [_jsx(AvatarDisplay, { cropData: user.avatarMetadata ||
|
|
31
31
|
(user.photoURL ? { url: user.photoURL, position: { x: 50, y: 50 }, zoom: 1 } : null), size: "sm", alt: user.displayName || "User", displayName: user.displayName, email: user.email }), user.role && (_jsx(Span, { className: `text-[7px] font-semibold uppercase leading-none ${getRoleBadgeClass ? getRoleBadgeClass(user.role) : "text-zinc-500 dark:text-slate-400"}`, children: user.role }))] })) : (_jsx(NavItem, { href: loginHref, label: labels.profile, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" }) }), isActive: pathname === loginHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName })) })] }));
|
|
32
32
|
}
|
|
@@ -31,7 +31,9 @@ function LightboxControlsBar({ items, index, zoom, isFullscreen, L, goPrev, goNe
|
|
|
31
31
|
return (_jsxs(Div, { className: "flex items-center justify-center gap-1.5 px-4 py-3 bg-black/85 backdrop-blur-sm shrink-0 border-t border-white/10 flex-wrap", children: [items.length > 1 && (_jsx(IconBtn, { onClick: (e) => { e.stopPropagation(); goPrev(); }, ariaLabel: L.prevImage, children: _jsx(ChevronLeft, { className: "w-7 h-7" }) })), _jsx(IconBtn, { onClick: zoomOut, disabled: zoom <= MIN_ZOOM, ariaLabel: L.zoomOut, children: _jsx(ZoomOut, { className: "w-7 h-7" }) }), _jsxs(Span, { size: "xs", className: "text-white/70 w-14 text-center tabular-nums", children: [Math.round(zoom * 100), "%"] }), _jsx(IconBtn, { onClick: zoomIn, disabled: zoom >= MAX_ZOOM, ariaLabel: L.zoomIn, children: _jsx(ZoomIn, { className: "w-7 h-7" }) }), zoom > 1 && (_jsx(IconBtn, { onClick: resetZoom, ariaLabel: L.resetZoom, children: _jsx(RotateCcw, { className: "w-7 h-7" }) })), _jsx(Span, { className: "w-px h-6 bg-white/15 mx-1 shrink-0", "aria-hidden": "true" }), _jsxs(Span, { size: "xs", className: "text-white/60 w-12 text-center tabular-nums", children: [index + 1, " / ", items.length] }), _jsx(Span, { className: "w-px h-6 bg-white/15 mx-1 shrink-0", "aria-hidden": "true" }), _jsx(IconBtn, { onClick: toggleFullscreen, ariaLabel: isFullscreen ? L.exitFullscreen : L.enterFullscreen, children: isFullscreen ? _jsx(Minimize, { className: "w-6 h-6" }) : _jsx(Maximize, { className: "w-6 h-6" }) }), _jsx(IconBtn, { onClick: onClose, ariaLabel: L.close, className: CLS_CLOSE_HOVER, children: _jsx(X, { className: "w-7 h-7" }) }), items.length > 1 && (_jsx(IconBtn, { onClick: (e) => { e.stopPropagation(); goNext(); }, ariaLabel: L.nextImage, children: _jsx(ChevronRight, { className: "w-7 h-7" }) }))] }));
|
|
32
32
|
}
|
|
33
33
|
function LightboxThumbnailStrip({ items, index, onSelect, resetZoom, L, }) {
|
|
34
|
-
return (_jsx(Div, { className: "flex gap-2 px-4 py-3 bg-black/70 backdrop-blur-sm overflow-x-auto shrink-0 justify-center",
|
|
34
|
+
return (_jsx(Div, { className: "flex gap-2 px-4 py-3 bg-black/70 backdrop-blur-sm overflow-x-auto shrink-0 justify-center",
|
|
35
|
+
// audit-inline-style-ok: safe-area inset
|
|
36
|
+
style: { paddingBottom: "calc(0.75rem + env(safe-area-inset-bottom, 0px))" }, children: items.map((item, i) => (_jsx(Button, { type: "button", onClick: () => { onSelect(i); resetZoom(); }, "aria-label": L.imageThumbnail(i + 1), "aria-current": i === index ? "true" : undefined, className: cn("relative shrink-0 w-14 h-14 p-0 overflow-hidden rounded-lg border-2 transition-all active:scale-95", i === index ? "border-white opacity-100" : "border-transparent opacity-40 hover:opacity-75"), children: _jsx(MediaImage, { src: item.src, alt: item.alt, size: "thumbnail" }) }, i))) }));
|
|
35
37
|
}
|
|
36
38
|
// --- MediaLightbox ------------------------------------------------------------
|
|
37
39
|
export function MediaLightbox({ items, initialIndex = 0, isOpen, onClose, labels, }) {
|
|
@@ -223,13 +225,17 @@ export function MediaLightbox({ items, initialIndex = 0, isOpen, onClose, labels
|
|
|
223
225
|
if (!isOpen || items.length === 0)
|
|
224
226
|
return null;
|
|
225
227
|
const current = items[index];
|
|
226
|
-
return (_jsxs(Div, { ref: containerRef, className: "fixed inset-0 z-[var(--appkit-z-tooltip)] bg-black/95 flex flex-col", role: "dialog", "aria-modal": "true", "aria-label": L.lightboxTitle, children: [_jsx(Div, { ref: imageAreaRef, className: "flex-1 relative overflow-hidden select-none", onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onMouseUp: handleMouseUp, onMouseLeave: handleMouseUp, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd,
|
|
228
|
+
return (_jsxs(Div, { ref: containerRef, className: "fixed inset-0 z-[var(--appkit-z-tooltip)] bg-black/95 flex flex-col", role: "dialog", "aria-modal": "true", "aria-label": L.lightboxTitle, children: [_jsx(Div, { ref: imageAreaRef, className: "flex-1 relative overflow-hidden select-none", onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onMouseUp: handleMouseUp, onMouseLeave: handleMouseUp, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd,
|
|
229
|
+
// audit-inline-style-ok: dynamic CSS
|
|
230
|
+
style: {
|
|
227
231
|
cursor: zoom > 1 ? (isDragging ? "grabbing" : "grab") : "default",
|
|
228
232
|
touchAction: "none",
|
|
229
233
|
}, onClick: (e) => {
|
|
230
234
|
if (e.target === e.currentTarget)
|
|
231
235
|
onClose();
|
|
232
|
-
}, children: _jsx(Div, { className: "absolute inset-0",
|
|
236
|
+
}, children: _jsx(Div, { className: "absolute inset-0",
|
|
237
|
+
// audit-inline-style-ok: dynamic CSS
|
|
238
|
+
style: {
|
|
233
239
|
transform: `translate(${offset.x}px, ${offset.y}px) scale(${zoom})`,
|
|
234
240
|
transformOrigin: "center center",
|
|
235
241
|
transition: isDragging ? "none" : "transform 0.2s ease",
|
|
@@ -10,9 +10,10 @@ import { useState, useRef, useCallback } from "react";
|
|
|
10
10
|
import { useTranslations } from "next-intl";
|
|
11
11
|
import { Cropper } from "react-advanced-cropper";
|
|
12
12
|
import "react-advanced-cropper/dist/style.css";
|
|
13
|
-
import { Button, Div, Modal, Row, Text } from "../../../ui";
|
|
13
|
+
import { Button, Div, Modal, Row, Text, useToast } from "../../../ui";
|
|
14
14
|
export function ImageEditor({ isOpen, imageUrl, onClose, onSave, aspectRatio, outputFormat = "image/webp", outputQuality = 0.9, maxOutputWidth = 2048, }) {
|
|
15
15
|
const t = useTranslations("mediaEditor");
|
|
16
|
+
const { showToast } = useToast();
|
|
16
17
|
const cropperRef = useRef(null);
|
|
17
18
|
const [rotation, setRotation] = useState(0);
|
|
18
19
|
const [saving, setSaving] = useState(false);
|
|
@@ -59,10 +60,13 @@ export function ImageEditor({ isOpen, imageUrl, onClose, onSave, aspectRatio, ou
|
|
|
59
60
|
const file = new File([blob], `cropped.${ext}`, { type: outputFormat });
|
|
60
61
|
onSave(file);
|
|
61
62
|
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
showToast(err instanceof Error ? err.message : "Failed to save image.", "error");
|
|
65
|
+
}
|
|
62
66
|
finally {
|
|
63
67
|
setSaving(false);
|
|
64
68
|
}
|
|
65
|
-
}, [maxOutputWidth, outputFormat, outputQuality, onSave]);
|
|
69
|
+
}, [maxOutputWidth, outputFormat, outputQuality, onSave, showToast]);
|
|
66
70
|
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: t("cropTitle"), size: "lg", children: _jsxs(Div, { className: "space-y-3", children: [_jsx(Text, { variant: "secondary", size: "xs", children: t("cropInstruction") }), _jsx(Div, { className: "relative w-full max-h-[400px] overflow-hidden rounded-lg bg-zinc-100 dark:bg-slate-800", children: _jsx(Cropper, { ref: cropperRef, src: imageUrl, stencilProps: aspectRatio ? { aspectRatio } : undefined, className: "max-h-[400px]" }) }), _jsxs(Row, { gap: "sm", justify: "center", className: "flex-wrap", children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => handleRotate(-90), "aria-label": t("cropRotateLeft"), children: _jsxs("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: [_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 10h10a4 4 0 014 4v0a4 4 0 01-4 4H3" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 6l-4 4 4 4" })] }) }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => handleRotate(90), "aria-label": t("cropRotateRight"), children: _jsxs("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: [_jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 10H11a4 4 0 00-4 4v0a4 4 0 004 4h10" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M17 6l4 4-4 4" })] }) }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleFlipH, "aria-label": t("cropFlipH"), children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16V4l-4 4m0 4l4 4M17 16V4l4 4m0 4l-4 4M12 2v20" }) }) }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleFlipV, "aria-label": t("cropFlipV"), children: _jsx("svg", { className: "w-4 h-4 rotate-90", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16V4l-4 4m0 4l4 4M17 16V4l4 4m0 4l-4 4M12 2v20" }) }) }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleReset, children: t("cropReset") }), _jsxs(Text, { size: "xs", variant: "secondary", className: "ml-auto", children: [rotation, "\u00B0"] })] }), !aspectRatio && (_jsx(Row, { gap: "sm", justify: "center", className: "flex-wrap", children: [
|
|
67
71
|
{ label: "Free", ratio: undefined },
|
|
68
72
|
{ label: "1:1", ratio: 1 },
|
|
@@ -81,7 +81,7 @@ function ExternalUrlTabPanel({ extInput, setExtInput, extError, setExtError, onA
|
|
|
81
81
|
return (_jsxs(Div, { className: "space-y-2", children: [_jsxs(Row, { gap: "sm", children: [_jsx("input", { type: "url", value: extInput, onChange: (e) => { setExtInput(e.target.value); setExtError(""); }, placeholder: "https://example.com/image.jpg", className: "appkit-input flex-1" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: onApply, children: "Apply" })] }), extError && _jsx(Alert, { variant: "error", children: extError }), _jsx(Text, { size: "xs", variant: "secondary", children: "External URLs are stored as-is and are not watermarked." })] }));
|
|
82
82
|
}
|
|
83
83
|
function MediaPreviewPanel({ value, label, disabled, enableTrim, enableThumbnail, onEditVideo, onRemove, tUpload, tMediaEditor, }) {
|
|
84
|
-
return (_jsxs(Div, { className: `rounded-lg border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-900 ${__P.p3}`, children: [isVideo(value) ? (_jsxs(Div, { className: "space-y-2", children: [_jsx(Div, { className: `relative aspect-video ${__O.hidden} rounded-lg`, children: _jsx(MediaVideo, { src: value, alt: label, controls: true, objectFit: "contain" }) }), _jsx(Text, { size: "xs", variant: "secondary", children: "Video uploaded successfully. Large videos may take longer to process." })] })) : isImage(value) ? (_jsx(Div, { className: `relative aspect-video ${__O.hidden} rounded-lg`, children: _jsx(MediaImage, { src: value, alt: label, size: "card", objectFit: "contain" }) })) : isPdf(value) ? (_jsxs(Div, { className: "space-y-2", children: [_jsx(Div, { className: `relative w-full ${__O.hidden} rounded-lg border border-zinc-200 dark:border-zinc-700`, style: { height: 280 }, children: _jsx("iframe", { src: value, title: filenameFromUrl(value), className: "w-full h-full border-0" }) }), _jsxs(Row, { gap: "md", align: "center", children: [_jsx(Div, { "aria-hidden": true, className: CLS_PDF_BADGE, children: _jsx(Span, { size: "xs", weight: "bold", children: "PDF" }) }), _jsx("a", { href: value, target: "_blank", rel: "noopener noreferrer", className: CLS_PDF_NAME, children: filenameFromUrl(value) })] })] })) : (_jsx("a", { href: value, target: "_blank", rel: "noopener noreferrer", className: CLS_PDF_LINK, children: filenameFromUrl(value) })), !disabled && (_jsxs(Row, { wrap: true, gap: "sm", className: "mt-2", children: [isVideo(value) && (enableTrim || enableThumbnail) && (_jsx(Button, { type: "button", onClick: () => onEditVideo(value), variant: "secondary", size: "sm", children: tMediaEditor("editVideo") })), _jsx(Button, { type: "button", onClick: onRemove, variant: "danger", size: "sm", children: tUpload("remove") })] }))] }));
|
|
84
|
+
return (_jsxs(Div, { className: `rounded-lg border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-900 ${__P.p3}`, children: [isVideo(value) ? (_jsxs(Div, { className: "space-y-2", children: [_jsx(Div, { className: `relative aspect-video ${__O.hidden} rounded-lg`, children: _jsx(MediaVideo, { src: value, alt: label, controls: true, objectFit: "contain" }) }), _jsx(Text, { size: "xs", variant: "secondary", children: "Video uploaded successfully. Large videos may take longer to process." })] })) : isImage(value) ? (_jsx(Div, { className: `relative aspect-video ${__O.hidden} rounded-lg`, children: _jsx(MediaImage, { src: value, alt: label, size: "card", objectFit: "contain" }) })) : isPdf(value) ? (_jsxs(Div, { className: "space-y-2", children: ["// audit-inline-style-ok: dynamic CSS", _jsx(Div, { className: `relative w-full ${__O.hidden} rounded-lg border border-zinc-200 dark:border-zinc-700`, style: { height: 280 }, children: _jsx("iframe", { src: value, title: filenameFromUrl(value), className: "w-full h-full border-0" }) }), _jsxs(Row, { gap: "md", align: "center", children: [_jsx(Div, { "aria-hidden": true, className: CLS_PDF_BADGE, children: _jsx(Span, { size: "xs", weight: "bold", children: "PDF" }) }), _jsx("a", { href: value, target: "_blank", rel: "noopener noreferrer", className: CLS_PDF_NAME, children: filenameFromUrl(value) })] })] })) : (_jsx("a", { href: value, target: "_blank", rel: "noopener noreferrer", className: CLS_PDF_LINK, children: filenameFromUrl(value) })), !disabled && (_jsxs(Row, { wrap: true, gap: "sm", className: "mt-2", children: [isVideo(value) && (enableTrim || enableThumbnail) && (_jsx(Button, { type: "button", onClick: () => onEditVideo(value), variant: "secondary", size: "sm", children: tMediaEditor("editVideo") })), _jsx(Button, { type: "button", onClick: onRemove, variant: "danger", size: "sm", children: tUpload("remove") })] }))] }));
|
|
85
85
|
}
|
|
86
86
|
export function MediaUploadField({ label, value, onChange, onChangeField, onUpload,
|
|
87
87
|
// SB-UNI-Z5 2026-05-13 — `kind` derives accept/maxSizeMB when not set
|
|
@@ -40,6 +40,7 @@ export function useConversation(conversationId) {
|
|
|
40
40
|
const [error, setError] = useState(null);
|
|
41
41
|
const [isConnected, setIsConnected] = useState(false);
|
|
42
42
|
const refetch = useCallback(async () => {
|
|
43
|
+
// toast-intentionally-silent — error stored in setError() for UI inline render
|
|
43
44
|
if (!conversationId) {
|
|
44
45
|
setConversation(null);
|
|
45
46
|
return;
|
|
@@ -100,6 +101,7 @@ export function useConversation(conversationId) {
|
|
|
100
101
|
await refetch();
|
|
101
102
|
}, [conversationId, refetch]);
|
|
102
103
|
const markRead = useCallback(async () => {
|
|
104
|
+
// toast-intentionally-silent — best-effort background op
|
|
103
105
|
if (!conversationId)
|
|
104
106
|
return;
|
|
105
107
|
try {
|
|
@@ -20,6 +20,7 @@ export function useConversations(userId) {
|
|
|
20
20
|
const [isLoading, setIsLoading] = useState(false);
|
|
21
21
|
const [error, setError] = useState(null);
|
|
22
22
|
const refetch = useCallback(async () => {
|
|
23
|
+
// toast-intentionally-silent — error stored in setError() for UI inline render
|
|
23
24
|
if (!userId) {
|
|
24
25
|
setConversations([]);
|
|
25
26
|
return;
|
|
@@ -35,7 +35,9 @@ export function OrderCard({ order, onClick, labels = {} }) {
|
|
|
35
35
|
.sort()[0];
|
|
36
36
|
return (_jsxs(Div, { role: onClick ? "button" : undefined, tabIndex: onClick ? 0 : undefined, onKeyDown: onClick
|
|
37
37
|
? (e) => (e.key === "Enter" || e.key === " ") && onClick(order)
|
|
38
|
-
: undefined, onClick: onClick ? () => onClick(order) : undefined, className: `rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 p-5 ${onClick ? "cursor-pointer transition hover:shadow-md" : ""}`, children: [_jsxs(Row, { wrap: true, align: "start", justify: "between", gap: "3", children: [_jsxs(Div, { children: [_jsxs(Text, { className: "text-xs text-neutral-500 dark:text-zinc-400", children: ["Order #", order.id.slice(-8).toUpperCase()] }), date && (_jsx(Text, { className: "mt-0.5 text-xs text-zinc-400 dark:text-zinc-400", children: date }))] }), _jsx(Span, { size: "xs", weight: "semibold", className: `rounded-full px-3 py-1 capitalize ${statusColor}`, children: labels[order.orderStatus] ?? order.orderStatus.replace(/_/g, " ") })] }), revealsRemaining > 0 && (_jsxs(Row, { gap: "sm", className: "mt-2", children: [_jsxs(Span, { size: "xs", weight: "semibold", className: CLS_PRIZE_BADGE, children: [revealsRemaining, " ", revealsRemaining === 1 ? "reveal" : "reveals", " pending"] }), earliestDeadline && (_jsxs(Span, { size: "xs", className: "text-zinc-500 dark:text-zinc-400", children: ["before ", new Date(earliestDeadline).toLocaleDateString(getDefaultLocale(), { month: "short", day: "numeric" })] }))] })), _jsxs(Row, { wrap: true, gap: "3", className: "mt-4", children: [order.items.slice(0, 3).map((item, i) => (_jsxs(Row, { className: "gap-2", children: [item.image && (_jsx(Div, { role: "img", "aria-label": item.title, className: "h-10 w-10 rounded-lg bg-center bg-cover",
|
|
38
|
+
: undefined, onClick: onClick ? () => onClick(order) : undefined, className: `rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 p-5 ${onClick ? "cursor-pointer transition hover:shadow-md" : ""}`, children: [_jsxs(Row, { wrap: true, align: "start", justify: "between", gap: "3", children: [_jsxs(Div, { children: [_jsxs(Text, { className: "text-xs text-neutral-500 dark:text-zinc-400", children: ["Order #", order.id.slice(-8).toUpperCase()] }), date && (_jsx(Text, { className: "mt-0.5 text-xs text-zinc-400 dark:text-zinc-400", children: date }))] }), _jsx(Span, { size: "xs", weight: "semibold", className: `rounded-full px-3 py-1 capitalize ${statusColor}`, children: labels[order.orderStatus] ?? order.orderStatus.replace(/_/g, " ") })] }), revealsRemaining > 0 && (_jsxs(Row, { gap: "sm", className: "mt-2", children: [_jsxs(Span, { size: "xs", weight: "semibold", className: CLS_PRIZE_BADGE, children: [revealsRemaining, " ", revealsRemaining === 1 ? "reveal" : "reveals", " pending"] }), earliestDeadline && (_jsxs(Span, { size: "xs", className: "text-zinc-500 dark:text-zinc-400", children: ["before ", new Date(earliestDeadline).toLocaleDateString(getDefaultLocale(), { month: "short", day: "numeric" })] }))] })), _jsxs(Row, { wrap: true, gap: "3", className: "mt-4", children: [order.items.slice(0, 3).map((item, i) => (_jsxs(Row, { className: "gap-2", children: [item.image && (_jsx(Div, { role: "img", "aria-label": item.title, className: "h-10 w-10 rounded-lg bg-center bg-cover",
|
|
39
|
+
// audit-inline-style-ok: dynamic image URL
|
|
40
|
+
style: { backgroundImage: `url(${item.image})` } })), _jsxs(Div, { children: [_jsx(Text, { className: `text-sm font-medium text-neutral-900 dark:text-zinc-100 ${THEME_CONSTANTS.utilities.textClamp1}`, children: item.title }), _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: ["\u00D7", item.quantity] })] })] }, i))), order.items.length > 3 && (_jsxs(Span, { size: "xs", className: "self-center text-zinc-400 dark:text-zinc-400", children: ["+", order.items.length - 3, " more"] }))] }), _jsxs(Row, { justify: "between", className: "mt-4 border-t border-neutral-100 dark:border-slate-700 pt-3", children: [_jsxs(Span, { size: "sm", className: "text-neutral-500 dark:text-zinc-400", children: [order.currency ?? "", " Total"] }), _jsx(Span, { weight: "semibold", className: "text-neutral-900 dark:text-zinc-100", children: formatCurrency(order.total, order.currency) })] })] }));
|
|
39
41
|
}
|
|
40
42
|
export function OrdersList({ orders, isLoading, onOrderClick, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No orders found", }) {
|
|
41
43
|
if (isLoading) {
|
|
@@ -58,7 +58,7 @@ function PreOrderInfoSection({ title, productionStatus, maxPerUser, deliveryDate
|
|
|
58
58
|
} }), (categoryName || category || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [category && (_jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: "inline-flex items-center rounded-full border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800/50 px-2.5 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-300 transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: categoryName || category })), !category && categoryName && (_jsx(Span, { size: "xs", weight: "medium", className: "inline-flex items-center rounded-full border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-800/50 px-2.5 py-1 text-zinc-500 dark:text-zinc-400", children: categoryName })), brand && brandSlug && (_jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800/50 px-2.5 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-300 transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: brand })), brand && !brandSlug && (_jsx(Span, { size: "xs", weight: "medium", className: "inline-flex items-center rounded-full border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-800/50 px-2.5 py-1 text-zinc-500 dark:text-zinc-400", children: brand }))] })), productFeatures && features.length > 0 && (_jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures })), !productFeatures && features.length > 0 && (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 px-4 py-3", children: [_jsx(Text, { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: "About this product" }), _jsx(Ul, { className: "space-y-1.5", children: features.map((f, i) => (_jsxs(Li, { className: "flex items-start gap-2 text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i))) })] })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-sm leading-relaxed text-zinc-600 dark:text-zinc-400 line-clamp-4" })), safeSeller && (_jsx(Div, { className: `rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 ${__P.p3}`, children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] uppercase tracking-wide text-zinc-400 dark:text-zinc-400 mb-0.5", children: "Sold by" }), _jsx(Text, { className: "text-sm font-semibold text-zinc-800 dark:text-zinc-200", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-3 py-1.5 text-xs font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] }));
|
|
59
59
|
}
|
|
60
60
|
function PreOrderBuyBarPanel({ reserveTarget, reservedCount, progressPct, productId, price, currency, depositAmount, depositPercent, isCancellable, tags, onReserveNow, }) {
|
|
61
|
-
return (_jsxs(Div, { id: "pre-order-buy-bar", className: `rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 ${__P.p5} space-y-4`, children: [reserveTarget > 0 && (_jsxs(Div, { className: "space-y-2", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: [reservedCount, " of ", reserveTarget, " reserved"] }), _jsxs(Span, { size: "xs", weight: "semibold", className: "text-primary-600 dark:text-primary-400", children: [progressPct, "%"] })] }),
|
|
61
|
+
return (_jsxs(Div, { id: "pre-order-buy-bar", className: `rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 ${__P.p5} space-y-4`, children: [reserveTarget > 0 && (_jsxs(Div, { className: "space-y-2", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: [reservedCount, " of ", reserveTarget, " reserved"] }), _jsxs(Span, { size: "xs", weight: "semibold", className: "text-primary-600 dark:text-primary-400", children: [progressPct, "%"] })] }), _jsxs(Div, { className: `h-2 w-full ${__O.hidden} rounded-full bg-zinc-200 dark:bg-zinc-700`, children: ["// audit-inline-style-ok: computed percentage", _jsx(Div, { className: "h-full rounded-full bg-primary transition-all", style: { width: `${progressPct}%` } })] })] })), onReserveNow ? (_jsx(PreOrderActionsClient, { productId: productId, price: price, currency: currency, depositAmount: depositAmount, depositPercent: depositPercent, isCancellable: isCancellable, tags: tags, onReserveNow: onReserveNow })) : (_jsxs(_Fragment, { children: [price !== null && (_jsxs(Div, { children: [_jsx(Text, { className: "text-2xl font-bold text-zinc-900 dark:text-zinc-50", children: formatCurrency(price, currency) }), depositAmount !== null && (_jsxs(Text, { className: "mt-0.5 text-xs text-zinc-500 dark:text-zinc-400", children: ["Reserve with ", formatCurrency(depositAmount, currency), depositPercent !== null ? ` (${depositPercent}% deposit)` : ""] }))] })), _jsxs(Stack, { gap: "sm", children: [_jsx(Button, { variant: "primary", size: "md", className: "w-full", children: "Reserve Now" }), isCancellable && (_jsx(Text, { className: "text-center text-xs text-zinc-500 dark:text-zinc-400", children: "\u2713 Free cancellation before production" }))] }), tags.length > 0 && (_jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { size: "xs", className: "rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-1 text-zinc-600 dark:text-zinc-300", children: tag }, tag))) }) })), _jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "sm", justify: "center", className: "text-center", children: [
|
|
62
62
|
{ icon: "🔒", label: "Secure\nPayment" },
|
|
63
63
|
{ icon: "📅", label: "Guaranteed\nDelivery" },
|
|
64
64
|
{ icon: "↩", label: "Free\nCancellation" },
|
|
@@ -155,7 +155,11 @@ export function CompareOverlay({ isOpen, items: passedItems, productIds, product
|
|
|
155
155
|
}, [onRemove]);
|
|
156
156
|
if (!isOpen)
|
|
157
157
|
return null;
|
|
158
|
-
return (
|
|
158
|
+
return (
|
|
159
|
+
// audit-inline-style-ok: pass-through style prop
|
|
160
|
+
_jsxs(Div, { className: OVERLAY_CLASS, style: OVERLAY_STYLE, role: "dialog", "aria-modal": "true", "aria-label": labels.title, children: [_jsxs(Row, { justify: "between", align: "center", className: HEADER_CLASS, children: [_jsx(Heading, { level: 2, className: "text-base font-semibold", children: labels.title }), _jsx("button", { type: "button", onClick: onClose, "aria-label": labels.close, className: "rounded-full p-2 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800", children: _jsx(X, { className: "h-5 w-5" }) })] }), _jsx(Div, { className: `flex-1 ${__O.auto} ${__P.p4}`, children: isLoading ? (_jsx(Text, { variant: "secondary", className: "text-center py-12", children: "Loading\u00E2\u20AC\u00A6" })) : trimmed.length === 0 ? (_jsx(Text, { variant: "secondary", className: "text-center py-12", children: labels.empty })) : (_jsxs(_Fragment, { children: [_jsx(Div, { className: "hidden md:grid gap-4",
|
|
161
|
+
// audit-inline-style-ok: dynamic CSS
|
|
162
|
+
style: {
|
|
159
163
|
gridTemplateColumns: `repeat(${trimmed.length}, minmax(0, 1fr))`,
|
|
160
164
|
}, children: trimmed.map((item) => (_jsx(CompareColumn, { item: item, productType: productType, labels: labels, onRemove: onRemove ? handleRemove : undefined, onClose: onClose }, item.id))) }), _jsxs(Div, { className: "md:hidden", children: [_jsx(Div, { ref: swipeRef, className: "touch-pan-y", children: trimmed[activeIndex] && (_jsx(CompareColumn, { item: trimmed[activeIndex], productType: productType, labels: labels, onRemove: onRemove ? handleRemove : undefined, onClose: onClose })) }), trimmed.length > 1 && (_jsx(Row, { justify: "center", gap: "xs", className: "mt-4", children: trimmed.map((it, idx) => (_jsx("button", { type: "button", "aria-label": `Show item ${idx + 1}`, onClick: () => setActiveIndex(idx), className: `h-2 w-2 rounded-full transition-colors ${idx === activeIndex
|
|
161
165
|
? "bg-primary"
|
|
@@ -45,7 +45,9 @@ export function FeatureBadge({ featureId, features }) {
|
|
|
45
45
|
const f = features.find((x) => x.id === featureId);
|
|
46
46
|
if (!f)
|
|
47
47
|
return null;
|
|
48
|
-
return (_jsxs(Span, { title: f.description ?? undefined, className: BADGE_BASE_CLASS,
|
|
48
|
+
return (_jsxs(Span, { title: f.description ?? undefined, className: BADGE_BASE_CLASS,
|
|
49
|
+
// audit-inline-style-ok: computed color style fn
|
|
50
|
+
style: colorStyleFor(f.iconColor), children: [_jsx(FeatureIcon, { icon: f.icon }), f.label] }));
|
|
49
51
|
}
|
|
50
52
|
export function FeatureBadgeList({ productFeatureIds, features, maxVisible = 0, className, }) {
|
|
51
53
|
if (!productFeatureIds || productFeatureIds.length === 0)
|
|
@@ -93,7 +93,7 @@ export function MarketplacePrizeDrawCard({ product, className = "", variant = "g
|
|
|
93
93
|
const it = thumbItems[i];
|
|
94
94
|
const img = it?.images?.[0];
|
|
95
95
|
return (_jsx(Div, { className: "relative overflow-hidden bg-[var(--appkit-color-surface-muted)]", children: img ? (
|
|
96
|
-
/* eslint-disable-next-line @next/next/no-
|
|
96
|
+
/* eslint-disable-next-line @next/next/no-img-element */
|
|
97
97
|
_jsx("img", { src: img, alt: it?.title ?? `Prize ${i + 1}`, loading: "lazy", className: "absolute inset-0 h-full w-full object-cover" })) : null }, `thumb-${i}`));
|
|
98
98
|
}) })) : (_jsx(Div, { className: "absolute inset-0 flex items-center justify-center bg-[var(--appkit-color-surface-muted)]", children: _jsx(Text, { className: "text-xs text-[var(--appkit-color-text-muted)]", children: "No prizes" }) })) }), _jsxs(Div, { className: "absolute right-2 top-2 flex flex-col items-end gap-1", children: [_jsx(Span, { size: "xs", weight: "medium", className: CLS_PRIZE_PILL, children: mergedLabels.prizeDrawBadge }), _jsx(Span, { size: "xs", weight: "medium", className: `inline-flex items-center rounded-full px-2 py-0.5 ${statusVariant(status)}`, children: statusLabel(status, mergedLabels) })] }), status === "closed" ? (_jsx(Div, { className: "absolute inset-0 flex items-center justify-center bg-black/40", children: _jsx(Span, { size: "xs", weight: "bold", className: "rounded bg-zinc-900/80 px-3 py-1 uppercase tracking-wider text-white", children: mergedLabels.closedBadge }) })) : null, onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (event) => {
|
|
99
99
|
event.stopPropagation();
|
|
@@ -107,7 +107,9 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
|
|
|
107
107
|
return (_jsx(Text, { className: `mt-1 text-[11px] font-medium ${low
|
|
108
108
|
? "text-warning"
|
|
109
109
|
: "text-zinc-500 dark:text-zinc-400"}`, children: low ? `Only ${stock} left` : `${stock} in stock` }));
|
|
110
|
-
})(), featuresList && product.features && product.features.length > 0 && (_jsx(FeatureBadgeList, { productFeatureIds: product.features, features: featuresList, maxVisible: PRODUCT_FEATURE_CARD_MAX_VISIBLE, className: "mt-2" })), (onAddToCart || onBuyNow) && (
|
|
110
|
+
})(), featuresList && product.features && product.features.length > 0 && (_jsx(FeatureBadgeList, { productFeatureIds: product.features, features: featuresList, maxVisible: PRODUCT_FEATURE_CARD_MAX_VISIBLE, className: "mt-2" })), (onAddToCart || onBuyNow) && (
|
|
111
|
+
// audit-inline-style-ok: runtime grid template
|
|
112
|
+
_jsxs(Div, { className: "mt-2 grid gap-1.5", style: { gridTemplateColumns: onBuyNow && onAddToCart ? "1fr 1fr" : "1fr" }, children: [onBuyNow && (_jsxs(Button, { type: "button", variant: "primary", size: "sm", onClick: (e) => {
|
|
111
113
|
e.stopPropagation();
|
|
112
114
|
e.preventDefault();
|
|
113
115
|
onBuyNow(product);
|