diff --git a/apps/web/src/lib/api.ts b/apps/web/src/lib/api.ts index be6afdd8..109cf587 100644 --- a/apps/web/src/lib/api.ts +++ b/apps/web/src/lib/api.ts @@ -1,16 +1,35 @@ const API_BASE_URL = import.meta.env.VITE_API_URL ?? 'http://localhost:3000'; -export async function apiFetch(endpoint: string): Promise { - const response = await fetch(`${API_BASE_URL}${endpoint}`, { - headers: { 'Content-Type': 'application/json' }, - }); +export class ApiError extends Error { + status?: number; - if (!response.ok) { - const error = await response.json().catch(() => ({})); - throw new Error( - (error as Record)?.message ?? `Request failed: ${response.status}` - ); + constructor(message: string, status?: number) { + super(message); + this.name = 'ApiError'; + this.status = status; } - - return response.json() as Promise; } + +export async function apiFetch(endpoint: string): Promise { + try { + const response = await fetch(`${API_BASE_URL}${endpoint}`, { + headers: { 'Content-Type': 'application/json' }, + }); + + if (!response.ok) { + const error = await response.json().catch(() => ({})); + throw new ApiError( + (error as Record)?.message ?? `Request failed: ${response.status}`, + response.status + ); + } + + return response.json() as Promise; + } catch (error) { + if (error instanceof ApiError) { + throw error; + } + + throw new ApiError('Unable to connect to the server'); + } +} \ No newline at end of file diff --git a/apps/web/src/pages/CardPage.tsx b/apps/web/src/pages/CardPage.tsx index 690ce574..3a277763 100644 --- a/apps/web/src/pages/CardPage.tsx +++ b/apps/web/src/pages/CardPage.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import { useParams, Link } from 'react-router-dom'; import type { PublicCard } from '../shared'; -import { apiFetch } from '../lib/api'; +import { ApiError, apiFetch } from '../lib/api'; import './CardPage.css'; function getPlatformColor(platform: string): string { @@ -30,9 +30,12 @@ export default function CardPage() { setCard(data); setError(null); }) - .catch(() => { + .catch((error) => { setCard(null); - setError('Card not found'); + setError(error instanceof ApiError && error.status === 404 + ? 'Card not found' + : 'Unable to load card. Please try again later.' + ); }) .finally(() => setLoading(false)); }, [id]); @@ -42,7 +45,7 @@ export default function CardPage() { if (card) { document.title = `${card.title} | ${card.owner.displayName}`; } else if (error) { - document.title = 'Card Not Found | DevCard'; + document.title = `${error} | DevCard`; } }, [card, error]); @@ -67,8 +70,12 @@ export default function CardPage() {
😕
-

Card not found

-

This DevCard doesn't exist or has been removed.

+

{error}

+

+ {error === 'Card not found' + ? "This DevCard doesn't exist or has been removed." + : 'There was a problem connecting to the server.'} +

Return Home
diff --git a/apps/web/src/pages/ProfilePage.tsx b/apps/web/src/pages/ProfilePage.tsx index 94a84f54..bacda0fb 100644 --- a/apps/web/src/pages/ProfilePage.tsx +++ b/apps/web/src/pages/ProfilePage.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'; import { useParams, Link } from 'react-router-dom'; import { PLATFORMS, getProfileUrl } from '../shared'; import type { PublicProfile } from '../shared'; -import { apiFetch } from '../lib/api'; +import { ApiError, apiFetch } from '../lib/api'; import './ProfilePage.css'; const platformColors: Record = { @@ -34,9 +34,12 @@ export default function ProfilePage() { setProfile(data); setError(null); }) - .catch(() => { + .catch((error) => { setProfile(null); - setError('User not found'); + setError(error instanceof ApiError && error.status === 404 + ? 'User not found' + : 'Unable to load profile. Please try again later.' + ); }) .finally(() => setLoading(false)); }, [username]); @@ -63,7 +66,7 @@ export default function ProfilePage() { if (profile) { document.title = `${profile.displayName} | DevCard`; } else if (error) { - document.title = 'User Not Found | DevCard'; + document.title = `${error} | DevCard`; } }, [profile, error]); @@ -92,8 +95,12 @@ export default function ProfilePage() {
😕
-

Profile not found

-

This DevCard has vanished into the digital void.

+

{error}

+

+ {error === 'User not found' + ? 'This DevCard has vanished into the digital void.' + : 'There was a problem connecting to the server.'} +

Return Home