Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions backend/src/controllers/student.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
import { validationResult } from "express-validator";
import mongoose, { HydratedDocument } from "mongoose";

import { ValidationError } from "../errors/validation";
import EnrollmentModel from "../models/enrollment";
import { Image } from "../models/image";
import ProgramModel from "../models/program";
import ProgressNoteModel from "../models/progressNote";
import StudentModel from "../models/student";
import UserModel from "../models/user";
import { Enrollment } from "../types/enrollment";
import { createEnrollment, editEnrollment } from "../util/enrollment";
import validationErrorParser from "../util/validationErrorParser";
Expand All @@ -25,14 +27,12 @@

validationErrorParser(errors);

const newStudentId = new mongoose.Types.ObjectId();

const { enrollments, ...studentData } = req.body as StudentRequest;

// create enrollments for the student
const createdEnrollments = await Promise.all(
enrollments.map(async (program: Enrollment) => {
return await EnrollmentModel.create({ ...program, studentId: newStudentId });
return await EnrollmentModel.create({ ...program, studentId: studentData._id });
}),
);

Expand All @@ -51,6 +51,17 @@

export const editStudent: RequestHandler = async (req, res, next) => {
try {
const { uid } = req.body;

Check warning on line 54 in backend/src/controllers/student.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Unsafe assignment of an `any` value

const user = await UserModel.findById(uid);
if (!user) {
throw ValidationError.USER_NOT_FOUND;
}

if (user.accountType !== "admin") {
throw ValidationError.UNAUTHORIZED_USER;
}

const errors = validationResult(req);

validationErrorParser(errors);
Expand Down Expand Up @@ -122,7 +133,7 @@
const students = await StudentModel.find().populate("enrollments");

// Even though this is a get request, we have verifyAuthToken middleware that sets the accountType in the request body
const { accountType } = req.body;

Check warning on line 136 in backend/src/controllers/student.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Unsafe assignment of an `any` value

// Ensure that documents that are marked admin are not returned to non-admin users
if (accountType !== "admin") {
Expand All @@ -146,7 +157,7 @@
try {
const errors = validationResult(req);

const { accountType } = req.body;

Check warning on line 160 in backend/src/controllers/student.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Unsafe assignment of an `any` value

validationErrorParser(errors);

Expand Down
6 changes: 5 additions & 1 deletion backend/src/util/student.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ export const programValidatorUtil = async (enrollments: Enrollment[]) => {

enrollments.forEach((enrollment: Enrollment) => {
requiredFields.forEach((field) => {
if (!enrollment[field as keyof Enrollment])
// Can't just test for truthiness because 0 is a valid value for hoursLeft
if (
enrollment[field as keyof Enrollment] === undefined ||
enrollment[field as keyof Enrollment] === null
)
throw new Error(`Field ${field} is required on enrollment`);
});
});
Expand Down
14 changes: 10 additions & 4 deletions frontend/src/components/AttendanceTable.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Dot } from "lucide-react";
import Image from "next/image";
import React, { Dispatch, SetStateAction, useRef, useState } from "react";
import React, { Dispatch, SetStateAction, useMemo, useRef, useState } from "react";
import { FieldValues, SubmitHandler, useForm } from "react-hook-form";

import { cn } from "../lib/utils";
Expand Down Expand Up @@ -33,6 +33,12 @@
setRemainingAbsenceSessions,
firebaseToken,
}: TableProps) {
// Exclude students that are not in the session students map for some reason
const existingStudents = useMemo(
() => session.students.filter(({ studentId }) => students[studentId]),
[session.students, students],
);

const {
register,
setValue,
Expand Down Expand Up @@ -71,11 +77,11 @@
const onSubmit: SubmitHandler<FieldValues> = (data) => {
if (clickedRef.current) return;
clickedRef.current = true;
const studentInfo = session.students.map((student) => {
const studentInfo = existingStudents.map((student) => {
return {
studentId: student.studentId,
attended: data["attended_" + student.studentId] === "true" ? true : false,
hoursAttended: data["hoursAttended_" + student.studentId],

Check warning on line 84 in frontend/src/components/AttendanceTable.tsx

View workflow job for this annotation

GitHub Actions / Frontend lint and style check

Unsafe assignment of an `any` value
};
});
session.students = studentInfo;
Expand Down Expand Up @@ -143,12 +149,12 @@
</h1>
</div>
<div className="mb-8 grid w-full overflow-x-auto text-left text-sm lg:grid-cols-2 rtl:text-right">
{session.students.map((student, index) => {
{existingStudents.map((student, index) => {
return (
<div
className={cn(
"mr-5 flex grid grid-cols-3 bg-white",
index < session.students.length - (2 - (session.students.length % 2)) &&
index < existingStudents.length - (2 - (existingStudents.length % 2)) &&
"border-b",
index % 2 === 0 ? "ml-5" : "ml-5 lg:ml-10",
)}
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/components/StudentForm/EnrollmentsEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,11 @@ const EnrollmentFormItem = ({

let totalHours = 0;

for (let d = new Date(startDate); d <= new Date(endDate); d.setDate(d.getDate() + 1)) {
if (days.includes(d.toLocaleString("en-US", { weekday: "long" }))) {
totalHours += sessionLength;
if (!isNaN(sessionLength)) {
for (let d = new Date(startDate); d <= new Date(endDate); d.setDate(d.getDate() + 1)) {
if (days.includes(d.toLocaleString("en-US", { weekday: "long" }))) {
totalHours += sessionLength;
}
}
}
console.log(totalHours);
Expand Down
63 changes: 43 additions & 20 deletions frontend/src/components/StudentProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -309,27 +309,50 @@ export default function StudentProfile({ id }: StudentProfileProps) {
<div id="top" className="flex justify-between">
<ArrowHome />
{/*no need to set all students*/}
<button
className="flex cursor-pointer space-x-[5px]"
type="button"
onClick={() => {
handleViewChange();
}}
>
<svg
width="20"
height="22"
viewBox="0 0 10 11"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{isAdmin ? (
<button
className="flex cursor-pointer space-x-[5px]"
type="button"
onClick={() => {
handleViewChange();
}}
>
<path
d="M9.02645 1.38024C8.86568 1.21944 8.67481 1.09189 8.46475 1.00486C8.25468 0.917838 8.02953 0.873047 7.80215 0.873047C7.57477 0.873047 7.34962 0.917838 7.13955 1.00486C6.92948 1.09189 6.73861 1.21944 6.57785 1.38024L1.37812 6.57996C1.13117 6.82707 0.955067 7.13594 0.868192 7.47432L0.293527 9.71089C0.279277 9.76654 0.279787 9.82494 0.295008 9.88033C0.310229 9.93573 0.339634 9.98619 0.38032 10.0267C0.421006 10.0673 0.471565 10.0965 0.527006 10.1116C0.582447 10.1266 0.640852 10.1269 0.696453 10.1125L2.93236 9.53849C3.27081 9.45177 3.57971 9.27564 3.82672 9.02856L9.02645 3.82884C9.18724 3.66807 9.3148 3.4772 9.40182 3.26713C9.48884 3.05707 9.53364 2.83191 9.53364 2.60454C9.53364 2.37716 9.48884 2.152 9.40182 1.94194C9.3148 1.73187 9.18724 1.541 9.02645 1.38024ZM7.04485 1.84723C7.24569 1.64638 7.5181 1.53355 7.80215 1.53355C8.08619 1.53355 8.3586 1.64638 8.55945 1.84723C8.7603 2.04808 8.87313 2.32049 8.87313 2.60454C8.87313 2.88858 8.7603 3.16099 8.55945 3.36184L8.04489 3.87639L6.53029 2.36179L7.04485 1.84723ZM6.06329 2.82879L7.5779 4.34339L3.35973 8.56156C3.19616 8.72481 2.99177 8.84115 2.76789 8.89843L1.0723 9.33439L1.50825 7.6388C1.56511 7.41474 1.68151 7.21024 1.84512 7.04696L6.06329 2.82879Z"
fill="black"
/>
</svg>
<div>{currentView === "Edit" ? "View" : "Edit"} Mode</div>
</button>
<svg
width="20"
height="22"
viewBox="0 0 10 11"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.02645 1.38024C8.86568 1.21944 8.67481 1.09189 8.46475 1.00486C8.25468 0.917838 8.02953 0.873047 7.80215 0.873047C7.57477 0.873047 7.34962 0.917838 7.13955 1.00486C6.92948 1.09189 6.73861 1.21944 6.57785 1.38024L1.37812 6.57996C1.13117 6.82707 0.955067 7.13594 0.868192 7.47432L0.293527 9.71089C0.279277 9.76654 0.279787 9.82494 0.295008 9.88033C0.310229 9.93573 0.339634 9.98619 0.38032 10.0267C0.421006 10.0673 0.471565 10.0965 0.527006 10.1116C0.582447 10.1266 0.640852 10.1269 0.696453 10.1125L2.93236 9.53849C3.27081 9.45177 3.57971 9.27564 3.82672 9.02856L9.02645 3.82884C9.18724 3.66807 9.3148 3.4772 9.40182 3.26713C9.48884 3.05707 9.53364 2.83191 9.53364 2.60454C9.53364 2.37716 9.48884 2.152 9.40182 1.94194C9.3148 1.73187 9.18724 1.541 9.02645 1.38024ZM7.04485 1.84723C7.24569 1.64638 7.5181 1.53355 7.80215 1.53355C8.08619 1.53355 8.3586 1.64638 8.55945 1.84723C8.7603 2.04808 8.87313 2.32049 8.87313 2.60454C8.87313 2.88858 8.7603 3.16099 8.55945 3.36184L8.04489 3.87639L6.53029 2.36179L7.04485 1.84723ZM6.06329 2.82879L7.5779 4.34339L3.35973 8.56156C3.19616 8.72481 2.99177 8.84115 2.76789 8.89843L1.0723 9.33439L1.50825 7.6388C1.56511 7.41474 1.68151 7.21024 1.84512 7.04696L6.06329 2.82879Z"
fill="black"
/>
</svg>
<div>{currentView === "Edit" ? "View" : "Edit"} Mode</div>
</button>
) : (
<button
className="flex cursor-pointer space-x-[5px]"
type="button"
disabled
style={{ cursor: "unset" }}
>
<svg
width="20"
height="22"
viewBox="0 0 10 11"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.02645 1.38024C8.86568 1.21944 8.67481 1.09189 8.46475 1.00486C8.25468 0.917838 8.02953 0.873047 7.80215 0.873047C7.57477 0.873047 7.34962 0.917838 7.13955 1.00486C6.92948 1.09189 6.73861 1.21944 6.57785 1.38024L1.37812 6.57996C1.13117 6.82707 0.955067 7.13594 0.868192 7.47432L0.293527 9.71089C0.279277 9.76654 0.279787 9.82494 0.295008 9.88033C0.310229 9.93573 0.339634 9.98619 0.38032 10.0267C0.421006 10.0673 0.471565 10.0965 0.527006 10.1116C0.582447 10.1266 0.640852 10.1269 0.696453 10.1125L2.93236 9.53849C3.27081 9.45177 3.57971 9.27564 3.82672 9.02856L9.02645 3.82884C9.18724 3.66807 9.3148 3.4772 9.40182 3.26713C9.48884 3.05707 9.53364 2.83191 9.53364 2.60454C9.53364 2.37716 9.48884 2.152 9.40182 1.94194C9.3148 1.73187 9.18724 1.541 9.02645 1.38024ZM7.04485 1.84723C7.24569 1.64638 7.5181 1.53355 7.80215 1.53355C8.08619 1.53355 8.3586 1.64638 8.55945 1.84723C8.7603 2.04808 8.87313 2.32049 8.87313 2.60454C8.87313 2.88858 8.7603 3.16099 8.55945 3.36184L8.04489 3.87639L6.53029 2.36179L7.04485 1.84723ZM6.06329 2.82879L7.5779 4.34339L3.35973 8.56156C3.19616 8.72481 2.99177 8.84115 2.76789 8.89843L1.0723 9.33439L1.50825 7.6388C1.56511 7.41474 1.68151 7.21024 1.84512 7.04696L6.06329 2.82879Z"
fill="black"
/>
</svg>
<div>{currentView === "Edit" ? "View" : "Edit"} Mode</div>
</button>
)}
</div>

{currentView === "View" ? (
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/components/StudentsTable/useColumnSchema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Link from "next/link";
import { useMemo } from "react";

import { Popover, PopoverContent, PopoverTrigger } from "../../components/ui/popover";
import { Contact, Enrollment, ProgramLink } from "../StudentForm/types";
import { Contact, Enrollment } from "../StudentForm/types";

import { Columns, ProgramMap, StudentMap } from "./types";

Expand Down Expand Up @@ -38,13 +38,13 @@ const PopoverInfoRow = ({ label, value }: { label: string; value: string }) => (
</span>
);

const ProgramPopover = ({ link, program }: { link: ProgramLink; program: Program }) => {
const ProgramPopover = ({ link, program }: { link: Enrollment; program: Program }) => {
if (!program) return null;
const rowInfo = [
["Type", program.type],
["Schedule", program.daysOfWeek.join(", ")],
["Start Date", new Date(/*program.startDate*/).toLocaleDateString("en-US")],
["Renewal Date", new Date(/*program.endDate*/).toLocaleDateString("en-US")],
["Start Date", new Date(link.startDate).toLocaleDateString("en-US")],
["Renewal Date", new Date(link.renewalDate).toLocaleDateString("en-US")],
["Hours Left", link.hoursLeft.toString()],
];

Expand Down Expand Up @@ -127,7 +127,7 @@ export function useColumnSchema({
id: isTablet ? "Curr. P1" : "Curr. Program 1",
header: isTablet ? "Curr. P1" : "Curr. Program 1",
cell: (info) => {
const enrollments = info.getValue() as unknown as ProgramLink[];
const enrollments = info.getValue() as unknown as Enrollment[];
const link = enrollments.filter((enr) => enr.status === "Joined")[0];
if (!link) return null;
const program = allPrograms[link.programId];
Expand All @@ -140,7 +140,7 @@ export function useColumnSchema({
id: isTablet ? "Curr. P2" : "Curr. Program 2",
header: isTablet ? "Curr. P2" : "Curr. Program 2",
cell: (info) => {
const enrollments = info.getValue() as unknown as ProgramLink[];
const enrollments = info.getValue() as unknown as Enrollment[];
const link = enrollments.filter((enr) => enr.status === "Joined")[1];
if (!link) return null;
const program = allPrograms[link.programId];
Expand Down
29 changes: 16 additions & 13 deletions frontend/src/pages/attendance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,19 +156,22 @@ export default function AttendanceDashboard() {
className="flex overflow-x-hidden overflow-y-hidden hover:overflow-x-auto"
ref={scrollRef}
>
{allAbsenceSessions?.map((absenceSession, i) => {
const program = allPrograms[absenceSession.programId];
const student = allStudents[absenceSession.studentId];
return (
<AttendanceCard
program={program}
student={student}
key={i}
setRemainingSessions={setRemainingAbsenceSessions}
firebaseToken={firebaseToken}
/>
);
})}
{// Exclude students that are not in the session students map for some reason
allAbsenceSessions
?.filter((absenceSession) => allStudents[absenceSession.studentId])
.map((absenceSession, i) => {
const program = allPrograms[absenceSession.programId];
const student = allStudents[absenceSession.studentId];
return (
<AttendanceCard
program={program}
student={student}
key={i}
setRemainingSessions={setRemainingAbsenceSessions}
firebaseToken={firebaseToken}
/>
);
})}
{remainingAbsenceSessions === 0 && (
<div className="border-gray flex h-[326px] w-[240px] items-center justify-center rounded-2xl border bg-white">
<div className="ml-5">
Expand Down
Loading