Enhance UI components and animations: Introduce a shimmer animation effect in globals.css, refactor FeedbackPageClient, LoginPage, RegisterPage, and AdminPanel components to utilize new UI components for improved consistency and maintainability. Update event and feedback handling in EventsPageSection and FeedbackModal, ensuring a cohesive user experience across the application.

This commit is contained in:
Julien Froidefond
2025-12-12 16:44:57 +01:00
parent db01c25de7
commit 99a475736b
32 changed files with 2242 additions and 1389 deletions

View File

@@ -0,0 +1,495 @@
"use client";
import { useState } from "react";
import Navigation from "@/components/Navigation";
import {
Button,
Input,
Textarea,
Card,
Badge,
Alert,
Modal,
ProgressBar,
StarRating,
Avatar,
SectionTitle,
BackgroundSection,
CloseButton,
} from "@/components/ui";
export default function StyleGuidePage() {
const [modalOpen, setModalOpen] = useState(false);
const [inputValue, setInputValue] = useState("");
const [textareaValue, setTextareaValue] = useState("");
const [rating, setRating] = useState(0);
return (
<main className="min-h-screen bg-black relative">
<Navigation />
<BackgroundSection backgroundImage="/got-2.jpg" className="pt-24 pb-16">
<div className="w-full max-w-6xl mx-auto px-8">
<SectionTitle variant="gradient" size="xl" className="mb-12">
STYLE GUIDE
</SectionTitle>
<p className="text-gray-400 text-center mb-12 max-w-3xl mx-auto">
Guide de style complet avec tous les composants UI disponibles et
leurs variantes
</p>
{/* Buttons */}
<Card variant="dark" className="p-6 mb-8">
<h2 className="text-2xl font-bold text-pixel-gold mb-6">
Buttons
</h2>
<div className="space-y-6">
<div>
<h3 className="text-lg text-gray-300 mb-3">Variantes</h3>
<div className="flex flex-wrap gap-4">
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="success">Success</Button>
<Button variant="danger">Danger</Button>
<Button variant="ghost">Ghost</Button>
</div>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">Tailles</h3>
<div className="flex flex-wrap items-center gap-4">
<Button variant="primary" size="sm">
Small
</Button>
<Button variant="primary" size="md">
Medium
</Button>
<Button variant="primary" size="lg">
Large
</Button>
</div>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">États</h3>
<div className="flex flex-wrap gap-4">
<Button variant="primary">Normal</Button>
<Button variant="primary" disabled>
Disabled
</Button>
</div>
</div>
</div>
</Card>
{/* Inputs */}
<Card variant="dark" className="p-6 mb-8">
<h2 className="text-2xl font-bold text-pixel-gold mb-6">Inputs</h2>
<div className="space-y-6">
<div>
<h3 className="text-lg text-gray-300 mb-3">Types</h3>
<div className="space-y-4 max-w-md">
<Input
label="Text Input"
type="text"
placeholder="Entrez du texte"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<Input
label="Email Input"
type="email"
placeholder="email@example.com"
/>
<Input
label="Password Input"
type="password"
placeholder="••••••••"
/>
<Input
label="Number Input"
type="number"
placeholder="123"
/>
<Input
label="Date Input"
type="date"
/>
</div>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">Avec erreur</h3>
<div className="max-w-md">
<Input
label="Input avec erreur"
type="text"
error="Ce champ est requis"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
</div>
</div>
</div>
</Card>
{/* Textarea */}
<Card variant="dark" className="p-6 mb-8">
<h2 className="text-2xl font-bold text-pixel-gold mb-6">
Textarea
</h2>
<div className="space-y-6">
<div>
<h3 className="text-lg text-gray-300 mb-3">Basique</h3>
<div className="max-w-md">
<Textarea
label="Commentaire"
placeholder="Écrivez votre commentaire..."
value={textareaValue}
onChange={(e) => setTextareaValue(e.target.value)}
rows={4}
/>
</div>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">
Avec compteur de caractères
</h3>
<div className="max-w-md">
<Textarea
label="Bio"
placeholder="Parlez-nous de vous..."
value={textareaValue}
onChange={(e) => setTextareaValue(e.target.value)}
rows={4}
maxLength={500}
showCharCount
/>
</div>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">Avec erreur</h3>
<div className="max-w-md">
<Textarea
label="Textarea avec erreur"
placeholder="Écrivez quelque chose..."
error="Ce champ est requis"
value={textareaValue}
onChange={(e) => setTextareaValue(e.target.value)}
rows={4}
/>
</div>
</div>
</div>
</Card>
{/* Badges */}
<Card variant="dark" className="p-6 mb-8">
<h2 className="text-2xl font-bold text-pixel-gold mb-6">Badges</h2>
<div className="space-y-6">
<div>
<h3 className="text-lg text-gray-300 mb-3">Variantes</h3>
<div className="flex flex-wrap gap-4">
<Badge variant="default">Default</Badge>
<Badge variant="success">Success</Badge>
<Badge variant="warning">Warning</Badge>
<Badge variant="danger">Danger</Badge>
<Badge variant="info">Info</Badge>
</div>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">Tailles</h3>
<div className="flex flex-wrap items-center gap-4">
<Badge variant="default" size="sm">
Small
</Badge>
<Badge variant="default" size="md">
Medium
</Badge>
</div>
</div>
</div>
</Card>
{/* Alerts */}
<Card variant="dark" className="p-6 mb-8">
<h2 className="text-2xl font-bold text-pixel-gold mb-6">Alerts</h2>
<div className="space-y-4 max-w-md">
<Alert variant="success">
Opération réussie ! Votre action a é effectuée avec succès.
</Alert>
<Alert variant="error">
Une erreur est survenue. Veuillez réessayer.
</Alert>
<Alert variant="warning">
Attention ! Cette action est irréversible.
</Alert>
<Alert variant="info">
Information : Voici quelques informations utiles.
</Alert>
</div>
</Card>
{/* Cards */}
<Card variant="dark" className="p-6 mb-8">
<h2 className="text-2xl font-bold text-pixel-gold mb-6">Cards</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Card variant="default" className="p-4">
<h3 className="text-lg font-bold text-pixel-gold mb-2">
Card Default
</h3>
<p className="text-gray-300 text-sm">
Contenu de la carte avec variant default
</p>
</Card>
<Card variant="dark" className="p-4">
<h3 className="text-lg font-bold text-pixel-gold mb-2">
Card Dark
</h3>
<p className="text-gray-300 text-sm">
Contenu de la carte avec variant dark
</p>
</Card>
</div>
</Card>
{/* Progress Bars */}
<Card variant="dark" className="p-6 mb-8">
<h2 className="text-2xl font-bold text-pixel-gold mb-6">
Progress Bars
</h2>
<div className="space-y-6 max-w-md">
<div>
<h3 className="text-lg text-gray-300 mb-3">HP Bar (High)</h3>
<ProgressBar
value={75}
max={100}
variant="hp"
showLabel
label="HP"
/>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">HP Bar (Medium)</h3>
<ProgressBar
value={45}
max={100}
variant="hp"
showLabel
label="HP"
/>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">HP Bar (Low)</h3>
<ProgressBar
value={20}
max={100}
variant="hp"
showLabel
label="HP"
/>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">XP Bar</h3>
<ProgressBar
value={60}
max={100}
variant="xp"
showLabel
label="XP"
/>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">Default</h3>
<ProgressBar
value={50}
max={100}
variant="default"
showLabel
label="Progress"
/>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">Sans label</h3>
<ProgressBar value={60} max={100} variant="default" />
</div>
</div>
</Card>
{/* Star Rating */}
<Card variant="dark" className="p-6 mb-8">
<h2 className="text-2xl font-bold text-pixel-gold mb-6">
Star Rating
</h2>
<div className="space-y-6">
<div>
<h3 className="text-lg text-gray-300 mb-3">Interactif</h3>
<StarRating
value={rating}
onChange={setRating}
showValue
/>
<p className="text-gray-400 text-sm mt-2">
Note sélectionnée : {rating}/5
</p>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">Tailles</h3>
<div className="space-y-4">
<div>
<p className="text-gray-400 text-sm mb-2">Small</p>
<StarRating value={4} size="sm" />
</div>
<div>
<p className="text-gray-400 text-sm mb-2">Medium</p>
<StarRating value={4} size="md" />
</div>
<div>
<p className="text-gray-400 text-sm mb-2">Large</p>
<StarRating value={4} size="lg" />
</div>
</div>
</div>
</div>
</Card>
{/* Avatar */}
<Card variant="dark" className="p-6 mb-8">
<h2 className="text-2xl font-bold text-pixel-gold mb-6">Avatar</h2>
<div className="space-y-6">
<div>
<h3 className="text-lg text-gray-300 mb-3">Tailles</h3>
<div className="flex items-center gap-6">
<Avatar
src="/avatar-1.jpg"
username="User"
size="sm"
/>
<Avatar
src="/avatar-2.jpg"
username="User"
size="md"
/>
<Avatar
src="/avatar-3.jpg"
username="User"
size="lg"
/>
</div>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">
Sans image (fallback)
</h3>
<Avatar username="John Doe" size="lg" />
</div>
</div>
</Card>
{/* Section Title */}
<Card variant="dark" className="p-6 mb-8">
<h2 className="text-2xl font-bold text-pixel-gold mb-6">
Section Title
</h2>
<div className="space-y-8">
<div>
<h3 className="text-lg text-gray-300 mb-3">Variantes</h3>
<div className="space-y-4">
<SectionTitle variant="default" size="md">
Default Title
</SectionTitle>
<SectionTitle variant="gradient" size="md">
Gradient Title
</SectionTitle>
</div>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">Tailles</h3>
<div className="space-y-4">
<SectionTitle variant="gradient" size="sm">
Small Title
</SectionTitle>
<SectionTitle variant="gradient" size="md">
Medium Title
</SectionTitle>
<SectionTitle variant="gradient" size="lg">
Large Title
</SectionTitle>
<SectionTitle variant="gradient" size="xl">
Extra Large Title
</SectionTitle>
</div>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">Avec sous-titre</h3>
<SectionTitle
variant="gradient"
size="lg"
subtitle="Un sous-titre descriptif"
>
Title with Subtitle
</SectionTitle>
</div>
</div>
</Card>
{/* Modal */}
<Card variant="dark" className="p-6 mb-8">
<h2 className="text-2xl font-bold text-pixel-gold mb-6">Modal</h2>
<div className="space-y-6">
<div>
<h3 className="text-lg text-gray-300 mb-3">Tailles</h3>
<div className="flex flex-wrap gap-4">
<Button onClick={() => setModalOpen(true)}>
Ouvrir Modal
</Button>
</div>
</div>
</div>
</Card>
{/* Close Button */}
<Card variant="dark" className="p-6 mb-8">
<h2 className="text-2xl font-bold text-pixel-gold mb-6">
Close Button
</h2>
<div className="space-y-6">
<div>
<h3 className="text-lg text-gray-300 mb-3">Tailles</h3>
<div className="flex items-center gap-6">
<CloseButton onClick={() => {}} size="sm" />
<CloseButton onClick={() => {}} size="md" />
<CloseButton onClick={() => {}} size="lg" />
</div>
</div>
<div>
<h3 className="text-lg text-gray-300 mb-3">Disabled</h3>
<CloseButton onClick={() => {}} disabled />
</div>
</div>
</Card>
{/* Modal Demo */}
<Modal
isOpen={modalOpen}
onClose={() => setModalOpen(false)}
size="md"
>
<div className="p-8">
<div className="flex items-center justify-between mb-6">
<h2 className="text-2xl font-bold text-pixel-gold">
Exemple de Modal
</h2>
<CloseButton onClick={() => setModalOpen(false)} />
</div>
<p className="text-gray-300 mb-6">
Ceci est un exemple de modal avec différentes tailles
disponibles.
</p>
<div className="flex gap-4">
<Button onClick={() => setModalOpen(false)}>Fermer</Button>
</div>
</div>
</Modal>
</div>
</BackgroundSection>
</main>
);
}

