ThreadCount Community edition
Uniform stock management for healthcare linen rooms. Licensed under the GNU AGPL v3.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
|
||||
/* The one thing on the console that cannot be undone. Owner only (the server checks too), the
|
||||
* facility's exact name typed, and a live code from the operator's own second factor. */
|
||||
export default function Danger({ facilityId, name, owner, totpEnabled }: { facilityId: string; name: string; owner: boolean; totpEnabled: boolean }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [confirm, setConfirm] = useState("");
|
||||
const [code, setCode] = 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/controls", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ action: "facility.delete", facilityId, confirm, code }) });
|
||||
const j = await r.json().catch(() => ({}));
|
||||
if (!r.ok) { setErr(j.error || "That didn’t work."); return; }
|
||||
window.location.assign("/ops/facilities?deleted=1");
|
||||
} catch {
|
||||
setErr("No connection — check the network and try again.");
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
const label: React.CSSProperties = { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.1em", textTransform: "uppercase", color: "var(--color-neutral-600)", marginBottom: 4 };
|
||||
|
||||
if (!owner) return <div className="tc-panel-body" style={{ fontSize: 13, color: "var(--color-neutral-700)" }}>Only an owner can delete a facility.</div>;
|
||||
if (!totpEnabled) return <div className="tc-panel-body" style={{ fontSize: 13, color: "var(--color-neutral-700)" }}>Enrol a second factor under <a href="/ops/security">Your sign-in</a> before deleting anything.</div>;
|
||||
if (!open) {
|
||||
return (
|
||||
<div className="tc-panel-body" style={{ fontSize: 13, display: "flex", alignItems: "center", gap: 12 }}>
|
||||
<div style={{ flex: 1, color: "var(--color-neutral-700)" }}>Deletes every record, image and account belonging to this facility. There is no undo beyond the database dumps on the box.</div>
|
||||
<button type="button" className="btn" onClick={() => setOpen(true)}>Delete facility…</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<form onSubmit={submit} className="tc-panel-body" style={{ fontSize: 13, display: "grid", gap: 10 }}>
|
||||
<div>
|
||||
<label htmlFor="danger-confirm" style={label}>Type the facility name exactly</label>
|
||||
<input id="danger-confirm" className="input" value={confirm} onChange={(e) => setConfirm(e.target.value)} placeholder={name} autoComplete="off" autoFocus />
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="danger-code" style={label}>Code from your authenticator</label>
|
||||
<input id="danger-code" className="input" inputMode="numeric" autoComplete="one-time-code" maxLength={7} value={code} onChange={(e) => setCode(e.target.value)} style={{ maxWidth: 160 }} />
|
||||
</div>
|
||||
{err && <div role="alert" style={{ border: "2px solid var(--color-accent)", padding: "8px 12px", fontWeight: 600, color: "var(--color-accent-700)" }}>{err}</div>}
|
||||
<div style={{ display: "flex", gap: 8 }}>
|
||||
<button type="submit" className="btn btn-primary" disabled={busy || confirm.trim() !== name || code.replace(/\s+/g, "").length < 6}>{busy ? "Deleting…" : "Delete this facility"}</button>
|
||||
<button type="button" className="btn" onClick={() => { setOpen(false); setConfirm(""); setCode(""); setErr(""); }} disabled={busy}>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user