feat(portal-shell): wire dashboards to real data-ana queries (P1-2)

- add dashboard.graphql.ts with 6 real aggregate queries
  (teacherDashboard / studentDashboard / parentDashboard /
   adminDashboard / warnings / errorBookStats), snake_case aligned
- add dashboard.ts with 6 hooks + full domain model types
- add 4 role dashboard pages (teacher/student/parent/admin)
  using DashboardShell + StatCard + DashboardSection with
  loading / error / success tri-state
- update [[...route]]/page.tsx to redirect /shell -> /shell/{role}
- retire 6 fake contract queries and hooks (grades/homeworks/
  schedule/attendance/exams/announcements) and mark widget
  placeholders as migrated
- update universal.test.ts to drop retired hook tests
- mark ARCHITECTURE.md P1-2 as completed with acceptance evidence
This commit is contained in:
SpecialX
2026-07-22 12:33:22 +08:00
parent f92fdf8efe
commit 98058eb16b
18 changed files with 1086 additions and 846 deletions

View File

@@ -1,69 +1,16 @@
// Universal domain GraphQL documents
// Extracted from widgets/universal/*/index.tsx
// Related: spec section 2.2
//
// P1-2ARCHITECTURE.md §106 个假契约查询已下线grades/homeworks/
// schedule/attendance/exams/announcements root 字段在后端 schema 不存在)。
// 仪表盘改用 data-ana 的真实聚合查询dashboard.graphql.ts
// 仅保留 notifications查询真实字段 notifications(userId),形状待 P1-7 修正)。
import { gql } from "@apollo/client";
// From widgets/universal/grades-widget
export const GET_GRADES_DOC = gql`
query GetGrades($classId: ID!) {
grades(classId: $classId) {
studentId
score
}
}
`;
// From widgets/universal/homework-widget
export const GET_HOMEWORKS_DOC = gql`
query GetHomeworks($classId: ID!, $limit: Int) {
homeworks(classId: $classId, limit: $limit) {
id
title
dueDate
status
}
}
`;
// From widgets/universal/schedule-widget
export const GET_SCHEDULE_DOC = gql`
query GetSchedule($classId: ID!, $dayOfWeek: Int) {
schedule(classId: $classId, dayOfWeek: $dayOfWeek) {
id
subject
startTime
endTime
teacherName
}
}
`;
// From widgets/universal/attendance-widget
export const GET_ATTENDANCE_DOC = gql`
query GetAttendance($classId: ID!, $termId: ID!) {
attendance(classId: $classId, termId: $termId) {
present
absent
late
total
}
}
`;
// From widgets/universal/exams-widget
export const GET_EXAMS_DOC = gql`
query GetExams($classId: ID!, $limit: Int) {
exams(classId: $classId, limit: $limit) {
id
name
examDate
subject
maxScore
}
}
`;
// From widgets/universal/notifications-widget
// 注意notifications(userId) 返回平铺数组,非 {items, total} 包装。
// 形状修正待 P1-7codegen 恢复后统一处理)。
export const GET_NOTIFICATIONS_LIST_DOC = gql`
query GetNotificationsList($limit: Int, $offset: Int) {
notifications(limit: $limit, offset: $offset) {
@@ -78,16 +25,3 @@ export const GET_NOTIFICATIONS_LIST_DOC = gql`
}
}
`;
// From widgets/universal/announcements-widget
export const GET_ANNOUNCEMENTS_DOC = gql`
query GetAnnouncements($limit: Int) {
announcements(limit: $limit) {
id
title
body
author
publishedAt
}
}
`;