View File

@@ -5,6 +5,15 @@ import { useSession } from "next-auth/react";
import { useRouter, useParams } from "next/navigation";
import Navigation from "@/components/Navigation";
import { createFeedback } from "@/actions/events/feedback";
import {
StarRating,
Textarea,
Button,
Alert,
Card,
BackgroundSection,
SectionTitle,
} from "@/components/ui";
interface Event {
id: string;
@@ -156,25 +165,17 @@ export default function FeedbackPageClient({
return (
<main className="min-h-screen bg-black relative">
<Navigation />
<section className="relative w-full min-h-screen flex flex-col items-center justify-center overflow-hidden pt-24">
{/* Background Image */}
<div
className="absolute inset-0 bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: `url('${backgroundImage}')`,
}}
>
<div className="absolute inset-0 bg-gradient-to-b from-black/70 via-black/60 to-black/80"></div>
</div>
<BackgroundSection backgroundImage={backgroundImage} className="pt-24">
{/* Feedback Form */}
<div className="relative z-10 w-full max-w-2xl mx-auto px-8">
<div className="bg-black/80 border border-pixel-gold/30 rounded-lg p-8 backdrop-blur-sm">
<h1 className="text-4xl font-gaming font-black mb-2 text-center">
<span className="bg-gradient-to-r from-pixel-gold via-orange-400 to-pixel-gold bg-clip-text text-transparent">
FEEDBACK
</span>
</h1>
<div className="w-full max-w-2xl mx-auto px-8">
<Card variant="dark" className="p-8">
<SectionTitle
variant="gradient"
size="lg"
className="mb-2 text-center"
>
FEEDBACK
</SectionTitle>
<p className="text-gray-400 text-sm text-center mb-2">
{existingFeedback
? "Modifier votre feedback pour"
@@ -185,15 +186,15 @@ export default function FeedbackPageClient({
</p>
{success && (
<div className="bg-green-900/50 border border-green-500/50 text-green-400 px-4 py-3 rounded text-sm mb-6">
<Alert variant="success" className="mb-6">
Feedback enregistré avec succès ! Redirection...
</div>
</Alert>
)}
{error && (
<div className="bg-red-900/50 border border-red-500/50 text-red-400 px-4 py-3 rounded text-sm mb-6">
<Alert variant="error" className="mb-6">
{error}
</div>
</Alert>
)}
<form onSubmit={handleSubmit} className="space-y-6">
@@ -202,66 +203,44 @@ export default function FeedbackPageClient({
<label className="block text-sm font-semibold text-gray-300 mb-4 uppercase tracking-wider">
Note
</label>
<div className="flex items-center justify-center gap-2">
{[1, 2, 3, 4, 5].map((star) => (
<button
key={star}
type="button"
onClick={() => setRating(star)}
className={`text-4xl transition-transform hover:scale-110 ${
star <= rating
? "text-pixel-gold"
: "text-gray-600 hover:text-gray-500"
}`}
aria-label={`Noter ${star} étoile${star > 1 ? "s" : ""}`}
>
</button>
))}
</div>
<p className="text-gray-500 text-xs text-center mt-2">
{rating > 0 && `${rating}/5`}
</p>
<StarRating
value={rating}
onChange={setRating}
size="lg"
showValue
/>
</div>
{/* Comment */}
<div>
<label
htmlFor="comment"
className="block text-sm font-semibold text-gray-300 mb-2 uppercase tracking-wider"
>
Commentaire (optionnel)
</label>
<textarea
id="comment"
value={comment}
onChange={(e) => setComment(e.target.value)}
rows={6}
maxLength={1000}
className="w-full px-4 py-3 bg-black/60 border border-pixel-gold/30 rounded text-white placeholder-gray-500 focus:outline-none focus:border-pixel-gold transition resize-none"
placeholder="Partagez votre expérience, vos suggestions..."
/>
<p className="text-gray-500 text-xs mt-1 text-right">
{comment.length}/1000 caractères
</p>
</div>
<Textarea
id="comment"
label="Commentaire (optionnel)"
value={comment}
onChange={(e) => setComment(e.target.value)}
rows={6}
maxLength={1000}
showCharCount
placeholder="Partagez votre expérience, vos suggestions..."
/>
{/* Submit Button */}
<button
<Button
type="submit"
variant="primary"
size="lg"
disabled={submitting || rating === 0}
className="w-full px-6 py-3 border border-pixel-gold/50 bg-black/60 text-white uppercase text-sm tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition disabled:opacity-50 disabled:cursor-not-allowed"
className="w-full"
>
{submitting
? "Enregistrement..."
: existingFeedback
? "Modifier le feedback"
: "Envoyer le feedback"}
</button>
</Button>
</form>
</div>
</Card>
</div>
</section>
</BackgroundSection>
</main>
);
}

View File

@@ -31,4 +31,17 @@
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
.animate-shimmer {
animation: shimmer 2s infinite;
}
}

View File

@@ -5,6 +5,14 @@ import { signIn } from "next-auth/react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import Navigation from "@/components/Navigation";
import {
Input,
Button,
Alert,
Card,
BackgroundSection,
SectionTitle,
} from "@/components/ui";
export default function LoginPage() {
const router = useRouter();
@@ -46,79 +54,53 @@ export default function LoginPage() {
return (
<main className="min-h-screen bg-black relative">
<Navigation />
<section className="relative w-full min-h-screen flex flex-col items-center justify-center overflow-hidden pt-24">
{/* Background Image */}
<div
className="absolute inset-0 bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: `url('/got-2.jpg')`,
}}
>
<div className="absolute inset-0 bg-gradient-to-b from-black/70 via-black/60 to-black/80"></div>
</div>
<BackgroundSection backgroundImage="/got-2.jpg" className="pt-24">
{/* Login Form */}
<div className="relative z-10 w-full max-w-md mx-auto px-8">
<div className="bg-black/80 border border-pixel-gold/30 rounded-lg p-8 backdrop-blur-sm">
<h1 className="text-4xl font-gaming font-black mb-2 text-center">
<span className="bg-gradient-to-r from-pixel-gold via-orange-400 to-pixel-gold bg-clip-text text-transparent">
CONNEXION
</span>
</h1>
<div className="w-full max-w-md mx-auto px-8">
<Card variant="dark" className="p-8">
<SectionTitle
variant="gradient"
size="lg"
className="mb-2 text-center"
>
CONNEXION
</SectionTitle>
<p className="text-gray-400 text-sm text-center mb-8">
Connectez-vous à votre compte
</p>
<form onSubmit={handleSubmit} className="space-y-6">
{error && (
<div className="bg-red-900/50 border border-red-500/50 text-red-400 px-4 py-3 rounded text-sm">
{error}
</div>
)}
{error && <Alert variant="error">{error}</Alert>}
<div>
<label
htmlFor="email"
className="block text-sm font-semibold text-gray-300 mb-2 uppercase tracking-wider"
>
Email
</label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="w-full px-4 py-3 bg-black/60 border border-pixel-gold/30 rounded text-white placeholder-gray-500 focus:outline-none focus:border-pixel-gold transition"
placeholder="votre@email.com"
/>
</div>
<Input
id="email"
type="email"
label="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
placeholder="votre@email.com"
/>
<div>
<label
htmlFor="password"
className="block text-sm font-semibold text-gray-300 mb-2 uppercase tracking-wider"
>
Mot de passe
</label>
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
className="w-full px-4 py-3 bg-black/60 border border-pixel-gold/30 rounded text-white placeholder-gray-500 focus:outline-none focus:border-pixel-gold transition"
placeholder="••••••••"
/>
</div>
<Input
id="password"
type="password"
label="Mot de passe"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
placeholder="••••••••"
/>
<button
<Button
type="submit"
variant="primary"
size="lg"
disabled={loading}
className="w-full px-6 py-3 border border-pixel-gold/50 bg-black/60 text-white uppercase text-sm tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition disabled:opacity-50 disabled:cursor-not-allowed"
className="w-full"
>
{loading ? "Connexion..." : "Se connecter"}
</button>
</Button>
</form>
<div className="mt-6 text-center">
@@ -132,9 +114,9 @@ export default function LoginPage() {
</Link>
</p>
</div>
</div>
</Card>
</div>
</section>
</BackgroundSection>
</main>
);
}

View File

@@ -4,7 +4,16 @@ import { useState, useRef, type ChangeEvent, type FormEvent } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import Navigation from "@/components/Navigation";
import Avatar from "@/components/Avatar";
import {
Avatar,
Input,
Textarea,
Button,
Alert,
Card,
BackgroundSection,
SectionTitle,
} from "@/components/ui";
export default function RegisterPage() {
const router = useRouter();
@@ -162,25 +171,17 @@ export default function RegisterPage() {
return (
<main className="min-h-screen bg-black relative">
<Navigation />
<section className="relative w-full min-h-screen flex flex-col items-center justify-center overflow-hidden pt-24">
{/* Background Image */}
<div
className="absolute inset-0 bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: `url('/got-2.jpg')`,
}}
>
<div className="absolute inset-0 bg-gradient-to-b from-black/70 via-black/60 to-black/80"></div>
</div>
<BackgroundSection backgroundImage="/got-2.jpg" className="pt-24">
{/* Register Form */}
<div className="relative z-10 w-full max-w-md mx-auto px-8">
<div className="bg-black/80 border border-pixel-gold/30 rounded-lg p-8 backdrop-blur-sm">
<h1 className="text-4xl font-gaming font-black mb-2 text-center">
<span className="bg-gradient-to-r from-pixel-gold via-orange-400 to-pixel-gold bg-clip-text text-transparent">
INSCRIPTION
</span>
</h1>
<div className="w-full max-w-md mx-auto px-8">
<Card variant="dark" className="p-8">
<SectionTitle
variant="gradient"
size="lg"
className="mb-2 text-center"
>
INSCRIPTION
</SectionTitle>
<p className="text-gray-400 text-sm text-center mb-4">
{step === 1
? "Créez votre compte pour commencer"
@@ -216,103 +217,65 @@ export default function RegisterPage() {
{step === 1 ? (
<form onSubmit={handleStep1Submit} className="space-y-6">
{error && (
<div className="bg-red-900/50 border border-red-500/50 text-red-400 px-4 py-3 rounded text-sm">
{error}
</div>
)}
{error && <Alert variant="error">{error}</Alert>}
<div>
<label
htmlFor="email"
className="block text-sm font-semibold text-gray-300 mb-2 uppercase tracking-wider"
>
Email
</label>
<input
id="email"
name="email"
type="email"
value={formData.email}
onChange={handleChange}
required
className="w-full px-4 py-3 bg-black/60 border border-pixel-gold/30 rounded text-white placeholder-gray-500 focus:outline-none focus:border-pixel-gold transition"
placeholder="votre@email.com"
/>
</div>
<Input
id="email"
name="email"
type="email"
label="Email"
value={formData.email}
onChange={handleChange}
required
placeholder="votre@email.com"
/>
<div>
<label
htmlFor="username"
className="block text-sm font-semibold text-gray-300 mb-2 uppercase tracking-wider"
>
Nom d&apos;utilisateur
</label>
<input
id="username"
name="username"
type="text"
value={formData.username}
onChange={handleChange}
required
className="w-full px-4 py-3 bg-black/60 border border-pixel-gold/30 rounded text-white placeholder-gray-500 focus:outline-none focus:border-pixel-gold transition"
placeholder="VotrePseudo"
/>
</div>
<Input
id="username"
name="username"
type="text"
label="Nom d'utilisateur"
value={formData.username}
onChange={handleChange}
required
placeholder="VotrePseudo"
/>
<div>
<label
htmlFor="password"
className="block text-sm font-semibold text-gray-300 mb-2 uppercase tracking-wider"
>
Mot de passe
</label>
<input
id="password"
name="password"
type="password"
value={formData.password}
onChange={handleChange}
required
className="w-full px-4 py-3 bg-black/60 border border-pixel-gold/30 rounded text-white placeholder-gray-500 focus:outline-none focus:border-pixel-gold transition"
placeholder="••••••••"
/>
</div>
<Input
id="password"
name="password"
type="password"
label="Mot de passe"
value={formData.password}
onChange={handleChange}
required
placeholder="••••••••"
/>
<div>
<label
htmlFor="confirmPassword"
className="block text-sm font-semibold text-gray-300 mb-2 uppercase tracking-wider"
>
Confirmer le mot de passe
</label>
<input
id="confirmPassword"
name="confirmPassword"
type="password"
value={formData.confirmPassword}
onChange={handleChange}
required
className="w-full px-4 py-3 bg-black/60 border border-pixel-gold/30 rounded text-white placeholder-gray-500 focus:outline-none focus:border-pixel-gold transition"
placeholder="••••••••"
/>
</div>
<Input
id="confirmPassword"
name="confirmPassword"
type="password"
label="Confirmer le mot de passe"
value={formData.confirmPassword}
onChange={handleChange}
required
placeholder="••••••••"
/>
<button
<Button
type="submit"
variant="primary"
size="lg"
disabled={loading}
className="w-full px-6 py-3 border border-pixel-gold/50 bg-black/60 text-white uppercase text-sm tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition disabled:opacity-50 disabled:cursor-not-allowed"
className="w-full"
>
{loading ? "Création..." : "Suivant"}
</button>
</Button>
</form>
) : (
<form onSubmit={handleStep2Submit} className="space-y-6">
{error && (
<div className="bg-red-900/50 border border-red-500/50 text-red-400 px-4 py-3 rounded text-sm">
{error}
</div>
)}
{error && <Alert variant="error">{error}</Alert>}
{/* Avatar Selection */}
<div>
@@ -387,61 +350,47 @@ export default function RegisterPage() {
className="hidden"
id="avatar-upload"
/>
<label
htmlFor="avatar-upload"
className="px-4 py-2 border border-pixel-gold/50 bg-black/40 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition cursor-pointer inline-block"
>
{uploadingAvatar
? "Upload en cours..."
: "Upload un avatar custom"}
<label htmlFor="avatar-upload">
<Button
variant="primary"
size="md"
as="span"
className="cursor-pointer"
>
{uploadingAvatar
? "Upload en cours..."
: "Upload un avatar custom"}
</Button>
</label>
</div>
</div>
</div>
<div>
<label
htmlFor="username-step2"
className="block text-sm font-semibold text-gray-300 mb-2 uppercase tracking-wider"
>
Nom d&apos;utilisateur
</label>
<input
id="username-step2"
name="username"
type="text"
value={formData.username}
onChange={handleChange}
required
className="w-full px-4 py-3 bg-black/60 border border-pixel-gold/30 rounded text-white placeholder-gray-500 focus:outline-none focus:border-pixel-gold transition"
placeholder="VotrePseudo"
minLength={3}
maxLength={20}
/>
<p className="text-gray-500 text-xs mt-1">3-20 caractères</p>
</div>
<Input
id="username-step2"
name="username"
type="text"
label="Nom d'utilisateur"
value={formData.username}
onChange={handleChange}
required
placeholder="VotrePseudo"
minLength={3}
maxLength={20}
/>
<p className="text-gray-500 text-xs mt-1">3-20 caractères</p>
<div>
<label
htmlFor="bio"
className="block text-sm font-semibold text-gray-300 mb-2 uppercase tracking-wider"
>
Bio (optionnel)
</label>
<textarea
id="bio"
name="bio"
value={formData.bio}
onChange={handleChange}
className="w-full px-4 py-3 bg-black/60 border border-pixel-gold/30 rounded text-white placeholder-gray-500 focus:outline-none focus:border-pixel-gold transition resize-none"
rows={4}
maxLength={500}
placeholder="Parlez-nous de vous..."
/>
<p className="text-gray-500 text-xs mt-1">
{formData.bio.length}/500 caractères
</p>
</div>
<Textarea
id="bio"
name="bio"
label="Bio (optionnel)"
value={formData.bio}
onChange={handleChange}
rows={4}
maxLength={500}
showCharCount
placeholder="Parlez-nous de vous..."
/>
<div>
<label className="block text-sm font-semibold text-gray-300 mb-3 uppercase tracking-wider">
@@ -500,20 +449,24 @@ export default function RegisterPage() {
</div>
<div className="flex gap-4">
<button
<Button
type="button"
variant="secondary"
size="lg"
onClick={() => setStep(1)}
className="flex-1 px-6 py-3 border border-gray-600/50 bg-black/40 text-gray-400 uppercase text-sm tracking-widest rounded hover:bg-gray-900/40 hover:border-gray-500 transition"
className="flex-1"
>
Retour
</button>
<button
</Button>
<Button
type="submit"
variant="primary"
size="lg"
disabled={loading}
className="flex-1 px-6 py-3 border border-pixel-gold/50 bg-black/60 text-white uppercase text-sm tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition disabled:opacity-50 disabled:cursor-not-allowed"
className="flex-1"
>
{loading ? "Finalisation..." : "Terminer"}
</button>
</Button>
</div>
</form>
)}
@@ -529,9 +482,9 @@ export default function RegisterPage() {
</Link>
</p>
</div>
</div>
</Card>
</div>
</section>
</BackgroundSection>
</main>
);
}