import { useRef, useState, type DragEvent } from 'react'; import { Controller, useFormContext } from 'react-hook-form'; import { FieldError } from './FieldError'; type Kind = 'image' | 'document'; type Props = { name: string; label: string; folder?: string; hint?: string; accept?: string; maxSizeMb?: number; kind?: Kind; }; const ACCEPT: Record = { image: 'image/png,image/jpeg,image/webp,image/svg+xml,image/gif', document: 'application/pdf,.pdf', }; const HELP_TEXT: Record = { image: 'png, jpg, webp, svg, gif', document: 'pdf uniquement', }; function fileNameFromUrl(url: string): string { try { const clean = url.split('?')[0]; const segments = clean.split('/'); return segments[segments.length - 1] || url; } catch { return url; } } export function FileUploadField({ name, label, folder = '', hint, accept, maxSizeMb, kind = 'image', }: Props) { const { control } = useFormContext(); const inputRef = useRef(null); const [hover, setHover] = useState(false); const [busy, setBusy] = useState(false); const [localError, setLocalError] = useState(null); const effectiveAccept = accept ?? ACCEPT[kind]; const effectiveMax = maxSizeMb ?? (kind === 'document' ? 8 : 5); return ( { const url: string = field.value || ''; const upload = async (file: File) => { setLocalError(null); if (file.size > effectiveMax * 1024 * 1024) { setLocalError(`Fichier trop lourd (max ${effectiveMax} Mo).`); return; } setBusy(true); try { const fd = new FormData(); fd.append('file', file); fd.append('kind', kind); if (folder) fd.append('folder', folder); const res = await fetch('/api/upload', { method: 'POST', body: fd }); const data = await res.json(); if (!res.ok || !data.url) { setLocalError(data?.error || 'upload échoué'); return; } field.onChange(data.url); } catch (e) { setLocalError((e as Error).message || 'upload échoué'); } finally { setBusy(false); } }; const onDrop = (e: DragEvent) => { e.preventDefault(); setHover(false); const file = e.dataTransfer.files?.[0]; if (file) upload(file); }; return (
{label} {hint && ( {hint} )}
{url && kind === 'image' ? (
ok
) : null} {url && kind === 'document' ? (
pdf actuel {fileNameFromUrl(url)} voir le fichier ↗
) : null}
{ e.preventDefault(); setHover(true); }} onDragOver={(e) => { e.preventDefault(); setHover(true); }} onDragLeave={() => setHover(false)} onDrop={onDrop} onClick={() => inputRef.current?.click()} role="button" tabIndex={0} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); inputRef.current?.click(); } }} className={`flex-1 cursor-pointer p-4 rounded text-center transition-all ${ hover ? 'neon-border-cyan glow-cyan' : 'border border-dashed border-[color:var(--color-border)] hover:border-[color:var(--color-cyan)]' }`} > {busy ? (
upload en cours…
) : (
{url ? '↻ remplacer' : '↑ glisser-déposer'} ou cliquer · {HELP_TEXT[kind]} (max {effectiveMax} Mo)
)}
{url && ( )}
{ const file = e.target.files?.[0]; if (file) upload(file); e.target.value = ''; }} /> {url && (

{url}

)} {localError && (

{localError}

)}
); }} /> ); }