69 lines
2.2 KiB
TypeScript
69 lines
2.2 KiB
TypeScript
import Link from "next/link";
|
|
import { notFound } from "next/navigation";
|
|
import { ArrowLeft, Bell } from "lucide-react";
|
|
|
|
import { AnnouncementReadMarker } from "@/components/announcement-read-marker";
|
|
import { PageHeader } from "@/components/page-header";
|
|
import { StatusPill } from "@/components/status-pill";
|
|
import { BackendError } from "@/lib/backend";
|
|
import { formatDateTime } from "@/lib/format";
|
|
import { getAnnouncementDetail } from "@/lib/mobile-data";
|
|
import type { AnnouncementSummary } from "@/lib/types";
|
|
|
|
type AnnouncementDetailPageProps = {
|
|
params: Promise<{ id: string }>;
|
|
};
|
|
|
|
const levelText: Record<AnnouncementSummary["level"], string> = {
|
|
normal: "普通",
|
|
important: "重要",
|
|
urgent: "紧急"
|
|
};
|
|
|
|
const levelTone: Record<AnnouncementSummary["level"], "default" | "warning" | "danger"> = {
|
|
normal: "default",
|
|
important: "warning",
|
|
urgent: "danger"
|
|
};
|
|
|
|
export default async function AnnouncementDetailPage({ params }: AnnouncementDetailPageProps) {
|
|
const { id } = await params;
|
|
let announcement: Awaited<ReturnType<typeof getAnnouncementDetail>>;
|
|
|
|
try {
|
|
announcement = await getAnnouncementDetail(id);
|
|
} catch (error) {
|
|
if (error instanceof BackendError && error.status === 404) {
|
|
notFound();
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
|
|
return (
|
|
<div className="page-stack">
|
|
<Link className="inline-link" href="/announcements">
|
|
<ArrowLeft aria-hidden size={16} />
|
|
返回公告
|
|
</Link>
|
|
<PageHeader
|
|
eyebrow={formatDateTime(announcement.publishedAt)}
|
|
title={announcement.title}
|
|
description={announcement.summary}
|
|
/>
|
|
<AnnouncementReadMarker id={announcement.id} read={announcement.read} />
|
|
<section className="detail-card">
|
|
<div className="detail-meta">
|
|
<span>
|
|
<Bell aria-hidden size={16} />
|
|
公告级别
|
|
</span>
|
|
<StatusPill tone={levelTone[announcement.level]}>{levelText[announcement.level]}</StatusPill>
|
|
<StatusPill tone={announcement.read ? "default" : "warning"}>{announcement.read ? "已读" : "未读"}</StatusPill>
|
|
</div>
|
|
<article className="long-copy">{announcement.content}</article>
|
|
</section>
|
|
</div>
|
|
);
|
|
}
|