Files
juanitosport/app/admin/media/media-admin.tsx
T

175 lines
6.3 KiB
TypeScript

"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
const VISIBILITY_OPTIONS = [
{ value: "MEMBERS_ONLY", label: "👥 Tous les adhérents" },
{ value: "MUAY_THAI_ONLY", label: "🥊 Muay Thai uniquement" },
{ value: "YOGA_ONLY", label: "🧘 Yoga uniquement" },
{ value: "PUBLIC", label: "🌍 Public" },
];
const TYPE_OPTIONS = [
{ value: "PHOTO", label: "📷 Photo" },
{ value: "VIDEO", label: "🎥 Vidéo" },
];
export default function AdminMedia({ media }: { media: any[] }) {
const router = useRouter();
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [type, setType] = useState("PHOTO");
const [visibility, setVisibility] = useState("MEMBERS_ONLY");
const [file, setFile] = useState<File | null>(null);
const [uploading, setUploading] = useState(false);
const [error, setError] = useState("");
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
if (!file) return;
setUploading(true);
setError("");
try {
const dir = type === "VIDEO" ? "media/videos" : "media/photos";
const formData = new FormData();
formData.append("file", file);
formData.append("subdir", dir);
const up = await fetch("/api/upload", { method: "POST", body: formData });
if (!up.ok) throw new Error("Upload échoué");
const { url } = await up.json();
const res = await fetch("/api/media", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title, description, url, type, visibility }),
});
if (!res.ok) throw new Error("Enregistrement échoué");
setTitle("");
setDescription("");
setFile(null);
setType("PHOTO");
router.refresh();
} catch (err: any) {
setError(err.message || "Une erreur est survenue");
} finally {
setUploading(false);
}
}
async function deleteMedia(id: string) {
if (!confirm("Supprimer ce média ?")) return;
const res = await fetch("/api/media", {
method: "DELETE",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ id }),
});
if (res.ok) router.refresh();
}
return (
<div className="space-y-4">
<h2 className="text-lg font-bold">🎬 Photos & vidéos</h2>
<form onSubmit={handleSubmit} className="card space-y-3 border-primary-500/30">
<h3 className="font-semibold">📤 Ajouter un média</h3>
<div className="flex gap-2">
{TYPE_OPTIONS.map((t) => (
<button
key={t.value}
type="button"
onClick={() => setType(t.value)}
className={`flex-1 rounded-xl px-3 py-2 text-sm ${type === t.value ? "bg-primary-600 text-white" : "bg-white/5 text-white/60"}`}
>
{t.label}
</button>
))}
</div>
<label className="label">❌ Visibilité</label>
<div className="grid grid-cols-1 gap-2">
{VISIBILITY_OPTIONS.map((v) => (
<label key={v.value} className="flex cursor-pointer items-center gap-2 rounded-xl bg-white/5 px-3 py-2 text-sm">
<input
type="radio"
name="visibility"
checked={visibility === v.value}
onChange={() => setVisibility(v.value)}
className="accent-primary-600"
/>
{v.label}
</label>
))}
</div>
<div>
<label className="label">Titre</label>
<input className="input" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Ex: Entraînement du mardi" required />
</div>
<div>
<label className="label">Description (optionnel)</label>
<input className="input" value={description} onChange={(e) => setDescription(e.target.value)} />
</div>
<div className="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-white/20 bg-white/5 py-6">
<span className="mb-1 text-3xl">{type === "VIDEO" ? "🎥" : "📷"}</span>
{file ? (
<p className="text-sm text-primary-300">{file.name}</p>
) : (
<p className="text-sm text-white/50">
{type === "VIDEO" ? "Choisir une vidéo" : "Choisir une photo"}
</p>
)}
<label className="btn-secondary mt-3 cursor-pointer">
Choisir un fichier
<input
type="file"
accept={type === "VIDEO" ? "video/*" : "image/*"}
className="hidden"
onChange={(e) => {
setFile(e.target.files?.[0] || null);
setError("");
}}
/>
</label>
</div>
{error && <p className="text-sm text-red-400">{error}</p>}
<button type="submit" className="btn-primary w-full" disabled={uploading || !file}>
{uploading ? "Upload..." : "🚀 Publier"}
</button>
</form>
<div className="grid grid-cols-2 gap-3">
{media.length === 0 && (
<p className="card col-span-2 text-center text-sm text-white/50">Aucun média.</p>
)}
{media.map((m) => (
<div key={m.id} className="card overflow-hidden !p-0">
{m.type === "PHOTO" ? (
<img src={m.url} alt={m.title} className="aspect-square w-full object-cover" loading="lazy" />
) : (
<video controls playsInline preload="metadata" className="aspect-video w-full" src={m.url} />
)}
<div className="p-3">
<p className="truncate text-sm font-semibold">{m.title}</p>
<div className="mt-1 flex items-center justify-between">
<span className="text-xs text-white/40">
{m.visibility === "PUBLIC" ? "🌍 Public" : m.visibility === "MUAY_THAI_ONLY" ? "🥊 Muay Thai" : m.visibility === "YOGA_ONLY" ? "🧘 Yoga" : "👥 Adhérents"} ·{" "}
{new Date(m.createdAt).toLocaleDateString("fr-FR")}
</span>
<button onClick={() => deleteMedia(m.id)} className="text-xs text-red-400">
Supprimer
</button>
</div>
</div>
</div>
))}
</div>
</div>
);
}