add admin pages

This commit is contained in:
root
2026-04-23 15:28:56 -04:00
parent 94700d4f0f
commit 7fe34dde0d
9 changed files with 6513 additions and 99 deletions
+818 -2
View File
@@ -1,11 +1,25 @@
import { apiUrl } from '../lib/apiOrigin'
import { apiFetch, getStoredToken } from './http'
import type {
AdministratorAbsenceFormResponse,
AdministratorAbsenceSubmitResponse,
AdministratorAdminAttendanceResponse,
AdministratorDailyAttendanceResponse,
AdministratorDashboardMetricsResponse,
AdministratorDashboardSearchResponse,
ApiEnvelope,
BroadcastEmailOptionsResponse,
BroadcastEmailSendResponse,
ClassSectionShowResponse,
ClassSectionsResponse,
DashboardPayload,
FamilyAdminIndexResponse,
GradingOverviewResponse,
IpBansResponse,
LoginFailure,
LoginResponse,
LoginSuccess,
LateSlipLogsResponse,
NavBuilderDataResponse,
NavBuilderDeleteResponse,
NavBuilderSaveResponse,
@@ -19,16 +33,41 @@ import type {
ParentRegistrationOverviewResponse,
ParentEmergencyContactsResponse,
ParentAttendanceReportsListResponse,
SchoolCalendarListResponse,
SchoolCalendarOptionsResponse,
SchoolCalendarShowResponse,
ParentAuthorizedUsersApiResponse,
ParentMessagesResponse,
NotificationAlertsResponse,
PaypalTransactionsResponse,
PrintNotificationRecipientsResponse,
SchoolCalendarEventsResponse,
StudentAssignmentsResponse,
StudentScoresResponse,
ReportCardAcknowledgementResponse,
ReportCardMetaResponse,
RemovedStudentsResponse,
ClassProgressGroupsResponse,
ClassProgressMetaResponse,
ClassProgressShowResponse,
ParentProfileRecord,
ContactSubmitResponse,
CompetitionScoresEditResponse,
CompetitionScoresIndexResponse,
ExamDraftAdminResponse,
PublicCompetitionIndexResponse,
PublicCompetitionShowResponse,
StudentDirectoryResponse,
StudentScoreCardResponse,
StudentEmergencyContactResponse,
StudentParentResponse,
StudentPromotionTotalsResponse,
SubjectCurriculumResponse,
TeacherClassAssignmentsResponse,
TeacherSubmissionsResponse,
EmergencyContactGroupsResponse,
EmergencyContactShowResponse,
UsersResponse,
} from './types'
export async function loginRequest(
@@ -57,6 +96,702 @@ export async function fetchDashboardRoute(): Promise<ApiEnvelope<DashboardPayloa
return apiFetch<ApiEnvelope<DashboardPayload>>('/api/v1/dashboard/route')
}
export async function fetchAdministratorDashboardMetrics(): Promise<AdministratorDashboardMetricsResponse> {
return apiFetch<AdministratorDashboardMetricsResponse>('/api/v1/administrator/dashboard/metrics')
}
export async function searchAdministratorDashboard(query: string): Promise<AdministratorDashboardSearchResponse> {
const q = query.trim()
const qs = q ? `?query=${encodeURIComponent(q)}` : ''
return apiFetch<AdministratorDashboardSearchResponse>(`/api/v1/administrator/dashboard/user-search${qs}`)
}
export async function fetchAdministratorAbsenceForm(): Promise<AdministratorAbsenceFormResponse> {
return apiFetch<AdministratorAbsenceFormResponse>('/api/v1/administrator/absence')
}
export async function submitAdministratorAbsence(payload: {
dates: string[]
reason_type?: string
reason: string
}): Promise<AdministratorAbsenceSubmitResponse> {
return apiFetch<AdministratorAbsenceSubmitResponse>('/api/v1/administrator/absence', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function fetchAdministratorDailyAttendance(
params?: { semester?: string | null; schoolYear?: string | null },
): Promise<AdministratorDailyAttendanceResponse> {
const query = new URLSearchParams()
if (params?.semester) query.set('semester', params.semester)
if (params?.schoolYear) query.set('school_year', params.schoolYear)
const qs = query.size > 0 ? `?${query.toString()}` : ''
return apiFetch<AdministratorDailyAttendanceResponse>(`/api/v1/attendance/admin/daily${qs}`)
}
export async function addAdministratorAttendanceEntry(payload: {
class_section_id: number
student_id: number
school_id?: string | null
status: 'present' | 'absent' | 'late'
date?: string | null
reason?: string | null
class_id?: number | null
is_reported?: 'yes' | 'no' | null
}): Promise<{ message?: string }> {
return apiFetch<{ message?: string }>('/api/v1/attendance/admin/add-entry', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function updateAdministratorAttendance(payload: {
class_section_id: number
student_id: number
school_id?: string | null
status: 'present' | 'absent' | 'late'
date: string
reason?: string | null
class_id?: number | null
semester?: string | null
school_year?: string | null
is_reported?: string | null
}): Promise<{ message?: string }> {
return apiFetch<{ message?: string }>('/api/v1/attendance/admin/update', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function fetchAdministratorAdminAttendance(
params?: { date?: string | null; semester?: string | null; schoolYear?: string | null },
): Promise<AdministratorAdminAttendanceResponse> {
const query = new URLSearchParams()
if (params?.date) query.set('date', params.date)
if (params?.semester) query.set('semester', params.semester)
if (params?.schoolYear) query.set('school_year', params.schoolYear)
const qs = query.size > 0 ? `?${query.toString()}` : ''
return apiFetch<AdministratorAdminAttendanceResponse>(`/api/v1/attendance/staff/admins${qs}`)
}
export async function saveAdministratorAdminAttendance(payload: {
date: string
semester: string
school_year: string
admins: Record<
string,
{
status?: string | null
reason?: string | null
role_name?: string | null
role_slug?: string | null
}
>
}): Promise<{ message?: string }> {
return apiFetch<{ message?: string }>('/api/v1/attendance/staff/admins/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function fetchBroadcastEmailOptions(): Promise<BroadcastEmailOptionsResponse> {
return apiFetch<BroadcastEmailOptionsResponse>('/api/v1/broadcast-email/options')
}
export async function sendBroadcastEmail(payload: {
from_key: string
mode: 'personalized' | 'standard'
subject: string
body_html: string
wrap_layout?: boolean
preheader?: string
cta_text?: string
cta_url?: string
test_email?: string
send_test_only?: boolean
parent_ids?: number[]
}): Promise<BroadcastEmailSendResponse> {
return apiFetch<BroadcastEmailSendResponse>('/api/v1/broadcast-email/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function fetchSchoolCalendarOptions(): Promise<SchoolCalendarOptionsResponse> {
return apiFetch<SchoolCalendarOptionsResponse>('/api/v1/settings/school-calendar/options')
}
export async function fetchAdministratorCalendarEvents(params?: {
schoolYear?: string | null
semester?: string | null
audience?: 'admin' | 'teacher' | 'parent' | null
}): Promise<SchoolCalendarListResponse> {
const query = new URLSearchParams()
if (params?.schoolYear) query.set('school_year', params.schoolYear)
if (params?.semester) query.set('semester', params.semester)
if (params?.audience) query.set('audience', params.audience)
const qs = query.size > 0 ? `?${query.toString()}` : ''
return apiFetch<SchoolCalendarListResponse>(`/api/v1/settings/school-calendar/events${qs}`)
}
export async function fetchAdministratorCalendarEvent(eventId: number): Promise<SchoolCalendarShowResponse> {
return apiFetch<SchoolCalendarShowResponse>(`/api/v1/settings/school-calendar/events/${eventId}`)
}
export async function createAdministratorCalendarEvent(payload: {
title: string
description?: string
event_type?: string
date: string
notify_parent?: boolean
notify_teacher?: boolean
notify_admin?: boolean
no_school?: boolean
school_year?: string
semester?: string
send_email_parent?: boolean
send_email_teacher?: boolean
send_email_admin?: boolean
}): Promise<SchoolCalendarShowResponse> {
return apiFetch<SchoolCalendarShowResponse>('/api/v1/settings/school-calendar/events', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function updateAdministratorCalendarEvent(
eventId: number,
payload: Record<string, unknown>,
): Promise<SchoolCalendarShowResponse> {
return apiFetch<SchoolCalendarShowResponse>(`/api/v1/settings/school-calendar/events/${eventId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function deleteAdministratorCalendarEvent(eventId: number): Promise<ApiEnvelope<null>> {
return apiFetch<ApiEnvelope<null>>(`/api/v1/settings/school-calendar/events/${eventId}`, {
method: 'DELETE',
})
}
export async function fetchStudentAssignments(
schoolYear?: string | null,
): Promise<StudentAssignmentsResponse> {
const query = schoolYear ? `?school_year=${encodeURIComponent(schoolYear)}` : ''
return apiFetch<StudentAssignmentsResponse>(`/api/v1/students/assignments${query}`)
}
export async function assignStudentClass(payload: {
student_id: number
class_section_ids?: number[]
class_section_id?: number[]
is_event_only?: boolean
}): Promise<{ ok: boolean; message?: string }> {
return apiFetch<{ ok: boolean; message?: string }>('/api/v1/students/assign-class', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
student_id: payload.student_id,
class_section_id: payload.class_section_id ?? payload.class_section_ids ?? [],
is_event_only: payload.is_event_only ?? false,
}),
})
}
export async function removeStudentClass(payload: {
student_id: number
class_section_id: number
}): Promise<{ ok: boolean; message?: string }> {
return apiFetch<{ ok: boolean; message?: string }>('/api/v1/students/remove-class', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function fetchRemovedStudents(
schoolYear?: string | null,
): Promise<RemovedStudentsResponse> {
const query = schoolYear ? `?school_year=${encodeURIComponent(schoolYear)}` : ''
return apiFetch<RemovedStudentsResponse>(`/api/v1/students/removed${query}`)
}
export async function setStudentActive(payload: {
student_id: number
is_active: boolean
}): Promise<{ ok: boolean; message?: string }> {
return apiFetch<{ ok: boolean; message?: string }>('/api/v1/students/set-active', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function autoDistributeStudents(payload: {
class_id?: number
class_section_id?: number
students_per_section: number
school_year?: string | null
}): Promise<ApiEnvelope<{ sections?: Array<Record<string, unknown>>; message?: string }>> {
return apiFetch<ApiEnvelope<{ sections?: Array<Record<string, unknown>>; message?: string }>>(
'/api/v1/students/auto-distribute',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
},
)
}
export async function fetchStudentPromotionTotals(
schoolYear?: string | null,
): Promise<StudentPromotionTotalsResponse> {
const query = schoolYear ? `?school_year=${encodeURIComponent(schoolYear)}` : ''
return apiFetch<StudentPromotionTotalsResponse>(`/api/v1/students/promotion-totals${query}`)
}
export async function fetchStudentDirectory(
schoolYear?: string | null,
): Promise<StudentDirectoryResponse> {
const query = schoolYear ? `?school_year=${encodeURIComponent(schoolYear)}` : ''
return apiFetch<StudentDirectoryResponse>(`/api/v1/students${query}`)
}
export async function updateStudentProfile(
studentId: number,
payload: Record<string, unknown>,
): Promise<{ ok: boolean; message?: string }> {
return apiFetch<{ ok: boolean; message?: string }>(`/api/v1/students/${studentId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function fetchStudentParent(studentId: number): Promise<StudentParentResponse> {
return apiFetch<StudentParentResponse>(`/api/v1/students/${studentId}/parents`)
}
export async function fetchStudentEmergencyContacts(
studentId: number,
): Promise<StudentEmergencyContactResponse> {
return apiFetch<StudentEmergencyContactResponse>(`/api/v1/students/${studentId}/emergency-contacts`)
}
export async function fetchTeacherClassAssignments(
schoolYear?: string | null,
): Promise<TeacherClassAssignmentsResponse> {
const query = schoolYear ? `?school_year=${encodeURIComponent(schoolYear)}` : ''
return apiFetch<TeacherClassAssignmentsResponse>(
`/api/v1/administrator/teacher-class/assignments${query}`,
)
}
export async function assignTeacherClass(payload: {
teacher_id: number
class_section_id: number
teacher_role?: string
school_year?: string
}): Promise<{ ok: boolean; message?: string; position?: string | null }> {
return apiFetch<{ ok: boolean; message?: string; position?: string | null }>(
'/api/v1/administrator/teacher-class/assign',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
},
)
}
export async function deleteTeacherClassAssignment(payload: {
teacher_id: number
class_section_id: number
position: 'main' | 'ta'
school_year?: string
}): Promise<{ ok: boolean; message?: string }> {
return apiFetch<{ ok: boolean; message?: string }>(
'/api/v1/administrator/teacher-class/delete',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
},
)
}
export async function fetchTeacherSubmissions(): Promise<TeacherSubmissionsResponse> {
return apiFetch<TeacherSubmissionsResponse>('/api/v1/administrator/teacher-submissions')
}
export async function fetchPublishedCompetitions(): Promise<PublicCompetitionIndexResponse> {
return apiFetch<PublicCompetitionIndexResponse>('/api/winners/competitions', {}, { attachAuth: false })
}
export async function fetchPublishedCompetition(id: number): Promise<PublicCompetitionShowResponse> {
return apiFetch<PublicCompetitionShowResponse>(`/api/winners/competitions/${id}`, {}, { attachAuth: false })
}
export async function fetchCompetitionScoresIndex(
classSectionId?: number | null,
): Promise<CompetitionScoresIndexResponse> {
const query = classSectionId ? `?class_section_id=${encodeURIComponent(String(classSectionId))}` : ''
return apiFetch<CompetitionScoresIndexResponse>(`/api/v1/competition-scores${query}`)
}
export async function fetchCompetitionScoresDetail(
id: number,
classSectionId?: number | null,
): Promise<CompetitionScoresEditResponse> {
const query = classSectionId ? `?class_section_id=${encodeURIComponent(String(classSectionId))}` : ''
return apiFetch<CompetitionScoresEditResponse>(`/api/v1/competition-scores/${id}${query}`)
}
export async function saveCompetitionScores(
id: number,
payload: { class_section_id?: number | null; scores: Record<string, number | string> },
): Promise<{ ok: boolean; message?: string; savedCount?: number; invalidScores?: string[] }> {
return apiFetch<{ ok: boolean; message?: string; savedCount?: number; invalidScores?: string[] }>(
`/api/v1/competition-scores/${id}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
},
)
}
export async function notifyTeacherSubmissions(payload: {
notify: number[]
missing_items?: Record<string, string[]>
}): Promise<{ message?: string; sent?: number; failed?: number }> {
return apiFetch<{ message?: string; sent?: number; failed?: number }>(
'/api/v1/administrator/teacher-submissions/notify',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
},
)
}
export async function fetchSubjectCurriculum(): Promise<SubjectCurriculumResponse> {
return apiFetch<SubjectCurriculumResponse>('/api/v1/subjects/curriculum')
}
export async function createSubjectCurriculum(payload: {
class_id: number
subject: string
unit_number?: number | null
unit_title?: string | null
chapter_name: string
}): Promise<ApiEnvelope<{ entry?: unknown }>> {
return apiFetch<ApiEnvelope<{ entry?: unknown }>>('/api/v1/subjects/curriculum', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function updateSubjectCurriculum(
id: number,
payload: Record<string, unknown>,
): Promise<ApiEnvelope<{ entry?: unknown }>> {
return apiFetch<ApiEnvelope<{ entry?: unknown }>>(`/api/v1/subjects/curriculum/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function deleteSubjectCurriculum(id: number): Promise<ApiEnvelope<null>> {
return apiFetch<ApiEnvelope<null>>(`/api/v1/subjects/curriculum/${id}`, {
method: 'DELETE',
})
}
export async function fetchClassSections(params?: {
search?: string
schoolYear?: string | null
semester?: string | null
page?: number
perPage?: number
}): Promise<ClassSectionsResponse> {
const query = new URLSearchParams()
if (params?.search) query.set('search', params.search)
if (params?.schoolYear) query.set('school_year', params.schoolYear)
if (params?.semester) query.set('semester', params.semester)
if (params?.page) query.set('page', String(params.page))
if (params?.perPage) query.set('per_page', String(params.perPage))
const qs = query.size > 0 ? `?${query.toString()}` : ''
return apiFetch<ClassSectionsResponse>(`/api/v1/class-sections${qs}`)
}
export async function fetchClassSection(sectionId: number): Promise<ClassSectionShowResponse> {
return apiFetch<ClassSectionShowResponse>(`/api/v1/class-sections/${sectionId}`)
}
export async function createClassSection(payload: {
class_id: number
class_section_id: number
class_section_name: string
semester: string
school_year: string
}): Promise<ClassSectionShowResponse> {
return apiFetch<ClassSectionShowResponse>('/api/v1/class-sections', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function updateClassSection(
sectionId: number,
payload: Partial<{
class_id: number
class_section_id: number
class_section_name: string
semester: string
school_year: string
}>,
): Promise<ClassSectionShowResponse> {
return apiFetch<ClassSectionShowResponse>(`/api/v1/class-sections/${sectionId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function deleteClassSection(sectionId: number): Promise<ApiEnvelope<null>> {
return apiFetch<ApiEnvelope<null>>(`/api/v1/class-sections/${sectionId}`, {
method: 'DELETE',
})
}
export async function fetchExamDraftAdminData(): Promise<ExamDraftAdminResponse> {
return apiFetch<ExamDraftAdminResponse>('/api/v1/exams/drafts/admin')
}
async function fetchMultipartJson<T>(path: string, formData: FormData): Promise<T> {
const headers = new Headers({ Accept: 'application/json' })
const token = getStoredToken()
if (token) headers.set('Authorization', `Bearer ${token}`)
const res = await fetch(apiUrl(path), {
method: 'POST',
headers,
body: formData,
})
const body: unknown = await res.json().catch(() => null)
if (!res.ok) {
const msg =
typeof body === 'object' && body !== null && 'message' in body
? String((body as { message?: unknown }).message ?? '')
: ''
throw new Error(msg || `HTTP ${res.status}`)
}
return body as T
}
export async function uploadLegacyExamDraft(payload: {
class_section_id: number
school_year: string
semester: string
exam_type?: string
old_exam_file: File
}): Promise<{ ok: boolean; message?: string }> {
const formData = new FormData()
formData.set('class_section_id', String(payload.class_section_id))
formData.set('school_year', payload.school_year)
formData.set('semester', payload.semester)
if (payload.exam_type) formData.set('exam_type', payload.exam_type)
formData.set('old_exam_file', payload.old_exam_file)
return fetchMultipartJson('/api/v1/exams/drafts/admin/legacy', formData)
}
export async function reviewExamDraft(payload: {
draft_id: number
review_status?: string
admin_comments?: string
final_file?: File | null
}): Promise<{ ok: boolean; message?: string }> {
const formData = new FormData()
formData.set('draft_id', String(payload.draft_id))
if (payload.review_status) formData.set('review_status', payload.review_status)
if (payload.admin_comments) formData.set('admin_comments', payload.admin_comments)
if (payload.final_file) formData.set('final_file', payload.final_file)
return fetchMultipartJson('/api/v1/exams/drafts/admin/review', formData)
}
export async function fetchGradingOverview(params?: {
classId?: number | null
semester?: string | null
schoolYear?: string | null
}): Promise<GradingOverviewResponse> {
const query = new URLSearchParams()
if (params?.classId) query.set('class_id', String(params.classId))
if (params?.semester) query.set('semester', params.semester)
if (params?.schoolYear) query.set('school_year', params.schoolYear)
const qs = query.size > 0 ? `?${query.toString()}` : ''
return apiFetch<GradingOverviewResponse>(`/api/v1/grading/overview${qs}`)
}
export async function fetchIpBans(params?: {
status?: 'all' | 'active'
page?: number
perPage?: number
}): Promise<IpBansResponse> {
const query = new URLSearchParams()
if (params?.status) query.set('status', params.status)
if (params?.page) query.set('page', String(params.page))
if (params?.perPage) query.set('per_page', String(params.perPage))
const qs = query.size > 0 ? `?${query.toString()}` : ''
return apiFetch<IpBansResponse>(`/api/v1/ip-bans${qs}`)
}
export async function banIp(payload: {
id?: number
ip?: string
hours?: number
}): Promise<ApiEnvelope<{ ban?: unknown }>> {
return apiFetch<ApiEnvelope<{ ban?: unknown }>>('/api/v1/ip-bans/ban', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function unbanIp(payload: {
id?: number
ip?: string
all?: boolean
}): Promise<ApiEnvelope<{ ban?: unknown; count?: number }>> {
return apiFetch<ApiEnvelope<{ ban?: unknown; count?: number }>>('/api/v1/ip-bans/unban', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function fetchLateSlipLogs(params?: {
schoolYear?: string | null
semester?: string | null
q?: string
dateFrom?: string
dateTo?: string
page?: number
perPage?: number
}): Promise<LateSlipLogsResponse> {
const query = new URLSearchParams()
if (params?.schoolYear) query.set('school_year', params.schoolYear)
if (params?.semester) query.set('semester', params.semester)
if (params?.q) query.set('q', params.q)
if (params?.dateFrom) query.set('date_from', params.dateFrom)
if (params?.dateTo) query.set('date_to', params.dateTo)
if (params?.page) query.set('page', String(params.page))
if (params?.perPage) query.set('per_page', String(params.perPage))
const qs = query.size > 0 ? `?${query.toString()}` : ''
return apiFetch<LateSlipLogsResponse>(`/api/v1/attendance/late-slip-logs${qs}`)
}
export async function deleteLateSlipLog(id: number): Promise<ApiEnvelope<null>> {
return apiFetch<ApiEnvelope<null>>(`/api/v1/attendance/late-slip-logs/${id}`, {
method: 'DELETE',
})
}
export async function fetchUsers(params?: {
sort?: string
order?: 'asc' | 'desc'
}): Promise<UsersResponse> {
const query = new URLSearchParams()
if (params?.sort) query.set('sort', params.sort)
if (params?.order) query.set('order', params.order)
const qs = query.size > 0 ? `?${query.toString()}` : ''
return apiFetch<UsersResponse>(`/api/v1/users${qs}`)
}
export async function deleteUser(userId: number): Promise<{ ok: boolean; message?: string }> {
return apiFetch<{ ok: boolean; message?: string }>(`/api/v1/users/${userId}`, {
method: 'DELETE',
})
}
export async function fetchNotificationAlerts(): Promise<NotificationAlertsResponse> {
return apiFetch<NotificationAlertsResponse>('/api/v1/administrator/notifications/alerts')
}
export async function saveNotificationAlerts(
subjects: Record<string, string[]>,
): Promise<{ message?: string }> {
return apiFetch<{ message?: string }>('/api/v1/administrator/notifications/alerts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ subjects }),
})
}
export async function fetchPrintNotificationRecipients(): Promise<PrintNotificationRecipientsResponse> {
return apiFetch<PrintNotificationRecipientsResponse>(
'/api/v1/administrator/notifications/print-recipients',
)
}
export async function savePrintNotificationRecipients(
notify: Record<string, number | boolean>,
): Promise<{ message?: string }> {
return apiFetch<{ message?: string }>(
'/api/v1/administrator/notifications/print-recipients',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ notify }),
},
)
}
export async function fetchFamilyAdminIndex(params?: {
studentId?: number | null
guardianId?: number | null
}): Promise<FamilyAdminIndexResponse> {
const query = new URLSearchParams()
if (params?.studentId) query.set('student_id', String(params.studentId))
if (params?.guardianId) query.set('guardian_id', String(params.guardianId))
const qs = query.size > 0 ? `?${query.toString()}` : ''
return apiFetch<FamilyAdminIndexResponse>(`/api/v1/family-admin${qs}`)
}
export async function fetchPaypalTransactions(params?: {
q?: string
perPage?: number
}): Promise<PaypalTransactionsResponse> {
const query = new URLSearchParams()
if (params?.q) query.set('q', params.q)
if (params?.perPage) query.set('per_page', String(params.perPage))
const qs = query.size > 0 ? `?${query.toString()}` : ''
return apiFetch<PaypalTransactionsResponse>(`/api/v1/paypal-transactions${qs}`)
}
export async function fetchPaypalTransactionsCsv(params?: { q?: string }): Promise<Blob> {
const headers = new Headers({ Accept: 'text/csv' })
const token = getStoredToken()
if (token) headers.set('Authorization', `Bearer ${token}`)
const query = new URLSearchParams()
if (params?.q) query.set('q', params.q)
const suffix = query.size > 0 ? `?${query.toString()}` : ''
const res = await fetch(apiUrl(`/api/v1/paypal-transactions/csv${suffix}`), { headers })
if (!res.ok) throw new Error(`HTTP ${res.status}`)
return res.blob()
}
export async function fetchNavMenu(): Promise<NavMenuResponse> {
return apiFetch<NavMenuResponse>('/api/v1/nav-builder/menu')
}
@@ -208,14 +943,95 @@ export async function fetchReportCardAcknowledgement(
)
}
export async function fetchClassProgressGroups(): Promise<ClassProgressGroupsResponse> {
return apiFetch<ClassProgressGroupsResponse>('/api/v1/class-progress?group_by_week=1')
export async function fetchClassProgressGroups(params?: {
classSectionId?: number | null
status?: string | null
weekStart?: string | null
weekEnd?: string | null
subject?: string | null
page?: number
perPage?: number
}): Promise<ClassProgressGroupsResponse> {
const query = new URLSearchParams()
query.set('group_by_week', '1')
if (params?.classSectionId) query.set('class_section_id', String(params.classSectionId))
if (params?.status) query.set('status', params.status)
if (params?.weekStart) query.set('week_start', params.weekStart)
if (params?.weekEnd) query.set('week_end', params.weekEnd)
if (params?.subject) query.set('subject', params.subject)
if (params?.page) query.set('page', String(params.page))
if (params?.perPage) query.set('per_page', String(params.perPage))
return apiFetch<ClassProgressGroupsResponse>(`/api/v1/class-progress?${query.toString()}`)
}
export async function fetchClassProgressDetail(id: number): Promise<ClassProgressShowResponse> {
return apiFetch<ClassProgressShowResponse>(`/api/v1/class-progress/${id}`)
}
export async function fetchClassProgressMeta(params?: {
classId?: number | null
sundayCount?: number | null
}): Promise<ClassProgressMetaResponse> {
const query = new URLSearchParams()
if (params?.classId) query.set('class_id', String(params.classId))
if (params?.sundayCount) query.set('sunday_count', String(params.sundayCount))
const qs = query.size > 0 ? `?${query.toString()}` : ''
return apiFetch<ClassProgressMetaResponse>(`/api/v1/class-progress/meta${qs}`)
}
export async function fetchStudentScoreCard(studentId: number): Promise<StudentScoreCardResponse> {
return apiFetch<StudentScoreCardResponse>(`/api/v1/students/${studentId}/score-card`)
}
export async function fetchAdministratorEmergencyContacts(params?: {
parentId?: number | null
parentIds?: number[]
}): Promise<EmergencyContactGroupsResponse> {
const query = new URLSearchParams()
if (params?.parentId) query.set('parent_id', String(params.parentId))
for (const id of params?.parentIds ?? []) {
query.append('parent_ids[]', String(id))
}
const qs = query.size > 0 ? `?${query.toString()}` : ''
return apiFetch<EmergencyContactGroupsResponse>(`/api/v1/administrator/emergency-contacts${qs}`)
}
export async function fetchAdministratorEmergencyContact(
contactId: number,
parentId: number,
): Promise<EmergencyContactShowResponse> {
return apiFetch<EmergencyContactShowResponse>(
`/api/v1/administrator/emergency-contacts/${contactId}?parent_id=${encodeURIComponent(String(parentId))}`,
)
}
export async function updateAdministratorEmergencyContact(
contactId: number,
payload: {
parent_id: number
name: string
cellphone: string
email?: string | null
relation?: string | null
},
): Promise<EmergencyContactShowResponse> {
return apiFetch<EmergencyContactShowResponse>(`/api/v1/administrator/emergency-contacts/${contactId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export async function deleteAdministratorEmergencyContact(
contactId: number,
parentId: number,
): Promise<{ ok: true }> {
return apiFetch<{ ok: true }>(
`/api/v1/administrator/emergency-contacts/${contactId}?parent_id=${encodeURIComponent(String(parentId))}`,
{ method: 'DELETE' },
)
}
export async function updateParentProfile(
payload: Required<
Pick<
+838 -2
View File
@@ -87,6 +87,120 @@ export type NavBuilderDataResponse = ApiEnvelope<{ data: NavBuilderData }>
export type NavBuilderSaveResponse = ApiEnvelope<{ id: number }>
export type NavBuilderDeleteResponse = ApiEnvelope<null>
export type AdministratorDashboardMetricsResponse = {
counts?: {
students?: number
teachers?: number
teacherAssistants?: number
admins?: number
parents?: number
}
recentActivities?: Array<{
login_time?: string | null
email?: string | null
}>
meta?: {
schoolYear?: string | null
semester?: string | null
}
}
export type AdministratorDashboardSearchResponse = {
query: string
results: {
users?: Record<string, unknown>[]
students?: Record<string, unknown>[]
parents?: Record<string, unknown>[]
staff?: Record<string, unknown>[]
emergency_contacts?: Record<string, unknown>[]
}
scope_used?: string
scope_label?: string
total_found?: number
}
export type AdministratorAbsenceRow = {
date?: string | null
status?: string | null
reason?: string | null
}
export type AdministratorAbsenceFormResponse = {
admin_name?: string | null
semester?: string | null
schoolYear?: string | null
existing?: AdministratorAbsenceRow[]
availableDates?: string[]
}
export type AdministratorAbsenceSubmitResponse = {
message?: string
saved?: number
dates?: string[]
}
export type AdministratorDailyAttendanceEntry = {
date?: string | null
status?: string | null
reason?: string | null
is_reported?: string | null
modified_by?: string | number | null
}
export type AdministratorDailyAttendanceSummary = {
total_presence?: number
total_late?: number
total_absence?: number
total_attendance?: number
}
export type AdministratorDailyAttendanceStudent = {
id: number
firstname?: string | null
lastname?: string | null
school_id?: string | null
}
export type AdministratorDailyAttendanceSection = {
id?: number
class_id?: number
class_section_id?: number | string
class_section_name?: string | null
}
export type AdministratorDailyAttendanceResponse = {
attendance_data?: Record<string, Record<string, AdministratorDailyAttendanceEntry[]>>
attendance_record?: Record<string, Record<string, AdministratorDailyAttendanceSummary>>
students_by_section?: Record<string, AdministratorDailyAttendanceStudent[]>
grades?: Record<string, AdministratorDailyAttendanceSection[]>
dates_by_section?: Record<string, string[]>
date_list?: string[]
no_school_days?: Record<string, boolean>
total_passed_days?: number
semester?: string | null
school_year?: string | null
current_admin_name?: string | null
}
export type AdministratorAdminAttendanceRow = {
user_id?: number
firstname?: string | null
lastname?: string | null
email?: string | null
role_name?: string | null
role_slug?: string | null
status?: string | null
reason?: string | null
date?: string | null
}
export type AdministratorAdminAttendanceResponse = {
semester?: string | null
school_year?: string | null
date?: string | null
admins_grid?: AdministratorAdminAttendanceRow[]
}
export type ParentAttendanceRow = {
firstname: string
lastname: string
@@ -158,6 +272,646 @@ export type ContactSubmitResponse = {
message?: string
}
export type BroadcastEmailParentOption = {
id: number
firstname?: string | null
lastname?: string | null
email?: string | null
school_id?: string | null
}
export type BroadcastEmailSenderOption = {
key: string
label: string
}
export type BroadcastEmailOptionsResponse = {
ok: true
parents: BroadcastEmailParentOption[]
fromOptions: BroadcastEmailSenderOption[]
}
export type BroadcastEmailSendResponse = {
ok: boolean
message?: string
stats?: {
mode?: string
sent?: number
attempted?: number
failed?: number
}
}
export type SchoolCalendarDetailRow = {
id: number
title: string
description?: string
event_type?: string | null
date: string
notify_parent?: number
notify_teacher?: number
notify_admin?: number
no_school?: number
semester?: string
school_year?: string
created_at?: string | null
updated_at?: string | null
}
export type SchoolCalendarOptionsResponse = ApiEnvelope<{
event_types: string[]
defaults: {
school_year?: string | null
semester?: string | null
}
}>
export type SchoolCalendarListResponse = ApiEnvelope<{
events: SchoolCalendarEventRow[]
}>
export type SchoolCalendarShowResponse = ApiEnvelope<{
event: SchoolCalendarDetailRow
}>
export type ClassSectionRow = {
id: number
class_id?: number | null
class_section_id?: number | null
class_section_name?: string | null
class_name?: string | null
semester?: string | null
school_year?: string | null
created_at?: string | null
updated_at?: string | null
}
export type ClassSectionsResponse = ApiEnvelope<{
sections: ClassSectionRow[]
meta?: {
current_page?: number
per_page?: number
total?: number
last_page?: number
}
}>
export type ClassSectionShowResponse = ApiEnvelope<{
section: ClassSectionRow
}>
export type StudentAssignmentRow = {
student_id: number
name: string
age?: number | string | null
email?: string | null
phone?: string | null
registration_grade?: string | null
class_section_name?: string | null
class_section_names?: string[]
class_section_ids?: number[]
new_student?: string | null
registration_date?: string | null
school_year?: string | null
semester?: string | null
}
export type StudentAssignmentOption = {
id: number
class_section_id: number
class_section_name: string
school_year?: string | null
}
export type StudentAssignmentsResponse = {
ok: true
students: StudentAssignmentRow[]
classes: StudentAssignmentOption[]
schoolYears: string[]
selectedYear?: string | null
currentYear?: string | null
isCurrentYear?: boolean
}
export type ExamDraftRow = {
id: number
teacher_id: number
class_section_id: number
class_section_name?: string | null
semester?: string | null
school_year?: string | null
exam_type?: string | null
draft_title?: string | null
description?: string | null
teacher_file?: string | null
teacher_filename?: string | null
status?: string | null
admin_id?: number | null
admin_comments?: string | null
reviewed_at?: string | null
final_file?: string | null
final_filename?: string | null
final_pdf_file?: string | null
version?: number | null
previous_draft_id?: number | null
is_legacy?: boolean | number | null
teacher_first?: string | null
teacher_last?: string | null
admin_first?: string | null
admin_last?: string | null
}
export type ExamDraftAdminResponse = {
ok: true
drafts: ExamDraftRow[]
class_sections: Array<{ class_section_id: number; class_section_name: string }>
legacy_by_class: Record<
string,
{
class_section_id: number
class_section_name: string
items: ExamDraftRow[]
}
>
exam_types: string[]
status_options: string[]
school_year?: string | null
semester?: string | null
allowed_extensions?: string[]
max_upload_bytes?: number
}
export type GradingOverviewStudentRow = {
id: number
school_id?: string | null
firstname?: string | null
lastname?: string | null
class_id?: number
ptap?: number | string | null
semester_score?: number | string | null
attendance?: number | string | null
homework_avg?: number | string | null
project_avg?: number | string | null
quiz_avg?: number | string | null
participation?: number | string | null
midterm_exam?: number | string | null
final_exam?: number | string | null
matched_biz_csid?: number | null
matched_pk_csid?: number | null
placement_level?: string | null
}
export type GradingOverviewResponse = {
ok: true
grades: Record<string, Array<{ class_section_id: number; class_section_name: string }>>
students_by_section: Record<string, GradingOverviewStudentRow[]>
semester?: string | null
school_year?: string | null
requested_class_id?: number | null
semester_options?: string[]
scores_released?: boolean
scores_released_fall?: boolean
scores_released_spring?: boolean
score_locks?: Record<string, boolean>
}
export type IpBanRow = {
id: number
ip_address?: string | null
attempts?: number
last_attempt_at?: string | null
blocked_until?: string | null
is_active?: boolean
created_at?: string | null
updated_at?: string | null
}
export type IpBansResponse = ApiEnvelope<{
bans: IpBanRow[]
meta?: {
current_page?: number
per_page?: number
total?: number
last_page?: number
}
}>
export type LateSlipLogRow = {
id: number
school_year?: string | null
semester?: string | null
student_name?: string | null
slip_date?: string | null
time_in?: string | null
grade?: string | null
reason?: string | null
admin_name?: string | null
printed_by?: string | null
printed_at?: string | null
created_at?: string | null
updated_at?: string | null
}
export type LateSlipLogsResponse = ApiEnvelope<{
logs: LateSlipLogRow[]
meta?: {
current_page?: number
per_page?: number
total?: number
last_page?: number
}
}>
export type UserListRow = {
user?: {
id?: number
firstname?: string | null
lastname?: string | null
email?: string | null
status?: string | null
cellphone?: string | null
gender?: string | null
address_street?: string | null
city?: string | null
state?: string | null
zip?: string | null
school_year?: string | null
semester?: string | null
[key: string]: unknown
} | null
roles?: Array<{ id?: number; name?: string | null; slug?: string | null }>
role_ids?: number[]
parent_type?: string
second_from_parents?: string
}
export type UsersResponse = {
ok: true
users: UserListRow[]
}
export type NotificationAdminRow = {
id: number
firstname?: string | null
lastname?: string | null
email?: string | null
}
export type NotificationAlertsResponse = {
admins: NotificationAdminRow[]
subjects: Record<string, string>
assignedSubjects: Record<string, Record<string, boolean>>
tableReady: boolean
}
export type PrintNotificationRecipientsResponse = {
admins: NotificationAdminRow[]
assigned: Record<string, boolean>
tableReady: boolean
}
export type FamilyGuardianRow = {
user_id?: number
firstname?: string | null
lastname?: string | null
email?: string | null
cellphone?: string | null
relation?: string | null
is_primary?: boolean
receive_emails?: boolean
receive_sms?: boolean
}
export type FamilyStudentRow = {
id?: number
firstname?: string | null
lastname?: string | null
school_id?: string | null
class_section_name?: string | null
[key: string]: unknown
}
export type FamilyRow = {
id: number
family_code?: string
household_name?: string
address_line1?: string
address_line2?: string
city?: string
state?: string
postal_code?: string
country?: string
primary_phone?: string
preferred_lang?: string
preferred_contact_method?: string
is_active?: boolean
is_primary_home?: boolean
guardians?: FamilyGuardianRow[]
students?: FamilyStudentRow[]
[key: string]: unknown
}
export type FamilyAdminIndexResponse = ApiEnvelope<{
student?: { id?: number; firstname?: string | null; lastname?: string | null } | null
families: FamilyRow[]
guardians: Record<string, unknown>[]
students: Record<string, unknown>[]
searchStudents: Record<string, unknown>[]
searchGuardians: NotificationAdminRow[]
resolved_student_id?: number | null
}>
export type PaypalTransactionRow = {
id: number
transaction_id?: string | null
order_id?: string | null
parent_school_id?: string | null
payer_email?: string | null
amount?: number
net_amount?: number
currency?: string | null
status?: string | null
event_type?: string | null
created_at?: string | null
}
export type PaypalTransactionsResponse = {
ok: true
transactions: PaypalTransactionRow[]
pagination?: {
current_page?: number
per_page?: number
total?: number
last_page?: number
}
}
export type RemovedStudentRow = {
id: number
school_id?: string | null
firstname?: string | null
lastname?: string | null
gender?: string | null
age?: number | string | null
class_sections?: string[]
class_section_name?: string | null
}
export type RemovedStudentsResponse = {
ok: true
active_students: RemovedStudentRow[]
removed_students: RemovedStudentRow[]
school_year?: string | null
}
export type StudentPromotionTotalsRow = {
class_id?: number | null
class_section_id?: number | null
class_section_name?: string | null
total?: number
}
export type StudentPromotionTotalsResponse = {
ok: true
year?: string | null
rows: StudentPromotionTotalsRow[]
}
export type StudentDirectoryRow = {
id: number
school_id?: string | null
firstname?: string | null
lastname?: string | null
dob?: string | null
age?: number | null
gender?: string | null
is_active?: boolean | number | null
registration_grade?: string | null
is_new?: boolean | number | null
photo_consent?: boolean | number | null
parent_id?: number | null
registration_date?: string | null
tuition_paid?: boolean | number | null
semester?: string | null
year_of_registration?: string | number | null
school_year?: string | null
medical_conditions?: string | null
allergies?: string | null
}
export type StudentDirectoryResponse = {
ok: true
students: StudentDirectoryRow[]
school_year?: string | null
current_year?: string | null
}
export type StudentParentResponse = {
ok: true
student_id: number
parent?: {
id?: number
firstname?: string | null
lastname?: string | null
email?: string | null
cellphone?: string | null
} | null
}
export type StudentEmergencyContactResponse = {
ok: true
student_id: number
rows: Array<Record<string, unknown>>
}
export type TeacherClassAssignmentRow = {
teacher_id: number
firstname?: string | null
lastname?: string | null
name?: string | null
email?: string | null
cellphone?: string | null
role?: string | null
school_year?: string | null
main_assignments?: Array<{
section_id?: number
class_section_name?: string | null
role?: string | null
semester?: string | null
school_year?: string | null
}>
ta_assignments?: Array<{
section_id?: number
class_section_name?: string | null
role?: string | null
semester?: string | null
school_year?: string | null
}>
}
export type TeacherClassOption = {
class_section_id: number
class_section_name: string
class_id?: number | null
class_name?: string | null
teacher_role?: string | null
school_year?: string | null
semester?: string | null
}
export type TeacherClassAssignmentsResponse = {
ok: true
school_year?: string | null
teachers: TeacherClassAssignmentRow[]
classes: TeacherClassOption[]
}
export type TeacherSubmissionTeacherRow = {
id: number
label: string
role_key?: string | null
}
export type TeacherSubmissionReportRow = {
class_section?: string | null
class_section_id?: number
teachers?: TeacherSubmissionTeacherRow[]
midterm_score_status?: string | null
midterm_comment_status?: string | null
participation_status?: string | null
ptap_comment_status?: string | null
attendance_status?: string | null
missing_items?: string[]
student_count?: number
}
export type TeacherSubmissionsResponse = {
rows: TeacherSubmissionReportRow[]
semester?: string | null
schoolYear?: string | null
notificationHistory?: Record<string, unknown>
summary?: {
total_items?: number
missing_items?: number
submitted_items?: number
submission_percentage?: number
}
}
export type SubjectCurriculumClassRow = {
id: number
class_name: string
school_year?: string | null
}
export type SubjectCurriculumEntryRow = {
id: number
class_id: number
class_name?: string | null
subject: string
unit_number?: number | string | null
unit_title?: string | null
chapter_name: string
}
export type SubjectCurriculumResponse = {
ok: true
classes: SubjectCurriculumClassRow[]
entries: SubjectCurriculumEntryRow[]
subject_labels: Record<string, string>
}
export type PublicCompetitionRow = {
id: number
title?: string | null
class_section_id?: number | null
semester?: string | null
school_year?: string | null
is_published?: boolean | number | null
is_locked?: boolean | number | null
published_at?: string | null
[key: string]: unknown
}
export type PublicCompetitionIndexResponse = {
status: boolean
data?: {
competitions: PublicCompetitionRow[]
}
message?: string
}
export type PublicCompetitionWinnerRow = {
student_id?: number
class_section_id?: number
rank?: number
score?: number | string | null
prize_amount?: number | string | null
firstname?: string | null
lastname?: string | null
class_section_name?: string | null
name?: string | null
[key: string]: unknown
}
export type PublicCompetitionShowResponse = {
status: boolean
data?: {
competition: PublicCompetitionRow
winners_by_class: Record<string, PublicCompetitionWinnerRow[]>
section_map: Record<string, string>
question_counts: Record<string, number>
}
message?: string
}
export type CompetitionScoresIndexCompetitionRow = {
id: number
title?: string | null
class_section_id?: number | null
is_locked?: boolean | number | null
is_published?: boolean | number | null
[key: string]: unknown
}
export type CompetitionScoresIndexResponse = {
ok: true
competitions: CompetitionScoresIndexCompetitionRow[]
activeClassId?: number
activeClassName?: string | null
questionCounts?: Record<string, number>
scoreCounts?: Record<string, number>
studentTotal?: number
sectionMap?: Record<string, string>
schoolYear?: string | null
semester?: string | null
hasClasses?: boolean
}
export type CompetitionScoreStudentRow = {
id?: number
student_id?: number
school_id?: string | null
firstname?: string | null
lastname?: string | null
}
export type CompetitionScoresEditResponse = {
ok: boolean
competition?: PublicCompetitionRow
students?: CompetitionScoreStudentRow[]
scoreMap?: Record<string, number | string>
classSectionId?: number
classSectionName?: string | null
classStudentCount?: number
questionCount?: number | null
classSelectionLocked?: boolean
isLocked?: boolean
schoolYear?: string | null
semester?: string | null
message?: string
}
/** Loose JSON payloads for complex parent dashboards (events, registration, attendance report form). */
export type ParentEventsResponse = Record<string, unknown>
export type ParentRegistrationResponse = Record<string, unknown>
@@ -259,6 +1013,36 @@ export type StudentScoresResponse = {
rows: StudentScoreRow[]
}
export type StudentScoreCardStudent = {
id: number
firstname?: string | null
lastname?: string | null
school_id?: string | null
}
export type StudentScoreCardRow = {
school_year?: string | null
semester?: string | null
homework_avg?: number | string | null
project_avg?: number | string | null
participation_score?: number | string | null
quiz_avg?: number | string | null
test_avg?: number | string | null
attendance_score?: number | string | null
ptap_score?: number | string | null
midterm_exam_score?: number | string | null
semester_score?: number | string | null
class_section_name?: string | null
comments?: Record<string, string>
year_score?: number | string | null
}
export type StudentScoreCardResponse = {
ok: true
student: StudentScoreCardStudent
rows: StudentScoreCardRow[]
}
export type ReportCardStudentMetaRow = {
id: number
firstname: string
@@ -289,6 +1073,7 @@ export type ClassProgressReportRow = {
id: number
teacher_name?: string | null
class_section_name?: string | null
class_section_id?: number | null
subject?: string | null
unit_title?: string | null
status?: string | null
@@ -296,9 +1081,12 @@ export type ClassProgressReportRow = {
covered?: string | null
homework?: string | null
support_needed?: string | null
flags?: string[]
week_start?: string | null
week_end?: string | null
attachments?: { id?: number; original_name?: string | null; url?: string | null }[]
created_at?: string | null
updated_at?: string | null
attachments?: { id?: number; name?: string | null; file_path?: string | null; download_url?: string | null }[]
}
export type ClassProgressGroupRow = {
@@ -316,10 +1104,58 @@ export type ClassProgressGroupsResponse = ApiEnvelope<{
export type ClassProgressShowResponse = ApiEnvelope<{
report?: ClassProgressReportRow
weekly_reports?: Record<string, ClassProgressReportRow>
weekly_reports?: ClassProgressReportRow[]
status_options?: Record<string, string>
}>
export type ClassProgressMetaResponse = ApiEnvelope<{
subject_sections?: Record<string, { label?: string; db_subject?: string }>
status_options?: Record<string, string>
sunday_options?: string[]
curriculum?: Record<string, Array<Record<string, unknown>>>
}>
export type EmergencyContactGroupRow = {
parent_id: number
parent_name?: string | null
parent_phones?: string[]
students?: Array<{
id?: number
student_id?: number
firstname?: string | null
lastname?: string | null
}>
contacts?: Array<{
id: number
parent_id?: number
name?: string | null
cellphone?: string | null
email?: string | null
relation?: string | null
semester?: string | null
school_year?: string | null
}>
}
export type EmergencyContactGroupsResponse = {
ok: true
groups: EmergencyContactGroupRow[]
}
export type EmergencyContactShowResponse = {
ok: true
contact: {
id: number
parent_id: number
name?: string | null
cellphone?: string | null
email?: string | null
relation?: string | null
semester?: string | null
school_year?: string | null
}
}
export type ParentRegistrationOverviewResponse = {
ok: true
parent: ParentProfileRecord | null