- Add error.tsx and loading.tsx boundaries for admin, parent, student, teacher routes - Add admin announcements edit, audit-logs overview, curriculum-map, invitation-codes, permissions, questions, roles routes - Add admin elective detail and components, files, course-plans, users, scheduling boundaries - Add messages group-compose route - Add parent course-plans, elective, grades report-card, practice routes - Add student course-plans, elective detail, error-book dialogs, grades report-card, learning study-path, leave, schedule boundaries - Add teacher attendance report, classes boundaries, course-plans boundaries, elective, exams analytics/edit-rich/all/create/new, grades report-card, homework boundaries, leave, lesson-plans calendar - Add auth loading, onboarding loading, api cron
71 lines
2.4 KiB
TypeScript
71 lines
2.4 KiB
TypeScript
import type { Metadata } from "next"
|
||
import { getTranslations } from "next-intl/server"
|
||
|
||
import { requirePermission } from "@/shared/lib/auth-guard"
|
||
import { Permissions } from "@/shared/types/permissions"
|
||
import { getUserAnnouncementsPageData } from "@/modules/announcements/data-access"
|
||
import { AnnouncementList } from "@/modules/announcements/components/announcement-list"
|
||
import { AnnouncementsServiceProvider } from "@/modules/announcements/components/announcements-service-context"
|
||
|
||
export const dynamic = "force-dynamic"
|
||
|
||
export async function generateMetadata(): Promise<Metadata> {
|
||
const t = await getTranslations("announcements")
|
||
return { title: t("title.list"), description: t("description.list") }
|
||
}
|
||
|
||
const PAGE_SIZE = 12
|
||
|
||
export default async function AnnouncementsPage({
|
||
searchParams,
|
||
}: {
|
||
searchParams: Promise<{ page?: string; status?: string }>
|
||
}) {
|
||
const t = await getTranslations("announcements")
|
||
const ctx = await requirePermission(Permissions.ANNOUNCEMENT_READ)
|
||
|
||
const sp = await searchParams
|
||
const requestedPage = Number(sp.page)
|
||
const page = Number.isFinite(requestedPage) && requestedPage > 0 ? Math.floor(requestedPage) : 1
|
||
|
||
const { items, total, page: currentPage, pageSize } = await getUserAnnouncementsPageData(
|
||
ctx.userId,
|
||
ctx.dataScope,
|
||
page,
|
||
PAGE_SIZE
|
||
)
|
||
|
||
// 构建分页 URL:保留 ?status= 过滤参数(与 AnnouncementList 的过滤机制一致)
|
||
const buildPageHref = (targetPage: number): string => {
|
||
const params = new URLSearchParams()
|
||
if (sp.status) params.set("status", sp.status)
|
||
if (targetPage > 1) params.set("page", String(targetPage))
|
||
const qs = params.toString()
|
||
return qs ? `/announcements?${qs}` : "/announcements"
|
||
}
|
||
|
||
return (
|
||
<AnnouncementsServiceProvider>
|
||
<div className="flex h-full flex-col space-y-8 p-8">
|
||
<div>
|
||
<h2 className="text-2xl font-bold tracking-tight">{t("title.list")}</h2>
|
||
<p className="text-muted-foreground">
|
||
{t("description.list")}
|
||
</p>
|
||
</div>
|
||
<AnnouncementList
|
||
announcements={items}
|
||
detailHrefPrefix="/announcements"
|
||
initialStatus={sp.status === "published" || sp.status === "draft" || sp.status === "archived" ? sp.status : "all"}
|
||
pagination={{
|
||
page: currentPage,
|
||
pageSize,
|
||
total,
|
||
buildPageHref,
|
||
}}
|
||
/>
|
||
</div>
|
||
</AnnouncementsServiceProvider>
|
||
)
|
||
}
|