Spaces:
Running
Running
| import { useState } from 'react' | |
| import { motion, AnimatePresence } from 'framer-motion' | |
| import { Database, Table2, ChevronDown, ChevronRight, GitFork, ShoppingCart } from 'lucide-react' | |
| import { useStore } from '../store/useStore' | |
| import type { Difficulty } from '../lib/types' | |
| const DIFFICULTY_CONFIG: Record<Difficulty, { label: string; bg: string; text: string; border: string }> = { | |
| easy: { label: 'Easy', bg: 'bg-green-500/10', text: 'text-green-400', border: 'border-green-500/30' }, | |
| medium: { label: 'Medium', bg: 'bg-amber-500/10', text: 'text-amber-400', border: 'border-amber-500/30' }, | |
| hard: { label: 'Hard', bg: 'bg-red-500/10', text: 'text-red-400', border: 'border-red-500/30' }, | |
| } | |
| export function LeftSidebar() { | |
| const { tables, taskDifficulty, setTaskDifficulty, dbSeeded, isCustomDb, dbLabel } = useStore() | |
| const [tablesExpanded, setTablesExpanded] = useState(true) | |
| const cfg = DIFFICULTY_CONFIG[taskDifficulty] | |
| return ( | |
| <div className="flex flex-col gap-4 py-1"> | |
| {/* Task Difficulty — hidden for custom databases */} | |
| {!isCustomDb && ( | |
| <section> | |
| <div className="text-[10px] font-semibold text-gray-500 uppercase tracking-widest mb-2 flex items-center gap-1.5"> | |
| <GitFork size={10} className="text-violet-400" /> | |
| Task Difficulty | |
| </div> | |
| <div className="flex flex-col gap-1"> | |
| {(Object.keys(DIFFICULTY_CONFIG) as Difficulty[]).map((d) => { | |
| const c = DIFFICULTY_CONFIG[d] | |
| const active = d === taskDifficulty | |
| return ( | |
| <button | |
| key={d} | |
| onClick={() => setTaskDifficulty(d)} | |
| className={`flex items-center justify-between px-3 py-2 rounded-lg border text-xs font-medium transition-all ${ | |
| active | |
| ? `${c.bg} ${c.text} ${c.border}` | |
| : 'border-transparent text-gray-500 hover:text-gray-300 hover:bg-white/5' | |
| }`} | |
| > | |
| <span>{c.label}</span> | |
| {active && ( | |
| <span className={`text-[9px] font-mono ${c.text} opacity-70`}>selected</span> | |
| )} | |
| </button> | |
| ) | |
| })} | |
| </div> | |
| </section> | |
| )} | |
| {/* Schema Tables */} | |
| <section> | |
| <button | |
| className="text-[10px] font-semibold text-gray-500 uppercase tracking-widest mb-2 flex items-center gap-1.5 w-full" | |
| onClick={() => setTablesExpanded((v) => !v)} | |
| > | |
| <Database size={10} className="text-blue-400" /> | |
| <span className="flex-1 text-left">Database Schema</span> | |
| {tablesExpanded ? <ChevronDown size={10} /> : <ChevronRight size={10} />} | |
| </button> | |
| <AnimatePresence> | |
| {tablesExpanded && ( | |
| <motion.div | |
| initial={{ opacity: 0, height: 0 }} | |
| animate={{ opacity: 1, height: 'auto' }} | |
| exit={{ opacity: 0, height: 0 }} | |
| className="overflow-hidden" | |
| > | |
| {dbSeeded && tables.length > 0 ? ( | |
| <div className="flex flex-col gap-1"> | |
| {tables.map((t) => ( | |
| <div | |
| key={t.name} | |
| className="flex items-center justify-between px-2.5 py-1.5 rounded-lg border border-white/[0.04] bg-white/[0.02] hover:bg-white/[0.04] transition-colors" | |
| > | |
| <div className="flex items-center gap-1.5"> | |
| <Table2 size={10} className="text-blue-400 shrink-0" /> | |
| <span className="text-xs text-gray-300 font-mono">{t.name}</span> | |
| </div> | |
| <span className="text-[9px] text-gray-600 font-mono tabular-nums"> | |
| {t.rows.toLocaleString()} | |
| </span> | |
| </div> | |
| ))} | |
| </div> | |
| ) : ( | |
| <div className="flex flex-col gap-1"> | |
| {[120, 80, 95, 60, 70].map((w, i) => ( | |
| <div | |
| key={i} | |
| className="flex items-center justify-between px-2.5 py-1.5 rounded-lg border border-white/[0.04] bg-white/[0.02]" | |
| > | |
| <div | |
| className="h-2 rounded bg-white/10 animate-pulse" | |
| style={{ width: w }} | |
| /> | |
| <div className="h-2 w-8 rounded bg-white/10 animate-pulse" /> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| </motion.div> | |
| )} | |
| </AnimatePresence> | |
| </section> | |
| {/* Business Context */} | |
| <section> | |
| <div className="text-[10px] font-semibold text-gray-500 uppercase tracking-widest mb-2 flex items-center gap-1.5"> | |
| <ShoppingCart size={10} className="text-orange-400" /> | |
| Business Context | |
| </div> | |
| <div | |
| className="rounded-xl border border-white/[0.05] p-3 text-[11px] text-gray-500 leading-relaxed" | |
| style={{ background: 'var(--bg-card)' }} | |
| > | |
| {isCustomDb ? ( | |
| <p className="text-gray-600 italic"> | |
| Connected to <span className="text-violet-400 not-italic font-medium">{dbLabel}</span>. | |
| Ask questions about your data in natural language. | |
| </p> | |
| ) : ( | |
| <> | |
| <p className="mb-2 text-gray-400 font-medium">E-Commerce Marketplace</p> | |
| <p> | |
| Multi-vendor marketplace with products, orders, sellers, users, and reviews. | |
| Supports complex analytical queries across sales, inventory, and user behavior. | |
| </p> | |
| <div className="mt-2 flex flex-wrap gap-1"> | |
| {['Products', 'Orders', 'Sellers', 'Users', 'Reviews', 'Categories'].map((t) => ( | |
| <span | |
| key={t} | |
| className="text-[9px] px-1.5 py-0.5 rounded border border-white/[0.06] text-gray-600" | |
| > | |
| {t} | |
| </span> | |
| ))} | |
| </div> | |
| </> | |
| )} | |
| </div> | |
| </section> | |
| {/* Current task badge — hidden for custom databases */} | |
| {!isCustomDb && ( | |
| <section> | |
| <div | |
| className={`rounded-xl border ${cfg.border} ${cfg.bg} p-3 flex flex-col gap-1.5`} | |
| > | |
| <div className="flex items-center justify-between"> | |
| <span className={`text-[10px] font-semibold uppercase tracking-wider ${cfg.text}`}> | |
| Current Task | |
| </span> | |
| <span className={`text-[10px] font-mono ${cfg.text}`}>{cfg.label}</span> | |
| </div> | |
| <p className="text-[11px] text-gray-400 leading-relaxed"> | |
| {taskDifficulty === 'easy' | |
| ? 'Simple SELECT queries, basic filtering and aggregation' | |
| : taskDifficulty === 'medium' | |
| ? 'Multi-table JOINs, GROUP BY, subqueries, window functions' | |
| : 'Complex CTEs, rolling aggregations, cohort analysis, ranking'} | |
| </p> | |
| </div> | |
| </section> | |
| )} | |
| </div> | |
| ) | |
| } | |