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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user