From 830077a75f31b2272f78d774b732bebeac096666 Mon Sep 17 00:00:00 2001 From: Aditya Raut Date: Sun, 5 Apr 2026 09:09:57 +0530 Subject: [PATCH] feat(assignments): add file attachment functionality for assignments and submissions --- .../components/CourseView/AssignmentCard.jsx | 105 +++++++++++++++--- .../components/CourseView/AssignmentModal.jsx | 34 ++++++ .../components/CourseView/AssignmentsTab.jsx | 6 +- .../src/components/CourseView/QuizzesTab.jsx | 63 ++++++++--- client/src/pages/CourseView.jsx | 79 ++++++++++--- .../app/controllers/assignmentController.js | 98 +++++++++++++++- server/app/controllers/authController.js | 37 +++--- server/app/middleware/documentUpload.js | 19 ++++ server/app/models/Assignment.js | 8 ++ server/app/models/Submission.js | 2 + server/app/routes/assignments.js | 7 +- 11 files changed, 389 insertions(+), 69 deletions(-) create mode 100644 server/app/middleware/documentUpload.js diff --git a/client/src/components/CourseView/AssignmentCard.jsx b/client/src/components/CourseView/AssignmentCard.jsx index 76e47b3..74597d7 100644 --- a/client/src/components/CourseView/AssignmentCard.jsx +++ b/client/src/components/CourseView/AssignmentCard.jsx @@ -12,6 +12,7 @@ function AssignmentCard({ mySubmission, submissions, submissionText, + submissionFile, isLocked, lockedByTitle, lockedByOrder, @@ -179,6 +180,28 @@ function AssignmentCard({ )} + {/* Assignment attachments (visible to all) */} + {assignment.attachments?.length > 0 && ( +
+

+ πŸ“Ž Attachments +

+
+ {assignment.attachments.map((att) => ( + + {att.name.endsWith(".pdf") ? "πŸ“„" : "πŸ“"} {att.name} + + ))} +
+
+ )} + {/* Student: submission form */} {!isTeacher && !hasSubmission && !isLocked && (
@@ -189,17 +212,41 @@ function AssignmentCard({ className="w-full px-4 py-3.5 bg-[var(--surface-elevated)] border border-[var(--border)] rounded-2xl text-sm outline-none resize-y focus:border-[var(--accent)] focus:ring-2 focus:ring-[var(--accent)]/12 transition-all duration-300 text-[var(--text)] placeholder:text-[var(--muted)] min-h-[100px] font-medium" - placeholder="Write your answer here..." + placeholder="Write your answer here... (optional if uploading a file)" value={submissionText || ""} - onChange={(e) => onSubmit(assignment.id, e.target.value, true)} + onChange={(e) => onSubmit(assignment.id, e.target.value, "text")} /> + {/* File upload */} +
+ + {submissionFile && ( + + )} +
@@ -212,9 +259,21 @@ function AssignmentCard({ Your Submission

-

- {mySubmission.content} -

+ {mySubmission.content && ( +

+ {mySubmission.content} +

+ )} + {mySubmission.fileUrl && ( + + πŸ“Ž {mySubmission.fileName || "Download file"} + + )} {mySubmission.feedback && (

@@ -286,14 +345,28 @@ function AssignmentCard({

-

- {s.content} -

- {s.feedback && ( -

- "{s.feedback}" -

- )} +
+ {s.content && ( +

+ {s.content} +

+ )} + {s.fileUrl && ( + + πŸ“Ž {s.fileName || "View file"} + + )} + {s.feedback && ( +

+ "{s.feedback}" +

+ )} +
); })} diff --git a/client/src/components/CourseView/AssignmentModal.jsx b/client/src/components/CourseView/AssignmentModal.jsx index aab3162..3bcedfa 100644 --- a/client/src/components/CourseView/AssignmentModal.jsx +++ b/client/src/components/CourseView/AssignmentModal.jsx @@ -10,6 +10,11 @@ function AssignmentModal({ }) { if (!isOpen) return null; + const handleFileChange = (e) => { + const file = e.target.files[0] || null; + onChange({ ...form, attachmentFile: file }); + }; + return modalOverlay( onClose, <> @@ -68,6 +73,35 @@ function AssignmentModal({ /> + + {/* Optional document attachment */} +
+ + + {form.attachmentFile && ( + + )} +
+
{/* Actions */} -
- {!isTeacher && ( - - )} + {taken ? ( + <> + + + + Quiz Taken + + ) : ( + <> + Take Quiz + + + + + )} + + ); + })()} {isTeacher && ( <>