Skip to content

Commit 12c969e

Browse files
committed
feat: display friendly error messages on API failure
closes #34 Add user-friendly error messages when GitHub API calls fail: - ErrorMessage component with error type detection (rate limit/not found/network) - Retry button for transient errors - Replaces generic red error box with proper Alert component Signed-off-by: Ai-chan-0411 <ai-chan-0411@users.noreply.github.com>
1 parent fd084c5 commit 12c969e

3 files changed

Lines changed: 75 additions & 10 deletions

File tree

app/api/compare/route.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,15 @@ export async function GET(request: Request) {
3636
return NextResponse.json({ success: true, users: results });
3737
} catch (error: any) {
3838
console.error("GitHub score error:", error);
39-
const message =
40-
error?.message === "User not found"
41-
? "GitHub user not found"
42-
: "Failed to calculate score";
39+
let message = "Failed to calculate score";
40+
if (error?.message === "User not found") {
41+
message = "User not found";
42+
} else if (
43+
error?.message?.toLowerCase().includes("rate limit") ||
44+
error?.status === 403
45+
) {
46+
message = "GitHub API rate limit exceeded. Please try again later.";
47+
}
4348
return NextResponse.json(
4449
{ success: false, error: message },
4550
{ status: 500 }

app/page.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { useMemo, useState } from "react";
44
import { CompareForm } from "../components/compare-form";
5+
import { ErrorMessage } from "../components/error-message";
56
import { ResultDashboard } from "../components/result-dashboard";
67
import { DashboardSkeleton } from "../components/skeletons";
78
import { UserResult } from "@/types/user-result";
@@ -65,7 +66,7 @@ export default function HomePage() {
6566
console.log("Swapped users", data);
6667
};
6768
return (
68-
<main className="min-h-screen">
69+
<main className="min-h-screen flex flex-col">
6970
{" "}
7071
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
7172
<div className="container flex h-16 max-w-7xl items-center justify-between m-auto px-4">
@@ -74,10 +75,10 @@ export default function HomePage() {
7475
DevImpact
7576
</span>
7677
</div>
77-
78+
7879
</div>
7980
</header>
80-
<div className="max-w-6xl mx-auto px-4 py-10 space-y-6">
81+
<div className="flex-1 max-w-6xl mx-auto px-4 py-10 space-y-6 w-full">
8182
<CompareForm
8283
onSubmit={handleCompare}
8384
loading={loading}
@@ -88,9 +89,7 @@ export default function HomePage() {
8889

8990
{loading && skeleton}
9091
{error && (
91-
<div className="card p-4 text-sm text-red-600 bg-red-50 border border-red-100">
92-
{error}
93-
</div>
92+
<ErrorMessage error={error} onRetry={() => setError(null)} />
9493
)}
9594
{data && <ResultDashboard user1={data.user1} user2={data.user2} />}
9695
{!loading && !error && !data && (

components/error-message.tsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { AlertCircle } from "lucide-react";
2+
import { Alert, AlertDescription, AlertTitle } from "./ui/alert";
3+
4+
type ErrorMessageProps = {
5+
error: string;
6+
onRetry?: () => void;
7+
};
8+
9+
function getErrorDetails(error: string): {
10+
title: string;
11+
description: string;
12+
} {
13+
const lower = error.toLowerCase();
14+
if (lower.includes("not found") || lower.includes("user not found")) {
15+
return {
16+
title: "User not found",
17+
description:
18+
"The GitHub username you entered does not exist. Please check the spelling and try again.",
19+
};
20+
}
21+
if (lower.includes("rate limit") || lower.includes("rate_limit")) {
22+
return {
23+
title: "Rate limit exceeded",
24+
description:
25+
"GitHub API rate limit has been reached. Please wait a few minutes and try again.",
26+
};
27+
}
28+
if (lower.includes("network") || lower.includes("failed to fetch")) {
29+
return {
30+
title: "Network error",
31+
description:
32+
"Could not connect to the server. Check your internet connection and try again.",
33+
};
34+
}
35+
return {
36+
title: "Something went wrong",
37+
description: error || "An unexpected error occurred. Please try again.",
38+
};
39+
}
40+
41+
export function ErrorMessage({ error, onRetry }: ErrorMessageProps) {
42+
const { title, description } = getErrorDetails(error);
43+
44+
return (
45+
<Alert variant="destructive">
46+
<AlertCircle className="h-4 w-4" />
47+
<AlertTitle>{title}</AlertTitle>
48+
<AlertDescription>
49+
<p>{description}</p>
50+
{onRetry && (
51+
<button
52+
onClick={onRetry}
53+
className="mt-2 text-sm underline underline-offset-2 hover:no-underline"
54+
>
55+
Try again
56+
</button>
57+
)}
58+
</AlertDescription>
59+
</Alert>
60+
);
61+
}

0 commit comments

Comments
 (0)