diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..30cf57ed
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,10 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..0782aef9
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 00000000..0f7bc519
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/.idea/csm_web.iml b/.idea/csm_web.iml
new file mode 100644
index 00000000..d7e41aab
--- /dev/null
+++ b/.idea/csm_web.iml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 00000000..cc5462da
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 00000000..e6b072c7
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/.idea/prettier.xml b/.idea/prettier.xml
new file mode 100644
index 00000000..2e084d40
--- /dev/null
+++ b/.idea/prettier.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..dcb6b8c4
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/ci/requirements.txt b/ci/requirements.txt
index 5c54b7cd..66bf4f95 100644
--- a/ci/requirements.txt
+++ b/ci/requirements.txt
@@ -57,7 +57,7 @@ social-auth-core==4.5.6 ; python_full_version >= "3.12.4" and python_full_versio
sqlparse==0.5.3 ; python_full_version >= "3.12.4" and python_full_version < "3.13.0"
stack-data==0.6.3 ; python_full_version >= "3.12.4" and python_full_version < "3.13.0"
traitlets==5.14.3 ; python_full_version >= "3.12.4" and python_full_version < "3.13.0"
-typing-extensions==4.12.2 ; python_full_version >= "3.12.4" and python_full_version < "3.13.0"
+typing-extensions==4.16.0 ; python_full_version >= "3.12.4" and python_full_version < "3.13.0"
tzdata==2025.1 ; python_full_version >= "3.12.4" and python_full_version < "3.13.0"
urllib3==2.3.0 ; python_full_version >= "3.12.4" and python_full_version < "3.13.0"
wcwidth==0.2.13 ; python_full_version >= "3.12.4" and python_full_version < "3.13.0"
diff --git a/csm_web/frontend/src/components/enrollment_automation/coordinator/fakefile.py b/csm_web/frontend/src/components/enrollment_automation/coordinator/fakefile.py
new file mode 100644
index 00000000..e69de29b
diff --git a/csm_web/frontend/src/components/section/MentorSectionRoster.tsx b/csm_web/frontend/src/components/section/MentorSectionRoster.tsx
index 4d2cd4de..150c4539 100644
--- a/csm_web/frontend/src/components/section/MentorSectionRoster.tsx
+++ b/csm_web/frontend/src/components/section/MentorSectionRoster.tsx
@@ -1,6 +1,6 @@
import React, { useState } from "react";
-import { useSectionStudents } from "../../utils/queries/sections";
+import { useSectionStudents, useSectionWaitlistedStudents } from "../../utils/queries/sections";
import LoadingSpinner from "../LoadingSpinner";
import CheckCircle from "../../../static/frontend/img/check_circle.svg";
@@ -13,6 +13,13 @@ interface MentorSectionRosterProps {
export default function MentorSectionRoster({ id }: MentorSectionRosterProps) {
const { data: students, isSuccess: studentsLoaded, isError: studentsLoadError } = useSectionStudents(id);
const [emailsCopied, setEmailsCopied] = useState(false);
+ const {
+ data: wlStudents,
+ isSuccess: wlStudentsLoaded,
+ isError: wlStudentsLoadError
+ } = useSectionWaitlistedStudents(id);
+ const [wlEmailsCopied, wlSetEmailsCopied] = useState(false);
+
const handleCopyEmails = () => {
if (studentsLoaded) {
navigator.clipboard.writeText(students.map(({ email }) => email).join("\n")).then(() => {
@@ -21,6 +28,14 @@ export default function MentorSectionRoster({ id }: MentorSectionRosterProps) {
});
}
};
+ const wlHandleCopyEmails = () => {
+ if (wlStudentsLoaded) {
+ navigator.clipboard.writeText(wlStudents.map(({ email }) => email).join("\n")).then(() => {
+ wlSetEmailsCopied(true);
+ setTimeout(() => wlSetEmailsCopied(false), 1500);
+ });
+ }
+ };
return (
Roster
@@ -50,6 +65,35 @@ export default function MentorSectionRoster({ id }: MentorSectionRosterProps) {
) : (
)}
+ {wlStudentsLoaded && wlStudents && wlStudents.length > 0 && (
+
+
+ Waitlisted
+
+
+
+ | Position |
+ Name |
+ Email |
+
+
+ {wlEmailsCopied && }
+ |
+
+
+
+ {wlStudents.map(({ position, name, email, id }) => (
+
+ | {position} |
+ {name} |
+ {email} |
+
+ ))}
+
+
+
+ )}{" "}
+ {wlStudentsLoadError && Waitlisted students could not be loaded
}
);
}
diff --git a/csm_web/frontend/src/utils/queries/sections.tsx b/csm_web/frontend/src/utils/queries/sections.tsx
index b17948a2..96268d47 100644
--- a/csm_web/frontend/src/utils/queries/sections.tsx
+++ b/csm_web/frontend/src/utils/queries/sections.tsx
@@ -4,7 +4,7 @@
import { useMutation, UseMutationResult, useQuery, useQueryClient, UseQueryResult } from "@tanstack/react-query";
import { fetchNormalized, fetchWithMethod, HTTP_METHODS } from "../api";
-import { Attendance, RawAttendance, Section, Spacetime, Student } from "../types";
+import { Attendance, RawAttendance, Section, Spacetime, Student, WaitlistedStudent } from "../types";
import { handleError, handlePermissionsError, handleRetry, PermissionError, ServerError } from "./helpers";
/* ===== Queries ===== */
@@ -71,8 +71,8 @@ export const useSectionStudents = (id: number): UseQueryResult => {
- const queryResult = useQuery(
+export const useSectionWaitlistedStudents = (id: number): UseQueryResult => {
+ const queryResult = useQuery(
["sections", id, "waitlisted-students"],
async () => {
if (isNaN(id)) {
@@ -82,7 +82,7 @@ export const useSectionWaitlistedStudents = (id: number): UseQueryResult a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
+ return students.sort((a: WaitlistedStudent, b: WaitlistedStudent) => a.position > b.position);
}
// if (resp1.status === 404) {
diff --git a/csm_web/frontend/src/utils/types.tsx b/csm_web/frontend/src/utils/types.tsx
index 6bd5d130..b862c113 100644
--- a/csm_web/frontend/src/utils/types.tsx
+++ b/csm_web/frontend/src/utils/types.tsx
@@ -85,6 +85,13 @@ export interface Student {
email: string;
}
+export interface WaitlistedStudent {
+ id: number;
+ name: string;
+ email: string;
+ position: number;
+}
+
export enum AttendancePresence {
EX = "EX",
PR = "PR",
diff --git a/csm_web/scheduler/factories.py b/csm_web/scheduler/factories.py
index 86ae44f5..0e2cf5cf 100644
--- a/csm_web/scheduler/factories.py
+++ b/csm_web/scheduler/factories.py
@@ -603,12 +603,17 @@ def generate_test_data(preconfirm=False):
)
user_objects.extend(waitlisted_users)
waitlisted_students = []
+ wl_position = 1
for waitlisted_user in waitlisted_users:
waitlisted_students.append(
WaitlistedStudentFactory.build(
- section=section, course=course, user=waitlisted_user
+ section=section,
+ course=course,
+ user=waitlisted_user,
+ position=wl_position,
)
)
+ wl_position = wl_position + 1
waitlisted_student_objects.extend(waitlisted_students)
# courses with many sections/students
diff --git a/poetry.lock b/poetry.lock
index 27bb7bfe..fe553658 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 2.3.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand.
[[package]]
name = "asgiref"
@@ -1673,14 +1673,14 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,
[[package]]
name = "typing-extensions"
-version = "4.12.2"
-description = "Backported and Experimental Type Hints for Python 3.8+"
+version = "4.16.0"
+description = "Backported and Experimental Type Hints for Python 3.9+"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
groups = ["main"]
files = [
- {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
- {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
+ {file = "typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8"},
+ {file = "typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5"},
]
[[package]]