Gradebook: classes, weighted grading, assignments, grades, student role

- Migration: student role, students.user_id, grade_categories/grade_scale
  (school defaults + per-class override), assignments, grades + RLS
- Classes area (nav): list/create classes, class page with Gradebook / Roster /
  Grading setup tabs; teachers see their classes, admins all
- Weighted-category grade calc -> letter scale (shared helper)
- Roster assignment (admin), assignment + grade entry (teacher/admin)
- Student profile: view-only Grades tab (parents/students)
- Portal access: create parent OR student logins linked to the student

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 16:57:27 -04:00
co-authored by Claude Opus 4.8
parent 73a4e70f0b
commit 8fe6ad00f4
12 changed files with 901 additions and 20 deletions
+71
View File
@@ -17,12 +17,15 @@ import { Route as AuthenticatedMessagesRouteImport } from './routes/_authenticat
import { Route as AuthenticatedLedgerRouteImport } from './routes/_authenticated/ledger'
import { Route as AuthenticatedFormsRouteImport } from './routes/_authenticated/forms'
import { Route as AuthenticatedDashboardRouteImport } from './routes/_authenticated/dashboard'
import { Route as AuthenticatedClassesRouteImport } from './routes/_authenticated/classes'
import { Route as AuthenticatedCalendarRouteImport } from './routes/_authenticated/calendar'
import { Route as AuthenticatedAttendanceRouteImport } from './routes/_authenticated/attendance'
import { Route as AuthenticatedAdminRouteImport } from './routes/_authenticated/admin'
import { Route as AuthenticatedStudentsIndexRouteImport } from './routes/_authenticated/students.index'
import { Route as AuthenticatedClassesIndexRouteImport } from './routes/_authenticated/classes.index'
import { Route as AuthenticatedStudentsNewRouteImport } from './routes/_authenticated/students.new'
import { Route as AuthenticatedStudentsIdRouteImport } from './routes/_authenticated/students.$id'
import { Route as AuthenticatedClassesIdRouteImport } from './routes/_authenticated/classes.$id'
const AuthRoute = AuthRouteImport.update({
id: '/auth',
@@ -63,6 +66,11 @@ const AuthenticatedDashboardRoute = AuthenticatedDashboardRouteImport.update({
path: '/dashboard',
getParentRoute: () => AuthenticatedRouteRoute,
} as any)
const AuthenticatedClassesRoute = AuthenticatedClassesRouteImport.update({
id: '/classes',
path: '/classes',
getParentRoute: () => AuthenticatedRouteRoute,
} as any)
const AuthenticatedCalendarRoute = AuthenticatedCalendarRouteImport.update({
id: '/calendar',
path: '/calendar',
@@ -84,6 +92,12 @@ const AuthenticatedStudentsIndexRoute =
path: '/',
getParentRoute: () => AuthenticatedStudentsRoute,
} as any)
const AuthenticatedClassesIndexRoute =
AuthenticatedClassesIndexRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => AuthenticatedClassesRoute,
} as any)
const AuthenticatedStudentsNewRoute =
AuthenticatedStudentsNewRouteImport.update({
id: '/new',
@@ -95,6 +109,11 @@ const AuthenticatedStudentsIdRoute = AuthenticatedStudentsIdRouteImport.update({
path: '/$id',
getParentRoute: () => AuthenticatedStudentsRoute,
} as any)
const AuthenticatedClassesIdRoute = AuthenticatedClassesIdRouteImport.update({
id: '/$id',
path: '/$id',
getParentRoute: () => AuthenticatedClassesRoute,
} as any)
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
@@ -102,13 +121,16 @@ export interface FileRoutesByFullPath {
'/admin': typeof AuthenticatedAdminRoute
'/attendance': typeof AuthenticatedAttendanceRoute
'/calendar': typeof AuthenticatedCalendarRoute
'/classes': typeof AuthenticatedClassesRouteWithChildren
'/dashboard': typeof AuthenticatedDashboardRoute
'/forms': typeof AuthenticatedFormsRoute
'/ledger': typeof AuthenticatedLedgerRoute
'/messages': typeof AuthenticatedMessagesRoute
'/students': typeof AuthenticatedStudentsRouteWithChildren
'/classes/$id': typeof AuthenticatedClassesIdRoute
'/students/$id': typeof AuthenticatedStudentsIdRoute
'/students/new': typeof AuthenticatedStudentsNewRoute
'/classes/': typeof AuthenticatedClassesIndexRoute
'/students/': typeof AuthenticatedStudentsIndexRoute
}
export interface FileRoutesByTo {
@@ -121,8 +143,10 @@ export interface FileRoutesByTo {
'/forms': typeof AuthenticatedFormsRoute
'/ledger': typeof AuthenticatedLedgerRoute
'/messages': typeof AuthenticatedMessagesRoute
'/classes/$id': typeof AuthenticatedClassesIdRoute
'/students/$id': typeof AuthenticatedStudentsIdRoute
'/students/new': typeof AuthenticatedStudentsNewRoute
'/classes': typeof AuthenticatedClassesIndexRoute
'/students': typeof AuthenticatedStudentsIndexRoute
}
export interface FileRoutesById {
@@ -133,13 +157,16 @@ export interface FileRoutesById {
'/_authenticated/admin': typeof AuthenticatedAdminRoute
'/_authenticated/attendance': typeof AuthenticatedAttendanceRoute
'/_authenticated/calendar': typeof AuthenticatedCalendarRoute
'/_authenticated/classes': typeof AuthenticatedClassesRouteWithChildren
'/_authenticated/dashboard': typeof AuthenticatedDashboardRoute
'/_authenticated/forms': typeof AuthenticatedFormsRoute
'/_authenticated/ledger': typeof AuthenticatedLedgerRoute
'/_authenticated/messages': typeof AuthenticatedMessagesRoute
'/_authenticated/students': typeof AuthenticatedStudentsRouteWithChildren
'/_authenticated/classes/$id': typeof AuthenticatedClassesIdRoute
'/_authenticated/students/$id': typeof AuthenticatedStudentsIdRoute
'/_authenticated/students/new': typeof AuthenticatedStudentsNewRoute
'/_authenticated/classes/': typeof AuthenticatedClassesIndexRoute
'/_authenticated/students/': typeof AuthenticatedStudentsIndexRoute
}
export interface FileRouteTypes {
@@ -150,13 +177,16 @@ export interface FileRouteTypes {
| '/admin'
| '/attendance'
| '/calendar'
| '/classes'
| '/dashboard'
| '/forms'
| '/ledger'
| '/messages'
| '/students'
| '/classes/$id'
| '/students/$id'
| '/students/new'
| '/classes/'
| '/students/'
fileRoutesByTo: FileRoutesByTo
to:
@@ -169,8 +199,10 @@ export interface FileRouteTypes {
| '/forms'
| '/ledger'
| '/messages'
| '/classes/$id'
| '/students/$id'
| '/students/new'
| '/classes'
| '/students'
id:
| '__root__'
@@ -180,13 +212,16 @@ export interface FileRouteTypes {
| '/_authenticated/admin'
| '/_authenticated/attendance'
| '/_authenticated/calendar'
| '/_authenticated/classes'
| '/_authenticated/dashboard'
| '/_authenticated/forms'
| '/_authenticated/ledger'
| '/_authenticated/messages'
| '/_authenticated/students'
| '/_authenticated/classes/$id'
| '/_authenticated/students/$id'
| '/_authenticated/students/new'
| '/_authenticated/classes/'
| '/_authenticated/students/'
fileRoutesById: FileRoutesById
}
@@ -254,6 +289,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedDashboardRouteImport
parentRoute: typeof AuthenticatedRouteRoute
}
'/_authenticated/classes': {
id: '/_authenticated/classes'
path: '/classes'
fullPath: '/classes'
preLoaderRoute: typeof AuthenticatedClassesRouteImport
parentRoute: typeof AuthenticatedRouteRoute
}
'/_authenticated/calendar': {
id: '/_authenticated/calendar'
path: '/calendar'
@@ -282,6 +324,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedStudentsIndexRouteImport
parentRoute: typeof AuthenticatedStudentsRoute
}
'/_authenticated/classes/': {
id: '/_authenticated/classes/'
path: '/'
fullPath: '/classes/'
preLoaderRoute: typeof AuthenticatedClassesIndexRouteImport
parentRoute: typeof AuthenticatedClassesRoute
}
'/_authenticated/students/new': {
id: '/_authenticated/students/new'
path: '/new'
@@ -296,9 +345,29 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedStudentsIdRouteImport
parentRoute: typeof AuthenticatedStudentsRoute
}
'/_authenticated/classes/$id': {
id: '/_authenticated/classes/$id'
path: '/$id'
fullPath: '/classes/$id'
preLoaderRoute: typeof AuthenticatedClassesIdRouteImport
parentRoute: typeof AuthenticatedClassesRoute
}
}
}
interface AuthenticatedClassesRouteChildren {
AuthenticatedClassesIdRoute: typeof AuthenticatedClassesIdRoute
AuthenticatedClassesIndexRoute: typeof AuthenticatedClassesIndexRoute
}
const AuthenticatedClassesRouteChildren: AuthenticatedClassesRouteChildren = {
AuthenticatedClassesIdRoute: AuthenticatedClassesIdRoute,
AuthenticatedClassesIndexRoute: AuthenticatedClassesIndexRoute,
}
const AuthenticatedClassesRouteWithChildren =
AuthenticatedClassesRoute._addFileChildren(AuthenticatedClassesRouteChildren)
interface AuthenticatedStudentsRouteChildren {
AuthenticatedStudentsIdRoute: typeof AuthenticatedStudentsIdRoute
AuthenticatedStudentsNewRoute: typeof AuthenticatedStudentsNewRoute
@@ -320,6 +389,7 @@ interface AuthenticatedRouteRouteChildren {
AuthenticatedAdminRoute: typeof AuthenticatedAdminRoute
AuthenticatedAttendanceRoute: typeof AuthenticatedAttendanceRoute
AuthenticatedCalendarRoute: typeof AuthenticatedCalendarRoute
AuthenticatedClassesRoute: typeof AuthenticatedClassesRouteWithChildren
AuthenticatedDashboardRoute: typeof AuthenticatedDashboardRoute
AuthenticatedFormsRoute: typeof AuthenticatedFormsRoute
AuthenticatedLedgerRoute: typeof AuthenticatedLedgerRoute
@@ -331,6 +401,7 @@ const AuthenticatedRouteRouteChildren: AuthenticatedRouteRouteChildren = {
AuthenticatedAdminRoute: AuthenticatedAdminRoute,
AuthenticatedAttendanceRoute: AuthenticatedAttendanceRoute,
AuthenticatedCalendarRoute: AuthenticatedCalendarRoute,
AuthenticatedClassesRoute: AuthenticatedClassesRouteWithChildren,
AuthenticatedDashboardRoute: AuthenticatedDashboardRoute,
AuthenticatedFormsRoute: AuthenticatedFormsRoute,
AuthenticatedLedgerRoute: AuthenticatedLedgerRoute,