- Update parent: children/[studentId]/page, elective/page, leave/page - Update student: error-book/student-error-book-list-client - Update teacher: classes/schedule/schedule-filters, classes/students/students-filters - Update src/app/globals.css
119 lines
4.0 KiB
TypeScript
119 lines
4.0 KiB
TypeScript
import Link from "next/link"
|
||
import { getTranslations } from "next-intl/server"
|
||
import { ArrowLeft, CalendarDays } from "lucide-react"
|
||
|
||
import { getAuthContext } from "@/shared/lib/auth-guard"
|
||
import { Button } from "@/shared/components/ui/button"
|
||
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
|
||
import { getChildrenAction, getChildBasicInfoAction } from "@/modules/parent/actions"
|
||
import { getLeaveRequests } from "@/modules/leave-requests/data-access"
|
||
import {
|
||
LeaveRequestForm,
|
||
type ChildOption,
|
||
} from "@/modules/leave-requests/components/leave-request-form"
|
||
import { LeaveRequestList } from "@/modules/leave-requests/components/leave-request-list"
|
||
|
||
export const dynamic = "force-dynamic"
|
||
|
||
/**
|
||
* 家长在线请假页面。
|
||
*
|
||
* L-5 功能:
|
||
* - 顶部:请假申请表单(下拉选择子女,自动写入 classId)
|
||
* - 底部:该家长所有子女的请假申请列表(按 dataScope=children 过滤)
|
||
*/
|
||
export default async function ParentLeavePage() {
|
||
const t = await getTranslations("leave")
|
||
const ctx = await getAuthContext()
|
||
|
||
if (ctx.dataScope.type !== "children" || ctx.dataScope.childrenIds.length === 0) {
|
||
return (
|
||
<div className="p-6 md:p-8 space-y-6">
|
||
<div className="space-y-1">
|
||
<h1 className="text-2xl font-bold tracking-tight">{t("title.parent")}</h1>
|
||
<p className="text-sm text-muted-foreground">{t("description.parent")}</p>
|
||
</div>
|
||
<Card>
|
||
<CardContent className="py-10 text-center text-sm text-muted-foreground">
|
||
{t("empty.parentDesc")}
|
||
</CardContent>
|
||
</Card>
|
||
<Button asChild variant="ghost" size="sm" className="gap-2 -ml-2">
|
||
<Link href="/parent/dashboard">{t("backToDashboard")}</Link>
|
||
</Button>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
// 并行:子女关系列表 + 已有请假申请列表
|
||
// 审计 v1 P0 修复(G4-002):改调 Server Action,统一权限校验入口
|
||
const [relations, leaveResult] = await Promise.all([
|
||
getChildrenAction(),
|
||
getLeaveRequests({
|
||
scope: ctx.dataScope,
|
||
currentUserId: ctx.userId,
|
||
page: 1,
|
||
pageSize: 50,
|
||
}),
|
||
])
|
||
|
||
// 构造子女选项(含 activeClass),供表单下拉使用
|
||
const childOptions: ChildOption[] = []
|
||
for (const r of relations) {
|
||
const basic = await getChildBasicInfoAction(r.studentId, r.relation)
|
||
if (basic && basic.classId && basic.className) {
|
||
childOptions.push({
|
||
id: basic.id,
|
||
name: basic.name ?? "Unknown",
|
||
classId: basic.classId,
|
||
className: basic.className,
|
||
})
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="p-6 md:p-8 space-y-6">
|
||
<div className="space-y-1">
|
||
<h1 className="text-2xl font-bold tracking-tight">{t("title.parent")}</h1>
|
||
<p className="text-sm text-muted-foreground">{t("description.parent")}</p>
|
||
</div>
|
||
|
||
<Button asChild variant="ghost" size="sm" className="gap-2 -ml-2">
|
||
<Link href="/parent/dashboard" aria-label={t("backToDashboard")}>
|
||
<ArrowLeft className="h-4 w-4" />
|
||
{t("backToDashboard")}
|
||
</Link>
|
||
</Button>
|
||
|
||
{childOptions.length > 0 ? (
|
||
<Card>
|
||
<CardHeader>
|
||
<CardTitle className="flex items-center gap-2 text-base">
|
||
<CalendarDays className="h-4 w-4 text-muted-foreground" aria-hidden />
|
||
{t("onlineLeave")}
|
||
</CardTitle>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<LeaveRequestForm childOptions={childOptions} />
|
||
</CardContent>
|
||
</Card>
|
||
) : (
|
||
<Card>
|
||
<CardContent className="py-10 text-center text-sm text-muted-foreground">
|
||
{t("empty.parentDesc")}
|
||
</CardContent>
|
||
</Card>
|
||
)}
|
||
|
||
<div className="space-y-3">
|
||
<h2 className="text-lg font-semibold">{t("onlineLeave")}</h2>
|
||
<LeaveRequestList
|
||
items={leaveResult.items}
|
||
emptyTitle={t("empty.parentTitle")}
|
||
emptyDescription={t("empty.parentDesc")}
|
||
/>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|