/** * Globals Sidebar — sliding drill-down navigation for the Globals feature * * Rendered inside AppSidebar when currentPage === 'globals' && isPro. * * Three panels slide horizontally: * Level 0 (home): Colors & Typography nav items + info note * Level 1 (colors): Color token editors * Level 1 (typography): Typography token editors * * A sticky footer with Save / Reset buttons is always visible. */ import { ScrollArea } from '@/components/ui/scroll-area' import { Button } from '@/components/ui/button' import { ArrowLeft, ChevronRight, Info, Loader2, Palette, Plus, RotateCcw, Save, Type, } from 'lucide-react' import { useState } from 'react' import { cn } from '@/lib/utils' import { useGlobalsContext } from '../contexts/globals-context' import { useNavigation } from '@/contexts/navigation-context' import { GlobalColor, GlobalTypography } from '../config' import { ColorEditor } from './color-editor' import { TypographyEditor } from './typography-editor' import { toast } from 'sonner' import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem, } from '@/components/ui/sidebar' /* ── Helpers ──────────────────────────────────────────────── */ function slugify(name: string): string { return ( name .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, '') || `token-${Date.now()}` ) } /* ── Component ────────────────────────────────────────────── */ export function GlobalsSidebar() { const { navigateTo } = useNavigation() const { settings, loading, setSettings, saving, hasChanges, saveSettings, resetToDefaults, globalsView, setGlobalsView, } = useGlobalsContext() // Track which single item is expanded (accordion-style, one at a time) const [expandedItem, setExpandedItem] = useState(null) const toggleItem = (id: string) => setExpandedItem((prev) => (prev === id ? null : id)) /* ── CRUD — Colors ──────────────────────────────────── */ const updateColor = (index: number, updated: GlobalColor) => { const next = [...settings.colors] next[index] = updated setSettings({ ...settings, colors: next }) } const addColor = () => { const newColor: GlobalColor = { id: slugify(`custom-${settings.colors.length + 1}`), name: `Custom ${settings.colors.length + 1}`, value: '#6366f1', isDefault: false, } setSettings({ ...settings, colors: [...settings.colors, newColor] }) } const deleteColor = (index: number) => { const next = settings.colors.filter((_: GlobalColor, i: number) => i !== index) setSettings({ ...settings, colors: next }) } /* ── CRUD — Typography ──────────────────────────────── */ const updateTypography = (index: number, updated: GlobalTypography) => { const next = [...settings.typography] next[index] = updated setSettings({ ...settings, typography: next }) } const addTypography = () => { const newTypo: GlobalTypography = { id: slugify(`custom-font-${settings.typography.length + 1}`), name: `Custom Font ${settings.typography.length + 1}`, fontFamily: '', fontWeight: '400', fontSize: '', lineHeight: '', letterSpacing: '', textTransform: 'none', isDefault: false, } setSettings({ ...settings, typography: [...settings.typography, newTypo] }) } const deleteTypography = (index: number) => { const next = settings.typography.filter( (_: GlobalTypography, i: number) => i !== index, ) setSettings({ ...settings, typography: next }) } /* ── Actions ────────────────────────────────────────── */ const handleSave = async () => { const ok = await saveSettings() if (ok) toast.success('Global styles saved') else toast.error('Failed to save') } const handleReset = () => { resetToDefaults() toast.info('Reset to defaults — save to apply') } /* ── Loading skeleton ───────────────────────────────── */ if (loading) { return (
{[...Array(5)].map((_, i: number) => (
))}
) } /* ── Render ─────────────────────────────────────────── */ return (
{/* ── Sliding panels ─────────────────────────────── */}
{/* ▸ Panel 0 — Home */}
{/* Back to dashboard */}
{/* Info note */}

Control your global design tokens from here. Changes apply across all Swift Commerce features.

{/* Nav items */} Design Tokens setGlobalsView('colors')} className="cursor-pointer" > Colors {settings.colors.length} setGlobalsView('typography')} className="cursor-pointer" > Typography {settings.typography.length}
{/* ▸ Panel 1a — Colors */}
{settings.colors.map((color: GlobalColor, index: number) => ( updateColor(index, updated)} onDelete={ !color.isDefault ? () => deleteColor(index) : undefined } isOpen={expandedItem === `color-${color.id}`} onToggle={() => toggleItem(`color-${color.id}`)} /> ))}
{/* ▸ Panel 1b — Typography */}
{settings.typography.map( (typo: GlobalTypography, index: number) => ( updateTypography(index, updated) } onDelete={ !typo.isDefault ? () => deleteTypography(index) : undefined } isOpen={expandedItem === `typo-${typo.id}`} onToggle={() => toggleItem(`typo-${typo.id}`)} /> ), )}
{/* ── Save / Reset footer ────────────────────────── */}
) }