diff --git a/backend/src/controllers/student.ts b/backend/src/controllers/student.ts index d5d443a..82990b4 100644 --- a/backend/src/controllers/student.ts +++ b/backend/src/controllers/student.ts @@ -112,10 +112,22 @@ export const editStudent: RequestHandler = async (req, res, next) => { } }; -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; + + // 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); @@ -126,6 +138,8 @@ export const getStudent: RequestHandler = async (req, res, next) => { try { const errors = validationResult(req); + const { accountType } = req.body; + validationErrorParser(errors); const studentId = req.params.id; @@ -135,6 +149,13 @@ export const getStudent: RequestHandler = async (req, res, next) => { 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 }); diff --git a/frontend/src/components/ProgressNotes/hooks/useProgressNotes.ts b/frontend/src/components/ProgressNotes/hooks/useProgressNotes.ts index 697e69c..ef4aba3 100644 --- a/frontend/src/components/ProgressNotes/hooks/useProgressNotes.ts +++ b/frontend/src/components/ProgressNotes/hooks/useProgressNotes.ts @@ -47,9 +47,9 @@ export const useProgressNotes = () => { } }; - const fetchStudentData = async (progressNotes: Record) => { + const fetchStudentData = async (progressNotes: Record, token: string) => { try { - const result = await getAllStudents(firebaseToken); + const result = await getAllStudents(token); if (result.success) { const studentDataWithNotes: StudentWithNotes[] = result.data.map((student) => ({ ...student, @@ -92,7 +92,7 @@ export const useProgressNotes = () => { const progressNotes = await fetchProgressNotes(token); if (progressNotes) { - await fetchStudentData(progressNotes); + await fetchStudentData(progressNotes, token); } }) .catch((error) => { diff --git a/frontend/src/components/StudentForm/StudentForm.tsx b/frontend/src/components/StudentForm/StudentForm.tsx index 7985165..9573a1f 100644 --- a/frontend/src/components/StudentForm/StudentForm.tsx +++ b/frontend/src/components/StudentForm/StudentForm.tsx @@ -283,7 +283,7 @@ export default function StudentForm({ Student Information - +
diff --git a/frontend/src/components/StudentForm/StudentInfo.tsx b/frontend/src/components/StudentForm/StudentInfo.tsx index a4e567d..c92672e 100644 --- a/frontend/src/components/StudentForm/StudentInfo.tsx +++ b/frontend/src/components/StudentForm/StudentInfo.tsx @@ -42,6 +42,7 @@ type StudentInfoProps = { setStudentDocuments: Dispatch>; setDidDeleteOrMark: Dispatch>; }; + isAdmin: boolean; }; const SUPPORTED_FILETYPES = [ @@ -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(); const [modalOpen, setModalOpen] = useState(false); const [documentError, setDocumentError] = useState(""); @@ -200,6 +201,8 @@ export default function StudentInfo({ classname, data, documentData }: StudentIn ); }; + console.log({ isAdmin }); + return (
@@ -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 )} - } - triggerElement={ - - } - 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); - }} - /> - } - triggerElement={ - - } - title="Are you sure you want to delete?" - confirmText="Delete" - kind="destructive" - onConfirmClick={() => { - handleDeleteDocument(document); - setDidDeleteOrMark(true); - }} - /> + {isAdmin ? ( + <> + } + triggerElement={ + + } + 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); + }} + /> + } + triggerElement={ + + } + title="Are you sure you want to delete?" + confirmText="Delete" + kind="destructive" + onConfirmClick={() => { + handleDeleteDocument(document); + setDidDeleteOrMark(true); + }} + /> + + ) : ( + "" + )} diff --git a/frontend/src/components/StudentProfile.tsx b/frontend/src/components/StudentProfile.tsx index fc6c8ee..4e33487 100644 --- a/frontend/src/components/StudentProfile.tsx +++ b/frontend/src/components/StudentProfile.tsx @@ -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(); const [notFound, setNotFound] = useState(false); const [studentData, setStudentData] = useState(); @@ -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) { @@ -423,12 +447,24 @@ export default function StudentProfile({ id }: StudentProfileProps) {
Documents
- - + {studentData.documents?.map((doc, index) => ( + + ))}
@@ -472,43 +508,46 @@ export default function StudentProfile({ id }: StudentProfileProps) {
*/} +
- -
-
-
  • This cannot be undone!
  • -
  • - This will remove this student from all enrolled programs and delete all - notes and documents. -
  • + {isAdmin && ( + +
    +
    +
  • This cannot be undone!
  • +
  • + This will remove this student from all enrolled programs and delete all + notes and documents. +
  • +
    -
    -
    - Enter the student's last name to proceed - -
    - - } - kind="destructive" - triggerElement={ - - } - confirmText="Delete" - icon={
    } - isParentOpen={deleteDialogOpen} - setIsParentOpen={setDeleteDialogOpen} - onConfirmClick={deleteStudentHandler} - /> +
    + Enter the student's last name to proceed + +
    + + } + kind="destructive" + triggerElement={ + + } + confirmText="Delete" + icon={
    } + isParentOpen={deleteDialogOpen} + setIsParentOpen={setDeleteDialogOpen} + onConfirmClick={deleteStudentHandler} + /> + )}