384 lines
No EOL
11 KiB
JavaScript
384 lines
No EOL
11 KiB
JavaScript
const express = require('express');
|
|
const { body, validationResult } = require('express-validator');
|
|
const { Assignment, Course, User } = require('../models');
|
|
const { auth, requireTrainer } = require('../middleware/auth');
|
|
|
|
const router = express.Router();
|
|
|
|
// @route POST /api/assignments
|
|
// @desc Create new assignment (Trainer or Super Admin)
|
|
// @access Private
|
|
router.post('/', [
|
|
auth,
|
|
requireTrainer,
|
|
body('title').isLength({ min: 3, max: 200 }),
|
|
body('description').notEmpty(),
|
|
body('courseId').isUUID(),
|
|
body('maxScore').isInt({ min: 1, max: 1000 }),
|
|
body('weight').isFloat({ min: 0, max: 100 }),
|
|
body('type').isIn(['homework', 'quiz', 'project', 'exam', 'presentation']),
|
|
body('dueDate').optional().isISO8601()
|
|
], async (req, res) => {
|
|
try {
|
|
const errors = validationResult(req);
|
|
if (!errors.isEmpty()) {
|
|
return res.status(400).json({ errors: errors.array() });
|
|
}
|
|
|
|
const {
|
|
title,
|
|
description,
|
|
instructions,
|
|
courseId,
|
|
dueDate,
|
|
maxScore,
|
|
weight,
|
|
type,
|
|
isRequired,
|
|
allowLateSubmission,
|
|
latePenalty,
|
|
attachments,
|
|
rubric
|
|
} = req.body;
|
|
|
|
// Check if course exists and user has permission
|
|
const course = await Course.findByPk(courseId);
|
|
if (!course) {
|
|
return res.status(404).json({ error: 'Course not found.' });
|
|
}
|
|
|
|
if (req.user.role !== 'super_admin' && course.trainerId !== req.user.id) {
|
|
return res.status(403).json({ error: 'Not authorized to create assignments for this course.' });
|
|
}
|
|
|
|
const assignment = await Assignment.create({
|
|
title,
|
|
description,
|
|
instructions,
|
|
courseId,
|
|
trainerId: req.user.id,
|
|
dueDate,
|
|
maxScore,
|
|
weight,
|
|
type,
|
|
isRequired: isRequired !== undefined ? isRequired : true,
|
|
allowLateSubmission: allowLateSubmission !== undefined ? allowLateSubmission : false,
|
|
latePenalty,
|
|
attachments: attachments || [],
|
|
rubric: rubric || null,
|
|
isPublished: req.user.role === 'super_admin' // Auto-publish for super admin
|
|
});
|
|
|
|
const assignmentWithDetails = await Assignment.findByPk(assignment.id, {
|
|
include: [
|
|
{
|
|
model: Course,
|
|
as: 'course',
|
|
attributes: ['id', 'title']
|
|
},
|
|
{
|
|
model: User,
|
|
as: 'trainer',
|
|
attributes: ['id', 'firstName', 'lastName']
|
|
}
|
|
]
|
|
});
|
|
|
|
res.status(201).json({
|
|
message: 'Assignment created successfully.',
|
|
assignment: assignmentWithDetails
|
|
});
|
|
} catch (error) {
|
|
console.error('Create assignment error:', error);
|
|
res.status(500).json({ error: 'Server error.' });
|
|
}
|
|
});
|
|
|
|
// @route GET /api/assignments/course/:courseId
|
|
// @desc Get assignments for a specific course
|
|
// @access Private
|
|
router.get('/course/:courseId', auth, async (req, res) => {
|
|
try {
|
|
const { courseId } = req.params;
|
|
const { type, isPublished = true, page = 1, limit = 10 } = req.query;
|
|
const offset = (page - 1) * limit;
|
|
|
|
// Check if user can access this course
|
|
const course = await Course.findByPk(courseId);
|
|
if (!course) {
|
|
return res.status(404).json({ error: 'Course not found.' });
|
|
}
|
|
|
|
const canAccess = req.user.role === 'super_admin' ||
|
|
course.trainerId === req.user.id ||
|
|
await course.hasEnrollment(req.user.id);
|
|
|
|
if (!canAccess) {
|
|
return res.status(403).json({ error: 'Not authorized to view assignments for this course.' });
|
|
}
|
|
|
|
const whereClause = { courseId };
|
|
if (type) whereClause.type = type;
|
|
if (isPublished !== undefined) whereClause.isPublished = isPublished === 'true';
|
|
|
|
const { count, rows: assignments } = await Assignment.findAndCountAll({
|
|
where: whereClause,
|
|
include: [
|
|
{
|
|
model: User,
|
|
as: 'trainer',
|
|
attributes: ['id', 'firstName', 'lastName', 'avatar']
|
|
}
|
|
],
|
|
order: [['createdAt', 'DESC']],
|
|
limit: parseInt(limit),
|
|
offset: parseInt(offset)
|
|
});
|
|
|
|
res.json({
|
|
assignments,
|
|
pagination: {
|
|
currentPage: parseInt(page),
|
|
totalPages: Math.ceil(count / limit),
|
|
totalItems: count,
|
|
itemsPerPage: parseInt(limit)
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('Get assignments error:', error);
|
|
res.status(500).json({ error: 'Server error.' });
|
|
}
|
|
});
|
|
|
|
// @route GET /api/assignments/:id
|
|
// @desc Get assignment by ID
|
|
// @access Private
|
|
router.get('/:id', auth, async (req, res) => {
|
|
try {
|
|
const assignment = await Assignment.findByPk(req.params.id, {
|
|
include: [
|
|
{
|
|
model: Course,
|
|
as: 'course',
|
|
include: [
|
|
{
|
|
model: User,
|
|
as: 'trainer',
|
|
attributes: ['id', 'firstName', 'lastName', 'avatar']
|
|
}
|
|
]
|
|
},
|
|
{
|
|
model: User,
|
|
as: 'trainer',
|
|
attributes: ['id', 'firstName', 'lastName', 'avatar']
|
|
}
|
|
]
|
|
});
|
|
|
|
if (!assignment) {
|
|
return res.status(404).json({ error: 'Assignment not found.' });
|
|
}
|
|
|
|
// Check if user can access this assignment
|
|
const canAccess = req.user.role === 'super_admin' ||
|
|
assignment.trainerId === req.user.id ||
|
|
await assignment.course.hasEnrollment(req.user.id);
|
|
|
|
if (!canAccess) {
|
|
return res.status(403).json({ error: 'Not authorized to view this assignment.' });
|
|
}
|
|
|
|
res.json({ assignment });
|
|
} catch (error) {
|
|
console.error('Get assignment error:', error);
|
|
res.status(500).json({ error: 'Server error.' });
|
|
}
|
|
});
|
|
|
|
// @route PUT /api/assignments/:id
|
|
// @desc Update assignment (Owner or Super Admin)
|
|
// @access Private
|
|
router.put('/:id', [
|
|
auth,
|
|
body('title').optional().isLength({ min: 3, max: 200 }),
|
|
body('description').optional().notEmpty(),
|
|
body('maxScore').optional().isInt({ min: 1, max: 1000 }),
|
|
body('weight').optional().isFloat({ min: 0, max: 100 }),
|
|
body('type').optional().isIn(['homework', 'quiz', 'project', 'exam', 'presentation']),
|
|
body('dueDate').optional().isISO8601()
|
|
], async (req, res) => {
|
|
try {
|
|
const errors = validationResult(req);
|
|
if (!errors.isEmpty()) {
|
|
return res.status(400).json({ errors: errors.array() });
|
|
}
|
|
|
|
const assignment = await Assignment.findByPk(req.params.id);
|
|
if (!assignment) {
|
|
return res.status(404).json({ error: 'Assignment not found.' });
|
|
}
|
|
|
|
// Check permissions
|
|
if (req.user.role !== 'super_admin' && assignment.trainerId !== req.user.id) {
|
|
return res.status(403).json({ error: 'Not authorized to update this assignment.' });
|
|
}
|
|
|
|
const updateData = req.body;
|
|
await assignment.update(updateData);
|
|
|
|
const updatedAssignment = await Assignment.findByPk(assignment.id, {
|
|
include: [
|
|
{
|
|
model: Course,
|
|
as: 'course',
|
|
attributes: ['id', 'title']
|
|
},
|
|
{
|
|
model: User,
|
|
as: 'trainer',
|
|
attributes: ['id', 'firstName', 'lastName']
|
|
}
|
|
]
|
|
});
|
|
|
|
res.json({
|
|
message: 'Assignment updated successfully.',
|
|
assignment: updatedAssignment
|
|
});
|
|
} catch (error) {
|
|
console.error('Update assignment error:', error);
|
|
res.status(500).json({ error: 'Server error.' });
|
|
}
|
|
});
|
|
|
|
// @route DELETE /api/assignments/:id
|
|
// @desc Delete assignment (Owner or Super Admin)
|
|
// @access Private
|
|
router.delete('/:id', auth, async (req, res) => {
|
|
try {
|
|
const assignment = await Assignment.findByPk(req.params.id);
|
|
if (!assignment) {
|
|
return res.status(404).json({ error: 'Assignment not found.' });
|
|
}
|
|
|
|
// Check permissions
|
|
if (req.user.role !== 'super_admin' && assignment.trainerId !== req.user.id) {
|
|
return res.status(403).json({ error: 'Not authorized to delete this assignment.' });
|
|
}
|
|
|
|
await assignment.destroy();
|
|
|
|
res.json({ message: 'Assignment deleted successfully.' });
|
|
} catch (error) {
|
|
console.error('Delete assignment error:', error);
|
|
res.status(500).json({ error: 'Server error.' });
|
|
}
|
|
});
|
|
|
|
// @route PUT /api/assignments/:id/publish
|
|
// @desc Publish/unpublish assignment (Owner or Super Admin)
|
|
// @access Private
|
|
router.put('/:id/publish', [
|
|
auth,
|
|
body('isPublished').isBoolean()
|
|
], async (req, res) => {
|
|
try {
|
|
const errors = validationResult(req);
|
|
if (!errors.isEmpty()) {
|
|
return res.status(400).json({ errors: errors.array() });
|
|
}
|
|
|
|
const assignment = await Assignment.findByPk(req.params.id);
|
|
if (!assignment) {
|
|
return res.status(404).json({ error: 'Assignment not found.' });
|
|
}
|
|
|
|
// Check permissions
|
|
if (req.user.role !== 'super_admin' && assignment.trainerId !== req.user.id) {
|
|
return res.status(403).json({ error: 'Not authorized to update this assignment.' });
|
|
}
|
|
|
|
await assignment.update({ isPublished: req.body.isPublished });
|
|
|
|
res.json({
|
|
message: `Assignment ${req.body.isPublished ? 'published' : 'unpublished'} successfully.`,
|
|
assignment: {
|
|
id: assignment.id,
|
|
title: assignment.title,
|
|
isPublished: assignment.isPublished
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('Publish assignment error:', error);
|
|
res.status(500).json({ error: 'Server error.' });
|
|
}
|
|
});
|
|
|
|
// @route GET /api/assignments/my
|
|
// @desc Get user's assignments (as trainee)
|
|
// @access Private
|
|
router.get('/my', auth, async (req, res) => {
|
|
try {
|
|
const { status, page = 1, limit = 10 } = req.query;
|
|
const offset = (page - 1) * limit;
|
|
|
|
// Get courses where user is enrolled
|
|
const enrollments = await require('../models').Enrollment.findAll({
|
|
where: { userId: req.user.id, status: 'active' },
|
|
include: [
|
|
{
|
|
model: Course,
|
|
as: 'course',
|
|
include: [
|
|
{
|
|
model: Assignment,
|
|
as: 'assignments',
|
|
where: { isPublished: true },
|
|
required: false
|
|
}
|
|
]
|
|
}
|
|
]
|
|
});
|
|
|
|
const courseIds = enrollments.map(enrollment => enrollment.courseId);
|
|
|
|
const whereClause = { courseId: courseIds };
|
|
if (status) whereClause.status = status;
|
|
|
|
const { count, rows: assignments } = await Assignment.findAndCountAll({
|
|
where: whereClause,
|
|
include: [
|
|
{
|
|
model: Course,
|
|
as: 'course',
|
|
attributes: ['id', 'title', 'thumbnail']
|
|
},
|
|
{
|
|
model: User,
|
|
as: 'trainer',
|
|
attributes: ['id', 'firstName', 'lastName', 'avatar']
|
|
}
|
|
],
|
|
order: [['dueDate', 'ASC']],
|
|
limit: parseInt(limit),
|
|
offset: parseInt(offset)
|
|
});
|
|
|
|
res.json({
|
|
assignments,
|
|
pagination: {
|
|
currentPage: parseInt(page),
|
|
totalPages: Math.ceil(count / limit),
|
|
totalItems: count,
|
|
itemsPerPage: parseInt(limit)
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('Get my assignments error:', error);
|
|
res.status(500).json({ error: 'Server error.' });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|