// components/account/AvatarUploader.tsx "use client"; import { useMemo, useState } from "react"; export default function AvatarUploader({ avatarId, onUpdated, }: { avatarId?: string | null; onUpdated?: () => void; }) { const [file, setFile] = useState(null); const [busy, setBusy] = useState(false); const [msg, setMsg] = useState(null); const API_BASE = useMemo( () => (process.env.NEXT_PUBLIC_API_BASE_URL || "").replace(/\/$/, ""), [] ); const currentUrl = avatarId ? `${API_BASE}/assets/${avatarId}` : null; const onUpload = async () => { setMsg(null); if (!file) { setMsg("Choose a file first."); return; } setBusy(true); try { const fd = new FormData(); fd.set("file", file, file.name); const r = await fetch("/api/account/avatar", { method: "POST", body: fd }); if (r.status === 401) { location.assign(`/auth/sign-in?reauth=1&next=${encodeURIComponent("/portal/account")}`); return; } const j = await r.json().catch(() => ({})); if (!r.ok) { setMsg(j?.error || "Upload failed"); return; } setMsg("Avatar updated."); setFile(null); onUpdated?.(); } catch (e: any) { setMsg(e?.message || "Upload failed"); } finally { setBusy(false); } }; return (

Avatar

{currentUrl ? ( // eslint-disable-next-line @next/next/no-img-element Avatar ) : ( No Avatar )}
setFile(e.currentTarget.files?.[0] ?? null)} />
{msg &&
{msg}
}
); }