Skip to content

Commit 8365085

Browse files
authored
feat: scores viewer, auth hardening, perf optimisations, and infra upgrades (#469)
feat(scores): add Scores page with exam/assessment viewer New protected route /scores showing all exams and assessments grouped by course. - ScoresClient: stats strip (total / scored / pending / avg%), filter tabs (all / assessments / assignments), course-grouped cards, animated detail drawer, resolved score logic (resolvedScore > pivot.score), maxMark fallback, ungraded "Pending" state, keyboard-accessible close (Esc / autoFocus) - useExams / useAllExamAnswers / useAllExamQuestions hooks with TanStack Query (staleTime 5 min) - exam.d.ts: Exam, ExamAnswer, ExamQuestion, ExamParticipant types - error.tsx boundary scoped to /scores - robots.ts + sitemap.ts updated to include /scores - 48 unit tests covering all states and edge cases - OpenAPI spec updated with /scores entry fix(auth): harden session expiry and logout flow - lib/security/auth.ts: handleLogout() clears both EzyGo (ezygo_access_token) and Supabase session; eliminates bare router.replace("/") calls that left HttpOnly cookies intact - protected layout: missing session/user → await handleLogout() instead of bare redirect; auth session missing errors similarly routed through handleLogout() - login-form.tsx: improved post-login redirect handling perf: reduce unnecessary network requests across protected pages - user-settings provider: staleTime 30 s → 5 min, gcTime → 30 min, refetchOnWindowFocus/Interval disabled; cache invalidated only on SIGNED_IN (not TOKEN_REFRESHED) - useNotifications: add countOnly param — when true, only the HEAD-only unread-count query runs; action-conflict and infinite-feed queries (both with 30 s polling) are skipped - private-navbar: useNotifications(true, true) — eliminates two wasted Supabase connections + 30 s polling on every protected page - protected layout: institution loading/error no longer gates page render; useInstitutions() still called for cache pre-warming - DashboardClient ChartSkeleton: <Loading minimal /> instead of full- viewport <Loading /> inside chart card containers - GET /api/profile: fast path — DB row exists → return immediately (avatar renders at once); EzyGo sync deferred to after() background task feat(ui): Loading minimal prop for compact inline spinner - loading.tsx: minimal mode uses py-8 compact container, hides ghost message and timeout buttons; full mode unchanged - sr-only label updated to "Loading, please wait..." feat(http): axios interceptors and circuit-breaker integration - lib/axios.ts: request-signing interceptor, response error normalisation, circuit-breaker wrapping for EzyGo API calls refactor(accept-terms): extract AcceptTermsClient component - Accept-terms page split into server page.tsx + client AcceptTermsClient.tsx for better RSC boundary separation - Metadata and OG tags improved chore(infra): bump npm pin in Dockerfile to 11.10.1 - Updated tarball URL and SHA-256 in base layer test: fix stale assertions, lint errors, add 48 new tests - ScoresClient.test.tsx: 48 tests — loading/error/empty states, stats strip, course grouping, filter tabs, score display, drawer, a11y - exams.test.tsx: unit tests for useExams and related query hooks - profile route.test.ts: after() mocked via next/server mock - robots/sitemap tests updated for new /scores route - Fixed: unused waitFor/DeepPartial/sectionHeaders imports, stale jsx-a11y/no-autofocus disable comment, missing activity_name_id: null in makeExam fixture, unused mockPush in vi.hoisted()
1 parent 749105a commit 8365085

36 files changed

Lines changed: 3120 additions & 469 deletions

.example.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ NEXT_PUBLIC_APP_NAME=GhostClass
4141
# (calculate-version job). A GitHub Secret here would always be stale after
4242
# an auto-version bump. Keep in sync with package.json for local dev only.
4343
# 🔨 Build-time (auto-derived from git tag by pipeline — not a GitHub Secret)
44-
NEXT_PUBLIC_APP_VERSION=1.9.2
44+
NEXT_PUBLIC_APP_VERSION=1.9.3
4545

4646
# ⚠️ Your production domain WITHOUT https://
4747
# All URL-based variables are derived from this.

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ FROM ${NODE_IMAGE} AS base
1414
# so overwriting that directory via tar achieves the same result with no unpinned npm invocation.
1515
# The tarball is verified by SHA-256 before extraction.
1616
RUN apk add --no-cache wget && \
17-
wget -O /tmp/npm.tgz https://registry.npmjs.org/npm/-/npm-11.10.0.tgz && \
18-
echo "43c653384c39617756846ad405705061a78fb6bbddb2ced57ab79fb92e8af2a7 /tmp/npm.tgz" | sha256sum -c - && \
17+
wget -O /tmp/npm.tgz https://registry.npmjs.org/npm/-/npm-11.10.1.tgz && \
18+
echo "2190945151842685142f5085b3c5dd356b1021ab390d7d02c2bb2c580f0c4840 /tmp/npm.tgz" | sha256sum -c - && \
1919
rm -rf /usr/local/lib/node_modules/npm && \
2020
mkdir -p /usr/local/lib/node_modules/npm && \
2121
tar -xz --strip-components=1 -C /usr/local/lib/node_modules/npm -f /tmp/npm.tgz && \

README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ GhostClass is the ultimate academic survival tool for students who want to manag
2424
- **The Bunk Calc** 🧮: Know exactly how many classes you can miss before the threshold comes for your neck.
2525
- **Visual Receipts** 📊: Performance charts and a detailed calendar history so you can see your attendance glow-up in real-time.
2626
- **Anti-Ghosting Tracker** 👻: A personalized list to watch wrongly marked absences like a hawk until they get updated.
27+
- **Scores Viewer** 📋: Browse all your exam and assignment results grouped by course, with a stats summary and a per-question breakdown drawer showing individual answer scores and max marks.
2728
- **Ezygo Integration** 🔄 - Use your existing ezygo credentials - no new accounts needed
2829
- **Multi-Device Support** 🔐 - Login from multiple devices simultaneously without losing sessions
2930
- **Real-time Updates** ⚡ - Get instant updates on your attendance status and skip calculations
@@ -97,10 +98,11 @@ src/
9798
├── sw.ts # Service worker with runtime caching
9899
├── app/ # Next.js app router pages and layouts
99100
│ ├── (auth)/ # Authentication routes (login, signup)
100-
│ ├── (protected)/ # Login-restricted routes (dashboard, profile, tracking)
101+
│ ├── (protected)/ # Login-restricted routes (dashboard, profile, tracking, scores)
101102
│ │ ├── dashboard/ # Main dashboard with attendance overview
102103
│ │ ├── profile/ # User profile and settings
103104
│ │ ├── tracking/ # Manual attendance tracking interface
105+
│ │ ├── scores/ # Exam & assignment scores viewer with per-question breakdown drawer
104106
│ │ └── notifications/ # Notification center
105107
│ ├── (public)/ # Public routes (home, contact, legal, build-info, help)
106108
│ │ ├── build-info/ # Build provenance and transparency page
@@ -133,6 +135,8 @@ src/
133135
│ │ ├── attendance-chart.tsx # Performance charts
134136
│ │ ├── AddAttendanceDialog.tsx # Dialog for adding manual attendance records
135137
│ │ └── AddRecordTrigger.tsx # Trigger button for the add-record dialog
138+
│ │ # Scores UI is colocated in app/(protected)/scores/ScoresClient.tsx
139+
│ │ # (ScoreCard + ExamDetailDrawer sub-components inline in that file)
136140
│ ├── layout/ # Layout components (navbar, footer, sidebar)
137141
│ ├── legal/ # Legal content components
138142
│ ├── user/ # User-related components
@@ -152,7 +156,10 @@ src/
152156
│ ├── react-query.tsx # TanStack Query provider
153157
│ └── user-settings.ts # User settings context
154158
├── hooks/ # Custom React hooks
155-
│ ├── courses/ # Course data fetching hooks
159+
│ ├── courses/ # Course and exam data fetching hooks
160+
│ │ │ # courses.ts — attendance/course queries
161+
│ │ │ # exams.ts — useExams, useExamAnswers, useExamQuestions,
162+
│ │ │ # useAllExamAnswers, useAllExamQuestions
156163
│ ├── tracker/ # Tracking data hooks
157164
│ ├── users/ # User data hooks
158165
│ ├── notifications/ # Notification subscription hooks
@@ -499,11 +506,16 @@ GhostClass uses **Vitest** for unit/component tests and **Playwright** for E2E t
499506
500507
```text
501508
src/
509+
├── app/(protected)/scores/__tests__/
510+
│ └── ScoresClient.test.tsx # Scores page component tests (loading, error, stats, drawer)
502511
├── components/__tests__/
503512
│ └── error-boundary.test.tsx # Error boundary component tests
504513
├── hooks/
505514
│ ├── __tests__/useUser.test.tsx # User hook tests
506-
│ └── courses/__tests__/courses.test.tsx # Course hook tests
515+
│ └── courses/__tests__/
516+
│ ├── courses.test.tsx # Course hook tests
517+
│ └── exams.test.tsx # Exam data hook tests (useExams, useExamAnswers, useExamQuestions,
518+
│ # useAllExamAnswers, useAllExamQuestions)
507519
└── lib/
508520
├── __tests__/
509521
│ ├── utils.test.ts # Utility function tests
@@ -544,6 +556,8 @@ Current test suite includes:
544556
- ✅ **Utility Functions** (`utils.test.ts`) - Helper function validation
545557
- ✅ **Error Boundaries** (`error-boundary.test.tsx`) - Error handling UI
546558
- ✅ **Custom Hooks** - User and course data fetching
559+
- ✅ **Exam Data Hooks** (`exams.test.tsx`) - All 5 exam hooks: fetching, parallel queries, error isolation, stale-time config
560+
- ✅ **Scores Page** (`ScoresClient.test.tsx`) - Loading/error/empty states, stats strip, course grouping, score display, visibility rules, per-question drawer, accessibility (ARIA roles, focus management)
547561
- ✅ **E2E Smoke Tests** - Critical user flows
548562
549563
**Coverage Goals:**

0 commit comments

Comments
 (0)