Skip to content

Commit

Permalink
Feature/adhi0331/calendar (#110)
Browse files Browse the repository at this point in the history
* basic implementation

* completed UI

* basic backend connection

* view calendar

* preliminary calendar complete

* fixed issue with program enrollment

* remove unnecssary logs
  • Loading branch information
adhi0331 authored Jan 10, 2025
1 parent e59620c commit ca89496
Show file tree
Hide file tree
Showing 30 changed files with 1,128 additions and 18 deletions.
116 changes: 116 additions & 0 deletions backend/src/controllers/calendar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
/**
* Function handlers for calendar route requests
*/
import { RequestHandler } from "express";
import createHttpError from "http-errors";

import EnrollmentModel from "../models/enrollment";
import SessionModel from "../models/session";

import { Calendar, CalendarSlot } from "./types/calendarTypes";

/**
* Calendar Body: {
*
* studentId: string;
* programId: string;
* calendar: {
* date: Date;
* hours: number;
* session: string;
* }[]
*
* }
*/

/**
* Request handler for getting calendar for student in program
* @param req
* @param res
* @param next
*/
export const getCalendar: RequestHandler = async (req, res, next) => {
try {
const studentId = req.params.studentId;
const programId = req.params.programId;

const enrollment = EnrollmentModel.find({ studentId, programId });
if (!enrollment) {
throw createHttpError(404, "Enrollment not found");
}

// get all sessions with studentId and programId
const sessions = await SessionModel.find({ programId });

const calendar: Calendar = { studentId, programId, calendar: [] };
for (const session of sessions) {
for (const student of session.students) {
if (student.studentId.toString() === studentId) {
let hours = 0;
if (session.marked) {
hours = student.hoursAttended;
}
const date = session.date;
const sessionId = session._id.toString();
calendar.calendar.push({ date, hours, session: sessionId });
}
}
}
console.log(calendar);

return res.status(200).send(calendar);
} catch (error) {
next(error);
}
};

/**
* Handler for editing a day in a calendar
* @param req
* @param res
* @param next
*/
export const editCalendar: RequestHandler = async (req, res, next) => {
try {
const studentId = req.params.studentId;
const programId = req.params.programId;

const enrollment = await EnrollmentModel.findOne({ studentId, programId });
if (!enrollment) {
throw createHttpError(404, "Enrollment not found");
}

const { hours, session } = req.body as CalendarSlot;

const sessionObject = await SessionModel.findById(session);

if (!sessionObject) {
throw createHttpError(404, "Session not found");
}

if (sessionObject.programId.toString() !== programId) {
throw createHttpError(404, "Incorrect program for session");
}

const student = sessionObject.students.find((s) => s.studentId.toString() === studentId);

if (!student) {
throw createHttpError(404, "Student not in session");
}

const prevHoursAttended = student.hoursAttended;
let hoursLeft = enrollment.hoursLeft + prevHoursAttended;

student.hoursAttended = hours;
hoursLeft -= student.hoursAttended;
enrollment.hoursLeft = hoursLeft > 0 ? hoursLeft : 0;

await sessionObject.save();
await enrollment.save();

res.status(200).send("Updated");
} catch (error) {
next(error);
}
};
6 changes: 6 additions & 0 deletions backend/src/controllers/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ export const updateProgram: RequestHandler = async (req, res, next) => {
{ $set: { status: "Waitlisted", dateUpdated: Date.now() } },
);

// update days of week when changed
await EnrollmentModel.updateMany(
{ programId: { $eq: programId }, status: { $eq: "Joined" } },
{ $set: { schedule: programData.daysOfWeek } },
);

res.status(200).json(editedProgram);
} catch (error) {
next(error);
Expand Down
12 changes: 12 additions & 0 deletions backend/src/controllers/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,18 @@ export const updateSession: RequestHandler = async (req, res, next) => {
return res.status(404).json({ message: "No object in database with provided ID" });
}

programData.students.forEach(async (student: StudentInfo) => {
const enrollment = await EnrollmentModel.findOne({
studentId: student.studentId,
programId: programData.programId,
});
if (enrollment) {
const hours = enrollment.hoursLeft - student.hoursAttended;
enrollment.hoursLeft = hours > 0 ? hours : 0;
await enrollment.save();
}
});

const absentStudents = programData.students.filter((student: StudentInfo) => !student.attended);

const absenceSessions = absentStudents.map((absentStudent) => ({
Expand Down
7 changes: 6 additions & 1 deletion backend/src/controllers/student.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
/**
* Functions that process task route requests.
* Functions that process student route requests.
*/

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

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 { Enrollment } from "../types/enrollment";
Expand Down Expand Up @@ -81,6 +82,10 @@ export const editStudent: RequestHandler = async (req, res, next) => {
enrollments.map(async (enrollment: Enrollment) => {
const enrollmentExists = await EnrollmentModel.findById(enrollment._id);
const enrollmentBody = { ...enrollment, studentId: new mongoose.Types.ObjectId(studentId) };
const program = await ProgramModel.findById({ _id: enrollment.programId });
if (program?.type === "regular") {
enrollmentBody.schedule = program.daysOfWeek;
}
if (!enrollmentExists) {
return await createEnrollment(enrollmentBody);
} else {
Expand Down
11 changes: 11 additions & 0 deletions backend/src/controllers/types/calendarTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type CalendarSlot = {
date: Date;
hours: number;
session: string;
};

export type Calendar = {
studentId: string;
programId: string;
calendar: CalendarSlot[];
};
2 changes: 2 additions & 0 deletions backend/src/routes/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import express from "express";

import calendarRouter from "./calendar";
import imageRouter from "./image";
import programRoutes from "./program";
import progressNoteRoutes from "./progressNote";
Expand All @@ -15,6 +16,7 @@ router.use("/student", studentRoutes);
router.use("/program", programRoutes);
router.use("/session", sessionRoutes);
router.use("/progressNote", progressNoteRoutes);
router.use("/calendar", calendarRouter);
router.use("/image", imageRouter);

export default router;
20 changes: 20 additions & 0 deletions backend/src/routes/calendar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Calendar route requests
*/
import express from "express";

import * as CalendarController from "../controllers/calendar";
import { verifyAuthToken } from "../validators/auth";
import * as CalendarValidator from "../validators/calendar";

const router = express.Router();

router.get("/:studentId/:programId", [verifyAuthToken], CalendarController.getCalendar);
router.patch(
"/:studentId/:programId",
[verifyAuthToken],
CalendarValidator.editCalendar,
CalendarController.editCalendar,
);

export default router;
2 changes: 1 addition & 1 deletion backend/src/routes/student.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Task route requests.
* Student route requests.
*/

import express from "express";
Expand Down
33 changes: 33 additions & 0 deletions backend/src/validators/calendar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { body } from "express-validator";

// const makeDateValidator = () =>
// body("date")
// .exists()
// .withMessage("date needed")
// .bail()
// .isDate()
// .bail();

const makeHoursValidator = () =>
body("hours")
.exists()
.withMessage("hours needed")
.bail()
.isNumeric()
.withMessage("needs to be number")
.bail();

const makeSessionValidator = () =>
body("session")
.exists()
.withMessage("sessionId needed")
.bail()
.isString()
.withMessage("needs to be string")
.bail();

export const editCalendar = [
// makeDateValidator(),
makeHoursValidator(),
makeSessionValidator(),
];
46 changes: 46 additions & 0 deletions frontend/src/api/calendar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { GET, PATCH, createAuthHeader, handleAPIError } from "../api/requests";

import type { APIResult } from "../api/requests";

export type CalendarResponse = {
studentId: string;
programId: string;
calendar: {
date: Date;
hours: number;
session: string;
}[];
};

export async function getCalendar(
studentId: string,
programId: string,
firebaseToken: string,
): Promise<APIResult<CalendarResponse>> {
try {
const headers = createAuthHeader(firebaseToken);
const response = await GET(`/calendar/${studentId}/${programId}`, headers);
const json = (await response.json()) as CalendarResponse;
return { success: true, data: json };
} catch (error) {
return handleAPIError(error);
}
}

export async function editCalendar(
studentId: string,
programId: string,
firebaseToken: string,
hours: number,
session: string,
): Promise<APIResult<string>> {
try {
const headers = createAuthHeader(firebaseToken);
const body = { hours, session };
const res = await PATCH(`/calendar/${studentId}/${programId}`, body, headers);
const json = (await res.json()) as string;
return { success: true, data: json };
} catch (error) {
return handleAPIError(error);
}
}
4 changes: 1 addition & 3 deletions frontend/src/api/programs.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { GET, PATCH, POST, handleAPIError } from "../api/requests";
import { GET, PATCH, POST, createAuthHeader, handleAPIError } from "../api/requests";
import { CreateProgramRequest } from "../components/ProgramForm/types";

import { createAuthHeader } from "./progressNotes";

import type { APIResult } from "../api/requests";

export type Program = CreateProgramRequest & { _id: string; dateUpdated: string };
Expand Down
14 changes: 9 additions & 5 deletions frontend/src/api/progressNotes.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { APIResult, DELETE, GET, POST, PUT, handleAPIError } from "@/api/requests";
import {
APIResult,
DELETE,
GET,
POST,
PUT,
createAuthHeader,
handleAPIError,
} from "@/api/requests";
import { ProgressNote } from "@/components/ProgressNotes/types";

export const createAuthHeader = (firebaseToken: string) => ({
Authorization: `Bearer ${firebaseToken}`,
});

export async function createProgressNote(
studentId: string,
dateLastUpdated: Date,
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/api/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,7 @@ export function handleAPIError(error: unknown): APIError {
}
return { success: false, error: `Unknown error; ${String(error)}` };
}

export const createAuthHeader = (firebaseToken: string) => ({
Authorization: `Bearer ${firebaseToken}`,
});
4 changes: 1 addition & 3 deletions frontend/src/api/sessions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { GET, PATCH, POST, handleAPIError } from "../api/requests";

import { createAuthHeader } from "./progressNotes";
import { GET, PATCH, POST, createAuthHeader, handleAPIError } from "../api/requests";

import type { APIResult } from "../api/requests";

Expand Down
4 changes: 1 addition & 3 deletions frontend/src/api/students.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { DELETE, GET, POST, PUT, handleAPIError } from "../api/requests";
import { DELETE, GET, POST, PUT, createAuthHeader, handleAPIError } from "../api/requests";
import { StudentData as CreateStudentRequest } from "../components/StudentForm/types";

import { createAuthHeader } from "./progressNotes";

import type { APIResult } from "../api/requests";

export type Student = CreateStudentRequest & {
Expand Down
1 change: 0 additions & 1 deletion frontend/src/api/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export const verifyUser = async (firebaseToken: string): Promise<APIResult<User>
export async function getNotApprovedUsers(firebaseToken: string): Promise<APIResult<User[]>> {
try {
const headers = createAuthHeader(firebaseToken);
console.log(headers);
const response = await GET("/user/not-approved", headers);
const json = (await response.json()) as User[];
return { success: true, data: json };
Expand Down
Loading

0 comments on commit ca89496

Please sign in to comment.