172 lines
6.6 KiB
TypeScript
172 lines
6.6 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
|
import { Trophy, Calendar } from "lucide-react"
|
|
import Link from "next/link"
|
|
import { BadgeList } from "@/components/profile/badge-list"
|
|
import { getApiUrl } from "@/lib/api-config"
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
|
import { UserAttendanceList } from "@/components/profile/user-attendance-list"
|
|
import { UserReviewsList } from "@/components/profile/user-reviews-list"
|
|
import { UserGroupsList } from "@/components/profile/user-groups-list"
|
|
|
|
// Types
|
|
interface UserProfile {
|
|
id: number
|
|
email: string
|
|
username: string // Assuming we add this to UserRead or fetch separately
|
|
}
|
|
|
|
interface UserBadge {
|
|
id: number
|
|
badge: {
|
|
id: number
|
|
name: string
|
|
description: string
|
|
icon: string
|
|
slug: string
|
|
}
|
|
awarded_at: string
|
|
}
|
|
|
|
export default function ProfilePage() {
|
|
const [user, setUser] = useState<UserProfile | null>(null)
|
|
const [badges, setBadges] = useState<UserBadge[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
|
|
const [stats, setStats] = useState({ attendance_count: 0, review_count: 0, group_count: 0 })
|
|
|
|
useEffect(() => {
|
|
const token = localStorage.getItem("token")
|
|
if (!token) {
|
|
setLoading(false)
|
|
return
|
|
}
|
|
|
|
// Fetch User Data
|
|
fetch(`${getApiUrl()}/auth/users/me`, {
|
|
headers: { Authorization: `Bearer ${token}` }
|
|
})
|
|
.then(res => {
|
|
if (res.ok) return res.json()
|
|
throw new Error("Failed to fetch user")
|
|
})
|
|
.then(data => {
|
|
setUser(data)
|
|
// Fetch Badges
|
|
fetch(`${getApiUrl()}/badges/me`, {
|
|
headers: { Authorization: `Bearer ${token}` }
|
|
})
|
|
.then(res => res.ok ? res.json() : [])
|
|
.then(data => setBadges(data))
|
|
|
|
// Fetch Stats
|
|
fetch(`${getApiUrl()}/users/${data.id}/stats`, {
|
|
headers: { Authorization: `Bearer ${token}` }
|
|
})
|
|
.then(res => res.ok ? res.json() : { attendance_count: 0, review_count: 0, group_count: 0 })
|
|
.then((statsData: { attendance_count: number; review_count: number; group_count: number }) => setStats(statsData))
|
|
})
|
|
.catch(err => console.error(err))
|
|
.finally(() => setLoading(false))
|
|
}, [])
|
|
|
|
if (loading) {
|
|
return <div className="p-8 text-center">Loading profile...</div>
|
|
}
|
|
|
|
if (!user) {
|
|
return (
|
|
<div className="flex flex-col items-center justify-center gap-4 py-20">
|
|
<h1 className="text-2xl font-bold">Please Log In</h1>
|
|
<Link href="/login">
|
|
<Button>Log In</Button>
|
|
</Link>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// Transform badges for the list component
|
|
const displayBadges = badges.map(b => b.badge)
|
|
|
|
return (
|
|
<div className="flex flex-col gap-6">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-3xl font-bold tracking-tight">{user.email}</h1>
|
|
<p className="text-muted-foreground flex items-center gap-2">
|
|
<Calendar className="h-4 w-4" />
|
|
Member
|
|
</p>
|
|
</div>
|
|
<Link href="/settings">
|
|
<Button variant="outline">Settings</Button>
|
|
</Link>
|
|
</div>
|
|
|
|
<Tabs defaultValue="overview" className="w-full">
|
|
<TabsList>
|
|
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
<TabsTrigger value="attendance">Attendance</TabsTrigger>
|
|
<TabsTrigger value="reviews">Reviews</TabsTrigger>
|
|
<TabsTrigger value="groups">Groups</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<TabsContent value="overview" className="space-y-6 mt-6">
|
|
<div className="grid gap-6 md:grid-cols-3">
|
|
<Card>
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-sm font-medium text-muted-foreground">Shows Attended</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{stats.attendance_count}</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-sm font-medium text-muted-foreground">Reviews Written</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{stats.review_count}</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-sm font-medium text-muted-foreground">Groups Joined</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{stats.group_count}</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Trophy className="h-5 w-5 text-yellow-500" />
|
|
Badges
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<BadgeList badges={displayBadges} />
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="attendance" className="mt-6">
|
|
<UserAttendanceList userId={user.id} />
|
|
</TabsContent>
|
|
|
|
<TabsContent value="reviews" className="mt-6">
|
|
<UserReviewsList userId={user.id} />
|
|
</TabsContent>
|
|
|
|
<TabsContent value="groups" className="mt-6">
|
|
<UserGroupsList userId={user.id} />
|
|
</TabsContent>
|
|
</Tabs>
|
|
</div>
|
|
)
|
|
}
|