feat: журнальная главная, страница Зеро, TG-баннер, stats, auto-publish UI
- Журнальная главная: hero, CategoryRow, PopularBlock, RecentBlock (Сегодня/Вчера/Неделя) - ArticleCard: 3 размера (hero/regular/compact), цветной badge без дублей тегов - ArticleCoverSVG: 6 брендовых палитр, аватар Зеро в углу вместо #ZEROPOST - /about/zero: страница персонажа с галереей 8 поз - Footer: TG-баннер с аватаром Зеро на каждой странице - Конец статьи: блок «Понравилась? → Подписаться на канал» - ChannelEditor: 4 вкладки (Настройки/Расписание/Авто-публикация/Ручная) - AutoPublishTab: toggle, категории, delay, template, live preview - ArticlePicker: typeahead с was_sent_to_channel / next_scheduled_at флагами - /admin/channels/[id]/stats: график роста подписчиков (recharts) - Dashboard: блок TG-статистики (подписчики, delta 24h/7d, постов) - Header: упрощён до 2 пунктов desktop + расширенное мобильное меню - AutogenPanel: корректные time-picker'ы, calcNextRun с учётом last_run_at
This commit is contained in:
@@ -154,3 +154,71 @@ export async function adminGetChannelPosts(channelId) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ── Admin Settings API ────────────────────────────────────────────────────────
|
||||
|
||||
export async function adminListSettings() {
|
||||
return call('/api/settings/admin');
|
||||
}
|
||||
|
||||
export async function adminUpdateSetting(key, value) {
|
||||
return call(`/api/settings/admin/${encodeURIComponent(key)}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ value }),
|
||||
});
|
||||
}
|
||||
|
||||
// ── Admin Articles search (typeahead) ─────────────────────────────────────────
|
||||
|
||||
export async function adminSearchArticles({ q = '', status = 'published', category = '', channelId = null, limit = 20 } = {}) {
|
||||
const params = new URLSearchParams();
|
||||
if (q) params.set('q', q);
|
||||
if (status) params.set('status', status);
|
||||
if (category) params.set('category', category);
|
||||
if (channelId)params.set('channel_id', String(channelId));
|
||||
params.set('limit', String(limit));
|
||||
return call(`/api/articles/admin/search?${params.toString()}`);
|
||||
}
|
||||
|
||||
// ── Admin Scheduled posts ─────────────────────────────────────────────────────
|
||||
|
||||
export async function adminGetScheduledQueue(channelId = null) {
|
||||
if (channelId) return call(`/api/channels/admin/${channelId}/scheduled`);
|
||||
return call('/api/scheduled-posts/queue');
|
||||
}
|
||||
|
||||
export async function adminScheduleArticle(channelId, { article_id, custom_text, scheduled_at } = {}) {
|
||||
return call(`/api/channels/admin/${channelId}/schedule`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ article_id, custom_text, scheduled_at }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function adminCancelScheduled(scheduledPostId) {
|
||||
return call(`/api/scheduled-posts/${scheduledPostId}`, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
export async function adminPreviewTemplate({ article_id, template }) {
|
||||
return call('/api/scheduled-posts/preview', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ article_id, template }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function adminRequeueArticle(articleId) {
|
||||
return call(`/api/scheduled-posts/schedule-article/${articleId}`, { method: 'POST' });
|
||||
}
|
||||
|
||||
// Главная страница — собранный набор секций
|
||||
export async function getHomeData() {
|
||||
return call('/api/articles/home');
|
||||
}
|
||||
|
||||
// ── Channel stats ─────────────────────────────────────────────────────────────
|
||||
export async function getChannelSummary(channelId) {
|
||||
return call(`/api/channel-stats/${channelId}/summary`);
|
||||
}
|
||||
|
||||
export async function getChannelHistory(channelId, days = 30) {
|
||||
return call(`/api/channel-stats/${channelId}/history?days=${days}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user