Skip to content

v1.9.3#469

Merged
devakesu merged 1 commit intomainfrom
1.9.3
Feb 23, 2026
Merged

v1.9.3#469
devakesu merged 1 commit intomainfrom
1.9.3

Conversation

@devakesu
Copy link
Copy Markdown
Owner

Pull Request

Description

feat: scores viewer, auth hardening, perf optimisations, and infra upgrades

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: instead of full- viewport 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()

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test updates

Version Bump

  • Version automatically bumped by workflow (same-repo PRs)
  • Version manually bumped using node scripts/bump-version.js (fork PRs)
  • Version already up-to-date (no bump needed)

…grades

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()
Copilot AI review requested due to automatic review settings February 23, 2026 14:00
@github-actions
Copy link
Copy Markdown
Contributor

Version already bumped to v1.9.3

No automatic version bump needed - the PR already includes a version update.

This PR is ready for review! 🚀

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a comprehensive scores viewer feature alongside significant auth hardening, performance optimizations, and infrastructure upgrades. The changes include a new protected /scores route with exam/assignment viewing capabilities, performance improvements through reduced network requests, enhanced auth session handling, automatic CSRF token refresh, and various infrastructure updates.

Changes:

  • New scores viewer with course-grouped cards, stats strip, filter tabs, per-question breakdown drawer, and accessibility features
  • Performance optimizations: eliminated redundant auth.getUser() calls in user-settings provider, added countOnly param to useNotifications, implemented fast-path response in profile API with background sync
  • Auth hardening: handleLogout now clears both EzyGo and Supabase sessions, protected layout uses handleLogout instead of bare redirects, added isSupabaseLockTimeoutError helper
  • CSRF token auto-refresh in axios interceptors with singleton deduplication
  • Infrastructure: npm 11.10.1 bump in Dockerfile, dependency updates in package-lock.json

Reviewed changes

Copilot reviewed 34 out of 36 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/types/exam.d.ts New TypeScript types for Exam, ExamAnswer, ExamQuestion, ExamParticipant, ExamSettings, ExamCourse
src/hooks/courses/exams.ts New hooks: useExams, useExamAnswers, useExamQuestions, useAllExamAnswers, useAllExamQuestions with proper staleTime/gcTime configs
src/hooks/courses/__tests__/exams.test.tsx Comprehensive unit tests for all exam hooks (48 tests)
src/app/(protected)/scores/ScoresClient.tsx Main scores component with ScoreCard, ExamDetailDrawer sub-components, stats strip, filter tabs, course grouping
src/app/(protected)/scores/__tests__/ScoresClient.test.tsx 48 tests covering loading/error/empty states, stats, filters, drawer, accessibility
src/app/(protected)/scores/page.tsx Server page with metadata and dynamic rendering config
src/app/(protected)/scores/error.tsx Error boundary scoped to scores route
src/providers/user-settings.ts Eliminated redundant getUser() calls, increased staleTime to 5 min, disabled refetchOnWindowFocus/Interval, invalidate only on SIGNED_IN
src/hooks/notifications/useNotifications.ts Added countOnly param to skip action-conflict and feed queries when only badge count needed
src/lib/security/auth.ts Added isSupabaseLockTimeoutError helper for browser lock manager timeouts
src/lib/axios.ts Added CSRF token auto-refresh interceptor with singleton promise deduplication and single-retry logic
src/components/user/login-form.tsx Use getSession instead of getUser, handle lock timeout errors
src/app/api/profile/route.ts Fast-path response for existing users with background EzyGo sync via after()
src/app/(protected)/layout.tsx Use handleLogout instead of bare router.replace, removed institution loading gate
src/app/(protected)/dashboard/page.tsx Updated robots metadata to noindex/nofollow
src/app/(protected)/dashboard/DashboardClient.tsx Use minimal loading spinner in charts, updated welcome message
src/components/loading.tsx Added minimal prop for compact inline spinner
src/components/layout/private-navbar.tsx Use useNotifications(true, true) for count-only, added Scores nav button
src/proxy.ts Added scores route to protected routes list
src/app/sitemap.ts Added /build-info entry
src/app/robots.ts Added /scores to disallow list
src/app/accept-terms/page.tsx Refactored to RSC with metadata
src/app/accept-terms/AcceptTermsClient.tsx Extracted client component
src/app/(auth)/page.tsx Added metadata with robots noindex/nofollow
src/app/(public)/build-info/page.tsx Added metadata with robots index/follow
Dockerfile Bumped npm to 11.10.1 with SHA-256 verification
package.json / package-lock.json Version bump to 1.9.3, dependency updates
public/api-docs/openapi.yaml Version bump to 1.9.3
.example.env Updated NEXT_PUBLIC_APP_VERSION to 1.9.3
README.md Added scores viewer documentation

@devakesu devakesu merged commit 8365085 into main Feb 23, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants