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 (
)
}
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')}>
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 */}
)
}