117 lines
3.7 KiB
TypeScript
117 lines
3.7 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
|
|
const SPORT_LABEL: Record<string, { icon: string; name: string }> = {
|
|
"muay-thai": { icon: "🥊", name: "Muay Thai" },
|
|
yoga: { icon: "🧘", name: "Yoga" },
|
|
};
|
|
|
|
export default function AdminClasses({ slots }: { slots: any[] }) {
|
|
const router = useRouter();
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<h2 className="text-lg font-bold">📅 Cours & places disponibles</h2>
|
|
<p className="text-sm text-white/60">
|
|
Renseigne le nombre de places pour chaque cours. Quand les places sont
|
|
épuisées, le cours s'affiche « COMPLET » sur le site.
|
|
</p>
|
|
|
|
<div className="space-y-4">
|
|
{slots.map((slot) => (
|
|
<SlotCard key={slot.id} slot={slot} onUpdate={() => router.refresh()} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function SlotCard({ slot, onUpdate }: { slot: any; onUpdate: () => void }) {
|
|
const sport = SPORT_LABEL[slot.sport] || { icon: "🏅", name: slot.sport };
|
|
const [capacity, setCapacity] = useState(String(slot.capacity));
|
|
const [booked, setBooked] = useState(String(slot.booked));
|
|
const [isFull, setIsFull] = useState(slot.isFull);
|
|
const [saving, setSaving] = useState(false);
|
|
|
|
const capacityNum = Math.max(0, parseInt(capacity) || 0);
|
|
const bookedNum = Math.max(0, parseInt(booked) || 0);
|
|
const remaining = Math.max(0, capacityNum - bookedNum);
|
|
const full = isFull || remaining === 0;
|
|
|
|
async function save() {
|
|
setSaving(true);
|
|
const res = await fetch("/api/admin/classes", {
|
|
method: "PATCH",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ id: slot.id, capacity, booked, isFull }),
|
|
});
|
|
setSaving(false);
|
|
if (res.ok) onUpdate();
|
|
}
|
|
|
|
return (
|
|
<div className="card">
|
|
<div className="mb-3 flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<span className="text-2xl">{sport.icon}</span>
|
|
<div>
|
|
<p className="font-bold">
|
|
{sport.name} — {slot.label}
|
|
</p>
|
|
<p className="text-xs text-white/50">{slot.time}</p>
|
|
</div>
|
|
</div>
|
|
<span
|
|
className={`rounded-full px-3 py-1 text-xs font-bold ${
|
|
full
|
|
? "bg-red-500/20 text-red-300"
|
|
: remaining <= 3
|
|
? "bg-amber-500/20 text-amber-300"
|
|
: "bg-emerald-500/20 text-emerald-300"
|
|
}`}
|
|
>
|
|
{full ? "❌ COMPLET" : `${remaining} places libres`}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<div>
|
|
<label className="label">Capacité totale</label>
|
|
<input
|
|
type="number"
|
|
min="0"
|
|
className="input"
|
|
value={capacity}
|
|
onChange={(e) => setCapacity(e.target.value)}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="label">Places occupées</label>
|
|
<input
|
|
type="number"
|
|
min="0"
|
|
className="input"
|
|
value={booked}
|
|
onChange={(e) => setBooked(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<label className="mt-3 flex cursor-pointer items-center gap-2 rounded-xl bg-white/5 px-4 py-2 text-sm">
|
|
<input
|
|
type="checkbox"
|
|
checked={isFull}
|
|
onChange={(e) => setIsFull(e.target.checked)}
|
|
className="h-4 w-4 accent-red-600"
|
|
/>
|
|
<span className="font-semibold text-red-300">Marquer ce cours comme COMPLET</span>
|
|
</label>
|
|
|
|
<button onClick={save} className="btn-primary mt-3 w-full" disabled={saving}>
|
|
{saving ? "Enregistrement..." : "💾 Enregistrer"}
|
|
</button>
|
|
</div>
|
|
);
|
|
} |