Skip to content

Commit ca89496

Browse files
authored
Feature/adhi0331/calendar (#110)
* basic implementation * completed UI * basic backend connection * view calendar * preliminary calendar complete * fixed issue with program enrollment * remove unnecssary logs
1 parent e59620c commit ca89496

File tree

30 files changed

+1128
-18
lines changed

30 files changed

+1128
-18
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/* eslint-disable @typescript-eslint/no-misused-promises */
2+
/**
3+
* Function handlers for calendar route requests
4+
*/
5+
import { RequestHandler } from "express";
6+
import createHttpError from "http-errors";
7+
8+
import EnrollmentModel from "../models/enrollment";
9+
import SessionModel from "../models/session";
10+
11+
import { Calendar, CalendarSlot } from "./types/calendarTypes";
12+
13+
/**
14+
* Calendar Body: {
15+
*
16+
* studentId: string;
17+
* programId: string;
18+
* calendar: {
19+
* date: Date;
20+
* hours: number;
21+
* session: string;
22+
* }[]
23+
*
24+
* }
25+
*/
26+
27+
/**
28+
* Request handler for getting calendar for student in program
29+
* @param req
30+
* @param res
31+
* @param next
32+
*/
33+
export const getCalendar: RequestHandler = async (req, res, next) => {
34+
try {
35+
const studentId = req.params.studentId;
36+
const programId = req.params.programId;
37+
38+
const enrollment = EnrollmentModel.find({ studentId, programId });
39+
if (!enrollment) {
40+
throw createHttpError(404, "Enrollment not found");
41+
}
42+
43+
// get all sessions with studentId and programId
44+
const sessions = await SessionModel.find({ programId });
45+
46+
const calendar: Calendar = { studentId, programId, calendar: [] };
47+
for (const session of sessions) {
48+
for (const student of session.students) {
49+
if (student.studentId.toString() === studentId) {
50+
let hours = 0;
51+
if (session.marked) {
52+
hours = student.hoursAttended;
53+
}
54+
const date = session.date;
55+
const sessionId = session._id.toString();
56+
calendar.calendar.push({ date, hours, session: sessionId });
57+
}
58+
}
59+
}
60+
console.log(calendar);
61+
62+
return res.status(200).send(calendar);
63+
} catch (error) {
64+
next(error);
65+
}
66+
};
67+
68+
/**
69+
* Handler for editing a day in a calendar
70+
* @param req
71+
* @param res
72+
* @param next
73+
*/
74+
export const editCalendar: RequestHandler = async (req, res, next) => {
75+
try {
76+
const studentId = req.params.studentId;
77+
const programId = req.params.programId;
78+
79+
const enrollment = await EnrollmentModel.findOne({ studentId, programId });
80+
if (!enrollment) {
81+
throw createHttpError(404, "Enrollment not found");
82+
}
83+
84+
const { hours, session } = req.body as CalendarSlot;
85+
86+
const sessionObject = await SessionModel.findById(session);
87+
88+
if (!sessionObject) {
89+
throw createHttpError(404, "Session not found");
90+
}
91+
92+
if (sessionObject.programId.toString() !== programId) {
93+
throw createHttpError(404, "Incorrect program for session");
94+
}
95+
96+
const student = sessionObject.students.find((s) => s.studentId.toString() === studentId);
97+
98+
if (!student) {
99+
throw createHttpError(404, "Student not in session");
100+
}
101+
102+
const prevHoursAttended = student.hoursAttended;
103+
let hoursLeft = enrollment.hoursLeft + prevHoursAttended;
104+
105+
student.hoursAttended = hours;
106+
hoursLeft -= student.hoursAttended;
107+
enrollment.hoursLeft = hoursLeft > 0 ? hoursLeft : 0;
108+
109+
await sessionObject.save();
110+
await enrollment.save();
111+
112+
res.status(200).send("Updated");
113+
} catch (error) {
114+
next(error);
115+
}
116+
};

backend/src/controllers/program.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ export const updateProgram: RequestHandler = async (req, res, next) => {
6868
{ $set: { status: "Waitlisted", dateUpdated: Date.now() } },
6969
);
7070

71+
// update days of week when changed
72+
await EnrollmentModel.updateMany(
73+
{ programId: { $eq: programId }, status: { $eq: "Joined" } },
74+
{ $set: { schedule: programData.daysOfWeek } },
75+
);
76+
7177
res.status(200).json(editedProgram);
7278
} catch (error) {
7379
next(error);

backend/src/controllers/session.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,18 @@ export const updateSession: RequestHandler = async (req, res, next) => {
266266
return res.status(404).json({ message: "No object in database with provided ID" });
267267
}
268268

269+
programData.students.forEach(async (student: StudentInfo) => {
270+
const enrollment = await EnrollmentModel.findOne({
271+
studentId: student.studentId,
272+
programId: programData.programId,
273+
});
274+
if (enrollment) {
275+
const hours = enrollment.hoursLeft - student.hoursAttended;
276+
enrollment.hoursLeft = hours > 0 ? hours : 0;
277+
await enrollment.save();
278+
}
279+
});
280+
269281
const absentStudents = programData.students.filter((student: StudentInfo) => !student.attended);
270282

271283
const absenceSessions = absentStudents.map((absentStudent) => ({

backend/src/controllers/student.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable @typescript-eslint/no-misused-promises */
22
/**
3-
* Functions that process task route requests.
3+
* Functions that process student route requests.
44
*/
55

66
import { RequestHandler } from "express";
@@ -9,6 +9,7 @@ import mongoose, { HydratedDocument } from "mongoose";
99

1010
import EnrollmentModel from "../models/enrollment";
1111
import { Image } from "../models/image";
12+
import ProgramModel from "../models/program";
1213
import ProgressNoteModel from "../models/progressNote";
1314
import StudentModel from "../models/student";
1415
import { Enrollment } from "../types/enrollment";
@@ -81,6 +82,10 @@ export const editStudent: RequestHandler = async (req, res, next) => {
8182
enrollments.map(async (enrollment: Enrollment) => {
8283
const enrollmentExists = await EnrollmentModel.findById(enrollment._id);
8384
const enrollmentBody = { ...enrollment, studentId: new mongoose.Types.ObjectId(studentId) };
85+
const program = await ProgramModel.findById({ _id: enrollment.programId });
86+
if (program?.type === "regular") {
87+
enrollmentBody.schedule = program.daysOfWeek;
88+
}
8489
if (!enrollmentExists) {
8590
return await createEnrollment(enrollmentBody);
8691
} else {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export type CalendarSlot = {
2+
date: Date;
3+
hours: number;
4+
session: string;
5+
};
6+
7+
export type Calendar = {
8+
studentId: string;
9+
programId: string;
10+
calendar: CalendarSlot[];
11+
};

backend/src/routes/api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import express from "express";
22

3+
import calendarRouter from "./calendar";
34
import imageRouter from "./image";
45
import programRoutes from "./program";
56
import progressNoteRoutes from "./progressNote";
@@ -15,6 +16,7 @@ router.use("/student", studentRoutes);
1516
router.use("/program", programRoutes);
1617
router.use("/session", sessionRoutes);
1718
router.use("/progressNote", progressNoteRoutes);
19+
router.use("/calendar", calendarRouter);
1820
router.use("/image", imageRouter);
1921

2022
export default router;

backend/src/routes/calendar.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Calendar route requests
3+
*/
4+
import express from "express";
5+
6+
import * as CalendarController from "../controllers/calendar";
7+
import { verifyAuthToken } from "../validators/auth";
8+
import * as CalendarValidator from "../validators/calendar";
9+
10+
const router = express.Router();
11+
12+
router.get("/:studentId/:programId", [verifyAuthToken], CalendarController.getCalendar);
13+
router.patch(
14+
"/:studentId/:programId",
15+
[verifyAuthToken],
16+
CalendarValidator.editCalendar,
17+
CalendarController.editCalendar,
18+
);
19+
20+
export default router;

backend/src/routes/student.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Task route requests.
2+
* Student route requests.
33
*/
44

55
import express from "express";

backend/src/validators/calendar.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { body } from "express-validator";
2+
3+
// const makeDateValidator = () =>
4+
// body("date")
5+
// .exists()
6+
// .withMessage("date needed")
7+
// .bail()
8+
// .isDate()
9+
// .bail();
10+
11+
const makeHoursValidator = () =>
12+
body("hours")
13+
.exists()
14+
.withMessage("hours needed")
15+
.bail()
16+
.isNumeric()
17+
.withMessage("needs to be number")
18+
.bail();
19+
20+
const makeSessionValidator = () =>
21+
body("session")
22+
.exists()
23+
.withMessage("sessionId needed")
24+
.bail()
25+
.isString()
26+
.withMessage("needs to be string")
27+
.bail();
28+
29+
export const editCalendar = [
30+
// makeDateValidator(),
31+
makeHoursValidator(),
32+
makeSessionValidator(),
33+
];

frontend/src/api/calendar.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { GET, PATCH, createAuthHeader, handleAPIError } from "../api/requests";
2+
3+
import type { APIResult } from "../api/requests";
4+
5+
export type CalendarResponse = {
6+
studentId: string;
7+
programId: string;
8+
calendar: {
9+
date: Date;
10+
hours: number;
11+
session: string;
12+
}[];
13+
};
14+
15+
export async function getCalendar(
16+
studentId: string,
17+
programId: string,
18+
firebaseToken: string,
19+
): Promise<APIResult<CalendarResponse>> {
20+
try {
21+
const headers = createAuthHeader(firebaseToken);
22+
const response = await GET(`/calendar/${studentId}/${programId}`, headers);
23+
const json = (await response.json()) as CalendarResponse;
24+
return { success: true, data: json };
25+
} catch (error) {
26+
return handleAPIError(error);
27+
}
28+
}
29+
30+
export async function editCalendar(
31+
studentId: string,
32+
programId: string,
33+
firebaseToken: string,
34+
hours: number,
35+
session: string,
36+
): Promise<APIResult<string>> {
37+
try {
38+
const headers = createAuthHeader(firebaseToken);
39+
const body = { hours, session };
40+
const res = await PATCH(`/calendar/${studentId}/${programId}`, body, headers);
41+
const json = (await res.json()) as string;
42+
return { success: true, data: json };
43+
} catch (error) {
44+
return handleAPIError(error);
45+
}
46+
}

0 commit comments

Comments
 (0)