Dashboard

ServerStatus

Services with up, degraded, down or maintenance, their uptime and the last days as bars, summed up in one line.

#dashboard#status#admin#list#no-js

Demo

Preview
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.qwe
main.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>}
Thread
66Useful? Like it, or add to its thread below.

Thread 0 posts

On ServerStatus: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.