057da00fd2
Uniform stock management for healthcare linen rooms: the coordinator app, the phone counter and the staff app, for your own server. Built from e2d6d42 on 2026-09-13. Licensed under the Functional Source License (FSL-1.1-ALv2).
29 lines
1.6 KiB
TypeScript
29 lines
1.6 KiB
TypeScript
"use client";
|
|
import Link from "next/link";
|
|
import { useSnap } from "@/lib/client";
|
|
|
|
/* One bar across every /app screen when the plan needs a coordinator's attention, and nothing
|
|
* otherwise. Three states earn it: read-only (writes refused, reads and exports still fine), grace
|
|
* (the period has ended, a fortnight to sort the invoice), and the last fortnight of a trial. A
|
|
* free or paid room in good standing sees nothing here — plans are not a thing to be reminded of.
|
|
* Quiet until plans are live, whatever the columns say. */
|
|
export default function PlanBanner() {
|
|
const { s } = useSnap();
|
|
const p = s.plan;
|
|
if (!p || !p.live || s.demo) return null;
|
|
let text: string | null = null;
|
|
let tone: "warn" | "ink" = "ink";
|
|
if (p.readOnly) { text = "Read-only. Reports, exports and the backup still work."; tone = "warn"; }
|
|
else if (p.state === "grace") { text = `${p.label} ended. Writable for ${p.graceDaysLeft ?? 0} more days.`; tone = "warn"; }
|
|
else if (p.state === "trial" && p.daysLeft !== null && p.daysLeft <= 14) text = `Trial ends in ${p.daysLeft} days.`;
|
|
if (!text) return null;
|
|
const bg = tone === "warn" ? "var(--color-accent)" : "var(--color-text)";
|
|
return (
|
|
<div role="status" style={{ display: "flex", alignItems: "center", gap: 14, padding: "8px 16px", background: bg, color: "#fff", fontSize: 12.5, flexWrap: "wrap" }}>
|
|
<span style={{ width: 8, height: 8, background: "#fff", flex: "none" }} />
|
|
<b>{text}</b>
|
|
{s.session.role === "Admin" && <Link href="/app/settings?tab=plan" style={{ marginLeft: "auto", color: "#fff", fontWeight: 700 }}>Plan</Link>}
|
|
</div>
|
|
);
|
|
}
|