ThreadCount Community edition

Uniform stock management for healthcare linen rooms. Licensed under the GNU AGPL v3.
This commit is contained in:
ThreadCount
2026-09-13 08:45:19 +10:00
commit 1bc2de655a
505 changed files with 56223 additions and 0 deletions
@@ -0,0 +1,53 @@
"use client";
import { useState } from "react";
/* The reveal control: a button, a reason, a confirmation. On success the page reloads and the
* server renders the contacts through the reveal role — the values never travel in this response. */
export default function Reveal({ facilityId, minutes }: { facilityId: string; minutes: number }) {
const [open, setOpen] = useState(false);
const [reason, setReason] = useState("");
const [err, setErr] = useState("");
const [busy, setBusy] = useState(false);
async function submit(e: React.FormEvent) {
e.preventDefault();
setBusy(true); setErr("");
try {
const r = await fetch("/api/ops/reveal", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ facilityId, reason }) });
const j = await r.json().catch(() => ({}));
if (!r.ok) { setErr(j.error || "That didnt work."); return; }
window.location.reload();
} catch {
setErr("No connection — check the network and try again.");
} finally {
setBusy(false);
}
}
if (!open) {
return <button type="button" className="btn" onClick={() => setOpen(true)} style={{ marginTop: 12 }}>Reveal contacts</button>;
}
return (
<form onSubmit={submit} style={{ marginTop: 12, display: "grid", gap: 8 }}>
<label htmlFor="reveal-reason" style={{ fontSize: 11, fontWeight: 700, letterSpacing: "0.1em", textTransform: "uppercase", color: "var(--color-neutral-600)" }}>
Reason goes in the trail and the email
</label>
<textarea
id="reveal-reason"
className="input"
rows={3}
maxLength={400}
value={reason}
onChange={(e) => setReason(e.target.value)}
placeholder="e.g. Coordinator emailed support about a restore; calling back."
autoFocus
required
/>
{err && <div role="alert" style={{ border: "2px solid var(--color-accent)", padding: "8px 12px", fontSize: 13, fontWeight: 600, color: "var(--color-accent-700)" }}>{err}</div>}
<div style={{ display: "flex", gap: 8 }}>
<button type="submit" className="btn btn-primary" disabled={busy || reason.trim().length < 8}>{busy ? "Revealing…" : `Reveal for ${minutes} minutes`}</button>
<button type="button" className="btn" onClick={() => { setOpen(false); setErr(""); }} disabled={busy}>Cancel</button>
</div>
</form>
);
}