Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 158 additions & 4 deletions backend/controllers/courseController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const getCourses = async (
try {
const filters = req.query;
const courseResponses = await Course.find(filters)
.populate(["ratings"])
.populate(["speakers"])
.exec();
res.status(200).json({
success: true,
Expand All @@ -52,9 +52,7 @@ export const getCourseById = async (

if (id) {
// Find course by ID and populate related fields
const course = await Course.findById(id)
.populate(["ratings", "managers"])
.exec();
const course = await Course.findById(id).populate(["speakers"]).exec();

if (!course) {
res.status(404).json({
Expand Down Expand Up @@ -526,6 +524,84 @@ export const getCourseProgress = async (
}
};

// @desc Get progress for a single user in a course
// @route GET /api/courses/:courseId/progress/:userId
// @access Public
export const getUserCourseProgress = async (
req: Request,
res: Response
): Promise<void> => {
try {
const { courseId, userId } = req.params;

// Validate input
if (!courseId || !userId) {
res.status(400).json({
success: false,
message: "Course ID and User ID are required.",
});
return;
}

// Check if the course exists
const course = await Course.findById(courseId);
if (!course) {
res.status(404).json({
success: false,
message: "Course not found.",
});
return;
}

// Check if the user is enrolled in the course
const isEnrolled = course.students.some(
(id) => id.toString() === userId.toString()
);
if (!isEnrolled) {
res.status(403).json({
success: false,
message: "User is not enrolled in this course.",
});
return;
}

// Check for existing progress
let progress = await Progress.findOne({ course: courseId, user: userId })
.populate("user")
.populate("course");

// If no progress exists, create one
if (!progress) {
progress = await new Progress({
user: userId,
course: courseId,
isComplete: false,
completedComponents: {
webinar: false,
survey: false,
certificate: false,
},
dateCompleted: null,
}).save();

// repopulate after save
progress = await Progress.findById(progress._id)
.populate("user")
.populate("course");
}

res.status(200).json({
success: true,
progress,
});
} catch (error: any) {
res.status(500).json({
success: false,
message: error.message || "Internal server error.",
});
}
};

// @desc Update user's progress in a course
// @route PUT /api/courses/:courseId/progress/:userId
// @access Public
Expand Down Expand Up @@ -605,3 +681,81 @@ export const updateUserProgress = async (
});
}
};

// @desc Batch update user progress in a course
// @route PUT /api/courses/:courseId/progress/batch
// @access Public
export const batchUpdateUserProgress = async (
req: Request,
res: Response
): Promise<void> => {
try {
const { courseId } = req.params;
const updates = req.body.updates; // Array of updates

if (!Array.isArray(updates) || updates.length === 0) {
res.status(400).json({
success: false,
message: "No updates provided.",
});
return;
}

const results: any[] = [];

for (const update of updates) {
const { userId, webinarComplete, surveyComplete, certificateComplete } =
update;

console.log("Processing update for user:", userId);

const progress = await Progress.findOne({
course: courseId,
user: userId,
});

if (!progress) {
console.warn(`Progress not found for user ${userId}`);
results.push({
userId,
success: false,
message: "Progress record not found.",
});
continue;
}

const completedComponents = {
webinar: Boolean(webinarComplete),
survey: Boolean(surveyComplete),
certificate: Boolean(certificateComplete),
};

progress.completedComponents = completedComponents;

const allComplete = Object.values(completedComponents).every(Boolean);
progress.isComplete = allComplete;
if (allComplete && !progress.dateCompleted) {
progress.dateCompleted = new Date();
}

await progress.save();

results.push({
userId,
success: true,
});
}

res.status(200).json({
success: true,
message: "Batch update complete.",
results,
});
} catch (error: any) {
console.error("Error in batchUpdateUserProgress:", error);
res.status(500).json({
success: false,
message: error.message || "Internal server error.",
});
}
};
17 changes: 4 additions & 13 deletions backend/controllers/speakerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,26 +49,22 @@ export const createSpeaker = async (
res: Response
): Promise<void> => {
try {
const { name, title, email, company, bio, disclosures } = req.body;

// Store image URL if an image is uploaded
const imageUrl = req.file
? `http://localhost:5001/uploads/${req.file.filename}`
: null;
console.log(req.body);
const { name, title, email, company, bio, image } = req.body;

const speaker = new Speaker({
name,
title,
email,
company,
bio,
disclosures: disclosures ? disclosures.split(",") : [],
image: imageUrl,
image,
});

await speaker.save();
res.status(201).json({ speaker, message: "Speaker created successfully" });
} catch (error) {
console.error(error);
res.status(500).json({
message:
error instanceof Error ? error.message : "An unknown error occurred",
Expand All @@ -87,11 +83,6 @@ export const updateSpeaker = async (
const { id: speakerId } = req.params;
const updates = req.body;

// If a new image is uploaded, store its URL
if (req.file) {
updates.image = `http://localhost:5001/uploads/${req.file.filename}`;
}

const updatedSpeaker = await Speaker.findByIdAndUpdate(speakerId, updates, {
new: true,
});
Expand Down
8 changes: 7 additions & 1 deletion backend/controllers/userController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,17 @@ export const register = async (req: Request, res: Response): Promise<void> => {
user: new mongoose.Types.ObjectId(userId),
course: new mongoose.Types.ObjectId(courseId),
isComplete: false,
completedComponents: {},
completedComponents: {
webinar: false,
survey: false,
certificate: false,
},
dateCompleted: null,
});
await progress.save();

console.log("progress", progress);

if (!course.students.includes(new mongoose.Types.ObjectId(userId))) {
course.students.push(new mongoose.Types.ObjectId(userId));
await course.save();
Expand Down
22 changes: 18 additions & 4 deletions backend/models/courseModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ export interface ICourse extends Document {
regStart: Date;
regEnd: Date;
//Virtual Training - Live Meeting, In-Person Training, Virtual Training - On Demand, Virtual Training - Live Webinar
productType: string;
productInfo: string
productType:
| "Virtual Training - Live Meeting"
| "In-Person Training"
| "Virtual Training - On Demand"
| "Virtual Training - Live Webinar";
productInfo: string;
shortUrl: string;
draft: boolean;
}
Expand Down Expand Up @@ -78,8 +82,18 @@ const CourseSchema: Schema = new Schema(
],
regStart: { type: Date, required: false },
regEnd: { type: Date, required: false },
productType: { type: String, required: false },
productInfo: {type:String, required: false},
productType: {
type: String,
required: false,
enum: [
"Virtual Training - Live Meeting",
"In-Person Training",
"Virtual Training - On Demand",
"Virtual Training - Live Webinar",
"",
],
},
productInfo: { type: String, required: false },
shortUrl: { type: String, required: false },
draft: { type: Boolean, required: true, default: true },
},
Expand Down
8 changes: 7 additions & 1 deletion backend/routes/courseRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
getCourseUsers,
getCourseProgress,
updateUserProgress,
batchUpdateUserProgress,
getUserCourseProgress,
} from "../controllers/courseController";

const router = express.Router();
Expand All @@ -33,7 +35,11 @@ router.get("/:courseId/users", getCourseUsers);
// GET progress for all users in a course
router.get("/:courseId/progress", getCourseProgress);

router.get("/:courseId/progress/single/:userId", getUserCourseProgress);

// PUT update user's progress in a course
router.put("/:courseId/progress/:userId", updateUserProgress);
router.put("/:courseId/progress/single/:userId", updateUserProgress);

router.put("/:courseId/progress/batch", batchUpdateUserProgress);

export default router;
56 changes: 56 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading