"use client"; /* 1J — Account. * * What a wearer can do to their own sign-in, and one thing they deliberately cannot. * * Changing the password is the only revocation they have: a staff token carries a fingerprint of * the password hash, so setting a new one ends every session signed against the old one at once — * a phone left on a ward, a cookie copied off it, a password read over somebody's shoulder. The * copy says so plainly, because the consequence is the feature and somebody who does not know it * happens will not reach for this when they most need it. * * Deleting the account is not here, and is not an oversight. Access is the linen room's to grant * and theirs to remove: a wearer who could delete their own account would take the record of what * they were issued with it. */ import { DELETE_ACCOUNT_URL, PRIVACY_EMAIL, PRIVACY_URL, TERMS_URL } from "@/lib/links"; import { useState } from "react"; import { MBar, MBody, MError, MRow, MRule, MSection, MTop } from "@/components/m"; import { INK, N600, N700 } from "@/components/staffui"; import { useStaff } from "@/lib/staffclient"; import { forgetPush } from "@/lib/staffpush"; import NotificationSettings, { type NotifyPrefs } from "@/components/screens/NotificationSettings"; const field: React.CSSProperties = { width: "100%", minHeight: 52, padding: "0 14px", border: "2px solid var(--color-divider)", borderRadius: 0, font: "inherit", fontSize: 16, background: "#fff", color: "var(--color-text)", }; const label: React.CSSProperties = { display: "block", fontSize: 12.5, fontWeight: 800, letterSpacing: "0.06em", textTransform: "uppercase", color: N600, }; export default function AccountScreen({ email, prefs, pushReady }: { email: string; prefs: NotifyPrefs; pushReady: boolean; }) { const { me, mutate, busy } = useStaff(); const [open, setOpen] = useState(false); const [current, setCurrent] = useState(""); const [next, setNext] = useState(""); const [err, setErr] = useState(""); const [done, setDone] = useState(false); const [leaving, setLeaving] = useState(false); // The server enforces the same floor; checking it here only saves a round trip and a refusal. const ready = current.length > 0 && next.length >= 8; /* ⛔ Signing out never waits on push.forget succeeding, and never fails because it didn't. * Somebody on a ward with no signal still has to be able to leave a phone they are handing on. * An orphaned token is reclaimed three other ways — the next registration re-points it, FCM * reports it gone, and a password change clears the lot. */ async function signOut() { setLeaving(true); const token = forgetPush(); if (token) void mutate("push.forget", { token }); await fetch("/api/staff/logout", { method: "POST", headers: { "content-type": "application/json" }, body: "{}", }).catch(() => {}); // A full navigation: the cookie has just been cleared and every screen behind it is // server-rendered. window.location.replace("/my/signin"); } return ( <> setErr("")} />
{me.name}
{email}
{done ? (
Password changed

Every other device signed in as you has been signed out, and any phone of yours set up for notifications has been unregistered. This one stays signed in.

) : ( <> {/* A disclosure, not a link: the form is on this screen, so the row says so in words a screen reader is given rather than only by what appears underneath it. */} {open && (
{/* The words wrap the box rather than sitting beside it: on the one screen where typing in the wrong one of two password fields is silent, both must announce which they are. */}

At least 8 characters. Changing it signs you out everywhere else straight away. This device stays signed in.

)} )}

ThreadCount holds your sign-in and the linen room’s record of what you have been issued. You can’t delete this account from here — ask your uniform coordinator and they can remove it{PRIVACY_EMAIL ? <>, or write to {PRIVACY_EMAIL} : null}.

{PRIVACY_URL && } {DELETE_ACCOUNT_URL && } {TERMS_URL && } {/* The one thing a wearer can do to a phone they no longer have, so it is findable without asking — and at the foot, because it is the last thing anybody comes here to do. */}
{open && !done && ( { const r = await mutate("account.password", { current, next }); if (!r.ok) { setErr(r.error); return; } setCurrent(""); setNext(""); setOpen(false); setDone(true); }} /> )} ); }