POL-125: Frontend visual upgrade — dark luxury pole dance theme
- New dark theme with rose/purple/gold accent palette - Premium typography: Cormorant Garamond (display) + Outfit (body) - Glassmorphism cards, gradient mesh backgrounds, glow effects - CSS split into theme.css, utilities.css, animations.css - Staggered fade-in animations on list pages - Redesigned all pages: auth, championships, registrations, profile, admin - Lucide icons replace emoji throughout - Responsive mobile nav with hamburger menu Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,38 @@
|
||||
export default function AuthLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-violet-50 to-purple-100 p-4">
|
||||
<div className="w-full max-w-md">{children}</div>
|
||||
<div className="relative flex min-h-screen items-center justify-center overflow-hidden p-4">
|
||||
{/* Gradient mesh background */}
|
||||
<div className="fixed inset-0 bg-background bg-mesh-strong" />
|
||||
|
||||
{/* Decorative flowing lines — pole dance silhouette abstraction */}
|
||||
<svg
|
||||
className="fixed inset-0 h-full w-full opacity-[0.04]"
|
||||
viewBox="0 0 1200 800"
|
||||
fill="none"
|
||||
preserveAspectRatio="xMidYMid slice"
|
||||
>
|
||||
<path
|
||||
d="M-100,400 C100,200 300,600 500,350 C700,100 900,500 1100,300 C1300,100 1400,400 1400,400"
|
||||
stroke="url(#line-grad)"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M-100,500 C200,300 400,700 600,450 C800,200 1000,600 1300,350"
|
||||
stroke="url(#line-grad)"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient id="line-grad" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" stopColor="#E91E63" />
|
||||
<stop offset="50%" stopColor="#9C27B0" />
|
||||
<stop offset="100%" stopColor="#D4A843" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<div className="relative z-10 w-full max-w-md animate-fade-in-up">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
import { useLoginForm } from "@/hooks/useAuthForms";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
@@ -8,37 +9,77 @@ import { Label } from "@/components/ui/label";
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
||||
export default function LoginPage() {
|
||||
const { email, setEmail, password, setPassword, error, isLoading, submit } = useLoginForm();
|
||||
const { email, setEmail, password, setPassword, showPassword, setShowPassword, error, isLoading, submit } = useLoginForm();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="text-center">
|
||||
<div className="mx-auto mb-2 text-4xl">🏆</div>
|
||||
<CardTitle className="text-2xl">Welcome back</CardTitle>
|
||||
<CardDescription>Sign in to your account</CardDescription>
|
||||
<Card className="glass-strong glow-rose overflow-hidden">
|
||||
<CardHeader className="text-center pb-2">
|
||||
<div className="mx-auto mb-4 h-px w-16 bg-gradient-to-r from-transparent via-rose-accent to-transparent" />
|
||||
<CardTitle className="font-display text-3xl font-semibold tracking-wide">
|
||||
Welcome back
|
||||
</CardTitle>
|
||||
<CardDescription className="text-muted-foreground">
|
||||
Sign in to your account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
<form onSubmit={submit}>
|
||||
<CardContent className="space-y-4">
|
||||
{error && <p className="rounded-md bg-red-50 px-3 py-2 text-sm text-red-600">{error}</p>}
|
||||
{error && (
|
||||
<p className="rounded-lg bg-destructive/10 border border-destructive/20 px-3 py-2 text-sm text-destructive">
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input id="email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} required />
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email" className="text-xs uppercase tracking-widest text-dim">
|
||||
Email
|
||||
</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
className="bg-surface border-border/60 focus:border-rose-accent focus:ring-rose-accent/30 placeholder:text-dim"
|
||||
placeholder="your@email.com"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input id="password" type="password" value={password} onChange={(e) => setPassword(e.target.value)} required />
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password" className="text-xs uppercase tracking-widest text-dim">
|
||||
Password
|
||||
</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
className="bg-surface border-border/60 pr-10 focus:border-rose-accent focus:ring-rose-accent/30"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-dim hover:text-foreground transition-colors"
|
||||
>
|
||||
{showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
<CardFooter className="flex flex-col gap-3">
|
||||
<Button type="submit" className="w-full bg-violet-600 hover:bg-violet-700" disabled={isLoading}>
|
||||
<CardFooter className="flex flex-col gap-4 pt-2">
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full bg-rose-accent hover:bg-rose-accent/90 text-white font-medium tracking-wide"
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? "Signing in…" : "Sign in"}
|
||||
</Button>
|
||||
<p className="text-center text-sm text-gray-500">
|
||||
<p className="text-center text-sm text-muted-foreground">
|
||||
No account?{" "}
|
||||
<Link href="/register" className="font-medium text-violet-600 hover:underline">
|
||||
<Link href="/register" className="font-medium text-rose-accent hover:text-rose-accent/80 transition-colors">
|
||||
Register
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
import Link from "next/link";
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Clock } from "lucide-react";
|
||||
|
||||
export default function PendingPage() {
|
||||
return (
|
||||
<Card className="text-center">
|
||||
<Card className="glass-strong glow-purple text-center overflow-hidden">
|
||||
<CardHeader>
|
||||
<div className="mx-auto mb-2 text-5xl">⏳</div>
|
||||
<CardTitle className="text-2xl">Awaiting approval</CardTitle>
|
||||
<CardDescription>
|
||||
<div className="mx-auto mb-3 flex h-14 w-14 items-center justify-center rounded-full bg-purple-accent/10 border border-purple-accent/20">
|
||||
<Clock className="h-7 w-7 text-purple-accent" />
|
||||
</div>
|
||||
<CardTitle className="font-display text-3xl font-semibold tracking-wide">
|
||||
Awaiting approval
|
||||
</CardTitle>
|
||||
<CardDescription className="text-muted-foreground">
|
||||
Your organizer account has been submitted. An admin will review it shortly.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="mb-6 text-sm text-gray-500">
|
||||
<p className="mb-6 text-sm text-dim">
|
||||
Once approved you can log in and start creating championships.
|
||||
</p>
|
||||
<Button asChild variant="outline" className="w-full">
|
||||
<Button asChild variant="outline" className="w-full border-border/60 hover:bg-surface-hover">
|
||||
<Link href="/login">Back to login</Link>
|
||||
</Button>
|
||||
</CardContent>
|
||||
|
||||
@@ -11,16 +11,24 @@ export default function RegisterPage() {
|
||||
const { role, setRole, form, update, error, isLoading, submit } = useRegisterForm();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="text-center">
|
||||
<div className="mx-auto mb-2 text-4xl">🏅</div>
|
||||
<CardTitle className="text-2xl">Create account</CardTitle>
|
||||
<CardDescription>Join the pole dance community</CardDescription>
|
||||
<Card className="glass-strong glow-rose overflow-hidden">
|
||||
<CardHeader className="text-center pb-2">
|
||||
<div className="mx-auto mb-4 h-px w-16 bg-gradient-to-r from-transparent via-purple-accent to-transparent" />
|
||||
<CardTitle className="font-display text-3xl font-semibold tracking-wide">
|
||||
Create account
|
||||
</CardTitle>
|
||||
<CardDescription className="text-muted-foreground">
|
||||
Join the pole dance community
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
<form onSubmit={submit}>
|
||||
<CardContent className="space-y-4">
|
||||
{error && <p className="rounded-md bg-red-50 px-3 py-2 text-sm text-red-600">{error}</p>}
|
||||
{error && (
|
||||
<p className="rounded-lg bg-destructive/10 border border-destructive/20 px-3 py-2 text-sm text-destructive">
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{(["member", "organizer"] as const).map((r) => (
|
||||
@@ -28,11 +36,13 @@ export default function RegisterPage() {
|
||||
key={r}
|
||||
type="button"
|
||||
onClick={() => setRole(r)}
|
||||
className={`rounded-lg border-2 p-3 text-sm font-medium transition-colors ${
|
||||
role === r ? "border-violet-600 bg-violet-50 text-violet-700" : "border-gray-200 text-gray-600 hover:border-gray-300"
|
||||
className={`rounded-xl border-2 p-3 text-sm font-medium transition-all duration-200 ${
|
||||
role === r
|
||||
? "border-rose-accent bg-rose-accent/10 text-foreground glow-rose"
|
||||
: "border-border/40 text-muted-foreground hover:border-border hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
{r === "member" ? "🏅 Athlete" : "🏆 Organizer"}
|
||||
{r === "member" ? "Athlete" : "Organizer"}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -54,13 +64,17 @@ export default function RegisterPage() {
|
||||
)}
|
||||
</CardContent>
|
||||
|
||||
<CardFooter className="flex flex-col gap-3">
|
||||
<Button type="submit" className="w-full bg-violet-600 hover:bg-violet-700" disabled={isLoading}>
|
||||
<CardFooter className="flex flex-col gap-4 pt-2">
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full bg-rose-accent hover:bg-rose-accent/90 text-white font-medium tracking-wide"
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? "Creating…" : role === "member" ? "Create account" : "Submit for approval"}
|
||||
</Button>
|
||||
<p className="text-center text-sm text-gray-500">
|
||||
<p className="text-center text-sm text-muted-foreground">
|
||||
Have an account?{" "}
|
||||
<Link href="/login" className="font-medium text-violet-600 hover:underline">
|
||||
<Link href="/login" className="font-medium text-rose-accent hover:text-rose-accent/80 transition-colors">
|
||||
Sign in
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user