全新UI语言

This commit is contained in:
2026-07-11 23:37:48 +08:00
parent 4dde861c99
commit 386a28709f
19 changed files with 1032 additions and 1931 deletions
+32 -204
View File
@@ -4,224 +4,52 @@ import { notFound } from "next/navigation";
import { ManageDeviceForm, SendMessageForm } from "@/components/dashboard-actions";
import { PanelShell } from "@/components/panel-shell";
import { getCaregiverDeviceDetail } from "@/lib/caregiver-panel";
import {
formatDateTime,
getConversationRoleLabel,
getDeviceName,
getImportanceLabel,
getMessageHref,
getUsageLabel,
truncateText,
} from "@/lib/panel-format";
import { formatDateTime, getConversationRoleLabel, getDeviceName, getMessageHref, getUsageLabel, truncateText } from "@/lib/panel-format";
import { requireCaregiverSession } from "@/lib/page-auth";
import { getCaregiverDisplayName } from "@/lib/session";
export const dynamic = "force-dynamic";
type DeviceDetailPageProps = {
params: Promise<{ deviceUuid: string }>;
};
export default async function DeviceDetailPage({ params }: DeviceDetailPageProps) {
export default async function DeviceDetailPage({ params, searchParams }: PageProps<"/devices/[deviceUuid]">) {
const { deviceUuid } = await params;
const caregiver = await requireCaregiverSession(`/devices/${deviceUuid}`);
const binding = await getCaregiverDeviceDetail(caregiver.id, deviceUuid);
if (!binding) {
notFound();
}
const elderMessages = binding.elderDevice.messages.filter(
(message) => message.direction === "ELDER_TO_FAMILY",
);
const familyMessages = binding.elderDevice.messages.filter(
(message) => message.direction === "FAMILY_TO_ELDER",
);
if (!binding) notFound();
const rawTab = (await searchParams).tab;
const tab = rawTab === "chat" || rawTab === "usage" ? rawTab : "msg";
const messages = binding.elderDevice.messages;
return (
<PanelShell
currentPath="/devices"
title={getDeviceName(binding.elderDevice.displayName)}
description="给长辈写问候、查看留言和最近的使用动态。"
caregiverLabel={getCaregiverDisplayName(caregiver)}
>
<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">
<p className="text-sm text-[var(--muted)]">新留言</p>
<p className="mt-3 font-[family-name:var(--font-display)] text-4xl text-[var(--ink)]">
{binding.elderUnreadCount}
</p>
</div>
<div className="rounded-[28px] border border-white/80 bg-white/88 p-5">
<p className="text-sm text-[var(--muted)]">待查看</p>
<p className="mt-3 font-[family-name:var(--font-display)] text-4xl text-[var(--ink)]">
{binding.familyUnreadCount}
</p>
</div>
<div className="rounded-[28px] border border-white/80 bg-white/88 p-5">
<p className="text-sm text-[var(--muted)]">最近在线</p>
<p className="mt-3 text-lg font-semibold text-[var(--ink)]">
{formatDateTime(binding.elderDevice.lastSeenAt)}
</p>
</div>
<div className="rounded-[28px] border border-white/80 bg-white/88 p-5">
<p className="text-sm text-[var(--muted)]">设备链接</p>
<p className="mt-3 text-sm leading-7 text-[var(--ink)]">
{truncateText(binding.bindUrl, 52)}
</p>
</div>
<PanelShell currentPath="/devices" title={getDeviceName(binding.alias, binding.elderDevice.displayName)} caregiverLabel={getCaregiverDisplayName(caregiver)} backHref="/devices"
actions={<span className="row-meta" style={{ margin: 0 }}><span className="dot online" style={{ margin: 0 }} />{formatDateTime(binding.elderDevice.lastSeenAt)}</span>}>
<section className="stats" style={{ gridTemplateColumns: "repeat(3,1fr)" }}>
<div className="stat accent"><strong>{binding.elderUnreadCount}</strong><span>新留言</span></div>
<div className="stat"><strong>{binding.familyUnreadCount}</strong><span>待长辈听</span></div>
<div className="stat"><strong>{binding.elderDevice._count.conversationTurns}</strong><span>陪伴对话</span></div>
</section>
<section className="grid gap-6 xl:grid-cols-[0.95fr_1.05fr]">
<div className="space-y-6">
<ManageDeviceForm
deviceUuid={binding.elderDevice.deviceUuid}
initialAlias={binding.alias}
deviceLabel={getDeviceName(binding.elderDevice.displayName)}
/>
<SendMessageForm deviceUuid={binding.elderDevice.deviceUuid} />
<SendMessageForm deviceUuid={binding.elderDevice.deviceUuid} />
<div className="segment">
<Link href={`/devices/${deviceUuid}?tab=msg`} className={tab === "msg" ? "active" : ""}>留言</Link>
<Link href={`/devices/${deviceUuid}?tab=chat`} className={tab === "chat" ? "active" : ""}>对话</Link>
<Link href={`/devices/${deviceUuid}?tab=usage`} className={tab === "usage" ? "active" : ""}>使用</Link>
</div>
<section className="rounded-[32px] border border-white/75 bg-white/82 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">
{elderMessages.length === 0 ? (
<p className="rounded-[24px] bg-[var(--paper-soft)] px-5 py-4 text-sm leading-7 text-[var(--muted)]">
还没有收到长辈的留言。
</p>
) : (
elderMessages.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>{getImportanceLabel(message.importance)}</span>
<span>{formatDateTime(message.createdAt)}</span>
</div>
<p className="mt-3 text-sm leading-7 text-[var(--ink)]">
{truncateText(message.content, 112)}
</p>
</Link>
))
)}
</div>
</section>
<section className="rounded-[32px] border border-white/75 bg-white/82 p-6 shadow-[0_26px_60px_rgba(115,76,42,0.12)]">
<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 className="mt-5 space-y-3">
{binding.elderDevice.usageEvents.length === 0 ? (
<p className="rounded-[24px] bg-[var(--paper-soft)] px-5 py-4 text-sm leading-7 text-[var(--muted)]">
暂时还没有使用记录。
</p>
) : (
binding.elderDevice.usageEvents.map((event) => (
<div key={event.id} className="rounded-[24px] bg-[var(--paper-soft)] px-5 py-4">
<div className="flex items-center justify-between gap-3 text-xs text-[var(--muted)]">
<span>{getUsageLabel(event.eventType)}</span>
<span>{formatDateTime(event.createdAt)}</span>
</div>
</div>
))
)}
</div>
</section>
</div>
<div className="space-y-6">
<section className="rounded-[32px] border border-white/75 bg-white/82 p-6 shadow-[0_26px_60px_rgba(115,76,42,0.12)]">
<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 className="mt-5 space-y-3">
{familyMessages.length === 0 ? (
<p className="rounded-[24px] bg-[var(--paper-soft)] px-5 py-4 text-sm leading-7 text-[var(--muted)]">
还没有给这位长辈写过留言。
</p>
) : (
familyMessages.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>{getImportanceLabel(message.importance)}</span>
<span>{formatDateTime(message.createdAt)}</span>
</div>
<p className="mt-3 text-sm leading-7 text-[var(--ink)]">
{truncateText(message.content, 112)}
</p>
</Link>
))
)}
</div>
</section>
<section className="rounded-[32px] border border-white/75 bg-white/82 p-6 shadow-[0_26px_60px_rgba(115,76,42,0.12)]">
<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 className="mt-5 space-y-3">
{binding.elderDevice.conversationTurns.length === 0 ? (
<p className="rounded-[24px] bg-[var(--paper-soft)] px-5 py-4 text-sm leading-7 text-[var(--muted)]">
还没有聊天记录。
</p>
) : (
binding.elderDevice.conversationTurns.map((turn) => (
<div key={turn.id} className="rounded-[24px] bg-[var(--paper-soft)] px-5 py-4">
<div className="flex items-center justify-between gap-3 text-xs text-[var(--muted)]">
<span>{getConversationRoleLabel(turn.role)}</span>
<span>{formatDateTime(turn.createdAt)}</span>
</div>
<p className="mt-3 text-sm leading-7 text-[var(--ink)]">
{truncateText(turn.content, 124)}
</p>
</div>
))
)}
</div>
</section>
</div>
<section className="card">
{tab === "msg" ? (messages.length ? messages.map((message) => (
<Link key={message.id} href={getMessageHref(message.publicId)} className="timeline-item" style={{ display: "block" }}>
<div className="timeline-top" style={{ color: message.direction === "FAMILY_TO_ELDER" ? "var(--accent)" : undefined }}>{message.direction === "FAMILY_TO_ELDER" ? "我发出的问候" : "长辈捎来的话"}<span className="timeline-time">{formatDateTime(message.createdAt)}</span></div>
<p className="timeline-body">{truncateText(message.content, 126)}</p>
</Link>
)) : <div className="empty">还没有留言。</div>) : tab === "chat" ? (binding.elderDevice.conversationTurns.length ? binding.elderDevice.conversationTurns.map((turn) => (
<article key={turn.id} className="timeline-item"><div className="timeline-top">{getConversationRoleLabel(turn.role)}<span className="timeline-time">{formatDateTime(turn.createdAt)}</span></div><p className="timeline-body">{truncateText(turn.content, 150)}</p></article>
)) : <div className="empty">还没有聊天记录。</div>) : (binding.elderDevice.usageEvents.length ? binding.elderDevice.usageEvents.map((event) => (
<article key={event.id} className="timeline-item"><div className="timeline-top">使用记录<span className="timeline-time">{formatDateTime(event.createdAt)}</span></div><p className="timeline-body">{getUsageLabel(event.eventType)}</p></article>
)) : <div className="empty">暂时还没有使用记录。</div>)}
</section>
<details className="device-manage"><summary className="btn block">设备设置</summary><ManageDeviceForm deviceUuid={binding.elderDevice.deviceUuid} initialAlias={binding.alias} deviceLabel={getDeviceName(binding.elderDevice.displayName)} /></details>
</PanelShell>
);
}
}