Frontend Overview
Lumie's frontend is a Next.js 16 App Router application that serves the public site, authentication flows, tenant onboarding, the staff admin workspace, and the student dashboard. Routing lives in app/, product logic lives in src/, and browser requests go through the frontend's own /api proxy instead of calling the backend directly from the browser.
What This Repo Owns
- Public marketing and academy landing pages
- Login, owner signup, student self-registration, and session refresh flows
- Owner onboarding after registration
- Admin-facing academy operations pages under
/admin - Admin OMR grading, grade-management tables, and result correction actions
- Student-facing pages under
/dashboard, including assignment list and submission flows - Frontend-side proxying, auth-cookie handling, and tenant header propagation
Runtime Shape
In local development, the frontend runs on http://localhost:3000 while the backend and stateful services run in the dev cluster. Browser code calls /api/v1/..., and app/api/[...path]/route.ts forwards those requests to NEXT_PUBLIC_API_BASE, rewrites response headers for localhost cookies, and attaches tenant context when custom-domain resolution is enabled.
The root layout in app/layout.tsx wires up:
QueryProviderfor TanStack QueryAuthModalUrlSyncplus the shared auth modal- the global
Toaster - local Noto Sans KR fonts and the document shell
Main Implementation Conventions
| Area | Current approach |
|---|---|
| Routing | Next.js App Router with route groups and nested layouts |
| Application structure | Feature-Sliced Design under src/ |
| Server state | TanStack Query |
| Client UI state | Zustand and local component state |
| Forms | React Hook Form with Zod validation |
| API clients | Shared fetch layer plus Orval-generated hooks and query helpers |
| E2E tests | Playwright |
| Unit tests | Vitest with jsdom |
Code Map
| Path | Responsibility |
|---|---|
app/ | Routes, layouts, loading states, error boundaries, and route handlers |
src/shared/ | API primitives, config, generic UI, providers, and utilities |
src/entities/ | Domain models, generated API hooks, and entity-level helpers |
src/features/ | User actions and feature-specific UI or state |
src/widgets/ | Composed screens and shells such as headers, sidebars, and auth modal |
components/ui/ | Forked shadcn primitives kept at the repo root by convention |
Source Paths
| Claim | Source path |
|---|---|
Same-origin /api proxy and cookie rewriting | lumie-frontend/app/api/[...path]/route.ts |
Browser API base is /api | lumie-frontend/src/shared/config/env.ts |
| Root providers and global shell | lumie-frontend/app/layout.tsx |
| TanStack Query provider | lumie-frontend/src/shared/providers/QueryProvider.tsx |
| Feature-Sliced Design aliases | lumie-frontend/tsconfig.json |
| Student assignment routes | lumie-frontend/app/dashboard/assignments/page.tsx, lumie-frontend/app/dashboard/assignments/[id]/page.tsx |
| Student assignment API hooks | lumie-frontend/src/entities/assignment/api/student-queries.ts |
| Student assignment feature UI | lumie-frontend/src/features/assignment-management/list-student-assignments/, lumie-frontend/src/features/assignment-management/view-student-assignment/ |
| OMR result phone correction dialog | lumie-frontend/src/entities/exam/ui/ExamResultPhoneEditDialog.tsx |
| OMR completion modal phone-correction entry point | lumie-frontend/src/features/grade-omr/track-omr-grading/ui/OmrResultPreview.tsx |
| Student-grade table registration entry point | lumie-frontend/src/features/grade-management/view-grade-detail/ui/StudentGradeTable.tsx |
Admin OMR Result Correction
When OMR recognition reads a phone number incorrectly, staff can correct the phone from the OMR completion modal. The shared dialog validates the phone number with React Hook Form and Zod, then calls the exam result phone-correction mutation. If the corrected phone matches an active registered student, the backend response returns the result as registered and the frontend invalidates the exam-result, student-grade, student-result, statistics, and OMR-job caches affected by the correction.
The grade table exposes student registration for unregistered result rows. If
staff changes the prefilled OMR phone while registering a student from that
row, StudentGradeTable compares the created student's normalized phone with
the original row phone and calls the same result-phone correction mutation for
that resultId before invalidating the grade and statistics queries. Deleted
student rows are blocked from opening grade detail in the table click handler,
but this page does not promise broader "view-only" behavior beyond that check.
Verification
cd lumie-frontend
rg -n "API_URL: '/api'|app/api/\\[\\.\\.\\.path\\]|QueryProvider|@/shared" \
app src tsconfig.json
rg -n "dashboard/assignments|useStudentAssignments|useSubmitStudentAssignment" \
app/dashboard src/entities/assignment src/features/assignment-management src/widgets/student-sidebar
rg -n "ExamResultPhoneEditDialog|useUpdateExamResultPhone|RegisterStudentModal" \
src/entities/exam src/entities/student src/features/grade-omr src/features/grade-management
npm run lint
Success means the grep finds the proxy, root provider, FSD alias surfaces,
student assignment routes, query hooks, sidebar entry, and OMR result correction
entry points, and npm run lint completes with the repo's current ESLint plus
Lumie-lint sweep.