优化ui
This commit is contained in:
+88
-12
@@ -1,4 +1,5 @@
|
||||
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";
|
||||
@@ -7,17 +8,92 @@ 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 openAlerts = workspace.alerts.filter((a) => a.status === "OPEN" || a.status === "ACKNOWLEDGED");
|
||||
const todayDoses = workspace.medicationPlans.flatMap((p) => p.doses).filter((d) => d.scheduledFor.toDateString() === new Date().toDateString());
|
||||
const doneDoses = todayDoses.filter((d) => d.status === "TAKEN").length;
|
||||
const openTasks = workspace.tasks.filter((t) => t.status !== "COMPLETED" && t.status !== "CANCELLED");
|
||||
const pendingCheckIns = workspace.checkIns.filter((c) => c.status === "PENDING");
|
||||
return <PanelShell currentPath="/guard" title="家庭守护" description={`${workspace.elderProfile.preferredName} · 风险线索不是医疗诊断`} caregiverLabel={getCaregiverDisplayName(caregiver)}>
|
||||
<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>
|
||||
<CheckInButton deviceUuid={workspace.elderProfile.elderDevice.deviceUuid} />
|
||||
<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 style={{ color: alert.level === "RED" ? "#b33a2b" : "var(--accent)" }}><AlertTriangle size={15} style={{ verticalAlign: -3, marginRight: 5 }} />{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"><div className="timeline-item"><div className="timeline-top"><HeartPulse size={15} />设备最近在线<span className="timeline-time">{formatDateTime(workspace.elderProfile.elderDevice.lastSeenAt)}</span></div><p className="timeline-body">仅根据终端在线和主动交互记录展示,不代表生命体征状态。</p></div><div className="timeline-item"><div className="timeline-top"><PhoneCall size={15} />家庭值守</div><p className="timeline-body">{workspace.memberships.filter((m) => m.emergencyPush).map((m) => m.caregiver.nickname || m.caregiver.username || "家人").join("、") || "尚未设置"}</p></div></div></section>
|
||||
</PanelShell>;
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user