Enhance user profiles with character class feature: Add character class field to user model and update related API routes, UI components, and validation logic. This update improves user profile customization and leaderboard entries by allowing users to select and display their character class.
This commit is contained in:
@@ -3,12 +3,26 @@
|
||||
import { useState, useRef } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
type CharacterClass =
|
||||
| "WARRIOR"
|
||||
| "MAGE"
|
||||
| "ROGUE"
|
||||
| "RANGER"
|
||||
| "PALADIN"
|
||||
| "ENGINEER"
|
||||
| "MERCHANT"
|
||||
| "SCHOLAR"
|
||||
| "BERSERKER"
|
||||
| "NECROMANCER"
|
||||
| null;
|
||||
|
||||
interface UserProfile {
|
||||
id: string;
|
||||
email: string;
|
||||
username: string;
|
||||
avatar: string | null;
|
||||
bio: string | null;
|
||||
characterClass: CharacterClass;
|
||||
hp: number;
|
||||
maxHp: number;
|
||||
xp: number;
|
||||
@@ -40,6 +54,9 @@ export default function ProfileForm({
|
||||
const [username, setUsername] = useState(initialProfile.username);
|
||||
const [avatar, setAvatar] = useState<string | null>(initialProfile.avatar);
|
||||
const [bio, setBio] = useState<string | null>(initialProfile.bio || null);
|
||||
const [characterClass, setCharacterClass] = useState<CharacterClass>(
|
||||
initialProfile.characterClass || null
|
||||
);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [uploadingAvatar, setUploadingAvatar] = useState(false);
|
||||
|
||||
@@ -102,6 +119,7 @@ export default function ProfileForm({
|
||||
username,
|
||||
avatar,
|
||||
bio,
|
||||
characterClass,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -109,6 +127,7 @@ export default function ProfileForm({
|
||||
const data = await response.json();
|
||||
setProfile(data);
|
||||
setBio(data.bio || null);
|
||||
setCharacterClass(data.characterClass || null);
|
||||
setSuccess("Profil mis à jour avec succès");
|
||||
setTimeout(() => setSuccess(null), 3000);
|
||||
} else {
|
||||
@@ -338,6 +357,111 @@ export default function ProfileForm({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Character Class Selection */}
|
||||
<div>
|
||||
<label className="block text-pixel-gold text-sm uppercase tracking-widest mb-3">
|
||||
Classe de Personnage
|
||||
</label>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
{[
|
||||
{
|
||||
value: "WARRIOR",
|
||||
name: "Guerrier",
|
||||
icon: "⚔️",
|
||||
desc: "Maître du combat au corps à corps",
|
||||
},
|
||||
{
|
||||
value: "MAGE",
|
||||
name: "Mage",
|
||||
icon: "🔮",
|
||||
desc: "Manipulateur des arcanes",
|
||||
},
|
||||
{
|
||||
value: "ROGUE",
|
||||
name: "Voleur",
|
||||
icon: "🗡️",
|
||||
desc: "Furtif et mortel",
|
||||
},
|
||||
{
|
||||
value: "RANGER",
|
||||
name: "Rôdeur",
|
||||
icon: "🏹",
|
||||
desc: "Chasseur des terres sauvages",
|
||||
},
|
||||
{
|
||||
value: "PALADIN",
|
||||
name: "Paladin",
|
||||
icon: "🛡️",
|
||||
desc: "Protecteur sacré",
|
||||
},
|
||||
{
|
||||
value: "ENGINEER",
|
||||
name: "Ingénieur",
|
||||
icon: "⚙️",
|
||||
desc: "Créateur d'artefacts",
|
||||
},
|
||||
{
|
||||
value: "MERCHANT",
|
||||
name: "Marchand",
|
||||
icon: "💰",
|
||||
desc: "Maître du commerce",
|
||||
},
|
||||
{
|
||||
value: "SCHOLAR",
|
||||
name: "Érudit",
|
||||
icon: "📚",
|
||||
desc: "Gardien du savoir",
|
||||
},
|
||||
{
|
||||
value: "BERSERKER",
|
||||
name: "Berserker",
|
||||
icon: "🔥",
|
||||
desc: "Rage destructrice",
|
||||
},
|
||||
{
|
||||
value: "NECROMANCER",
|
||||
name: "Nécromancien",
|
||||
icon: "💀",
|
||||
desc: "Maître des morts",
|
||||
},
|
||||
].map((cls) => (
|
||||
<button
|
||||
key={cls.value}
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setCharacterClass(cls.value as CharacterClass)
|
||||
}
|
||||
className={`p-4 border-2 rounded-lg text-left transition-all ${
|
||||
characterClass === cls.value
|
||||
? "border-pixel-gold bg-pixel-gold/20 shadow-lg shadow-pixel-gold/30"
|
||||
: "border-pixel-gold/30 bg-black/40 hover:border-pixel-gold/50 hover:bg-black/60"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className="text-2xl">{cls.icon}</span>
|
||||
<span
|
||||
className={`font-bold text-sm uppercase tracking-wider ${
|
||||
characterClass === cls.value
|
||||
? "text-pixel-gold"
|
||||
: "text-white"
|
||||
}`}
|
||||
>
|
||||
{cls.name}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-400 leading-tight">
|
||||
{cls.desc}
|
||||
</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{characterClass && (
|
||||
<p className="text-pixel-gold text-xs mt-2 uppercase tracking-widest">
|
||||
✓ Classe sélectionnée
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Stats Display */}
|
||||
<div className="border-t border-pixel-gold/20 pt-6">
|
||||
<h3 className="text-pixel-gold text-sm uppercase tracking-widest mb-4">
|
||||
|
||||
Reference in New Issue
Block a user