Add Event Management section to admin page, allowing users to manage events alongside preferences and user management. Updated UI to include event button and corresponding display area.
This commit is contained in:
381
components/EventManagement.tsx
Normal file
381
components/EventManagement.tsx
Normal file
@@ -0,0 +1,381 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
interface Event {
|
||||
id: string;
|
||||
date: string;
|
||||
name: string;
|
||||
description: string;
|
||||
type: "SUMMIT" | "LAUNCH" | "FESTIVAL" | "COMPETITION";
|
||||
status: "UPCOMING" | "LIVE" | "PAST";
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
interface EventFormData {
|
||||
date: string;
|
||||
name: string;
|
||||
description: string;
|
||||
type: "SUMMIT" | "LAUNCH" | "FESTIVAL" | "COMPETITION";
|
||||
status: "UPCOMING" | "LIVE" | "PAST";
|
||||
}
|
||||
|
||||
const eventTypes: Event["type"][] = [
|
||||
"SUMMIT",
|
||||
"LAUNCH",
|
||||
"FESTIVAL",
|
||||
"COMPETITION",
|
||||
];
|
||||
const eventStatuses: Event["status"][] = ["UPCOMING", "LIVE", "PAST"];
|
||||
|
||||
const getEventTypeLabel = (type: Event["type"]) => {
|
||||
switch (type) {
|
||||
case "SUMMIT":
|
||||
return "Sommet";
|
||||
case "LAUNCH":
|
||||
return "Lancement";
|
||||
case "FESTIVAL":
|
||||
return "Festival";
|
||||
case "COMPETITION":
|
||||
return "Compétition";
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusLabel = (status: Event["status"]) => {
|
||||
switch (status) {
|
||||
case "UPCOMING":
|
||||
return "À venir";
|
||||
case "LIVE":
|
||||
return "En cours";
|
||||
case "PAST":
|
||||
return "Passé";
|
||||
default:
|
||||
return status;
|
||||
}
|
||||
};
|
||||
|
||||
export default function EventManagement() {
|
||||
const [events, setEvents] = useState<Event[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [editingEvent, setEditingEvent] = useState<Event | null>(null);
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [formData, setFormData] = useState<EventFormData>({
|
||||
date: "",
|
||||
name: "",
|
||||
description: "",
|
||||
type: "SUMMIT",
|
||||
status: "UPCOMING",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
fetchEvents();
|
||||
}, []);
|
||||
|
||||
const fetchEvents = async () => {
|
||||
try {
|
||||
const response = await fetch("/api/admin/events");
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setEvents(data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching events:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreate = () => {
|
||||
setIsCreating(true);
|
||||
setEditingEvent(null);
|
||||
setFormData({
|
||||
date: "",
|
||||
name: "",
|
||||
description: "",
|
||||
type: "SUMMIT",
|
||||
status: "UPCOMING",
|
||||
});
|
||||
};
|
||||
|
||||
const handleEdit = (event: Event) => {
|
||||
setEditingEvent(event);
|
||||
setIsCreating(false);
|
||||
setFormData({
|
||||
date: event.date,
|
||||
name: event.name,
|
||||
description: event.description,
|
||||
type: event.type,
|
||||
status: event.status,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
setSaving(true);
|
||||
try {
|
||||
let response;
|
||||
if (isCreating) {
|
||||
response = await fetch("/api/admin/events", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(formData),
|
||||
});
|
||||
} else if (editingEvent) {
|
||||
response = await fetch(`/api/admin/events/${editingEvent.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(formData),
|
||||
});
|
||||
}
|
||||
|
||||
if (response?.ok) {
|
||||
await fetchEvents();
|
||||
setEditingEvent(null);
|
||||
setIsCreating(false);
|
||||
setFormData({
|
||||
date: "",
|
||||
name: "",
|
||||
description: "",
|
||||
type: "SUMMIT",
|
||||
status: "UPCOMING",
|
||||
});
|
||||
} else {
|
||||
const error = await response?.json();
|
||||
alert(error.error || "Erreur lors de la sauvegarde");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error saving event:", error);
|
||||
alert("Erreur lors de la sauvegarde");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (eventId: string) => {
|
||||
if (!confirm("Êtes-vous sûr de vouloir supprimer cet événement ?")) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/admin/events/${eventId}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
await fetchEvents();
|
||||
} else {
|
||||
const error = await response.json();
|
||||
alert(error.error || "Erreur lors de la suppression");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error deleting event:", error);
|
||||
alert("Erreur lors de la suppression");
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setEditingEvent(null);
|
||||
setIsCreating(false);
|
||||
setFormData({
|
||||
date: "",
|
||||
name: "",
|
||||
description: "",
|
||||
type: "SUMMIT",
|
||||
status: "UPCOMING",
|
||||
});
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <div className="text-center text-gray-400 py-8">Chargement...</div>;
|
||||
}
|
||||
|
||||
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">
|
||||
É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"
|
||||
>
|
||||
+ Nouvel événement
|
||||
</button>
|
||||
)}
|
||||
</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">
|
||||
{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>
|
||||
<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"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block 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-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block 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-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm text-gray-300 mb-1">Type</label>
|
||||
<select
|
||||
value={formData.type}
|
||||
onChange={(e) =>
|
||||
setFormData({
|
||||
...formData,
|
||||
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"
|
||||
>
|
||||
{eventTypes.map((type) => (
|
||||
<option key={type} value={type}>
|
||||
{getEventTypeLabel(type)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm text-gray-300 mb-1">
|
||||
Statut
|
||||
</label>
|
||||
<select
|
||||
value={formData.status}
|
||||
onChange={(e) =>
|
||||
setFormData({
|
||||
...formData,
|
||||
status: e.target.value as Event["status"],
|
||||
})
|
||||
}
|
||||
className="w-full px-3 py-2 bg-black/60 border border-pixel-gold/30 rounded text-white text-sm"
|
||||
>
|
||||
{eventStatuses.map((status) => (
|
||||
<option key={status} value={status}>
|
||||
{getStatusLabel(status)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={handleSave}
|
||||
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"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{events.length === 0 ? (
|
||||
<div className="text-center text-gray-400 py-8">
|
||||
Aucun événement trouvé
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{events.map((event) => (
|
||||
<div
|
||||
key={event.id}
|
||||
className="bg-black/60 border border-pixel-gold/20 rounded 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">
|
||||
{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">
|
||||
{getEventTypeLabel(event.type)}
|
||||
</span>
|
||||
<span
|
||||
className={`px-2 py-1 text-xs uppercase rounded ${
|
||||
event.status === "UPCOMING"
|
||||
? "bg-green-900/50 border border-green-500/50 text-green-400"
|
||||
: event.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(event.status)}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-gray-400 text-sm mb-2">
|
||||
{event.description}
|
||||
</p>
|
||||
<p className="text-gray-500 text-xs">
|
||||
Date: {new Date(event.date).toLocaleDateString("fr-FR")}
|
||||
</p>
|
||||
</div>
|
||||
{!isCreating && !editingEvent && (
|
||||
<div className="flex gap-2 ml-4">
|
||||
<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"
|
||||
>
|
||||
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"
|
||||
>
|
||||
Supprimer
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user