Skip to content

Commit 6de93c6

Browse files
authored
Merge pull request #22 from O2sa/feat/ui-ux-enhancements
feat: enhance the ui/ux
2 parents aa20076 + f326946 commit 6de93c6

21 files changed

Lines changed: 2827 additions & 618 deletions

app/api/compare/route.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ export async function GET(request: Request) {
2323

2424
return {
2525
username,
26-
repoScore: score.repoScore,
27-
prScore: score.prScore,
28-
contributionScore: score.contributionScore,
29-
finalScore: score.finalScore,
26+
repoScore: Math.round(score.repoScore),
27+
prScore: Math.round(score.prScore),
28+
contributionScore: Math.round(score.contributionScore),
29+
finalScore: Math.round(score.finalScore),
3030
topRepos: score.topRepos,
3131
topPullRequests: score.topPullRequests,
3232
};

app/globals.css

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,11 @@ body {
3131
.card {
3232
@apply bg-white/90 shadow-card rounded-2xl border border-slate-100 backdrop-blur;
3333
transition: transform 180ms ease, box-shadow 180ms ease;
34-
}
34+
box-shadow: 0 18px 48px rgba(15, 23, 42, 0.12);
3535

36-
.card:hover {
37-
transform: translateY(-2px);
38-
box-shadow: 0 18px 48px rgba(15, 23, 42, 0.12);
3936
}
4037

38+
4139
.dark .card {
4240
@apply bg-slate-900/80 border-slate-800;
4341
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);

app/layout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "./globals.css";
22
import type { ReactNode } from "react";
3+
import Providers from "./providers";
34

45
export const metadata = {
56
title: "DevImpact",
@@ -10,7 +11,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
1011
return (
1112
<html>
1213
<body>
13-
{children}
14+
<Providers>{children}</Providers>
1415
</body>
1516
</html>
1617
);

app/page.tsx

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,7 @@ import { useMemo, useState } from "react";
44
import { CompareForm } from "../components/compare-form";
55
import { ResultDashboard } from "../components/result-dashboard";
66
import { DashboardSkeleton } from "../components/skeletons";
7-
8-
9-
type UserResult = {
10-
username: string;
11-
repoScore: number;
12-
prScore: number;
13-
contributionScore: number;
14-
finalScore: number;
15-
topRepos: { name?: string; stars?: number; score?: number }[];
16-
topPullRequests: { repo?: string; stars?: number; score?: number }[];
17-
insights?: string[];
18-
};
7+
import { UserResult } from "@/types/user-result";
198

209
type ApiResponse = {
2110
success: boolean;
@@ -26,9 +15,10 @@ type ApiResponse = {
2615
export default function HomePage() {
2716
const [loading, setLoading] = useState(false);
2817
const [error, setError] = useState<string | null>(null);
29-
const [data, setData] = useState<{ user1: UserResult; user2: UserResult } | null>(
30-
null
31-
);
18+
const [data, setData] = useState<{
19+
user1: UserResult;
20+
user2: UserResult;
21+
} | null>(null);
3222

3323
const handleCompare = async (u1: string, u2: string) => {
3424
setLoading(true);
@@ -43,7 +33,19 @@ export default function HomePage() {
4333
if (!body.success || !body.users || body.users.length < 2) {
4434
throw new Error(body.error || "Comparison failed");
4535
}
46-
setData({ user1: body.users[0], user2: body.users[1] });
36+
if (body.users[0].finalScore > body.users[1].finalScore) {
37+
setData({
38+
user1: { ...body.users[0], isWinner: true },
39+
user2: body.users[1],
40+
});
41+
} else if (body.users[1].finalScore > body.users[0].finalScore) {
42+
setData({
43+
user1: body.users[0],
44+
user2: { ...body.users[1], isWinner: true },
45+
});
46+
} else {
47+
setData({ user1: body.users[0], user2: body.users[1] });
48+
}
4749
} catch (err: any) {
4850
setError(err.message || "Failed to fetch");
4951
} finally {
@@ -53,27 +55,37 @@ export default function HomePage() {
5355

5456
const skeleton = useMemo(() => <DashboardSkeleton />, []);
5557

58+
const reset = () => {
59+
setData(null);
60+
setError(null);
61+
};
62+
const swapUsers = () => {
63+
if (!data) return;
64+
setData((d) => ({ user1: d!.user2, user2: d!.user1 }));
65+
console.log("Swapped users", data);
66+
};
5667
return (
5768
<main className="min-h-screen">
58-
<div className="max-w-6xl mx-auto px-4 py-10 space-y-6" >
59-
<div className="flex items-center justify-between gap-4">
60-
<div className="flex items-center gap-3">
61-
<div>
62-
<p className="text-sm text-slate-500">GitHub Developer Compare</p>
63-
<h1 className="text-3xl font-semibold text-slate-900">
64-
Compare two developers
65-
</h1>
66-
</div>
69+
{" "}
70+
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
71+
<div className="container flex h-16 max-w-7xl items-center justify-between m-auto px-4">
72+
<div className="flex items-center gap-2 font-bold text-xl">
73+
<span className="bg-gradient-to-r from-primary to-primary/60 bg-clip-text text-transparent">
74+
DevImpact
75+
</span>
6776
</div>
68-
77+
6978
</div>
79+
</header>
80+
<div className="max-w-6xl mx-auto px-4 py-10 space-y-6">
81+
<CompareForm
82+
onSubmit={handleCompare}
83+
loading={loading}
84+
reset={reset}
85+
swapUsers={swapUsers}
86+
data={data}
87+
/>
7088

71-
72-
<CompareForm
73-
onSubmit={handleCompare}
74-
loading={loading}
75-
/>
76-
7789
{loading && skeleton}
7890
{error && (
7991
<div className="card p-4 text-sm text-red-600 bg-red-50 border border-red-100">

app/providers.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"use client";
2+
3+
import { TooltipProvider } from "@/components/ui/tooltip";
4+
5+
export default function Providers({ children }: { children: React.ReactNode }) {
6+
return (
7+
<TooltipProvider>
8+
{children}
9+
</TooltipProvider>
10+
);
11+
}

components/breakdown-bars.tsx

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
import { UserResult } from "@/types/user-result";
2+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card";
3+
import { Progress } from "./ui/progress";
4+
5+
16
type Props = {
2-
user: any;
7+
user1: UserResult;
8+
user2: UserResult;
39
};
410

511
const items = [
@@ -8,39 +14,46 @@ const items = [
814
{ key: "contributionScore", label: "Activity", color: "bg-emerald-500" },
915
];
1016

11-
export function BreakdownBars({ user }: Props) {
12-
const max = Math.max(
13-
user.repoScore ?? 0,
14-
user.prScore ?? 0,
15-
user.contributionScore ?? 0,
16-
1
17-
);
17+
export function BreakdownBars({ user1, user2 }: Props) {
18+
const getMaxScore = (score1: number, score2: number) => Math.max(score1, score2, 1)
19+
1820

1921
return (
20-
<div className="card p-4">
21-
<h3 className="text-sm font-semibold text-slate-800 mb-3">
22-
Breakdown — {user.username}
23-
</h3>
24-
<div className="flex flex-col gap-3">
25-
{items.map((item) => {
26-
const val = user[item.key] ?? 0;
27-
const pct = Math.min(100, Math.round((val / max) * 100));
28-
return (
29-
<div key={item.key} className="space-y-1">
30-
<div className="flex justify-between text-xs text-slate-600">
31-
<span>{item.label}</span>
32-
<span>{val.toFixed(2)}</span>
33-
</div>
34-
<div className="h-2 rounded-full bg-slate-100">
35-
<div
36-
className={`h-full rounded-full ${item.color}`}
37-
style={{ width: `${pct}%` }}
38-
/>
39-
</div>
40-
</div>
41-
);
42-
})}
43-
</div>
44-
</div>
22+
<Card>
23+
<CardHeader>
24+
<CardTitle>Detailed Breakdown</CardTitle>
25+
<CardDescription>Progress bars showing relative performance</CardDescription>
26+
</CardHeader>
27+
<CardContent className="space-y-6">
28+
{["repoScore", "prScore", "contributionScore"].map((metric) => {
29+
const user1Value = user1[metric as keyof UserResult] as number
30+
const user2Value = user2[metric as keyof UserResult] as number
31+
const maxVal = getMaxScore(user1Value, user2Value)
32+
const metricLabel = metric === "repoScore" ? "Repository Score" : metric === "prScore" ? "Pull Request Score" : "Contribution Score"
33+
return (
34+
<div key={metric} className="space-y-2 pe-2">
35+
<div className="flex justify-between text-sm">
36+
<span>{metricLabel}</span>
37+
<span className="text-muted-foreground">
38+
{user1.username}: {user1Value} | {user2.username}: {user2Value}
39+
</span>
40+
</div>
41+
<div className="space-y-1 ">
42+
<div className="flex items-center gap-2">
43+
<span className="text-xs w-24 truncate">{user1.username}</span>
44+
<Progress value={(user1Value / maxVal) * 100} className="flex-1 h-2" />
45+
<span className="text-xs w-8">{user1Value}</span>
46+
</div>
47+
<div className="flex items-center gap-2">
48+
<span className="text-xs w-24 truncate">{user2.username}</span>
49+
<Progress value={(user2Value / maxVal) * 100} className="flex-1 h-2" />
50+
<span className="text-xs w-8">{user2Value}</span>
51+
</div>
52+
</div>
53+
</div>
54+
)
55+
})}
56+
</CardContent>
57+
</Card>
4558
);
4659
}

0 commit comments

Comments
 (0)