Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/lisasiliu/view-student-profile-v2 #103

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
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
45 changes: 45 additions & 0 deletions backend/src/controllers/student.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { validationResult } from "express-validator";
import mongoose, { HydratedDocument } from "mongoose";

import EnrollmentModel from "../models/enrollment";
import { Image } from "../models/image";
import ProgressNoteModel from "../models/progressNote";
import StudentModel from "../models/student";
import { Enrollment } from "../types/enrollment";
import { createEnrollment, editEnrollment } from "../util/enrollment";
Expand Down Expand Up @@ -89,3 +91,46 @@ export const getAllStudents: RequestHandler = async (_, res, next) => {
next(error);
}
};

export const getStudent: RequestHandler = async (req, res, next) => {
try {
const errors = validationResult(req);

validationErrorParser(errors);

const studentId = req.params.id;
const studentData = await StudentModel.findById(req.params.id);

if (!studentData) {
return res.status(404).json({ message: "Student not found" });
}

const enrollments = await EnrollmentModel.find({ studentId });

res.status(200).json({ ...studentData.toObject(), programs: enrollments });
} catch (error) {
next(error);
}
};

export const deleteStudent: RequestHandler = async (req, res, next) => {
try {
const errors = validationResult(req);
validationErrorParser(errors);

const studentId = req.params.id;
const deletedStudent = await StudentModel.findById(studentId);
if (!deletedStudent) {
return res.status(404).json({ message: "Student not found" });
}

await EnrollmentModel.deleteMany({ studentId });
await ProgressNoteModel.deleteMany({ studentId });
await Image.deleteMany({ userId: studentId });
await StudentModel.deleteOne({ _id: studentId });

res.status(200).json(deletedStudent);
} catch (error) {
next(error);
}
};
4 changes: 3 additions & 1 deletion backend/src/routes/student.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import express from "express";

import * as StudentController from "../controllers/student";
import { verifyAuthToken } from "../validators/auth";
import * as StudentValidator from "../validators/student";

const router = express.Router();

router.post("/create", StudentValidator.createStudent, StudentController.createStudent);
router.put("/edit/:id", StudentValidator.editStudent, StudentController.editStudent);
router.get("/all", StudentController.getAllStudents);
router.get("/:id", [verifyAuthToken], StudentController.getStudent);
router.delete("/:id", [verifyAuthToken], StudentController.deleteStudent);

