From e078081b6db25244399eabf3a65f8cf4dc8903dc Mon Sep 17 00:00:00 2001 From: Aaron Chan <42254254+aaronchan32@users.noreply.github.com> Date: Mon, 20 May 2024 01:27:26 -0700 Subject: [PATCH] Squashed commit of the following: commit 0b133b8a0f9465224f927564ff42c7ee95798d38 Author: Michael Sullivan <96799955+mraysu@users.noreply.github.com> Date: Tue May 14 09:09:16 2024 -0700 Feature/mraysu/program form v2 (#100) * Update Backend Program Schema * V2 UI * Disabled Editing Program Type * Frontend-backend integration * Lint fixes --------- Co-authored-by: mraysu Co-authored-by: Adhithya Ananthan <85322002+adhi0331@users.noreply.github.com> commit e17b509601b89d1a34eaddf55d652b5213ea9d8d Author: parth4apple <72187062+parth4apple@users.noreply.github.com> Date: Tue May 14 09:01:15 2024 -0700 Student and Enrollment Schema modifications (#101) * feat: initial schema * feat: edit routes * feat: test and fix routes --- backend/src/controllers/program.ts | 5 - backend/src/controllers/student.ts | 115 ++++------- backend/src/models/enrollment.ts | 28 +++ backend/src/models/program.ts | 8 +- backend/src/models/student.ts | 18 +- backend/src/types/enrollment.ts | 13 ++ backend/src/types/programLink.ts | 8 - backend/src/util/enrollment.ts | 21 ++ backend/src/util/student.ts | 67 +++---- backend/src/validators/program.ts | 49 +++-- backend/src/validators/student.ts | 59 +++++- frontend/src/api/programs.ts | 2 +- frontend/src/components/ProgramCard.tsx | 22 ++- .../components/ProgramForm/ProgramArchive.tsx | 103 +++++++++- .../components/ProgramForm/ProgramCancel.tsx | 23 ++- .../components/ProgramForm/ProgramInfo.tsx | 86 +++------ frontend/src/components/ProgramForm/types.ts | 17 +- frontend/src/components/ProgramFormButton.tsx | 179 +++--------------- .../StudentsTable/useColumnSchema.tsx | 4 +- 19 files changed, 424 insertions(+), 403 deletions(-) create mode 100644 backend/src/models/enrollment.ts create mode 100644 backend/src/types/enrollment.ts delete mode 100644 backend/src/types/programLink.ts create mode 100644 backend/src/util/enrollment.ts diff --git a/backend/src/controllers/program.ts b/backend/src/controllers/program.ts index 581f4db2..a2e93391 100644 --- a/backend/src/controllers/program.ts +++ b/backend/src/controllers/program.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/no-misused-promises */ import { RequestHandler } from "express"; import { validationResult } from "express-validator"; -import { Schema } from "mongoose"; //import { error } from "firebase-functions/logger"; import ProgramModel from "../models/program"; @@ -13,11 +12,7 @@ export type Program = { abbreviation: string; type: string; daysOfWeek: string[]; - startDate: Date; - endDate: Date; color: string; //colorValueHex; - studentUIDs: Schema.Types.ObjectId[]; - renewalDate: Date; hourlyPay: string; sessions: [string[]]; }; diff --git a/backend/src/controllers/student.ts b/backend/src/controllers/student.ts index a93056ae..ef5e965e 100644 --- a/backend/src/controllers/student.ts +++ b/backend/src/controllers/student.ts @@ -5,54 +5,16 @@ import { RequestHandler } from "express"; import { validationResult } from "express-validator"; +import mongoose, { HydratedDocument } from "mongoose"; +import EnrollmentModel from "../models/enrollment"; import StudentModel from "../models/student"; -import { programLink } from "../types/programLink"; -import { addStudentToPrograms, removeStudentFromPrograms } from "../util/student"; +import { Enrollment } from "../types/enrollment"; +import { createEnrollment, editEnrollment } from "../util/enrollment"; import validationErrorParser from "../util/validationErrorParser"; -export type contact = { - lastName: string; - firstName: string; - email: string; - phoneNumber: string; -}; - -export type typedModel = { - student: contact; - emergency: contact; - serviceCoordinator: contact; - location: string; - medication: string; - birthday: string; - intakeDate: string; - tourDate: string; - programs: programLink[]; - dietary: string[]; - otherString: string; -}; - -type Contact = { - lastName: string; - firstName: string; - email: string; - phoneNumber: string; -}; - -type Student = { - _id: string; - student: Contact; - emergency: Contact; - serviceCoordinator: Contact; - location: string; - medication?: string; - birthday: Date; - intakeDate: Date; - tourDate: Date; - programs: programLink[]; - dietary: string[]; - otherString?: string; -}; +type Student = HydratedDocument; +type StudentRequest = Student & { enrollments: Enrollment[] }; export const createStudent: RequestHandler = async (req, res, next) => { try { @@ -60,9 +22,14 @@ export const createStudent: RequestHandler = async (req, res, next) => { validationErrorParser(errors); - const newStudent = await StudentModel.create(req.body as typedModel); - const programIds = newStudent.programs.map((programObj: programLink) => programObj.programId); - await addStudentToPrograms(newStudent._id, programIds); + const { enrollments, ...studentData } = req.body as StudentRequest; + const newStudent = await StudentModel.create(studentData); + // create enrollments for the student + await Promise.all( + enrollments.map(async (program: Enrollment) => { + await createEnrollment({ ...program, studentId: newStudent._id }); + }), + ); res.status(201).json(newStudent); } catch (error) { @@ -77,34 +44,29 @@ export const editStudent: RequestHandler = async (req, res, next) => { validationErrorParser(errors); const studentId = req.params.id; - const studentData = req.body as Student; + const { enrollments, ...studentData } = req.body as StudentRequest; - if (studentId !== studentData._id) { + if (studentId !== studentData._id.toString()) { return res.status(400).json({ message: "Invalid student ID" }); } - - const prevStudent = await StudentModel.findById(studentId); - const editedStudent = await StudentModel.findOneAndUpdate({ _id: studentId }, studentData, { + const updatedStudent = await StudentModel.findByIdAndUpdate(studentId, studentData, { new: true, }); - - if (!prevStudent || !editedStudent) { - return res.status(404).json({ message: "No object in database with provided ID" }); + if (!updatedStudent) { + return res.status(404).json({ message: "Student not found" }); } - // remove student from possibly stale programs - const prevProgramIds = prevStudent.programs.map( - (programObj: programLink) => programObj.programId, - ); - await removeStudentFromPrograms(prevStudent._id, prevProgramIds); - - // add student to new programs - const newProgramIds = editedStudent.programs.map( - (programObj: programLink) => programObj.programId, + // update enrollments for the student + await Promise.all( + enrollments.map(async (enrollment: Enrollment) => { + const enrollmentExists = await EnrollmentModel.findById(enrollment._id); + const enrollmentBody = { ...enrollment, studentId: new mongoose.Types.ObjectId(studentId) }; + if (!enrollmentExists) await createEnrollment(enrollmentBody); + else await editEnrollment(enrollmentBody); + }), ); - await addStudentToPrograms(editedStudent._id, newProgramIds); - res.status(200).json(editedStudent); + res.status(200).json({ ...updatedStudent, enrollments }); } catch (error) { next(error); } @@ -114,26 +76,15 @@ export const getAllStudents: RequestHandler = async (_, res, next) => { try { const students = await StudentModel.find(); - res.status(200).json(students); - } catch (error) { - next(error); - } -}; - -export const deleteAllStudents: RequestHandler = async (_, res, next) => { - try { - // remove students from all programs - const students = await StudentModel.find(); - await Promise.all( + // gather all enrollments for each student and put them in student.programs + const hydratedStudents = await Promise.all( students.map(async (student) => { - const programIds = student.programs.map((programObj: programLink) => programObj.programId); - await removeStudentFromPrograms(student._id, programIds); + const enrollments = await EnrollmentModel.find({ studentId: student._id }); + return { ...student.toObject(), programs: enrollments }; }), ); - await StudentModel.deleteMany(); - - res.status(204).end(); + res.status(200).json(hydratedStudents); } catch (error) { next(error); } diff --git a/backend/src/models/enrollment.ts b/backend/src/models/enrollment.ts new file mode 100644 index 00000000..8d7b7d9b --- /dev/null +++ b/backend/src/models/enrollment.ts @@ -0,0 +1,28 @@ +import mongoose, { InferSchemaType } from "mongoose"; + +const enrollmentSchema = new mongoose.Schema({ + studentId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Student", + required: true, + unique: false, + }, + programId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Program", + required: true, + unique: false, + }, + status: { type: String, required: true }, + dateUpdated: { type: Date, required: true, default: Date.now() }, + hoursLeft: { type: Number, required: true }, + schedule: { type: [String], required: true }, + sessionTime: { type: [String], required: true }, + startDate: { type: Date, required: true }, + renewalDate: { type: Date, required: true }, + authNumber: { type: String, required: true }, +}); + +type Enrollment = InferSchemaType; + +export default mongoose.model("Enrollment", enrollmentSchema); diff --git a/backend/src/models/program.ts b/backend/src/models/program.ts index 7d558e09..bc39d1d7 100644 --- a/backend/src/models/program.ts +++ b/backend/src/models/program.ts @@ -2,15 +2,11 @@ import { InferSchemaType, Schema, model } from "mongoose"; const programSchema = new Schema({ name: { type: String, required: true }, - abbreviation: { type: String, required: true }, // e.g. ENTR + abbreviation: { type: String, required: true }, // e.g. ENTR, should be unique type: { type: String, required: true }, // regular vs. varying daysOfWeek: { type: [String], required: true }, // M, T, W, TH, F - startDate: { type: Date, required: true }, - endDate: { type: Date, required: true }, color: { type: String, required: true }, - students: { type: [Schema.Types.ObjectId], ref: "Students", required: false }, - renewalDate: { type: Date, required: true }, - hourly: { type: Number, required: true }, + hourlyPay: { type: Number, required: true }, sessions: { type: [[String]], required: true }, }); diff --git a/backend/src/models/student.ts b/backend/src/models/student.ts index f530f8c9..db6ad8b9 100644 --- a/backend/src/models/student.ts +++ b/backend/src/models/student.ts @@ -35,17 +35,11 @@ const studentSchema = new Schema({ intakeDate: { type: Date, required: true }, tourDate: { type: Date, required: true }, - programs: { - type: [ - { - programId: { type: Schema.Types.ObjectId, ref: "Program", required: true }, - status: { type: String, required: true }, - dateUpdated: { type: Date, required: true, default: Date.now() }, - hoursLeft: { type: Number, required: true }, - }, - ], - required: true, - }, + conservation: { type: Boolean, required: true }, + UCINumber: { type: String, required: true }, + incidentForm: { type: String, required: true }, + documents: { type: [String], required: true }, + profilePicture: { type: Schema.Types.ObjectId, ref: "Image", required: false }, progressNotes: { type: [Schema.Types.ObjectId], @@ -56,8 +50,6 @@ const studentSchema = new Schema({ //Will contain list of all dietary restrictions dietary: { type: [String] }, - - otherString: { type: String, default: "" }, }); type Student = InferSchemaType; diff --git a/backend/src/types/enrollment.ts b/backend/src/types/enrollment.ts new file mode 100644 index 00000000..82149754 --- /dev/null +++ b/backend/src/types/enrollment.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import mongoose, { Schema } from "mongoose"; + +import EnrollmentModel from "../models/enrollment"; + +// get the enrollment type from the enrollment model +export type Enrollment = Extract< + typeof EnrollmentModel, + mongoose.Model +> extends mongoose.Model + ? U & { _id: Schema.Types.ObjectId } + : never; diff --git a/backend/src/types/programLink.ts b/backend/src/types/programLink.ts deleted file mode 100644 index 43d0088b..00000000 --- a/backend/src/types/programLink.ts +++ /dev/null @@ -1,8 +0,0 @@ -import mongoose from "mongoose"; - -export type programLink = { - programId: mongoose.Types.ObjectId; - status: string; - dateUpdated: Date; - hoursLeft: number; -}; diff --git a/backend/src/util/enrollment.ts b/backend/src/util/enrollment.ts new file mode 100644 index 00000000..0c92e3c1 --- /dev/null +++ b/backend/src/util/enrollment.ts @@ -0,0 +1,21 @@ +import EnrollmentModel from "../models/enrollment"; +import { Enrollment } from "../types/enrollment"; + +export const createEnrollment = async (req: Enrollment) => { + try { + await EnrollmentModel.create(req); + } catch (e) { + console.log(e); + throw e; + } +}; + +export const editEnrollment = async (req: Enrollment) => { + try { + console.log(req); + await EnrollmentModel.findByIdAndUpdate(req._id, req); + } catch (e) { + console.log(e); + throw e; + } +}; diff --git a/backend/src/util/student.ts b/backend/src/util/student.ts index 13c0224d..39c564ec 100644 --- a/backend/src/util/student.ts +++ b/backend/src/util/student.ts @@ -1,58 +1,53 @@ import mongoose from "mongoose"; import ProgramModel from "../models/program"; -import { programLink } from "../types/programLink"; - -type ObjectId = mongoose.Types.ObjectId; - -export const programValidatorUtil = async (programs: programLink[]) => { +import { Enrollment } from "../types/enrollment"; + +export const programValidatorUtil = async (enrollments: Enrollment[]) => { + // verify all fields are present + const requiredFields = [ + "programId", + "status", + "hoursLeft", + "schedule", + "sessionTime", + "startDate", + "renewalDate", + "authNumber", + ]; + enrollments.forEach((enrollment: Enrollment) => { + requiredFields.forEach((field) => { + if (!enrollment[field as keyof Enrollment]) + throw new Error(`Field ${field} is required on enrollment`); + }); + }); + + // verify statuses are correct and student is not in more than 2 programs const allowedStatuses = ["Joined", "Waitlisted", "Archived", "Not a fit"]; const programIds = new Set(); let active = 0; let varying = 0; await Promise.all( - programs.map(async (program) => { - programIds.add(program.programId); - if (!mongoose.Types.ObjectId.isValid(program.programId)) + enrollments.map(async (enrollment) => { + programIds.add(enrollment.programId); + if (!mongoose.Types.ObjectId.isValid(enrollment.programId)) throw new Error("Program ID format is invalid"); - if (!allowedStatuses.includes(program.status)) + if (!allowedStatuses.includes(enrollment.status)) throw new Error("Status must be one of: " + allowedStatuses.join(", ")); - const programType = (await ProgramModel.findById(program.programId))?.type; - if (program.status === "Joined") { + const programType = (await ProgramModel.findById(enrollment.programId))?.type; + if (enrollment.status === "Joined") { active++; if (programType === "varying") varying++; } }), ); - if (programIds.size !== programs.length) throw new Error("Programs must be unique"); + + // handle error reporting + if (programIds.size !== enrollments.length) throw new Error("Programs must be unique"); if (active > 2) throw new Error("Student can only be active in 2 programs"); if (varying > 1) throw new Error("Student can only be in 1 varying program"); return true; }; - -export const addStudentToPrograms = async (studentId: ObjectId, programIds: ObjectId[]) => { - await Promise.all( - programIds.map(async (programId) => { - await ProgramModel.findByIdAndUpdate( - programId, - { $push: { students: studentId } }, - { new: true }, - ); - }), - ); -}; - -export const removeStudentFromPrograms = async (studentId: ObjectId, programIds: ObjectId[]) => { - await Promise.all( - programIds.map(async (programId) => { - await ProgramModel.findByIdAndUpdate( - programId, - { $pull: { students: studentId } }, - { new: true }, - ); - }), - ); -}; diff --git a/backend/src/validators/program.ts b/backend/src/validators/program.ts index 702113c4..0b30bb70 100644 --- a/backend/src/validators/program.ts +++ b/backend/src/validators/program.ts @@ -2,6 +2,7 @@ import { body } from "express-validator"; //import mongoose from "mongoose"; import { Program } from "../controllers/program"; +import ProgramModel from "../models/program"; const makeNameValidator = () => body("name") @@ -27,7 +28,15 @@ const makeAbbreviationValidator = () => .withMessage("abbreviation must be a string") .bail() .notEmpty() - .withMessage("abbreviation must not be empty"); + .withMessage("abbreviation must not be empty") + .custom(async (value: string, { req }) => { + const program = await ProgramModel.findOne({ + _id: { $ne: (req.body as Program)._id }, + abbreviation: value, + }); + if (program) throw new Error("Program Abbreviation must be unique"); + return true; + }); const makeTypeValidator = () => body("type") .exists() @@ -50,7 +59,12 @@ const makeDaysOfWeekValidator = () => .bail() .isArray() .withMessage("days of week selection must be an array") - .custom((value: string[]) => { + .custom((value: string[], { req }) => { + if ((req.body as Program).type === "varying") { + if (value.length !== 0) throw new Error("Varying sessions cannot assigned Days of Week"); + return true; + } + if (value.length === 0) throw new Error("days of week selection needed"); for (const valuei of value) { if ( @@ -66,7 +80,7 @@ const makeDaysOfWeekValidator = () => } return true; }); -const makeStartDateValidator = () => +/*const makeStartDateValidator = () => body("startDate") .exists() .withMessage("start date needed") @@ -86,7 +100,8 @@ const makeEndDateValidator = () => if (new Date(value) < new Date(reqBody.startDate)) throw new Error("end date must be after start date"); return true; - }); + });*/ + const makeColorValidator = () => body("color") .exists() @@ -133,15 +148,16 @@ const makeColorValidator = () => }) .bail() .withMessage("students must be valid student ids");*/ -const makeRenewalDateValidator = () => +/*const makeRenewalDateValidator = () => body("renewalDate") .exists() .withMessage("renewal date needed") .bail() .isISO8601() - .withMessage("renewal date must be a valid date-time string"); + .withMessage("renewal date must be a valid date-time string");*/ + const makeHourlyPayValidator = () => - body("hourly") + body("hourlyPay") .exists() .withMessage("hourly pay needed") .bail() @@ -157,7 +173,12 @@ const makeSessionsValidator = () => .isArray() .withMessage("Sessions must be a 2D String Array") .bail() - .custom((sessions: string[][]) => { + .custom((sessions: string[][], { req }) => { + if ((req.body as Program).type === "varying") { + if (sessions.length !== 0) throw new Error("Varying sessions cannot have session times"); + return true; + } + // Assumes program type is regular if (sessions.length === 0) throw new Error("Must specify a session time"); sessions.forEach((session) => { if (!Array.isArray(session)) throw new Error("Session must be an array"); @@ -178,10 +199,10 @@ export const createProgram = [ makeAbbreviationValidator(), makeTypeValidator(), makeDaysOfWeekValidator(), - makeStartDateValidator(), - makeEndDateValidator(), + //makeStartDateValidator(), + //makeEndDateValidator(), makeColorValidator(), - makeRenewalDateValidator(), + //makeRenewalDateValidator(), makeHourlyPayValidator(), makeSessionsValidator(), ]; @@ -191,10 +212,10 @@ export const updateProgram = [ makeAbbreviationValidator(), makeTypeValidator(), makeDaysOfWeekValidator(), - makeStartDateValidator(), - makeEndDateValidator(), + //makeStartDateValidator(), + //makeEndDateValidator(), makeColorValidator(), - makeRenewalDateValidator(), + //makeRenewalDateValidator(), makeHourlyPayValidator(), makeSessionsValidator(), //makeStudentUIDsValidator(), diff --git a/backend/src/validators/student.ts b/backend/src/validators/student.ts index 302bcba6..aa7254d8 100644 --- a/backend/src/validators/student.ts +++ b/backend/src/validators/student.ts @@ -115,13 +115,57 @@ const makeTourDateValidator = () => .toDate() .withMessage("Tour Date string must be a valid date-time string"); -const makePrograms = () => - body("programs") +const makeConservationValidator = () => + body("conservation") .exists() - .withMessage("Programs field required") + .withMessage("Conservation field required") + .bail() + .isBoolean() + .withMessage("Conservation must be a boolean"); + +const makeUCINumberValidator = () => + body("UCINumber") + .exists() + .withMessage("UCI Number field required") + .bail() + .isString() + .withMessage("UCI Number must be a string") + .bail() + .notEmpty() + .withMessage("UCI Number field required"); + +const makeIncidentFormValidator = () => + body("incidentForm") + .exists() + .withMessage("Incident Form field required") + .bail() + .isString() + .withMessage("Incident Form must be a string") + .bail() + .notEmpty() + .withMessage("Incident Form field required"); + +const makeDocumentsValidator = () => + body("documents") + .exists() + .withMessage("Documents field required") + .bail() + .isArray() + .withMessage("Documents must be an array") + .bail() + .custom((value: string[]) => value.every((doc) => typeof doc === "string")) + .withMessage("Documents must be an array of strings"); + +const makeProfilePictureValidator = () => + body("profilePicture").optional().isString().withMessage("Profile picture must be a string"); + +const makeEnrollments = () => + body("enrollments") + .exists() + .withMessage("Enrollments field required") .bail() .isArray() - .withMessage("Programs must be a non-empty array") + .withMessage("Enrollments must be a non-empty array") .bail() .custom(programValidatorUtil); @@ -153,7 +197,12 @@ export const createStudent = [ makeBirthdayValidator(), makeIntakeDateValidator(), makeTourDateValidator(), - makePrograms(), + makeConservationValidator(), + makeUCINumberValidator(), + makeIncidentFormValidator(), + makeDocumentsValidator(), + makeProfilePictureValidator(), + makeEnrollments(), makeDietaryArrayValidator(), makeDietaryItemsValidator(), makeDietaryOtherValidator(), diff --git a/frontend/src/api/programs.ts b/frontend/src/api/programs.ts index 7bafeda0..83b82f2f 100644 --- a/frontend/src/api/programs.ts +++ b/frontend/src/api/programs.ts @@ -3,7 +3,7 @@ import { CreateProgramRequest } from "../components/ProgramForm/types"; import type { APIResult } from "../api/requests"; -export type Program = CreateProgramRequest & { _id: string; students: string[] }; +export type Program = CreateProgramRequest & { _id: string }; export async function createProgram(program: CreateProgramRequest): Promise> { try { diff --git a/frontend/src/components/ProgramCard.tsx b/frontend/src/components/ProgramCard.tsx index e406d9c2..b54da3b8 100644 --- a/frontend/src/components/ProgramCard.tsx +++ b/frontend/src/components/ProgramCard.tsx @@ -87,13 +87,13 @@ export function ProgramCard({ program, isAdmin, className, setPrograms }: CardPr abbreviation: program.abbreviation, type: program.type, daysOfWeek: program.daysOfWeek, - startDate: program.startDate, - endDate: program.endDate, + //startDate: program.startDate, + //endDate: program.endDate, color: program.color, - renewalDate: program.renewalDate, - hourly: program.hourly, + //renewalDate: program.renewalDate, + hourlyPay: program.hourlyPay, sessions: program.sessions, - students: program.students, + //students: program.students, }; if (isTablet) { @@ -196,11 +196,13 @@ export function ProgramCard({ program, isAdmin, className, setPrograms }: CardPr width={18} className={iconClass} /> - {program.students.length === 0 &&

No Students

} - {program.students.length === 1 &&

1 Student

} - {program.students.length > 1 && ( -

{program.students.length} Students

- )} + {/*program.students.length === 0 &&

No Students

*/} + {/*program.students.length === 1 &&

1 Student

*/} + { + //program.students.length > 1 && ( +

{/*program.students.length*/}0 Students

+ //) + } diff --git a/frontend/src/components/ProgramForm/ProgramArchive.tsx b/frontend/src/components/ProgramForm/ProgramArchive.tsx index a81b9b5f..480421f8 100644 --- a/frontend/src/components/ProgramForm/ProgramArchive.tsx +++ b/frontend/src/components/ProgramForm/ProgramArchive.tsx @@ -1,8 +1,22 @@ +import { useState } from "react"; +import { useForm } from "react-hook-form"; + +import { Program } from "../../api/programs"; +import { Button } from "../Button"; +import { Textfield } from "../Textfield"; +import { Dialog, DialogClose, DialogContentSlide, DialogTrigger } from "../ui/dialog"; + type props = { label: string; }; -export default function ProgramArchiveHeader({ label }: props) { +type archiveProps = { + setOpenParent: React.Dispatch>; + data: Program; + isMobile?: boolean; +}; + +function ProgramArchiveHeader({ label }: props) { return (

@@ -32,3 +46,90 @@ export default function ProgramArchiveHeader({ label }: props) {

); } + +//Currently no functionality, just a button that closes the form +export default function ProgramArchive({ setOpenParent, data, isMobile = false }: archiveProps) { + const [openArchive, setOpenArchive] = useState(false); + const { + register: archiveRegister, + reset: archiveReset, + setValue: setArchiveCalendarValue, + getValues: getArchiveValue, + } = useForm<{ date: string }>(); + + return ( + +
+ + {!isMobile ? ( +
+ +
+
+ +

Confirm by entering today's date

+
+ +
+ +
{" "} + +
{" "} +
+
+
+ ); +} diff --git a/frontend/src/components/ProgramForm/ProgramCancel.tsx b/frontend/src/components/ProgramForm/ProgramCancel.tsx index e3aa585a..d3be0663 100644 --- a/frontend/src/components/ProgramForm/ProgramCancel.tsx +++ b/frontend/src/components/ProgramForm/ProgramCancel.tsx @@ -3,15 +3,24 @@ import { Dialog, DialogClose, DialogContent, DialogTrigger } from "../ui/dialog" type cancelProps = { isMobile?: boolean; + open: boolean; + setOpen: React.Dispatch>; onCancel: () => void; }; -export default function ProgramCancel({ isMobile = false, onCancel }: cancelProps) { +export default function ProgramCancel({ isMobile = false, open, setOpen, onCancel }: cancelProps) { return ( - + {!isMobile ? ( - - )} + {type === "edit" && data && }

{type === "add" ? "Add new program" : data?.name} @@ -194,12 +132,17 @@ export default function ProgramFormButton({ register={register} data={data ?? null} setCalendarValue={setCalendarValue} + mode={type} />
{" "} - - {" "} - - -

+ {type === "edit" && data && ( + )} + {type === "add" ? (

Add new program @@ -302,6 +184,7 @@ export default function ProgramFormButton({ register={register} data={data ?? null} setCalendarValue={setCalendarValue} + mode={type} />
diff --git a/frontend/src/components/StudentsTable/useColumnSchema.tsx b/frontend/src/components/StudentsTable/useColumnSchema.tsx index 6473fe4f..ef6a4ade 100644 --- a/frontend/src/components/StudentsTable/useColumnSchema.tsx +++ b/frontend/src/components/StudentsTable/useColumnSchema.tsx @@ -42,8 +42,8 @@ const ProgramPopover = ({ link, program }: { link: ProgramLink; program: Program 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(/*program.startDate*/).toLocaleDateString("en-US")], + ["Renewal Date", new Date(/*program.endDate*/).toLocaleDateString("en-US")], ["Hours Left", link.hoursLeft.toString()], ];