From 44be5d2e982c69c1a0e262d2a694a15131beb39d Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Wed, 10 Dec 2025 06:01:34 +0100 Subject: [PATCH] Remove ESLint configuration file and update type imports across components: Deleted eslint.config.js to streamline project setup. Updated type imports in layout, login, register, and other components to use direct imports for improved clarity and consistency. Enhanced error handling in various components and replaced apostrophes with HTML entities for better rendering. --- app/layout.tsx | 3 +- app/login/page.tsx | 7 ++-- app/register/page.tsx | 14 +++---- components/AdminPanel.tsx | 3 +- components/EventsPageSection.tsx | 6 +-- components/HeroSection.tsx | 10 ++--- components/ImageSelector.tsx | 10 ++--- components/PlayerStats.tsx | 69 +++++++++++++++++++------------- components/ProfileForm.tsx | 8 ++-- components/SessionProvider.tsx | 8 +--- eslint.config.js | 62 ---------------------------- hooks/usePreferences.ts | 5 ++- 12 files changed, 75 insertions(+), 130 deletions(-) delete mode 100644 eslint.config.js diff --git a/app/layout.tsx b/app/layout.tsx index 45ec66e..51f2691 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,4 +1,5 @@ import type { Metadata } from "next"; +import type { ReactNode } from "react"; import { Orbitron, Rajdhani } from "next/font/google"; import "./globals.css"; import SessionProvider from "@/components/SessionProvider"; @@ -23,7 +24,7 @@ export const metadata: Metadata = { export default function RootLayout({ children, }: Readonly<{ - children: React.ReactNode; + children: ReactNode; }>) { return ( diff --git a/app/login/page.tsx b/app/login/page.tsx index a749255..07627b5 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useState, type FormEvent } from "react"; import { signIn } from "next-auth/react"; import { useRouter } from "next/navigation"; import Link from "next/link"; @@ -13,7 +13,7 @@ export default function LoginPage() { const [error, setError] = useState(""); const [loading, setLoading] = useState(false); - const handleSubmit = async (e: React.FormEvent) => { + const handleSubmit = async (e: FormEvent) => { e.preventDefault(); setError(""); setLoading(true); @@ -128,7 +128,7 @@ export default function LoginPage() { href="/register" className="text-pixel-gold hover:text-orange-400 transition" > - S'inscrire + S'inscrire

@@ -138,4 +138,3 @@ export default function LoginPage() { ); } - diff --git a/app/register/page.tsx b/app/register/page.tsx index 6b7a902..5f60495 100644 --- a/app/register/page.tsx +++ b/app/register/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useRef } from "react"; +import { useState, useRef, type ChangeEvent, type FormEvent } from "react"; import { useRouter } from "next/navigation"; import Link from "next/link"; import Navigation from "@/components/Navigation"; @@ -24,7 +24,7 @@ export default function RegisterPage() { const fileInputRef = useRef(null); const handleChange = ( - e: React.ChangeEvent + e: ChangeEvent ) => { setFormData({ ...formData, @@ -32,7 +32,7 @@ export default function RegisterPage() { }); }; - const handleAvatarUpload = async (e: React.ChangeEvent) => { + const handleAvatarUpload = async (e: ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; @@ -110,14 +110,14 @@ export default function RegisterPage() { setUserId(data.userId); setStep(2); - } catch (err) { + } catch { setError("Une erreur est survenue"); } finally { setLoading(false); } }; - const handleStep2Submit = async (e: React.FormEvent) => { + const handleStep2Submit = async (e: FormEvent) => { e.preventDefault(); setError(""); @@ -245,7 +245,7 @@ export default function RegisterPage() { htmlFor="username" className="block text-sm font-semibold text-gray-300 mb-2 uppercase tracking-wider" > - Nom d'utilisateur + Nom d'utilisateur - Nom d'utilisateur + Nom d'utilisateur

- Ces préférences s'appliquent à tous les utilisateurs + Ces préférences s'appliquent à tous les utilisateurs

{!isEditing && ( @@ -278,4 +278,3 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) { ); } - diff --git a/components/EventsPageSection.tsx b/components/EventsPageSection.tsx index 3c35c63..ea745ea 100644 --- a/components/EventsPageSection.tsx +++ b/components/EventsPageSection.tsx @@ -533,7 +533,7 @@ export default function EventsPageSection({ ...prev, [eventId]: true, })); - } catch (err) { + } catch { setError("Une erreur est survenue"); } finally { setLoading((prev) => ({ ...prev, [eventId]: false })); @@ -559,7 +559,7 @@ export default function EventsPageSection({ ...prev, [eventId]: false, })); - } catch (err) { + } catch { setError("Une erreur est survenue"); } finally { setLoading((prev) => ({ ...prev, [eventId]: false })); @@ -600,7 +600,7 @@ export default function EventsPageSection({

Rejoignez-nous pour des événements tech passionnants, des - compétitions et des célébrations tout au long de l'année + compétitions et des célébrations tout au long de l'année

diff --git a/components/HeroSection.tsx b/components/HeroSection.tsx index 8fa3a7e..cc637af 100644 --- a/components/HeroSection.tsx +++ b/components/HeroSection.tsx @@ -100,11 +100,11 @@ export default function HeroSection() { {/* Description */}

- Dans un monde numérique de technologie de pointe, où les systèmes d'IA - évoluent et où d'anciens codes attendent d'être découverts. Partez - pour un voyage épique pour forger des alliances, conquérir des défis - et raconter votre histoire d'innovation au sein d'une communauté de - gaming tech florissante. + Dans un monde numérique de technologie de pointe, où les systèmes + d'IA évoluent et où d'anciens codes attendent d'être + découverts. Partez pour un voyage épique pour forger des alliances, + conquérir des défis et raconter votre histoire d'innovation au + sein d'une communauté de gaming tech florissante.

{/* Call-to-Action Buttons */} diff --git a/components/ImageSelector.tsx b/components/ImageSelector.tsx index c738c65..03a8675 100644 --- a/components/ImageSelector.tsx +++ b/components/ImageSelector.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect, useRef } from "react"; +import { useState, useEffect, useRef, type ChangeEvent } from "react"; interface ImageSelectorProps { value: string; @@ -14,7 +14,6 @@ export default function ImageSelector({ label, }: ImageSelectorProps) { const [availableImages, setAvailableImages] = useState([]); - const [loading, setLoading] = useState(false); const [uploading, setUploading] = useState(false); const [urlInput, setUrlInput] = useState(""); const [showGallery, setShowGallery] = useState(false); @@ -36,7 +35,7 @@ export default function ImageSelector({ } }; - const handleFileUpload = async (e: React.ChangeEvent) => { + const handleFileUpload = async (e: ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; @@ -153,9 +152,7 @@ export default function ImageSelector({ {/* Chemin de l'image */} - {value && ( -

{value}

- )} + {value &&

{value}

} @@ -204,4 +201,3 @@ export default function ImageSelector({ ); } - diff --git a/components/PlayerStats.tsx b/components/PlayerStats.tsx index 07adc84..c54ee8c 100644 --- a/components/PlayerStats.tsx +++ b/components/PlayerStats.tsx @@ -51,40 +51,48 @@ export default function PlayerStats({ initialUserData }: PlayerStatsProps) { .then((res) => res.json()) .then((data) => { if (data) { - 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, + // 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 - setUserData({ - username: session.user.username || "Guest", - avatar: null, - hp: 1000, - maxHp: 1000, - xp: 0, - maxXp: 5000, - level: 1, + requestAnimationFrame(() => { + setUserData({ + username: session.user.username || "Guest", + avatar: null, + hp: 1000, + maxHp: 1000, + xp: 0, + maxXp: 5000, + level: 1, + }); }); }); - } else { - setUserData(defaultUserData); + } 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( @@ -109,11 +117,13 @@ export default function PlayerStats({ initialUserData }: PlayerStatsProps) { clearTimeout(hpTimer); clearTimeout(xpTimer); }; - } else { - // Sinon, mettre à jour directement (pour les pages Client Components) + } + // 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 = @@ -126,7 +136,10 @@ export default function PlayerStats({ initialUserData }: PlayerStatsProps) { return (
{/* Avatar */} - +
{avatar ? ( {/* Username & Level */}
- +
{username}
@@ -155,7 +171,7 @@ export default function PlayerStats({ initialUserData }: PlayerStatsProps) { Lv.{level}
- + {/* Bars side by side */}
@@ -210,4 +226,3 @@ export default function PlayerStats({ initialUserData }: PlayerStatsProps) {
); } - diff --git a/components/ProfileForm.tsx b/components/ProfileForm.tsx index 935664b..96223b4 100644 --- a/components/ProfileForm.tsx +++ b/components/ProfileForm.tsx @@ -1,7 +1,6 @@ "use client"; -import { useState, useRef } from "react"; -import { useRouter } from "next/navigation"; +import { useState, useRef, type ChangeEvent } from "react"; type CharacterClass = | "WARRIOR" @@ -45,7 +44,6 @@ export default function ProfileForm({ initialProfile, backgroundImage, }: ProfileFormProps) { - const router = useRouter(); const [profile, setProfile] = useState(initialProfile); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); @@ -67,7 +65,7 @@ export default function ProfileForm({ const [confirmPassword, setConfirmPassword] = useState(""); const [changingPassword, setChangingPassword] = useState(false); - const handleAvatarUpload = async (e: React.ChangeEvent) => { + const handleAvatarUpload = async (e: ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; @@ -325,7 +323,7 @@ export default function ProfileForm({ {/* Username Field */}
{children} ); } - diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index 8ebcaff..0000000 --- a/eslint.config.js +++ /dev/null @@ -1,62 +0,0 @@ -import js from '@eslint/js'; -import tseslint from '@typescript-eslint/eslint-plugin'; -import tsparser from '@typescript-eslint/parser'; -import react from 'eslint-plugin-react'; -import reactHooks from 'eslint-plugin-react-hooks'; -import globals from 'globals'; - -export default [ - js.configs.recommended, - { - files: ['**/*.{ts,tsx}'], - languageOptions: { - parser: tsparser, - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - ecmaFeatures: { - jsx: true, - }, - }, - globals: { - ...globals.node, - ...globals.browser, - }, - }, - plugins: { - '@typescript-eslint': tseslint, - react, - 'react-hooks': reactHooks, - }, - rules: { - ...tseslint.configs.recommended.rules, - ...react.configs.recommended.rules, - ...reactHooks.configs.recommended.rules, - 'react/react-in-jsx-scope': 'off', - 'react/prop-types': 'off', - '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], - '@typescript-eslint/no-explicit-any': 'warn', - 'react/no-unescaped-entities': 'warn', - 'react/no-unknown-property': ['error', { ignore: ['jsx'] }], - 'react-hooks/set-state-in-effect': 'warn', - '@typescript-eslint/triple-slash-reference': 'off', - }, - settings: { - react: { - version: 'detect', - }, - }, - }, - { - ignores: [ - 'node_modules/**', - '.next/**', - 'out/**', - 'build/**', - 'dist/**', - '*.config.js', - 'prisma/generated/**', - ], - }, -]; - diff --git a/hooks/usePreferences.ts b/hooks/usePreferences.ts index 2d0b71b..ab477f0 100644 --- a/hooks/usePreferences.ts +++ b/hooks/usePreferences.ts @@ -48,7 +48,10 @@ export function useBackgroundImage( if (preferences) { const imageKey = `${page}Background` as keyof Preferences; const customImage = preferences[imageKey]; - setBackgroundImage(customImage || defaultImage); + // Utiliser requestAnimationFrame pour éviter les cascades de rendu + requestAnimationFrame(() => { + setBackgroundImage(customImage || defaultImage); + }); } }, [preferences, page, defaultImage]);