Building Homework System in LMS
Homework system is for assignments with file submission, deadlines, late submission handling, grading rubrics, and feedback. Instructors create assignment templates, students submit work, instructors grade with comments.
Assignment Model
interface Assignment {
id: string;
lessonId: string;
title: string;
description: string;
dueDate: Date;
maxScore: number;
rubric?: Array<{ criterion: string; points: number }>;
allowLateSubmission: boolean;
lateDeductionPercent?: number;
requiresApproval: boolean;
}
interface Submission {
id: string;
assignmentId: string;
userId: string;
files: Array<{ name: string; url: string; size: number }>;
submittedAt: Date;
isLate: boolean;
grade?: number;
feedback?: string;
rubricScores?: Record<string, number>;
}
Create Assignment API
app.post('/api/assignments', authenticate, async (req, res) => {
const { lessonId, title, description, dueDate, maxScore, rubric } = req.body;
const assignment = await db.assignments.create({
lessonId,
title,
description,
dueDate: new Date(dueDate),
maxScore,
rubric: rubric || [],
createdBy: req.user.id,
});
res.json(assignment);
});
// Student submission
app.post('/api/assignments/:assignmentId/submit', authenticate, upload.array('files'), async (req, res) => {
const assignment = await db.assignments.findById(req.params.assignmentId);
const existingSubmission = await db.submissions.findByUserAndAssignment(
req.user.id, req.params.assignmentId
);
if (existingSubmission && !assignment.allowResubmission) {
return res.status(409).json({ error: 'Already submitted' });
}
const now = new Date();
const isLate = now > new Date(assignment.dueDate);
const files = await Promise.all(
req.files?.map(async (f) => ({
name: f.originalname,
url: await uploadToStorage(f),
size: f.size,
})) || []
);
const submission = await db.submissions.create({
assignmentId: req.params.assignmentId,
userId: req.user.id,
files,
submittedAt: now,
isLate,
});
res.json(submission);
});
Grading Interface
function GradingPanel({ submission, assignment, onSave }) {
const [rubricScores, setRubricScores] = useState<Record<string, number>>({});
const [feedback, setFeedback] = useState(submission.feedback || '');
const [grade, setGrade] = useState(submission.grade || 0);
const totalRubricScore = Object.values(rubricScores).reduce((a, b) => a + b, 0);
const handleSave = async () => {
await fetch(`/api/submissions/${submission.id}/grade`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grade,
rubricScores,
feedback,
}),
});
onSave?.();
};
return (
<div className="max-w-2xl mx-auto p-6 space-y-6">
<div>
<h3 className="font-semibold mb-4">Submission Files</h3>
{submission.files.map(f => (
<a key={f.url} href={f.url} className="block text-blue-600 hover:underline">
{f.name}
</a>
))}
</div>
{assignment.rubric?.length > 0 && (
<div>
<h3 className="font-semibold mb-4">Rubric</h3>
{assignment.rubric.map(criterion => (
<div key={criterion.criterion} className="mb-3">
<label className="text-sm text-gray-600">{criterion.criterion} (max {criterion.points})</label>
<input
type="number"
min="0"
max={criterion.points}
value={rubricScores[criterion.criterion] || 0}
onChange={(e) => setRubricScores({
...rubricScores,
[criterion.criterion]: Number(e.target.value),
})}
className="w-24 border rounded px-2 py-1"
/>
</div>
))}
</div>
)}
<div>
<label className="block text-sm font-medium mb-2">Final Grade</label>
<input
type="number"
min="0"
max={assignment.maxScore}
value={grade}
onChange={(e) => setGrade(Number(e.target.value))}
className="w-24 border rounded px-2 py-1"
/>
</div>
<div>
<label className="block text-sm font-medium mb-2">Feedback</label>
<textarea
value={feedback}
onChange={(e) => setFeedback(e.target.value)}
rows={6}
className="w-full border rounded px-3 py-2"
/>
</div>
<button
onClick={handleSave}
className="w-full bg-blue-600 text-white rounded-lg py-2 font-medium hover:bg-blue-700"
>
Save Grade
</button>
</div>
);
}
Late Submission Handling
async function calculateGrade(submission: Submission, assignment: Assignment): Promise<number> {
let finalGrade = submission.grade || 0;
if (submission.isLate && assignment.lateDeductionPercent) {
finalGrade = finalGrade * (1 - assignment.lateDeductionPercent / 100);
}
return Math.round(finalGrade * 100) / 100;
}
Timeframe
Basic assignment submission and grading — 1 week. With rubrics, late penalties, and detailed feedback — 2–3 weeks.







