Skip to content

Commit 3c80b8a

Browse files
msoginclaude
andcommitted
feat: clickable Glooker header + multi-select developer filter
Glooker title navigates home from any page. Developer table on the report page now has a multi-select filter with autocomplete by name or login, avatar chips, and clear-all button. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a354b8a commit 3c80b8a

3 files changed

Lines changed: 96 additions & 22 deletions

File tree

src/app/page.tsx

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ export default function Home() {
8686
const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);
8787
const logsEndRef = useRef<HTMLDivElement>(null);
8888
const commitCache = useRef<Map<string, any[]>>(new Map());
89+
const [filterLogins, setFilterLogins] = useState<Set<string>>(new Set());
90+
const [filterQuery, setFilterQuery] = useState('');
91+
const [filterOpen, setFilterOpen] = useState(false);
8992
const generationRef = useRef(0);
9093
const lastCompletedDevsRef = useRef(0);
9194

@@ -519,7 +522,7 @@ export default function Home() {
519522
<div className="max-w-7xl mx-auto px-4 py-8">
520523
{/* Header */}
521524
<div className="mb-8">
522-
<h1 className="text-3xl font-bold tracking-tight text-white">Glooker</h1>
525+
<h1 className="text-3xl font-bold tracking-tight text-white cursor-pointer hover:text-blue-400 transition-colors" onClick={() => { setActiveReport(null); setDevelopers([]); setProgress(null); setRunning(false); stopPolling(); }}>Glooker</h1>
523526
<p className="text-gray-400 mt-1">GitHub org developer impact analytics</p>
524527
</div>
525528

@@ -1028,8 +1031,72 @@ export default function Home() {
10281031
</div>
10291032
)}
10301033

