/* ================================================================== Overview → Parents ------------------------------------------------------------------ The parent portal seen from the office: account state, how much of the student roll is actually covered by a linked parent, the teacher-chat backlog, and how far announcements are reaching. Reads /admin/cockpit/parents. "Announcement reach" is delivered vs read from parent_notifications — if nothing was ever delivered the card says so rather than reporting 0%, which would read as "nobody opened it". ================================================================== */ const ParentsOverviewScreen = ({ setScreen }) => { const CX = window.CX; const state = CX.useCockpitData("/admin/cockpit/parents", { pollMs: 120000 }); const d = state.data; return (
setScreen("announcements")}> Broadcast announcement } /> {d && ( <>
setScreen("parents")} actionLabel="Open parent accounts" footNote={ `${d.coverage.studentsCovered} of ${CX.num(d.coverage.studentsTotal)} students linked · ` + `${d.coverage.coveragePct}%` } action={Accounts} /> setScreen("parents")} actionLabel="Review sign-ups" footNote={d.parents.pending ? "Sign-ups awaiting activation" : "Nothing waiting for review"} action={d.parents.pending ? : Clear} /> Threads} />
Teacher ↔ Parent Threads
Most recent
{d.chats.list.length === 0 ? ( ) : d.chats.list.map((c) => (
{c.parent_name || "Parent"} {c.teacher_name ? <> ↔ {c.teacher_name} : null} {c.student_name ? · about {c.student_name} : null}
{c.last_body ? `${c.last_sender === "parent" ? "Parent" : "Teacher"}: ${c.last_body}` : "No messages yet"}
{c.unread_from_parent > 0 && ( {c.unread_from_parent} unread )}
{c.last_message_at ? CX.relTime(c.last_message_at) : "—"}
))}
{d.parents.active7d} parent{d.parents.active7d === 1 ? "" : "s"} signed in this week ·{" "} {d.parents.pushEnabled} with push enabled
Announcement Reach
Read rate
{d.announcements.length === 0 ? ( ) : (
{d.announcements.map((a, i) => { const colors = ["var(--green-solid)", "var(--blue-solid)", "var(--pink-solid)", "var(--amber-solid)", "var(--violet-solid)"]; return (
{a.title} {a.delivered === 0 ? "not delivered" : <>{a.readPct}% · {a.read}/{a.delivered} read}
{/* An undelivered notice gets an empty track, not a 0% bar — the distinction is the whole point. */}
); })}
)} {d.announcements.some((a) => a.delivered === 0) && (
Some notices have no delivery records. A notice only reaches a parent once their account is approved and linked to a student — currently{" "} {d.coverage.coveragePct}% of the roll.
)}
{d.coverage.verified} of {d.coverage.links} parent–student links verified
)}
); }; window.CockpitParents = ParentsOverviewScreen;