| 1 | import { apiUrl } from "@/lib/api";
|
|---|
| 2 | import { getAccessToken } from "@/lib/auth";
|
|---|
| 3 |
|
|---|
| 4 | export type AdminResult = { ok: boolean; message?: string; id?: number | null };
|
|---|
| 5 |
|
|---|
| 6 | export type SubjectRef = { id: number; name?: string; code?: string | null };
|
|---|
| 7 |
|
|---|
| 8 | export type SubjectMajor = {
|
|---|
| 9 | majorId: number;
|
|---|
| 10 | majorName?: string;
|
|---|
| 11 | mandatorySemester: number;
|
|---|
| 12 | };
|
|---|
| 13 |
|
|---|
| 14 | export type AdminSubject = {
|
|---|
| 15 | id: number;
|
|---|
| 16 | name?: string;
|
|---|
| 17 | code?: string;
|
|---|
| 18 | awardedCredits: number;
|
|---|
| 19 | dependencyCredit?: number | null;
|
|---|
| 20 | majors: SubjectMajor[];
|
|---|
| 21 | prerequisites: SubjectRef[];
|
|---|
| 22 | enrolledCount: number;
|
|---|
| 23 | };
|
|---|
| 24 |
|
|---|
| 25 | export type AdminSemester = {
|
|---|
| 26 | id: number;
|
|---|
| 27 | year: number;
|
|---|
| 28 | type?: string;
|
|---|
| 29 | name?: string;
|
|---|
| 30 | enrolmentCount: number;
|
|---|
| 31 | uncoveredSubjects: SubjectRef[];
|
|---|
| 32 | };
|
|---|
| 33 |
|
|---|
| 34 | export type ScheduleRow = {
|
|---|
| 35 | subjectId: number;
|
|---|
| 36 | subjectName?: string;
|
|---|
| 37 | subjectCode?: string;
|
|---|
| 38 | professorId: number;
|
|---|
| 39 | professorName?: string;
|
|---|
| 40 | };
|
|---|
| 41 |
|
|---|
| 42 | export type ProfessorLoad = {
|
|---|
| 43 | professorId: number;
|
|---|
| 44 | professorName?: string;
|
|---|
| 45 | subjects: number;
|
|---|
| 46 | };
|
|---|
| 47 |
|
|---|
| 48 | export type Schedule = {
|
|---|
| 49 | semesterId: number;
|
|---|
| 50 | semesterName?: string;
|
|---|
| 51 | assignments: ScheduleRow[];
|
|---|
| 52 | load: ProfessorLoad[];
|
|---|
| 53 | uncoveredSubjects: SubjectRef[];
|
|---|
| 54 | allSubjects: SubjectRef[];
|
|---|
| 55 | professors: { id: number; name?: string }[];
|
|---|
| 56 | };
|
|---|
| 57 |
|
|---|
| 58 | function authHeaders(): Record<string, string> {
|
|---|
| 59 | const token = getAccessToken();
|
|---|
| 60 | if (!token) throw new Error("You are not signed in.");
|
|---|
| 61 | return { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
|
|---|
| 62 | }
|
|---|
| 63 |
|
|---|
| 64 | /** GET that throws with the server message rather than a bare status code. */
|
|---|
| 65 | export async function adminGet<T>(path: string): Promise<T> {
|
|---|
| 66 | const res = await fetch(apiUrl(path), { cache: "no-store", headers: authHeaders() });
|
|---|
| 67 | if (!res.ok) {
|
|---|
| 68 | const body = await res.json().catch(() => null);
|
|---|
| 69 | throw new Error(body?.message || `Request failed (${res.status})`);
|
|---|
| 70 | }
|
|---|
| 71 | return (await res.json()) as T;
|
|---|
| 72 | }
|
|---|
| 73 |
|
|---|
| 74 | /** POST/PUT/DELETE returning the standard {ok, message} envelope. */
|
|---|
| 75 | export async function adminSend(
|
|---|
| 76 | path: string,
|
|---|
| 77 | method: "POST" | "PUT" | "DELETE",
|
|---|
| 78 | body?: unknown,
|
|---|
| 79 | ): Promise<AdminResult> {
|
|---|
| 80 | const res = await fetch(apiUrl(path), {
|
|---|
| 81 | method,
|
|---|
| 82 | headers: authHeaders(),
|
|---|
| 83 | body: body === undefined ? undefined : JSON.stringify(body),
|
|---|
| 84 | });
|
|---|
| 85 | const result = (await res.json().catch(() => null)) as AdminResult | null;
|
|---|
| 86 | if (!res.ok || !result?.ok) {
|
|---|
| 87 | throw new Error(result?.message || `Request failed (${res.status})`);
|
|---|
| 88 | }
|
|---|
| 89 | return result;
|
|---|
| 90 | }
|
|---|