Skip to content

Commit 027f0ad

Browse files
committed
feat(dashboard): equal grid layout - clean and scalable
- Removed bento/featured card logic - All repos same size in 3-column grid - Sorted: indexed first, then by function count - Cleaner code, 143 lines vs 250 - Scales better for 10+ repos
1 parent 28cf75b commit 027f0ad

1 file changed

Lines changed: 22 additions & 130 deletions

File tree

frontend/src/components/RepoList.tsx

Lines changed: 22 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -13,112 +13,19 @@ interface RepoListProps {
1313
const StatusBadge = ({ status }: { status: string }) => {
1414
const isIndexed = status === 'indexed'
1515

16-
if (isIndexed) {
17-
return (
18-
<span className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-full bg-blue-500/10 text-blue-400 border border-blue-500/20">
19-
<span className="w-1.5 h-1.5 rounded-full bg-blue-400" />
20-
Indexed
21-
</span>
22-
)
23-
}
24-
25-
return (
26-
<span className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-full bg-zinc-800 text-zinc-400 border border-zinc-700">
27-
<span className="w-1.5 h-1.5 rounded-full bg-zinc-500 animate-pulse" />
28-
Pending
29-
</span>
30-
)
31-
}
32-
33-
// Featured card - tall, prominent
34-
const FeaturedRepoCard = ({ repo, totalFunctions, onSelect }: {
35-
repo: Repository
36-
totalFunctions: number
37-
onSelect: () => void
38-
}) => {
39-
const cardRef = useRef<HTMLButtonElement>(null)
40-
const [mousePos, setMousePos] = useState({ x: 0, y: 0 })
41-
const [hovering, setHovering] = useState(false)
42-
const pct = totalFunctions > 0 ? Math.round((repo.file_count || 0) / totalFunctions * 100) : 0
43-
4416
return (
45-
<motion.button
46-
ref={cardRef}
47-
initial={{ opacity: 0, y: 20 }}
48-
animate={{ opacity: 1, y: 0 }}
49-
whileHover={{ y: -3 }}
50-
onClick={onSelect}
51-
onMouseMove={(e) => {
52-
if (!cardRef.current) return
53-
const rect = cardRef.current.getBoundingClientRect()
54-
setMousePos({ x: e.clientX - rect.left, y: e.clientY - rect.top })
55-
}}
56-
onMouseEnter={() => setHovering(true)}
57-
onMouseLeave={() => setHovering(false)}
58-
className="group relative text-left rounded-2xl overflow-hidden w-full h-full min-h-[300px]
59-
bg-[#111113] border border-white/[0.06] hover:border-blue-500/40
60-
focus:outline-none focus:ring-2 focus:ring-blue-500/50 p-6 transition-colors"
17+
<span className={`inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-full border
18+
${isIndexed
19+
? 'bg-blue-500/10 text-blue-400 border-blue-500/20'
20+
: 'bg-zinc-800 text-zinc-400 border-zinc-700'
21+
}`}
6122
>
62-
{/* Mouse glow - BLUE only */}
63-
{hovering && (
64-
<div
65-
className="pointer-events-none absolute inset-0"
66-
style={{
67-
background: `radial-gradient(500px circle at ${mousePos.x}px ${mousePos.y}px, rgba(37, 99, 235, 0.1), transparent 50%)`,
68-
}}
69-
/>
70-
)}
71-
72-
{/* Top accent - solid blue */}
73-
<div className="absolute top-0 left-0 right-0 h-[2px] bg-blue-500" />
74-
75-
<div className="relative flex flex-col h-full">
76-
{/* Header */}
77-
<div className="flex items-start justify-between mb-6">
78-
<div className="w-14 h-14 rounded-xl bg-blue-500/10 border border-blue-500/20 flex items-center justify-center">
79-
<svg className="w-7 h-7 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
80-
<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" />
81-
</svg>
82-
</div>
83-
<StatusBadge status={repo.status} />
84-
</div>
85-
86-
{/* Title */}
87-
<h3 className="text-2xl font-semibold text-white mb-1 group-hover:text-blue-400 transition-colors">
88-
{repo.name}
89-
</h3>
90-
<p className="text-sm text-zinc-500 font-mono mb-auto">{repo.branch}</p>
91-
92-
{/* Stats */}
93-
<div className="pt-6 mt-6 border-t border-white/[0.06]">
94-
<div className="flex items-end justify-between mb-3">
95-
<span className="text-sm text-zinc-500">Functions indexed</span>
96-
<span className="text-4xl font-bold text-blue-500">
97-
{(repo.file_count || 0).toLocaleString()}
98-
</span>
99-
</div>
100-
101-
{/* Progress bar */}
102-
{totalFunctions > 0 && repo.file_count > 0 && (
103-
<div className="space-y-2">
104-
<div className="h-1.5 bg-zinc-800 rounded-full overflow-hidden">
105-
<motion.div
106-
initial={{ width: 0 }}
107-
animate={{ width: `${pct}%` }}
108-
transition={{ duration: 0.8, ease: "easeOut", delay: 0.3 }}
109-
className="h-full bg-blue-500 rounded-full"
110-
/>
111-
</div>
112-
<p className="text-xs text-zinc-600">{pct}% of total indexed</p>
113-
</div>
114-
)}
115-
</div>
116-
</div>
117-
</motion.button>
23+
<span className={`w-1.5 h-1.5 rounded-full ${isIndexed ? 'bg-blue-400' : 'bg-zinc-500 animate-pulse'}`} />
24+
{isIndexed ? 'Indexed' : 'Pending'}
25+
</span>
11826
)
11927
}
12028

121-
// Regular card - compact
12229
const RepoCard = ({ repo, index, onSelect }: {
12330
repo: Repository
12431
index: number
@@ -133,7 +40,7 @@ const RepoCard = ({ repo, index, onSelect }: {
13340
ref={cardRef}
13441
initial={{ opacity: 0, y: 20 }}
13542
animate={{ opacity: 1, y: 0 }}
136-
transition={{ delay: index * 0.1 }}
43+
transition={{ delay: index * 0.05, duration: 0.3 }}
13744
whileHover={{ y: -3 }}
13845
onClick={onSelect}
13946
onMouseMove={(e) => {
@@ -143,8 +50,8 @@ const RepoCard = ({ repo, index, onSelect }: {
14350
}}
14451
onMouseEnter={() => setHovering(true)}
14552
onMouseLeave={() => setHovering(false)}
146-
className="group relative text-left rounded-2xl overflow-hidden w-full h-full
147-
bg-[#111113] border border-white/[0.06] hover:border-white/[0.15]
53+
className="group relative text-left rounded-2xl overflow-hidden w-full
54+
bg-[#111113] border border-white/[0.06] hover:border-blue-500/40
14855
focus:outline-none focus:ring-2 focus:ring-blue-500/50 p-5 transition-colors"
14956
>
15057
{/* Mouse glow */}
@@ -157,28 +64,28 @@ const RepoCard = ({ repo, index, onSelect }: {
15764
/>
15865
)}
15966

160-
<div className="relative flex flex-col h-full">
67+
<div className="relative">
16168
{/* Header */}
16269
<div className="flex items-start justify-between mb-4">
163-
<div className="w-10 h-10 rounded-lg bg-zinc-800 border border-zinc-700 flex items-center justify-center">
164-
<svg className="w-5 h-5 text-zinc-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
70+
<div className="w-11 h-11 rounded-xl bg-blue-500/10 border border-blue-500/20 flex items-center justify-center group-hover:bg-blue-500/15 transition-colors">
71+
<svg className="w-5 h-5 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
16572
<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" />
16673
</svg>
16774
</div>
16875
<StatusBadge status={repo.status} />
16976
</div>
17077

17178
{/* Title */}
172-
<h3 className="font-semibold text-white mb-0.5 group-hover:text-blue-400 transition-colors">
79+
<h3 className="text-lg font-semibold text-white mb-0.5 group-hover:text-blue-400 transition-colors">
17380
{repo.name}
17481
</h3>
175-
<p className="text-xs text-zinc-500 font-mono mb-auto">{repo.branch}</p>
82+
<p className="text-xs text-zinc-500 font-mono mb-5">{repo.branch}</p>
17683

17784
{/* Stats */}
178-
<div className="pt-4 mt-4 border-t border-white/[0.04]">
85+
<div className="pt-4 border-t border-white/[0.06]">
17986
<div className="flex items-center justify-between">
180-
<span className="text-xs text-zinc-500">Functions</span>
181-
<span className="text-xl font-bold text-blue-500">
87+
<span className="text-sm text-zinc-500">Functions</span>
88+
<span className="text-2xl font-bold text-blue-500">
18289
{(repo.file_count || 0).toLocaleString()}
18390
</span>
18491
</div>
@@ -220,28 +127,13 @@ export function RepoList({ repos, selectedRepo, onSelect, loading }: RepoListPro
220127
})
221128
}, [repos])
222129

223-
const totalFunctions = repos.reduce((acc, r) => acc + (r.file_count || 0), 0)
224-
const [featured, ...rest] = sortedRepos
225-
226130
return (
227-
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 auto-rows-fr">
228-
{/* Featured - spans 2 rows on desktop */}
229-
{featured && (
230-
<div className="lg:row-span-2">
231-
<FeaturedRepoCard
232-
repo={featured}
233-
totalFunctions={totalFunctions}
234-
onSelect={() => onSelect(featured.id)}
235-
/>
236-
</div>
237-
)}
238-
239-
{/* Other repos */}
240-
{rest.map((repo, index) => (
131+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
132+
{sortedRepos.map((repo, index) => (
241133
<RepoCard
242134
key={repo.id}
243135
repo={repo}
244-
index={index + 1}
136+
index={index}
245137
onSelect={() => onSelect(repo.id)}
246138
/>
247139
))}

0 commit comments

Comments
 (0)