From b989c58169076d8ea0d7c57302c15e568aa6d122 Mon Sep 17 00:00:00 2001 From: Devanshu Rajesh Chicholikar Date: Sat, 24 Jan 2026 16:02:15 -0500 Subject: [PATCH 1/9] feat(dashboard): Phase 1 - Theme foundation with light/dark mode support - DashboardLayout: Added theme-aware Toaster, sidebar state persistence - Sidebar: Migrated to theme tokens, lucide-react icons - TopNav: Theme toggle button, shadcn Button, proper theme tokens - DashboardStats: Theme tokens for stat cards with indigo accent - DashboardHome: Full theme migration, lucide-react icons - RepoList: Theme tokens, lucide-react icons for repo cards - AddRepoForm: Migrated to shadcn Input, Label, Button components All hardcoded colors (bg-[#09090b], text-gray-400, border-white/5) replaced with theme tokens (bg-background, text-muted-foreground, border-border) --- frontend/src/components/AddRepoForm.tsx | 72 +++++------ frontend/src/components/RepoList.tsx | 46 ++++--- .../components/dashboard/DashboardHome.tsx | 38 +++--- .../components/dashboard/DashboardLayout.tsx | 34 +++--- .../components/dashboard/DashboardStats.tsx | 90 ++++++++------ frontend/src/components/dashboard/Sidebar.tsx | 112 +++++++---------- frontend/src/components/dashboard/TopNav.tsx | 113 ++++++++++-------- 7 files changed, 253 insertions(+), 252 deletions(-) diff --git a/frontend/src/components/AddRepoForm.tsx b/frontend/src/components/AddRepoForm.tsx index 7f01544..d0cf43a 100644 --- a/frontend/src/components/AddRepoForm.tsx +++ b/frontend/src/components/AddRepoForm.tsx @@ -1,6 +1,9 @@ import { useState } from 'react' import { motion, AnimatePresence } from 'framer-motion' -import { Package, Plus, X } from 'lucide-react' +import { Package, Plus, X, Loader2 } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' interface AddRepoFormProps { onAdd: (gitUrl: string, branch: string) => Promise @@ -23,16 +26,14 @@ export function AddRepoForm({ onAdd, loading }: AddRepoFormProps) { return ( <> - setShowForm(true)} - className="px-5 py-2.5 bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white text-sm font-medium rounded-xl transition-all shadow-lg shadow-blue-500/20 flex items-center gap-2" disabled={loading} + className="bg-primary hover:bg-primary/90 text-primary-foreground gap-2" > - + - Add Repository - + + Add Repository + {showForm && ( @@ -40,7 +41,7 @@ export function AddRepoForm({ onAdd, loading }: AddRepoFormProps) { initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} - className="fixed inset-0 bg-black/70 backdrop-blur-md flex items-center justify-center z-50" + className="fixed inset-0 bg-background/80 backdrop-blur-md flex items-center justify-center z-50" onClick={() => !loading && setShowForm(false)} > e.stopPropagation()} - className="bg-gradient-to-br from-[#111113] to-[#0a0a0c] border border-white/10 rounded-2xl shadow-2xl w-full max-w-md mx-4" + className="bg-card border border-border rounded-xl shadow-2xl w-full max-w-md mx-4" > {/* Header */} -
+
-
- +
+
-

Add Repository

-

Clone and index with AI

+

Add Repository

+

Clone and index with AI

- +
diff --git a/frontend/src/components/RepoList.tsx b/frontend/src/components/RepoList.tsx index 14b241a..a8a2777 100644 --- a/frontend/src/components/RepoList.tsx +++ b/frontend/src/components/RepoList.tsx @@ -1,5 +1,6 @@ import { useState, useRef, useMemo } from 'react' import { motion } from 'framer-motion' +import { FolderGit2, Plus } from 'lucide-react' import type { Repository } from '../types' import { RepoGridSkeleton } from './ui/Skeleton' @@ -16,11 +17,11 @@ const StatusBadge = ({ status }: { status: string }) => { return ( - + {isIndexed ? 'Indexed' : 'Pending'} ) @@ -50,16 +51,16 @@ const RepoCard = ({ repo, index, onSelect }: { }} onMouseEnter={() => setHovering(true)} onMouseLeave={() => setHovering(false)} - className="group relative text-left rounded-2xl overflow-hidden w-full - bg-[#111113] border border-white/[0.06] hover:border-blue-500/40 - focus:outline-none focus:ring-2 focus:ring-blue-500/50 p-5 transition-colors" + className="group relative text-left rounded-xl overflow-hidden w-full + bg-card border border-border hover:border-primary/40 + focus:outline-none focus:ring-2 focus:ring-primary/50 p-5 transition-colors" > - {/* Mouse glow */} + {/* Mouse glow effect */} {hovering && (
)} @@ -67,25 +68,23 @@ const RepoCard = ({ repo, index, onSelect }: {
{/* Header */}
-
- - - +
+
{/* Title */} -

+

{repo.name}

-

{repo.branch}

+

{repo.branch}

{/* Stats */} -
+
- Functions - + Functions + {(repo.file_count || 0).toLocaleString()}
@@ -103,22 +102,19 @@ export function RepoList({ repos, selectedRepo, onSelect, loading }: RepoListPro -
- - - +
+
-

No repositories yet

-

+

No repositories yet

+

Add your first repository to start searching code with AI

) } - // Sort: indexed first, then by function count desc const sortedRepos = useMemo(() => { return [...repos].sort((a, b) => { if (a.status === 'indexed' && b.status !== 'indexed') return -1 diff --git a/frontend/src/components/dashboard/DashboardHome.tsx b/frontend/src/components/dashboard/DashboardHome.tsx index e2d5115..0873bfc 100644 --- a/frontend/src/components/dashboard/DashboardHome.tsx +++ b/frontend/src/components/dashboard/DashboardHome.tsx @@ -1,7 +1,16 @@ import { useState, useEffect } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { toast } from 'sonner' -import { LayoutDashboard, Search, GitFork, Code2, Zap } from 'lucide-react' +import { + LayoutDashboard, + Search, + GitFork, + Code2, + Zap, + ArrowLeft, + FolderGit2, + ExternalLink +} from 'lucide-react' import { useAuth } from '../../contexts/AuthContext' import { RepoList } from '../RepoList' import { AddRepoForm } from '../AddRepoForm' @@ -122,8 +131,8 @@ export function DashboardHome() { {/* Header */}
-

Repositories

-

+

Repositories

+

Semantic code search powered by AI

@@ -158,28 +167,27 @@ export function DashboardHome() {
-
- - - +
+
-

{selectedRepoData.name}

+

{selectedRepoData.name}

{selectedRepoData.git_url} +
@@ -187,15 +195,15 @@ export function DashboardHome() {
{/* Tabs */} -
+
{tabs.map(tab => (
{/* Tab Content */} -
+
{activeTab === 'overview' && ( { + const stored = localStorage.getItem(SIDEBAR_STORAGE_KEY) + return stored ? JSON.parse(stored) : false + }) const [commandPaletteOpen, setCommandPaletteOpen] = useState(false) - // Keyboard shortcuts + useEffect(() => { + localStorage.setItem(SIDEBAR_STORAGE_KEY, JSON.stringify(sidebarCollapsed)) + }, [sidebarCollapsed]) + useKeyboardShortcut(SHORTCUTS.COMMAND_PALETTE, () => { setCommandPaletteOpen(true) }) useKeyboardShortcut(SHORTCUTS.TOGGLE_SIDEBAR, () => { - setSidebarCollapsed(prev => !prev) + setSidebarCollapsed((prev: boolean) => !prev) }) return ( -
- {/* Top Navigation */} +
setSidebarCollapsed(!sidebarCollapsed)} sidebarCollapsed={sidebarCollapsed} @@ -33,13 +43,11 @@ export function DashboardLayout({ children }: DashboardLayoutProps) { />
- {/* Sidebar */} setSidebarCollapsed(!sidebarCollapsed)} /> - {/* Main Content */}
- {/* Command Palette */} setCommandPaletteOpen(false)} />
) diff --git a/frontend/src/components/dashboard/DashboardStats.tsx b/frontend/src/components/dashboard/DashboardStats.tsx index 7d31fb1..9c83c72 100644 --- a/frontend/src/components/dashboard/DashboardStats.tsx +++ b/frontend/src/components/dashboard/DashboardStats.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react' import { motion } from 'framer-motion' +import { FolderGit2, CheckCircle, Code2 } from 'lucide-react' import type { Repository } from '../../types' interface DashboardStatsProps { @@ -35,48 +36,59 @@ export function DashboardStats({ repos }: DashboardStatsProps) { const animatedIndexed = useAnimatedCounter(indexedRepos) const animatedFunctions = useAnimatedCounter(totalFunctions) + const stats = [ + { + label: 'Total Repositories', + value: animatedTotal, + icon: FolderGit2, + suffix: '', + }, + { + label: 'Indexed', + value: animatedIndexed, + icon: CheckCircle, + suffix: `/${totalRepos}`, + extra: indexingCount > 0 ? ( +
+ + {indexingCount} indexing... +
+ ) : null, + }, + { + label: 'Functions Indexed', + value: animatedFunctions, + icon: Code2, + suffix: '', + format: true, + }, + ] + return (
- {/* Total Repos */} - -

Total Repositories

-

{animatedTotal}

-
- - {/* Indexed */} - -

Indexed

-
- {animatedIndexed} - /{totalRepos} -
- {indexingCount > 0 && ( -
- - {indexingCount} indexing... + {stats.map((stat, index) => ( + +
+

{stat.label}

+
- )} -
- - {/* Functions */} - -

Functions Indexed

-

{animatedFunctions.toLocaleString()}

-
+
+ + {stat.format ? stat.value.toLocaleString() : stat.value} + + {stat.suffix && ( + {stat.suffix} + )} +
+ {stat.extra} + + ))}
) } diff --git a/frontend/src/components/dashboard/Sidebar.tsx b/frontend/src/components/dashboard/Sidebar.tsx index 98a9d02..154c48e 100644 --- a/frontend/src/components/dashboard/Sidebar.tsx +++ b/frontend/src/components/dashboard/Sidebar.tsx @@ -1,48 +1,19 @@ import { Link, useLocation } from 'react-router-dom' -import { useAuth } from '../../contexts/AuthContext' +import { + FolderGit2, + Search, + Settings, + BookOpen, + ChevronLeft, + ChevronRight, + ExternalLink +} from 'lucide-react' interface SidebarProps { collapsed: boolean onToggle: () => void } -// Icons -const RepoIcon = () => ( - - - -) - -const SearchIcon = () => ( - - - -) - -const SettingsIcon = () => ( - - - - -) - -const ChevronIcon = ({ direction = 'left' }: { direction?: 'left' | 'right' }) => ( - - - -) - -const DocsIcon = () => ( - - - -) - interface NavItem { name: string href: string @@ -51,18 +22,17 @@ interface NavItem { } const mainNavItems: NavItem[] = [ - { name: 'Repositories', href: '/dashboard', icon: }, - { name: 'Global Search', href: '/dashboard/search', icon: }, + { name: 'Repositories', href: '/dashboard', icon: }, + { name: 'Global Search', href: '/dashboard/search', icon: }, ] const bottomNavItems: NavItem[] = [ - { name: 'Documentation', href: '/docs', icon: , external: true }, - { name: 'Settings', href: '/dashboard/settings', icon: }, + { name: 'Documentation', href: '/docs', icon: , external: true }, + { name: 'Settings', href: '/dashboard/settings', icon: }, ] export function Sidebar({ collapsed, onToggle }: SidebarProps) { const location = useLocation() - const { session } = useAuth() const isActive = (href: string) => { if (href === '/dashboard') { @@ -72,24 +42,31 @@ export function Sidebar({ collapsed, onToggle }: SidebarProps) { } const NavLink = ({ item }: { item: NavItem }) => { - // External links open in new tab + const active = isActive(item.href) + + const baseClasses = ` + flex items-center gap-3 px-3 py-2.5 rounded-lg transition-all group + ${active + ? 'bg-primary/10 text-primary' + : 'text-muted-foreground hover:text-foreground hover:bg-muted' + } + ` + if (item.external) { return ( - + {item.icon} {!collapsed && ( <> {item.name} - - - + )} @@ -97,15 +74,8 @@ export function Sidebar({ collapsed, onToggle }: SidebarProps) { } return ( - - + + {item.icon} {!collapsed && ( @@ -117,30 +87,36 @@ export function Sidebar({ collapsed, onToggle }: SidebarProps) { return ( diff --git a/frontend/src/components/dashboard/TopNav.tsx b/frontend/src/components/dashboard/TopNav.tsx index 5aa596c..364865c 100644 --- a/frontend/src/components/dashboard/TopNav.tsx +++ b/frontend/src/components/dashboard/TopNav.tsx @@ -1,6 +1,19 @@ import { Link } from 'react-router-dom' import { useAuth } from '../../contexts/AuthContext' import { useState } from 'react' +import { + Menu, + Search, + Github, + Sun, + Moon, + LogOut, + Settings, + BookOpen, + ExternalLink +} from 'lucide-react' +import { useTheme } from 'next-themes' +import { Button } from '@/components/ui/button' interface TopNavProps { onToggleSidebar: () => void @@ -8,131 +21,127 @@ interface TopNavProps { onOpenCommandPalette?: () => void } -// Icons -const MenuIcon = () => ( - - - -) - -const SearchIcon = () => ( - - - -) - -const GitHubIcon = () => ( - - - -) - -const CodeIntelLogo = () => ( -
- CI -
-) - export function TopNav({ onToggleSidebar, sidebarCollapsed, onOpenCommandPalette }: TopNavProps) { const { session, signOut } = useAuth() + const { theme, setTheme } = useTheme() const [showUserMenu, setShowUserMenu] = useState(false) const userEmail = session?.user?.email || 'User' const userInitial = userEmail.charAt(0).toUpperCase() + const toggleTheme = () => { + setTheme(theme === 'dark' ? 'light' : 'dark') + } + return ( -