Skip to content

Commit fc52660

Browse files
committed
fix(dashboard): unified blue-violet brand colors, cleaner design
1 parent 3ae2a30 commit fc52660

3 files changed

Lines changed: 175 additions & 306 deletions

File tree

frontend/src/components/RepoList.tsx

Lines changed: 68 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,24 @@ interface RepoListProps {
1111
}
1212

1313
const StatusBadge = ({ status }: { status: string }) => {
14-
const config = {
15-
indexed: { bg: 'bg-emerald-500/10', text: 'text-emerald-400', border: 'border-emerald-500/20', label: 'Indexed', dot: 'bg-emerald-400' },
16-
cloned: { bg: 'bg-blue-500/10', text: 'text-blue-400', border: 'border-blue-500/20', label: 'Pending Index', dot: 'bg-blue-400' },
17-
indexing: { bg: 'bg-amber-500/10', text: 'text-amber-400', border: 'border-amber-500/20', label: 'Indexing...', dot: 'bg-amber-400 animate-pulse' },
18-
cloning: { bg: 'bg-amber-500/10', text: 'text-amber-400', border: 'border-amber-500/20', label: 'Cloning...', dot: 'bg-amber-400 animate-pulse' },
19-
error: { bg: 'bg-red-500/10', text: 'text-red-400', border: 'border-red-500/20', label: 'Error', dot: 'bg-red-400' },
20-
}[status] || { bg: 'bg-white/5', text: 'text-gray-400', border: 'border-white/10', label: status, dot: 'bg-gray-400' }
21-
14+
const isIndexed = status === 'indexed'
15+
const isPending = status === 'cloned' || status === 'cloning' || status === 'indexing'
16+
2217
return (
23-
<span className={`inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-full ${config.bg} ${config.text} border ${config.border}`}>
24-
<span className={`w-1.5 h-1.5 rounded-full ${config.dot}`} />
25-
{config.label}
18+
<span className={`inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-full border
19+
${isIndexed
20+
? 'bg-blue-500/10 text-blue-400 border-blue-500/20'
21+
: isPending
22+
? 'bg-zinc-500/10 text-zinc-400 border-zinc-500/20'
23+
: 'bg-red-500/10 text-red-400 border-red-500/20'
24+
}`}
25+
>
26+
<span className={`w-1.5 h-1.5 rounded-full ${isIndexed ? 'bg-blue-400' : isPending ? 'bg-zinc-400 animate-pulse' : 'bg-red-400'}`} />
27+
{isIndexed ? 'Indexed' : isPending ? 'Pending' : 'Error'}
2628
</span>
2729
)
2830
}
2931

