完整性更新
现在已经实现了大部分基础功能
This commit is contained in:
151
src/app/(dashboard)/profile/page.tsx
Normal file
151
src/app/(dashboard)/profile/page.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
import Link from "next/link"
|
||||
import { redirect } from "next/navigation"
|
||||
|
||||
import { auth } from "@/auth"
|
||||
import { getStudentClasses, getStudentSchedule } from "@/modules/classes/data-access"
|
||||
import { StudentGradesCard } from "@/modules/dashboard/components/student-dashboard/student-grades-card"
|
||||
import { StudentRankingCard } from "@/modules/dashboard/components/student-dashboard/student-ranking-card"
|
||||
import { StudentStatsGrid } from "@/modules/dashboard/components/student-dashboard/student-stats-grid"
|
||||
import { StudentTodayScheduleCard } from "@/modules/dashboard/components/student-dashboard/student-today-schedule-card"
|
||||
import { StudentUpcomingAssignmentsCard } from "@/modules/dashboard/components/student-dashboard/student-upcoming-assignments-card"
|
||||
import { getStudentDashboardGrades, getStudentHomeworkAssignments } from "@/modules/homework/data-access"
|
||||
import { Badge } from "@/shared/components/ui/badge"
|
||||
import { Button } from "@/shared/components/ui/button"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/shared/components/ui/card"
|
||||
|
||||
export const dynamic = "force-dynamic"
|
||||
|
||||
const toWeekday = (d: Date): 1 | 2 | 3 | 4 | 5 | 6 | 7 => {
|
||||
const day = d.getDay()
|
||||
return (day === 0 ? 7 : day) as 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
}
|
||||
|
||||
export default async function ProfilePage() {
|
||||
const session = await auth()
|
||||
if (!session?.user) redirect("/login")
|
||||
|
||||
const name = session.user.name ?? "User"
|
||||
const email = session.user.email ?? "-"
|
||||
const role = String(session.user.role ?? "teacher")
|
||||
const userId = String(session.user.id ?? "").trim()
|
||||
|
||||
const studentData =
|
||||
role === "student" && userId
|
||||
? await (async () => {
|
||||
const [classes, schedule, assignmentsAll, grades] = await Promise.all([
|
||||
getStudentClasses(userId),
|
||||
getStudentSchedule(userId),
|
||||
getStudentHomeworkAssignments(userId),
|
||||
getStudentDashboardGrades(userId),
|
||||
])
|
||||
|
||||
const now = new Date()
|
||||
const in7Days = new Date(now)
|
||||
in7Days.setDate(in7Days.getDate() + 7)
|
||||
|
||||
const dueSoonCount = assignmentsAll.filter((a) => {
|
||||
if (!a.dueAt) return false
|
||||
const due = new Date(a.dueAt)
|
||||
return due >= now && due <= in7Days && a.progressStatus !== "graded"
|
||||
}).length
|
||||
|
||||
const overdueCount = assignmentsAll.filter((a) => {
|
||||
if (!a.dueAt) return false
|
||||
const due = new Date(a.dueAt)
|
||||
return due < now && a.progressStatus !== "graded"
|
||||
}).length
|
||||
|
||||
const gradedCount = assignmentsAll.filter((a) => a.progressStatus === "graded").length
|
||||
|
||||
const upcomingAssignments = [...assignmentsAll]
|
||||
.sort((a, b) => {
|
||||
const aTime = a.dueAt ? new Date(a.dueAt).getTime() : Number.POSITIVE_INFINITY
|
||||
const bTime = b.dueAt ? new Date(b.dueAt).getTime() : Number.POSITIVE_INFINITY
|
||||
if (aTime !== bTime) return aTime - bTime
|
||||
return a.id.localeCompare(b.id)
|
||||
})
|
||||
.slice(0, 8)
|
||||
|
||||
const todayWeekday = toWeekday(now)
|
||||
const todayScheduleItems = schedule
|
||||
.filter((s) => s.weekday === todayWeekday)
|
||||
.map((s) => ({
|
||||
id: s.id,
|
||||
classId: s.classId,
|
||||
className: s.className,
|
||||
course: s.course,
|
||||
startTime: s.startTime,
|
||||
endTime: s.endTime,
|
||||
location: s.location ?? null,
|
||||
}))
|
||||
|
||||
return {
|
||||
enrolledClassCount: classes.length,
|
||||
dueSoonCount,
|
||||
overdueCount,
|
||||
gradedCount,
|
||||
todayScheduleItems,
|
||||
upcomingAssignments,
|
||||
grades,
|
||||
}
|
||||
})()
|
||||
: null
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col gap-8 p-8">
|
||||
<div className="flex flex-col justify-between gap-4 md:flex-row md:items-center">
|
||||
<div className="space-y-1">
|
||||
<h1 className="text-3xl font-bold tracking-tight">Profile</h1>
|
||||
<div className="text-sm text-muted-foreground">Your account information.</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button asChild variant="outline">
|
||||
<Link href="/settings">Open settings</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Account</CardTitle>
|
||||
<CardDescription>Signed-in user details from session.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="text-sm font-medium">{name}</div>
|
||||
<Badge variant="secondary" className="capitalize">
|
||||
{role}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">{email}</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{studentData ? (
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-1">
|
||||
<h2 className="text-xl font-semibold tracking-tight">Student</h2>
|
||||
<div className="text-sm text-muted-foreground">Your learning overview.</div>
|
||||
</div>
|
||||
|
||||
<StudentStatsGrid
|
||||
enrolledClassCount={studentData.enrolledClassCount}
|
||||
dueSoonCount={studentData.dueSoonCount}
|
||||
overdueCount={studentData.overdueCount}
|
||||
gradedCount={studentData.gradedCount}
|
||||
/>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<StudentGradesCard grades={studentData.grades} />
|
||||
<StudentRankingCard ranking={studentData.grades.ranking} />
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-7">
|
||||
<StudentTodayScheduleCard items={studentData.todayScheduleItems} />
|
||||
<StudentUpcomingAssignmentsCard upcomingAssignments={studentData.upcomingAssignments} />
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user