Refactor UI components for improved responsiveness and consistency: Update styles in AdminPanel, EventManagement, FeedbackManagement, HeroSection, ImageSelector, LeaderboardSection, Navigation, PlayerStats, and UserManagement to enhance mobile and desktop layouts. Adjust text sizes, padding, and button styles for better user experience across devices.

This commit is contained in:
Julien Froidefond
2025-12-11 06:45:14 +01:00
parent f0c9a9e4cc
commit 7dbd044859
9 changed files with 449 additions and 260 deletions

View File

@@ -69,77 +69,124 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
return (
<section className="relative w-full min-h-screen flex flex-col items-center overflow-hidden pt-24 pb-16">
<div className="relative z-10 w-full max-w-6xl mx-auto px-8 py-16">
<h1 className="text-4xl font-gaming font-black mb-8 text-center">
<div className="relative z-10 w-full max-w-6xl mx-auto px-4 sm:px-8 py-16">
<h1 className="text-2xl sm:text-4xl font-gaming font-black mb-8 text-center break-words">
<span className="bg-gradient-to-r from-pixel-gold via-orange-400 to-pixel-gold bg-clip-text text-transparent">
ADMIN
</span>
</h1>
{/* Navigation Tabs */}
<div className="flex gap-4 mb-8 justify-center">
<button
onClick={() => setActiveSection("preferences")}
className={`px-6 py-3 border uppercase text-xs tracking-widest rounded transition ${
activeSection === "preferences"
? "border-pixel-gold bg-pixel-gold/10 text-pixel-gold"
: "border-pixel-gold/30 bg-black/60 text-gray-400 hover:border-pixel-gold/50"
}`}
>
Préférences UI
</button>
<button
onClick={() => setActiveSection("users")}
className={`px-6 py-3 border uppercase text-xs tracking-widest rounded transition ${
activeSection === "users"
? "border-pixel-gold bg-pixel-gold/10 text-pixel-gold"
: "border-pixel-gold/30 bg-black/60 text-gray-400 hover:border-pixel-gold/50"
}`}
>
Utilisateurs
</button>
<button
onClick={() => setActiveSection("events")}
className={`px-6 py-3 border uppercase text-xs tracking-widest rounded transition ${
activeSection === "events"
? "border-pixel-gold bg-pixel-gold/10 text-pixel-gold"
: "border-pixel-gold/30 bg-black/60 text-gray-400 hover:border-pixel-gold/50"
}`}
>
Événements
</button>
<button
onClick={() => setActiveSection("feedbacks")}
className={`px-6 py-3 border uppercase text-xs tracking-widest rounded transition ${
activeSection === "feedbacks"
? "border-pixel-gold bg-pixel-gold/10 text-pixel-gold"
: "border-pixel-gold/30 bg-black/60 text-gray-400 hover:border-pixel-gold/50"
}`}
>
Feedbacks
</button>
<div className="mb-8">
{/* Mobile: Grid layout */}
<div className="grid grid-cols-2 sm:hidden gap-2">
<button
onClick={() => setActiveSection("preferences")}
className={`px-3 py-2.5 border uppercase text-xs tracking-wider rounded transition ${
activeSection === "preferences"
? "border-pixel-gold bg-pixel-gold/20 text-pixel-gold font-semibold"
: "border-pixel-gold/30 bg-black/60 text-gray-400 hover:border-pixel-gold/50"
}`}
>
Préférences
</button>
<button
onClick={() => setActiveSection("users")}
className={`px-3 py-2.5 border uppercase text-xs tracking-wider rounded transition ${
activeSection === "users"
? "border-pixel-gold bg-pixel-gold/20 text-pixel-gold font-semibold"
: "border-pixel-gold/30 bg-black/60 text-gray-400 hover:border-pixel-gold/50"
}`}
>
Utilisateurs
</button>
<button
onClick={() => setActiveSection("events")}
className={`px-3 py-2.5 border uppercase text-xs tracking-wider rounded transition ${
activeSection === "events"
? "border-pixel-gold bg-pixel-gold/20 text-pixel-gold font-semibold"
: "border-pixel-gold/30 bg-black/60 text-gray-400 hover:border-pixel-gold/50"
}`}
>
Événements
</button>
<button
onClick={() => setActiveSection("feedbacks")}
className={`px-3 py-2.5 border uppercase text-xs tracking-wider rounded transition ${
activeSection === "feedbacks"
? "border-pixel-gold bg-pixel-gold/20 text-pixel-gold font-semibold"
: "border-pixel-gold/30 bg-black/60 text-gray-400 hover:border-pixel-gold/50"
}`}
>
Feedbacks
</button>
</div>
{/* Desktop: Horizontal tabs */}
<div className="hidden sm:flex gap-4 justify-center">
<button
onClick={() => setActiveSection("preferences")}
className={`px-6 py-3 border uppercase text-xs tracking-widest rounded transition whitespace-nowrap ${
activeSection === "preferences"
? "border-pixel-gold bg-pixel-gold/10 text-pixel-gold"
: "border-pixel-gold/30 bg-black/60 text-gray-400 hover:border-pixel-gold/50"
}`}
>
Préférences UI
</button>
<button
onClick={() => setActiveSection("users")}
className={`px-6 py-3 border uppercase text-xs tracking-widest rounded transition whitespace-nowrap ${
activeSection === "users"
? "border-pixel-gold bg-pixel-gold/10 text-pixel-gold"
: "border-pixel-gold/30 bg-black/60 text-gray-400 hover:border-pixel-gold/50"
}`}
>
Utilisateurs
</button>
<button
onClick={() => setActiveSection("events")}
className={`px-6 py-3 border uppercase text-xs tracking-widest rounded transition whitespace-nowrap ${
activeSection === "events"
? "border-pixel-gold bg-pixel-gold/10 text-pixel-gold"
: "border-pixel-gold/30 bg-black/60 text-gray-400 hover:border-pixel-gold/50"
}`}
>
Événements
</button>
<button
onClick={() => setActiveSection("feedbacks")}
className={`px-6 py-3 border uppercase text-xs tracking-widest rounded transition whitespace-nowrap ${
activeSection === "feedbacks"
? "border-pixel-gold bg-pixel-gold/10 text-pixel-gold"
: "border-pixel-gold/30 bg-black/60 text-gray-400 hover:border-pixel-gold/50"
}`}
>
Feedbacks
</button>
</div>
</div>
{activeSection === "preferences" && (
<div className="bg-black/80 border border-pixel-gold/30 rounded-lg p-6 backdrop-blur-sm">
<h2 className="text-2xl font-gaming font-bold mb-6 text-pixel-gold">
<div className="bg-black/80 border border-pixel-gold/30 rounded-lg p-4 sm:p-6 backdrop-blur-sm">
<h2 className="text-xl sm:text-2xl font-gaming font-bold mb-6 text-pixel-gold break-words">
Préférences UI Globales
</h2>
<div className="space-y-4">
<div className="bg-black/60 border border-pixel-gold/20 rounded p-4">
<div className="flex justify-between items-start mb-4">
<div>
<h3 className="text-pixel-gold font-bold text-lg">
<div className="bg-black/60 border border-pixel-gold/20 rounded p-3 sm:p-4">
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-3 mb-4">
<div className="min-w-0 flex-1">
<h3 className="text-pixel-gold font-bold text-base sm:text-lg break-words">
Images de fond du site
</h3>
<p className="text-gray-400 text-sm">
<p className="text-gray-400 text-xs sm:text-sm">
Ces préférences s&apos;appliquent à tous les utilisateurs
</p>
</div>
{!isEditing && (
<button
onClick={handleEdit}
className="px-4 py-2 border border-pixel-gold/50 bg-black/60 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition"
className="px-3 sm:px-4 py-2 border border-pixel-gold/50 bg-black/60 text-white uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition whitespace-nowrap flex-shrink-0"
>
Modifier
</button>
@@ -178,7 +225,7 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
}
label="Background Leaderboard"
/>
<div className="flex gap-2 pt-4">
<div className="flex flex-col sm:flex-row gap-2 pt-4">
<button
onClick={handleSave}
className="px-4 py-2 border border-green-500/50 bg-green-900/20 text-green-400 uppercase text-xs tracking-widest rounded hover:bg-green-900/30 transition"
@@ -195,70 +242,76 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
</div>
) : (
<div className="space-y-4">
<div className="flex items-center gap-4">
<span className="text-pixel-gold font-bold min-w-[120px]">
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
<span className="text-pixel-gold font-bold text-sm sm:text-base min-w-0 sm:min-w-[120px] flex-shrink-0">
Home:
</span>
{preferences?.homeBackground ? (
<div className="flex items-center gap-3">
<div className="flex items-center gap-2 sm:gap-3 min-w-0 flex-1">
<img
src={preferences.homeBackground}
alt="Home background"
className="w-20 h-12 object-cover rounded border border-pixel-gold/30"
className="w-16 h-10 sm:w-20 sm:h-12 object-cover rounded border border-pixel-gold/30 flex-shrink-0"
onError={(e) => {
e.currentTarget.src = "/got-2.jpg";
}}
/>
<span className="text-xs text-gray-400 truncate max-w-xs">
<span className="text-xs text-gray-400 truncate min-w-0">
{preferences.homeBackground}
</span>
</div>
) : (
<span className="text-gray-400">Par défaut</span>
<span className="text-gray-400 text-sm sm:text-base">
Par défaut
</span>
)}
</div>
<div className="flex items-center gap-4">
<span className="text-pixel-gold font-bold min-w-[120px]">
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
<span className="text-pixel-gold font-bold text-sm sm:text-base min-w-0 sm:min-w-[120px] flex-shrink-0">
Events:
</span>
{preferences?.eventsBackground ? (
<div className="flex items-center gap-3">
<div className="flex items-center gap-2 sm:gap-3 min-w-0 flex-1">
<img
src={preferences.eventsBackground}
alt="Events background"
className="w-20 h-12 object-cover rounded border border-pixel-gold/30"
className="w-16 h-10 sm:w-20 sm:h-12 object-cover rounded border border-pixel-gold/30 flex-shrink-0"
onError={(e) => {
e.currentTarget.src = "/got-2.jpg";
}}
/>
<span className="text-xs text-gray-400 truncate max-w-xs">
<span className="text-xs text-gray-400 truncate min-w-0">
{preferences.eventsBackground}
</span>
</div>
) : (
<span className="text-gray-400">Par défaut</span>
<span className="text-gray-400 text-sm sm:text-base">
Par défaut
</span>
)}
</div>
<div className="flex items-center gap-4">
<span className="text-pixel-gold font-bold min-w-[120px]">
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
<span className="text-pixel-gold font-bold text-sm sm:text-base min-w-0 sm:min-w-[120px] flex-shrink-0">
Leaderboard:
</span>
{preferences?.leaderboardBackground ? (
<div className="flex items-center gap-3">
<div className="flex items-center gap-2 sm:gap-3 min-w-0 flex-1">
<img
src={preferences.leaderboardBackground}
alt="Leaderboard background"
className="w-20 h-12 object-cover rounded border border-pixel-gold/30"
className="w-16 h-10 sm:w-20 sm:h-12 object-cover rounded border border-pixel-gold/30 flex-shrink-0"
onError={(e) => {
e.currentTarget.src = "/got-2.jpg";
}}
/>
<span className="text-xs text-gray-400 truncate max-w-xs">
<span className="text-xs text-gray-400 truncate min-w-0">
{preferences.leaderboardBackground}
</span>
</div>
) : (
<span className="text-gray-400">Par défaut</span>
<span className="text-gray-400 text-sm sm:text-base">
Par défaut
</span>
)}
</div>
</div>
@@ -269,7 +322,7 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
)}
{activeSection === "users" && (
<div className="bg-black/80 border border-pixel-gold/30 rounded-lg p-6 backdrop-blur-sm">
<div className="bg-black/80 border border-pixel-gold/30 rounded-lg p-4 sm:p-6 backdrop-blur-sm">
<h2 className="text-2xl font-gaming font-bold mb-6 text-pixel-gold">
Gestion des Utilisateurs
</h2>
@@ -278,7 +331,7 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
)}
{activeSection === "events" && (
<div className="bg-black/80 border border-pixel-gold/30 rounded-lg p-6 backdrop-blur-sm">
<div className="bg-black/80 border border-pixel-gold/30 rounded-lg p-4 sm:p-6 backdrop-blur-sm">
<h2 className="text-2xl font-gaming font-bold mb-6 text-pixel-gold">
Gestion des Événements
</h2>
@@ -287,7 +340,7 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
)}
{activeSection === "feedbacks" && (
<div className="bg-black/80 border border-pixel-gold/30 rounded-lg p-6 backdrop-blur-sm">
<div className="bg-black/80 border border-pixel-gold/30 rounded-lg p-4 sm:p-6 backdrop-blur-sm">
<h2 className="text-2xl font-gaming font-bold mb-6 text-pixel-gold">
Gestion des Feedbacks
</h2>

View File

@@ -213,14 +213,14 @@ export default function EventManagement() {
return (
<div className="space-y-4">
<div className="flex justify-between items-center mb-4">
<h3 className="text-xl font-gaming font-bold text-pixel-gold">
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 mb-4">
<h3 className="text-lg sm:text-xl font-gaming font-bold text-pixel-gold break-words">
Événements ({events.length})
</h3>
{!isCreating && !editingEvent && (
<button
onClick={handleCreate}
className="px-4 py-2 border border-green-500/50 bg-green-900/20 text-green-400 uppercase text-xs tracking-widest rounded hover:bg-green-900/30 transition"
className="px-3 sm:px-4 py-2 border border-green-500/50 bg-green-900/20 text-green-400 uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-green-900/30 transition whitespace-nowrap flex-shrink-0"
>
+ Nouvel événement
</button>
@@ -228,24 +228,28 @@ export default function EventManagement() {
</div>
{(isCreating || editingEvent) && (
<div className="bg-black/60 border border-pixel-gold/20 rounded p-4 mb-4">
<h4 className="text-pixel-gold font-bold mb-4">
<div className="bg-black/60 border border-pixel-gold/20 rounded p-3 sm:p-4 mb-4">
<h4 className="text-pixel-gold font-bold mb-4 text-base sm:text-lg break-words">
{isCreating ? "Créer un événement" : "Modifier l'événement"}
</h4>
<div className="space-y-4">
<div>
<label className="block text-sm text-gray-300 mb-1">Date</label>
<label className="block text-xs sm:text-sm text-gray-300 mb-1">
Date
</label>
<input
type="date"
value={formData.date}
onChange={(e) =>
setFormData({ ...formData, date: e.target.value })
}
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-sm"
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm"
/>
</div>
<div>
<label className="block text-sm text-gray-300 mb-1">Nom</label>
<label className="block text-xs sm:text-sm text-gray-300 mb-1">
Nom
</label>
<input
type="text"
value={formData.name}
@@ -253,11 +257,11 @@ export default function EventManagement() {
setFormData({ ...formData, name: e.target.value })
}
placeholder="Nom de l'événement"
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-sm"
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm"
/>
</div>
<div>
<label className="block text-sm text-gray-300 mb-1">
<label className="block text-xs sm:text-sm text-gray-300 mb-1">
Description
</label>
<textarea
@@ -267,12 +271,14 @@ export default function EventManagement() {
}
placeholder="Description de l'événement"
rows={4}
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-sm"
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm text-gray-300 mb-1">Type</label>
<label className="block text-xs sm:text-sm text-gray-300 mb-1">
Type
</label>
<select
value={formData.type}
onChange={(e) =>
@@ -281,7 +287,7 @@ export default function EventManagement() {
type: e.target.value as Event["type"],
})
}
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-sm"
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm"
>
{eventTypes.map((type) => (
<option key={type} value={type}>
@@ -291,9 +297,9 @@ export default function EventManagement() {
</select>
</div>
</div>
<div className="grid grid-cols-3 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div>
<label className="block text-sm text-gray-300 mb-1">
<label className="block text-xs sm:text-sm text-gray-300 mb-1">
Salle
</label>
<input
@@ -303,11 +309,11 @@ export default function EventManagement() {
setFormData({ ...formData, room: e.target.value })
}
placeholder="Ex: Nautilus"
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-sm"
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm"
/>
</div>
<div>
<label className="block text-sm text-gray-300 mb-1">
<label className="block text-xs sm:text-sm text-gray-300 mb-1">
Heure
</label>
<input
@@ -317,11 +323,11 @@ export default function EventManagement() {
setFormData({ ...formData, time: e.target.value })
}
placeholder="Ex: 11h-12h"
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-sm"
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm"
/>
</div>
<div>
<label className="block text-sm text-gray-300 mb-1">
<label className="block text-xs sm:text-sm text-gray-300 mb-1">
Places max
</label>
<input
@@ -336,11 +342,11 @@ export default function EventManagement() {
})
}
placeholder="Ex: 25"
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-sm"
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm"
/>
</div>
</div>
<div className="flex gap-2">
<div className="flex flex-col sm:flex-row gap-2">
<button
onClick={handleSave}
disabled={saving}
@@ -368,19 +374,19 @@ export default function EventManagement() {
{events.map((event) => (
<div
key={event.id}
className="bg-black/60 border border-pixel-gold/20 rounded p-4"
className="bg-black/60 border border-pixel-gold/20 rounded p-3 sm:p-4"
>
<div className="flex justify-between items-start">
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h4 className="text-pixel-gold font-bold text-lg">
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-3">
<div className="flex-1 min-w-0">
<div className="flex flex-wrap items-center gap-2 sm:gap-3 mb-2">
<h4 className="text-pixel-gold font-bold text-base sm:text-lg break-words">
{event.name}
</h4>
<span className="px-2 py-1 bg-pixel-gold/20 border border-pixel-gold/50 text-pixel-gold text-xs uppercase rounded">
<span className="px-2 py-1 bg-pixel-gold/20 border border-pixel-gold/50 text-pixel-gold text-[10px] sm:text-xs uppercase rounded whitespace-nowrap flex-shrink-0">
{getEventTypeLabel(event.type)}
</span>
<span
className={`px-2 py-1 text-xs uppercase rounded ${(() => {
className={`px-2 py-1 text-[10px] sm:text-xs uppercase rounded whitespace-nowrap flex-shrink-0 ${(() => {
const status = calculateEventStatus(event.date);
return status === "UPCOMING"
? "bg-green-900/50 border border-green-500/50 text-green-400"
@@ -392,45 +398,45 @@ export default function EventManagement() {
{getStatusLabel(calculateEventStatus(event.date))}
</span>
</div>
<p className="text-gray-400 text-sm mb-2">
<p className="text-gray-400 text-xs sm:text-sm mb-2 break-words">
{event.description}
</p>
<div className="flex flex-wrap items-center gap-4 mt-2">
<p className="text-gray-500 text-xs">
<div className="flex flex-wrap items-center gap-2 sm:gap-4 mt-2">
<p className="text-gray-500 text-[10px] sm:text-xs whitespace-nowrap">
Date: {new Date(event.date).toLocaleDateString("fr-FR")}
</p>
{event.room && (
<p className="text-gray-500 text-xs">
<p className="text-gray-500 text-[10px] sm:text-xs whitespace-nowrap">
📍 Salle: {event.room}
</p>
)}
{event.time && (
<p className="text-gray-500 text-xs">
<p className="text-gray-500 text-[10px] sm:text-xs whitespace-nowrap">
🕐 Heure: {event.time}
</p>
)}
{event.maxPlaces && (
<p className="text-gray-500 text-xs">
<p className="text-gray-500 text-[10px] sm:text-xs whitespace-nowrap">
👥 Places: {event.maxPlaces}
</p>
)}
<span className="px-2 py-1 bg-blue-900/30 border border-blue-500/50 text-blue-400 text-xs rounded">
<span className="px-2 py-1 bg-blue-900/30 border border-blue-500/50 text-blue-400 text-[10px] sm:text-xs rounded whitespace-nowrap flex-shrink-0">
{event.registrationsCount || 0} inscrit
{event.registrationsCount !== 1 ? "s" : ""}
</span>
</div>
</div>
{!isCreating && !editingEvent && (
<div className="flex gap-2 ml-4">
<div className="flex gap-2 sm:ml-4 flex-shrink-0">
<button
onClick={() => handleEdit(event)}
className="px-3 py-1 border border-pixel-gold/50 bg-black/60 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition"
className="px-2 sm:px-3 py-1 border border-pixel-gold/50 bg-black/60 text-white uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition whitespace-nowrap"
>
Modifier
</button>
<button
onClick={() => handleDelete(event.id)}
className="px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 uppercase text-xs tracking-widest rounded hover:bg-red-900/30 transition"
className="px-2 sm:px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-red-900/30 transition whitespace-nowrap"
>
Supprimer
</button>

View File

@@ -74,18 +74,20 @@ export default function FeedbackManagement() {
const renderStars = (rating: number) => {
return (
<div className="flex items-center gap-1">
<div className="flex items-center gap-0.5 sm:gap-1">
{[1, 2, 3, 4, 5].map((star) => (
<span
key={star}
className={`text-lg ${
className={`text-sm sm:text-lg ${
star <= rating ? "text-pixel-gold" : "text-gray-600"
}`}
>
</span>
))}
<span className="text-gray-400 text-sm ml-2">({rating}/5)</span>
<span className="text-gray-400 text-xs sm:text-sm ml-1 sm:ml-2">
({rating}/5)
</span>
</div>
);
};
@@ -96,25 +98,25 @@ export default function FeedbackManagement() {
if (loading) {
return (
<div className="bg-black/60 border border-pixel-gold/30 rounded-lg p-8">
<p className="text-gray-400 text-center">Chargement...</p>
<div className="bg-black/60 border border-pixel-gold/30 rounded-lg p-4 sm:p-8">
<p className="text-gray-400 text-center text-sm">Chargement...</p>
</div>
);
}
return (
<div className="space-y-6">
<div className="space-y-4 sm:space-y-6">
{/* Statistiques par événement */}
{statistics.length > 0 && (
<div className="bg-black/60 border border-pixel-gold/30 rounded-lg p-6">
<h3 className="text-pixel-gold font-bold text-lg mb-4">
<div className="bg-black/60 border border-pixel-gold/30 rounded-lg p-3 sm:p-6">
<h3 className="text-pixel-gold font-bold text-base sm:text-lg mb-4 break-words">
Statistiques par événement
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4">
{statistics.map((stat) => (
<div
key={stat.eventId}
className={`bg-black/40 border rounded p-4 cursor-pointer transition ${
className={`bg-black/40 border rounded p-3 sm:p-4 cursor-pointer transition ${
selectedEvent === stat.eventId
? "border-pixel-gold bg-pixel-gold/10"
: "border-pixel-gold/30 hover:border-pixel-gold/50"
@@ -125,21 +127,21 @@ export default function FeedbackManagement() {
)
}
>
<div className="flex items-start justify-between mb-2">
<h4 className="text-white font-semibold text-sm">
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-2 mb-2">
<h4 className="text-white font-semibold text-xs sm:text-sm break-words">
{stat.eventName}
</h4>
<span className="text-pixel-gold text-xs uppercase">
<span className="text-pixel-gold text-[10px] sm:text-xs uppercase whitespace-nowrap flex-shrink-0">
{stat.eventType && getEventTypeLabel(stat.eventType)}
</span>
</div>
<div className="flex items-center gap-2 mb-2">
{renderStars(Math.round(stat.averageRating))}
</div>
<div className="text-gray-400 text-xs">
<div className="text-gray-400 text-[10px] sm:text-xs">
Moyenne: {stat.averageRating.toFixed(2)}/5
</div>
<div className="text-gray-400 text-xs">
<div className="text-gray-400 text-[10px] sm:text-xs">
{stat.feedbackCount} feedback
{stat.feedbackCount > 1 ? "s" : ""}
</div>
@@ -149,7 +151,7 @@ export default function FeedbackManagement() {
{selectedEvent && (
<button
onClick={() => setSelectedEvent(null)}
className="mt-4 text-pixel-gold text-sm hover:text-orange-400 transition"
className="mt-4 text-pixel-gold text-xs sm:text-sm hover:text-orange-400 transition"
>
Voir tous les feedbacks
</button>
@@ -158,8 +160,8 @@ export default function FeedbackManagement() {
)}
{/* Liste des feedbacks */}
<div className="bg-black/60 border border-pixel-gold/30 rounded-lg p-6">
<h3 className="text-pixel-gold font-bold text-lg mb-4">
<div className="bg-black/60 border border-pixel-gold/30 rounded-lg p-3 sm:p-6">
<h3 className="text-pixel-gold font-bold text-base sm:text-lg mb-4 break-words">
{selectedEvent
? `Feedbacks pour: ${
statistics.find((s) => s.eventId === selectedEvent)?.eventName
@@ -168,36 +170,36 @@ export default function FeedbackManagement() {
</h3>
{error && (
<div className="bg-red-900/50 border border-red-500/50 text-red-400 px-4 py-3 rounded text-sm mb-4">
<div className="bg-red-900/50 border border-red-500/50 text-red-400 px-3 sm:px-4 py-2 sm:py-3 rounded text-xs sm:text-sm mb-4">
{error}
</div>
)}
{filteredFeedbacks.length === 0 ? (
<p className="text-gray-400 text-center py-8">
<p className="text-gray-400 text-center py-8 text-sm">
Aucun feedback pour le moment
</p>
) : (
<div className="space-y-4">
<div className="space-y-3 sm:space-y-4">
{filteredFeedbacks.map((feedback) => (
<div
key={feedback.id}
className="bg-black/40 border border-pixel-gold/20 rounded p-4"
className="bg-black/40 border border-pixel-gold/20 rounded p-3 sm:p-4"
>
<div className="flex items-start justify-between mb-3">
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h4 className="text-white font-semibold">
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3 mb-3">
<div className="flex-1 min-w-0">
<div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-3 mb-2">
<h4 className="text-white font-semibold text-sm sm:text-base break-words">
{feedback.user.username}
</h4>
<span className="text-gray-500 text-xs">
<span className="text-gray-500 text-[10px] sm:text-xs break-all">
{feedback.user.email}
</span>
</div>
<div className="text-pixel-gold text-sm font-semibold mb-2">
<div className="text-pixel-gold text-xs sm:text-sm font-semibold mb-2 break-words">
{feedback.event.name}
</div>
<div className="text-gray-500 text-xs mb-2">
<div className="text-gray-500 text-[10px] sm:text-xs mb-2">
{new Date(feedback.createdAt).toLocaleDateString(
"fr-FR",
{
@@ -210,11 +212,13 @@ export default function FeedbackManagement() {
)}
</div>
</div>
<div>{renderStars(feedback.rating)}</div>
<div className="flex-shrink-0">
{renderStars(feedback.rating)}
</div>
</div>
{feedback.comment && (
<div className="mt-3 pt-3 border-t border-pixel-gold/20">
<p className="text-gray-300 text-sm whitespace-pre-wrap">
<p className="text-gray-300 text-xs sm:text-sm whitespace-pre-wrap break-words">
{feedback.comment}
</p>
</div>

View File

@@ -45,10 +45,10 @@ export default function HeroSection() {
</div>
{/* Hero Content */}
<div className="relative z-10 w-full max-w-5xl xl:max-w-6xl mx-auto px-8 py-16 text-center flex flex-col items-center">
<div className="relative z-10 w-full max-w-5xl xl:max-w-6xl mx-auto px-4 sm:px-8 py-16 text-center flex flex-col items-center">
{/* Game Title */}
<div className="w-full flex justify-center mb-4">
<h1 className="text-6xl md:text-8xl lg:text-9xl xl:text-9xl font-gaming font-black tracking-tight relative">
<div className="w-full flex justify-center mb-4 overflow-hidden">
<h1 className="text-4xl sm:text-5xl md:text-8xl lg:text-9xl xl:text-9xl font-gaming font-black tracking-tight relative break-words">
<span
ref={titleRef}
className="title-animated inline-block relative z-10"

View File

@@ -76,13 +76,15 @@ export default function ImageSelector({
return (
<div className="space-y-3">
<label className="block text-sm text-gray-300 mb-1">{label}</label>
<label className="block text-xs sm:text-sm text-gray-300 mb-1 break-words">
{label}
</label>
<div className="flex gap-4">
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4">
{/* Colonne gauche - Image */}
<div className="flex-shrink-0">
<div className="flex-shrink-0 flex justify-center sm:justify-start">
{value ? (
<div className="relative w-48 h-32 border border-pixel-gold/30 rounded overflow-hidden bg-black/60">
<div className="relative w-full sm:w-48 h-40 sm:h-32 border border-pixel-gold/30 rounded overflow-hidden bg-black/60">
<img
src={value}
alt="Preview"
@@ -99,34 +101,34 @@ export default function ImageSelector({
</button>
</div>
) : (
<div className="w-48 h-32 border border-pixel-gold/30 rounded bg-black/60 flex items-center justify-center">
<div className="w-full sm:w-48 h-40 sm:h-32 border border-pixel-gold/30 rounded bg-black/60 flex items-center justify-center">
<span className="text-xs text-gray-500">Aucune</span>
</div>
)}
</div>
{/* Colonne droite - Contrôles */}
<div className="flex-1 space-y-3">
<div className="flex-1 space-y-3 min-w-0">
{/* Input URL */}
<div className="flex gap-2">
<div className="flex flex-col sm:flex-row gap-2">
<input
type="text"
value={urlInput}
onChange={(e) => setUrlInput(e.target.value)}
onKeyPress={(e) => e.key === "Enter" && handleUrlSubmit()}
placeholder="https://example.com/image.jpg ou /image.jpg"
className="flex-1 px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-sm"
className="flex-1 px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm min-w-0"
/>
<button
onClick={handleUrlSubmit}
className="px-4 py-2 border border-pixel-gold/50 bg-black/60 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition"
className="px-3 sm:px-4 py-2 border border-pixel-gold/50 bg-black/60 text-white uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition whitespace-nowrap flex-shrink-0"
>
URL
</button>
</div>
{/* Upload depuis le disque */}
<div className="flex gap-2">
<div className="flex flex-col sm:flex-row gap-2">
<input
ref={fileInputRef}
type="file"
@@ -137,7 +139,7 @@ export default function ImageSelector({
/>
<label
htmlFor={`file-${label}`}
className={`flex-1 px-4 py-2 border border-pixel-gold/50 bg-black/60 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition text-center cursor-pointer ${
className={`flex-1 px-3 sm:px-4 py-2 border border-pixel-gold/50 bg-black/60 text-white uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition text-center cursor-pointer ${
uploading ? "opacity-50 cursor-not-allowed" : ""
}`}
>
@@ -145,22 +147,26 @@ export default function ImageSelector({
</label>
<button
onClick={() => setShowGallery(!showGallery)}
className="px-4 py-2 border border-pixel-gold/50 bg-black/60 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition"
className="px-3 sm:px-4 py-2 border border-pixel-gold/50 bg-black/60 text-white uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition whitespace-nowrap"
>
{showGallery ? "Masquer" : "Galerie"}
</button>
</div>
{/* Chemin de l'image */}
{value && <p className="text-xs text-gray-400 truncate">{value}</p>}
{value && (
<p className="text-xs text-gray-400 truncate break-all">{value}</p>
)}
</div>
</div>
{/* Galerie d'images */}
{showGallery && (
<div className="mt-4 p-4 bg-black/40 border border-pixel-gold/20 rounded">
<h4 className="text-sm text-gray-300 mb-3">Images disponibles</h4>
<div className="grid grid-cols-3 md:grid-cols-4 gap-3 max-h-64 overflow-y-auto">
<div className="mt-4 p-3 sm:p-4 bg-black/40 border border-pixel-gold/20 rounded">
<h4 className="text-xs sm:text-sm text-gray-300 mb-3">
Images disponibles
</h4>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2 sm:gap-3 max-h-64 overflow-y-auto">
{availableImages.length === 0 ? (
<div className="col-span-full text-center text-gray-400 text-sm py-4">
Aucune image disponible

View File

@@ -45,10 +45,10 @@ export default function LeaderboardSection({
</div>
{/* Content */}
<div className="relative z-10 w-full max-w-6xl mx-auto px-8 py-16">
<div className="relative z-10 w-full max-w-6xl mx-auto px-4 sm:px-8 py-16">
{/* Title Section */}
<div className="text-center mb-12">
<h1 className="text-5xl md:text-7xl font-gaming font-black mb-4 tracking-tight">
<div className="text-center mb-12 overflow-hidden">
<h1 className="text-3xl sm:text-4xl md:text-7xl font-gaming font-black mb-4 tracking-tight break-words">
<span
className="bg-gradient-to-r from-pixel-gold via-orange-400 to-pixel-gold bg-clip-text text-transparent"
style={{
@@ -66,11 +66,11 @@ export default function LeaderboardSection({
</div>
{/* Leaderboard Table */}
<div className="bg-black/60 border border-pixel-gold/30 rounded-lg backdrop-blur-sm">
<div className="bg-black/60 border border-pixel-gold/30 rounded-lg backdrop-blur-sm overflow-x-auto">
{/* Header */}
<div className="bg-gray-900/80 border-b border-pixel-gold/30 grid grid-cols-12 gap-4 p-4 font-bold text-xs uppercase tracking-widest text-gray-300">
<div className="col-span-1 text-center">Rank</div>
<div className="col-span-6">Player</div>
<div className="bg-gray-900/80 border-b border-pixel-gold/30 grid grid-cols-12 gap-2 sm:gap-4 p-2 sm:p-4 font-bold text-[10px] sm:text-xs uppercase tracking-widest text-gray-300">
<div className="col-span-2 sm:col-span-1 text-center">Rank</div>
<div className="col-span-5 sm:col-span-6">Player</div>
<div className="col-span-3 text-right">Score</div>
<div className="col-span-2 text-right">Level</div>
</div>
@@ -80,16 +80,16 @@ export default function LeaderboardSection({
{leaderboard.map((entry) => (
<div
key={entry.rank}
className={`grid grid-cols-12 gap-4 p-4 hover:bg-gray-900/50 transition relative ${
className={`grid grid-cols-12 gap-2 sm:gap-4 p-2 sm:p-4 hover:bg-gray-900/50 transition relative ${
entry.rank <= 3
? "bg-gradient-to-r from-pixel-gold/10 via-pixel-gold/5 to-transparent"
: "bg-black/40"
}`}
>
{/* Rank */}
<div className="col-span-1 flex items-center justify-center">
<div className="col-span-2 sm:col-span-1 flex items-center justify-center">
<span
className={`inline-flex items-center justify-center w-10 h-10 rounded-full font-bold text-sm ${
className={`inline-flex items-center justify-center w-8 h-8 sm:w-10 sm:h-10 rounded-full font-bold text-xs sm:text-sm ${
entry.rank === 1
? "bg-gradient-to-br from-pixel-gold to-orange-500 text-black shadow-lg shadow-pixel-gold/50"
: entry.rank === 2
@@ -104,9 +104,9 @@ export default function LeaderboardSection({
</div>
{/* Player */}
<div className="col-span-6 flex items-center gap-3">
<div className="col-span-5 sm:col-span-6 flex items-center gap-2 sm:gap-3 min-w-0">
{entry.avatar ? (
<div className="w-10 h-10 rounded-full border border-pixel-gold/30 overflow-hidden">
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-full border border-pixel-gold/30 overflow-hidden flex-shrink-0">
<img
src={entry.avatar}
alt={entry.username}
@@ -114,18 +114,18 @@ export default function LeaderboardSection({
/>
</div>
) : (
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-gray-800 to-gray-900 border border-pixel-gold/30 flex items-center justify-center">
<span className="text-pixel-gold text-xs font-bold">
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-full bg-gradient-to-br from-gray-800 to-gray-900 border border-pixel-gold/30 flex items-center justify-center flex-shrink-0">
<span className="text-pixel-gold text-[10px] sm:text-xs font-bold">
{entry.username.charAt(0).toUpperCase()}
</span>
</div>
)}
<div
className="flex items-center gap-2 cursor-pointer hover:opacity-80 transition"
className="flex items-center gap-1 sm:gap-2 cursor-pointer hover:opacity-80 transition min-w-0"
onClick={() => setSelectedEntry(entry)}
>
<span
className={`font-bold ${
className={`font-bold text-xs sm:text-sm truncate ${
entry.rank <= 3 ? "text-pixel-gold" : "text-white"
}`}
>
@@ -153,14 +153,14 @@ export default function LeaderboardSection({
{/* Score */}
<div className="col-span-3 flex items-center justify-end">
<span className="font-mono text-gray-300">
<span className="font-mono text-gray-300 text-xs sm:text-sm">
{formatScore(entry.score)}
</span>
</div>
{/* Level */}
<div className="col-span-2 flex items-center justify-end">
<span className="font-bold text-gray-400">
<span className="font-bold text-gray-400 text-xs sm:text-sm">
Lv.{entry.level}
</span>
</div>
@@ -190,10 +190,10 @@ export default function LeaderboardSection({
className="bg-black border-2 border-pixel-gold/70 rounded-lg max-w-2xl w-full max-h-[90vh] overflow-y-auto shadow-2xl"
onClick={(e) => e.stopPropagation()}
>
<div className="p-8">
<div className="p-4 sm:p-8">
{/* Header */}
<div className="flex items-center justify-between mb-6">
<h2 className="text-3xl font-bold text-pixel-gold uppercase tracking-wider">
<h2 className="text-xl sm:text-3xl font-bold text-pixel-gold uppercase tracking-wider break-words">
{selectedEntry.username}
</h2>
<button
@@ -205,9 +205,9 @@ export default function LeaderboardSection({
</div>
{/* Avatar and Class */}
<div className="flex items-center gap-6 mb-6">
<div className="flex items-center gap-4 sm:gap-6 mb-6">
{selectedEntry.avatar ? (
<div className="w-24 h-24 rounded-full border-4 border-pixel-gold/50 overflow-hidden">
<div className="w-16 h-16 sm:w-24 sm:h-24 rounded-full border-2 sm:border-4 border-pixel-gold/50 overflow-hidden flex-shrink-0">
<img
src={selectedEntry.avatar}
alt={selectedEntry.username}
@@ -215,8 +215,8 @@ export default function LeaderboardSection({
/>
</div>
) : (
<div className="w-24 h-24 rounded-full border-4 border-pixel-gold/50 bg-gray-900 flex items-center justify-center">
<span className="text-pixel-gold text-4xl font-bold">
<div className="w-16 h-16 sm:w-24 sm:h-24 rounded-full border-2 sm:border-4 border-pixel-gold/50 bg-gray-900 flex items-center justify-center flex-shrink-0">
<span className="text-pixel-gold text-2xl sm:text-4xl font-bold">
{selectedEntry.username.charAt(0).toUpperCase()}
</span>
</div>

View File

@@ -2,6 +2,8 @@
import Link from "next/link";
import { useSession, signOut } from "next-auth/react";
import { useState } from "react";
import { usePathname } from "next/navigation";
import PlayerStats from "./PlayerStats";
interface UserData {
@@ -24,29 +26,37 @@ export default function Navigation({
initialIsAdmin,
}: NavigationProps) {
const { data: session } = useSession();
const [isMenuOpen, setIsMenuOpen] = useState(false);
const pathname = usePathname();
// Ne pas afficher le profil sur les pages login/register
const isAuthPage = pathname === "/login" || pathname === "/register";
// Utiliser initialUserData pour déterminer l'état de connexion pendant l'hydratation
// Cela évite le clignottement au reload
const isAuthenticated = initialUserData !== null || session !== null;
// Vérifier explicitement que initialUserData n'est pas undefined
const isAuthenticated =
(initialUserData !== undefined && initialUserData !== null) ||
session !== null;
const isAdmin = initialIsAdmin ?? session?.user?.role === "ADMIN";
return (
<nav className="w-full fixed top-0 left-0 z-50 px-8 py-3 bg-black/80 backdrop-blur-sm border-b border-gray-800/30">
<nav className="w-full fixed top-0 left-0 z-50 px-4 sm:px-8 py-3 bg-black/80 backdrop-blur-sm border-b border-gray-800/30">
<div className="max-w-7xl mx-auto flex items-center justify-between">
{/* Logo - Left */}
<div className="flex flex-col">
<div className="text-white text-xl font-gaming font-bold tracking-tight">
<div className="text-white text-lg sm:text-xl font-gaming font-bold tracking-tight">
GAME.OF.TECH
</div>
<div className="text-pixel-gold text-xs font-gaming-subtitle font-semibold flex items-center gap-1 tracking-wide">
<div className="text-pixel-gold text-[10px] sm:text-xs font-gaming-subtitle font-semibold flex items-center gap-1 tracking-wide">
<span></span>
<span>Peaksys</span>
<span></span>
</div>
</div>
{/* Navigation Links - Center */}
<div className="flex items-center gap-6">
{/* Navigation Links - Center (Desktop) */}
<div className="hidden md:flex items-center gap-6">
<Link
href="/"
className="text-white hover:text-pixel-gold transition text-xs font-normal uppercase tracking-widest"
@@ -76,35 +86,145 @@ export default function Navigation({
</div>
{/* Right Side */}
<div className="flex items-center gap-4">
{isAuthenticated ? (
<>
<div className="flex items-center gap-2 sm:gap-4">
{/* PlayerStats - Hidden on mobile */}
{isAuthenticated && !isAuthPage && (
<div className="hidden lg:block">
<PlayerStats initialUserData={initialUserData} />
</div>
)}
{/* Desktop Auth Buttons */}
<div className="hidden md:flex items-center gap-4">
{isAuthenticated ? (
<button
onClick={() => signOut()}
className="text-gray-400 hover:text-pixel-gold transition text-xs font-normal uppercase tracking-widest"
>
Déconnexion
</button>
</>
) : (
<>
<Link
href="/login"
className="text-white hover:text-pixel-gold transition text-xs font-normal uppercase tracking-widest"
>
Connexion
</Link>
<Link
href="/register"
className="px-4 py-2 border border-pixel-gold/50 bg-black/60 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition"
>
Inscription
</Link>
</>
)}
) : (
<>
<Link
href="/login"
className="text-white hover:text-pixel-gold transition text-xs font-normal uppercase tracking-widest"
>
Connexion
</Link>
<Link
href="/register"
className="px-4 py-2 border border-pixel-gold/50 bg-black/60 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition"
>
Inscription
</Link>
</>
)}
</div>
{/* Mobile Menu Button */}
<button
onClick={() => setIsMenuOpen(!isMenuOpen)}
className="md:hidden text-white hover:text-pixel-gold transition p-2"
aria-label="Toggle menu"
>
<svg
className="w-6 h-6"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
stroke="currentColor"
>
{isMenuOpen ? (
<path d="M6 18L18 6M6 6l12 12" />
) : (
<path d="M4 6h16M4 12h16M4 18h16" />
)}
</svg>
</button>
</div>
</div>
{/* Mobile Menu */}
{isMenuOpen && (
<div className="md:hidden absolute top-full left-0 w-full bg-black/95 backdrop-blur-sm border-b border-gray-800/30">
<div className="px-4 py-4 flex flex-col gap-4">
{/* Mobile Navigation Links */}
<div className="flex flex-col gap-3">
<Link
href="/"
onClick={() => setIsMenuOpen(false)}
className="text-white hover:text-pixel-gold transition text-xs font-normal uppercase tracking-widest py-2"
>
HOME
</Link>
<Link
href="/events"
onClick={() => setIsMenuOpen(false)}
className="text-white hover:text-pixel-gold transition text-xs font-normal uppercase tracking-widest py-2"
>
EVENTS
</Link>
<Link
href="/leaderboard"
onClick={() => setIsMenuOpen(false)}
className="text-white hover:text-pixel-gold transition text-xs font-normal uppercase tracking-widest py-2"
>
LEADERBOARD
</Link>
{isAdmin && (
<Link
href="/admin"
onClick={() => setIsMenuOpen(false)}
className="text-pixel-gold hover:text-orange-400 transition text-xs font-normal uppercase tracking-widest py-2"
>
ADMIN
</Link>
)}
</div>
{/* Mobile PlayerStats */}
{isAuthenticated && !isAuthPage && (
<div className="lg:hidden pt-2 border-t border-gray-800/30">
<PlayerStats initialUserData={initialUserData} />
</div>
)}
{/* Mobile Auth Buttons */}
<div className="flex flex-col gap-3 pt-2 border-t border-gray-800/30">
{isAuthenticated ? (
<button
onClick={() => {
signOut();
setIsMenuOpen(false);
}}
className="text-gray-400 hover:text-pixel-gold transition text-xs font-normal uppercase tracking-widest text-left py-2"
>
Déconnexion
</button>
) : (
<>
<Link
href="/login"
onClick={() => setIsMenuOpen(false)}
className="text-white hover:text-pixel-gold transition text-xs font-normal uppercase tracking-widest py-2"
>
Connexion
</Link>
<Link
href="/register"
onClick={() => setIsMenuOpen(false)}
className="px-4 py-2 border border-pixel-gold/50 bg-black/60 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 hover:border-pixel-gold transition text-center"
>
Inscription
</Link>
</>
)}
</div>
</div>
</div>
)}
</nav>
);
}

View File

@@ -156,7 +156,7 @@ export default function PlayerStats({ initialUserData }: PlayerStatsProps) {
</Link>
{/* Stats */}
<div className="flex flex-col gap-1.5 min-w-[200px]">
<div className="flex flex-col gap-1.5 min-w-[180px] sm:min-w-[200px]">
{/* Username & Level */}
<div className="flex items-center gap-2">
<Link

View File

@@ -174,12 +174,12 @@ export default function UserManagement() {
return (
<div
key={user.id}
className="bg-black/60 border border-pixel-gold/20 rounded p-3"
className="bg-black/60 border border-pixel-gold/20 rounded p-3 sm:p-4"
>
<div className="flex justify-between items-center mb-2">
<div className="flex gap-3 items-center flex-1 min-w-0">
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 mb-2">
<div className="flex gap-2 sm:gap-3 items-center flex-1 min-w-0">
{/* Avatar */}
<div className="w-10 h-10 rounded-full border-2 border-pixel-gold/50 overflow-hidden bg-black/60 flex-shrink-0">
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-full border-2 border-pixel-gold/50 overflow-hidden bg-black/60 flex-shrink-0">
{user.avatar ? (
<img
src={user.avatar}
@@ -194,7 +194,7 @@ export default function UserManagement() {
/>
) : null}
<div
className={`w-full h-full flex items-center justify-center text-pixel-gold text-sm font-bold ${
className={`w-full h-full flex items-center justify-center text-pixel-gold text-xs sm:text-sm font-bold ${
user.avatar ? "hidden" : ""
}`}
>
@@ -202,18 +202,18 @@ export default function UserManagement() {
</div>
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 flex-wrap">
<h3 className="text-pixel-gold font-bold text-base">
<div className="flex items-center gap-1.5 sm:gap-2 flex-wrap">
<h3 className="text-pixel-gold font-bold text-sm sm:text-base break-words">
{user.username}
</h3>
<span className="text-xs text-gray-500">
<span className="text-[10px] sm:text-xs text-gray-500 whitespace-nowrap">
Niveau {user.level}
</span>
<span className="text-xs text-gray-500">
<span className="text-[10px] sm:text-xs text-gray-500 whitespace-nowrap">
Score: {formatNumber(user.score)}
</span>
<span
className={`text-xs ${
className={`text-[10px] sm:text-xs whitespace-nowrap ${
user.role === "ADMIN"
? "text-pixel-gold"
: "text-gray-500"
@@ -222,23 +222,23 @@ export default function UserManagement() {
{user.role}
</span>
</div>
<p className="text-gray-400 text-xs truncate">
<p className="text-gray-400 text-[10px] sm:text-xs truncate">
{user.email}
</p>
</div>
</div>
{!isEditing && (
<div className="flex gap-2 flex-shrink-0 ml-2">
<div className="flex gap-2 flex-shrink-0 sm:ml-2">
<button
onClick={() => handleEdit(user)}
className="px-3 py-1.5 border border-pixel-gold/50 bg-black/60 text-white uppercase text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition"
className="px-2 sm:px-3 py-1.5 border border-pixel-gold/50 bg-black/60 text-white uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition whitespace-nowrap"
>
Modifier
</button>
<button
onClick={() => handleDelete(user.id)}
disabled={deletingUserId === user.id}
className="px-3 py-1.5 border border-red-500/50 bg-red-900/20 text-red-400 uppercase text-xs tracking-widest rounded hover:bg-red-900/30 transition disabled:opacity-50"
className="px-2 sm:px-3 py-1.5 border border-red-500/50 bg-red-900/20 text-red-400 uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-red-900/30 transition disabled:opacity-50 whitespace-nowrap"
>
{deletingUserId === user.id
? "Suppression..."
@@ -253,14 +253,14 @@ export default function UserManagement() {
{/* HP Section */}
<div>
<div className="flex justify-between items-center mb-2">
<label className="text-sm text-gray-300">
<label className="text-xs sm:text-sm text-gray-300">
Points de Vie (HP)
</label>
<span className="text-xs text-gray-400">
<span className="text-[10px] sm:text-xs text-gray-400">
{previewHp} / {user.maxHp}
</span>
</div>
<div className="flex gap-2">
<div className="flex gap-1 sm:gap-2 flex-wrap">
<button
onClick={() =>
setEditingUser({
@@ -268,7 +268,7 @@ export default function UserManagement() {
hpDelta: editingUser.hpDelta - 100,
})
}
className="px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-xs rounded hover:bg-red-900/30 transition"
className="px-2 sm:px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-[10px] sm:text-xs rounded hover:bg-red-900/30 transition flex-shrink-0"
>
-100
</button>
@@ -279,7 +279,7 @@ export default function UserManagement() {
hpDelta: editingUser.hpDelta - 10,
})
}
className="px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-xs rounded hover:bg-red-900/30 transition"
className="px-2 sm:px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-[10px] sm:text-xs rounded hover:bg-red-900/30 transition flex-shrink-0"
>
-10
</button>
@@ -292,7 +292,7 @@ export default function UserManagement() {
hpDelta: parseInt(e.target.value) || 0,
})
}
className="flex-1 px-3 py-1 bg-black/60 border border-pixel-gold/30 rounded text-white text-sm text-center"
className="flex-1 min-w-[60px] px-2 sm:px-3 py-1 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm text-center"
/>
<button
onClick={() =>
@@ -301,7 +301,7 @@ export default function UserManagement() {
hpDelta: editingUser.hpDelta + 10,
})
}
className="px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-xs rounded hover:bg-green-900/30 transition"
className="px-2 sm:px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-[10px] sm:text-xs rounded hover:bg-green-900/30 transition flex-shrink-0"
>
+10
</button>
@@ -312,7 +312,7 @@ export default function UserManagement() {
hpDelta: editingUser.hpDelta + 100,
})
}
className="px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-xs rounded hover:bg-green-900/30 transition"
className="px-2 sm:px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-[10px] sm:text-xs rounded hover:bg-green-900/30 transition flex-shrink-0"
>
+100
</button>
@@ -333,14 +333,14 @@ export default function UserManagement() {
{/* XP Section */}
<div>
<div className="flex justify-between items-center mb-2">
<label className="text-sm text-gray-300">
<label className="text-xs sm:text-sm text-gray-300">
Expérience (XP)
</label>
<span className="text-xs text-gray-400">
<span className="text-[10px] sm:text-xs text-gray-400">
{formatNumber(previewXp)} / {formatNumber(user.maxXp)}
</span>
</div>
<div className="flex gap-2">
<div className="flex gap-1 sm:gap-2 flex-wrap">
<button
onClick={() =>
setEditingUser({
@@ -348,7 +348,7 @@ export default function UserManagement() {
xpDelta: editingUser.xpDelta - 1000,
})
}
className="px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-xs rounded hover:bg-red-900/30 transition"
className="px-2 sm:px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-[10px] sm:text-xs rounded hover:bg-red-900/30 transition flex-shrink-0"
>
-1000
</button>
@@ -359,7 +359,7 @@ export default function UserManagement() {
xpDelta: editingUser.xpDelta - 100,
})
}
className="px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-xs rounded hover:bg-red-900/30 transition"
className="px-2 sm:px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-[10px] sm:text-xs rounded hover:bg-red-900/30 transition flex-shrink-0"
>
-100
</button>
@@ -372,7 +372,7 @@ export default function UserManagement() {
xpDelta: parseInt(e.target.value) || 0,
})
}
className="flex-1 px-3 py-1 bg-black/60 border border-pixel-gold/30 rounded text-white text-sm text-center"
className="flex-1 min-w-[60px] px-2 sm:px-3 py-1 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm text-center"
/>
<button
onClick={() =>
@@ -381,7 +381,7 @@ export default function UserManagement() {
xpDelta: editingUser.xpDelta + 100,
})
}
className="px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-xs rounded hover:bg-green-900/30 transition"
className="px-2 sm:px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-[10px] sm:text-xs rounded hover:bg-green-900/30 transition flex-shrink-0"
>
+100
</button>
@@ -392,7 +392,7 @@ export default function UserManagement() {
xpDelta: editingUser.xpDelta + 1000,
})
}
className="px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-xs rounded hover:bg-green-900/30 transition"
className="px-2 sm:px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-[10px] sm:text-xs rounded hover:bg-green-900/30 transition flex-shrink-0"
>
+1000
</button>
@@ -412,10 +412,10 @@ export default function UserManagement() {
{/* Score Section */}
<div>
<label className="block text-sm text-gray-300 mb-2">
<label className="block text-xs sm:text-sm text-gray-300 mb-2">
Score
</label>
<div className="flex gap-2">
<div className="flex gap-1 sm:gap-2 flex-wrap">
<button
onClick={() =>
setEditingUser({
@@ -423,7 +423,7 @@ export default function UserManagement() {
score: (editingUser.score || 0) - 1000,
})
}
className="px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-xs rounded hover:bg-red-900/30 transition"
className="px-2 sm:px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-[10px] sm:text-xs rounded hover:bg-red-900/30 transition flex-shrink-0"
>
-1000
</button>
@@ -434,7 +434,7 @@ export default function UserManagement() {
score: (editingUser.score || 0) - 100,
})
}
className="px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-xs rounded hover:bg-red-900/30 transition"
className="px-2 sm:px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-[10px] sm:text-xs rounded hover:bg-red-900/30 transition flex-shrink-0"
>
-100
</button>
@@ -447,7 +447,7 @@ export default function UserManagement() {
score: parseInt(e.target.value) || 0,
})
}
className="flex-1 px-3 py-1 bg-black/60 border border-pixel-gold/30 rounded text-white text-sm text-center"
className="flex-1 min-w-[60px] px-2 sm:px-3 py-1 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm text-center"
/>
<button
onClick={() =>
@@ -456,7 +456,7 @@ export default function UserManagement() {
score: (editingUser.score || 0) + 100,
})
}
className="px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-xs rounded hover:bg-green-900/30 transition"
className="px-2 sm:px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-[10px] sm:text-xs rounded hover:bg-green-900/30 transition flex-shrink-0"
>
+100
</button>
@@ -467,7 +467,7 @@ export default function UserManagement() {
score: (editingUser.score || 0) + 1000,
})
}
className="px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-xs rounded hover:bg-green-900/30 transition"
className="px-2 sm:px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-[10px] sm:text-xs rounded hover:bg-green-900/30 transition flex-shrink-0"
>
+1000
</button>
@@ -476,10 +476,10 @@ export default function UserManagement() {
{/* Level Section */}
<div>
<label className="block text-sm text-gray-300 mb-2">
<label className="block text-xs sm:text-sm text-gray-300 mb-2">
Niveau
</label>
<div className="flex gap-2">
<div className="flex gap-1 sm:gap-2">
<button
onClick={() =>
setEditingUser({
@@ -487,7 +487,7 @@ export default function UserManagement() {
level: Math.max(1, (editingUser.level || 1) - 1),
})
}
className="px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-xs rounded hover:bg-red-900/30 transition"
className="px-2 sm:px-3 py-1 border border-red-500/50 bg-red-900/20 text-red-400 text-[10px] sm:text-xs rounded hover:bg-red-900/30 transition flex-shrink-0"
>
-1
</button>
@@ -501,7 +501,7 @@ export default function UserManagement() {
level: Math.max(1, parseInt(e.target.value) || 1),
})
}
className="flex-1 px-3 py-1 bg-black/60 border border-pixel-gold/30 rounded text-white text-sm text-center"
className="flex-1 min-w-[60px] px-2 sm:px-3 py-1 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm text-center"
/>
<button
onClick={() =>
@@ -510,7 +510,7 @@ export default function UserManagement() {
level: (editingUser.level || 1) + 1,
})
}
className="px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-xs rounded hover:bg-green-900/30 transition"
className="px-2 sm:px-3 py-1 border border-green-500/50 bg-green-900/20 text-green-400 text-[10px] sm:text-xs rounded hover:bg-green-900/30 transition flex-shrink-0"
>
+1
</button>
@@ -519,7 +519,7 @@ export default function UserManagement() {
{/* Role Section */}
<div>
<label className="block text-sm text-gray-300 mb-2">
<label className="block text-xs sm:text-sm text-gray-300 mb-2">
Rôle
</label>
<div className="flex gap-2">
@@ -530,7 +530,7 @@ export default function UserManagement() {
role: "USER",
})
}
className={`flex-1 px-4 py-2 border rounded text-xs uppercase tracking-widest transition ${
className={`flex-1 px-3 sm:px-4 py-2 border rounded text-[10px] sm:text-xs uppercase tracking-widest transition ${
editingUser.role === "USER"
? "border-pixel-gold bg-pixel-gold/20 text-pixel-gold"
: "border-gray-600/50 bg-gray-900/20 text-gray-400 hover:bg-gray-900/30"
@@ -545,7 +545,7 @@ export default function UserManagement() {
role: "ADMIN",
})
}
className={`flex-1 px-4 py-2 border rounded text-xs uppercase tracking-widest transition ${
className={`flex-1 px-3 sm:px-4 py-2 border rounded text-[10px] sm:text-xs uppercase tracking-widest transition ${
editingUser.role === "ADMIN"
? "border-pixel-gold bg-pixel-gold/20 text-pixel-gold"
: "border-gray-600/50 bg-gray-900/20 text-gray-400 hover:bg-gray-900/30"
@@ -556,7 +556,7 @@ export default function UserManagement() {
</div>
</div>
<div className="flex gap-2 pt-2">
<div className="flex flex-col sm:flex-row gap-2 pt-2">
<button
onClick={handleSave}
disabled={saving}
@@ -573,7 +573,7 @@ export default function UserManagement() {
</div>
</div>
) : (
<div className="flex gap-4 text-xs">
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4 text-[10px] sm:text-xs">
<div className="flex-1">
<div className="flex justify-between items-center mb-0.5">
<span className="text-gray-400">HP</span>