export default router;
10 changes: 10 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"react-day-picker": "^8.10.0",
"react-dom": "^18",
"react-hook-form": "^7.49.3",
"react-to-print": "^3.0.2",
"tailwind-merge": "^2.2.0",
"tailwindcss": "^3.4.1",
"tailwindcss-animate": "^1.0.7",
Expand Down
3 changes: 3 additions & 0 deletions frontend/public/back-arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/public/email.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions frontend/public/exclamation.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/public/pencil.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions frontend/public/phone.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions frontend/public/profile-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion frontend/src/api/programs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ export type Enrollment = {
dateUpdated: Date;
hoursLeft: number;
schedule: string[];
sessionTime: string[];
sessionTime: {
start_time: string;
end_time: string;
};
required: true;
startDate: Date;
renewalDate: Date;
authNumber: string;
Expand Down
32 changes: 31 additions & 1 deletion frontend/src/api/students.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { GET, POST, PUT, handleAPIError } from "../api/requests";
import { DELETE, GET, POST, PUT, 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 & {
_id: string;
medication: string;
otherString: string;
progressNotes?: string[];
UCINumber?: string;
conservation?: boolean;
profilePicture?: string;
};

export async function createStudent(student: CreateStudentRequest): Promise<APIResult<Student>> {
Expand Down Expand Up @@ -40,3 +45,28 @@ export async function getAllStudents(): Promise<APIResult<[Student]>> {
return handleAPIError(error);
}
}

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

export async function deleteStudent(
id: string,
firebaseToken: string,
): Promise<APIResult<Student>> {
try {
const headers = createAuthHeader(firebaseToken);
const response = await DELETE(`/student/${id}`, undefined, headers);
const json = (await response.json()) as Student;
return { success: true, data: json };
} catch (error) {
return handleAPIError(error);
}
}
13 changes: 12 additions & 1 deletion frontend/src/components/Modals/ModalConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { Dialog, DialogClose, DialogContent, DialogTrigger } from "../ui/dialog"

type ModalConfirmationProps = {
ref?: React.RefObject<HTMLDivElement>;
// Optional dialog styling
className?: string;
icon: React.ReactNode;
triggerElement: React.ReactNode;
onCancelClick?: (e: React.MouseEvent) => void;
Expand All @@ -15,6 +17,8 @@ type ModalConfirmationProps = {
confirmText: string;
kind: "primary" | "destructive";
nestedDialog?: React.ReactNode;
//Used for inner react content (between title and buttons)
innerContent?: React.ReactNode;
//Used for nested dialog to close parent dialog
isParentOpen?: boolean;
setIsParentOpen?: React.Dispatch<React.SetStateAction<boolean>>;
Expand All @@ -23,6 +27,7 @@ type ModalConfirmationProps = {
const ModalConfirmation = forwardRef<HTMLDivElement, ModalConfirmationProps>(
(
{
className,
icon,
triggerElement,
onCancelClick = () => {},
Expand All @@ -33,6 +38,7 @@ const ModalConfirmation = forwardRef<HTMLDivElement, ModalConfirmationProps>(
confirmText,
kind,
nestedDialog,
innerContent,
isParentOpen,
setIsParentOpen,
},
Expand All @@ -43,12 +49,17 @@ const ModalConfirmation = forwardRef<HTMLDivElement, ModalConfirmationProps>(
<DialogTrigger asChild>{triggerElement}</DialogTrigger>
<DialogContent
ref={ref}
className="max-h-[50%] max-w-[80%] rounded-[8px] md:max-w-[50%] lg:max-w-[25%]"
className={
className
? className
: "max-h-[50%] max-w-[80%] rounded-[8px] md:max-w-[50%] lg:max-w-[25%]"
}
>
<div className="grid place-items-center p-3 min-[450px]:p-10">
<div className="mb-8">{icon}</div>
<h3 className="text-bold mb-2 text-lg font-bold">{title}</h3>
{description ? <p className="text-center">{description}</p> : null}
{innerContent}
<div className="grid justify-center gap-5 pt-6 min-[450px]:flex min-[450px]:w-[70%] min-[450px]:justify-between min-[450px]:[&>*]:basis-full">
<DialogClose asChild>
<Button
Expand Down
10 changes: 8 additions & 2 deletions frontend/src/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,20 @@ const Links = ({ setShelf, isMobile }: LinkProps) => {
function Navigation({ children }: { children: React.ReactNode }) {
const router = useRouter();
const [offset, setOffset] = React.useState(0);
const [isVisible, setVisible] = React.useState(true);
const [shelf, setShelf] = React.useState(false); // on mobile whether the navbar is open
const { isMobile } = useWindowSize();
const navigation = useNavigation();

useEffect(() => {
const ordering = navigation.map((item) => item.href);
const idx = ordering.indexOf(router.pathname) || 0;
setOffset(idx * 68);
if (idx === -1)
setVisible(false); // if the page is not on the navigation list, do not display bar
else {
if (!isVisible) setVisible(true);
setOffset(idx * 68);
}
}, [router.pathname]);

return (
Expand Down Expand Up @@ -124,7 +130,7 @@ function Navigation({ children }: { children: React.ReactNode }) {
>
<Logo setShelf={setShelf} isMobile={isMobile} />
<div className="relative flex flex-col gap-7 max-sm:gap-2 sm:max-lg:flex-row">
{router.pathname !== "/" && (
{router.pathname !== "/" && isVisible && (
<div
className="absolute h-10 w-2 rounded-br-lg rounded-tr-lg bg-[white] max-lg:w-1 sm:max-lg:hidden"
style={{
Expand Down
23 changes: 15 additions & 8 deletions frontend/src/components/StudentFormButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Image from "next/image";
import { Dispatch, SetStateAction, useContext, useState } from "react";
import { SubmitHandler, useForm } from "react-hook-form";

Expand Down Expand Up @@ -161,13 +160,21 @@ export default function StudentFormButton({
<Dialog open={openForm} onOpenChange={setOpenForm}>
<DialogTrigger asChild>
{type === "edit" ? (
<Image
src="/eye.svg"
alt="view student"
width={40}
height={40}
className="cursor-pointer"
/>
<div className="flex cursor-pointer space-x-[5px]">
<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>Edit Mode</div>
</div>
) : (
<Button
label="Add Student"
Expand Down
Loading
Loading