// Sources screen — list grouped by type (RSS, Telegram, sites, files) + add modal + uploader.
const { useState: useStateSrc, useRef: useRefSrc } = React;
function Sources({ data, setData }) {
const { Button, IconButton, Card, Toggle, TextInput, Modal, SectionHead, EmptyState, Badge } = window.UI;
const I = window.Icons;
const [addOpen, setAddOpen] = useStateSrc(false);
const [type, setType] = useStateSrc('rss');
const [url, setUrl] = useStateSrc('');
const [dragActive, setDragActive] = useStateSrc(false);
const fileInputRef = useRefSrc(null);
const files = data.files || [];
const grouped = {
rss: data.sources.filter(s => s.type === 'rss'),
tg: data.sources.filter(s => s.type === 'tg'),
site: data.sources.filter(s => s.type === 'site'),
};
const toggleEnabled = (id) => setData({ ...data, sources: data.sources.map(s => s.id === id ? { ...s, enabled: !s.enabled } : s) });
const remove = (id) => setData({ ...data, sources: data.sources.filter(s => s.id !== id) });
const addSource = () => {
if (!url.trim()) return;
setData({ ...data, sources: [...data.sources, { id: 's' + Date.now(), type, url: url.trim(), last: 'только что', count: 0, enabled: true }] });
setUrl(''); setAddOpen(false);
};
const handleFiles = (filesList) => {
const items = Array.from(filesList || []).map((f, i) => {
const ext = (f.name.split('.').pop() || '').toLowerCase();
const kind =
ext === 'pdf' ? 'pdf' :
['doc', 'docx'].includes(ext) ? 'doc' :
['xls', 'xlsx', 'csv'].includes(ext) ? 'xls' :
['png', 'jpg', 'jpeg', 'webp', 'gif', 'svg'].includes(ext) ? 'image' : 'file';
return {
id: 'f' + Date.now() + '-' + i,
name: f.name,
kind,
size: f.size ? (f.size > 1024 * 1024 ? (f.size / 1048576).toFixed(1) + ' МБ' : Math.round(f.size / 1024) + ' КБ') : '—',
when: 'только что',
};
});
setData({ ...data, files: [...items, ...files] });
};
const removeFile = (id) => setData({ ...data, files: files.filter(f => f.id !== id) });
const isEmpty = data.sources.length === 0 && files.length === 0;
return (
} onClick={() => setAddOpen(true)}>Добавить источник}
/>
{isEmpty ? (
}
title="Пока ни одного источника"
sub="Подключите RSS-ленту, телеграм-канал, сайт или загрузите файлы — и Omneee начнёт предлагать темы."
action={}
/>
) : (
{[
{ key: 'rss', label: 'RSS-фиды', icon: 'Rss', sub: 'статьи из блогов, изданий, любые ленты' },
{ key: 'tg', label: 'Telegram-каналы', icon: 'Telegram', sub: 'публичные каналы, которые читаете' },
{ key: 'site', label: 'Сайты', icon: 'Globe', sub: 'свои и любимые — для контекста' },
].map((g) => {
const list = grouped[g.key];
if (!list.length) return null;
const Icon = I[g.icon];
return (
{list.map((s, i) => (
0 ? 'border-t border-ink-200' : '')}>
{s.url}
обновлено {s.last} · {s.count} материалов
toggleEnabled(s.id)} size="sm"/>
remove(s.id)} title="Удалить">
))}
);
})}
{/* Files section */}
Файлы
PDF, DOCX, XLS и изображения — контекст для генерации
{files.length}
handleFiles(e.target.files)}/>
{/* Dropzone */}
{ e.preventDefault(); setDragActive(true); }}
onDragOver={(e) => { e.preventDefault(); setDragActive(true); }}
onDragLeave={() => setDragActive(false)}
onDrop={(e) => { e.preventDefault(); setDragActive(false); handleFiles(e.dataTransfer.files); }}
className={'rounded-2xl border-2 border-dashed p-5 mb-3 transition-colors flex items-center gap-4 cursor-pointer ' + (dragActive ? 'border-ink-900 bg-ink-50' : 'border-ink-200 bg-paper hover:bg-ink-50')}
onClick={() => fileInputRef.current && fileInputRef.current.click()}
>
Перетащите файлы сюда
или нажмите, чтобы выбрать · PDF, DOCX, XLS, PNG/JPG · до 25 МБ каждый
PDF
DOCX
XLS
IMG
{files.length > 0 && (
{files.map((f, i) => removeFile(f.id)}/>)}
)}
)}
setAddOpen(false)} title="Подключить источник" sub="Выберите тип и вставьте адрес.">
{[
{ v: 'rss', label: 'RSS', icon: 'Rss' },
{ v: 'tg', label: 'Telegram', icon: 'Telegram' },
{ v: 'site', label: 'Сайт', icon: 'Globe' },
].map((t) => {
const Icon = I[t.icon];
const active = type === t.v;
return (
);
})}
: type === 'tg' ? : }
size="lg"
/>
);
}
function FileRow({ file, first, onRemove }) {
const I = window.Icons;
const { IconButton } = window.UI;
const meta = {
pdf: { Icon: I.Pdf, tint: 'bg-red-50 text-red-700', label: 'PDF' },
doc: { Icon: I.FileText, tint: 'bg-blue-50 text-blue-700', label: 'DOCX' },
xls: { Icon: I.Sheet, tint: 'bg-emerald-50 text-emerald-700', label: 'XLS' },
image: { Icon: I.ImageFile, tint: 'bg-violet-soft text-violet-ink', label: 'IMG' },
file: { Icon: I.File, tint: 'bg-ink-100 text-ink-700', label: 'FILE' },
}[file.kind] || { Icon: I.File, tint: 'bg-ink-100 text-ink-700', label: 'FILE' };
const { Icon } = meta;
return (
{file.name}
{meta.label} · {file.size} · {file.when}
);
}
window.Screens = window.Screens || {};
window.Screens.Sources = Sources;