add school year model
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Link, useParams } from 'react-router-dom'
|
||||
import { Link, useParams, useSearchParams } from 'react-router-dom'
|
||||
import {
|
||||
fetchTeacherDashboard,
|
||||
isTeacherDashboardApiEnabled,
|
||||
type TeacherDashboardPayload,
|
||||
} from '../../api/teacherSession'
|
||||
import { useSchoolYearOptions } from '../../hooks/useSchoolYearOptions'
|
||||
import { TeacherShell } from './TeacherShell'
|
||||
import { useTeacherResource } from './useTeacherResource'
|
||||
|
||||
@@ -18,6 +19,16 @@ type SentPayload = { sentMessages?: MsgRow[] }
|
||||
|
||||
type TeacherMessagePayload = { message?: Record<string, unknown>; thread?: unknown[] }
|
||||
|
||||
function appendSchoolYearParam(path: string, schoolYear: string): string {
|
||||
if (!schoolYear) return path
|
||||
const [base, hash = ''] = path.split('#', 2)
|
||||
const [pathname, query = ''] = base.split('?', 2)
|
||||
const params = new URLSearchParams(query)
|
||||
if (!params.get('school_year')) params.set('school_year', schoolYear)
|
||||
const next = `${pathname}?${params.toString()}`
|
||||
return hash ? `${next}#${hash}` : next
|
||||
}
|
||||
|
||||
/** Every `/app/teacher/*` route so nothing is hidden behind the slim top nav alone. */
|
||||
const TEACHER_PAGE_GROUPS = [
|
||||
{
|
||||
@@ -204,14 +215,20 @@ export function TeacherNoClassesPage() {
|
||||
|
||||
/** `teacher/select_semester.php` */
|
||||
export function TeacherSelectSemesterPage() {
|
||||
const [searchParams] = useSearchParams()
|
||||
const { data, loading, error } = useTeacherResource<{
|
||||
invalidChoice?: string
|
||||
fallPath?: string
|
||||
springPath?: string
|
||||
}>('/select-semester')
|
||||
const requestedSchoolYear = searchParams.get('school_year')?.trim() ?? ''
|
||||
const { selectedYear } = useSchoolYearOptions({
|
||||
preferredYear: requestedSchoolYear,
|
||||
})
|
||||
const activeSchoolYear = requestedSchoolYear || selectedYear
|
||||
|
||||
const fallTo = data?.fallPath ?? '/app/teacher/scores?semester=Fall'
|
||||
const springTo = data?.springPath ?? '/app/teacher/scores?semester=Spring'
|
||||
const fallTo = appendSchoolYearParam(data?.fallPath ?? '/app/teacher/scores?semester=Fall', activeSchoolYear)
|
||||
const springTo = appendSchoolYearParam(data?.springPath ?? '/app/teacher/scores?semester=Spring', activeSchoolYear)
|
||||
|
||||
return (
|
||||
<TeacherShell title="Choose a semester" loading={loading} error={error}>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useTeacherResource } from './useTeacherResource'
|
||||
import { postTeacherMultipart } from '../../api/teacherSession'
|
||||
import { useAuth } from '../../auth/AuthProvider'
|
||||
import { ApiHttpError, apiFetch } from '../../api/http'
|
||||
import { useSchoolYearOptions } from '../../hooks/useSchoolYearOptions'
|
||||
|
||||
function TeacherApiDumpPage({
|
||||
title,
|
||||
@@ -29,6 +30,20 @@ function TeacherApiDumpPage({
|
||||
)
|
||||
}
|
||||
|
||||
function teacherRouteWithTerm(
|
||||
path: string,
|
||||
params: { classSectionId?: number; schoolYear?: string; semester?: string },
|
||||
): string {
|
||||
const query = new URLSearchParams()
|
||||
if (params.classSectionId && params.classSectionId > 0) {
|
||||
query.set('class_section_id', String(params.classSectionId))
|
||||
}
|
||||
if (params.schoolYear) query.set('school_year', params.schoolYear)
|
||||
if (params.semester) query.set('semester', params.semester)
|
||||
const value = query.toString()
|
||||
return value ? `${path}?${value}` : path
|
||||
}
|
||||
|
||||
type CompetitionIndexPayload = {
|
||||
success?: string
|
||||
error?: string
|
||||
@@ -1372,6 +1387,10 @@ export function TeacherScoresPage() {
|
||||
const [search] = useSearchParams()
|
||||
const requestedSemester = search.get('semester')?.trim() ?? ''
|
||||
const requestedSchoolYear = search.get('school_year')?.trim() ?? ''
|
||||
const { selectedYear: defaultSchoolYear } = useSchoolYearOptions({
|
||||
preferredYear: requestedSchoolYear,
|
||||
})
|
||||
const effectiveRequestedSchoolYear = requestedSchoolYear || defaultSchoolYear
|
||||
|
||||
const [payload, setPayload] = useState<TeacherScoresPayload | null>(null)
|
||||
const [rows, setRows] = useState<TeacherScoresRow[]>([])
|
||||
@@ -1384,7 +1403,7 @@ export function TeacherScoresPage() {
|
||||
const [tableSearch, setTableSearch] = useState('')
|
||||
|
||||
const resolvedSemester = payload?.semester?.trim() || requestedSemester
|
||||
const resolvedSchoolYear = payload?.school_year?.trim() || requestedSchoolYear
|
||||
const resolvedSchoolYear = payload?.school_year?.trim() || effectiveRequestedSchoolYear
|
||||
const semesterKey = teacherSemesterKey(resolvedSemester)
|
||||
const termLabel = semesterKey === 'midterm' ? 'Midterm' : semesterKey === 'final' ? 'Final Exam' : 'Term Score'
|
||||
const termCommentLabel = semesterKey === 'midterm' ? 'Midterm Comment' : semesterKey === 'final' ? 'Final Comment' : 'Term Comment'
|
||||
@@ -1464,7 +1483,7 @@ export function TeacherScoresPage() {
|
||||
setWarningLines([])
|
||||
const params = new URLSearchParams()
|
||||
if (requestedSemester !== '') params.set('semester', requestedSemester)
|
||||
if (requestedSchoolYear !== '') params.set('school_year', requestedSchoolYear)
|
||||
if (effectiveRequestedSchoolYear !== '') params.set('school_year', effectiveRequestedSchoolYear)
|
||||
const query = params.toString()
|
||||
const body = await apiFetch<TeacherScoresPayload>(`/api/v1/teacher/scores${query ? `?${query}` : ''}`)
|
||||
setPayload(body)
|
||||
@@ -1478,7 +1497,7 @@ export function TeacherScoresPage() {
|
||||
|
||||
useEffect(() => {
|
||||
void load()
|
||||
}, [requestedSchoolYear, requestedSemester])
|
||||
}, [effectiveRequestedSchoolYear, requestedSemester])
|
||||
|
||||
const visibleRows = useMemo(() => {
|
||||
const needle = tableSearch.trim().toLowerCase()
|
||||
@@ -1674,7 +1693,12 @@ export function TeacherScoresPage() {
|
||||
) : null}
|
||||
|
||||
<div className="text-center mb-3">
|
||||
<Link to="/app/teacher/select-semester" className="btn btn-outline-secondary btn-sm">
|
||||
<Link
|
||||
to={teacherRouteWithTerm('/app/teacher/select-semester', {
|
||||
schoolYear: resolvedSchoolYear,
|
||||
})}
|
||||
className="btn btn-outline-secondary btn-sm"
|
||||
>
|
||||
Choose another semester
|
||||
</Link>
|
||||
</div>
|
||||
@@ -1807,25 +1831,67 @@ export function TeacherScoresPage() {
|
||||
</div>
|
||||
|
||||
<div className="d-flex flex-wrap gap-2 mt-3">
|
||||
<Link to={`/app/teacher/add-homework?class_section_id=${classSectionId}`} className="btn btn-secondary">
|
||||
<Link
|
||||
to={teacherRouteWithTerm('/app/teacher/add-homework', {
|
||||
classSectionId,
|
||||
schoolYear: resolvedSchoolYear,
|
||||
semester: resolvedSemester,
|
||||
})}
|
||||
className="btn btn-secondary"
|
||||
>
|
||||
Add Homework
|
||||
</Link>
|
||||
<Link to={`/app/teacher/add-quiz?class_section_id=${classSectionId}`} className="btn btn-secondary">
|
||||
<Link
|
||||
to={teacherRouteWithTerm('/app/teacher/add-quiz', {
|
||||
classSectionId,
|
||||
schoolYear: resolvedSchoolYear,
|
||||
semester: resolvedSemester,
|
||||
})}
|
||||
className="btn btn-secondary"
|
||||
>
|
||||
Add Quiz
|
||||
</Link>
|
||||
<Link to={`/app/teacher/add-project?class_section_id=${classSectionId}`} className="btn btn-secondary">
|
||||
<Link
|
||||
to={teacherRouteWithTerm('/app/teacher/add-project', {
|
||||
classSectionId,
|
||||
schoolYear: resolvedSchoolYear,
|
||||
semester: resolvedSemester,
|
||||
})}
|
||||
className="btn btn-secondary"
|
||||
>
|
||||
Add Project
|
||||
</Link>
|
||||
<Link to={`/app/teacher/add-participation?class_section_id=${classSectionId}`} className="btn btn-secondary">
|
||||
<Link
|
||||
to={teacherRouteWithTerm('/app/teacher/add-participation', {
|
||||
classSectionId,
|
||||
schoolYear: resolvedSchoolYear,
|
||||
semester: resolvedSemester,
|
||||
})}
|
||||
className="btn btn-secondary"
|
||||
>
|
||||
Add Participation
|
||||
</Link>
|
||||
{semesterKey === 'midterm' ? (
|
||||
<Link to={`/app/teacher/add-midterm-exam?class_section_id=${classSectionId}`} className="btn btn-secondary">
|
||||
<Link
|
||||
to={teacherRouteWithTerm('/app/teacher/add-midterm-exam', {
|
||||
classSectionId,
|
||||
schoolYear: resolvedSchoolYear,
|
||||
semester: resolvedSemester,
|
||||
})}
|
||||
className="btn btn-secondary"
|
||||
>
|
||||
Add Midterm
|
||||
</Link>
|
||||
) : null}
|
||||
{semesterKey === 'final' ? (
|
||||
<Link to={`/app/teacher/add-final-exam?class_section_id=${classSectionId}`} className="btn btn-secondary">
|
||||
<Link
|
||||
to={teacherRouteWithTerm('/app/teacher/add-final-exam', {
|
||||
classSectionId,
|
||||
schoolYear: resolvedSchoolYear,
|
||||
semester: resolvedSemester,
|
||||
})}
|
||||
className="btn btn-secondary"
|
||||
>
|
||||
Add Final
|
||||
</Link>
|
||||
) : null}
|
||||
|
||||
Reference in New Issue
Block a user