add grading, attendnace management

This commit is contained in:
root
2026-04-29 17:39:37 -04:00
parent ea495f8382
commit dc7acf2536
27 changed files with 4150 additions and 960 deletions
+82 -14
View File
@@ -1,10 +1,10 @@
/**
* Administrator grading API (parity with CI `Views/grading/*.php`).
* Base: `/api/v1/administrator/grading/...` with JWT.
* Base: `/api/v1/grading/...` with JWT.
*/
import { apiFetch } from './http'
const BASE = '/api/v1/administrator/grading'
const BASE = '/api/v1/grading'
export type GradingScoreRow = {
id?: number
@@ -12,40 +12,108 @@ export type GradingScoreRow = {
comment?: string | null
}
export type GradingSectionScoreStudent = {
student_id: number
firstname?: string | null
lastname?: string | null
school_id?: string | null
score?: number | string | null
scores?: Record<string, number | string | null>
comments?: Record<
string,
{
comment?: string | null
comment_review?: string | null
commented_by?: string | number | null
reviewed_by?: string | number | null
}
>
}
export type GradingScoreFormPayload = {
scoresLocked?: boolean
student?: { id?: number; firstname?: string; lastname?: string }
student?: { id?: number; firstname?: string; lastname?: string; school_id?: string | null }
classSectionId?: number
class_section_name?: string | null
scores?: GradingScoreRow[]
}
export type GradingSectionScorePayload = {
ok?: boolean
students?: GradingSectionScoreStudent[]
headers?: number[]
semester?: string
school_year?: string
class_section_id?: number | string
class_section_name?: string | null
scores_locked?: boolean
missing_ok_map?: Record<string, unknown>
}
function q(sp: URLSearchParams): string {
const s = sp.toString()
return s ? `?${s}` : ''
}
export async function fetchGradingMain(searchParams: URLSearchParams): Promise<unknown> {
return apiFetch(`${BASE}/main${q(searchParams)}`)
return apiFetch(`${BASE}/overview${q(searchParams)}`)
}
export async function fetchGradingScoreForm(
scoreType: string,
studentId: string,
classSectionId: string,
studentId: string,
searchParams: URLSearchParams,
): Promise<GradingScoreFormPayload> {
const qs = new URLSearchParams({
student_id: studentId,
class_section_id: classSectionId,
})
return apiFetch(`${BASE}/scores/${encodeURIComponent(scoreType)}?${qs}`)
return apiFetch(`${BASE}/scores/${encodeURIComponent(scoreType)}/${encodeURIComponent(classSectionId)}/${encodeURIComponent(studentId)}${q(searchParams)}`)
}
function scoreCollectionPath(scoreType: string): string {
switch (scoreType) {
case 'homework':
return '/api/v1/scores/homework'
case 'quiz':
return '/api/v1/scores/quizzes'
case 'project':
return '/api/v1/scores/projects'
case 'midterm':
return '/api/v1/scores/midterm'
case 'final':
return '/api/v1/scores/final'
case 'comments':
return '/api/v1/scores/comments'
default:
throw new Error(`Unsupported score type: ${scoreType}`)
}
}
export async function fetchGradingSectionScoreTable(
scoreType: string,
classSectionId: string,
searchParams: URLSearchParams,
): Promise<GradingSectionScorePayload> {
const next = new URLSearchParams(searchParams)
next.set('class_section_id', classSectionId)
return apiFetch(`${scoreCollectionPath(scoreType)}${q(next)}`)
}
export async function postGradingScoreUpdate(
body: Record<string, unknown>,
): Promise<unknown> {
return apiFetch(`${BASE}/scores`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
}
export async function postGradingSectionScoreUpdate(
scoreType: string,
body: Record<string, unknown>,
): Promise<unknown> {
return apiFetch(`${BASE}/scores/${encodeURIComponent(scoreType)}`, {
method: 'POST',
const method = scoreType === 'comments' ? 'PUT' : 'POST'
return apiFetch(scoreCollectionPath(scoreType), {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
@@ -89,11 +157,11 @@ export async function fetchHomeworkTracking(
}
export async function fetchParticipation(searchParams: URLSearchParams): Promise<unknown> {
return apiFetch(`${BASE}/participation${q(searchParams)}`)
return apiFetch(`/api/v1/scores/participation${q(searchParams)}`)
}
export async function postParticipation(body: Record<string, unknown>): Promise<unknown> {
return apiFetch(`${BASE}/participation`, {
return apiFetch('/api/v1/scores/participation', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),