30-
// Featured card - larger, more info
3132
const FeaturedRepoCard = ({ repo, totalFunctions, onSelect }: {
3233
repo: Repository
3334
totalFunctions: number
@@ -49,95 +50,74 @@ const FeaturedRepoCard = ({ repo, totalFunctions, onSelect }: {
4950
ref={cardRef}
5051
initial={{ opacity: 0, y: 20 }}
5152
animate={{ opacity: 1, y: 0 }}
52-
transition={{ duration: 0.4, ease: [0.25, 0.46, 0.45, 0.94] }}
53-
whileHover={{ y: -4, transition: { duration: 0.2 } }}
53+
transition={{ duration: 0.4 }}
54+
whileHover={{ y: -2 }}
5455
onClick={onSelect}
5556
onMouseMove={handleMouseMove}
5657
onMouseEnter={() => setIsHovering(true)}
5758
onMouseLeave={() => setIsHovering(false)}
58-
className="group relative text-left rounded-2xl overflow-hidden h-full min-h-[280px]
59-
bg-gradient-to-br from-[#0f1114] via-[#0d0f12] to-[#0a0c0f]
60-
border border-white/[0.08] hover:border-blue-500/30
61-
hover:shadow-2xl hover:shadow-blue-500/10
62-
focus:outline-none focus:ring-2 focus:ring-blue-500/40 p-7"
59+
className="group relative text-left rounded-2xl overflow-hidden w-full h-full
60+
bg-[#111113] border border-white/[0.06] hover:border-blue-500/30
61+
focus:outline-none focus:ring-2 focus:ring-blue-500/40 p-6"
6362
>
64-
{/* Mouse-following glow */}
63+
{/* Mouse glow */}
6564
{isHovering && (
6665
<div
6766
className="pointer-events-none absolute inset-0 transition-opacity duration-300"
6867
style={{
69-
background: `radial-gradient(500px circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(59, 130, 246, 0.15), transparent 50%)`,
68+
background: `radial-gradient(400px circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(59, 130, 246, 0.08), transparent 50%)`,
7069
}}
7170
/>
7271
)}
7372

74-
{/* Top accent line */}
75-
<div className="absolute top-0 left-0 right-0 h-[2px] bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500 opacity-60" />
73+
{/* Top gradient line */}
74+
<div className="absolute top-0 left-0 right-0 h-[2px] bg-gradient-to-r from-blue-500 to-violet-500" />
7675

77-
{/* Content */}
7876
<div className="relative flex flex-col h-full">
79-
{/* Badge */}
80-
<div className="flex items-center gap-2 mb-4">
81-
<span className="text-xs text-blue-400 font-medium px-2 py-0.5 bg-blue-500/10 rounded-full border border-blue-500/20">
82-
★ Most Functions
83-
</span>
84-
</div>
85-
8677
{/* Header */}
8778
<div className="flex items-start justify-between mb-6">
88-
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-blue-500/20 to-purple-500/20 border border-white/10 flex items-center justify-center group-hover:scale-105 group-hover:border-blue-500/30 transition-all duration-300">
89-
<svg className="w-8 h-8 text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
79+
<div className="w-14 h-14 rounded-xl bg-gradient-to-br from-blue-500/10 to-violet-500/10 border border-white/[0.08] flex items-center justify-center">
80+
<svg className="w-7 h-7 text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9081
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
9182
</svg>
9283
</div>
9384
<StatusBadge status={repo.status} />
9485
</div>
9586

9687
{/* Title */}
97-
<div className="flex-1">
98-
<h3 className="text-2xl font-bold text-white mb-1 group-hover:text-blue-400 transition-colors duration-200">
99-
{repo.name}
100-
</h3>
101-
<p className="text-sm text-gray-500 font-mono">{repo.branch}</p>
102-
</div>
88+
<h3 className="text-xl font-semibold text-white mb-1 group-hover:text-blue-400 transition-colors">
89+
{repo.name}
90+
</h3>
91+
<p className="text-sm text-zinc-500 font-mono mb-auto">{repo.branch}</p>
10392

10493
{/* Stats */}
105-
<div className="pt-5 mt-auto border-t border-white/[0.06] space-y-4">
106-
<div className="flex items-end justify-between">
107-
<span className="text-sm text-gray-400">Functions indexed</span>
108-
<span className="text-3xl font-bold font-mono text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-400">
94+
<div className="pt-6 mt-6 border-t border-white/[0.06]">
95+
<div className="flex items-end justify-between mb-3">
96+
<span className="text-sm text-zinc-500">Functions indexed</span>
97+
<span className="text-3xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-violet-400">
10998
{(repo.file_count || 0).toLocaleString()}
11099
</span>
111100
</div>
112101

113-
{/* Progress bar */}
114-
{totalFunctions > 0 && (
102+
{totalFunctions > 0 && repo.file_count > 0 && (
115103
<div className="space-y-2">
116-
<div className="h-1.5 bg-white/5 rounded-full overflow-hidden">
104+
<div className="h-1 bg-white/[0.06] rounded-full overflow-hidden">
117105
<motion.div
118106
initial={{ width: 0 }}
119107
animate={{ width: `${percentage}%` }}
120-
transition={{ duration: 1, ease: "easeOut", delay: 0.3 }}
121-
className="h-full bg-gradient-to-r from-blue-500 to-purple-500 rounded-full"
108+
transition={{ duration: 0.8, ease: "easeOut", delay: 0.2 }}
109+
className="h-full bg-gradient-to-r from-blue-500 to-violet-500 rounded-full"
122110
/>
123111
</div>
124-
<p className="text-xs text-gray-500">{percentage}% of total indexed functions</p>
112+
<p className="text-xs text-zinc-600">{percentage}% of total</p>
125113
</div>
126114
)}
127115
</div>
128-
129-
{/* Hover CTA */}
130-
<div className="absolute bottom-6 right-6 opacity-0 group-hover:opacity-100 transition-all duration-200 transform translate-x-2 group-hover:translate-x-0">
131-
<span className="text-sm text-white flex items-center gap-2 bg-blue-500/20 px-4 py-2 rounded-xl border border-blue-500/30 backdrop-blur-sm">
132-
Explore <span></span>
133-
</span>
134-
</div>
135116
</div>
136117
</motion.button>
137118
)
138119
}
139120

140-
// Regular card - compact
141121
const RepoCard = ({ repo, index, onSelect }: {
142122
repo: Repository
143123
index: number
@@ -146,7 +126,6 @@ const RepoCard = ({ repo, index, onSelect }: {
146126
const cardRef = useRef<HTMLButtonElement>(null)
147127
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 })
148128
const [isHovering, setIsHovering] = useState(false)
149-
const isPending = repo.status === 'cloned' || repo.status === 'cloning'
150129

151130
const handleMouseMove = (e: React.MouseEvent) => {
152131
if (!cardRef.current) return
@@ -159,56 +138,48 @@ const RepoCard = ({ repo, index, onSelect }: {
159138
ref={cardRef}
160139
initial={{ opacity: 0, y: 20 }}
161140
animate={{ opacity: 1, y: 0 }}
162-
transition={{ delay: index * 0.08, duration: 0.4, ease: [0.25, 0.46, 0.45, 0.94] }}
163-
whileHover={{ y: -4, transition: { duration: 0.2 } }}
141+
transition={{ delay: index * 0.1, duration: 0.4 }}
142+
whileHover={{ y: -2 }}
164143
onClick={onSelect}
165144
onMouseMove={handleMouseMove}
166145
onMouseEnter={() => setIsHovering(true)}
167146
onMouseLeave={() => setIsHovering(false)}
168-
className={`group relative text-left rounded-2xl overflow-hidden h-full min-h-[140px]
169-
border transition-all duration-300
170-
focus:outline-none focus:ring-2 focus:ring-blue-500/40 p-5
171-
${isPending
172-
? 'bg-gradient-to-br from-[#0f1114] to-[#0d0f12] border-dashed border-white/[0.08] hover:border-blue-500/30'
173-
: 'bg-gradient-to-br from-[#0f1114] to-[#0d0f12] border-white/[0.08] hover:border-white/[0.15]'
174-
}
175-
hover:shadow-xl hover:shadow-blue-500/[0.06]`}
147+
className="group relative text-left rounded-2xl overflow-hidden w-full
148+
bg-[#111113] border border-white/[0.06] hover:border-white/[0.12]
149+
focus:outline-none focus:ring-2 focus:ring-blue-500/40 p-5"
176150
>
177-
{/* Mouse-following glow */}
151+
{/* Mouse glow */}
178152
{isHovering && (
179153
<div
180154
className="pointer-events-none absolute inset-0 transition-opacity duration-300"
181155
style={{
182-
background: `radial-gradient(300px circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(59, 130, 246, 0.1), transparent 50%)`,
156+
background: `radial-gradient(250px circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(59, 130, 246, 0.06), transparent 50%)`,
183157
}}
184158
/>
185159
)}
186160

187-
{/* Content */}
188-
<div className="relative flex flex-col h-full">
161+
<div className="relative">
189162
{/* Header */}
190-
<div className="flex items-start justify-between mb-3">
191-
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-blue-500/15 to-purple-500/15 border border-white/[0.08] flex items-center justify-center group-hover:scale-105 group-hover:border-blue-500/20 transition-all duration-300">
192-
<svg className="w-5 h-5 text-blue-400/80" fill="none" viewBox="0 0 24 24" stroke="currentColor">
163+
<div className="flex items-start justify-between mb-4">
164+
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-blue-500/10 to-violet-500/10 border border-white/[0.08] flex items-center justify-center">
165+
<svg className="w-5 h-5 text-blue-400/70" fill="none" viewBox="0 0 24 24" stroke="currentColor">
193166
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
194167
</svg>
195168
</div>
196169
<StatusBadge status={repo.status} />
197170
</div>
198171

199172
{/* Title */}
200-
<div className="flex-1">
201-
<h3 className="font-semibold text-white mb-0.5 truncate group-hover:text-blue-400 transition-colors duration-200">
202-
{repo.name}
203-
</h3>
204-
<p className="text-xs text-gray-500 font-mono">{repo.branch}</p>
205-
</div>
173+
<h3 className="font-semibold text-white mb-0.5 group-hover:text-blue-400 transition-colors">
174+
{repo.name}
175+
</h3>
176+
<p className="text-xs text-zinc-500 font-mono mb-4">{repo.branch}</p>
206177

207178
{/* Stats */}
208-
<div className="pt-3 mt-auto border-t border-white/[0.04]">
179+
<div className="pt-3 border-t border-white/[0.04]">
209180
<div className="flex items-center justify-between">
210-
<span className="text-xs text-gray-500">Functions</span>
211-
<span className={`text-lg font-bold font-mono ${isPending ? 'text-gray-500' : 'text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-400'}`}>
181+
<span className="text-xs text-zinc-500">Functions</span>
182+
<span className="text-lg font-semibold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-violet-400">
212183
{(repo.file_count || 0).toLocaleString()}
213184
</span>
214185
</div>
@@ -219,54 +190,43 @@ const RepoCard = ({ repo, index, onSelect }: {
219190
}
220191

221192
export function RepoList({ repos, selectedRepo, onSelect, loading }: RepoListProps) {
222-
if (loading) {
223-
return <RepoGridSkeleton count={3} />
224-
}
193+
if (loading) return <RepoGridSkeleton count={3} />
225194

226195
if (repos.length === 0) {
227196
return (
228197
<motion.div
229-
initial={{ opacity: 0, scale: 0.95 }}
230-
animate={{ opacity: 1, scale: 1 }}
231-
className="bg-gradient-to-br from-[#111113] to-[#0a0a0c] border border-white/[0.06] rounded-2xl p-16 text-center"
198+
initial={{ opacity: 0 }}
199+
animate={{ opacity: 1 }}
200+
className="bg-[#111113] border border-white/[0.06] rounded-2xl p-16 text-center"
232201
>
233-
<div className="w-20 h-20 mx-auto mb-6 rounded-2xl bg-gradient-to-br from-blue-500/10 to-purple-500/10 border border-white/[0.06] flex items-center justify-center">
234-
<span className="text-4xl">📦</span>
202+
<div className="w-16 h-16 mx-auto mb-4 rounded-xl bg-gradient-to-br from-blue-500/10 to-violet-500/10 border border-white/[0.06] flex items-center justify-center">
203+
<span className="text-2xl">📦</span>
235204
</div>
236205
<h3 className="text-lg font-semibold mb-2 text-white">No repositories yet</h3>
237-
<p className="text-sm text-gray-400 max-w-sm mx-auto">
238-
Add your first repository to start searching code semantically with AI
206+
<p className="text-sm text-zinc-500 max-w-xs mx-auto">
207+
Add your first repository to start searching code with AI
239208
</p>
240209
</motion.div>
241210
)
242211
}
243212

244-
// Sort repos: indexed first, then by file_count descending
213+
// Sort: indexed first, then by function count
245214
const sortedRepos = useMemo(() => {
246215
return [...repos].sort((a, b) => {
247-
// Indexed repos first
248216
if (a.status === 'indexed' && b.status !== 'indexed') return -1
249217
if (b.status === 'indexed' && a.status !== 'indexed') return 1
250-
// Then by file count
251218
return (b.file_count || 0) - (a.file_count || 0)
252219
})
253220
}, [repos])
254221

255222
const totalFunctions = repos.reduce((acc, r) => acc + (r.file_count || 0), 0)
256223
const [featured, ...rest] = sortedRepos
257224

258-
// Determine grid layout based on repo count
259-
const gridClass = rest.length === 0
260-
? 'grid-cols-1'
261-
: rest.length === 1
262-
? 'grid-cols-1 lg:grid-cols-2'
263-
: 'grid-cols-1 lg:grid-cols-3'
264-
265225
return (
266-
<div className={`grid ${gridClass} gap-4`}>
267-
{/* Featured repo - spans full width on mobile, left column on desktop */}
226+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
227+
{/* Featured - spans 2 rows */}
268228
{featured && (
269-
<div className={rest.length > 0 ? 'lg:row-span-2' : ''}>
229+
<div className="lg:row-span-2 min-h-[320px]">
270230
<FeaturedRepoCard
271231
repo={featured}
272232
totalFunctions={totalFunctions}
@@ -275,7 +235,7 @@ export function RepoList({ repos, selectedRepo, onSelect, loading }: RepoListPro
275235
</div>
276236
)}
277237

278-
{/* Rest of repos */}
238+
{/* Rest */}
279239
{rest.map((repo, index) => (
280240
<RepoCard
281241
key={repo.id}

0 commit comments

Comments
 (0)