240 lines
11 KiB
TypeScript
240 lines
11 KiB
TypeScript
import Link from "next/link";
|
||
|
||
import { PanelShell } from "@/components/panel-shell";
|
||
import { PwaControls } from "@/components/pwa-controls";
|
||
import { getCaregiverOverview } from "@/lib/caregiver-panel";
|
||
import {
|
||
formatDateTime,
|
||
getDeviceName,
|
||
getImportanceLabel,
|
||
getMessageHref,
|
||
truncateText,
|
||
} from "@/lib/panel-format";
|
||
import { requireCaregiverSession } from "@/lib/page-auth";
|
||
|
||
export const dynamic = "force-dynamic";
|
||
|
||
export default async function Home() {
|
||
const caregiver = await requireCaregiverSession("/");
|
||
const { dashboard, recentIncomingMessages, recentOutgoingMessages } =
|
||
await getCaregiverOverview(caregiver.id);
|
||
|
||
const totalUnreadForElder = dashboard.devices.reduce(
|
||
(total, device) => total + device.familyUnreadCount,
|
||
0,
|
||
);
|
||
const totalUnreadForCaregiver = dashboard.devices.reduce(
|
||
(total, device) => total + device.elderUnreadCount,
|
||
0,
|
||
);
|
||
const totalConversations = dashboard.devices.reduce(
|
||
(total, device) => total + device.elderDevice._count.conversationTurns,
|
||
0,
|
||
);
|
||
|
||
return (
|
||
<PanelShell
|
||
currentPath="/"
|
||
title="首页只保留摘要,不再把所有操作挤在一处。"
|
||
description="设备绑定、留言管理、活动回看和扫码入口已经拆到独立路径。这里保留今天最值得先看的摘要,以及最近的新留言。"
|
||
caregiverToken={dashboard.caregiver.sessionToken}
|
||
actions={<PwaControls />}
|
||
>
|
||
<section className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||
<div className="rounded-[28px] border border-white/80 bg-white/88 p-5 shadow-[0_20px_50px_rgba(115,76,42,0.10)]">
|
||
<p className="text-sm text-[var(--muted)]">已连接老人设备</p>
|
||
<p className="mt-3 font-[family-name:var(--font-display)] text-4xl text-[var(--ink)]">
|
||
{dashboard.devices.length}
|
||
</p>
|
||
</div>
|
||
<div className="rounded-[28px] border border-white/80 bg-white/88 p-5 shadow-[0_20px_50px_rgba(115,76,42,0.10)]">
|
||
<p className="text-sm text-[var(--muted)]">老人给家里的未读留言</p>
|
||
<p className="mt-3 font-[family-name:var(--font-display)] text-4xl text-[var(--ink)]">
|
||
{totalUnreadForCaregiver}
|
||
</p>
|
||
</div>
|
||
<div className="rounded-[28px] border border-white/80 bg-white/88 p-5 shadow-[0_20px_50px_rgba(115,76,42,0.10)]">
|
||
<p className="text-sm text-[var(--muted)]">待老人查看的家属留言</p>
|
||
<p className="mt-3 font-[family-name:var(--font-display)] text-4xl text-[var(--ink)]">
|
||
{totalUnreadForElder}
|
||
</p>
|
||
</div>
|
||
<div className="rounded-[28px] border border-white/80 bg-white/88 p-5 shadow-[0_20px_50px_rgba(115,76,42,0.10)]">
|
||
<p className="text-sm text-[var(--muted)]">最近存档对话条数</p>
|
||
<p className="mt-3 font-[family-name:var(--font-display)] text-4xl text-[var(--ink)]">
|
||
{totalConversations}
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
<section className="grid gap-4 lg:grid-cols-4">
|
||
{[
|
||
{
|
||
href: "/messages",
|
||
title: "留言中心",
|
||
text: "集中看所有老人留言和家属留言,每条消息都有 URL 编号可直接打开。",
|
||
tone: "bg-[rgba(53,84,141,0.12)]",
|
||
},
|
||
{
|
||
href: "/devices",
|
||
title: "设备与绑定",
|
||
text: "去管理设备、扫码绑定、查看某一台设备的专属详情页和留言入口。",
|
||
tone: "bg-[rgba(118,132,94,0.14)]",
|
||
},
|
||
{
|
||
href: "/activity",
|
||
title: "活动回看",
|
||
text: "把使用事件、工具调用和对话片段拆出来,回看时不再和留言混在一起。",
|
||
tone: "bg-[rgba(199,103,51,0.12)]",
|
||
},
|
||
{
|
||
href: "/scan",
|
||
title: "扫码绑定",
|
||
text: "拿起另一台手机时,直接走扫码页,不必再回首页寻找入口。",
|
||
tone: "bg-[rgba(36,27,20,0.08)]",
|
||
},
|
||
].map((item) => (
|
||
<Link
|
||
key={item.href}
|
||
href={item.href}
|
||
className="rounded-[28px] border border-white/80 bg-white/84 p-5 shadow-[0_20px_50px_rgba(115,76,42,0.10)] transition hover:-translate-y-0.5"
|
||
>
|
||
<div className={`inline-flex rounded-full px-3 py-1 text-xs font-semibold text-[var(--ink)] ${item.tone}`}>
|
||
跳转路径
|
||
</div>
|
||
<h2 className="mt-4 font-[family-name:var(--font-display)] text-2xl text-[var(--ink)]">
|
||
{item.title}
|
||
</h2>
|
||
<p className="mt-3 text-sm leading-7 text-[var(--muted)]">{item.text}</p>
|
||
<p className="mt-4 text-sm font-semibold text-[var(--copper)]">前往 {item.href}</p>
|
||
</Link>
|
||
))}
|
||
</section>
|
||
|
||
{dashboard.devices.length === 0 ? (
|
||
<section className="rounded-[32px] border border-dashed border-[var(--line)] bg-white/70 px-6 py-10 text-center shadow-[0_20px_50px_rgba(115,76,42,0.10)]">
|
||
<p className="text-sm font-medium tracking-[0.18em] text-[var(--copper)] uppercase">
|
||
还没有绑定老人设备
|
||
</p>
|
||
<h2 className="mt-3 font-[family-name:var(--font-display)] text-3xl text-[var(--ink)]">
|
||
先去设备页或扫码页建立第一条连接
|
||
</h2>
|
||
<p className="mx-auto mt-4 max-w-2xl text-base leading-8 text-[var(--muted)]">
|
||
绑定完成后,留言中心会出现 URL 编号消息卡,活动页会出现陪伴记录,推送也能直达具体留言。
|
||
</p>
|
||
<div className="mt-6 flex justify-center gap-3">
|
||
<Link
|
||
href="/devices"
|
||
className="inline-flex h-12 items-center justify-center rounded-full bg-[var(--copper)] px-5 text-sm font-semibold text-white transition hover:bg-[var(--copper-deep)]"
|
||
>
|
||
去设备页
|
||
</Link>
|
||
<Link
|
||
href="/scan"
|
||
className="inline-flex h-12 items-center justify-center rounded-full border border-[var(--line)] bg-white px-5 text-sm font-semibold text-[var(--ink)] transition hover:bg-[var(--paper-soft)]"
|
||
>
|
||
直接扫码
|
||
</Link>
|
||
</div>
|
||
</section>
|
||
) : (
|
||
<section className="grid gap-6 xl:grid-cols-2">
|
||
<div className="rounded-[32px] border border-white/75 bg-white/80 p-6 shadow-[0_26px_60px_rgba(115,76,42,0.12)]">
|
||
<div className="flex items-center justify-between gap-3">
|
||
<div>
|
||
<p className="text-sm font-medium tracking-[0.18em] text-[var(--copper)] uppercase">
|
||
新收到的老人留言
|
||
</p>
|
||
<h2 className="mt-2 font-[family-name:var(--font-display)] text-3xl text-[var(--ink)]">
|
||
点一下就能按编号直达
|
||
</h2>
|
||
</div>
|
||
<Link
|
||
href="/messages"
|
||
className="inline-flex h-11 items-center justify-center rounded-full border border-[var(--line)] bg-white px-4 text-sm font-semibold text-[var(--ink)] transition hover:bg-[var(--paper-soft)]"
|
||
>
|
||
查看全部留言
|
||
</Link>
|
||
</div>
|
||
|
||
<div className="mt-5 space-y-3">
|
||
{recentIncomingMessages.length === 0 ? (
|
||
<p className="rounded-[24px] bg-[var(--paper-soft)] px-5 py-4 text-sm leading-7 text-[var(--muted)]">
|
||
目前还没有新的老人留言。
|
||
</p>
|
||
) : (
|
||
recentIncomingMessages.map((message) => (
|
||
<Link
|
||
key={message.id}
|
||
href={getMessageHref(message.publicId)}
|
||
className="block rounded-[24px] bg-[var(--paper-soft)] px-5 py-4 transition hover:bg-white"
|
||
>
|
||
<div className="flex flex-wrap items-center gap-2 text-xs text-[var(--muted)]">
|
||
<span className="rounded-full bg-white px-3 py-1 font-semibold text-[var(--ink)]">
|
||
#{message.publicId}
|
||
</span>
|
||
<span>{getDeviceName(message.elderDevice.displayName)}</span>
|
||
<span>{getImportanceLabel(message.importance)}</span>
|
||
<span>{formatDateTime(message.createdAt)}</span>
|
||
</div>
|
||
<p className="mt-3 text-sm leading-7 text-[var(--ink)]">
|
||
{truncateText(message.content)}
|
||
</p>
|
||
</Link>
|
||
))
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="rounded-[32px] border border-white/75 bg-white/80 p-6 shadow-[0_26px_60px_rgba(115,76,42,0.12)]">
|
||
<div className="flex items-center justify-between gap-3">
|
||
<div>
|
||
<p className="text-sm font-medium tracking-[0.18em] text-[var(--copper)] uppercase">
|
||
最近发给老人的问候
|
||
</p>
|
||
<h2 className="mt-2 font-[family-name:var(--font-display)] text-3xl text-[var(--ink)]">
|
||
发出后也能按编号回看
|
||
</h2>
|
||
</div>
|
||
<Link
|
||
href="/devices"
|
||
className="inline-flex h-11 items-center justify-center rounded-full border border-[var(--line)] bg-white px-4 text-sm font-semibold text-[var(--ink)] transition hover:bg-[var(--paper-soft)]"
|
||
>
|
||
去设备页留言
|
||
</Link>
|
||
</div>
|
||
|
||
<div className="mt-5 space-y-3">
|
||
{recentOutgoingMessages.length === 0 ? (
|
||
<p className="rounded-[24px] bg-[var(--paper-soft)] px-5 py-4 text-sm leading-7 text-[var(--muted)]">
|
||
还没有新的家属留言,可以到设备页针对某一台设备写下问候。
|
||
</p>
|
||
) : (
|
||
recentOutgoingMessages.map((message) => (
|
||
<Link
|
||
key={message.id}
|
||
href={getMessageHref(message.publicId)}
|
||
className="block rounded-[24px] bg-[var(--paper-soft)] px-5 py-4 transition hover:bg-white"
|
||
>
|
||
<div className="flex flex-wrap items-center gap-2 text-xs text-[var(--muted)]">
|
||
<span className="rounded-full bg-white px-3 py-1 font-semibold text-[var(--ink)]">
|
||
#{message.publicId}
|
||
</span>
|
||
<span>{getDeviceName(message.elderDevice.displayName)}</span>
|
||
<span>{getImportanceLabel(message.importance)}</span>
|
||
<span>{formatDateTime(message.createdAt)}</span>
|
||
</div>
|
||
<p className="mt-3 text-sm leading-7 text-[var(--ink)]">
|
||
{truncateText(message.content)}
|
||
</p>
|
||
</Link>
|
||
))
|
||
)}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
)}
|
||
</PanelShell>
|
||
);
|
||
}
|