ServerStatus
Services with up, degraded, down or maintenance, their uptime and the last days as bars, summed up in one line.
Demo
Some systems downUpdated 2 minutes ago
- API99.98% uptimeOperational
- Dashboard99.5% uptimeDegraded
Slow page loads for some visitors in the EU.
- Email98.1% uptimeDown
Provider outage since 09:12; messages are queued.
- Search99.9% uptimeMaintenance
Reindexing until 11:00.
All systems operational
- API100% uptimeOperational
- Website100% uptimeOperational
Get it
It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.
$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/server_status.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/server_status.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/server_status.qwe
view ServerStatusDemo() = <div.demo-stack> <ServerStatus id="server-status-demo" updated="2 minutes ago" services={[ { name: "API", status: "up", uptime: 99.98, href: "#server_status", history: ["up", "up", "up", "up", "up", "up", "up", "up", "up", "up", "up", "up", "up", "up"] }, { name: "Dashboard", status: "degraded", uptime: 99.5, note: "Slow page loads for some visitors in the EU.", history: ["up", "up", "up", "degraded", "up", "up", "up", "up", "up", "up", "up", "up", "up", "degraded"] }, { name: "Email", status: "down", uptime: 98.1, note: "Provider outage since 09:12; messages are queued.", history: ["up", "up", "up", "up", "up", "down", "up", "up", "up", "up", "up", "up", "up", "down"] }, { name: "Search", status: "maintenance", uptime: 99.9, note: "Reindexing until 11:00." }, ]}/> <ServerStatus services={[{ name: "API", status: "up", uptime: 100 }, { name: "Website", status: "up", uptime: 100 }]}/> </div>Source
ui/server_status.qwe
// ServerStatus: the services of a system and how each is doing: up, degraded,// down or in maintenance, with its uptime and, if given, the last days as a strip// of small bars. One line at the top sums it up.//// <ServerStatus updated="2 minutes ago" services={[// { name: "API", status: "up", uptime: 99.98 },// { name: "Dashboard", status: "degraded", uptime: 99.5, note: "Slow page loads in EU" },// { name: "Email", status: "down", note: "Provider outage since 09:12" },// { name: "Search", status: "maintenance", history: ["up", "up", "down", "up", "up", "up", "up"] },// ]}/>//// services: a list of { name, status, uptime?, note?, href?, history? }. status:// "up", "degraded", "down" or "maintenance". uptime: a percentage ("99.98%").// note: what is going on. href: a link from the name (its status page).// history: statuses for the last days, oldest first, drawn as bars (a title on// each says which day back). The summary says "All systems operational", "Some// systems degraded" or "Some systems down" (down wins), and is a status region// screen readers hear change. updated: when this was last checked ("2 minutes// ago"). label: the list's name ("System status").import ui/theme.qwe use ui_clslet server_status_css = css`.ui-ss { display: grid; gap: 12px; font: 14px/1.4 var(--ui-font); color: var(--ui-fg); }.ui-ss-sum { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 12px; padding: 12px 16px; border-radius: var(--ui-radius); font-weight: 600; --t: var(--ui-success); --ts: var(--ui-success-soft); background: var(--ts); color: var(--t); }.ui-ss-sum-degraded { --t: var(--ui-warning); --ts: var(--ui-warning-soft); }.ui-ss-sum-down { --t: var(--ui-danger); --ts: var(--ui-danger-soft); }.ui-ss-sum-maintenance { --t: var(--ui-info); --ts: var(--ui-info-soft); }.ui-ss-updated { font-weight: 400; font-size: 13px; opacity: .85; }.ui-ss-list { margin: 0; padding: 0; list-style: none; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface); }.ui-ss-row { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 4px 16px; align-items: center; padding: 12px 16px; --t: var(--ui-strong); }.ui-ss-row + .ui-ss-row { border-top: 1px solid var(--ui-border); }.ui-ss-up { --t: var(--ui-success); }.ui-ss-degraded { --t: var(--ui-warning); }.ui-ss-down { --t: var(--ui-danger); }.ui-ss-maintenance { --t: var(--ui-info); }.ui-ss-name { font-weight: 500; }.ui-ss-name a { color: inherit; text-decoration: none; border-radius: 3px; }.ui-ss-name a:hover { color: var(--ui-accent); text-decoration: underline; }.ui-ss-name a:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-ss-note { grid-column: 1 / -1; margin: 0; color: var(--ui-muted); font-size: 13px; }.ui-ss-uptime { color: var(--ui-muted); font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }.ui-ss-state { display: inline-flex; align-items: center; gap: 7px; justify-self: end; color: var(--t); font-weight: 600; font-size: 13px; white-space: nowrap; }.ui-ss-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--t); }.ui-ss-down .ui-ss-dot { animation: ui-ss-blink 1.2s ease-in-out infinite; }@keyframes ui-ss-blink { 50% { opacity: .35; } }.ui-ss-bars { grid-column: 1 / -1; display: flex; gap: 3px; height: 14px; margin-top: 4px; }.ui-ss-bar { flex: 1; border-radius: 2px; background: var(--ui-strong); --t: var(--ui-strong); background: var(--t); }.ui-ss-bar-up { --t: var(--ui-success); }.ui-ss-bar-degraded { --t: var(--ui-warning); }.ui-ss-bar-down { --t: var(--ui-danger); }.ui-ss-bar-maintenance { --t: var(--ui-info); }@media (max-width: 480px) { .ui-ss-row { grid-template-columns: minmax(0, 1fr) auto; } .ui-ss-uptime { display: none; } }@media (prefers-reduced-motion: reduce) { .ui-ss-down .ui-ss-dot { animation: none; } }`// The word for a status: "Operational", "Degraded", "Down" or "Maintenance".fn server_status_word(status: str) -> str = match status { "up" => "Operational", "degraded" => "Degraded", "down" => "Down", "maintenance" => "Maintenance", _ => "Unknown" }// The worst status among the services: down, then degraded, then maintenance, then up.fn server_status_overall(services: list) -> str { let has = (s: str) => any(services, (x) => x.status == s) has("down") ? "down" : has("degraded") ? "degraded" : has("maintenance") ? "maintenance" : "up"}view ServerStatus(services: list, updated: str? = null, label: str = "System status", id: str? = null, class: str? = null) { style global server_status_css let overall = server_status_overall(services) let summary = match overall { "down" => "Some systems down" "degraded" => "Some systems degraded" "maintenance" => "Maintenance under way" _ => "All systems operational" } <div id={id} class={ui_cls(["ui-ss", class])}> <div class={ui_cls(["ui-ss-sum", overall != "up" && `ui-ss-sum-${overall}`])} role="status"> <span>{summary}</span> {updated != null && <span.ui-ss-updated>Updated {updated}</span>} </div> <ul.ui-ss-list aria-label={label}> {for s in services => <li class={ui_cls(["ui-ss-row", `ui-ss-${s.status}`])}> <span.ui-ss-name>{s.href != null ? <a href={s.href}>{s.name}</a> : s.name}</span> <span.ui-ss-uptime>{s.uptime != null ? `${s.uptime}% uptime` : ""}</span> <span.ui-ss-state><span.ui-ss-dot aria-hidden="true"></span>{server_status_word(s.status)}</span> {s.note != null && <p.ui-ss-note>{s.note}</p>} {s.history != null && len(s.history) > 0 && <div.ui-ss-bars role="img" aria-label={`Last ${len(s.history)} days`}> {for [i, h] in enumerate(s.history) => { let back = len(s.history) - 1 - i <span class={`ui-ss-bar ui-ss-bar-${h}`} title={`${back == 0 ? "Today" : back == 1 ? "Yesterday" : `${back} days ago`}: ${server_status_word(h)}`}></span> }} </div>} </li>} </ul> </div>}Related
QuotaBarSeveral quotas in one list, each a bar with its numbers, amber near its limit and red at it.7 views · 6 downloadsOnboardingChecklistSteps to finish setting up, with a progress bar, a tick on each done and a link to where the rest get done.7 views · 5 downloadsPermissionsMatrixRoles across, permissions down, a checkbox at each: a form sends the pairs ticked, a column box ticks a whole role.6 views · 6 downloadsSessionListSigned-in devices with browser, system, place and time, this one marked, and Sign out for each or all the others.6 views · 5 downloadsSettingsSectionA titled block of settings rows, each a label and hint with its control at the right (SettingsRow).5 views · 5 downloadsTokenListAPI tokens with name, prefix, created, last used, scopes and expiry, and a Revoke that asks first.6 views · 8 downloads
Thread
Thread 0 posts
On ServerStatus: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.