// web/screens/upload.jsx — Веб-загрузка фото пользователем (И4, §3.4). // multipart POST /v1/upload → создаёт серию (владелец = текущий юзер). function UploadScreen({ setRoute, me }) { const [files, setFiles] = React.useState([]); const [contentType, setContentType] = React.useState("photo"); const [editorial, setEditorial] = React.useState(false); const [busy, setBusy] = React.useState(false); const [result, setResult] = React.useState(null); const [, toast] = useToasts(); const inputRef = React.useRef(null); const quota = me && me.quota; const remaining = quota && quota.fileLimit >= 0 ? Math.max(0, quota.fileLimit - quota.filesUsed) : null; const addFiles = (list) => { const arr = Array.from(list).filter((f) => /\.(jpe?g|png)$/i.test(f.name)); setFiles((prev) => [...prev, ...arr]); }; const onDrop = (e) => { e.preventDefault(); addFiles(e.dataTransfer.files); }; const removeAt = (i) => setFiles((p) => p.filter((_, j) => j !== i)); const submit = async () => { if (!files.length) return; setBusy(true); setResult(null); try { const r = await api.upload(files, { contentType, isEditorial: editorial }); setResult(r); if (r.shootId) { toast("Создана серия · " + r.accepted + " фото", "ok"); setTimeout(() => setRoute("series", r.shootId), 800); } else { toast("Ничего не загружено", "error"); } } catch (e) { toast("Ошибка: " + e.message, "error"); } finally { setBusy(false); } }; return (

Загрузить фото

{remaining != null && (
Осталось по лимиту: {remaining} файлов
)}
e.preventDefault()} onClick={() => inputRef.current && inputRef.current.click()} style={{ border: "2px dashed var(--line-2,#d7dad3)", borderRadius: 12, padding: 32, textAlign: "center", cursor: "pointer", background: "var(--surface,#fafafa)" }}>
⬆ Перетащите файлы сюда или нажмите, чтобы выбрать
JPEG / PNG
addFiles(e.target.files)} />
{files.length > 0 && (
{files.map((f, i) => (
{f.name} {Math.round(f.size / 1024)} КБ
))}
)} {result && result.rejected && result.rejected.length > 0 && (
Не загружено: {result.rejected.join(", ")}
)}
); } window.UploadScreen = UploadScreen;