feat:/ added uptime page
This commit is contained in:
+71
-6
@@ -120,7 +120,7 @@ function App() {
|
|||||||
const [route, setRoute] = useState(() => parseRoute())
|
const [route, setRoute] = useState(() => parseRoute())
|
||||||
const [leaderboard, setLeaderboard] = useState({ status: 'idle', data: null, error: null })
|
const [leaderboard, setLeaderboard] = useState({ status: 'idle', data: null, error: null })
|
||||||
const [live, setLive] = useState({ status: 'idle', data: null, error: null })
|
const [live, setLive] = useState({ status: 'idle', data: null, error: null })
|
||||||
const [uptime, setUptime] = useState({ status: 'idle', checks: [], updatedAt: null })
|
const [uptime, setUptime] = useState({ status: 'idle', checks: [], history: [], updatedAt: null })
|
||||||
const [teamQuery, setTeamQuery] = useState('')
|
const [teamQuery, setTeamQuery] = useState('')
|
||||||
const [searchHint, setSearchHint] = useState({ status: 'idle', name: '' })
|
const [searchHint, setSearchHint] = useState({ status: 'idle', name: '' })
|
||||||
const [profile, setProfile] = useState({
|
const [profile, setProfile] = useState({
|
||||||
@@ -326,6 +326,7 @@ function App() {
|
|||||||
setUptime((current) => ({
|
setUptime((current) => ({
|
||||||
status: current.status === 'ready' ? 'refreshing' : 'loading',
|
status: current.status === 'ready' ? 'refreshing' : 'loading',
|
||||||
checks: current.checks,
|
checks: current.checks,
|
||||||
|
history: current.history,
|
||||||
updatedAt: current.updatedAt,
|
updatedAt: current.updatedAt,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@@ -343,10 +344,8 @@ function App() {
|
|||||||
|
|
||||||
if (controller.signal.aborted) return
|
if (controller.signal.aborted) return
|
||||||
|
|
||||||
setUptime({
|
setUptime((current) => {
|
||||||
status: 'ready',
|
const checks = [
|
||||||
updatedAt: Date.now(),
|
|
||||||
checks: [
|
|
||||||
{
|
{
|
||||||
name: 'Website',
|
name: 'Website',
|
||||||
detail: 'App shell and static assets',
|
detail: 'App shell and static assets',
|
||||||
@@ -368,7 +367,23 @@ function App() {
|
|||||||
label: apiResult.label,
|
label: apiResult.label,
|
||||||
latency: Math.round(performance.now() - startedAt),
|
latency: Math.round(performance.now() - startedAt),
|
||||||
},
|
},
|
||||||
],
|
]
|
||||||
|
const onlineChecks = checks.filter((check) => check.ok).length
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 'ready',
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
checks,
|
||||||
|
history: [
|
||||||
|
...current.history,
|
||||||
|
{
|
||||||
|
timestamp: Date.now(),
|
||||||
|
onlineChecks,
|
||||||
|
totalChecks: checks.length,
|
||||||
|
ok: onlineChecks === checks.length,
|
||||||
|
},
|
||||||
|
].slice(-48),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1059,9 +1074,12 @@ function BattleLogsPage({ live, matches }) {
|
|||||||
|
|
||||||
function UptimePage({ uptime }) {
|
function UptimePage({ uptime }) {
|
||||||
const checks = uptime.checks
|
const checks = uptime.checks
|
||||||
|
const history = uptime.history
|
||||||
const operationalCount = checks.filter((check) => check.ok).length
|
const operationalCount = checks.filter((check) => check.ok).length
|
||||||
const allOperational = checks.length > 0 && operationalCount === checks.length
|
const allOperational = checks.length > 0 && operationalCount === checks.length
|
||||||
const updatedAt = uptime.updatedAt ? dateFormat.format(new Date(uptime.updatedAt)) : 'Not checked yet'
|
const updatedAt = uptime.updatedAt ? dateFormat.format(new Date(uptime.updatedAt)) : 'Not checked yet'
|
||||||
|
const onlineSamples = history.filter((sample) => sample.ok).length
|
||||||
|
const availability = history.length ? (onlineSamples / history.length) * 100 : 0
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6">
|
<section className="space-y-6">
|
||||||
@@ -1089,6 +1107,53 @@ function UptimePage({ uptime }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-lg border border-border bg-fury-white p-5 shadow-sm">
|
||||||
|
<div className="flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-lg font-semibold">Availability timeline</h2>
|
||||||
|
<p className="mt-1 text-sm text-text-soft">
|
||||||
|
Last {formatNumber(history.length)} checks from this browser session
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm font-semibold text-fury-cyan">
|
||||||
|
{history.length ? `${availability.toFixed(1)}% uptime` : 'Waiting for first sample'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 flex h-28 items-end gap-1 rounded-md border border-border bg-bg p-3">
|
||||||
|
{history.map((sample) => {
|
||||||
|
const height = `${Math.max(12, (sample.onlineChecks / sample.totalChecks) * 100)}%`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
aria-label={`${sample.ok ? 'Uptime' : 'Downtime'} at ${dateFormat.format(new Date(sample.timestamp))}`}
|
||||||
|
className={`min-w-2 flex-1 rounded-sm ${sample.ok ? 'bg-success' : 'bg-danger'}`}
|
||||||
|
key={sample.timestamp}
|
||||||
|
style={{ height }}
|
||||||
|
title={`${sample.onlineChecks}/${sample.totalChecks} online`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
|
{!history.length ? (
|
||||||
|
<div className="flex h-full w-full items-center justify-center text-sm text-text-soft">
|
||||||
|
Checking website status
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-3 flex flex-wrap gap-4 text-xs text-text-soft">
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<span className="h-2.5 w-2.5 rounded-sm bg-success" />
|
||||||
|
Uptime
|
||||||
|
</span>
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<span className="h-2.5 w-2.5 rounded-sm bg-danger" />
|
||||||
|
Downtime or degraded
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-4 lg:grid-cols-3">
|
<div className="grid gap-4 lg:grid-cols-3">
|
||||||
{checks.map((check) => (
|
{checks.map((check) => (
|
||||||
<article className="rounded-lg border border-border bg-fury-white p-5 shadow-sm" key={check.name}>
|
<article className="rounded-lg border border-border bg-fury-white p-5 shadow-sm" key={check.name}>
|
||||||
|
|||||||
Reference in New Issue
Block a user