diff --git a/app/register/page.tsx b/app/register/page.tsx
index bfcebfd..23b580e 100644
--- a/app/register/page.tsx
+++ b/app/register/page.tsx
@@ -14,6 +14,7 @@ import {
BackgroundSection,
SectionTitle,
} from "@/components/ui";
+import { CHARACTER_CLASSES } from "@/lib/character-classes";
export default function RegisterPage() {
const router = useRouter();
@@ -397,22 +398,7 @@ export default function RegisterPage() {
Classe de Personnage (optionnel)
- {[
- { value: "WARRIOR", name: "Guerrier", icon: "⚔️" },
- { value: "MAGE", name: "Mage", icon: "🔮" },
- { value: "ROGUE", name: "Voleur", icon: "🗡️" },
- { value: "RANGER", name: "Rôdeur", icon: "🏹" },
- { value: "PALADIN", name: "Paladin", icon: "🛡️" },
- { value: "ENGINEER", name: "Ingénieur", icon: "⚙️" },
- { value: "MERCHANT", name: "Marchand", icon: "💰" },
- { value: "SCHOLAR", name: "Érudit", icon: "📚" },
- { value: "BERSERKER", name: "Berserker", icon: "🔥" },
- {
- value: "NECROMANCER",
- name: "Nécromancien",
- icon: "💀",
- },
- ].map((cls) => (
+ {CHARACTER_CLASSES.map((cls) => (
@@ -177,34 +167,10 @@ export default function Leaderboard() {
{selectedEntry.characterClass && (
- {selectedEntry.characterClass === "WARRIOR" && "⚔️"}
- {selectedEntry.characterClass === "MAGE" && "🔮"}
- {selectedEntry.characterClass === "ROGUE" && "🗡️"}
- {selectedEntry.characterClass === "RANGER" && "🏹"}
- {selectedEntry.characterClass === "PALADIN" && "🛡️"}
- {selectedEntry.characterClass === "ENGINEER" && "⚙️"}
- {selectedEntry.characterClass === "MERCHANT" && "💰"}
- {selectedEntry.characterClass === "SCHOLAR" && "📚"}
- {selectedEntry.characterClass === "BERSERKER" && "🔥"}
- {selectedEntry.characterClass === "NECROMANCER" && "💀"}
+ {getCharacterClassIcon(selectedEntry.characterClass)}
- {selectedEntry.characterClass === "WARRIOR" &&
- "Guerrier"}
- {selectedEntry.characterClass === "MAGE" && "Mage"}
- {selectedEntry.characterClass === "ROGUE" && "Voleur"}
- {selectedEntry.characterClass === "RANGER" && "Rôdeur"}
- {selectedEntry.characterClass === "PALADIN" &&
- "Paladin"}
- {selectedEntry.characterClass === "ENGINEER" &&
- "Ingénieur"}
- {selectedEntry.characterClass === "MERCHANT" &&
- "Marchand"}
- {selectedEntry.characterClass === "SCHOLAR" && "Érudit"}
- {selectedEntry.characterClass === "BERSERKER" &&
- "Berserker"}
- {selectedEntry.characterClass === "NECROMANCER" &&
- "Nécromancien"}
+ {getCharacterClassName(selectedEntry.characterClass)}
)}
diff --git a/components/leaderboard/LeaderboardSection.tsx b/components/leaderboard/LeaderboardSection.tsx
index d34c080..e79367c 100644
--- a/components/leaderboard/LeaderboardSection.tsx
+++ b/components/leaderboard/LeaderboardSection.tsx
@@ -9,6 +9,11 @@ import {
BackgroundSection,
SectionTitle,
} from "@/components/ui";
+import {
+ getCharacterClassIcon,
+ getCharacterClassName,
+ type CharacterClass,
+} from "@/lib/character-classes";
interface LeaderboardEntry {
rank: number;
@@ -18,7 +23,7 @@ interface LeaderboardEntry {
level: number;
avatar?: string | null;
bio?: string | null;
- characterClass?: string | null;
+ characterClass?: CharacterClass | null;
}
interface LeaderboardSectionProps {
@@ -111,16 +116,7 @@ export default function LeaderboardSection({
{entry.characterClass && (
- [{entry.characterClass === "WARRIOR" && "⚔️"}
- {entry.characterClass === "MAGE" && "🔮"}
- {entry.characterClass === "ROGUE" && "🗡️"}
- {entry.characterClass === "RANGER" && "🏹"}
- {entry.characterClass === "PALADIN" && "🛡️"}
- {entry.characterClass === "ENGINEER" && "⚙️"}
- {entry.characterClass === "MERCHANT" && "💰"}
- {entry.characterClass === "SCHOLAR" && "📚"}
- {entry.characterClass === "BERSERKER" && "🔥"}
- {entry.characterClass === "NECROMANCER" && "💀"}]
+ [{getCharacterClassIcon(entry.characterClass)}]
)}
{entry.rank <= 3 && (
@@ -190,32 +186,10 @@ export default function LeaderboardSection({
{selectedEntry.characterClass && (
- {selectedEntry.characterClass === "WARRIOR" && "⚔️"}
- {selectedEntry.characterClass === "MAGE" && "🔮"}
- {selectedEntry.characterClass === "ROGUE" && "🗡️"}
- {selectedEntry.characterClass === "RANGER" && "🏹"}
- {selectedEntry.characterClass === "PALADIN" && "🛡️"}
- {selectedEntry.characterClass === "ENGINEER" && "⚙️"}
- {selectedEntry.characterClass === "MERCHANT" && "💰"}
- {selectedEntry.characterClass === "SCHOLAR" && "📚"}
- {selectedEntry.characterClass === "BERSERKER" && "🔥"}
- {selectedEntry.characterClass === "NECROMANCER" && "💀"}
+ {getCharacterClassIcon(selectedEntry.characterClass)}
- {selectedEntry.characterClass === "WARRIOR" && "Guerrier"}
- {selectedEntry.characterClass === "MAGE" && "Mage"}
- {selectedEntry.characterClass === "ROGUE" && "Voleur"}
- {selectedEntry.characterClass === "RANGER" && "Rôdeur"}
- {selectedEntry.characterClass === "PALADIN" && "Paladin"}
- {selectedEntry.characterClass === "ENGINEER" &&
- "Ingénieur"}
- {selectedEntry.characterClass === "MERCHANT" &&
- "Marchand"}
- {selectedEntry.characterClass === "SCHOLAR" && "Érudit"}
- {selectedEntry.characterClass === "BERSERKER" &&
- "Berserker"}
- {selectedEntry.characterClass === "NECROMANCER" &&
- "Nécromancien"}
+ {getCharacterClassName(selectedEntry.characterClass)}
)}
diff --git a/components/profile/ProfileForm.tsx b/components/profile/ProfileForm.tsx
index f04d7ae..a29197f 100644
--- a/components/profile/ProfileForm.tsx
+++ b/components/profile/ProfileForm.tsx
@@ -1,22 +1,23 @@
"use client";
import { useState, useRef, useTransition, type ChangeEvent } from "react";
-import { Avatar, Input, Textarea, Button, Alert, Card, BackgroundSection, SectionTitle, ProgressBar } from "@/components/ui";
+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";
-
-type CharacterClass =
- | "WARRIOR"
- | "MAGE"
- | "ROGUE"
- | "RANGER"
- | "PALADIN"
- | "ENGINEER"
- | "MERCHANT"
- | "SCHOLAR"
- | "BERSERKER"
- | "NECROMANCER"
- | null;
+import {
+ CHARACTER_CLASSES,
+ type CharacterClass,
+} from "@/lib/character-classes";
interface UserProfile {
id: string;
@@ -24,7 +25,7 @@ interface UserProfile {
username: string;
avatar: string | null;
bio: string | null;
- characterClass: CharacterClass;
+ characterClass: CharacterClass | null;
hp: number;
maxHp: number;
xp: number;
@@ -55,7 +56,7 @@ export default function ProfileForm({
const [username, setUsername] = useState(initialProfile.username);
const [avatar, setAvatar] = useState(initialProfile.avatar);
const [bio, setBio] = useState(initialProfile.bio || null);
- const [characterClass, setCharacterClass] = useState(
+ const [characterClass, setCharacterClass] = useState(
initialProfile.characterClass || null
);
const fileInputRef = useRef(null);
@@ -120,12 +121,15 @@ export default function ProfileForm({
if (result.success && result.data) {
setProfile({
...result.data,
- createdAt: result.data.createdAt instanceof Date
- ? result.data.createdAt.toISOString()
- : result.data.createdAt,
+ createdAt:
+ result.data.createdAt instanceof Date
+ ? result.data.createdAt.toISOString()
+ : result.data.createdAt,
} as UserProfile);
setBio(result.data.bio || null);
- setCharacterClass(result.data.characterClass as CharacterClass || null);
+ setCharacterClass(
+ (result.data.characterClass as CharacterClass) || null
+ );
setSuccess("Profil mis à jour avec succès");
setTimeout(() => setSuccess(null), 3000);
} else {
@@ -154,7 +158,9 @@ export default function ProfileForm({
setShowPasswordForm(false);
setTimeout(() => setSuccess(null), 3000);
} else {
- setError(result.error || "Erreur lors de la modification du mot de passe");
+ setError(
+ result.error || "Erreur lors de la modification du mot de passe"
+ );
}
});
};
@@ -163,7 +169,12 @@ export default function ProfileForm({
{/* Title Section */}
-
+
PROFIL
@@ -238,8 +249,15 @@ export default function ProfileForm({
id="avatar-upload"
/>
@@ -276,68 +294,7 @@ export default function ProfileForm({
Classe de Personnage
- {[
- {
- 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) => (
+ {CHARACTER_CLASSES.map((cls) => (
-
- {isPending ? "Enregistrement..." : "Enregistrer les modifications"}
+
+ {isPending
+ ? "Enregistrement..."
+ : "Enregistrer les modifications"}
diff --git a/lib/character-classes.ts b/lib/character-classes.ts
new file mode 100644
index 0000000..f86c72e
--- /dev/null
+++ b/lib/character-classes.ts
@@ -0,0 +1,118 @@
+export type CharacterClass =
+ | "WARRIOR"
+ | "MAGE"
+ | "ROGUE"
+ | "RANGER"
+ | "PALADIN"
+ | "ENGINEER"
+ | "MERCHANT"
+ | "SCHOLAR"
+ | "BERSERKER"
+ | "NECROMANCER";
+
+export interface CharacterClassConfig {
+ value: CharacterClass;
+ name: string;
+ icon: string;
+ desc?: string;
+}
+
+export const CHARACTER_CLASSES: CharacterClassConfig[] = [
+ {
+ 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",
+ },
+];
+
+export const CHARACTER_CLASS_MAP: Record =
+ CHARACTER_CLASSES.reduce(
+ (acc, cls) => {
+ acc[cls.value] = cls;
+ return acc;
+ },
+ {} as Record
+ );
+
+export function getCharacterClassIcon(
+ characterClass: CharacterClass | null | undefined
+): string {
+ if (!characterClass) return "";
+ return CHARACTER_CLASS_MAP[characterClass]?.icon || "";
+}
+
+export function getCharacterClassName(
+ characterClass: CharacterClass | null | undefined
+): string {
+ if (!characterClass) return "";
+ return CHARACTER_CLASS_MAP[characterClass]?.name || "";
+}
+
+export function getCharacterClassConfig(
+ characterClass: CharacterClass | null | undefined
+): CharacterClassConfig | null {
+ if (!characterClass) return null;
+ return CHARACTER_CLASS_MAP[characterClass] || null;
+}
+
+export function isValidCharacterClass(
+ characterClass: string | null | undefined
+): characterClass is CharacterClass {
+ if (!characterClass) return false;
+ return characterClass in CHARACTER_CLASS_MAP;
+}
diff --git a/services/users/user-stats.service.ts b/services/users/user-stats.service.ts
index 2535a15..6db381e 100644
--- a/services/users/user-stats.service.ts
+++ b/services/users/user-stats.service.ts
@@ -1,5 +1,5 @@
import { prisma } from "../database";
-import type { User, Role, Prisma } from "@/prisma/generated/prisma/client";
+import type { User, Role, Prisma, CharacterClass } from "@/prisma/generated/prisma/client";
import { NotFoundError } from "../errors";
import { userService } from "./user.service";
@@ -19,7 +19,7 @@ export interface LeaderboardEntry {
level: number;
avatar: string | null;
bio: string | null;
- characterClass: string | null;
+ characterClass: CharacterClass | null;
}
/**
diff --git a/services/users/user.service.ts b/services/users/user.service.ts
index c34e6cb..5683952 100644
--- a/services/users/user.service.ts
+++ b/services/users/user.service.ts
@@ -6,20 +6,7 @@ import type {
Prisma,
} from "@/prisma/generated/prisma/client";
import { ValidationError, NotFoundError, ConflictError } from "../errors";
-
-// Constantes de validation
-const VALID_CHARACTER_CLASSES = [
- "WARRIOR",
- "MAGE",
- "ROGUE",
- "RANGER",
- "PALADIN",
- "ENGINEER",
- "MERCHANT",
- "SCHOLAR",
- "BERSERKER",
- "NECROMANCER",
-] as const;
+import { isValidCharacterClass } from "@/lib/character-classes";
const USERNAME_MIN_LENGTH = 3;
const USERNAME_MAX_LENGTH = 20;
@@ -305,10 +292,7 @@ export class UserService {
}
// Validation du characterClass
- if (
- data.characterClass &&
- !VALID_CHARACTER_CLASSES.includes(data.characterClass as CharacterClass)
- ) {
+ if (data.characterClass && !isValidCharacterClass(data.characterClass)) {
throw new ValidationError(
"Classe de personnage invalide",
"characterClass"
@@ -402,7 +386,7 @@ export class UserService {
if (
data.characterClass !== undefined &&
data.characterClass !== null &&
- !VALID_CHARACTER_CLASSES.includes(data.characterClass as CharacterClass)
+ !isValidCharacterClass(data.characterClass)
) {
throw new ValidationError(
"Classe de personnage invalide",