344b1701dd
Uniform stock management for healthcare linen rooms. Licensed under the GNU AGPL v3.
79 lines
6.2 KiB
TypeScript
79 lines
6.2 KiB
TypeScript
import { requireOperator } from "@/lib/ops/session";
|
|
import { overview } from "@/lib/ops/projections";
|
|
import { version } from "@/lib/ops/version";
|
|
|
|
/* Overview: is everything up, how many facilities, who needs attention. Counts and dates only —
|
|
* nothing on this screen names a person or reads a record. */
|
|
export const dynamic = "force-dynamic";
|
|
|
|
const tile: React.CSSProperties = { background: "var(--color-bg)", padding: 16 };
|
|
const figure: React.CSSProperties = { fontSize: 30, fontWeight: 800, lineHeight: 1, fontVariantNumeric: "tabular-nums" };
|
|
const label: React.CSSProperties = { fontSize: 11, fontWeight: 700, letterSpacing: "0.09em", textTransform: "uppercase", color: "var(--color-neutral-600)", marginTop: 6 };
|
|
const note: React.CSSProperties = { fontSize: 12, color: "var(--color-neutral-700)", marginTop: 2 };
|
|
|
|
export default async function OpsOverview() {
|
|
await requireOperator();
|
|
const o = await overview();
|
|
const v = version();
|
|
const stale = !!(v.deployed && v.running && v.deployed.sha !== v.running);
|
|
const fmtAt = (d: Date) => d.toLocaleString("en-AU", { timeZone: "Australia/Brisbane", dateStyle: "medium", timeStyle: "short" });
|
|
const attention = [
|
|
...o.overdueBackups.map((f) => ({ tone: "warn", who: f.name, what: f.backupAgeDays === null ? "has never exported a backup" : `has not exported a backup in ${f.backupAgeDays} days`, meta: `${f.issues.toLocaleString()} issues on record`, href: `/ops/facilities/${f.id}` })),
|
|
...o.neverIssued.map((f) => ({ tone: "dormant", who: f.name, what: "has never issued", meta: `signed up ${f.createdAt.toLocaleDateString("en-AU", { day: "numeric", month: "short" })} · ${f.staff} staff · ${f.items} items`, href: `/ops/facilities/${f.id}` })),
|
|
...o.unconfiguredList.map((f) => ({ tone: "dormant", who: f.name, what: "has no staff groups named", meta: "everyone is on manager approval until a group goes on a route", href: `/ops/facilities/${f.id}` })),
|
|
];
|
|
const drift = o.migrations.applied !== o.migrations.inRepo;
|
|
|
|
return (
|
|
<>
|
|
<div className="page-head">
|
|
<div>
|
|
<div className="sec" style={{ border: 0, padding: 0 }}>Overview</div>
|
|
<h1 className="h1">Everything, at a glance</h1>
|
|
<div style={{ fontSize: 13, color: "var(--color-neutral-700)", marginTop: 4 }}>
|
|
{o.facilities} facilities · {o.live} live · migrations {o.migrations.applied}/{o.migrations.inRepo}{drift ? " — DRIFT" : ""}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))", gap: 2, background: "var(--color-divider)", border: "2px solid var(--color-text)", marginTop: 20 }}>
|
|
<div style={tile}><div style={figure}>{o.facilities}</div><div style={label}>Facilities</div><div style={note}>{o.demo} demo, excluded below</div></div>
|
|
<div style={tile}><div style={figure}>{o.live}</div><div style={label}>Live</div><div style={note}>have issued at least once</div></div>
|
|
<div style={tile}><div style={figure}>{o.dormant + o.unconfigured}</div><div style={label}>Not yet live</div><div style={note}>{o.unconfigured} never set up</div></div>
|
|
<div style={tile}><div style={figure}>{o.signupsThisMonth}</div><div style={label}>Signups this month</div><div style={note}>{o.reachedFirstIssue} reached first issue</div></div>
|
|
</div>
|
|
|
|
<div className="tc-panel" style={{ marginTop: 24 }}>
|
|
<div className="tc-panel-head"><span>Needs attention</span><span className="tc-panel-aside">{attention.length} {attention.length === 1 ? "item" : "items"}</span></div>
|
|
{attention.length === 0 ? (
|
|
<div className="tc-panel-body" style={{ fontSize: 13, color: "var(--color-neutral-700)" }}>Nothing. Every live facility has a recent backup and every signup has issued.</div>
|
|
) : (
|
|
<ul style={{ listStyle: "none", margin: 0, padding: 0 }}>
|
|
{attention.map((a, i) => (
|
|
<li key={i} style={{ display: "flex", gap: 12, alignItems: "baseline", padding: "11px 16px", borderBottom: "1px solid var(--color-divider)" }}>
|
|
<span style={{ width: 4, alignSelf: "stretch", flex: "none", background: a.tone === "warn" ? "#8a6218" : "var(--color-neutral-600)" }} />
|
|
<span style={{ flex: 1, minWidth: 0 }}><b>{a.who}</b> {a.what}<div style={{ fontSize: 12, color: "var(--color-neutral-700)" }}>{a.meta}</div></span>
|
|
<a className="btn" href={a.href} style={{ padding: "3px 9px", fontSize: 12 }}>Open</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
|
|
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))", gap: 24, marginTop: 24 }}>
|
|
<div className="tc-panel">
|
|
<div className="tc-panel-head"><span>Platform</span></div>
|
|
<dl className="tc-panel-body" style={{ display: "grid", gridTemplateColumns: "auto 1fr", gap: "8px 16px", fontSize: 13, margin: 0 }}>
|
|
<dt style={{ color: "var(--color-neutral-600)" }}>Migrations</dt><dd style={{ margin: 0, fontWeight: 600 }}>{o.migrations.applied} applied / {o.migrations.inRepo} in repo{drift ? " — drift" : ""}</dd>
|
|
<dt style={{ color: "var(--color-neutral-600)" }}>Enquiries</dt><dd style={{ margin: 0, fontWeight: 600 }}>{o.enquiriesUnhandled} unhandled</dd>
|
|
<dt style={{ color: "var(--color-neutral-600)" }}>Demo facility</dt><dd style={{ margin: 0, fontWeight: 600 }}>{o.demoRebuiltAgoMin === null ? "none" : `rebuilt ${o.demoRebuiltAgoMin} min ago`}</dd>
|
|
{/* Whole phrases (see the facility page): React puts comment nodes between adjacent expressions. */}
|
|
<dt style={{ color: "var(--color-neutral-600)" }}>Deployed</dt><dd style={{ margin: 0, fontWeight: 600 }}>{v.deployed ? `${v.deployed.sha} · ${fmtAt(v.deployed.at)}${v.deployed.from ? ` · was ${v.deployed.from}` : ""}` : "not recorded — the deploy script predates the release file"}</dd>
|
|
<dt style={{ color: "var(--color-neutral-600)" }}>Running</dt><dd style={{ margin: 0, fontWeight: 600, color: stale ? "var(--color-accent-700)" : undefined }}>{`${v.running || "release unknown"}${stale ? " — STALE: built but never restarted" : ""} · up ${v.uptimeMin} min · node ${v.node}`}</dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|