From 44cb2c61c2904754a32fd85b795a2d438b5c3fc4 Mon Sep 17 00:00:00 2001 From: DBT Date: Wed, 25 Feb 2026 13:43:42 +0000 Subject: [PATCH] webui: add first tab data dashboard with KPI cards --- webui/src/App.tsx | 42 ++++++++++++++++++++++++++++++++++++++++-- webui/src/styles.css | 11 +++++++++-- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/webui/src/App.tsx b/webui/src/App.tsx index a555bbd..8a5e382 100644 --- a/webui/src/App.tsx +++ b/webui/src/App.tsx @@ -4,7 +4,7 @@ type ChatItem = { role: 'user' | 'assistant'; text: string } type Session = { key: string; title: string } type CronJob = { id: string; name: string; enabled: boolean } type Cfg = Record -type View = 'chat' | 'config' | 'cron' | 'nodes' +type View = 'dashboard' | 'chat' | 'config' | 'cron' | 'nodes' const defaultSessions: Session[] = [{ key: 'webui:default', title: 'Default' }] @@ -25,7 +25,7 @@ function setPath(obj: any, path: string, value: any) { } export function App() { - const [view, setView] = useState('chat') + const [view, setView] = useState('dashboard') const [token, setToken] = useState('') const [cfg, setCfg] = useState({}) const [cfgRaw, setCfgRaw] = useState('{}') @@ -38,6 +38,14 @@ export function App() { const [cron, setCron] = useState([]) const activeChat = useMemo(() => chat[active] || [], [chat, active]) const q = token ? `?token=${encodeURIComponent(token)}` : '' + const onlineNodes = useMemo(() => { + try { + const arr = JSON.parse(nodes) + return Array.isArray(arr) ? arr.filter((n: any) => n?.online).length : 0 + } catch { + return 0 + } + }, [nodes]) async function loadConfig() { const r = await fetch(`/webui/api/config${q}`) @@ -69,6 +77,10 @@ export function App() { await refreshCron() } + async function refreshAll() { + await Promise.all([loadConfig(), refreshCron(), refreshNodes()]) + } + async function send() { let media = '' const input = document.getElementById('file') as HTMLInputElement | null @@ -110,6 +122,7 @@ export function App() {