// web/screens/archive.jsx — экран «Архив» серий. // Реальные серии с archivedAt != null, фильтры по типу/году/поиску, // группировка по месяцу, модал подтверждения удаления. const MONTHS_RU = ["январь", "февраль", "март", "апрель", "май", "июнь", "июль", "август", "сентябрь", "октябрь", "ноябрь", "декабрь"]; function ArchiveScreen({ setRoute }) { const [archiveState, reload] = useAsync(() => api.feed.shoots("true"), []); const [confirmDelete, setConfirmDelete] = React.useState(null); const [query, setQuery] = React.useState(""); const [kindFilter, setKindFilter] = React.useState("all"); const [sortBy, setSortBy] = React.useState("date-desc"); const [_, toast] = useToasts(); const all = archiveState.data || []; const totalPhotos = all.reduce((n, s) => n + (s.Photos || 0), 0); const byKind = (s) => { if (kindFilter === "all") return true; if (kindFilter === "editorial") return s.IsEditorial; return s.ContentType === kindFilter; }; const byQuery = (s) => { if (!query.trim()) return true; const q = query.trim().toLowerCase(); return ((s.Title || "") + " " + (s.ID || "")).toLowerCase().includes(q); }; const sortFn = (a, b) => { if (sortBy === "date-desc") return a.ArchivedAt < b.ArchivedAt ? 1 : -1; if (sortBy === "date-asc") return a.ArchivedAt > b.ArchivedAt ? 1 : -1; if (sortBy === "count") return (b.Photos || 0) - (a.Photos || 0); return 0; }; const filtered = all.filter(byKind).filter(byQuery).slice().sort(sortFn); // Группировка по месяцу архивации. const byMonth = {}; for (const s of filtered) { const d = new Date(s.ArchivedAt || s.CreatedAt); const key = d.getFullYear() + "-" + String(d.getMonth() + 1).padStart(2, "0"); if (!byMonth[key]) byMonth[key] = { label: MONTHS_RU[d.getMonth()] + " " + d.getFullYear(), items: [] }; byMonth[key].items.push(s); } const monthsInOrder = Object.keys(byMonth).sort().reverse(); const handleDelete = async (s) => { try { await api.feed.deleteShoot(s.ID); toast(`«${s.Title || s.ID}» удалено`, "info"); setConfirmDelete(null); reload(); } catch (e) { toast("Не удалось удалить: " + e.message, "error"); } }; return (

Архив

{all.length} серий · {totalPhotos} файлов всего
{/* Filter strip */}
setQuery(e.target.value)} placeholder="Поиск по серии…" style={{ flex: 1, maxWidth: 320, padding: "5px 10px", fontSize: 13 }}/>
{[ { id: "all", label: "Все" }, { id: "photo", label: "Фото", icon: "image" }, { id: "video", label: "Видео", icon: "film" }, { id: "vector", label: "Vector", icon: "layers" }, { id: "editorial", label: "Editorial", icon: "edit" }, ].map((c) => ( ))}
{archiveState.loading && (
загружаем архив…
)} {!archiveState.loading && all.length === 0 && (
Архив пуст
Готовые серии можно отправить в архив со страницы «Мои съёмки».
)} {!archiveState.loading && all.length > 0 && filtered.length === 0 && (
Ничего не нашли
Сбросьте фильтры или поищите по другому слову.
)} {monthsInOrder.map((mk) => (
{byMonth[mk].label} {byMonth[mk].items.length} серий · {byMonth[mk].items.reduce((n, s) => n + (s.Photos || 0), 0)} файлов
{byMonth[mk].items.map((s) => (
setRoute("series", s.ID)} style={{ cursor: "pointer" }}>
{s.Cover ? {s.Title :
} {s.ContentType !== "photo" && ( {s.ContentType === "video" ? "видео" : "vector"} )} {s.IsEditorial && ( editorial )}
{s.Title || `Серия ${s.ID}`}
{new Date(s.ArchivedAt || s.CreatedAt).toLocaleDateString("ru-RU", { day: "2-digit", month: "short", year: "numeric", })} · {s.Photos || 0} файлов
))}
))} {all.length > 0 && (
Что хранится в архиве: заголовки, описания, ключевые слова, анализ, CSV — навсегда.{" "} Что чистится: копии 512px удаляются через 30 дней после ухода серии в архив. Для переобработки сервис берёт исходник с Я.Диска.
)} {confirmDelete && (
setConfirmDelete(null)}>
e.stopPropagation()}>

Удалить серию насовсем?

«{confirmDelete.Title || `Серия ${confirmDelete.ID}`}»
Удалится навсегда: заголовки, описания, ключи, анализ, CSV — всё, что Pika сохранила про эту серию. Восстановить будет нельзя — только переобработать заново из _Pikasrv/, если исходники ещё там.
)}
); } window.ArchiveScreen = ArchiveScreen;