382 lines
12 KiB
TypeScript
382 lines
12 KiB
TypeScript
"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>
|
|
);
|
|
}
|