Files
elmprodvpn/selective-vpn-web/src/pages/overview/OverviewPage.tsx

159 lines
4.9 KiB
TypeScript

import { useQuery } from '@tanstack/react-query'
import { api } from '../../shared/api/endpoints'
function boolView(value: boolean | undefined): string {
if (value === undefined) {
return 'n/a'
}
return value ? 'ok' : 'missing'
}
function queryErrorMessage(error: unknown): string {
if (error instanceof Error) {
return error.message
}
return 'unknown error'
}
export function OverviewPage() {
const healthQuery = useQuery({
queryKey: ['healthz'],
queryFn: api.healthz,
refetchInterval: 5000,
})
const statusQuery = useQuery({
queryKey: ['status'],
queryFn: api.status,
refetchInterval: 5000,
})
const vpnQuery = useQuery({
queryKey: ['vpn-status'],
queryFn: api.vpnStatus,
refetchInterval: 5000,
})
const loginQuery = useQuery({
queryKey: ['vpn-login-state'],
queryFn: api.vpnLoginState,
refetchInterval: 5000,
})
return (
<section>
<h1 className="page-title">Overview</h1>
<p className="page-subtitle">
Foundation mode: read-only checks and realtime connectivity indicators.
</p>
<div className="panel-grid">
<article className="panel">
<h2>Core Health</h2>
{healthQuery.isLoading ? <p>Loading...</p> : null}
{healthQuery.error ? (
<p className="text-bad">Error: {queryErrorMessage(healthQuery.error)}</p>
) : null}
{healthQuery.data ? (
<div className="kv-list">
<div className="kv-row">
<span>Status</span>
<span>{healthQuery.data.status}</span>
</div>
<div className="kv-row">
<span>Time</span>
<span>{healthQuery.data.time}</span>
</div>
</div>
) : null}
</article>
<article className="panel">
<h2>Routes Snapshot</h2>
{statusQuery.isLoading ? <p>Loading...</p> : null}
{statusQuery.error ? (
<p className="text-bad">Error: {queryErrorMessage(statusQuery.error)}</p>
) : null}
{statusQuery.data ? (
<div className="kv-list">
<div className="kv-row">
<span>iface</span>
<span>{statusQuery.data.iface || '—'}</span>
</div>
<div className="kv-row">
<span>table</span>
<span>{statusQuery.data.table || '—'}</span>
</div>
<div className="kv-row">
<span>mark</span>
<span>{statusQuery.data.mark || '—'}</span>
</div>
<div className="kv-row">
<span>ip_count</span>
<span>{statusQuery.data.ip_count}</span>
</div>
<div className="kv-row">
<span>domain_count</span>
<span>{statusQuery.data.domain_count}</span>
</div>
<div className="kv-row">
<span>policy_route</span>
<span>{boolView(statusQuery.data.policy_route_ok)}</span>
</div>
</div>
) : null}
</article>
<article className="panel">
<h2>VPN Snapshot</h2>
{vpnQuery.isLoading ? <p>Loading...</p> : null}
{vpnQuery.error ? (
<p className="text-bad">Error: {queryErrorMessage(vpnQuery.error)}</p>
) : null}
{vpnQuery.data ? (
<div className="kv-list">
<div className="kv-row">
<span>desired_location</span>
<span>{vpnQuery.data.desired_location || '—'}</span>
</div>
<div className="kv-row">
<span>status_word</span>
<span>{vpnQuery.data.status_word || '—'}</span>
</div>
<div className="kv-row">
<span>unit_state</span>
<span>{vpnQuery.data.unit_state || '—'}</span>
</div>
</div>
) : null}
</article>
<article className="panel">
<h2>Login Snapshot</h2>
{loginQuery.isLoading ? <p>Loading...</p> : null}
{loginQuery.error ? (
<p className="text-bad">Error: {queryErrorMessage(loginQuery.error)}</p>
) : null}
{loginQuery.data ? (
<div className="kv-list">
<div className="kv-row">
<span>state</span>
<span>{loginQuery.data.state || '—'}</span>
</div>
<div className="kv-row">
<span>email</span>
<span>{loginQuery.data.email || '—'}</span>
</div>
<div className="kv-row">
<span>message</span>
<span>{loginQuery.data.msg || '—'}</span>
</div>
</div>
) : null}
</article>
</div>
</section>
)
}