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

V2 Student Form #108

Merged
merged 19 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
23 changes: 22 additions & 1 deletion backend/src/controllers/student.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,22 @@
}
};

export const getAllStudents: RequestHandler = async (_, res, next) => {
export const getAllStudents: RequestHandler = async (req, res, next) => {
try {
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 120 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") {
students.forEach((student) => {
student.documents = student.documents.filter(
(doc) => !doc.markedAdmin,
) as typeof student.documents;
});
}

res.status(200).json(students);
} catch (error) {
next(error);
Expand All @@ -126,6 +138,8 @@
try {
const errors = validationResult(req);

const { accountType } = req.body;

Check warning on line 141 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);

const studentId = req.params.id;
Expand All @@ -135,6 +149,13 @@
return res.status(404).json({ message: "Student not found" });
}

// Ensure that documents that are marked admin are not returned to non-admin users
if (accountType !== "admin") {
studentData.documents = studentData.documents.filter(
(doc) => !doc.markedAdmin,
) as typeof studentData.documents;
}

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

res.status(200).json({ ...studentData.toObject(), enrollments });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ export const useProgressNotes = () => {
}
};

const fetchStudentData = async (progressNotes: Record<string, ProgressNote>) => {
const fetchStudentData = async (progressNotes: Record<string, ProgressNote>, token: string) => {
try {
const result = await getAllStudents(firebaseToken);
const result = await getAllStudents(token);
if (result.success) {
const studentDataWithNotes: StudentWithNotes[] = result.data.map((student) => ({
...student,
Expand Down Expand Up @@ -92,7 +92,7 @@ export const useProgressNotes = () => {

const progressNotes = await fetchProgressNotes(token);
if (progressNotes) {
await fetchStudentData(progressNotes);
await fetchStudentData(progressNotes, token);
}
})
.catch((error) => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/StudentForm/StudentForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ export default function StudentForm({
Student Information
</legend>

<StudentInfo data={data ?? null} documentData={documentData} />
<StudentInfo data={data ?? null} documentData={documentData} isAdmin={isAdmin} />
</fieldset>
</div>
<div className="grid w-full gap-10 lg:grid-cols-2">
Expand Down
79 changes: 44 additions & 35 deletions frontend/src/components/StudentForm/StudentInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type StudentInfoProps = {
setStudentDocuments: Dispatch<SetStateAction<Document[]>>;
setDidDeleteOrMark: Dispatch<SetStateAction<boolean>>;
};
isAdmin: boolean;
};

const SUPPORTED_FILETYPES = [
Expand All @@ -52,7 +53,7 @@ const SUPPORTED_FILETYPES = [
"image/webp",
];

export default function StudentInfo({ classname, data, documentData }: StudentInfoProps) {
export default function StudentInfo({ classname, data, documentData, isAdmin }: StudentInfoProps) {
const { register, setValue: setCalendarValue } = useFormContext<StudentFormData>();
const [modalOpen, setModalOpen] = useState(false);
const [documentError, setDocumentError] = useState("");
Expand Down Expand Up @@ -200,6 +201,8 @@ export default function StudentInfo({ classname, data, documentData }: StudentIn
);
};

console.log({ isAdmin });

return (
<div className={cn("grid flex-1 gap-x-8 gap-y-10 md:grid-cols-2", classname)}>
<div>
Expand Down Expand Up @@ -283,45 +286,51 @@ export default function StudentInfo({ classname, data, documentData }: StudentIn
onClick={() => {
window.open(document.link, "_blank");
}}
className="rounded-tl-md rounded-tr-md border-[1px] border-solid border-black bg-white px-10 py-4"
className={`${!isAdmin ? "rounded-bl-md rounded-br-md" : ""} rounded-tl-md rounded-tr-md border-[1px] border-solid border-black bg-white px-10 py-4`}
>
View File
</button>
)}

<ModalConfirmation
icon={<GreenQuestionIcon />}
triggerElement={
<button
className={`${!document.link && "rounded-tl-md rounded-tr-md border-t-[1px]"} border-[1px] border-b-0 border-t-0 border-solid border-black bg-white px-10 py-4 text-pia_dark_green`}
>
{document.markedAdmin ? "Unmark" : "Mark"} Admin
</button>
}
title={document.markedAdmin ? "Unmark admin?" : "Mark admin only?"}
description={`${document.markedAdmin ? "Everyone will be able to" : "Only admin will"} see these files`}
confirmText={document.markedAdmin ? "Unmark" : "Mark"}
kind="primary"
onConfirmClick={() => {
handleMarkAdmin(document);
setDidDeleteOrMark(true);
}}
/>
<ModalConfirmation
icon={<RedDeleteIcon />}
triggerElement={
<button className="rounded-bl-md rounded-br-md border-[1px] border-solid border-black bg-white px-10 py-4 text-destructive">
Delete File
</button>
}
title="Are you sure you want to delete?"
confirmText="Delete"
kind="destructive"
onConfirmClick={() => {
handleDeleteDocument(document);
setDidDeleteOrMark(true);
}}
/>
{isAdmin ? (
<>
<ModalConfirmation
icon={<GreenQuestionIcon />}
triggerElement={
<button
className={`${!document.link && "rounded-tl-md rounded-tr-md border-t-[1px]"} border-[1px] border-b-0 border-t-0 border-solid border-black bg-white px-10 py-4 text-pia_dark_green`}
>
{document.markedAdmin ? "Unmark" : "Mark"} Admin
</button>
}
title={document.markedAdmin ? "Unmark admin?" : "Mark admin only?"}
description={`${document.markedAdmin ? "Everyone will be able to" : "Only admin will"} see these files`}
confirmText={document.markedAdmin ? "Unmark" : "Mark"}
kind="primary"
onConfirmClick={() => {
handleMarkAdmin(document);
setDidDeleteOrMark(true);
}}
/>
<ModalConfirmation
icon={<RedDeleteIcon />}
triggerElement={
<button className="rounded-bl-md rounded-br-md border-[1px] border-solid border-black bg-white px-10 py-4 text-destructive">
Delete File
</button>
}
title="Are you sure you want to delete?"
confirmText="Delete"
kind="destructive"
onConfirmClick={() => {
handleDeleteDocument(document);
setDidDeleteOrMark(true);
}}
/>
</>
) : (
""
)}
</PopoverContent>
</Popover>
</Fragment>
Expand Down
123 changes: 81 additions & 42 deletions frontend/src/components/StudentProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ function ProgramLayout({ enrollmentInfo }: ProgramLayoutProps) {

export default function StudentProfile({ id }: StudentProfileProps) {
const [currentView, setCurrentView] = useState<"View" | "Edit">("View");
const { firebaseUser } = useContext(UserContext);
const { firebaseUser, isAdmin } = useContext(UserContext);
const [firebaseToken, setFirebaseToken] = useState<string>();
const [notFound, setNotFound] = useState<boolean>(false);
const [studentData, setStudentData] = useState<Student>();
Expand All @@ -176,6 +176,30 @@ export default function StudentProfile({ id }: StudentProfileProps) {
setCurrentView(currentView === "View" ? "Edit" : "View");
};

const TruncateDocument = ({
documentName,
documentLength,
}: {
documentName: string;
documentLength: number;
}) => {
const minLength = 9; // Shortest truncation
const maxLength = 20; // Longest truncation
const baseName = documentName.slice(0, documentName.lastIndexOf("."));

// Use an inverse relationship: fewer documents = longer names
const dynamicLength = Math.max(
minLength,
Math.min(maxLength, 20 - Math.floor((documentLength - 1) * 2)),
);

// Only truncate and add ellipsis if the basename is longer than dynamicLength
const displayName =
baseName.length > dynamicLength ? baseName.substring(0, dynamicLength) + "..." : baseName;

return displayName;
};

const deleteStudentHandler: MouseEventHandler = () => {
const lastName = getDeleteValue("lastname");
if (studentData && firebaseToken && studentData.student.lastName === lastName) {
Expand Down Expand Up @@ -423,12 +447,24 @@ export default function StudentProfile({ id }: StudentProfileProps) {
<div id="documents" className="basis-1/2 space-y-[20px]">
<div className="font-[Poppins-Bold] text-[28px]">Documents</div>
<div className="flex space-x-[20px]">
<button className="h-[48px] w-[116px] rounded-lg border border-pia_border bg-pia_secondary_green text-pia_primary_white">
Student Info
</button>
<button className="h-[48px] w-[116px] rounded-lg border border-pia_border bg-pia_light_gray">
Waivers
</button>
{studentData.documents?.map((doc, index) => (
<button
onClick={() => {
window.open(doc.link, "_blank");
}}
key={index}
className={
index % 2 === 0
? "h-[48px] w-[116px] rounded-lg border border-pia_border bg-pia_light_gray"
: "h-[48px] w-[116px] rounded-lg border border-pia_border bg-pia_secondary_green text-pia_primary_white"
}
>
{TruncateDocument({
documentName: doc.name,
documentLength: doc.name.length,
}) || `Document ${index + 1}`}
</button>
))}
</div>
</div>
<div id="medications" className="basis-1/2 space-y-[20px]">
Expand Down Expand Up @@ -472,43 +508,46 @@ export default function StudentProfile({ id }: StudentProfileProps) {
</div>
<NotificationTable />
</div> */}

<div id="Bottom Buttons" className="flex justify-between">
<ModalConfirmation
className="h-[60%] w-[40%] rounded-[8px]"
title="Are you sure you want to delete this student?"
innerContent={
<>
<div className="flex w-[60%] justify-center">
<div className="font-base text-sm sm:text-base">
<li className="font-bold text-destructive">This cannot be undone!</li>
<li>
This will remove this student from all enrolled programs and delete all
notes and documents.
</li>
{isAdmin && (
<ModalConfirmation
className="h-[60%] w-[40%] rounded-[8px]"
title="Are you sure you want to delete this student?"
innerContent={
<>
<div className="flex w-[60%] justify-center">
<div className="font-base text-sm sm:text-base">
<li className="font-bold text-destructive">This cannot be undone!</li>
<li>
This will remove this student from all enrolled programs and delete all
notes and documents.
</li>
</div>
</div>
</div>
<div className="mx-8 mb-8 mt-6">
Enter the student&apos;s last name to proceed
<Textfield
name="lastname"
placeholder="Last Name"
register={deleteRegister}
/>
</div>
</>
}
kind="destructive"
triggerElement={
<button className="h-[48px] w-[96px] rounded-sm border border-pia_border text-pia_border">
Delete
</button>
}
confirmText="Delete"
icon={<div />}
isParentOpen={deleteDialogOpen}
setIsParentOpen={setDeleteDialogOpen}
onConfirmClick={deleteStudentHandler}
/>
<div className="mx-8 mb-8 mt-6">
Enter the student&apos;s last name to proceed
<Textfield
name="lastname"
placeholder="Last Name"
register={deleteRegister}
/>
</div>
</>
}
kind="destructive"
triggerElement={
<button className="h-[48px] w-[96px] rounded-sm border border-pia_border text-pia_border">
Delete
</button>
}
confirmText="Delete"
icon={<div />}
isParentOpen={deleteDialogOpen}
setIsParentOpen={setDeleteDialogOpen}
onConfirmClick={deleteStudentHandler}
/>
)}
<button
className="h-[48px] w-[96px] rounded-sm border border-pia_dark_green bg-pia_dark_green text-pia_primary_white"
onClick={() => {
Expand Down
Loading
Loading