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:
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useRef, useTransition, type ChangeEvent } from "react";
|
||||
import Avatar from "./Avatar";
|
||||
import { Avatar, Input, Textarea, Button, Alert, Card, BackgroundSection, SectionTitle, ProgressBar } from "@/components/ui";
|
||||
import { updateProfile } from "@/actions/profile/update-profile";
|
||||
import { updatePassword } from "@/actions/profile/update-password";
|
||||
|
||||
@@ -170,53 +170,19 @@ export default function ProfileForm({
|
||||
: "from-red-700 to-red-900";
|
||||
|
||||
return (
|
||||
<section className="relative w-full min-h-screen flex flex-col items-center justify-center overflow-hidden pt-24 pb-16">
|
||||
{/* Background Image */}
|
||||
<div
|
||||
className="absolute inset-0 bg-cover bg-center bg-no-repeat"
|
||||
style={{
|
||||
backgroundImage: `url('${backgroundImage}')`,
|
||||
}}
|
||||
>
|
||||
{/* Dark overlay for readability */}
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-black/70 via-black/60 to-black/80"></div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="relative z-10 w-full max-w-4xl mx-auto px-8 py-16">
|
||||
<BackgroundSection backgroundImage={backgroundImage}>
|
||||
<div className="w-full max-w-4xl mx-auto px-8">
|
||||
{/* Title Section */}
|
||||
<div className="text-center mb-12">
|
||||
<h1 className="text-5xl md:text-7xl font-gaming font-black mb-4 tracking-tight">
|
||||
<span
|
||||
className="bg-gradient-to-r from-pixel-gold via-orange-400 to-pixel-gold bg-clip-text text-transparent"
|
||||
style={{
|
||||
textShadow: "0 0 30px rgba(218, 165, 32, 0.5)",
|
||||
}}
|
||||
>
|
||||
PROFIL
|
||||
</span>
|
||||
</h1>
|
||||
<div className="text-pixel-gold text-lg md:text-xl font-gaming-subtitle font-semibold flex items-center justify-center gap-2 tracking-wide">
|
||||
<span>✦</span>
|
||||
<span>Gérez votre profil</span>
|
||||
<span>✦</span>
|
||||
</div>
|
||||
</div>
|
||||
<SectionTitle variant="gradient" size="lg" subtitle="Gérez votre profil" className="mb-12">
|
||||
PROFIL
|
||||
</SectionTitle>
|
||||
|
||||
{/* Profile Card */}
|
||||
<div className="bg-black/60 border border-pixel-gold/30 rounded-lg overflow-hidden backdrop-blur-sm">
|
||||
<Card variant="default" className="overflow-hidden">
|
||||
<form onSubmit={handleSubmit} className="p-8 space-y-8">
|
||||
{/* Messages */}
|
||||
{error && (
|
||||
<div className="bg-red-900/50 border border-red-500/50 text-red-400 px-4 py-3 rounded text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
{success && (
|
||||
<div className="bg-green-900/50 border border-green-500/50 text-green-400 px-4 py-3 rounded text-sm">
|
||||
{success}
|
||||
</div>
|
||||
)}
|
||||
{error && <Alert variant="error">{error}</Alert>}
|
||||
{success && <Alert variant="success">{success}</Alert>}
|
||||
|
||||
{/* Avatar Section */}
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
@@ -281,51 +247,38 @@ export default function ProfileForm({
|
||||
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>
|
||||
|
||||
{/* Username Field */}
|
||||
<div>
|
||||
<label className="block text-pixel-gold text-sm uppercase tracking-widest mb-2">
|
||||
Nom d'utilisateur
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
className="w-full px-4 py-3 bg-black/40 border border-pixel-gold/30 rounded text-white focus:outline-none focus:border-pixel-gold transition"
|
||||
required
|
||||
minLength={3}
|
||||
maxLength={20}
|
||||
/>
|
||||
<p className="text-gray-500 text-xs mt-1">3-20 caractères</p>
|
||||
</div>
|
||||
<Input
|
||||
type="text"
|
||||
label="Nom d'utilisateur"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
required
|
||||
minLength={3}
|
||||
maxLength={20}
|
||||
className="bg-black/40"
|
||||
/>
|
||||
<p className="text-gray-500 text-xs mt-1">3-20 caractères</p>
|
||||
|
||||
{/* Bio Field */}
|
||||
<div>
|
||||
<label className="block text-pixel-gold text-sm uppercase tracking-widest mb-2">
|
||||
Bio
|
||||
</label>
|
||||
<textarea
|
||||
value={bio || ""}
|
||||
onChange={(e) => setBio(e.target.value)}
|
||||
className="w-full px-4 py-3 bg-black/40 border border-pixel-gold/30 rounded text-white 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">
|
||||
{(bio || "").length}/500 caractères
|
||||
</p>
|
||||
</div>
|
||||
<Textarea
|
||||
label="Bio"
|
||||
value={bio || ""}
|
||||
onChange={(e) => setBio(e.target.value)}
|
||||
rows={4}
|
||||
maxLength={500}
|
||||
showCharCount
|
||||
placeholder="Parlez-nous de vous..."
|
||||
className="bg-black/40"
|
||||
/>
|
||||
|
||||
{/* Character Class Selection */}
|
||||
<div>
|
||||
@@ -458,36 +411,23 @@ export default function ProfileForm({
|
||||
</div>
|
||||
|
||||
{/* HP Bar */}
|
||||
<div className="mb-4">
|
||||
<div className="flex justify-between text-xs text-gray-400 mb-1">
|
||||
<span>HP</span>
|
||||
<span>
|
||||
{profile.hp} / {profile.maxHp}
|
||||
</span>
|
||||
</div>
|
||||
<div className="relative h-3 bg-gray-900 border border-gray-700 rounded overflow-hidden">
|
||||
<div
|
||||
className={`absolute inset-0 bg-gradient-to-r ${hpColor} transition-all duration-1000 ease-out`}
|
||||
style={{ width: `${hpPercentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ProgressBar
|
||||
value={profile.hp}
|
||||
max={profile.maxHp}
|
||||
variant="hp"
|
||||
showLabel
|
||||
label="HP"
|
||||
className="mb-4"
|
||||
/>
|
||||
|
||||
{/* XP Bar */}
|
||||
<div>
|
||||
<div className="flex justify-between text-xs text-gray-400 mb-1">
|
||||
<span>XP</span>
|
||||
<span>
|
||||
{formatNumber(profile.xp)} / {formatNumber(profile.maxXp)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="relative h-3 bg-gray-900 border border-pixel-gold/30 rounded overflow-hidden">
|
||||
<div
|
||||
className="absolute inset-0 bg-gradient-to-r from-pixel-gold/80 via-pixel-gold/70 to-pixel-gold/80 transition-all duration-1000 ease-out"
|
||||
style={{ width: `${xpPercentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ProgressBar
|
||||
value={profile.xp}
|
||||
max={profile.maxXp}
|
||||
variant="xp"
|
||||
showLabel
|
||||
label="XP"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Email (read-only) */}
|
||||
@@ -505,13 +445,9 @@ export default function ProfileForm({
|
||||
|
||||
{/* Submit Button */}
|
||||
<div className="flex justify-end gap-4 pt-4 border-t border-pixel-gold/20">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isPending}
|
||||
className="px-6 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 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<Button type="submit" variant="primary" size="md" disabled={isPending}>
|
||||
{isPending ? "Enregistrement..." : "Enregistrer les modifications"}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -522,65 +458,56 @@ export default function ProfileForm({
|
||||
Mot de passe
|
||||
</h3>
|
||||
{!showPasswordForm && (
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
variant="primary"
|
||||
size="md"
|
||||
onClick={() => setShowPasswordForm(true)}
|
||||
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"
|
||||
>
|
||||
Changer le mot de passe
|
||||
</button>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{showPasswordForm && (
|
||||
<form onSubmit={handlePasswordChange} className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-pixel-gold text-sm uppercase tracking-widest mb-2">
|
||||
Mot de passe actuel
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
value={currentPassword}
|
||||
onChange={(e) => setCurrentPassword(e.target.value)}
|
||||
className="w-full px-4 py-3 bg-black/40 border border-pixel-gold/30 rounded text-white focus:outline-none focus:border-pixel-gold transition"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
type="password"
|
||||
label="Mot de passe actuel"
|
||||
value={currentPassword}
|
||||
onChange={(e) => setCurrentPassword(e.target.value)}
|
||||
required
|
||||
className="bg-black/40"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<label className="block text-pixel-gold text-sm uppercase tracking-widest mb-2">
|
||||
Nouveau mot de passe
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
value={newPassword}
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
className="w-full px-4 py-3 bg-black/40 border border-pixel-gold/30 rounded text-white focus:outline-none focus:border-pixel-gold transition"
|
||||
required
|
||||
minLength={6}
|
||||
/>
|
||||
<p className="text-gray-500 text-xs mt-1">
|
||||
Minimum 6 caractères
|
||||
</p>
|
||||
</div>
|
||||
<Input
|
||||
type="password"
|
||||
label="Nouveau mot de passe"
|
||||
value={newPassword}
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
required
|
||||
minLength={6}
|
||||
className="bg-black/40"
|
||||
/>
|
||||
<p className="text-gray-500 text-xs mt-1">
|
||||
Minimum 6 caractères
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<label className="block text-pixel-gold text-sm uppercase tracking-widest mb-2">
|
||||
Confirmer le nouveau mot de passe
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
className="w-full px-4 py-3 bg-black/40 border border-pixel-gold/30 rounded text-white focus:outline-none focus:border-pixel-gold transition"
|
||||
required
|
||||
minLength={6}
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
type="password"
|
||||
label="Confirmer le nouveau mot de passe"
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
required
|
||||
minLength={6}
|
||||
className="bg-black/40"
|
||||
/>
|
||||
|
||||
<div className="flex justify-end gap-4">
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="md"
|
||||
onClick={() => {
|
||||
setShowPasswordForm(false);
|
||||
setCurrentPassword("");
|
||||
@@ -588,25 +515,25 @@ export default function ProfileForm({
|
||||
setConfirmPassword("");
|
||||
setError(null);
|
||||
}}
|
||||
className="px-4 py-2 border border-gray-600/50 bg-black/40 text-gray-400 uppercase text-xs tracking-widest rounded hover:bg-gray-900/40 hover:border-gray-500 transition"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<button
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
size="md"
|
||||
disabled={isChangingPassword}
|
||||
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 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{isChangingPassword
|
||||
? "Modification..."
|
||||
: "Modifier le mot de passe"}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
</BackgroundSection>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user