import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { RefreshCw, Bot, Clock } from 'lucide-react' import api from '../api' interface AIInsight { id: number generated_at: string content: string model: string input_tokens: number output_tokens: number triggered_by: string } function formatAge(iso: string): string { const ms = Date.now() - new Date(iso).getTime() const mins = Math.floor(ms / 60000) if (mins < 1) return 'just now' if (mins < 60) return `${mins}m ago` const hours = Math.floor(mins / 60) if (hours < 24) return `${hours}h ago` return `${Math.floor(hours / 24)}d ago` } function escHtml(s: string): string { return s .replace(/&/g, '&') .replace(//g, '>') } function renderContent(text: string) { return text.split('\n').map((line, i) => { const safe = escHtml(line) const processed = safe.replace(/\*\*(.+?)\*\*/g, '$1') if (line.startsWith('- ') || line.startsWith('* ')) { return (
) } if (line.startsWith('## ') || line.startsWith('# ')) { const txt = line.replace(/^#+\s*/, '') return

{txt}

} if (line.trim() === '') return
return

}) } export default function Dashboard() { const qc = useQueryClient() const [genError, setGenError] = useState(null) const { data: insight, isLoading } = useQuery({ queryKey: ['ai-insights-latest'], queryFn: () => api.get('/ai-insights/latest').then(r => r.data), refetchInterval: 5 * 60_000, }) const generate = useMutation({ mutationFn: () => api.post('/ai-insights/generate').then(r => r.data), onSuccess: () => { setGenError(null) qc.invalidateQueries({ queryKey: ['ai-insights-latest'] }) }, onError: (err: any) => { setGenError(err.response?.data?.detail || 'Failed to generate insight') }, }) return (

Dashboard
Daily AI-generated forecast summary
{genError && (
{genError}
)}
AI Insight {insight && ( {formatAge(insight.generated_at)} {insight.model && {insight.model}} )}
{isLoading && (
Loading insight…
)} {!isLoading && !insight && (

No insight generated yet.

Click Generate Now to produce a daily summary.

)} {insight && ( <>
{renderContent(insight.content)}
{(insight.input_tokens || insight.output_tokens) && (
{insight.input_tokens}↑ / {insight.output_tokens}↓ tokens · {insight.triggered_by}
)} )}
) }