import { useState, useEffect } from 'react'; import { useParams, Link, useNavigate } from 'react-router-dom'; import { apiFetch, ApiError } from '../lib/api'; interface Video { id: string; title: string; thumbnailUrl: string | null; duration: number | null; } interface Playlist { id: string; title: string; description: string | null; visibility: 'private' | 'shared' | 'public'; } interface PlaylistResponse { success: boolean; data: { playlist: Playlist; videos: Video[]; permission: 'owner' | 'view' | 'edit'; }; } export default function PlaylistPage() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const [data, setData] = useState(null); const [error, setError] = useState<'forbidden' | 'not_found' | null>(null); const [loading, setLoading] = useState(true); const [actionError, setActionError] = useState(null); // Edit inline form const [editOpen, setEditOpen] = useState(false); const [editTitle, setEditTitle] = useState(''); const [editVisibility, setEditVisibility] = useState<'private' | 'shared' | 'public'>('private'); const [saving, setSaving] = useState(false); // Share modal const [shareOpen, setShareOpen] = useState(false); const [shareUserId, setShareUserId] = useState(''); const [sharePermission, setSharePermission] = useState<'view' | 'edit'>('view'); const [sharing, setSharing] = useState(false); const [shareError, setShareError] = useState(null); const [shareOk, setShareOk] = useState(false); useEffect(() => { if (!id) return; apiFetch(`/playlists/${id}`) .then((res) => setData(res.data)) .catch((err: unknown) => { if (err instanceof ApiError && err.status === 403) setError('forbidden'); else setError('not_found'); }) .finally(() => setLoading(false)); }, [id]); function openEdit() { if (!data) return; setEditTitle(data.playlist.title); setEditVisibility(data.playlist.visibility); setEditOpen(true); setActionError(null); } async function handleEdit(e: React.FormEvent) { e.preventDefault(); if (!id || saving) return; setSaving(true); setActionError(null); try { const res = await apiFetch<{ success: boolean; data: { playlist: Playlist } }>( `/playlists/${id}`, { method: 'PATCH', body: JSON.stringify({ title: editTitle.trim(), visibility: editVisibility }) } ); setData((prev) => prev ? { ...prev, playlist: res.data.playlist } : prev); setEditOpen(false); } catch { setActionError('Impossible de modifier la playlist.'); } setSaving(false); } async function handleDelete() { if (!id || !confirm('Supprimer cette playlist ?')) return; setActionError(null); try { await apiFetch(`/playlists/${id}`, { method: 'DELETE' }); navigate('/playlists'); } catch { setActionError('Impossible de supprimer la playlist.'); } } async function handleRemoveVideo(videoId: string) { if (!id || !confirm('Retirer cette vidéo de la playlist ?')) return; setActionError(null); try { await apiFetch(`/playlists/${id}/videos/${videoId}`, { method: 'DELETE' }); setData((prev) => prev ? { ...prev, videos: prev.videos.filter((v) => v.id !== videoId) } : prev); } catch { setActionError('Impossible de retirer la vidéo.'); } } async function handleShare(e: React.FormEvent) { e.preventDefault(); if (!id || !shareUserId.trim() || sharing) return; setSharing(true); setShareError(null); setShareOk(false); try { await apiFetch(`/playlists/${id}/share`, { method: 'POST', body: JSON.stringify({ userId: shareUserId.trim(), permission: sharePermission }), }); setShareOk(true); setShareUserId(''); } catch { setShareError("Impossible d'envoyer l'invitation."); } setSharing(false); } if (loading) { return (
{[...Array(4)].map((_, i) => (
))}
); } if (error === 'forbidden') { return (

Accès refusé à cette playlist.

← Playlists
); } if (!data) { return (

Playlist introuvable.

← Playlists
); } const { playlist, videos, permission } = data; const isOwner = permission === 'owner'; return (
{/* Header */}
{editOpen ? (

Modifier

setEditTitle(e.target.value)} required className="rounded border border-od-border bg-od-bg px-3 py-2 text-sm text-od-text placeholder-od-muted outline-none focus:border-od-accent" />
) : ( <>

{playlist.title}

{permission}
{playlist.description && (

{playlist.description}

)} {isOwner && (
)} )} {actionError &&

{actionError}

}
{/* Share modal */} {shareOpen && (

Partager

setShareUserId(e.target.value)} placeholder="ID utilisateur" required className="rounded border border-od-border bg-od-bg px-3 py-2 text-sm text-od-text placeholder-od-muted outline-none focus:border-od-accent" /> {shareError &&

{shareError}

} {shareOk &&

Invitation envoyée.

}
)} {/* Videos */} {videos.length === 0 ? (

Aucune vidéo dans cette playlist.

) : (
{videos.map((v, i) => (
{i + 1} {v.thumbnailUrl && ( )} {v.title} {v.duration && ( {Math.floor(v.duration / 60)}:{String(v.duration % 60).padStart(2, '0')} )} {isOwner && ( )}
))}
)} ← Playlists
); }