// 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 (
{g.label}
{g.sub}
{list.length}
{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;