Enhance UI components and animations: Introduce a shimmer animation effect in globals.css, refactor FeedbackPageClient, LoginPage, RegisterPage, and AdminPanel components to utilize new UI components for improved consistency and maintainability. Update event and feedback handling in EventsPageSection and FeedbackModal, ensuring a cohesive user experience across the application.

This commit is contained in:
Julien Froidefond
2025-12-12 16:44:57 +01:00
parent db01c25de7
commit 99a475736b
32 changed files with 2242 additions and 1389 deletions

View File

@@ -3,6 +3,7 @@
import { useState, useEffect, useTransition } from "react";
import { calculateEventStatus } from "@/lib/eventStatus";
import { createEvent, updateEvent, deleteEvent } from "@/actions/admin/events";
import { Input, Textarea, Button, Card, Badge } from "@/components/ui";
interface Event {
id: string;
@@ -209,62 +210,52 @@ export default function EventManagement() {
Événements ({events.length})
</h3>
{!isCreating && !editingEvent && (
<button
<Button
onClick={handleCreate}
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"
variant="success"
size="sm"
className="whitespace-nowrap flex-shrink-0"
>
+ Nouvel événement
</button>
</Button>
)}
</div>
{(isCreating || editingEvent) && (
<div className="bg-black/60 border border-pixel-gold/20 rounded p-3 sm:p-4 mb-4">
<Card variant="default" className="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-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-xs sm:text-sm"
/>
</div>
<div>
<label className="block text-xs sm:text-sm text-gray-300 mb-1">
Nom
</label>
<input
type="text"
value={formData.name}
onChange={(e) =>
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-xs sm:text-sm"
/>
</div>
<div>
<label className="block text-xs sm:text-sm text-gray-300 mb-1">
Description
</label>
<textarea
value={formData.description}
onChange={(e) =>
setFormData({ ...formData, description: e.target.value })
}
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-xs sm:text-sm"
/>
</div>
<Input
type="date"
label="Date"
value={formData.date}
onChange={(e) =>
setFormData({ ...formData, date: e.target.value })
}
className="text-xs sm:text-sm px-3 py-2"
/>
<Input
type="text"
label="Nom"
value={formData.name}
onChange={(e) =>
setFormData({ ...formData, name: e.target.value })
}
placeholder="Nom de l'événement"
className="text-xs sm:text-sm px-3 py-2"
/>
<Textarea
label="Description"
value={formData.description}
onChange={(e) =>
setFormData({ ...formData, description: e.target.value })
}
placeholder="Description de l'événement"
rows={4}
className="text-xs sm:text-sm px-3 py-2"
/>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-xs sm:text-sm text-gray-300 mb-1">
@@ -289,71 +280,57 @@ export default function EventManagement() {
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div>
<label className="block text-xs sm:text-sm text-gray-300 mb-1">
Salle
</label>
<input
type="text"
value={formData.room || ""}
onChange={(e) =>
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-xs sm:text-sm"
/>
</div>
<div>
<label className="block text-xs sm:text-sm text-gray-300 mb-1">
Heure
</label>
<input
type="text"
value={formData.time || ""}
onChange={(e) =>
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-xs sm:text-sm"
/>
</div>
<div>
<label className="block text-xs sm:text-sm text-gray-300 mb-1">
Places max
</label>
<input
type="number"
value={formData.maxPlaces || ""}
onChange={(e) =>
setFormData({
...formData,
maxPlaces: e.target.value
? parseInt(e.target.value)
: undefined,
})
}
placeholder="Ex: 25"
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-xs sm:text-sm"
/>
</div>
<Input
type="text"
label="Salle"
value={formData.room || ""}
onChange={(e) =>
setFormData({ ...formData, room: e.target.value })
}
placeholder="Ex: Nautilus"
className="text-xs sm:text-sm px-3 py-2"
/>
<Input
type="text"
label="Heure"
value={formData.time || ""}
onChange={(e) =>
setFormData({ ...formData, time: e.target.value })
}
placeholder="Ex: 11h-12h"
className="text-xs sm:text-sm px-3 py-2"
/>
<Input
type="number"
label="Places max"
value={formData.maxPlaces || ""}
onChange={(e) =>
setFormData({
...formData,
maxPlaces: e.target.value
? parseInt(e.target.value)
: undefined,
})
}
placeholder="Ex: 25"
className="text-xs sm:text-sm px-3 py-2"
/>
</div>
<div className="flex flex-col sm:flex-row gap-2">
<button
<Button
onClick={handleSave}
variant="success"
size="md"
disabled={saving}
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 disabled:opacity-50"
>
{saving ? "Enregistrement..." : "Enregistrer"}
</button>
<button
onClick={handleCancel}
className="px-4 py-2 border border-gray-600/50 bg-gray-900/20 text-gray-400 uppercase text-xs tracking-widest rounded hover:bg-gray-900/30 transition"
>
</Button>
<Button onClick={handleCancel} variant="secondary" size="md">
Annuler
</button>
</Button>
</div>
</div>
</div>
</Card>
)}
{events.length === 0 ? (
@@ -362,80 +339,82 @@ export default function EventManagement() {
</div>
) : (
<div className="space-y-3">
{events.map((event) => (
<div
key={event.id}
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">
<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-[10px] sm:text-xs uppercase rounded whitespace-nowrap flex-shrink-0">
{getEventTypeLabel(event.type)}
</span>
<span
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"
: status === "LIVE"
? "bg-yellow-900/50 border border-yellow-500/50 text-yellow-400"
: "bg-gray-900/50 border border-gray-500/50 text-gray-400";
})()}`}
>
{getStatusLabel(calculateEventStatus(event.date))}
</span>
</div>
<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-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")}
{events.map((event) => {
const status = calculateEventStatus(event.date);
const statusVariant =
status === "UPCOMING"
? "success"
: status === "LIVE"
? "warning"
: "default";
return (
<Card key={event.id} variant="default" className="p-3 sm:p-4">
<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>
<Badge variant="default" size="sm">
{getEventTypeLabel(event.type)}
</Badge>
<Badge variant={statusVariant} size="sm">
{getStatusLabel(status)}
</Badge>
</div>
<p className="text-gray-400 text-xs sm:text-sm mb-2 break-words">
{event.description}
</p>
{event.room && (
<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">
📍 Salle: {event.room}
Date: {new Date(event.date).toLocaleDateString("fr-FR")}
</p>
)}
{event.time && (
<p className="text-gray-500 text-[10px] sm:text-xs whitespace-nowrap">
🕐 Heure: {event.time}
</p>
)}
{event.maxPlaces && (
<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-[10px] sm:text-xs rounded whitespace-nowrap flex-shrink-0">
{event.registrationsCount || 0} inscrit
{event.registrationsCount !== 1 ? "s" : ""}
</span>
{event.room && (
<p className="text-gray-500 text-[10px] sm:text-xs whitespace-nowrap">
📍 Salle: {event.room}
</p>
)}
{event.time && (
<p className="text-gray-500 text-[10px] sm:text-xs whitespace-nowrap">
🕐 Heure: {event.time}
</p>
)}
{event.maxPlaces && (
<p className="text-gray-500 text-[10px] sm:text-xs whitespace-nowrap">
👥 Places: {event.maxPlaces}
</p>
)}
<Badge variant="info" size="sm">
{event.registrationsCount || 0} inscrit
{event.registrationsCount !== 1 ? "s" : ""}
</Badge>
</div>
</div>
{!isCreating && !editingEvent && (
<div className="flex gap-2 sm:ml-4 flex-shrink-0">
<Button
onClick={() => handleEdit(event)}
variant="primary"
size="sm"
className="whitespace-nowrap"
>
Modifier
</Button>
<Button
onClick={() => handleDelete(event.id)}
variant="danger"
size="sm"
className="whitespace-nowrap"
>
Supprimer
</Button>
</div>
)}
</div>
{!isCreating && !editingEvent && (
<div className="flex gap-2 sm:ml-4 flex-shrink-0">
<button
onClick={() => handleEdit(event)}
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-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>
</div>
)}
</div>
</div>
))}
</Card>
);
})}
</div>
)}
</div>