344b1701dd
Uniform stock management for healthcare linen rooms. Licensed under the GNU AGPL v3.
67 lines
4.1 KiB
TypeScript
67 lines
4.1 KiB
TypeScript
import { requireOperator } from "@/lib/ops/session";
|
|
import { facilities } from "@/lib/ops/projections";
|
|
|
|
/* Every facility: counts, dates, state. No customer records are reachable from this screen, and
|
|
* no contact appears on it at all — a reveal is a separate, trailed act on the facility's own page. */
|
|
export const dynamic = "force-dynamic";
|
|
|
|
const PILL: Record<string, { text: string; color: string }> = {
|
|
live: { text: "Live", color: "#2f6b4f" },
|
|
dormant: { text: "Dormant", color: "var(--color-neutral-600)" },
|
|
unconfigured: { text: "Never set up", color: "var(--color-neutral-600)" },
|
|
demo: { text: "Demo", color: "var(--color-neutral-600)" },
|
|
};
|
|
|
|
function Pill({ state }: { state: string }) {
|
|
const p = PILL[state] || PILL.dormant;
|
|
return <span style={{ display: "inline-block", fontSize: 11, fontWeight: 700, letterSpacing: "0.05em", textTransform: "uppercase", padding: "2px 7px", border: `1.5px solid ${p.color}`, color: p.color }}>{p.text}</span>;
|
|
}
|
|
|
|
export default async function OpsFacilities() {
|
|
await requireOperator();
|
|
const rows = await facilities();
|
|
const real = rows.filter((f) => !f.isDemo);
|
|
const demo = rows.filter((f) => f.isDemo);
|
|
const fmt = (d: Date) => d.toLocaleDateString("en-AU", { day: "numeric", month: "short" });
|
|
const n = (x: number) => x.toLocaleString();
|
|
const th = (t: string, right = false) => <th style={{ textAlign: right ? "right" : "left", fontSize: 11, fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--color-neutral-600)", padding: "9px 16px", borderBottom: "2px solid var(--color-text)", whiteSpace: "nowrap" }}>{t}</th>;
|
|
const td = (c: React.ReactNode, right = false) => <td style={{ textAlign: right ? "right" : "left", padding: "9px 16px", borderBottom: "1px solid var(--color-divider)", whiteSpace: "nowrap", fontVariantNumeric: "tabular-nums" }}>{c}</td>;
|
|
|
|
return (
|
|
<>
|
|
<div className="page-head">
|
|
<div>
|
|
<div className="sec" style={{ border: 0, padding: 0 }}>Facilities</div>
|
|
<h1 className="h1">Every facility</h1>
|
|
<div style={{ fontSize: 13, color: "var(--color-neutral-700)", marginTop: 4 }}>Counts and dates only. No customer records are reachable from this screen.</div>
|
|
</div>
|
|
</div>
|
|
<div className="tc-panel" style={{ marginTop: 20 }}>
|
|
<div className="tc-panel-head"><span>{real.length} facilities</span><span className="tc-panel-aside">{demo.length ? "demo shown last" : "no demo facility"}</span></div>
|
|
<div style={{ overflowX: "auto" }}>
|
|
<table style={{ borderCollapse: "collapse", width: "100%", fontSize: 13 }}>
|
|
<thead><tr>{th("Facility")}{th("State")}{th("Plan")}{th("Created")}{th("Users", true)}{th("Staff", true)}{th("Items", true)}{th("Issues", true)}{th("Requests", true)}{th("rev", true)}{th("Backup")}</tr></thead>
|
|
<tbody>
|
|
{[...real, ...demo].map((f) => (
|
|
<tr key={f.id}>
|
|
{td(<a href={`/ops/facilities/${f.id}`} style={{ fontWeight: 700 }}>{f.name}</a>)}
|
|
{td(<Pill state={f.state} />)}
|
|
{td(f.isDemo ? "—" : (
|
|
<span style={{ color: f.planView.readOnly ? "var(--color-accent-700)" : undefined, fontWeight: f.planView.readOnly ? 700 : undefined }}>
|
|
{f.planView.grandfathered ? `${f.planView.label} · grandfathered` : `${f.planView.label} · ${f.planView.state.replace("_", "-")}`}
|
|
</span>
|
|
))}
|
|
{td(f.isDemo ? "—" : fmt(f.createdAt))}
|
|
{td(n(f.users), true)}{td(n(f.staff), true)}{td(n(f.items), true)}{td(n(f.issues), true)}{td(n(f.requests), true)}{td(f.isDemo ? "—" : n(f.rev), true)}
|
|
{td(f.isDemo ? "—" : f.backupAgeDays === null ? <span style={{ color: "#8a6218", fontWeight: 700 }}>never</span> : f.backupAgeDays > 7 ? <span style={{ color: "#8a6218", fontWeight: 700 }}>{f.backupAgeDays}d</span> : `${f.backupAgeDays}d`)}
|
|
</tr>
|
|
))}
|
|
{rows.length === 0 && <tr>{td("No facilities yet.")}</tr>}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|