feat: admin panel — new triggers, member/vote stats, health checks

Backend:
- /trigger-fetch-news, /trigger-fetch-votes, /trigger-member-trend-scores endpoints
- Stats: total_members, house_count, senate_count, members_missing_chamber,
  total_votes, stanced_bills_total, stanced_bills_with_votes
- API health: redis, smtp, pytrends tests added

Frontend:
- Fetch News, Fetch Votes, Member Trend Scores buttons in recurring tasks
- Fetch Votes shows "needed" badge when stanced bills are missing votes
- Stats table: member sync breakdown + vote/alignment coverage row
- Health panel: Redis, SMTP, pytrends rows

Authored by: Jack Levy
This commit is contained in:
Jack Levy
2026-03-15 16:50:46 -04:00
parent b952db1806
commit 9f4c9c7a56
3 changed files with 160 additions and 1 deletions

View File

@@ -208,6 +208,10 @@ export interface ApiHealth {
govinfo: ApiHealthResult;
newsapi: ApiHealthResult;
google_news: ApiHealthResult;
rep_lookup: ApiHealthResult;
redis: ApiHealthResult;
smtp: ApiHealthResult;
pytrends: ApiHealthResult;
}
export interface AnalysisStats {
@@ -224,6 +228,13 @@ export interface AnalysisStats {
bills_missing_actions: number;
unlabeled_briefs: number;
remaining: number;
total_members: number;
house_count: number;
senate_count: number;
members_missing_chamber: number;
total_votes: number;
stanced_bills_total: number;
stanced_bills_with_votes: number;
}
export interface NotificationTestResult {
@@ -277,6 +288,12 @@ export const adminAPI = {
apiClient.post("/api/admin/trigger-member-sync").then((r) => r.data),
triggerTrendScores: () =>
apiClient.post("/api/admin/trigger-trend-scores").then((r) => r.data),
triggerFetchNews: () =>
apiClient.post("/api/admin/trigger-fetch-news").then((r) => r.data),
triggerFetchVotes: () =>
apiClient.post("/api/admin/trigger-fetch-votes").then((r) => r.data),
triggerMemberTrendScores: () =>
apiClient.post("/api/admin/trigger-member-trend-scores").then((r) => r.data),
backfillSponsors: () =>
apiClient.post("/api/admin/backfill-sponsors").then((r) => r.data),
backfillCitations: () =>