POL-124: Migrate frontend from React Native to Next.js web app
- Replace mobile/ (Expo) with web/ (Next.js 16 + Tailwind + shadcn/ui) - Pages: login, register, pending, championships, championship detail, registrations, profile, admin - Logic/view separated: hooks/ for data, components/ for UI, pages compose both - Types in src/types/ (one interface per file) - Auth: Zustand store + localStorage tokens + cookie presence flag for proxy - API layer: axios client with JWT auto-refresh Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
82
web/src/app/(app)/profile/page.tsx
Normal file
82
web/src/app/(app)/profile/page.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useAuth } from "@/store/useAuth";
|
||||
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
||||
const ROLE_COLORS: Record<string, string> = {
|
||||
admin: "bg-red-100 text-red-700",
|
||||
organizer: "bg-violet-100 text-violet-700",
|
||||
member: "bg-green-100 text-green-700",
|
||||
};
|
||||
|
||||
export default function ProfilePage() {
|
||||
const router = useRouter();
|
||||
const user = useAuth((s) => s.user);
|
||||
const logout = useAuth((s) => s.logout);
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
const initials = user.full_name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2);
|
||||
const joinedDate = new Date(user.created_at).toLocaleDateString("en-GB", { month: "long", year: "numeric" });
|
||||
|
||||
async function handleLogout() {
|
||||
await logout();
|
||||
router.push("/login");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-md mx-auto space-y-6">
|
||||
<div className="flex flex-col items-center gap-3 pt-4">
|
||||
<Avatar className="h-20 w-20">
|
||||
<AvatarFallback className="bg-violet-100 text-violet-700 text-2xl font-bold">
|
||||
{initials}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="text-center">
|
||||
<p className="text-xl font-bold text-gray-900">{user.full_name}</p>
|
||||
<p className="text-sm text-gray-500">{user.email}</p>
|
||||
</div>
|
||||
<Badge className={`${ROLE_COLORS[user.role] ?? "bg-gray-100"} border-0 capitalize`}>
|
||||
{user.role}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-3 text-sm text-gray-700">
|
||||
{user.phone && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">Phone</span>
|
||||
<span>{user.phone}</span>
|
||||
</div>
|
||||
)}
|
||||
{user.organization_name && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">Organization</span>
|
||||
<span>{user.organization_name}</span>
|
||||
</div>
|
||||
)}
|
||||
{user.instagram_handle && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">Instagram</span>
|
||||
<span>{user.instagram_handle}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">Member since</span>
|
||||
<span>{joinedDate}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<Button variant="destructive" className="w-full" onClick={handleLogout}>
|
||||
Sign out
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user