@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
|
@@ -9,7 +9,9 @@ const CHEVRON_STYLE = {
|
|
|
9
9
|
export function SidebarCollapseToggle({ expanded, onToggle, expandedLabel = "Collapse sidebar", collapsedLabel = "Expand sidebar", }) {
|
|
10
10
|
return (_jsx("button", { type: "button", onClick: onToggle, "aria-label": expanded ? expandedLabel : collapsedLabel, "aria-expanded": expanded,
|
|
11
11
|
// eslint-disable-next-line lir/no-inline-static-style
|
|
12
|
+
// audit-inline-style-ok: pass-through style prop
|
|
12
13
|
style: HANDLE_STYLE, className: "w-9 shrink-0 flex items-center justify-center cursor-pointer rounded-r-[1.25rem] shadow-lg transition-all duration-200 hover:shadow-xl hover:brightness-110 active:scale-[0.96]", children: _jsx("svg", { className: `w-4 h-4 drop-shadow-sm transition-transform duration-300 ${expanded ? "" : "rotate-180"}`,
|
|
13
14
|
// eslint-disable-next-line lir/no-inline-static-style
|
|
15
|
+
// audit-inline-style-ok: pass-through style prop
|
|
14
16
|
style: CHEVRON_STYLE, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M15 19l-7-7 7-7" }) }) }));
|
|
15
17
|
}
|
|
@@ -20,7 +20,6 @@ function defaultSink(event) {
|
|
|
20
20
|
if (typeof window === "undefined") {
|
|
21
21
|
// SSR / Node — best-effort console log; production servers can swap
|
|
22
22
|
// the sink to a real logger via `setActionTrackerSink`.
|
|
23
|
-
// eslint-disable-next-line no-console
|
|
24
23
|
if (process.env.NODE_ENV !== "production") {
|
|
25
24
|
console.debug("[action-tracker]", event);
|
|
26
25
|
}
|
|
@@ -30,7 +29,6 @@ function defaultSink(event) {
|
|
|
30
29
|
// deployed; for now stay silent in prod to avoid 404 noise + dev-mode
|
|
31
30
|
// logs surface the events.
|
|
32
31
|
if (process.env.NODE_ENV !== "production") {
|
|
33
|
-
// eslint-disable-next-line no-console
|
|
34
32
|
console.debug("[action-tracker]", event);
|
|
35
33
|
}
|
|
36
34
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-require-imports
|
|
2
1
|
const getAdminDb = () => module.require("../providers/db-firebase").getAdminDb();
|
|
3
2
|
import { DatabaseError } from "../errors";
|
|
4
3
|
import { serverLogger } from "../monitoring";
|
|
@@ -50,6 +50,7 @@ export function NotificationBell({ limit = 10, viewAllHref, labels, icons, rende
|
|
|
50
50
|
const emitSuccess = onMarkAllReadSuccess ?? showSuccess;
|
|
51
51
|
const emitError = onMarkAllReadError ?? showError;
|
|
52
52
|
const handleMarkAllRead = useCallback(async () => {
|
|
53
|
+
// toast-intentionally-silent — uses emitError(labels.error) callback for feedback
|
|
53
54
|
try {
|
|
54
55
|
await markAllRead(undefined);
|
|
55
56
|
refetch();
|
|
@@ -65,6 +65,7 @@ export function UserOffersPanel({ fetchEndpoint = "/api/user/offers", onAcceptCo
|
|
|
65
65
|
const [fetchError, setFetchError] = useState("");
|
|
66
66
|
const [showLoginModal, setShowLoginModal] = useState(false);
|
|
67
67
|
const loadOffers = useCallback(async () => {
|
|
68
|
+
// toast-intentionally-silent — error rendered inline via setFetchError()
|
|
68
69
|
setLoading(true);
|
|
69
70
|
setFetchError("");
|
|
70
71
|
try {
|
|
@@ -77,7 +77,9 @@ export function UserSidebar({ items, groups, mobileOpen = false, onCloseMobile,
|
|
|
77
77
|
// ── Persistent sidebar variant (desktop left slide-over, mobile bottom-sheet) ──────
|
|
78
78
|
if (variant === "sidebar") {
|
|
79
79
|
const handleToggle = onToggle ?? close;
|
|
80
|
-
return (_jsxs(_Fragment, { children: [desktopOpen && (_jsx(Div, { className: "hidden md:block fixed inset-0 bg-black/40 backdrop-blur-[2px] z-30", onClick: handleToggle, "aria-hidden": "true" })), _jsxs(Div, { className: "hidden md:flex fixed left-0 z-40 transition-transform duration-300",
|
|
80
|
+
return (_jsxs(_Fragment, { children: [desktopOpen && (_jsx(Div, { className: "hidden md:block fixed inset-0 bg-black/40 backdrop-blur-[2px] z-30", onClick: handleToggle, "aria-hidden": "true" })), _jsxs(Div, { className: "hidden md:flex fixed left-0 z-40 transition-transform duration-300",
|
|
81
|
+
// audit-inline-style-ok: dynamic CSS
|
|
82
|
+
style: {
|
|
81
83
|
top: "var(--header-height, 3.5rem)",
|
|
82
84
|
height: "calc(100vh - var(--header-height, 3.5rem))",
|
|
83
85
|
width: "13rem",
|
|
@@ -59,7 +59,9 @@ const TEXT_ALIGN_OPTIONS = [
|
|
|
59
59
|
// ── Zone Picker ──────────────────────────────────────────────────────────────
|
|
60
60
|
function ZonePicker({ selected, onChange, disabled, }) {
|
|
61
61
|
const zones = [1, 2, 3, 4, 5, 6];
|
|
62
|
-
return (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "Zone (row \u00D7 col grid)" }), _jsx("div", { className: "grid gap-1",
|
|
62
|
+
return (_jsxs(Div, { children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "Zone (row \u00D7 col grid)" }), _jsx("div", { className: "grid gap-1",
|
|
63
|
+
// audit-inline-style-ok: dynamic CSS
|
|
64
|
+
style: { gridTemplateRows: "repeat(2, 1fr)", gridTemplateColumns: "repeat(3, 1fr)", width: 150 }, children: zones.map((z) => {
|
|
63
65
|
const isDisabled = disabled?.(z) ?? false;
|
|
64
66
|
return (_jsx("button", { type: "button", disabled: isDisabled, onClick: () => !isDisabled && onChange(z), className: `rounded text-xs font-mono py-2 border transition-colors ${selected === z
|
|
65
67
|
? "bg-primary text-white border-primary"
|
|
@@ -47,7 +47,9 @@ export function AdminSidebar({ renderNavItems, renderHeader, renderFooter, activ
|
|
|
47
47
|
const mobileNavContent = groups ? (_jsx(GroupsContent, { groups: groups, activePath: activePath, onItemClick: close })) : (_jsxs(Nav, { "aria-label": "Admin sidebar", className: "py-3", children: [renderHeader?.(), renderNavItems?.(activePath), renderFooter?.()] }));
|
|
48
48
|
if (variant === "sidebar") {
|
|
49
49
|
const handleToggle = onToggle ?? close;
|
|
50
|
-
return (_jsxs(_Fragment, { children: [desktopOpen && (_jsx(Div, { className: "hidden md:block fixed inset-0 bg-black/40 backdrop-blur-[2px] z-30", onClick: handleToggle, "aria-hidden": "true" })), _jsxs(Div, { className: "hidden md:flex fixed left-0 z-40 transition-transform duration-300",
|
|
50
|
+
return (_jsxs(_Fragment, { children: [desktopOpen && (_jsx(Div, { className: "hidden md:block fixed inset-0 bg-black/40 backdrop-blur-[2px] z-30", onClick: handleToggle, "aria-hidden": "true" })), _jsxs(Div, { className: "hidden md:flex fixed left-0 z-40 transition-transform duration-300",
|
|
51
|
+
// audit-inline-style-ok: dynamic CSS
|
|
52
|
+
style: {
|
|
51
53
|
top: "var(--header-height, 3.5rem)",
|
|
52
54
|
height: "calc(100vh - var(--header-height, 3.5rem))",
|
|
53
55
|
width: "18rem",
|
|
@@ -374,7 +374,9 @@ export function AdminSiteSettingsView({ labels = {}, ...rest }) {
|
|
|
374
374
|
["legal", "⑫ Legal"],
|
|
375
375
|
["whatsapp", "⑬ WhatsApp"],
|
|
376
376
|
["notifications", "⑭ Notifications"],
|
|
377
|
-
].map(([value, label]) => (_jsx(TabsTrigger, { value: value, children: label }, value))) }), _jsx(TabsContent, { value: "about", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); aboutMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Override the About page hero and mission text. Leave blank to use the platform defaults." }), _jsx(Input, { label: "Hero title", value: aboutTitle, onChange: (e) => setAboutTitle(e.target.value), placeholder: "About LetItRip" }), _jsx(Input, { label: "Hero subtitle", value: aboutSubtitle, onChange: (e) => setAboutSubtitle(e.target.value), placeholder: "Connecting buyers, sellers, and bidders in one vibrant marketplace" }), _jsx(Input, { label: "Mission section title", value: aboutMissionTitle, onChange: (e) => setAboutMissionTitle(e.target.value), placeholder: "Our Mission" }), _jsxs(_Fragment, { children: [_jsx(Text, { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Mission text" }), _jsx("textarea", { value: aboutMissionText, onChange: (e) => setAboutMissionText(e.target.value), placeholder: "LetItRip was built to democratise commerce\u2026", rows: 4, className: CLS_TEXTAREA })] }), _jsx(Input, { label: "CTA banner title", value: aboutCtaTitle, onChange: (e) => setAboutCtaTitle(e.target.value), placeholder: "Ready to get started?" }), _jsx(GroupSaveButton, { isPending: aboutMutation.isPending })] }) }), _jsx(TabsContent, { value: "branding", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); brandingMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Site name", value: siteName, onChange: (e) => setSiteName(e.target.value), placeholder: "LetItRip" }), _jsx(Input, { label: "Tagline", value: tagline, onChange: (e) => setTagline(e.target.value), placeholder: "India's Largest Collectibles Marketplace" }), _jsx(ImageUpload, { label: "Logo", currentImage: logoUrl, onUpload: (file) => upload(file, "store"), onChange: setLogoUrl }), _jsx(ImageUpload, { label: "Favicon", currentImage: faviconUrl, onUpload: (file) => upload(file, "store"), onChange: setFaviconUrl }), _jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "Maintenance mode", checked: maintenanceMode, onChange: setMaintenanceMode }), _jsx(Input, { label: "Maintenance message", value: maintenanceMessage, onChange: (e) => setMaintenanceMessage(e.target.value), placeholder: "We're back soon.", disabled: !maintenanceMode })] }), _jsx(GroupSaveButton, { isPending: brandingMutation.isPending })] }) }), _jsx(TabsContent, { value: "appearance", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); appearanceMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs(Grid, { gap: "md", className: "grid-cols-3", children: [_jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "mb-1", children: "Primary color" }), _jsx("input", { type: "color", value: primaryColor || "#000000", onChange: (e) => setPrimaryColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "mb-1", children: "Secondary color" }), _jsx("input", { type: "color", value: secondaryColor || "#000000", onChange: (e) => setSecondaryColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "mb-1", children: "Accent color" }), _jsx("input", { type: "color", value: accentColor || "#000000", onChange: (e) => setAccentColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] })] }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Select, { label: "Default theme", options: THEME_OPTIONS, value: defaultTheme, onValueChange: setDefaultTheme }), _jsx(Select, { label: "Font family", options: FONT_OPTIONS, value: fontFamily, onValueChange: setFontFamily })] }), _jsx(GroupSaveButton, { isPending: appearanceMutation.isPending })] }) }), _jsx(TabsContent, { value: "announcement", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); announcementMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Toggle, { label: "Show announcement bar", checked: announcementEnabled, onChange: setAnnouncementEnabled }), _jsx(Input, { label: "Announcement text", value: announcementText, onChange: (e) => setAnnouncementText(e.target.value), placeholder: "\uD83C\uDF89 Free shipping on orders \u20B9999+", disabled: !announcementEnabled }), _jsx(Input, { label: "Link URL (optional)", value: announcementLink, onChange: (e) => setAnnouncementLink(e.target.value), placeholder: "/products", disabled: !announcementEnabled }), _jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "mb-1", children: "Background color" }), _jsx("input", { type: "color", value: announcementBg || "#1d4ed8", onChange: (e) => setAnnouncementBg(e.target.value), className: "h-10 w-32 rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer", disabled: !announcementEnabled })] }), _jsx(GroupSaveButton, { isPending: announcementMutation.isPending })] }) }), _jsx(TabsContent, { value: "seo", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); seoMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Default meta title", value: seoTitle, onChange: (e) => setSeoTitle(e.target.value), placeholder: "LetItRip \u2014 Buy, Sell & Auction Collectibles in India", maxLength: 60, helperText: "Max 60 chars. Use {page} token for dynamic insertion." }), _jsx(Input, { label: "Default meta description", value: seoDescription, onChange: (e) => setSeoDescription(e.target.value), placeholder: "India's largest collectibles marketplace\u2026", maxLength: 160, helperText: "Max 160 chars." }), _jsx(ImageUpload, { label: "Default OG image", currentImage: seoOgImage, onUpload: (file) => upload(file, "store"), onChange: setSeoOgImage }), _jsx(Input, { label: "Canonical base URL", value: canonicalUrl, onChange: (e) => setCanonicalUrl(e.target.value), placeholder: "https://letitrip.in" }), _jsx(Toggle, { label: "Robots noindex (disables search indexing \u2014 use carefully)", checked: seoNoIndex, onChange: setSeoNoIndex }), _jsx(GroupSaveButton, { isPending: seoMutation.isPending })] }) }), _jsx(TabsContent, { value: "contact", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); contactMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Support email", value: supportEmail, onChange: (e) => setSupportEmail(e.target.value), type: "email", placeholder: "support@letitrip.in" }), _jsx(Input, { label: "Support phone", value: supportPhone, onChange: (e) => setSupportPhone(e.target.value), placeholder: "+91 XXXXX XXXXX" })] }), _jsx(Input, { label: "Physical address", value: supportAddress, onChange: (e) => setSupportAddress(e.target.value), placeholder: "Mumbai, Maharashtra, India" }), _jsx(Input, { label: "Support hours", value: supportHours, onChange: (e) => setSupportHours(e.target.value), placeholder: "Mon\u2013Fri, 10 AM \u2013 6 PM IST" }), _jsx(Text, { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400 pt-2", children: "Social links" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Instagram URL", value: instagram, onChange: (e) => setInstagram(e.target.value), placeholder: "https://instagram.com/letitrip" }), _jsx(Input, { label: "Twitter / X URL", value: twitter, onChange: (e) => setTwitter(e.target.value), placeholder: "https://twitter.com/letitrip" }), _jsx(Input, { label: "Facebook URL", value: facebook, onChange: (e) => setFacebook(e.target.value), placeholder: "https://facebook.com/letitrip" }), _jsx(Input, { label: "YouTube URL", value: youtube, onChange: (e) => setYoutube(e.target.value), placeholder: "https://youtube.com/@letitrip" }), _jsx(Input, { label: "WhatsApp number", value: whatsapp, onChange: (e) => setWhatsapp(e.target.value), placeholder: "+91XXXXXXXXXX" }), _jsx(Input, { label: "LinkedIn URL", value: linkedin, onChange: (e) => setLinkedin(e.target.value), placeholder: "https://linkedin.com/company/letitrip" }), _jsx(Input, { label: "Pinterest URL", value: pinterest, onChange: (e) => setPinterest(e.target.value), placeholder: "https://pinterest.com/letitrip" })] }), _jsx(GroupSaveButton, { isPending: contactMutation.isPending })] }) }), _jsx(TabsContent, { value: "watermark", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); watermarkMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Select, { label: "Watermark type", options: [{ label: "Text", value: "text" }, { label: "Image", value: "image" }], value: watermarkType, onValueChange: (v) => setWatermarkType(v) }), watermarkType === "text" ? (_jsx(Input, { label: "Watermark text", value: watermarkText, onChange: (e) => setWatermarkText(e.target.value), placeholder: "letitrip.in" })) : (_jsx(ImageUpload, { label: "Watermark image", currentImage: watermarkImageUrl, onUpload: (file) => upload(file, "store"), onChange: setWatermarkImageUrl })), _jsx(Slider, { label: `Size — ${watermarkSize}% of image width`, value: watermarkSize, onChange: setWatermarkSize, min: 5, max: 100, step: 5 }), _jsx(Slider, { label: `Opacity — ${watermarkOpacity}%`, value: watermarkOpacity, onChange: setWatermarkOpacity, min: 5, max: 100, step: 5 }), _jsxs(Stack, { gap: "xs", surface: "muted", rounded: "lg", border: "default", padding: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Preview (text watermark only)" }), _jsx(Row, { surface: "default", justify: "end", align: "end", className: `relative h-32 ${__O.hidden} rounded`, children: _jsx(Span, { weight: "medium", className: "text-zinc-400 select-none p-2", style: { fontSize: `${Math.max(10, watermarkSize / 5)}px`, opacity: watermarkOpacity / 100 }, children: watermarkText }) })] }), _jsx(GroupSaveButton, { isPending: watermarkMutation.isPending })] }) }), _jsx(TabsContent, { value: "fees", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); feesMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Platform fee \u2014 our cut (%)", helperText: "% charged on order value. Buyer pays this.", value: String(platformFeePercent), onChange: (e) => setPlatformFeePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "GST on platform fee (%)", helperText: "Applied to our fee only (not full order). Usually 18%.", value: String(gstPercent), onChange: (e) => setGstPercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "Razorpay gateway cost (%)", helperText: "Gateway's own fee \u2014 absorbed by platform, not passed through.", value: String(gatewayFeePercent), onChange: (e) => setGatewayFeePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 10, step: 0.01 }), _jsx(Input, { label: "Minimum transaction fee (\u20B9)", helperText: "Per-transaction floor. Total charge will never be below base + this.", value: String(minimumTransactionFee), onChange: (e) => setMinimumTransactionFee(parseFloat(e.target.value) || 0), type: "number", min: 0, step: 0.01 }), _jsx(Input, { label: "Seller payout hold (days)", value: String(payoutHoldDays), onChange: (e) => setPayoutHoldDays(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Minimum payout amount (\u20B9)", value: String(minPayoutAmount), onChange: (e) => setMinPayoutAmount(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Auction listing fee (\u20B9)", value: String(auctionListingFee), onChange: (e) => setAuctionListingFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Pre-order listing fee (\u20B9)", value: String(preOrderListingFee), onChange: (e) => setPreOrderListingFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Featured slot fee (\u20B9)", value: String(featuredSlotFee), onChange: (e) => setFeaturedSlotFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Promoted slot fee (\u20B9)", value: String(promotedSlotFee), onChange: (e) => setPromotedSlotFee(parseInt(e.target.value) || 0), type: "number", min: 0 })] }), _jsx(GroupSaveButton, { isPending: feesMutation.isPending })] }) }), _jsx(TabsContent, { value: "integrations", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); integrationsMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Keys are masked in transit and stored encrypted. Click Reveal to view." }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Razorpay" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Razorpay Key ID", value: razorpayKeyId, onChange: setRazorpayKeyId, placeholder: "rzp_live_\u2026" }), _jsx(MaskedInput, { label: "Razorpay Secret", value: razorpaySecret, onChange: setRazorpaySecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Shiprocket" }), _jsx(MaskedInput, { label: "Shiprocket API token", value: shiprocketToken, onChange: setShiprocketToken, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "SMTP / Email" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "SMTP host", value: smtpHost, onChange: (e) => setSmtpHost(e.target.value), placeholder: "smtp.sendgrid.net" }), _jsx(Input, { label: "SMTP port", value: smtpPort, onChange: (e) => setSmtpPort(e.target.value), type: "number", placeholder: "587" }), _jsx(Input, { label: "SMTP user", value: smtpUser, onChange: (e) => setSmtpUser(e.target.value), placeholder: "apikey" }), _jsx(MaskedInput, { label: "SMTP password", value: smtpPassword, onChange: setSmtpPassword, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsx(Input, { label: "From address", value: smtpFrom, onChange: (e) => setSmtpFrom(e.target.value), placeholder: "noreply@letitrip.in", type: "email" })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Analytics & Tracking" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Google Analytics ID", value: gaMeasurementId, onChange: (e) => setGaMeasurementId(e.target.value), placeholder: "G-XXXXXXXXXX" }), _jsx(Input, { label: "Facebook Pixel ID", value: fbPixelId, onChange: (e) => setFbPixelId(e.target.value), placeholder: "XXXXXXXXXXXXXXXX" }), _jsx(Input, { label: "GTM Container ID", value: gtmContainerId, onChange: (e) => setGtmContainerId(e.target.value), placeholder: "GTM-XXXXXXX" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Meta (Instagram & Facebook Social Feed)" }), _jsx(Text, { size: "xs", color: "muted", children: "Used by the Social Feed section to fetch Instagram and Facebook posts via Meta Graph API v19." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Page Access Token", value: metaPageAccessToken, onChange: setMetaPageAccessToken, placeholder: "EAAxxxxxxx\u2026" }), _jsx(Input, { label: "Page ID (or handle)", value: metaPageId, onChange: (e) => setMetaPageId(e.target.value), placeholder: "letitrip" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "TikTok for Developers (Social Feed)" }), _jsx(Text, { size: "xs", color: "muted", children: "Client credentials + long-lived access token from TikTok for Developers. Used to list your account's public videos." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Client Key", value: tiktokClientKey, onChange: setTiktokClientKey, placeholder: "aw\u2026" }), _jsx(MaskedInput, { label: "Client Secret", value: tiktokClientSecret, onChange: setTiktokClientSecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" }), _jsx(MaskedInput, { label: "Access Token (long-lived)", value: tiktokAccessToken, onChange: setTiktokAccessToken, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "DeviantArt OAuth2 (Social Feed)" }), _jsx(Text, { size: "xs", color: "muted", children: "Client credentials for DeviantArt gallery fetching (client-credentials OAuth2 flow \u2014 no user login required)." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Client ID", value: deviantartClientId, onChange: setDeviantartClientId, placeholder: "1234" }), _jsx(MaskedInput, { label: "Client Secret", value: deviantartClientSecret, onChange: setDeviantartClientSecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsx(GroupSaveButton, { isPending: integrationsMutation.isPending })] }) }), _jsx(TabsContent, { value: "shipping", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); shippingMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Free shipping threshold (\u20B9)", value: String(freeShippingThreshold), onChange: (e) => setFreeShippingThreshold(parseInt(e.target.value) || 0), type: "number", min: 0, helperText: "Orders above this amount get free shipping." }), _jsx(Toggle, { label: "Cash on delivery (COD) enabled", checked: codEnabled, onChange: setCodEnabled }), _jsx(Select, { label: "Default carrier", options: CARRIER_OPTIONS, value: defaultCarrier, onValueChange: setDefaultCarrier }), _jsx(Input, { label: "Max delivery radius (km, 0 = no limit)", value: String(maxDeliveryRadius), onChange: (e) => setMaxDeliveryRadius(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(GroupSaveButton, { isPending: shippingMutation.isPending })] }) }), _jsx(TabsContent, { value: "auction", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); auctionMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Minimum bid increment (\u20B9)", value: String(minBidIncrement), onChange: (e) => setMinBidIncrement(parseInt(e.target.value) || 0), type: "number", min: 1, helperText: "Global default \u2014 individual auctions may override." }), _jsx(Input, { label: "Auto-extend window (minutes before end)", value: String(autoExtendWindow), onChange: (e) => setAutoExtendWindow(parseInt(e.target.value) || 0), type: "number", min: 0, helperText: "Extend auction end time if a bid arrives within this window." }), _jsx(Input, { label: "Settlement grace period (hours)", value: String(settlementGrace), onChange: (e) => setSettlementGrace(parseInt(e.target.value) || 0), type: "number", min: 1, helperText: "Time winner has to pay before the auction is re-listed." }), _jsx(GroupSaveButton, { isPending: auctionMutation.isPending })] }) }), _jsx(TabsContent, { value: "limits", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); limitsMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Max products per store", value: String(maxProductsPerStore), onChange: (e) => setMaxProductsPerStore(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max images per product", value: String(maxImagesPerProduct), onChange: (e) => setMaxImagesPerProduct(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max video size (MB)", value: String(maxVideoSizeMb), onChange: (e) => setMaxVideoSizeMb(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max custom fields per product", value: String(maxCustomFields), onChange: (e) => setMaxCustomFields(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Max custom sections per product", value: String(maxCustomSections), onChange: (e) => setMaxCustomSections(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Order cancellation window (hours)", value: String(orderCancelWindow), onChange: (e) => setOrderCancelWindow(parseInt(e.target.value) || 0), type: "number", min: 0 })] }), _jsx(GroupSaveButton, { isPending: limitsMutation.isPending })] }) }), _jsx(TabsContent, { value: "whatsapp", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); whatsappMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Platform-level WhatsApp Business Cloud API credentials. Used for automated purchase announcements to admin numbers when orders are placed. Store owners configure their own credentials in Store \u2192 WhatsApp." }), _jsx(Input, { label: "Phone Number ID", value: waPhoneNumberId, onChange: (e) => setWaPhoneNumberId(e.target.value), placeholder: "987654321098765", helperText: "From Meta for Developers \u2192 App \u2192 WhatsApp \u2192 API Setup" }), _jsx(MaskedInput, { label: "Cloud API System User Token", value: waCloudApiToken, onChange: setWaCloudApiToken, placeholder: "EAAxxxxxxxx\u2026", helperText: "Long-lived system user access token with WhatsApp Business permissions" }), _jsx(Input, { label: "Admin Notify Numbers", value: waAdminNotifyNumbers, onChange: (e) => setWaAdminNotifyNumbers(e.target.value), placeholder: "919876543210,918765432109", helperText: "Comma-separated, digits-only, include country code. These receive a WhatsApp message when any order is placed." }), _jsx(GroupSaveButton, { isPending: whatsappMutation.isPending })] }) }), _jsx(TabsContent, { value: "notifications", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); notifChannelsMutation.mutate(); }, className: "space-y-6 pt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "In-app notifications are always on. Enable external channels below to let the platform fan out to email, WhatsApp, or SMS. Users can further restrict which types they receive." }), _jsxs(Stack, { gap: "xs", surface: "muted", rounded: "lg", border: "default", padding: "md", children: [_jsxs(Row, { justify: "between", gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "In-app (notification bell)" }), _jsx(Span, { size: "xs", weight: "semibold", className: "rounded-full bg-success-surface px-2 py-0.5 text-success", children: "Always on" })] }), _jsx(Text, { size: "xs", color: "muted", children: "Displayed in the notification bell and inbox. Cannot be disabled." })] }), _jsxs(Stack, { gap: "md", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "Email notifications", checked: notifEmailEnabled, onChange: setNotifEmailEnabled }), notifEmailEnabled && (_jsxs(Stack, { gap: "md", className: NOTIF_CHANNEL_INDENT, children: [_jsx(Select, { label: "Minimum priority to send email", options: PRIORITY_OPTIONS, value: notifEmailMinPriority, onValueChange: setNotifEmailMinPriority }), _jsx(Text, { size: "xs", weight: "medium", color: "muted", className: "pt-1", children: "Resend API (for transactional email)" }), _jsx(MaskedInput, { label: "Resend API Key", value: resendApiKey, onChange: setResendApiKey, placeholder: "re_live_\u2026", helperText: "Get your key at resend.com \u2014 used for all transactional notifications." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "From email", value: notifFromEmail, onChange: (e) => setNotifFromEmail(e.target.value), placeholder: "noreply@letitrip.in", type: "email" }), _jsx(Input, { label: "From name", value: notifFromName, onChange: (e) => setNotifFromName(e.target.value), placeholder: "LetItRip" })] })] }))] }), _jsxs(Stack, { gap: "md", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "WhatsApp notifications", checked: notifWhatsappEnabled, onChange: setNotifWhatsappEnabled }), notifWhatsappEnabled && (_jsxs(Stack, { gap: "md", className: NOTIF_CHANNEL_INDENT, children: [_jsx(Select, { label: "Minimum priority to send WhatsApp", options: PRIORITY_OPTIONS, value: notifWhatsappMinPriority, onValueChange: setNotifWhatsappMinPriority }), _jsx(Toggle, { label: "Enable WhatsApp OTP (for login and verification)", checked: notifWhatsappOtpEnabled, onChange: setNotifWhatsappOtpEnabled }), _jsx(Text, { size: "xs", color: "muted", children: "WhatsApp credentials are configured in the WhatsApp tab (\u246C). OTP messages use the same phone number." })] }))] }), _jsxs(Stack, { gap: "md", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "SMS notifications", checked: notifSmsEnabled, onChange: setNotifSmsEnabled }), notifSmsEnabled && (_jsxs(Stack, { gap: "md", className: NOTIF_CHANNEL_INDENT, children: [_jsx(Select, { label: "Minimum priority to send SMS", options: PRIORITY_OPTIONS, value: notifSmsMinPriority, onValueChange: setNotifSmsMinPriority }), _jsx(Text, { size: "xs", color: "muted", children: "SMS gateway credentials (e.g. Twilio, MSG91) can be configured in the Integrations tab once an SMS provider is connected." })] }))] }), _jsx(GroupSaveButton, { isPending: notifChannelsMutation.isPending })] }) }), _jsx(TabsContent, { value: "legal", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); legalMutation.mutate(); }, className: "space-y-5 pt-4", children: [[
|
|
377
|
+
].map(([value, label]) => (_jsx(TabsTrigger, { value: value, children: label }, value))) }), _jsx(TabsContent, { value: "about", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); aboutMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Override the About page hero and mission text. Leave blank to use the platform defaults." }), _jsx(Input, { label: "Hero title", value: aboutTitle, onChange: (e) => setAboutTitle(e.target.value), placeholder: "About LetItRip" }), _jsx(Input, { label: "Hero subtitle", value: aboutSubtitle, onChange: (e) => setAboutSubtitle(e.target.value), placeholder: "Connecting buyers, sellers, and bidders in one vibrant marketplace" }), _jsx(Input, { label: "Mission section title", value: aboutMissionTitle, onChange: (e) => setAboutMissionTitle(e.target.value), placeholder: "Our Mission" }), _jsxs(_Fragment, { children: [_jsx(Text, { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Mission text" }), _jsx("textarea", { value: aboutMissionText, onChange: (e) => setAboutMissionText(e.target.value), placeholder: "LetItRip was built to democratise commerce\u2026", rows: 4, className: CLS_TEXTAREA })] }), _jsx(Input, { label: "CTA banner title", value: aboutCtaTitle, onChange: (e) => setAboutCtaTitle(e.target.value), placeholder: "Ready to get started?" }), _jsx(GroupSaveButton, { isPending: aboutMutation.isPending })] }) }), _jsx(TabsContent, { value: "branding", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); brandingMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Site name", value: siteName, onChange: (e) => setSiteName(e.target.value), placeholder: "LetItRip" }), _jsx(Input, { label: "Tagline", value: tagline, onChange: (e) => setTagline(e.target.value), placeholder: "India's Largest Collectibles Marketplace" }), _jsx(ImageUpload, { label: "Logo", currentImage: logoUrl, onUpload: (file) => upload(file, "store"), onChange: setLogoUrl }), _jsx(ImageUpload, { label: "Favicon", currentImage: faviconUrl, onUpload: (file) => upload(file, "store"), onChange: setFaviconUrl }), _jsxs(Stack, { gap: "sm", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "Maintenance mode", checked: maintenanceMode, onChange: setMaintenanceMode }), _jsx(Input, { label: "Maintenance message", value: maintenanceMessage, onChange: (e) => setMaintenanceMessage(e.target.value), placeholder: "We're back soon.", disabled: !maintenanceMode })] }), _jsx(GroupSaveButton, { isPending: brandingMutation.isPending })] }) }), _jsx(TabsContent, { value: "appearance", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); appearanceMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs(Grid, { gap: "md", className: "grid-cols-3", children: [_jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "mb-1", children: "Primary color" }), _jsx("input", { type: "color", value: primaryColor || "#000000", onChange: (e) => setPrimaryColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "mb-1", children: "Secondary color" }), _jsx("input", { type: "color", value: secondaryColor || "#000000", onChange: (e) => setSecondaryColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] }), _jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "mb-1", children: "Accent color" }), _jsx("input", { type: "color", value: accentColor || "#000000", onChange: (e) => setAccentColor(e.target.value), className: "h-10 w-full rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer" })] })] }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Select, { label: "Default theme", options: THEME_OPTIONS, value: defaultTheme, onValueChange: setDefaultTheme }), _jsx(Select, { label: "Font family", options: FONT_OPTIONS, value: fontFamily, onValueChange: setFontFamily })] }), _jsx(GroupSaveButton, { isPending: appearanceMutation.isPending })] }) }), _jsx(TabsContent, { value: "announcement", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); announcementMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Toggle, { label: "Show announcement bar", checked: announcementEnabled, onChange: setAnnouncementEnabled }), _jsx(Input, { label: "Announcement text", value: announcementText, onChange: (e) => setAnnouncementText(e.target.value), placeholder: "\uD83C\uDF89 Free shipping on orders \u20B9999+", disabled: !announcementEnabled }), _jsx(Input, { label: "Link URL (optional)", value: announcementLink, onChange: (e) => setAnnouncementLink(e.target.value), placeholder: "/products", disabled: !announcementEnabled }), _jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", className: "mb-1", children: "Background color" }), _jsx("input", { type: "color", value: announcementBg || "#1d4ed8", onChange: (e) => setAnnouncementBg(e.target.value), className: "h-10 w-32 rounded border border-zinc-200 dark:border-zinc-700 cursor-pointer", disabled: !announcementEnabled })] }), _jsx(GroupSaveButton, { isPending: announcementMutation.isPending })] }) }), _jsx(TabsContent, { value: "seo", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); seoMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Default meta title", value: seoTitle, onChange: (e) => setSeoTitle(e.target.value), placeholder: "LetItRip \u2014 Buy, Sell & Auction Collectibles in India", maxLength: 60, helperText: "Max 60 chars. Use {page} token for dynamic insertion." }), _jsx(Input, { label: "Default meta description", value: seoDescription, onChange: (e) => setSeoDescription(e.target.value), placeholder: "India's largest collectibles marketplace\u2026", maxLength: 160, helperText: "Max 160 chars." }), _jsx(ImageUpload, { label: "Default OG image", currentImage: seoOgImage, onUpload: (file) => upload(file, "store"), onChange: setSeoOgImage }), _jsx(Input, { label: "Canonical base URL", value: canonicalUrl, onChange: (e) => setCanonicalUrl(e.target.value), placeholder: "https://letitrip.in" }), _jsx(Toggle, { label: "Robots noindex (disables search indexing \u2014 use carefully)", checked: seoNoIndex, onChange: setSeoNoIndex }), _jsx(GroupSaveButton, { isPending: seoMutation.isPending })] }) }), _jsx(TabsContent, { value: "contact", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); contactMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Support email", value: supportEmail, onChange: (e) => setSupportEmail(e.target.value), type: "email", placeholder: "support@letitrip.in" }), _jsx(Input, { label: "Support phone", value: supportPhone, onChange: (e) => setSupportPhone(e.target.value), placeholder: "+91 XXXXX XXXXX" })] }), _jsx(Input, { label: "Physical address", value: supportAddress, onChange: (e) => setSupportAddress(e.target.value), placeholder: "Mumbai, Maharashtra, India" }), _jsx(Input, { label: "Support hours", value: supportHours, onChange: (e) => setSupportHours(e.target.value), placeholder: "Mon\u2013Fri, 10 AM \u2013 6 PM IST" }), _jsx(Text, { className: "text-sm font-medium text-zinc-600 dark:text-zinc-400 pt-2", children: "Social links" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Instagram URL", value: instagram, onChange: (e) => setInstagram(e.target.value), placeholder: "https://instagram.com/letitrip" }), _jsx(Input, { label: "Twitter / X URL", value: twitter, onChange: (e) => setTwitter(e.target.value), placeholder: "https://twitter.com/letitrip" }), _jsx(Input, { label: "Facebook URL", value: facebook, onChange: (e) => setFacebook(e.target.value), placeholder: "https://facebook.com/letitrip" }), _jsx(Input, { label: "YouTube URL", value: youtube, onChange: (e) => setYoutube(e.target.value), placeholder: "https://youtube.com/@letitrip" }), _jsx(Input, { label: "WhatsApp number", value: whatsapp, onChange: (e) => setWhatsapp(e.target.value), placeholder: "+91XXXXXXXXXX" }), _jsx(Input, { label: "LinkedIn URL", value: linkedin, onChange: (e) => setLinkedin(e.target.value), placeholder: "https://linkedin.com/company/letitrip" }), _jsx(Input, { label: "Pinterest URL", value: pinterest, onChange: (e) => setPinterest(e.target.value), placeholder: "https://pinterest.com/letitrip" })] }), _jsx(GroupSaveButton, { isPending: contactMutation.isPending })] }) }), _jsx(TabsContent, { value: "watermark", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); watermarkMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Select, { label: "Watermark type", options: [{ label: "Text", value: "text" }, { label: "Image", value: "image" }], value: watermarkType, onValueChange: (v) => setWatermarkType(v) }), watermarkType === "text" ? (_jsx(Input, { label: "Watermark text", value: watermarkText, onChange: (e) => setWatermarkText(e.target.value), placeholder: "letitrip.in" })) : (_jsx(ImageUpload, { label: "Watermark image", currentImage: watermarkImageUrl, onUpload: (file) => upload(file, "store"), onChange: setWatermarkImageUrl })), _jsx(Slider, { label: `Size — ${watermarkSize}% of image width`, value: watermarkSize, onChange: setWatermarkSize, min: 5, max: 100, step: 5 }), _jsx(Slider, { label: `Opacity — ${watermarkOpacity}%`, value: watermarkOpacity, onChange: setWatermarkOpacity, min: 5, max: 100, step: 5 }), _jsxs(Stack, { gap: "xs", surface: "muted", rounded: "lg", border: "default", padding: "md", children: [_jsx(Text, { size: "xs", color: "muted", children: "Preview (text watermark only)" }), _jsx(Row, { surface: "default", justify: "end", align: "end", className: `relative h-32 ${__O.hidden} rounded`, children: _jsx(Span, { weight: "medium", className: "text-zinc-400 select-none p-2",
|
|
378
|
+
// audit-inline-style-ok: computed pixel offset
|
|
379
|
+
style: { fontSize: `${Math.max(10, watermarkSize / 5)}px`, opacity: watermarkOpacity / 100 }, children: watermarkText }) })] }), _jsx(GroupSaveButton, { isPending: watermarkMutation.isPending })] }) }), _jsx(TabsContent, { value: "fees", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); feesMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Platform fee \u2014 our cut (%)", helperText: "% charged on order value. Buyer pays this.", value: String(platformFeePercent), onChange: (e) => setPlatformFeePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "GST on platform fee (%)", helperText: "Applied to our fee only (not full order). Usually 18%.", value: String(gstPercent), onChange: (e) => setGstPercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 100, step: 0.1 }), _jsx(Input, { label: "Razorpay gateway cost (%)", helperText: "Gateway's own fee \u2014 absorbed by platform, not passed through.", value: String(gatewayFeePercent), onChange: (e) => setGatewayFeePercent(parseFloat(e.target.value) || 0), type: "number", min: 0, max: 10, step: 0.01 }), _jsx(Input, { label: "Minimum transaction fee (\u20B9)", helperText: "Per-transaction floor. Total charge will never be below base + this.", value: String(minimumTransactionFee), onChange: (e) => setMinimumTransactionFee(parseFloat(e.target.value) || 0), type: "number", min: 0, step: 0.01 }), _jsx(Input, { label: "Seller payout hold (days)", value: String(payoutHoldDays), onChange: (e) => setPayoutHoldDays(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Minimum payout amount (\u20B9)", value: String(minPayoutAmount), onChange: (e) => setMinPayoutAmount(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Auction listing fee (\u20B9)", value: String(auctionListingFee), onChange: (e) => setAuctionListingFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Pre-order listing fee (\u20B9)", value: String(preOrderListingFee), onChange: (e) => setPreOrderListingFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Featured slot fee (\u20B9)", value: String(featuredSlotFee), onChange: (e) => setFeaturedSlotFee(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Promoted slot fee (\u20B9)", value: String(promotedSlotFee), onChange: (e) => setPromotedSlotFee(parseInt(e.target.value) || 0), type: "number", min: 0 })] }), _jsx(GroupSaveButton, { isPending: feesMutation.isPending })] }) }), _jsx(TabsContent, { value: "integrations", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); integrationsMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Keys are masked in transit and stored encrypted. Click Reveal to view." }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Razorpay" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Razorpay Key ID", value: razorpayKeyId, onChange: setRazorpayKeyId, placeholder: "rzp_live_\u2026" }), _jsx(MaskedInput, { label: "Razorpay Secret", value: razorpaySecret, onChange: setRazorpaySecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Shiprocket" }), _jsx(MaskedInput, { label: "Shiprocket API token", value: shiprocketToken, onChange: setShiprocketToken, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "SMTP / Email" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "SMTP host", value: smtpHost, onChange: (e) => setSmtpHost(e.target.value), placeholder: "smtp.sendgrid.net" }), _jsx(Input, { label: "SMTP port", value: smtpPort, onChange: (e) => setSmtpPort(e.target.value), type: "number", placeholder: "587" }), _jsx(Input, { label: "SMTP user", value: smtpUser, onChange: (e) => setSmtpUser(e.target.value), placeholder: "apikey" }), _jsx(MaskedInput, { label: "SMTP password", value: smtpPassword, onChange: setSmtpPassword, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsx(Input, { label: "From address", value: smtpFrom, onChange: (e) => setSmtpFrom(e.target.value), placeholder: "noreply@letitrip.in", type: "email" })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Analytics & Tracking" }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Google Analytics ID", value: gaMeasurementId, onChange: (e) => setGaMeasurementId(e.target.value), placeholder: "G-XXXXXXXXXX" }), _jsx(Input, { label: "Facebook Pixel ID", value: fbPixelId, onChange: (e) => setFbPixelId(e.target.value), placeholder: "XXXXXXXXXXXXXXXX" }), _jsx(Input, { label: "GTM Container ID", value: gtmContainerId, onChange: (e) => setGtmContainerId(e.target.value), placeholder: "GTM-XXXXXXX" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Meta (Instagram & Facebook Social Feed)" }), _jsx(Text, { size: "xs", color: "muted", children: "Used by the Social Feed section to fetch Instagram and Facebook posts via Meta Graph API v19." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Page Access Token", value: metaPageAccessToken, onChange: setMetaPageAccessToken, placeholder: "EAAxxxxxxx\u2026" }), _jsx(Input, { label: "Page ID (or handle)", value: metaPageId, onChange: (e) => setMetaPageId(e.target.value), placeholder: "letitrip" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "TikTok for Developers (Social Feed)" }), _jsx(Text, { size: "xs", color: "muted", children: "Client credentials + long-lived access token from TikTok for Developers. Used to list your account's public videos." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Client Key", value: tiktokClientKey, onChange: setTiktokClientKey, placeholder: "aw\u2026" }), _jsx(MaskedInput, { label: "Client Secret", value: tiktokClientSecret, onChange: setTiktokClientSecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" }), _jsx(MaskedInput, { label: "Access Token (long-lived)", value: tiktokAccessToken, onChange: setTiktokAccessToken, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "DeviantArt OAuth2 (Social Feed)" }), _jsx(Text, { size: "xs", color: "muted", children: "Client credentials for DeviantArt gallery fetching (client-credentials OAuth2 flow \u2014 no user login required)." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(MaskedInput, { label: "Client ID", value: deviantartClientId, onChange: setDeviantartClientId, placeholder: "1234" }), _jsx(MaskedInput, { label: "Client Secret", value: deviantartClientSecret, onChange: setDeviantartClientSecret, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] })] }), _jsx(GroupSaveButton, { isPending: integrationsMutation.isPending })] }) }), _jsx(TabsContent, { value: "shipping", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); shippingMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Free shipping threshold (\u20B9)", value: String(freeShippingThreshold), onChange: (e) => setFreeShippingThreshold(parseInt(e.target.value) || 0), type: "number", min: 0, helperText: "Orders above this amount get free shipping." }), _jsx(Toggle, { label: "Cash on delivery (COD) enabled", checked: codEnabled, onChange: setCodEnabled }), _jsx(Select, { label: "Default carrier", options: CARRIER_OPTIONS, value: defaultCarrier, onValueChange: setDefaultCarrier }), _jsx(Input, { label: "Max delivery radius (km, 0 = no limit)", value: String(maxDeliveryRadius), onChange: (e) => setMaxDeliveryRadius(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(GroupSaveButton, { isPending: shippingMutation.isPending })] }) }), _jsx(TabsContent, { value: "auction", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); auctionMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Input, { label: "Minimum bid increment (\u20B9)", value: String(minBidIncrement), onChange: (e) => setMinBidIncrement(parseInt(e.target.value) || 0), type: "number", min: 1, helperText: "Global default \u2014 individual auctions may override." }), _jsx(Input, { label: "Auto-extend window (minutes before end)", value: String(autoExtendWindow), onChange: (e) => setAutoExtendWindow(parseInt(e.target.value) || 0), type: "number", min: 0, helperText: "Extend auction end time if a bid arrives within this window." }), _jsx(Input, { label: "Settlement grace period (hours)", value: String(settlementGrace), onChange: (e) => setSettlementGrace(parseInt(e.target.value) || 0), type: "number", min: 1, helperText: "Time winner has to pay before the auction is re-listed." }), _jsx(GroupSaveButton, { isPending: auctionMutation.isPending })] }) }), _jsx(TabsContent, { value: "limits", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); limitsMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "Max products per store", value: String(maxProductsPerStore), onChange: (e) => setMaxProductsPerStore(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max images per product", value: String(maxImagesPerProduct), onChange: (e) => setMaxImagesPerProduct(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max video size (MB)", value: String(maxVideoSizeMb), onChange: (e) => setMaxVideoSizeMb(parseInt(e.target.value) || 0), type: "number", min: 1 }), _jsx(Input, { label: "Max custom fields per product", value: String(maxCustomFields), onChange: (e) => setMaxCustomFields(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Max custom sections per product", value: String(maxCustomSections), onChange: (e) => setMaxCustomSections(parseInt(e.target.value) || 0), type: "number", min: 0 }), _jsx(Input, { label: "Order cancellation window (hours)", value: String(orderCancelWindow), onChange: (e) => setOrderCancelWindow(parseInt(e.target.value) || 0), type: "number", min: 0 })] }), _jsx(GroupSaveButton, { isPending: limitsMutation.isPending })] }) }), _jsx(TabsContent, { value: "whatsapp", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); whatsappMutation.mutate(); }, className: "space-y-4 pt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Platform-level WhatsApp Business Cloud API credentials. Used for automated purchase announcements to admin numbers when orders are placed. Store owners configure their own credentials in Store \u2192 WhatsApp." }), _jsx(Input, { label: "Phone Number ID", value: waPhoneNumberId, onChange: (e) => setWaPhoneNumberId(e.target.value), placeholder: "987654321098765", helperText: "From Meta for Developers \u2192 App \u2192 WhatsApp \u2192 API Setup" }), _jsx(MaskedInput, { label: "Cloud API System User Token", value: waCloudApiToken, onChange: setWaCloudApiToken, placeholder: "EAAxxxxxxxx\u2026", helperText: "Long-lived system user access token with WhatsApp Business permissions" }), _jsx(Input, { label: "Admin Notify Numbers", value: waAdminNotifyNumbers, onChange: (e) => setWaAdminNotifyNumbers(e.target.value), placeholder: "919876543210,918765432109", helperText: "Comma-separated, digits-only, include country code. These receive a WhatsApp message when any order is placed." }), _jsx(GroupSaveButton, { isPending: whatsappMutation.isPending })] }) }), _jsx(TabsContent, { value: "notifications", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); notifChannelsMutation.mutate(); }, className: "space-y-6 pt-4", children: [_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "In-app notifications are always on. Enable external channels below to let the platform fan out to email, WhatsApp, or SMS. Users can further restrict which types they receive." }), _jsxs(Stack, { gap: "xs", surface: "muted", rounded: "lg", border: "default", padding: "md", children: [_jsxs(Row, { justify: "between", gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "In-app (notification bell)" }), _jsx(Span, { size: "xs", weight: "semibold", className: "rounded-full bg-success-surface px-2 py-0.5 text-success", children: "Always on" })] }), _jsx(Text, { size: "xs", color: "muted", children: "Displayed in the notification bell and inbox. Cannot be disabled." })] }), _jsxs(Stack, { gap: "md", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "Email notifications", checked: notifEmailEnabled, onChange: setNotifEmailEnabled }), notifEmailEnabled && (_jsxs(Stack, { gap: "md", className: NOTIF_CHANNEL_INDENT, children: [_jsx(Select, { label: "Minimum priority to send email", options: PRIORITY_OPTIONS, value: notifEmailMinPriority, onValueChange: setNotifEmailMinPriority }), _jsx(Text, { size: "xs", weight: "medium", color: "muted", className: "pt-1", children: "Resend API (for transactional email)" }), _jsx(MaskedInput, { label: "Resend API Key", value: resendApiKey, onChange: setResendApiKey, placeholder: "re_live_\u2026", helperText: "Get your key at resend.com \u2014 used for all transactional notifications." }), _jsxs(Grid, { cols: 2, gap: "md", children: [_jsx(Input, { label: "From email", value: notifFromEmail, onChange: (e) => setNotifFromEmail(e.target.value), placeholder: "noreply@letitrip.in", type: "email" }), _jsx(Input, { label: "From name", value: notifFromName, onChange: (e) => setNotifFromName(e.target.value), placeholder: "LetItRip" })] })] }))] }), _jsxs(Stack, { gap: "md", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "WhatsApp notifications", checked: notifWhatsappEnabled, onChange: setNotifWhatsappEnabled }), notifWhatsappEnabled && (_jsxs(Stack, { gap: "md", className: NOTIF_CHANNEL_INDENT, children: [_jsx(Select, { label: "Minimum priority to send WhatsApp", options: PRIORITY_OPTIONS, value: notifWhatsappMinPriority, onValueChange: setNotifWhatsappMinPriority }), _jsx(Toggle, { label: "Enable WhatsApp OTP (for login and verification)", checked: notifWhatsappOtpEnabled, onChange: setNotifWhatsappOtpEnabled }), _jsx(Text, { size: "xs", color: "muted", children: "WhatsApp credentials are configured in the WhatsApp tab (\u246C). OTP messages use the same phone number." })] }))] }), _jsxs(Stack, { gap: "md", rounded: "lg", border: "default", padding: "md", children: [_jsx(Toggle, { label: "SMS notifications", checked: notifSmsEnabled, onChange: setNotifSmsEnabled }), notifSmsEnabled && (_jsxs(Stack, { gap: "md", className: NOTIF_CHANNEL_INDENT, children: [_jsx(Select, { label: "Minimum priority to send SMS", options: PRIORITY_OPTIONS, value: notifSmsMinPriority, onValueChange: setNotifSmsMinPriority }), _jsx(Text, { size: "xs", color: "muted", children: "SMS gateway credentials (e.g. Twilio, MSG91) can be configured in the Integrations tab once an SMS provider is connected." })] }))] }), _jsx(GroupSaveButton, { isPending: notifChannelsMutation.isPending })] }) }), _jsx(TabsContent, { value: "legal", children: _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); legalMutation.mutate(); }, className: "space-y-5 pt-4", children: [[
|
|
378
380
|
["Terms of Service", termsHtml, setTermsHtml],
|
|
379
381
|
["Privacy Policy", privacyHtml, setPrivacyHtml],
|
|
380
382
|
["Refund Policy", refundHtml, setRefundHtml],
|
|
@@ -250,7 +250,6 @@ export function AdminSupportTicketDetailView({ open, onClose, ticketId, subject,
|
|
|
250
250
|
["bidId", "Bid ID"],
|
|
251
251
|
].map(([key, label]) => (_jsx(Input, { label: label, type: "text", value: parties[key] ?? "", onChange: (e) => setParties((p) => ({ ...p, [key]: e.target.value })), placeholder: label }, key)))] }), isOrderModificationRequest && (_jsx(OrderItemsPanel, { linkedOrderId: linkedOrderId, orderItemsOpen: orderItemsOpen, setOrderItemsOpen: setOrderItemsOpen, orderItems: orderItems, setOrderItems: setOrderItems, loadOrderItems: loadOrderItems, applyOrderItems: applyOrderItems })), isStoreChangeRequest && (_jsx(StoreChangePanel, { linkedStoreId: linkedStoreId, storeStatus: storeStatus, setStoreStatus: setStoreStatus, storeIsVerified: storeIsVerified, setStoreIsVerified: setStoreIsVerified, storeIsFeatured: storeIsFeatured, setStoreIsFeatured: setStoreIsFeatured, applyStoreChange: applyStoreChange })), isUnbanRequest && (_jsx(UnbanRequestPanel, { linkedUserId: linkedUserId, liftHardBan: liftHardBan, liftSoftBanTickets: liftSoftBanTickets })), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "button", isLoading: updateMutation.isPending, disabled: !ticketId || updateMutation.isPending, onClick: () => updateMutation.mutate(), children: "Save changes" })] })] }) }));
|
|
252
252
|
}
|
|
253
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
254
253
|
function OrderItemsPanel(props) {
|
|
255
254
|
const { linkedOrderId, orderItemsOpen, setOrderItemsOpen, orderItems, setOrderItems, loadOrderItems, applyOrderItems, } = props;
|
|
256
255
|
const updateQty = (idx, raw) => {
|
|
@@ -259,7 +258,6 @@ function OrderItemsPanel(props) {
|
|
|
259
258
|
};
|
|
260
259
|
return (_jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-info/40 bg-info-surface/40", children: [_jsx(Text, { className: "text-xs font-semibold text-info uppercase tracking-wide", children: "Modify order items" }), linkedOrderId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Editing order: ", _jsx("code", { className: "font-mono", children: linkedOrderId })] }), !orderItemsOpen ? (_jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: loadOrderItems.isPending, disabled: loadOrderItems.isPending, onClick: () => loadOrderItems.mutate(), children: "Load current items" })) : (_jsxs(_Fragment, { children: [orderItems.length === 0 ? (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Order has no items." })) : (_jsx(Div, { className: "flex flex-col gap-2", children: orderItems.map((it, idx) => (_jsxs(Div, { className: "flex items-center gap-2 rounded border border-zinc-200 dark:border-zinc-700 px-2 py-1", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { className: "text-xs font-medium truncate", children: it.productTitle }), _jsxs(Text, { size: "xs", color: "muted", children: [it.productId, " \u00B7 \u20B9", (it.unitPrice / 100).toFixed(2), "/ea"] })] }), _jsx("input", { type: "number", min: "0", value: it.quantity, onChange: (e) => updateQty(idx, e.target.value), className: "w-16 rounded border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-2 py-1 text-xs" })] }, `${it.productId}-${idx}`))) })), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setOrderItemsOpen(false), children: "Cancel" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: applyOrderItems.isPending, disabled: applyOrderItems.isPending, onClick: () => applyOrderItems.mutate(), children: "Apply changes" })] })] }))] })) : (_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "Order ID" }), " ", "field in the Linked parties panel above to enable this action."] }))] }));
|
|
261
260
|
}
|
|
262
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
263
261
|
function StoreChangePanel(props) {
|
|
264
262
|
const { linkedStoreId, storeStatus, setStoreStatus, storeIsVerified, setStoreIsVerified, storeIsFeatured, setStoreIsFeatured, applyStoreChange, } = props;
|
|
265
263
|
return (_jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-warning/40 bg-warning-surface/40", children: [_jsx(Text, { className: "text-xs font-semibold text-warning uppercase tracking-wide", children: "Apply store change" }), linkedStoreId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Editing store: ", _jsx("code", { className: "font-mono", children: linkedStoreId })] }), _jsx(Select, { label: "Store status", options: [
|
|
@@ -269,7 +267,6 @@ function StoreChangePanel(props) {
|
|
|
269
267
|
{ label: "Rejected", value: "rejected" },
|
|
270
268
|
], value: storeStatus, onValueChange: setStoreStatus }), _jsx(Toggle, { label: "Verified badge", checked: storeIsVerified, onChange: setStoreIsVerified }), _jsx(Toggle, { label: "Featured store", checked: storeIsFeatured, onChange: setStoreIsFeatured }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: applyStoreChange.isPending, disabled: applyStoreChange.isPending, onClick: () => applyStoreChange.mutate(), children: "Apply store change" })] })) : (_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "Store slug" }), " ", "field in the Linked parties panel above to enable this action."] }))] }));
|
|
271
269
|
}
|
|
272
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
273
270
|
function UnbanRequestPanel(props) {
|
|
274
271
|
const { linkedUserId, liftHardBan, liftSoftBanTickets } = props;
|
|
275
272
|
return (_jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-error/40 bg-error-surface/40", children: [_jsx(Text, { className: "text-xs font-semibold text-error uppercase tracking-wide", children: "Lift account ban" }), linkedUserId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Appellant: ", _jsx("code", { className: "font-mono", children: linkedUserId })] }), _jsx(Text, { size: "xs", color: "muted", children: "Resolves the ticket and appends an audit note on success." }), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: liftHardBan.isPending, disabled: liftHardBan.isPending, onClick: () => liftHardBan.mutate(), children: "Lift hard ban" }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: liftSoftBanTickets.isPending, disabled: liftSoftBanTickets.isPending, onClick: () => liftSoftBanTickets.mutate(), children: "Lift ticket soft ban" })] })] })) : (_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "User slug" }), " ", "field in the Linked parties panel above to enable these actions."] }))] }));
|
|
@@ -15,7 +15,11 @@ const BRAND_FROM = "var(--appkit-color-primary-700, #1343de)";
|
|
|
15
15
|
const BRAND_MID = "var(--appkit-color-cobalt, #3570fc)";
|
|
16
16
|
const BRAND_TO = "var(--appkit-color-secondary-400, #84e122)";
|
|
17
17
|
function StatCard({ label, value, sub, icon: Icon, gradient, iconColor }) {
|
|
18
|
-
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${__O.hidden} shadow-sm hover:shadow-md transition-shadow`, children: [_jsx(Div, { className: "absolute top-0 left-0 right-0 h-[3px]",
|
|
18
|
+
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${__O.hidden} shadow-sm hover:shadow-md transition-shadow`, children: [_jsx(Div, { className: "absolute top-0 left-0 right-0 h-[3px]",
|
|
19
|
+
// audit-inline-style-ok: runtime theme gradient
|
|
20
|
+
style: { background: gradient }, "aria-hidden": "true" }), _jsxs(Div, { className: "px-5 pb-5 pt-6 flex items-start justify-between gap-3", children: [_jsxs(Div, { className: `min-w-0 flex-1 ${__O.hidden}`, children: [_jsx(Text, { className: "text-[11px] font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)] truncate", children: label }), _jsx(Text, { className: "mt-2 text-xl font-bold text-[var(--appkit-color-text)] tabular-nums leading-tight break-words", children: value }), sub && (_jsx(Text, { className: "mt-1.5 text-xs text-[var(--appkit-color-text-muted)]", children: sub }))] }), _jsx(Div, { className: "flex-shrink-0 w-10 h-10 rounded-lg flex items-center justify-center",
|
|
21
|
+
// audit-inline-style-ok: runtime theme gradient
|
|
22
|
+
style: { background: gradient, opacity: 0.92 }, children: _jsx(Icon, { className: "w-5 h-5 text-white drop-shadow-sm" }) })] })] }));
|
|
19
23
|
}
|
|
20
24
|
function SkeletonCard() {
|
|
21
25
|
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${__O.hidden} ${__P.p5} animate-pulse`, children: [_jsx(Div, { className: "absolute top-0 left-0 right-0 h-[3px] bg-[var(--appkit-color-border)]" }), _jsxs(Div, { className: "flex items-start justify-between gap-3 pt-1", children: [_jsxs(Div, { className: "flex-1 space-y-2", children: [_jsx(Div, { className: "h-2.5 w-20 rounded bg-[var(--appkit-color-border)]" }), _jsx(Div, { className: "h-7 w-24 rounded bg-[var(--appkit-color-border)]" })] }), _jsx(Div, { className: "w-10 h-10 rounded-lg bg-[var(--appkit-color-border)]" })] })] }));
|
|
@@ -19,7 +19,9 @@ const CartesianGrid = dynamic(() => import("recharts").then((m) => m.CartesianGr
|
|
|
19
19
|
const Tooltip = dynamic(() => import("recharts").then((m) => m.Tooltip), { ssr: false });
|
|
20
20
|
// Shared chart card wrapper
|
|
21
21
|
function ChartCard({ title, children, className = "", }) {
|
|
22
|
-
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm ${className}`, children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]",
|
|
22
|
+
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm ${className}`, children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]",
|
|
23
|
+
// audit-inline-style-ok: dynamic CSS
|
|
24
|
+
style: {
|
|
23
25
|
background: "linear-gradient(to right, var(--appkit-color-primary-700,#1343de) 0%, var(--appkit-color-cobalt,#3570fc) 55%, var(--appkit-color-secondary-400,#84e122) 100%)",
|
|
24
26
|
}, "aria-hidden": "true" }), _jsxs(Div, { className: `${__P.p4} sm:${__P.p6} pt-5 sm:pt-7`, children: [title && (_jsx(Heading, { level: 3, className: "text-sm font-semibold text-[var(--appkit-color-text)] mb-4", children: title })), children] })] }));
|
|
25
27
|
}
|
|
@@ -12,5 +12,9 @@ const GRADIENTS = {
|
|
|
12
12
|
};
|
|
13
13
|
export function AdminStatCard({ label, value, sub, icon, gradient = "brand", className = "", }) {
|
|
14
14
|
const resolvedGradient = GRADIENTS[gradient] ?? gradient;
|
|
15
|
-
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm hover:shadow-md transition-shadow ${className}`, children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]",
|
|
15
|
+
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm hover:shadow-md transition-shadow ${className}`, children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]",
|
|
16
|
+
// audit-inline-style-ok: dynamic CSS
|
|
17
|
+
style: { background: resolvedGradient }, "aria-hidden": "true" }), _jsxs(Div, { className: "px-5 pb-5 pt-6 flex items-start justify-between gap-3", children: [_jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { className: "text-[11px] font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)]", children: label }), _jsx(Heading, { level: 3, className: "mt-2 text-2xl font-bold tabular-nums leading-none text-[var(--appkit-color-text)]", children: value }), sub && (_jsx(Text, { className: "mt-1.5 text-xs text-[var(--appkit-color-text-muted)]", children: sub }))] }), icon && (_jsx(Div, { className: "flex-shrink-0 w-10 h-10 rounded-lg flex items-center justify-center text-xl",
|
|
18
|
+
// audit-inline-style-ok: dynamic CSS
|
|
19
|
+
style: { background: resolvedGradient }, children: icon }))] })] }));
|
|
16
20
|
}
|
|
@@ -14,6 +14,7 @@ export function useChat(chatId, options) {
|
|
|
14
14
|
const tokenExpiresAtRef = useRef(0);
|
|
15
15
|
const unsubscribeRef = useRef(null);
|
|
16
16
|
const connectAndSubscribe = useCallback(async () => {
|
|
17
|
+
// toast-intentionally-silent — error stored in setError() for UI inline render
|
|
17
18
|
if (!chatId)
|
|
18
19
|
return;
|
|
19
20
|
setIsLoading(true);
|
|
@@ -54,7 +54,11 @@ function AuctionRow({ auction, portal, }) {
|
|
|
54
54
|
const sorted = useMemo(() => [...auction.bids].sort((a, b) => b.bidAmount - a.bidAmount), [auction.bids]);
|
|
55
55
|
const highest = sorted[0]?.bidAmount ?? 0;
|
|
56
56
|
const isWinning = auction.bids.some((b) => b.isWinning);
|
|
57
|
-
return (_jsxs(Div, { className: `border border-[var(--appkit-color-border)] rounded-xl ${__O.hidden} bg-[var(--appkit-color-surface)] shadow-sm`, children: [_jsxs("button", { type: "button", onClick: () => setExpanded((v) => !v), className: "w-full flex items-center gap-3 px-4 py-3 hover:bg-[var(--appkit-color-border-subtle)] transition-colors text-left", "aria-expanded": expanded, children: [_jsx(ChevronRight, { className: `shrink-0 text-[var(--appkit-color-text-muted)] transition-transform ${expanded ? "rotate-90" : ""}`, size: 16 }), _jsx(Div, { className: "flex-1 min-w-0", children: _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text)] line-clamp-1", children: auction.productTitle }) }), _jsxs(Row, { gap: "sm", className: "shrink-0", children: [isWinning && (_jsx(Badge, { variant: "active", children: "Winning" })), _jsxs(Text, { variant: "secondary", className: "text-xs", children: [auction.bids.length, " bid", auction.bids.length !== 1 ? "s" : ""] }), _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text)]", children: paise(highest) })] })] }), expanded && (_jsxs(Div, { className: "border-t border-[var(--appkit-color-border)]", children: [_jsxs("div", { className: "grid text-xs font-medium text-[var(--appkit-color-text-muted)] uppercase tracking-wide px-4 py-2 border-b border-[var(--appkit-color-border-subtle)]",
|
|
57
|
+
return (_jsxs(Div, { className: `border border-[var(--appkit-color-border)] rounded-xl ${__O.hidden} bg-[var(--appkit-color-surface)] shadow-sm`, children: [_jsxs("button", { type: "button", onClick: () => setExpanded((v) => !v), className: "w-full flex items-center gap-3 px-4 py-3 hover:bg-[var(--appkit-color-border-subtle)] transition-colors text-left", "aria-expanded": expanded, children: [_jsx(ChevronRight, { className: `shrink-0 text-[var(--appkit-color-text-muted)] transition-transform ${expanded ? "rotate-90" : ""}`, size: 16 }), _jsx(Div, { className: "flex-1 min-w-0", children: _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text)] line-clamp-1", children: auction.productTitle }) }), _jsxs(Row, { gap: "sm", className: "shrink-0", children: [isWinning && (_jsx(Badge, { variant: "active", children: "Winning" })), _jsxs(Text, { variant: "secondary", className: "text-xs", children: [auction.bids.length, " bid", auction.bids.length !== 1 ? "s" : ""] }), _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text)]", children: paise(highest) })] })] }), expanded && (_jsxs(Div, { className: "border-t border-[var(--appkit-color-border)]", children: [_jsxs("div", { className: "grid text-xs font-medium text-[var(--appkit-color-text-muted)] uppercase tracking-wide px-4 py-2 border-b border-[var(--appkit-color-border-subtle)]",
|
|
58
|
+
// audit-inline-style-ok: runtime grid template
|
|
59
|
+
style: { gridTemplateColumns: portal === "buyer" ? "1fr auto auto" : "1fr 1fr auto auto" }, children: [_jsx("span", { children: portal === "buyer" ? "Amount" : "Bidder" }), portal !== "buyer" && _jsx("span", { children: "Amount" }), _jsx("span", { children: "Status" }), _jsx("span", { className: "text-right", children: "Time" })] }), sorted.map((bid) => (_jsxs("div", { className: "grid items-center px-4 py-2.5 border-b border-[var(--appkit-color-border-subtle)] last:border-0 hover:bg-[var(--appkit-color-border-subtle)] transition-colors",
|
|
60
|
+
// audit-inline-style-ok: runtime grid template
|
|
61
|
+
style: { gridTemplateColumns: portal === "buyer" ? "1fr auto auto" : "1fr 1fr auto auto" }, children: [portal !== "buyer" && (_jsx(Text, { className: "text-sm text-[var(--appkit-color-text)] truncate pr-3", children: bid.userName || bid.userId })), _jsx(Text, { className: "text-sm font-medium text-[var(--appkit-color-text)]", children: paise(bid.bidAmount) }), _jsx(Badge, { variant: STATUS_VARIANT[bid.status] ?? "pending", className: "capitalize", children: bid.status }), _jsx(Text, { variant: "secondary", className: "text-xs text-right", children: relDate(bid.bidDate) })] }, bid.id)))] }))] }));
|
|
58
62
|
}
|
|
59
63
|
export function AuctionBidsTable({ bids, portal = "buyer", emptyLabel = "No bids found.", }) {
|
|
60
64
|
const auctions = useMemo(() => groupByAuction(bids), [bids]);
|
|
@@ -119,6 +119,7 @@ export function useGoogleLogin(options) {
|
|
|
119
119
|
return () => window.removeEventListener("message", handleMessage);
|
|
120
120
|
}, []); // mount once — uses refs, no stale closure risk
|
|
121
121
|
const mutate = useCallback(async () => {
|
|
122
|
+
// toast-intentionally-silent — errors surfaced via onErrorRef.current() callback
|
|
122
123
|
calledRef.current = false; // reset for each new auth flow
|
|
123
124
|
const popup = window.open(`${window.location.origin}/auth.html`, "oauth_google", "width=500,height=660,left=400,top=100");
|
|
124
125
|
if (!popup) {
|
|
@@ -31,5 +31,7 @@ export function BlogFeaturedCard({ post, href, labels = {}, className = "", sele
|
|
|
31
31
|
day: "numeric",
|
|
32
32
|
})
|
|
33
33
|
: "";
|
|
34
|
-
return (_jsxs(Article, { className: `group relative flex h-full flex-col overflow-hidden rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm hover:shadow-md transition-shadow duration-200 ${isSelected ? "ring-2 ring-primary outline outline-2 outline-primary" : ""} ${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(post.id, !isSelected); }, label: isSelected ? "Deselect post" : "Select post", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsxs(TextLink, { href: href, className: "flex h-full flex-col", children: [_jsx(Div, { className: `aspect-video ${__O.hidden} flex-shrink-0`, children: coverImageUrl ? (_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 flex-col overflow-hidden rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm hover:shadow-md transition-shadow duration-200 ${isSelected ? "ring-2 ring-primary outline outline-2 outline-primary" : ""} ${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(post.id, !isSelected); }, label: isSelected ? "Deselect post" : "Select post", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsxs(TextLink, { href: href, className: "flex h-full flex-col", children: [_jsx(Div, { className: `aspect-video ${__O.hidden} flex-shrink-0`, children: coverImageUrl ? (_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(${coverImageUrl})` } })) : (_jsx(Div, { role: "img", "aria-label": safeTitle, className: "h-full w-full bg-gradient-to-br from-zinc-200 via-zinc-100 to-zinc-300 dark:from-slate-800 dark:via-slate-700 dark:to-slate-900" })) }), _jsxs(Div, { className: `flex flex-1 flex-col ${__P.p4}`, children: [_jsxs(Row, { className: "gap-1.5 mb-2 flex-wrap", children: [_jsx(Span, { size: "xs", weight: "medium", className: `inline-block px-2 py-0.5 rounded-full capitalize ${CATEGORY_BADGE[post.category] ?? "bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300"}`, children: post.category }), post.isFeatured && (_jsx(Span, { size: "xs", weight: "medium", className: CLS_FEATURED_BADGE, children: labels.featuredBadge ?? "Featured" }))] }), _jsx(Heading, { level: 3, className: `font-semibold text-neutral-900 dark:text-zinc-100 text-base leading-snug mb-1 ${THEME_CONSTANTS.utilities.textClamp2}`, children: safeTitle }), post.excerpt && (_jsx(Text, { className: `text-neutral-500 dark:text-zinc-400 text-sm ${THEME_CONSTANTS.utilities.textClamp2} mb-3`, children: post.excerpt })), _jsxs(Row, { className: "mt-auto gap-3 text-xs text-zinc-400 dark:text-zinc-400 flex-wrap", children: [post.authorName && (_jsx(Span, { children: safeDisplayName(post.authorName, "Author") })), post.readTimeMinutes != null && (_jsxs(Span, { children: [post.readTimeMinutes, " ", labels.readTime ?? "min read"] })), date && _jsx(Span, { children: date })] })] })] })] }));
|
|
35
37
|
}
|
|
@@ -26,7 +26,11 @@ export function BlogCard({ post, href, onClick, className = "" }) {
|
|
|
26
26
|
const isInteractive = !!(href || onClick);
|
|
27
27
|
const card = (_jsxs(Article, { role: onClick && !href ? "button" : undefined, tabIndex: onClick && !href ? 0 : undefined, onKeyDown: onClick && !href
|
|
28
28
|
? (e) => (e.key === "Enter" || e.key === " ") && onClick(post)
|
|
29
|
-
: undefined, onClick: onClick && !href ? () => onClick(post) : undefined, className: `group flex flex-col h-full overflow-hidden rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm transition hover:shadow-md ${isInteractive ? "cursor-pointer" : ""} ${className}`, children: [_jsx(Div, { className: `aspect-video w-full ${__O.hidden} bg-neutral-100 dark:bg-slate-800 flex-shrink-0`, children: coverImageUrl ? (_jsx(Div, { role: "img", "aria-label": post.title, className: "h-full w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105",
|
|
29
|
+
: undefined, onClick: onClick && !href ? () => onClick(post) : undefined, className: `group flex flex-col h-full overflow-hidden rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm transition hover:shadow-md ${isInteractive ? "cursor-pointer" : ""} ${className}`, children: [_jsx(Div, { className: `aspect-video w-full ${__O.hidden} bg-neutral-100 dark:bg-slate-800 flex-shrink-0`, children: coverImageUrl ? (_jsx(Div, { role: "img", "aria-label": post.title, className: "h-full w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105",
|
|
30
|
+
// audit-inline-style-ok: dynamic image URL
|
|
31
|
+
style: { backgroundImage: `url(${coverImageUrl})` } })) : (_jsx(Div, { className: "h-full w-full bg-gradient-to-br from-zinc-200 via-zinc-100 to-zinc-300 dark:from-slate-800 dark:via-slate-700 dark:to-slate-900 flex items-center justify-center", children: _jsx(Span, { className: "text-4xl opacity-30", "aria-hidden": "true", children: "\u270D\uFE0F" }) })) }), _jsxs(Div, { className: `flex flex-1 flex-col ${__P.p5}`, children: [_jsxs(Row, { className: "mb-2 gap-2 flex-wrap", children: [_jsx(Span, { size: "xs", weight: "medium", className: "rounded-full bg-primary/10 px-2 py-0.5 capitalize text-primary", children: post.category }), post.isFeatured && (_jsx(Span, { size: "xs", weight: "medium", className: CLS_FEATURED_BADGE, children: "Featured" })), post.readTimeMinutes && (_jsxs(Span, { size: "xs", className: "text-zinc-400 dark:text-zinc-400", children: [post.readTimeMinutes, " min read"] }))] }), _jsx(Heading, { level: 3, className: `${THEME_CONSTANTS.utilities.textClamp2} text-base font-semibold text-neutral-900 dark:text-zinc-100 group-hover:text-primary`, children: post.title }), post.excerpt && (_jsx(Text, { className: `mt-2 ${THEME_CONSTANTS.utilities.textClamp3} flex-1 text-sm text-neutral-500 dark:text-zinc-400`, children: post.excerpt })), _jsxs(Row, { className: "mt-auto pt-3 gap-3", children: [post.authorAvatar ? (_jsx(Div, { role: "img", "aria-label": post.authorName ?? "author", className: "h-7 w-7 flex-shrink-0 rounded-full bg-center bg-cover",
|
|
32
|
+
// audit-inline-style-ok: dynamic image URL
|
|
33
|
+
style: { backgroundImage: `url(${post.authorAvatar})` } })) : post.authorName ? (_jsx(Div, { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-primary/10 text-xs font-bold text-primary", children: post.authorName.charAt(0).toUpperCase() })) : null, _jsxs(Text, { className: "text-xs text-neutral-500 dark:text-zinc-400 min-w-0", children: [post.authorName && (_jsx(Span, { weight: "medium", className: "text-neutral-700 dark:text-zinc-300", children: safeDisplayName(post.authorName, "Author") })), date && _jsxs(Span, { className: "ml-1 text-neutral-400", children: ["\u00B7 ", date] })] })] })] })] }));
|
|
30
34
|
if (href) {
|
|
31
35
|
return (_jsx(Link, { href: href, className: "block h-full", children: card }));
|
|
32
36
|
}
|
|
@@ -53,5 +53,7 @@ export function BlogPostView({ slug, initialData, labels = {}, renderImage, rend
|
|
|
53
53
|
})
|
|
54
54
|
: "";
|
|
55
55
|
const coverImageUrl = getMediaUrl(post.coverImage);
|
|
56
|
-
return (_jsxs(Div, { className: `min-h-screen ${className}`, children: [coverImageUrl && (_jsxs(Div, { className: `relative h-72 md:h-96 ${__O.hidden}`, children: [renderImage ? (renderImage(post)) : (_jsx(Div, { role: "img", "aria-label": post.title, className: "h-full w-full bg-cover bg-center",
|
|
56
|
+
return (_jsxs(Div, { className: `min-h-screen ${className}`, children: [coverImageUrl && (_jsxs(Div, { className: `relative h-72 md:h-96 ${__O.hidden}`, children: [renderImage ? (renderImage(post)) : (_jsx(Div, { role: "img", "aria-label": post.title, className: "h-full w-full bg-cover bg-center",
|
|
57
|
+
// audit-inline-style-ok: dynamic image URL
|
|
58
|
+
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(Link, { href: `${String(ROUTES.PUBLIC.BLOG)}?tags=${encodeURIComponent(tag)}`, className: "inline-block px-3 py-1 rounded-full bg-neutral-100 dark:bg-zinc-800 hover:bg-primary/10 text-zinc-600 dark:text-zinc-400 hover:text-primary text-xs font-medium transition-colors", children: ["#", tag] }, tag))) })), renderAuthorBio && _jsx(Div, { className: "mb-6", children: renderAuthorBio(post) }), _jsx(Div, { surface: "card", padding: "xl", className: "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-zinc-200 dark:border-zinc-700", children: renderBackButton() })] })] }));
|
|
57
59
|
}
|
|
@@ -12,7 +12,9 @@ const __O = {
|
|
|
12
12
|
};
|
|
13
13
|
const CLS_REMOVE_BTN = "self-start text-zinc-400 dark:text-zinc-400 transition hover:text-red-500";
|
|
14
14
|
export function CartItemRow({ item, onQtyChange, onRemove, href, isOutOfStock = false }) {
|
|
15
|
-
return (_jsxs(Div, { surface: "card", padding: "sm", className: `flex gap-4 transition-opacity ${isOutOfStock ? "opacity-60" : ""}`, children: [_jsx(Div, { className: `h-20 w-20 flex-shrink-0 ${__O.hidden} rounded-lg bg-neutral-100 dark:bg-slate-800`, children: item.meta.image && (_jsx(Div, { role: "img", "aria-label": item.meta.title, className: "h-full w-full bg-center bg-cover",
|
|
15
|
+
return (_jsxs(Div, { surface: "card", padding: "sm", className: `flex gap-4 transition-opacity ${isOutOfStock ? "opacity-60" : ""}`, children: [_jsx(Div, { className: `h-20 w-20 flex-shrink-0 ${__O.hidden} rounded-lg bg-neutral-100 dark:bg-slate-800`, children: item.meta.image && (_jsx(Div, { role: "img", "aria-label": item.meta.title, className: "h-full w-full bg-center bg-cover",
|
|
16
|
+
// audit-inline-style-ok: dynamic image URL
|
|
17
|
+
style: { backgroundImage: `url(${item.meta.image})` } })) }), _jsxs(Div, { className: "flex flex-1 flex-col justify-between", children: [_jsxs(Div, { className: "flex items-start gap-1.5", children: [href ? (_jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: `font-medium text-neutral-900 dark:text-zinc-100 hover:underline underline-offset-2 ${THEME_CONSTANTS.utilities.textClamp2}`, children: item.meta.title })) : (_jsx(Text, { className: `font-medium text-neutral-900 dark:text-zinc-100 ${THEME_CONSTANTS.utilities.textClamp2}`, children: item.meta.title })), isOutOfStock && (_jsx(Span, { weight: "semibold", className: "flex-shrink-0 rounded bg-error-surface px-1.5 py-0.5 text-[10px] uppercase tracking-wide text-error", children: "Out of Stock" }))] }), item.meta.attributes &&
|
|
16
18
|
Object.keys(item.meta.attributes).length > 0 && (_jsx(Text, { className: "text-xs text-neutral-500 dark:text-zinc-400", children: Object.entries(item.meta.attributes)
|
|
17
19
|
.map(([k, v]) => `${k}: ${v}`)
|
|
18
20
|
.join(", ") })), _jsxs(Row, { justify: "between", children: [_jsx(Text, { className: "font-semibold text-neutral-900 dark:text-zinc-100", children: formatCurrency(item.meta.price * item.quantity, item.meta.currency) }), onQtyChange && !isOutOfStock && (_jsxs(Row, { className: "gap-2", children: [_jsx(Button, { onClick: () => onQtyChange(item.id, item.quantity - 1), disabled: item.quantity <= 1, variant: "outline", size: "sm", className: "flex h-7 w-7 items-center justify-center rounded-full border border-neutral-200 dark:border-slate-600 text-sm disabled:opacity-40", children: "\u2212" }), _jsx(Span, { size: "sm", className: "min-w-[1.5rem] text-center", children: item.quantity }), _jsx(Button, { onClick: () => onQtyChange(item.id, item.quantity + 1), variant: "outline", size: "sm", className: "flex h-7 w-7 items-center justify-center rounded-full border border-neutral-200 dark:border-slate-600 text-sm", children: "+" })] })), isOutOfStock && (_jsxs(Span, { size: "xs", className: "text-zinc-400 dark:text-zinc-400", children: ["Qty: ", item.quantity] }))] })] }), onRemove && (_jsx(Button, { onClick: () => onRemove(item.id), variant: "ghost", size: "sm", "aria-label": ACTIONS.CART["remove-item"].ariaLabel, className: CLS_REMOVE_BTN, children: "\u2715" }))] }));
|
|
@@ -86,5 +86,9 @@ export async function BrandDetailPageView({ slug, initialBrand }) {
|
|
|
86
86
|
counts.preOrders +
|
|
87
87
|
counts.prizeDraws +
|
|
88
88
|
counts.bundles;
|
|
89
|
-
return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative ${__O.hidden} ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover",
|
|
89
|
+
return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative ${__O.hidden} ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover",
|
|
90
|
+
// audit-inline-style-ok: dynamic image URL
|
|
91
|
+
style: { backgroundImage: `url(${coverImage})` } }), _jsx(Div, { className: "absolute inset-0 bg-black/55" })] })), !hasCover && (_jsx("div", { className: "absolute inset-0 opacity-10",
|
|
92
|
+
// audit-inline-style-ok: runtime theme color
|
|
93
|
+
style: { backgroundColor: brandColor } })), _jsxs(Div, { className: `relative z-10 max-w-7xl mx-auto px-4 ${hasCover ? "py-12" : "py-8"}`, children: [_jsxs(Nav, { className: "flex items-center gap-1.5 text-sm mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 dark:text-zinc-400 hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.BRANDS ?? "/brands"), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 dark:text-zinc-400 hover:text-primary-600 transition-colors", children: "Brands" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Span, { weight: "medium", className: hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-100", children: brand?.name ?? slug })] }), _jsxs(Div, { className: "flex items-center gap-4 mb-3", children: [brand?.display?.icon && (_jsx(Span, { className: "text-5xl leading-none", children: brand.display.icon })), _jsxs(_Fragment, { children: [_jsx(Heading, { level: 1, className: `text-3xl md:text-4xl font-bold ${hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-50"}`, children: brand?.name ?? slug }), brand?.description && typeof brand.description === "string" && !brand.description.startsWith("{") && (_jsx(Text, { className: `text-base max-w-2xl mt-1 ${hasCover ? "text-white/80" : "text-zinc-600 dark:text-zinc-400"}`, children: brand.description }))] })] }), _jsxs(Div, { className: "flex flex-wrap gap-2 mt-3", children: [counts.products > 0 && (_jsxs(Span, { size: "xs", weight: "medium", className: `inline-flex items-center gap-1 px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, children: [counts.products.toLocaleString(), " ", counts.products === 1 ? "product" : "products"] })), counts.auctions > 0 && (_jsxs(Span, { size: "xs", weight: "medium", className: `inline-flex items-center gap-1 px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"}`, children: [counts.auctions.toLocaleString(), " ", counts.auctions === 1 ? "auction" : "auctions"] })), counts.preOrders > 0 && (_jsxs(Span, { size: "xs", weight: "medium", className: `inline-flex items-center gap-1 px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400"}`, children: [counts.preOrders.toLocaleString(), " ", counts.preOrders === 1 ? "pre-order" : "pre-orders"] })), totalItems === 0 && (_jsx(Span, { size: "sm", className: hasCover ? "text-white/60" : "text-zinc-400", children: "No items listed yet" }))] })] })] }), _jsx(Section, { className: "py-6", children: _jsx(Container, { size: "xl", children: brandName ? (_jsx(BrandDetailTabs, { brandName: brandName, initialProductsData: productsResult ?? undefined, initialBundles: brandBundles, counts: counts })) : (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "Brand not found." })) }) })] }));
|
|
90
94
|
}
|
|
@@ -112,7 +112,9 @@ export async function CategoryDetailPageView({ slug }) {
|
|
|
112
112
|
const totalCount = productCount + auctionCount + preOrderCount + prizeDrawCount + bundleCount + storeCount;
|
|
113
113
|
const coverImage = category?.display?.coverImage;
|
|
114
114
|
const hasCover = Boolean(coverImage);
|
|
115
|
-
return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative ${__O.hidden} ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover",
|
|
115
|
+
return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative ${__O.hidden} ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover",
|
|
116
|
+
// audit-inline-style-ok: dynamic image URL
|
|
117
|
+
style: { backgroundImage: `url(${coverImage})` } }), _jsx(Div, { className: "absolute inset-0 bg-black/55" })] })), _jsxs(Div, { className: `relative z-10 max-w-7xl mx-auto px-4 ${hasCover ? "py-12" : "py-8"}`, children: [_jsxs(Nav, { className: "flex items-center gap-1.5 text-sm mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 dark:text-zinc-400 hover:text-primary-600 transition-colors", children: "Home" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORIES), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 dark:text-zinc-400 hover:text-primary-600 transition-colors", children: "Categories" }), _jsx(Span, { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Span, { weight: "medium", className: hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-100", children: category?.name ?? slug })] }), _jsx(Heading, { level: 1, className: `text-3xl md:text-4xl font-bold mb-2 ${hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-50"}`, children: category?.name ?? slug }), category?.description && typeof category.description === "string" && !category.description.startsWith("{") && (_jsx(Text, { className: `text-base max-w-2xl mb-4 ${hasCover ? "text-white/80" : "text-zinc-600 dark:text-zinc-400"}`, children: category.description })), _jsxs(Div, { className: "flex flex-wrap gap-2", children: [productCount > 0 && (_jsxs(Span, { size: "xs", weight: "medium", className: `inline-flex items-center gap-1 px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, children: [productCount.toLocaleString(), " ", productCount === 1 ? "product" : "products"] })), auctionCount > 0 && (_jsxs(Span, { size: "xs", weight: "medium", className: `inline-flex items-center gap-1 px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"}`, children: [auctionCount.toLocaleString(), " ", auctionCount === 1 ? "auction" : "auctions"] })), preOrderCount > 0 && (_jsxs(Span, { size: "xs", weight: "medium", className: `inline-flex items-center gap-1 px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400"}`, children: [preOrderCount.toLocaleString(), " ", preOrderCount === 1 ? "pre-order" : "pre-orders"] })), storeCount > 0 && (_jsxs(Span, { size: "xs", weight: "medium", className: `inline-flex items-center gap-1 px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400"}`, children: [storeCount.toLocaleString(), " ", storeCount === 1 ? "store" : "stores"] }))] })] })] }), childCategories.length > 0 && (_jsx(Section, { surface: "default", className: "border-b border-zinc-100 dark:border-zinc-800", children: _jsxs(Div, { className: "max-w-7xl mx-auto px-4 py-3", children: ["// audit-inline-style-ok: dynamic CSS", _jsx(Div, { className: `flex gap-2.5 ${__O.xAuto} pb-1`, style: { scrollbarWidth: "none" }, children: childCategories.map((child) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(child.slug)), className: "flex-shrink-0 flex items-center gap-1.5 rounded-full border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800 px-4 py-1.5 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:border-primary hover:text-primary transition-colors whitespace-nowrap", children: [child.display?.icon && (_jsx(Span, { className: "leading-none", children: child.display.icon })), child.name, (child.metrics?.productCount ?? 0) > 0 && (_jsx(Span, { size: "xs", className: "text-zinc-400 dark:text-zinc-400", children: (child.metrics?.productCount ?? 0).toLocaleString() }))] }, child.id))) })] }) })), _jsx(Section, { className: "py-6", children: _jsx(Container, { size: "xl", children: _jsx(CategoryDetailTabs, { categorySlug: slug, categoryId: category?.id, initialProductsData: productsResult ?? undefined, initialBundles: bundlesResult ?? [], initialStores: categoryStores, counts: {
|
|
116
118
|
products: productCount,
|
|
117
119
|
auctions: auctionCount,
|
|
118
120
|
preOrders: preOrderCount,
|
|
@@ -12,7 +12,11 @@ const __O = {
|
|
|
12
12
|
const CLS_FEATURED_DOT = "absolute left-2 top-2 rounded-full bg-amber-400 p-1 leading-none";
|
|
13
13
|
export function CategoryCard({ category, href, onClick, className = "", }) {
|
|
14
14
|
const productCount = category.metrics?.productCount ?? category.productCount ?? 0;
|
|
15
|
-
const inner = (_jsxs(Div, { className: "flex h-full flex-col", children: [_jsxs(Div, { className: `relative aspect-[4/3] w-full ${__O.hidden} bg-gradient-to-br from-neutral-100 to-neutral-200 dark:from-zinc-800 dark:to-zinc-700 flex-shrink-0`, children: [category.display?.coverImage ? (_jsx(Div, { role: "img", "aria-label": category.name, className: "h-full w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105",
|
|
15
|
+
const inner = (_jsxs(Div, { className: "flex h-full flex-col", children: [_jsxs(Div, { className: `relative aspect-[4/3] w-full ${__O.hidden} bg-gradient-to-br from-neutral-100 to-neutral-200 dark:from-zinc-800 dark:to-zinc-700 flex-shrink-0`, children: [category.display?.coverImage ? (_jsx(Div, { role: "img", "aria-label": category.name, className: "h-full w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105",
|
|
16
|
+
// audit-inline-style-ok: dynamic image URL
|
|
17
|
+
style: { backgroundImage: `url(${category.display.coverImage})` } })) : category.display?.color ? (_jsx(Div, { className: "h-full w-full opacity-80",
|
|
18
|
+
// audit-inline-style-ok: runtime theme color
|
|
19
|
+
style: { backgroundColor: category.display.color } })) : null, category.display?.icon && (_jsx(Div, { className: "absolute inset-0 flex items-center justify-center text-4xl", children: category.display.icon })), category.isFeatured && (_jsx(Span, { size: "xs", className: CLS_FEATURED_DOT, children: "\u2605" }))] }), _jsxs(Div, { className: `flex flex-1 flex-col ${__P.p3}.5`, children: [_jsx(Text, { className: `font-semibold text-sm leading-snug ${THEME_CONSTANTS.utilities.textClamp2} text-neutral-900 dark:text-white`, children: category.name }), category.description && (_jsx(Text, { className: `mt-1 text-xs ${THEME_CONSTANTS.utilities.textClamp2} text-neutral-500 dark:text-zinc-400 flex-1`, children: category.description })), _jsxs(Div, { className: "mt-2 flex items-center justify-between gap-2", children: [_jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-400", children: [productCount.toLocaleString(), " ", productCount === 1 ? "item" : "items"] }), _jsxs(Span, { size: "xs", weight: "medium", className: "inline-flex items-center gap-1 rounded-md border border-zinc-200 dark:border-zinc-700 px-2.5 py-1 text-zinc-600 dark:text-zinc-300 group-hover:bg-primary group-hover:border-primary group-hover:text-white transition-colors", children: ["Browse ", _jsx(ArrowRight, { className: "h-3 w-3" })] })] })] })] }));
|
|
16
20
|
const cardClass = `group relative flex flex-col overflow-hidden rounded-xl border border-neutral-200 bg-white dark:border-zinc-700 dark:bg-zinc-900 shadow-sm transition hover:shadow-md h-full ${className}`;
|
|
17
21
|
if (href) {
|
|
18
22
|
return (_jsx(Link, { href: href, className: cardClass, children: inner }));
|
|
@@ -22,13 +22,17 @@ export function getCategoryTableColumns(onEdit, onDelete, labels = {}) {
|
|
|
22
22
|
{
|
|
23
23
|
key: "image",
|
|
24
24
|
header: "",
|
|
25
|
-
render: (cat) => (_jsx(Div, { className: `relative w-9 h-9 rounded ${__O.hidden} flex-shrink-0`, children: cat.display?.coverImage ? (_jsx(MediaImage, { src: cat.display.coverImage, alt: "", size: "thumbnail", className: "w-full h-full object-cover" })) : (_jsx(Div, { className: "w-full h-full flex items-center justify-center text-base",
|
|
25
|
+
render: (cat) => (_jsx(Div, { className: `relative w-9 h-9 rounded ${__O.hidden} flex-shrink-0`, children: cat.display?.coverImage ? (_jsx(MediaImage, { src: cat.display.coverImage, alt: "", size: "thumbnail", className: "w-full h-full object-cover" })) : (_jsx(Div, { className: "w-full h-full flex items-center justify-center text-base",
|
|
26
|
+
// audit-inline-style-ok: runtime theme color
|
|
27
|
+
style: { backgroundColor: cat.display?.color ?? "#94a3b8", color: "#fff" }, children: cat.display?.icon ?? "🗂️" })) })),
|
|
26
28
|
},
|
|
27
29
|
{
|
|
28
30
|
key: "name",
|
|
29
31
|
header: L.colName,
|
|
30
32
|
sortable: true,
|
|
31
|
-
render: (cat) => (
|
|
33
|
+
render: (cat) => (
|
|
34
|
+
// audit-inline-style-ok: computed pixel offset
|
|
35
|
+
_jsxs(Div, { style: { paddingLeft: `${cat.tier * 20}px` }, children: [cat.name, cat.tier > 0 && (_jsxs(Span, { size: "xs", className: "text-zinc-400 dark:text-zinc-400 ml-2", children: ["(Tier ", cat.tier, ")"] }))] })),
|
|
32
36
|
},
|
|
33
37
|
{
|
|
34
38
|
key: "slug",
|