Skip to content

Commit e59620c

Browse files
committed
2 parents 6e6a7aa + 5710fff commit e59620c

File tree

7 files changed

+188
-121
lines changed

7 files changed

+188
-121
lines changed

backend/src/controllers/student.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,22 @@ export const editStudent: RequestHandler = async (req, res, next) => {
112112
}
113113
};
114114

115-
export const getAllStudents: RequestHandler = async (_, res, next) => {
115+
export const getAllStudents: RequestHandler = async (req, res, next) => {
116116
try {
117117
const students = await StudentModel.find().populate("enrollments");
118118

119+
// Even though this is a get request, we have verifyAuthToken middleware that sets the accountType in the request body
120+
const { accountType } = req.body;
121+
122+
// Ensure that documents that are marked admin are not returned to non-admin users
123+
if (accountType !== "admin") {
124+
students.forEach((student) => {
125+
student.documents = student.documents.filter(
126+
(doc) => !doc.markedAdmin,
127+
) as typeof student.documents;
128+
});
129+
}
130+
119131
res.status(200).json(students);
120132
} catch (error) {
121133
next(error);
@@ -126,6 +138,8 @@ export const getStudent: RequestHandler = async (req, res, next) => {
126138
try {
127139
const errors = validationResult(req);
128140

141+
const { accountType } = req.body;
142+
129143
validationErrorParser(errors);
130144

131145
const studentId = req.params.id;
@@ -135,6 +149,13 @@ export const getStudent: RequestHandler = async (req, res, next) => {
135149
return res.status(404).json({ message: "Student not found" });
136150
}
137151

152+
// Ensure that documents that are marked admin are not returned to non-admin users
153+
if (accountType !== "admin") {
154+
studentData.documents = studentData.documents.filter(
155+
(doc) => !doc.markedAdmin,
156+
) as typeof studentData.documents;
157+
}
158+
138159
const enrollments = await EnrollmentModel.find({ studentId });
139160

140161
res.status(200).json({ ...studentData.toObject(), enrollments });

frontend/src/components/ProgressNotes/hooks/useProgressNotes.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ export const useProgressNotes = () => {
4747
}
4848
};
4949

50-
const fetchStudentData = async (progressNotes: Record<string, ProgressNote>) => {
50+
const fetchStudentData = async (progressNotes: Record<string, ProgressNote>, token: string) => {
5151
try {
52-
const result = await getAllStudents(firebaseToken);
52+
const result = await getAllStudents(token);
5353
if (result.success) {
5454
const studentDataWithNotes: StudentWithNotes[] = result.data.map((student) => ({
5555
...student,
@@ -92,7 +92,7 @@ export const useProgressNotes = () => {
9292

9393
const progressNotes = await fetchProgressNotes(token);
9494
if (progressNotes) {
95-
await fetchStudentData(progressNotes);
95+
await fetchStudentData(progressNotes, token);
9696
}
9797
})
9898
.catch((error) => {

frontend/src/components/StudentForm/StudentForm.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ export default function StudentForm({
283283
Student Information
284284
</legend>
285285

286-
<StudentInfo data={data ?? null} documentData={documentData} />
286+
<StudentInfo data={data ?? null} documentData={documentData} isAdmin={isAdmin} />
287287
</fieldset>
288288
</div>
289289
<div className="grid w-full gap-10 lg:grid-cols-2">

frontend/src/components/StudentForm/StudentInfo.tsx

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type StudentInfoProps = {
4242
setStudentDocuments: Dispatch<SetStateAction<Document[]>>;
4343
setDidDeleteOrMark: Dispatch<SetStateAction<boolean>>;
4444
};
45+
isAdmin: boolean;
4546
};
4647

4748
const SUPPORTED_FILETYPES = [
@@ -52,7 +53,7 @@ const SUPPORTED_FILETYPES = [
5253
"image/webp",
5354
];
5455

55-
export default function StudentInfo({ classname, data, documentData }: StudentInfoProps) {
56+
export default function StudentInfo({ classname, data, documentData, isAdmin }: StudentInfoProps) {
5657
const { register, setValue: setCalendarValue } = useFormContext<StudentFormData>();
5758
const [modalOpen, setModalOpen] = useState(false);
5859
const [documentError, setDocumentError] = useState("");
@@ -200,6 +201,8 @@ export default function StudentInfo({ classname, data, documentData }: StudentIn
200201
);
201202
};
202203

204+
console.log({ isAdmin });
205+
203206
return (
204207
<div className={cn("grid flex-1 gap-x-8 gap-y-10 md:grid-cols-2", classname)}>
205208
<div>
@@ -283,45 +286,51 @@ export default function StudentInfo({ classname, data, documentData }: StudentIn
283286
onClick={() => {
284287
window.open(document.link, "_blank");
285288
}}
286-
className="rounded-tl-md rounded-tr-md border-[1px] border-solid border-black bg-white px-10 py-4"
289+
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`}
287290
>
288291
View File
289292
</button>
290293
)}
291294

292-
<ModalConfirmation
293-
icon={<GreenQuestionIcon />}
294-
triggerElement={
295-
<button
296-
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`}
297-
>
298-
{document.markedAdmin ? "Unmark" : "Mark"} Admin
299-
</button>
300-
}
301-
title={document.markedAdmin ? "Unmark admin?" : "Mark admin only?"}
302-
description={`${document.markedAdmin ? "Everyone will be able to" : "Only admin will"} see these files`}
303-
confirmText={document.markedAdmin ? "Unmark" : "Mark"}
304-
kind="primary"
305-
onConfirmClick={() => {
306-
handleMarkAdmin(document);
307-
setDidDeleteOrMark(true);
308-
}}
309-
/>
310-
<ModalConfirmation
311-
icon={<RedDeleteIcon />}
312-
triggerElement={
313-
<button className="rounded-bl-md rounded-br-md border-[1px] border-solid border-black bg-white px-10 py-4 text-destructive">
314-
Delete File
315-
</button>
316-
}
317-
title="Are you sure you want to delete?"
318-
confirmText="Delete"
319-
kind="destructive"
320-
onConfirmClick={() => {
321-
handleDeleteDocument(document);
322-
setDidDeleteOrMark(true);
323-
}}
324-
/>
295+
{isAdmin ? (
296+
<>
297+
<ModalConfirmation
298+
icon={<GreenQuestionIcon />}
299+
triggerElement={
300+
<button
301+
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`}
302+
>
303+
{document.markedAdmin ? "Unmark" : "Mark"} Admin
304+
</button>
305+
}
306+
title={document.markedAdmin ? "Unmark admin?" : "Mark admin only?"}
307+
description={`${document.markedAdmin ? "Everyone will be able to" : "Only admin will"} see these files`}
308+
confirmText={document.markedAdmin ? "Unmark" : "Mark"}
309+
kind="primary"
310+
onConfirmClick={() => {
311+
handleMarkAdmin(document);
312+
setDidDeleteOrMark(true);
313+
}}
314+
/>
315+
<ModalConfirmation
316+
icon={<RedDeleteIcon />}
317+
triggerElement={
318+
<button className="rounded-bl-md rounded-br-md border-[1px] border-solid border-black bg-white px-10 py-4 text-destructive">
319+
Delete File
320+
</button>
321+
}
322+
title="Are you sure you want to delete?"
323+
confirmText="Delete"
324+
kind="destructive"
325+
onConfirmClick={() => {
326+
handleDeleteDocument(document);
327+
setDidDeleteOrMark(true);
328+
}}
329+
/>
330+
</>
331+
) : (
332+
""
333+
)}
325334
</PopoverContent>
326335
</Popover>
327336
</Fragment>

frontend/src/components/StudentProfile.tsx

Lines changed: 81 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ function ProgramLayout({ enrollmentInfo }: ProgramLayoutProps) {
157157

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

179+
const TruncateDocument = ({
180+
documentName,
181+
documentLength,
182+
}: {
183+
documentName: string;
184+
documentLength: number;
185+
}) => {
186+
const minLength = 9; // Shortest truncation
187+
const maxLength = 20; // Longest truncation
188+
const baseName = documentName.slice(0, documentName.lastIndexOf("."));
189+
190+
// Use an inverse relationship: fewer documents = longer names
191+
const dynamicLength = Math.max(
192+
minLength,
193+
Math.min(maxLength, 20 - Math.floor((documentLength - 1) * 2)),
194+
);
195+
196+
// Only truncate and add ellipsis if the basename is longer than dynamicLength
197+
const displayName =
198+
baseName.length > dynamicLength ? baseName.substring(0, dynamicLength) + "..." : baseName;
199+
200+
return displayName;
201+
};
202+
179203
const deleteStudentHandler: MouseEventHandler = () => {
180204
const lastName = getDeleteValue("lastname");
181205
if (studentData && firebaseToken && studentData.student.lastName === lastName) {
@@ -423,12 +447,24 @@ export default function StudentProfile({ id }: StudentProfileProps) {
423447
<div id="documents" className="basis-1/2 space-y-[20px]">
424448
<div className="font-[Poppins-Bold] text-[28px]">Documents</div>
425449
<div className="flex space-x-[20px]">
426-
<button className="h-[48px] w-[116px] rounded-lg border border-pia_border bg-pia_secondary_green text-pia_primary_white">
427-
Student Info
428-
</button>
429-
<button className="h-[48px] w-[116px] rounded-lg border border-pia_border bg-pia_light_gray">
430-
Waivers
431-
</button>
450+
{studentData.documents?.map((doc, index) => (
451+
<button
452+
onClick={() => {
453+
window.open(doc.link, "_blank");
454+
}}
455+
key={index}
456+
className={
457+
index % 2 === 0
458+
? "h-[48px] w-[116px] rounded-lg border border-pia_border bg-pia_light_gray"
459+
: "h-[48px] w-[116px] rounded-lg border border-pia_border bg-pia_secondary_green text-pia_primary_white"
460+
}
461+
>
462+
{TruncateDocument({
463+
documentName: doc.name,
464+
documentLength: doc.name.length,
465+
}) || `Document ${index + 1}`}
466+
</button>
467+
))}
432468
</div>
433469
</div>
434470
<div id="medications" className="basis-1/2 space-y-[20px]">
@@ -472,43 +508,46 @@ export default function StudentProfile({ id }: StudentProfileProps) {
472508
</div>
473509
<NotificationTable />
474510
</div> */}
511+
475512
<div id="Bottom Buttons" className="flex justify-between">
476-
<ModalConfirmation
477-
className="h-[60%] w-[40%] rounded-[8px]"
478-
title="Are you sure you want to delete this student?"
479-
innerContent={
480-
<>
481-
<div className="flex w-[60%] justify-center">
482-
<div className="font-base text-sm sm:text-base">
483-
<li className="font-bold text-destructive">This cannot be undone!</li>
484-
<li>
485-
This will remove this student from all enrolled programs and delete all
486-
notes and documents.
487-
</li>
513+
{isAdmin && (
514+
<ModalConfirmation
515+
className="h-[60%] w-[40%] rounded-[8px]"
516+
title="Are you sure you want to delete this student?"
517+
innerContent={
518+
<>
519+
<div className="flex w-[60%] justify-center">
520+
<div className="font-base text-sm sm:text-base">
521+
<li className="font-bold text-destructive">This cannot be undone!</li>
522+
<li>
523+
This will remove this student from all enrolled programs and delete all
524+
notes and documents.
525+
</li>
526+
</div>
488527
</div>
489-
</div>
490-
<div className="mx-8 mb-8 mt-6">
491-
Enter the student&apos;s last name to proceed
492-
<Textfield
493-
name="lastname"
494-
placeholder="Last Name"
495-
register={deleteRegister}
496-
/>
497-
</div>
498-
</>
499-
}
500-
kind="destructive"
501-
triggerElement={
502-
<button className="h-[48px] w-[96px] rounded-sm border border-pia_border text-pia_border">
503-
Delete
504-
</button>
505-
}
506-
confirmText="Delete"
507-
icon={<div />}
508-
isParentOpen={deleteDialogOpen}
509-
setIsParentOpen={setDeleteDialogOpen}
510-
onConfirmClick={deleteStudentHandler}
511-
/>
528+
<div className="mx-8 mb-8 mt-6">
529+
Enter the student&apos;s last name to proceed
530+
<Textfield
531+
name="lastname"
532+
placeholder="Last Name"
533+
register={deleteRegister}
534+
/>
535+
</div>
536+
</>
537+
}
538+
kind="destructive"
539+
triggerElement={
540+
<button className="h-[48px] w-[96px] rounded-sm border border-pia_border text-pia_border">
541+
Delete
542+
</button>
543+
}
544+
confirmText="Delete"
545+
icon={<div />}
546+
isParentOpen={deleteDialogOpen}
547+
setIsParentOpen={setDeleteDialogOpen}
548+
onConfirmClick={deleteStudentHandler}
549+
/>
550+
)}
512551
<button
513552
className="h-[48px] w-[96px] rounded-sm border border-pia_dark_green bg-pia_dark_green text-pia_primary_white"
514553
onClick={() => {

0 commit comments

Comments
 (0)