2026-07-19 19:45:32 +08:00

100 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { AlertTriangle, CheckCircle2, HeartPulse, PhoneCall } from "lucide-react";
import { AlertActions, CheckInButton } from "@/components/family-guard-actions";
import { PanelShell } from "@/components/panel-shell";
import { requireFamilyWorkspace } from "@/lib/family-page";
import { formatDateTime } from "@/lib/panel-format";
import { requireCaregiverSession } from "@/lib/page-auth";
import { getCaregiverDisplayName } from "@/lib/session";
export const dynamic = "force-dynamic";
export default async function GuardPage() {
const caregiver = await requireCaregiverSession("/guard");
const workspace = await requireFamilyWorkspace(caregiver.id);
const elder = workspace.elderProfile;
const device = elder.elderDevice;
const lastSeenAt = device.lastSeenAt;
const isOnline = Boolean(lastSeenAt);
const openAlerts = workspace.alerts.filter((alert) => alert.status === "OPEN" || alert.status === "ACKNOWLEDGED");
const today = new Date().toDateString();
const todayDoses = workspace.medicationPlans.flatMap((plan) => plan.doses).filter((dose) => dose.scheduledFor.toDateString() === today);
const doneDoses = todayDoses.filter((dose) => dose.status === "TAKEN").length;
const openTasks = workspace.tasks.filter((task) => task.status !== "COMPLETED" && task.status !== "CANCELLED");
const pendingCheckIns = workspace.checkIns.filter((checkIn) => checkIn.status === "PENDING");
const dutyNames = workspace.memberships
.filter((membership) => membership.emergencyPush)
.map((membership) => membership.caregiver.nickname || membership.caregiver.username || "家人");
return (
<PanelShell
currentPath="/guard"
title="家庭守护"
description={`${elder.preferredName} · 风险线索不是医疗诊断`}
caregiverLabel={getCaregiverDisplayName(caregiver)}
caregiverId={caregiver.id}
>
<section className="elder-status">
<div className="elder-status-main">
<span className="elder-avatar">{elder.preferredName.slice(0, 1)}</span>
<span className="row-main">
<span className="elder-name">
{elder.preferredName}
<span className={`dot ${isOnline ? "online" : "offline"}`} style={{ marginTop: 0 }} />
<span className="online-label" style={{ color: isOnline ? "var(--success)" : "var(--muted-light)" }}>
{isOnline ? "在线" : "离线"}
</span>
</span>
<span className="elder-meta">
{isOnline ? `最近与数字人互动 · ${device.displayName || "长辈终端"}` : `最近在线:${formatDateTime(lastSeenAt)} · ${device.displayName || "长辈终端"}`}
</span>
</span>
</div>
<CheckInButton deviceUuid={device.deviceUuid} />
</section>
<section className="stats" style={{ gridTemplateColumns: "repeat(4, 1fr)" }}>
<div className="stat accent"><strong>{openAlerts.length}</strong><span></span></div>
<div className="stat"><strong>{doneDoses}/{todayDoses.length}</strong><span></span></div>
<div className="stat"><strong>{openTasks.length}</strong><span></span></div>
<div className="stat"><strong>{pendingCheckIns.length}</strong><span></span></div>
</section>
<section>
<div className="section-title"><h2></h2><span></span></div>
<div className="card">
{workspace.alerts.length ? workspace.alerts.map((alert) => (
<article id={`alert-${alert.id}`} key={alert.id} className="timeline-item">
<div className="timeline-top">
<span className={`alert-title${alert.level === "RED" ? " red" : ""}`}>
<AlertTriangle size={15} strokeWidth={2} />{alert.title}
</span>
<span className="timeline-time">{formatDateTime(alert.createdAt)}</span>
</div>
<p className="timeline-body">{alert.summary}</p>
<p className="row-meta">{alert.claimedBy ? `已由 ${alert.claimedBy.nickname || alert.claimedBy.username || "家人"} 接手` : "尚未认领"}</p>
<AlertActions alertId={alert.id} status={alert.status} />
</article>
)) : (
<div className="empty"><CheckCircle2 size={26} /><br /></div>
)}
</div>
</section>
<section>
<div className="section-title"><h2></h2></div>
<div className="card">
<article className="timeline-item">
<div className="timeline-top"><HeartPulse size={15} />线<span className="timeline-time">{formatDateTime(lastSeenAt)}</span></div>
<p className="timeline-body">线</p>
</article>
<article className="timeline-item">
<div className="timeline-top"><PhoneCall size={15} /></div>
<p className="timeline-body">{dutyNames.length ? `${dutyNames.join("、")}已开启紧急推送` : "尚未设置家庭值守"}</p>
</article>
</div>
</section>
</PanelShell>
);
}