"use client"; import { useEffect, useState } from "react"; import { useSession } from "next-auth/react"; import Link from "next/link"; import Avatar from "./Avatar"; interface UserData { username: string; avatar: string | null; hp: number; maxHp: number; xp: number; maxXp: number; level: number; } interface PlayerStatsProps { initialUserData?: UserData | null; } // Format number with consistent locale to avoid hydration mismatch const formatNumber = (num: number): string => { return num.toLocaleString("en-US"); }; const defaultUserData: UserData = { username: "Guest", avatar: null, hp: 1000, maxHp: 1000, xp: 0, maxXp: 5000, level: 1, }; export default function PlayerStats({ initialUserData }: PlayerStatsProps) { const { data: session } = useSession(); const [userData, setUserData] = useState( initialUserData || defaultUserData ); useEffect(() => { // Si on a déjà des données initiales, ne rien faire (déjà initialisé dans useState) if (initialUserData) { return; } // Sinon, fallback sur le fetch côté client (pour les pages Client Components) if (session?.user?.id) { fetch(`/api/users/${session.user.id}`) .then((res) => res.json()) .then((data) => { if (data) { // Utiliser requestAnimationFrame pour éviter les cascades de rendu requestAnimationFrame(() => { setUserData({ username: data.username || "Guest", avatar: data.avatar, hp: data.hp || 1000, maxHp: data.maxHp || 1000, xp: data.xp || 0, maxXp: data.maxXp || 5000, level: data.level || 1, }); }); } }) .catch(() => { // Utiliser les données de session si l'API échoue requestAnimationFrame(() => { setUserData({ username: session.user.username || "Guest", avatar: null, hp: 1000, maxHp: 1000, xp: 0, maxXp: 5000, level: 1, }); }); }); } else if (!initialUserData) { // Utiliser requestAnimationFrame pour éviter les cascades de rendu requestAnimationFrame(() => { setUserData(defaultUserData); }); } }, [session, initialUserData]); const { username, avatar, hp, maxHp, xp, maxXp, level } = userData; // Calculer les pourcentages cibles const targetHpPercentage = (hp / maxHp) * 100; const targetXpPercentage = (xp / maxXp) * 100; // Initialiser les pourcentages à 0 si on a des données initiales (pour l'animation) // Sinon utiliser directement les valeurs calculées const [hpPercentage, setHpPercentage] = useState( initialUserData ? 0 : targetHpPercentage ); const [xpPercentage, setXpPercentage] = useState( initialUserData ? 0 : targetXpPercentage ); useEffect(() => { // Si on a des données initiales, animer depuis 0 vers la valeur cible if (initialUserData) { const hpTimer = setTimeout(() => { setHpPercentage(targetHpPercentage); }, 100); const xpTimer = setTimeout(() => { setXpPercentage(targetXpPercentage); }, 200); return () => { clearTimeout(hpTimer); clearTimeout(xpTimer); }; } // Sinon, mettre à jour directement (pour les pages Client Components) // Utiliser requestAnimationFrame pour éviter les cascades de rendu requestAnimationFrame(() => { setHpPercentage(targetHpPercentage); setXpPercentage(targetXpPercentage); }); }, [targetHpPercentage, targetXpPercentage, initialUserData]); const hpColor = hpPercentage > 60 ? "from-green-600 to-green-700" : hpPercentage > 30 ? "from-yellow-600 to-orange-700" : "from-red-700 to-red-900"; return (
{/* Avatar */} {/* Stats */}
{/* Username & Level */}
{username}
Lv.{level}
{/* Bars side by side */}
{/* HP Bar */}
{hpPercentage < 30 && (
)}
{/* XP Bar */}
{/* Labels */}
HP {hp} / {maxHp}
XP {formatNumber(xp)} / {formatNumber(maxXp)}
); }