Compare commits

...

2 Commits

9 changed files with 451 additions and 312 deletions

View File

@@ -69,18 +69,64 @@ 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">
<div className="mb-8">
{/* Mobile: Grid layout */}
<div className="grid grid-cols-2 sm:hidden gap-2">
<button
onClick={() => setActiveSection("preferences")}
className={`px-6 py-3 border uppercase text-xs tracking-widest rounded transition ${
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"
@@ -90,7 +136,7 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
</button>
<button
onClick={() => setActiveSection("users")}
className={`px-6 py-3 border uppercase text-xs tracking-widest rounded transition ${
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"
@@ -100,7 +146,7 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
</button>
<button
onClick={() => setActiveSection("events")}
className={`px-6 py-3 border uppercase text-xs tracking-widest rounded transition ${
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"
@@ -110,7 +156,7 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
</button>
<button
onClick={() => setActiveSection("feedbacks")}
className={`px-6 py-3 border uppercase text-xs tracking-widest rounded transition ${
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"
@@ -119,27 +165,28 @@ export default function AdminPanel({ initialPreferences }: AdminPanelProps) {
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

@@ -2,34 +2,9 @@
import { useBackgroundImage } from "@/hooks/usePreferences";
import Link from "next/link";
import { useState, useEffect, useRef } from "react";
export default function HeroSection() {
const backgroundImage = useBackgroundImage("home", "/got-2.jpg");
const titleRef = useRef<HTMLSpanElement>(null);
const [mousePosition, setMousePosition] = useState({ x: 50, y: 50 });
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
if (titleRef.current) {
const rect = titleRef.current.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width) * 100;
const y = ((e.clientY - rect.top) / rect.height) * 100;
setMousePosition({
x: Math.max(0, Math.min(100, x)),
y: Math.max(0, Math.min(100, y)),
});
}
};
window.addEventListener("mousemove", handleMouseMove);
return () => window.removeEventListener("mousemove", handleMouseMove);
}, []);
// Calculer la position du gradient basée sur la souris avec plus d'amplitude
const gradientPosition = mousePosition.x;
const glowIntensity = 12 + mousePosition.x / 5;
const glowOpacity = 0.4 + mousePosition.y / 250;
return (
<section className="relative w-full min-h-screen flex flex-col items-center justify-center overflow-hidden pt-24">
@@ -45,49 +20,24 @@ 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"
style={{
backgroundImage: `linear-gradient(90deg, #daa520 0%, #ffa500 ${Math.max(
10,
gradientPosition - 20
)}%, #ff8c00 ${gradientPosition}%, #ffa500 ${Math.min(
90,
gradientPosition + 20
)}%, #daa520 100%)`,
backgroundImage: `linear-gradient(90deg, #daa520 0%, #ffa500 30%, #ff8c00 50%, #ffa500 70%, #daa520 100%)`,
backgroundSize: "200% auto",
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
backgroundClip: "text",
color: "transparent",
transition: "background-image 0.15s ease-out",
filter: `drop-shadow(0 0 ${glowIntensity}px rgba(255, 140, 0, ${glowOpacity}))`,
filter: `drop-shadow(0 0 12px rgba(255, 140, 0, 0.4))`,
}}
>
GAME.OF.TECH
</span>
{/* Glow effect qui suit la souris */}
<span
className="absolute inset-0 pointer-events-none"
style={{
backgroundImage: `linear-gradient(90deg, transparent 0%, rgba(255, 140, 0, 0.3) ${gradientPosition}%, transparent 100%)`,
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
backgroundClip: "text",
filter: `blur(${10 + mousePosition.x / 20}px)`,
opacity: 0.5,
zIndex: 0,
transition: "all 0.15s ease-out",
}}
aria-hidden="true"
>
GAME.OF.TECH
</span>
</h1>
</div>

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,17 +86,23 @@ 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
@@ -104,7 +120,111 @@ export default function Navigation({
</>
)}
</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>