1031-
{/* Developer table */}
1034+
{/* User filter */}
10321035
{developers.length > 0 && (
1036+
<div className="mb-3 relative">
1037+
<div className="flex items-center gap-2 flex-wrap">
1038+
{[...filterLogins].map(login => {
1039+
const dev = developers.find(d => d.github_login === login);
1040+
return (
1041+
<span key={login} className="inline-flex items-center gap-1.5 bg-blue-600/20 text-blue-300 text-xs font-medium px-2.5 py-1 rounded-lg border border-blue-500/30">
1042+
{dev?.avatar_url && <img src={dev.avatar_url} alt="" className="w-4 h-4 rounded-full" />}
1043+
{dev?.github_name || login}
1044+
<button onClick={() => setFilterLogins(prev => { const n = new Set(prev); n.delete(login); return n; })} className="text-blue-400 hover:text-white ml-0.5">&times;</button>
1045+
</span>
1046+
);
1047+
})}
1048+
<div className="relative">
1049+
<input
1050+
type="text"
1051+
value={filterQuery}
1052+
onChange={e => { setFilterQuery(e.target.value); setFilterOpen(true); }}
1053+
onFocus={() => setFilterOpen(true)}
1054+
placeholder={filterLogins.size > 0 ? 'Add more...' : 'Filter by developer...'}
1055+
className="bg-gray-800 border border-gray-700 rounded-lg px-3 py-1.5 text-sm text-white placeholder-gray-600 focus:outline-none focus:border-blue-500 w-48"
1056+
/>
1057+
{filterOpen && filterQuery.length > 0 && (() => {
1058+
const q = filterQuery.toLowerCase();
1059+
const matches = developers.filter(d =>
1060+
!filterLogins.has(d.github_login) && (
1061+
d.github_login.toLowerCase().includes(q) ||
1062+
(d.github_name || '').toLowerCase().includes(q)
1063+
)
1064+
).slice(0, 8);
1065+
if (matches.length === 0) return null;
1066+
return (
1067+
<div className="absolute z-40 top-full mt-1 left-0 w-64 bg-gray-800 border border-gray-700 rounded-lg shadow-xl overflow-hidden">
1068+
{matches.map(d => (
1069+
<button
1070+
key={d.github_login}
1071+
className="w-full flex items-center gap-2.5 px-3 py-2 text-left text-sm hover:bg-gray-700 transition-colors"
1072+
onClick={() => {
1073+
setFilterLogins(prev => new Set(prev).add(d.github_login));
1074+
setFilterQuery('');
1075+
setFilterOpen(false);
1076+
}}
1077+
>
1078+
{d.avatar_url && <img src={d.avatar_url} alt="" className="w-5 h-5 rounded-full" />}
1079+
<div>
1080+
<span className="text-white">{d.github_name || d.github_login}</span>
1081+
{d.github_name && <span className="text-gray-500 ml-1.5">@{d.github_login}</span>}
1082+
</div>
1083+
</button>
1084+
))}
1085+
</div>
1086+
);
1087+
})()}
1088+
</div>
1089+
{filterLogins.size > 0 && (
1090+
<button onClick={() => setFilterLogins(new Set())} className="text-xs text-gray-600 hover:text-gray-400">Clear all</button>
1091+
)}
1092+
</div>
1093+
</div>
1094+
)}
1095+
1096+
{/* Developer table */}
1097+
{(() => {
1098+
const filteredDevs = filterLogins.size > 0 ? developers.filter(d => filterLogins.has(d.github_login)) : developers;
1099+
return filteredDevs.length > 0 && (
10331100
<div className="bg-gray-900 rounded-xl overflow-hidden">
10341101
<table className="w-full text-sm table-fixed">
10351102
<thead>
@@ -1046,7 +1113,7 @@ export default function Home() {
10461113
</tr>
10471114
</thead>
10481115
<tbody>
1049-
{developers.map((dev, i) => (
1116+
{filteredDevs.map((dev, i) => (
10501117
<tr
10511118
key={dev.github_login}
10521119
className="border-b border-gray-800/50 hover:bg-gray-800/30 transition-colors cursor-pointer"
@@ -1103,7 +1170,8 @@ export default function Home() {
11031170
</tbody>
11041171
</table>
11051172
</div>
1106-
)}
1173+
);
1174+
})()}
11071175

11081176
{activeReport && developers.length === 0 && activeReport.status === 'completed' && (
11091177
<div className="text-center text-gray-500 py-16">

src/app/report/[id]/dev/[login]/page.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,18 @@ export default function DevDetailPage() {
142142
return (
143143
<div className="max-w-6xl mx-auto px-4 py-8">
144144
{/* Back link */}
145-
<button
146-
onClick={() => router.back()}
147-
className="text-sm text-gray-500 hover:text-gray-300 mb-6 flex items-center gap-1.5 no-print"
148-
>
149-
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
150-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
151-
</svg>
152-
Back to report
153-
</button>
145+
<div className="flex items-center justify-between mb-6 no-print">
146+
<button
147+
onClick={() => router.back()}
148+
className="text-sm text-gray-500 hover:text-gray-300 flex items-center gap-1.5"
149+
>
150+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
151+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
152+
</svg>
153+
Back to report
154+
</button>
155+
<span className="text-xl font-bold text-white cursor-pointer hover:text-blue-400 transition-colors" onClick={() => router.push('/')}>Glooker</span>
156+
</div>
154157

155158
{/* Header */}
156159
<div className="bg-gray-900 rounded-xl p-6 mb-6">

src/app/report/[id]/org/page.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,18 @@ export default function OrgDetailPage() {
102102
return (
103103
<div className="max-w-6xl mx-auto px-4 py-8">
104104
{/* Back link */}
105-
<button
106-
onClick={() => router.back()}
107-
className="text-sm text-gray-500 hover:text-gray-300 mb-6 flex items-center gap-1.5 no-print"
108-
>
109-
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
110-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
111-
</svg>
112-
Back to report
113-
</button>
105+
<div className="flex items-center justify-between mb-6 no-print">
106+
<button
107+
onClick={() => router.back()}
108+
className="text-sm text-gray-500 hover:text-gray-300 flex items-center gap-1.5"
109+
>
110+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
111+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
112+
</svg>
113+
Back to report
114+
</button>
115+
<span className="text-xl font-bold text-white cursor-pointer hover:text-blue-400 transition-colors" onClick={() => router.push('/')}>Glooker</span>
116+
</div>
114117

115118
{/* Header */}
116119
<div className="bg-gray-900 rounded-xl p-6 mb-6">

0 commit comments

Comments
 (0)