This commit is contained in:
jze9
2026-05-15 20:12:44 +05:00
parent 2335497226
commit dd7d8a7ccf
21 changed files with 672 additions and 121 deletions

View File

@@ -73,9 +73,10 @@ export const adminDeleteCategory = (token, id) =>
// ── Медиа (админ) ──────────────────────────────────────────────────────────
export const adminListMedia = (token, { type, offset = 0, limit = 40 } = {}) => {
export const adminListMedia = (token, { type, offset = 0, limit = 40, q } = {}) => {
const p = new URLSearchParams()
if (type) p.set('media_type', type)
if (q) p.set('q', q)
p.set('offset', offset)
p.set('limit', limit)
return req('GET', `admin/media?${p}`, token)

View File

@@ -48,6 +48,8 @@ export default function MediaLibrary() {
const [total, setTotal] = useState(0)
const [type, setType] = useState('')
const [page, setPage] = useState(1)
const [search, setSearch] = useState('')
const [searchInput, setSearchInput] = useState('')
const [loading, setLoading] = useState(true)
const [confirm, setConfirm] = useState(null)
const [uploading, setUploading] = useState(false)
@@ -58,13 +60,13 @@ export default function MediaLibrary() {
const load = useCallback(async () => {
setLoading(true)
try {
const data = await adminListMedia(token, { type: type || undefined, offset: (page - 1) * PAGE, limit: PAGE })
const data = await adminListMedia(token, { type: type || undefined, offset: (page - 1) * PAGE, limit: PAGE, q: search || undefined })
setItems(data.items ?? [])
setTotal(data.total ?? 0)
} finally {
setLoading(false)
}
}, [token, type, page])
}, [token, type, page, search])
useEffect(() => { load() }, [load])
@@ -129,6 +131,23 @@ export default function MediaLibrary() {
onClick={() => { setType(val); setPage(1) }}
>{label}</button>
))}
<form
onSubmit={e => { e.preventDefault(); setSearch(searchInput); setPage(1) }}
style={{ display: 'flex', gap: '0.25rem', marginLeft: '0.5rem' }}
>
<input
className="form-input"
style={{ width: 200, padding: '0.3rem 0.6rem', fontSize: '0.85rem' }}
placeholder="Поиск по имени..."
value={searchInput}
onChange={e => setSearchInput(e.target.value)}
/>
<button className="btn btn-ghost btn-sm" type="submit">🔍</button>
{search && (
<button className="btn btn-ghost btn-sm" type="button"
onClick={() => { setSearch(''); setSearchInput(''); setPage(1) }}></button>
)}
</form>
<span style={{ marginLeft: 'auto', fontSize: '0.82rem', color: 'var(--c-text-2)', alignSelf: 'center' }}>
Всего: {total}
</span>
@@ -160,6 +179,13 @@ export default function MediaLibrary() {
</div>
<div className="media-item__info">
<span className="media-item__name" title={m.filename}>{m.filename}</span>
{m.article_title && (
<span style={{ fontSize: '0.7rem', color: 'var(--c-primary)', overflow: 'hidden',
textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'block' }}
title={m.article_title}>
📄 {m.article_title}
</span>
)}
<span style={{ fontSize: '0.75rem', color: 'var(--c-text-2)' }}>{fmtSize(m.size_bytes)} · {fmtDate(m.created_at)}</span>
<button
className="btn btn-icon btn-sm"

View File

@@ -67,7 +67,11 @@ export default function VkSources() {
setImporting('new')
try {
const r = await adminVkImport(token)
flash('success', `Импорт завершён: +${r.imported ?? 0} новых`)
if (r.imported !== undefined) {
flash('success', `Импорт завершён: +${r.imported} новых, пропущено ${r.skipped ?? 0}`)
} else {
flash('success', r.message ?? 'Импорт запущен')
}
} catch { flash('error', 'Ошибка импорта') }
finally { setImporting('') }
}
@@ -76,7 +80,7 @@ export default function VkSources() {
setImporting('history')
try {
const r = await adminVkImportHistory(token, sourceId)
flash('success', `История загружена: +${r.imported ?? 0} постов`)
flash('success', r.message ?? `История загружена: +${r.imported ?? 0} постов`)
} catch { flash('error', 'Ошибка загрузки истории') }
finally { setImporting('') }
}