import { useState } from "react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Label } from "@/components/ui/label" import { Switch } from "@/components/ui/switch" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible" import { TemplateGrid, Template } from "@/components/ui/template-grid" import { LivePreview, LivePreviewContent } from "@/components/ui/live-preview" import { GlobalColorPicker } from "@/components/ui/global-color-picker" import { GlobalTypographyPicker } from "@/components/ui/global-typography-picker" import { Palette, LayoutTemplate, RotateCcw, MapPin, ChevronDown, Sliders, Type, Layers, MousePointerClick, Settings, Minus, Plus, } from "lucide-react" import { AppearanceSettings, BehaviorSettings, ContentSettings, CookieCategory, templatePresets, fontSizeOptions, titleFontSizeOptions, } from "../../types" import { usePro } from "@/contexts/pro-context" import { ProIndicator } from "@/components/pro/pro-indicator" import { CookieConsentPreview } from "../cookie-consent-preview" /** Compact number input with +/- buttons — replaces Slider */ function NumberStepper({ label, value, onChange, min, max, step = 1, unit = 'px', }: { label: string value: number onChange: (v: number) => void min: number max: number step?: number unit?: string }) { const clamp = (v: number) => Math.min(max, Math.max(min, v)) return (
{ const parsed = parseInt(e.target.value, 10) if (!isNaN(parsed)) onChange(clamp(parsed)) }} className="h-8 w-14 text-center text-sm border-x bg-transparent outline-none" />
) } interface AppearanceTabProps { settings: AppearanceSettings updateSettings: (key: K, value: AppearanceSettings[K]) => void behaviorSettings: BehaviorSettings updateBehaviorSettings: (key: K, value: BehaviorSettings[K]) => void contentSettings: ContentSettings categories: CookieCategory[] } export function AppearanceTab({ settings, updateSettings, behaviorSettings, updateBehaviorSettings, contentSettings, categories, }: AppearanceTabProps) { const { isPro } = usePro() const [expandedSections, setExpandedSections] = useState>({ colors: false, position: false, typography: false, overlay: false, buttons: false, style: false, reopen: false, }) const toggleSection = (section: string) => { setExpandedSections(prev => ({ ...prev, [section]: !prev[section] })) } const handleTemplateSelect = (templateId: string) => { const template = templatePresets.find(t => t.id === templateId) if (!template) return // Apply template settings Object.entries(template.settings).forEach(([key, value]) => { updateSettings(key as keyof AppearanceSettings, value as AppearanceSettings[keyof AppearanceSettings]) }) // Reset colors to design system defaults when switching templates updateSettings('primaryColor', '') updateSettings('backgroundColor', '') updateSettings('textColor', '') } // Convert templatePresets to Template format for TemplateGrid const templates: Template[] = templatePresets.map(t => ({ id: t.id, name: t.name, description: t.description, preview: t.preview, isPro: t.isPro, alwaysVisible: t.alwaysVisible, })); return (
{/* Left Column - Settings */}
{/* Templates */} Templates Choose a pre-designed template for your cookie popup {/* Customize Design - Accordion sections (always shown, Pro sections gated) */} Customize Design {isPro ? 'Fine-tune every aspect of your cookie popup appearance' : 'Customize the colors of your cookie popup'} {/* ─── Colors — Free ─── */} toggleSection('colors')}>
Colors
updateSettings('primaryColor', v)} globalTokenId="primary" featureDefault="" /> updateSettings('backgroundColor', v)} globalTokenId="background" featureDefault="" /> updateSettings('textColor', v)} globalTokenId="text" featureDefault="" />
{/* Upgrade nudge for Free users */} {!isPro && (

For full customization control including position, typography, button styling, overlay effects, and more, upgrade to Swift Commerce Pro.

)} {/* ─── Position & Animation — Pro ─── */} {isPro && ( toggleSection('position')}>
Position & Animation
{(settings.position === 'bottom' || settings.position === 'top') && (
)}
{settings.layout === 'boxed' && (settings.position === 'bottom' || settings.position === 'top') && ( updateSettings('boxedWidth', v)} min={30} max={95} step={5} unit="%" /> )}
)} {/* ─── Typography — Pro ─── */} {isPro && ( toggleSection('typography')}>
Typography
updateSettings('fontFamily', v)} globalTokenId="body" featureDefault="inherit" />
)} {/* ─── Button Styling — Pro ─── */} {isPro && ( toggleSection('buttons')}>
Button Styling
{/* Accept Button */}
updateSettings('acceptButtonColor', v)} globalTokenId="primary" featureDefault="" /> updateSettings('acceptButtonTextColor', v)} featureDefault="#ffffff" />
{/* Decline Button */}
updateSettings('declineButtonColor', v)} globalTokenId="text" featureDefault="" />
{/* Settings Button */}
)} {/* ─── Overlay — Pro ─── */} {isPro && ( toggleSection('overlay')}>
Overlay
updateSettings('overlay', checked)} />
{settings.overlay && ( <> updateSettings('overlayColor', v)} featureDefault="#000000" /> updateSettings('overlayOpacity', v)} min={10} max={90} step={5} unit="%" />
updateSettings('overlayBlur', checked)} />
)}
)} {/* ─── Style Options — Pro ─── */} {isPro && ( toggleSection('style')}>
Style Options
updateSettings('borderRadius', v)} min={0} max={24} step={1} />
updateSettings('showIcon', checked)} />
updateSettings('blur', checked)} />
)} {/* ─── Reopen Button — Pro ─── */} {isPro && ( toggleSection('reopen')}>
Reopen Button

How users can change their consent preferences after dismissing the popup.

{behaviorSettings.reopenMethod === 'floating-button' && (
)}
)}
{/* Right Column - Sticky Preview */}
) }