2026-04-17 12:38:59 +08:00

249 lines
11 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 Link from "next/link";
import { MessageCircle, Smartphone, Activity, ScanLine } from "lucide-react";
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: "查看长辈捎来的话,也能回看您发出的每一条问候。",
tone: "bg-[rgba(53,84,141,0.12)]",
icon: MessageCircle,
},
{
href: "/devices",
title: "我的设备",
text: "管理已绑定的长辈设备,进入设备详情给长辈写留言。",
tone: "bg-[rgba(118,132,94,0.14)]",
icon: Smartphone,
},
{
href: "/activity",
title: "陪伴动态",
text: "了解长辈最近的使用情况和聊天记录,安心守护。",
tone: "bg-[rgba(199,103,51,0.12)]",
icon: Activity,
},
{
href: "/scan",
title: "扫码绑定",
text: "扫描长辈手机上的二维码,一步完成设备连接。",
tone: "bg-[rgba(36,27,20,0.08)]",
icon: ScanLine,
},
].map((item) => {
const Icon = item.icon;
return (
<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 items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold text-[var(--ink)] ${item.tone}`}>
<Icon size={14} />
{item.title}
</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)]"> </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)]">
使
</p>
<div className="mt-6 flex justify-center gap-3">
<Link
href="/scan"
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="/devices"
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>
);
}