diff --git a/backend/alembic/versions/001_initial_schema.py b/backend/alembic/versions/001_initial_schema.py
index a7e059b..bd03ce0 100644
--- a/backend/alembic/versions/001_initial_schema.py
+++ b/backend/alembic/versions/001_initial_schema.py
@@ -87,4 +87,4 @@ def downgrade() -> None:
op.drop_table("sections")
op.drop_table("students")
op.drop_table("instructors")
- op.drop_table("time_blocks")
+ op.drop_table("time_blocks")
\ No newline at end of file
diff --git a/backend/alembic/versions/002_initial_schema.py b/backend/alembic/versions/002_initial_schema.py
new file mode 100644
index 0000000..61f637c
--- /dev/null
+++ b/backend/alembic/versions/002_initial_schema.py
@@ -0,0 +1,55 @@
+"""initial schema
+
+Revision ID: 002
+Revises:
+Create Date: 2026-05-06
+
+"""
+
+from typing import Sequence, Union
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+revision: str = "002"
+down_revision: Union[str, None] = "001"
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade() -> None:
+ # Drop the old FK constraint
+ op.drop_constraint(
+ "sections_instructor_id_fkey",
+ "sections",
+ type_="foreignkey"
+ )
+
+ # Create the new FK with CASCADE
+ op.create_foreign_key(
+ None, # Let Alembic generate a name
+ "sections", # Source table
+ "instructors", # Referenced table
+ ["instructor_id"], # Local column
+ ["id"], # Remote column
+ ondelete="CASCADE"
+ )
+
+
+def downgrade() -> None:
+ # Reverse the change: drop CASCADE FK
+ op.drop_constraint(
+ None,
+ "sections",
+ type_="foreignkey"
+ )
+
+ # Restore original FK without CASCADE
+ op.create_foreign_key(
+ "sections_instructor_id_fkey",
+ "sections",
+ "instructors",
+ ["instructor_id"],
+ ["id"]
+ )
\ No newline at end of file
diff --git a/backend/stp_scheduler/db/models/association.py b/backend/stp_scheduler/db/models/association.py
index 02ba134..da0e705 100644
--- a/backend/stp_scheduler/db/models/association.py
+++ b/backend/stp_scheduler/db/models/association.py
@@ -5,6 +5,6 @@
student_section_table = Table(
"student_sections",
Base.metadata,
- Column("student_id", String, ForeignKey("students.id"), primary_key=True),
- Column("section_id", String, ForeignKey("sections.id"), primary_key=True),
+ Column("student_id", String, ForeignKey("students.id", ondelete="CASCADE"), primary_key=True),
+ Column("section_id", String, ForeignKey("sections.id", ondelete="CASCADE"), primary_key=True),
)
diff --git a/backend/stp_scheduler/db/models/section.py b/backend/stp_scheduler/db/models/section.py
index 867dd7f..cbb7b0e 100644
--- a/backend/stp_scheduler/db/models/section.py
+++ b/backend/stp_scheduler/db/models/section.py
@@ -15,5 +15,5 @@ class SectionRow(Base):
)
days: Mapped[str | None] = mapped_column(String, nullable=True)
instructor_id: Mapped[str | None] = mapped_column(
- String, ForeignKey("instructors.id"), nullable=True
+ String, ForeignKey("instructors.id", ondelete="SET NULL"), nullable=True
)
diff --git a/frontend/app/Components/InputPage.tsx b/frontend/app/Components/InputPage.tsx
index 082f129..7c28b0f 100644
--- a/frontend/app/Components/InputPage.tsx
+++ b/frontend/app/Components/InputPage.tsx
@@ -140,7 +140,7 @@ export default function InputPage({ path }: InputPageProps) {
>
-
+
);
}
diff --git a/frontend/app/Components/Navbar.tsx b/frontend/app/Components/Navbar.tsx
index 70ef413..a64c08f 100644
--- a/frontend/app/Components/Navbar.tsx
+++ b/frontend/app/Components/Navbar.tsx
@@ -30,7 +30,9 @@ export default function Navbar(){
-
+
+
+
{navItems.map((navItem, index) => (
-
@@ -38,9 +40,9 @@ export default function Navbar(){
))}
- |
- - |
+
);
}
\ No newline at end of file
diff --git a/frontend/app/Components/NavbarAuthControls.tsx b/frontend/app/Components/NavbarAuthControls.tsx
index 74c0c22..49530b9 100644
--- a/frontend/app/Components/NavbarAuthControls.tsx
+++ b/frontend/app/Components/NavbarAuthControls.tsx
@@ -3,6 +3,7 @@
import Link from "next/link";
import { useEffect, useState } from "react";
import { API_URL, getToken, setToken } from "../apiClient";
+import NavItem from "./Navitem";
export default function NavbarAuthControls() {
const [loggedIn, setLoggedIn] = useState(false);
@@ -38,20 +39,18 @@ export default function NavbarAuthControls() {
}
return (
-
+
{loggedIn ? (
) : (
-
- Sign in
-
+
)}
-
+
);
}
diff --git a/frontend/app/Cruds/deleteStudent.tsx b/frontend/app/Cruds/deleteStudent.tsx
index ca4b407..3a8d586 100644
--- a/frontend/app/Cruds/deleteStudent.tsx
+++ b/frontend/app/Cruds/deleteStudent.tsx
@@ -28,7 +28,7 @@ export default function DeleteStudent({students}: DeleteStudentProps){
// TODO: update delete student in the API to do this because the file should not be responsible for re-updating data
// Reload students without the deleted student
- getFromBackendApi("Students");
+ // getFromBackendApi("Students");
}
return (
diff --git a/frontend/app/Cruds/deleteTeacher.tsx b/frontend/app/Cruds/deleteTeacher.tsx
index 7b9f7bb..d09bec7 100644
--- a/frontend/app/Cruds/deleteTeacher.tsx
+++ b/frontend/app/Cruds/deleteTeacher.tsx
@@ -1,12 +1,19 @@
import { FormEvent, useEffect, useState } from "react";
import * as API from "../SendToApi";
-import { getFromBackendApi, instructor_data } from "../GetFromApi";
+import { getFromBackendApi } from "../GetFromApi";
import type { InstructorProps } from "../InstructorProps";
-export default function DeleteTeacher() {
+interface DeleteInstructorProps{
+ instructors: InstructorProps[];
+}
+export default function DeleteTeacher({instructors}: DeleteInstructorProps) {
const [instructorId, setInstructorId] = useState("");
- const [instructors, setInstructors] = useState([]);
+ /**
+ * Delete an instructor
+ *
+ * @param e FormEvent
+ */
function deleteInstructorHandler(e: FormEvent) {
e.preventDefault();
@@ -15,14 +22,10 @@ export default function DeleteTeacher() {
e.currentTarget.reset();
setInstructorId("");
- getFromBackendApi("Instructors");
- setInstructors(instructor_data);
+ // TODO: update delete instructor in the API to do this because the file should not be responsible for re-updating data
+ // getFromBackendApi("Instructors");
}
- useEffect(() => {
- setInstructors(instructor_data);
- }, []);
-
return (
diff --git a/frontend/app/GetFromApi.ts b/frontend/app/GetFromApi.ts
index 95a7898..c3cb7a4 100644
--- a/frontend/app/GetFromApi.ts
+++ b/frontend/app/GetFromApi.ts
@@ -31,6 +31,15 @@ export function setSectionIds(ids: any) {
section_ids = ids;
}
+/**
+ * Calls getFromBackendApi("Instructors"), getFromBackendApi("Students"), and getFromBackendApi("Sections")
+ */
+export async function getAll() {
+ getFromBackendApi("Instructors")
+ getFromBackendApi("Students")
+ getFromBackendApi("Sections")
+}
+
/**
* Fetches data from the backend. Use type "Instructors", "Students", or "Sections".
*/
@@ -45,16 +54,16 @@ export async function getFromBackendApi(type: string) {
const result = await response.json();
console.log(result);
- switch (type) {
- case "Instructors":
+ switch (type.toLowerCase()) {
+ case "instructors":
instructor_data = result;
return;
- case "Students":
+ case "students":
student_data = result;
return;
- case "Sections":
+ case "sections":
var ids: string[] = [];
result.forEach((element: Record) => {
ids.push(element.id);
diff --git a/frontend/app/SendToApi.ts b/frontend/app/SendToApi.ts
index 1d848ee..3779704 100644
--- a/frontend/app/SendToApi.ts
+++ b/frontend/app/SendToApi.ts
@@ -7,11 +7,17 @@
*/
import { apiFetch } from "./apiClient";
+import { getFromBackendApi } from "./GetFromApi";
export function generateId() {
return "fake-id";
}
+/**
+ * POST /csv/update
+ * @param csvData the data the backend will update with
+ * @returns
+ */
export function updateFromCSV(csvData: any) {
try {
var result: any;
@@ -33,6 +39,10 @@ export function updateFromCSV(csvData: any) {
}
}
+/**
+ * POST /schedule/regenerate
+ * @returns
+ */
export function regenerateSchedule() {
try {
var result: any;
@@ -87,6 +97,7 @@ export function createInstructor(instructor: InstructorModel) {
apiFetch(`/instructors/create`, requestOptions)
.then((response) => response.json())
+ .then(() => getFromBackendApi("Instructors"))
.then((data) => (result = data));
return result;
@@ -102,6 +113,7 @@ export function editInstructor(instructor: any) {
apiFetch(`/instructors/update`, requestOptions)
.then((response) => response.json())
+ .then(() => getFromBackendApi("Instructors"))
.then((data) => (result = data));
return result;
@@ -119,6 +131,7 @@ export function deleteInstructor(instructor_id: string) {
requestOptions,
)
.then((response) => response.json())
+ .then(() => getFromBackendApi("Instructors"))
.then((data) => (result = data));
return result;
@@ -150,6 +163,7 @@ export function createStudent(student: StudentModel) {
apiFetch(`/students/create`, requestOptions)
.then((response) => response.json())
+ .then(() => getFromBackendApi("Students"))
.then((data) => (result = data));
return result;
@@ -166,6 +180,7 @@ export function editStudent(student: any) {
apiFetch(`/students/update`, requestOptions)
.then((response) => response.json())
+ .then(() => getFromBackendApi("Students"))
.then((data) => (result = data));
return result;
@@ -181,6 +196,7 @@ export function deleteStudent(student_id: string) {
apiFetch(`/students/delete?student_id=${encodeURIComponent(student_id)}`, requestOptions)
.then((response) => response.json())
+ .then(() => getFromBackendApi("Students"))
.then((data) => (result = data));
return result;
@@ -197,6 +213,7 @@ export function createSection(section: string) {
apiFetch(`/create/section`, requestOptions)
.then((response) => response.json())
+ .then(() => getFromBackendApi("Sections"))
.then((data) => (result = data));
return result;
diff --git a/frontend/app/login/page.tsx b/frontend/app/login/page.tsx
index d17ef27..ce74896 100644
--- a/frontend/app/login/page.tsx
+++ b/frontend/app/login/page.tsx
@@ -66,7 +66,7 @@ export default function LoginPage() {
}
return (
-
+
Sign in
Use the username and password matching AUTH_USERNAME and AUTH_PASSWORD on