From b60ecf7c27398d349652c37c633dd9f04668c303 Mon Sep 17 00:00:00 2001 From: vnthanhdng Date: Mon, 4 May 2026 15:35:27 -0400 Subject: [PATCH] feat: Implement pre and post survey phases --- backend/controllers/courseController.ts | 106 +++++++++++++--- backend/controllers/surveyController.ts | 8 +- .../controllers/surveyResponseController.ts | 69 ++++++---- backend/models/questionModel.ts | 7 ++ backend/models/surveyResponseModel.ts | 2 + .../src/pages/Admin/SurveyPage/Survey.tsx | 38 +++++- .../pages/courseDetailPage/SurveyModal.tsx | 33 +++-- .../courseDetailPage/courseDetailsPage.tsx | 119 ++++++++++++++---- frontend/src/shared/types/question.tsx | 1 + 9 files changed, 311 insertions(+), 72 deletions(-) diff --git a/backend/controllers/courseController.ts b/backend/controllers/courseController.ts index cfa1954..0524a3d 100644 --- a/backend/controllers/courseController.ts +++ b/backend/controllers/courseController.ts @@ -3,8 +3,32 @@ import Course from "../models/courseModel"; import Rating from "../models/ratingModel"; import User, { IUser } from "../models/userModel"; import Progress, { IProgress } from "../models/progressModel"; +import Survey from "../models/surveyModel"; +import Question from "../models/questionModel"; import { emailQueue } from "../jobs/emailQueue"; +// Returns { hasPre, hasPost } for the survey attached to a course (if any). +async function getSurveyPhasePresence( + courseId: string | { toString(): string } +): Promise<{ hasPre: boolean; hasPost: boolean }> { + const course = await Course.findById(courseId).select("surveyId"); + if (!course || !course.surveyId) return { hasPre: false, hasPost: false }; + const survey = await Survey.findById(course.surveyId).select("questions"); + if (!survey || !survey.questions || survey.questions.length === 0) { + return { hasPre: false, hasPost: false }; + } + const questions = await Question.find({ + _id: { $in: survey.questions }, + }).select("phase"); + let hasPre = false; + let hasPost = false; + for (const q of questions) { + if (q.phase === "pre") hasPre = true; + else hasPost = true; + } + return { hasPre, hasPost }; +} + // Define an interface for error objects interface ErrorWithDetails { message?: string; @@ -517,7 +541,8 @@ export const dropCourseEnrollment = async ( isComplete: false, completedComponents: { webinar: false, - survey: false, + preSurvey: false, + postSurvey: false, certificate: false, }, dateCompleted: null, @@ -608,7 +633,8 @@ export const getCourseProgress = async ( isComplete: false, completedComponents: { webinar: false, - survey: false, + preSurvey: false, + postSurvey: false, certificate: false, }, dateCompleted: null, @@ -693,7 +719,8 @@ export const getUserCourseProgress = async ( isComplete: false, completedComponents: { webinar: false, - survey: false, + preSurvey: false, + postSurvey: false, certificate: false, }, dateCompleted: null, @@ -726,13 +753,19 @@ export const updateUserProgress = async ( ): Promise => { try { const { courseId, userId } = req.params; - const { webinarComplete, surveyComplete, certificateComplete } = req.body; + const { + webinarComplete, + preSurveyComplete, + postSurveyComplete, + certificateComplete, + } = req.body; console.log("=== Progress Update Request ==="); console.log("Params:", { courseId, userId }); console.log("Body:", { webinarComplete, - surveyComplete, + preSurveyComplete, + postSurveyComplete, certificateComplete, }); @@ -753,11 +786,31 @@ export const updateUserProgress = async ( console.log("Found progress:", progress._id); - // Update completed components + // Merge with existing values so a single-phase update doesn't clobber the other. + const existing = progress.completedComponents || {}; + const legacySurvey = Boolean(existing.survey); + + const { hasPre, hasPost } = await getSurveyPhasePresence(courseId); + const completedComponents = { - webinar: Boolean(webinarComplete), - survey: Boolean(surveyComplete), - certificate: Boolean(certificateComplete), + webinar: + webinarComplete !== undefined + ? Boolean(webinarComplete) + : Boolean(existing.webinar), + preSurvey: hasPre + ? preSurveyComplete !== undefined + ? Boolean(preSurveyComplete) + : Boolean(existing.preSurvey ?? legacySurvey) + : true, + postSurvey: hasPost + ? postSurveyComplete !== undefined + ? Boolean(postSurveyComplete) + : Boolean(existing.postSurvey ?? legacySurvey) + : true, + certificate: + certificateComplete !== undefined + ? Boolean(certificateComplete) + : Boolean(existing.certificate), }; console.log("Setting completed components:", completedComponents); @@ -818,9 +871,16 @@ export const batchUpdateUserProgress = async ( const results: any[] = []; + const { hasPre, hasPost } = await getSurveyPhasePresence(courseId); + for (const update of updates) { - const { userId, webinarComplete, surveyComplete, certificateComplete } = - update; + const { + userId, + webinarComplete, + preSurveyComplete, + postSurveyComplete, + certificateComplete, + } = update; console.log("Processing update for user:", userId); @@ -839,10 +899,28 @@ export const batchUpdateUserProgress = async ( continue; } + const existing = progress.completedComponents || {}; + const legacySurvey = Boolean(existing.survey); + const completedComponents = { - webinar: Boolean(webinarComplete), - survey: Boolean(surveyComplete), - certificate: Boolean(certificateComplete), + webinar: + webinarComplete !== undefined + ? Boolean(webinarComplete) + : Boolean(existing.webinar), + preSurvey: hasPre + ? preSurveyComplete !== undefined + ? Boolean(preSurveyComplete) + : Boolean(existing.preSurvey ?? legacySurvey) + : true, + postSurvey: hasPost + ? postSurveyComplete !== undefined + ? Boolean(postSurveyComplete) + : Boolean(existing.postSurvey ?? legacySurvey) + : true, + certificate: + certificateComplete !== undefined + ? Boolean(certificateComplete) + : Boolean(existing.certificate), }; progress.completedComponents = completedComponents; diff --git a/backend/controllers/surveyController.ts b/backend/controllers/surveyController.ts index 5ba65bf..276f27a 100644 --- a/backend/controllers/surveyController.ts +++ b/backend/controllers/surveyController.ts @@ -108,7 +108,11 @@ export const updateSurvey = async(req: Request, res: Response) : Promise = // Check if there are existing responses for this survey const responseCount = await SurveyResponse.countDocuments({surveyId: survey._id}); - if (responseCount === 0) { + // If no responses AND (no partial course selection OR all courses selected), safe to update in place + const allCourseIds = survey.courseIds.map((id: any) => id.toString()); + const isPartialUpdate = courseIdsToUpdate && courseIdsToUpdate.length < allCourseIds.length; + + if (responseCount === 0 && !isPartialUpdate) { // No responses, safe to update in place if (name) survey.name = name; if (questions) survey.questions = questions; @@ -120,7 +124,7 @@ export const updateSurvey = async(req: Request, res: Response) : Promise = return; } - const coursesToUpdate = courseIdsToUpdate || survey.courseIds.map(id => id.toString()); + const coursesToUpdate = courseIdsToUpdate || allCourseIds; const coursesStaying = survey.courseIds.map(id => id.toString()).filter(id => !coursesToUpdate.includes(id)); diff --git a/backend/controllers/surveyResponseController.ts b/backend/controllers/surveyResponseController.ts index fe155db..20a9e1d 100644 --- a/backend/controllers/surveyResponseController.ts +++ b/backend/controllers/surveyResponseController.ts @@ -14,13 +14,14 @@ export const getSurveyResponses = async ( res: Response ): Promise => { try { - const { userId, surveyId, courseId, startDate, endDate } = req.query; + const { userId, surveyId, courseId, startDate, endDate, phase } = req.query; let filter: any = {}; if (userId) filter.userId = userId; if (surveyId) filter.surveyId = surveyId; if (courseId) filter.courseId = courseId; + if (phase === "pre" || phase === "post") filter.phase = phase; if (startDate || endDate) { filter.dateCompleted = {}; @@ -62,7 +63,7 @@ export const createSurveyResponse = async ( ): Promise => { try { // answers are a list of QuestionResponse IDs and QuestionResponse objects must be created first before calling this endpoint - const { userId, answers, surveyId, courseId } = req.body; + const { userId, answers, surveyId, courseId, phase } = req.body; if (!userId || !answers || answers.length === 0) { res.status(400).json({ @@ -80,12 +81,20 @@ export const createSurveyResponse = async ( return; } + if (phase !== "pre" && phase !== "post") { + res.status(400).json({ + success: false, + message: "Please provide a valid phase ('pre' or 'post').", + }); + return; + } const newSurveyResponse = new SurveyResponse({ userId, answers, surveyId, courseId, + phase, }); const savedSurveyResponse = await newSurveyResponse.save(); @@ -152,18 +161,23 @@ export const deleteSurveyResponse = async (req: Request, res: Response): Promise // } export const getSurveyResponseStats = async (req: Request, res: Response): Promise => { try { - const { surveyId, courseId } = req.query; + const { surveyId, courseId, phase } = req.query; const matchStage: any = {}; if (surveyId) matchStage.surveyId = new mongoose.Types.ObjectId(surveyId as string); if (courseId) matchStage.courseId = new mongoose.Types.ObjectId(courseId as string); + if (phase === "pre" || phase === "post") matchStage.phase = phase; - // Group responses by survey + courses + // Group responses by survey + course + phase const grouped = await SurveyResponse.aggregate([ { $match: matchStage }, { $group: { - _id: { surveyId: "$surveyId", courseId: "$courseId" }, + _id: { + surveyId: "$surveyId", + courseId: "$courseId", + phase: { $ifNull: ["$phase", "post"] }, + }, totalResponses: { $sum: 1 }, }, }, @@ -177,15 +191,24 @@ export const getSurveyResponseStats = async (req: Request, res: Response): Promi if (!survey) continue; - // Get all QuestionResponse IDs for this survey+course combo - const answerIds = await SurveyResponse.find({ + // Get all QuestionResponse IDs for this survey+course+phase combo + const phaseFilter: any = { surveyId: group._id.surveyId, courseId: group._id.courseId, - }).distinct("answers"); + }; + if (group._id.phase === "pre") { + phaseFilter.phase = "pre"; + } else { + phaseFilter.$or = [{ phase: "post" }, { phase: { $exists: false } }]; + } + const answerIds = await SurveyResponse.find(phaseFilter).distinct("answers"); - // For each question in the survey, calculate breakdown of answers + // For each question in the survey matching this phase, calculate breakdown + const phaseQuestions = (survey.questions as any[]).filter( + (q) => (q.phase || "post") === group._id.phase + ); const questionStats = []; - for (const question of survey.questions as any[]) { + for (const question of phaseQuestions) { const questionResponses = await QuestionResponse.find({ questionId: question._id, _id: { $in: answerIds }, @@ -217,6 +240,7 @@ export const getSurveyResponseStats = async (req: Request, res: Response): Promi surveyVersion: survey.version, courseId: group._id.courseId, courseName: course?.className || "Unknown Course", + phase: group._id.phase, totalResponses: group.totalResponses, questions: questionStats, }); @@ -233,11 +257,12 @@ export const getSurveyResponseStats = async (req: Request, res: Response): Promi // @route GET /api/surveyResponses/export?surveyId=&courseId=&format=row-per-response|row-per-answer export const exportSurveyResponses = async (req: Request, res: Response): Promise => { try { - const { surveyId, courseId, format = "row-per-response" } = req.query; + const { surveyId, courseId, format = "row-per-response", phase } = req.query; const filter: any = {}; - + if (surveyId) filter.surveyId = surveyId; if (courseId) filter.courseId = courseId; + if (phase === "pre" || phase === "post") filter.phase = phase; const responses = await SurveyResponse.find(filter) .populate("answers") @@ -256,19 +281,20 @@ export const exportSurveyResponses = async (req: Request, res: Response): Promis if (format === "row-per-answer") { // Normalized: one row per question-answer pair const headers = [ - "SurveyName", "CourseName", "UserId", "SubmittedAt", + "SurveyName", "CourseName", "Phase", "UserId", "SubmittedAt", "QuestionId", "QuestionText", "AnswerType", "Answer", ]; - + const rows: string[][] = []; for (const resp of responses as any[]) { const surveyName = resp.surveyId?.name || "Unknown"; const courseName = resp.courseId?.className || "Unknown"; - + const respPhase = resp.phase || "post"; + for (const answer of resp.answers) { const question = await Question.findById(answer.questionId); rows.push([ - surveyName, courseName, resp.userId, + surveyName, courseName, respPhase, resp.userId, resp.createdAt?.toISOString() || "", answer.questionId?.toString() || "", question?.question || "", @@ -297,22 +323,23 @@ export const exportSurveyResponses = async (req: Request, res: Response): Promis } const headers = [ - "SurveyName", "CourseName", "UserId", "SubmittedAt", + "SurveyName", "CourseName", "Phase", "UserId", "SubmittedAt", ...allQuestions.map((q, i) => `${i + 1}. ${q.question}`), ]; - + const rows: string[][] = []; for (const resp of responses as any[]) { const surveyName = resp.surveyId?.name || "Unknown"; const courseName = resp.courseId?.className || "Unknown"; - + const respPhase = resp.phase || "post"; + const answerMap = new Map(); for (const answer of resp.answers) { answerMap.set(answer.questionId?.toString() || "", answer.answer || ""); } - + rows.push([ - surveyName, courseName, resp.userId, + surveyName, courseName, respPhase, resp.userId, resp.createdAt?.toISOString() || "", ...allQuestions.map((q) => answerMap.get(q._id.toString()) || ""), ]); diff --git a/backend/models/questionModel.ts b/backend/models/questionModel.ts index 0fbd403..3baf36a 100644 --- a/backend/models/questionModel.ts +++ b/backend/models/questionModel.ts @@ -7,6 +7,7 @@ export interface IQuestion extends Document { answerType: string; answers?: string[]; isRequired: boolean; + phase: "pre" | "post"; } // Define the schema with placeholders for fields (others will fill this in) @@ -17,6 +18,12 @@ const QuestionSchema: Schema = new Schema( answerType: { type: String, required: true }, answers: [{ type: String }], isRequired: { type: Boolean, required: true }, + phase: { + type: String, + enum: ["pre", "post"], + default: "post", + required: true, + }, }, { timestamps: true, diff --git a/backend/models/surveyResponseModel.ts b/backend/models/surveyResponseModel.ts index 9e5865d..c435ea1 100644 --- a/backend/models/surveyResponseModel.ts +++ b/backend/models/surveyResponseModel.ts @@ -6,6 +6,7 @@ export interface ISurveyResponse extends Document { answers: (mongoose.Types.ObjectId | IQuestionResponse)[]; surveyId: mongoose.Types.ObjectId; courseId: mongoose.Types.ObjectId; + phase: "pre" | "post"; } const surveyResponseSchema: Schema = new Schema( @@ -19,6 +20,7 @@ const surveyResponseSchema: Schema = new Schema( ], surveyId: { type: Schema.Types.ObjectId, ref: "Survey"}, courseId: { type: Schema.Types.ObjectId, ref: "Course"}, + phase: { type: String, enum: ["pre", "post"], required: true, default: "post" }, }, { timestamps: true, diff --git a/frontend/src/pages/Admin/SurveyPage/Survey.tsx b/frontend/src/pages/Admin/SurveyPage/Survey.tsx index 3944eb2..f57f6b2 100644 --- a/frontend/src/pages/Admin/SurveyPage/Survey.tsx +++ b/frontend/src/pages/Admin/SurveyPage/Survey.tsx @@ -14,6 +14,7 @@ interface SurveyQuestion { answerType: string; answers: string[]; isRequired: boolean; + phase: "pre" | "post"; isEdited: boolean; } @@ -70,6 +71,7 @@ const Survey = () => { setQuestions( (surveyData.questions || []).map((q: any) => ({ ...q, + phase: q.phase || "post", isEdited: false, })) ); @@ -102,6 +104,7 @@ const Survey = () => { answerType: "Text Input", answers: [""], isRequired: true, + phase: "post", isEdited: true, }, ]); @@ -121,6 +124,14 @@ const Survey = () => { setHasUnsavedChanges(true); }; + const handlePhaseChange = (index: number, value: "pre" | "post") => { + const updated = [...questions]; + updated[index].phase = value; + updated[index].isEdited = true; + setQuestions(updated); + setHasUnsavedChanges(true); + }; + const handleRequiredChange = (index: number, value: boolean) => { const updated = [...questions]; updated[index].isRequired = value; @@ -189,6 +200,7 @@ const Survey = () => { answerType: q.answerType, answers: q.answers || [], isRequired: q.isRequired, + phase: q.phase || "post", }); return response.data._id; } @@ -321,7 +333,7 @@ const Survey = () => { setSurveyName(survey.name); setLinkedCourseIds([...survey.courseIds, course._id]); setQuestions( - survey.questions.map((q: any) => ({ ...q, isEdited: false })) + survey.questions.map((q: any) => ({ ...q, phase: q.phase || "post", isEdited: false })) ); setShowReuseModal(false); setHasUnsavedChanges(false); @@ -344,7 +356,7 @@ const Survey = () => { setSurveyName(data.name); setLinkedCourseIds(data.courseIds || []); setQuestions( - (data.questions || []).map((q: any) => ({ ...q, isEdited: false })) + (data.questions || []).map((q: any) => ({ ...q, phase: q.phase || "post", isEdited: false })) ); setShowReuseModal(false); setHasUnsavedChanges(false); @@ -353,7 +365,7 @@ const Survey = () => { const fetched = await apiClient.get(`/surveys/${data._id}`); const full = fetched.data.survey || fetched.data; setQuestions( - (full.questions || []).map((q: any) => ({ ...q, isEdited: false })) + (full.questions || []).map((q: any) => ({ ...q, phase: q.phase || "post", isEdited: false })) ); } catch (error) { console.error("Error duplicating survey:", error); @@ -480,6 +492,26 @@ const Survey = () => { +
+ + +
+ {(question.answerType === "Multiple Choice" || question.answerType === "Multi-select") && (
diff --git a/frontend/src/pages/courseDetailPage/SurveyModal.tsx b/frontend/src/pages/courseDetailPage/SurveyModal.tsx index 99f51d4..a8d71ad 100644 --- a/frontend/src/pages/courseDetailPage/SurveyModal.tsx +++ b/frontend/src/pages/courseDetailPage/SurveyModal.tsx @@ -8,6 +8,7 @@ interface SurveyModalProps { courseId: string | null; surveyId: string | null; setSurveyCompleted: any; + phase: "pre" | "post"; } export default function SurveyModal({ @@ -16,6 +17,7 @@ export default function SurveyModal({ courseId, surveyId, setSurveyCompleted, + phase, }: SurveyModalProps) { const [questionNumber, setQuestionNumber] = useState(0); const [surveyQuestions, setSurveyQuestions] = useState([]); @@ -28,14 +30,18 @@ export default function SurveyModal({ try { const response = await apiClient.get(`surveys/${surveyId}`); const survey = response.data.survey || response.data; - setSurveyQuestions(survey.questions || []); + const all = survey.questions || []; + const filtered = all.filter( + (q: any) => (q.phase || "post") === phase + ); + setSurveyQuestions(filtered); } catch (error) { console.error("Error fetching survey:", error); } }; populateQuestions(); - }, [surveyId]); + }, [surveyId, phase]); if (!isOpen) return null; @@ -64,21 +70,23 @@ export default function SurveyModal({ responseIds.push(response.data._id); } - // Create survey response with surveyId and courseId + // Create survey response with surveyId, courseId, and phase await apiClient.post("surveyResponses", { userId, answers: responseIds, surveyId, courseId, + phase, }); - // Update progress + // Update progress (phase-aware) + const progressBody: Record = + phase === "pre" + ? { preSurveyComplete: true } + : { postSurveyComplete: true, webinarComplete: true }; await apiClient.put( `/courses/${courseId}/progress/single/${userId}`, - { - surveyComplete: true, - webinarComplete: true, - } + progressBody ); setSurveyCompleted(true); } catch (error) { @@ -110,7 +118,9 @@ export default function SurveyModal({ > × -

Survey

+

+ {phase === "pre" ? "Pre-Survey" : "Post-Survey"} +

{surveyQuestions.length > 0 ? ( ) : ( -

Loading questions...

+

+ No {phase === "pre" ? "pre-survey" : "post-survey"} questions + for this course. +

)}
diff --git a/frontend/src/pages/courseDetailPage/courseDetailsPage.tsx b/frontend/src/pages/courseDetailPage/courseDetailsPage.tsx index b9a48ca..b38d9a7 100644 --- a/frontend/src/pages/courseDetailPage/courseDetailsPage.tsx +++ b/frontend/src/pages/courseDetailPage/courseDetailsPage.tsx @@ -60,7 +60,10 @@ const CoursePage = ({ setCartItemCount }: CatalogProps) => { const [hasProgress, setHasProgress] = useState(null); const [progress, setProgress] = useState(null); const [workshopCompleted, setWorkshopCompleted] = useState(false); - const [surveyCompleted, setSurveyCompleted] = useState(false); + const [preSurveyCompleted, setPreSurveyCompleted] = + useState(false); + const [postSurveyCompleted, setPostSurveyCompleted] = + useState(false); const [certificateCompleted, setCertificateCompleted] = useState(false); const [isDroppingCourse, setIsDroppingCourse] = useState(false); @@ -84,9 +87,18 @@ const CoursePage = ({ setCartItemCount }: CatalogProps) => { } else { setHasProgress(true); setProgress(progress); - setWorkshopCompleted(progress.completedComponents.webinar); - setSurveyCompleted(progress.completedComponents.survey); - setCertificateCompleted(progress.completedComponents.certificate); + const cc = progress.completedComponents || {}; + setWorkshopCompleted(Boolean(cc.webinar)); + // Fall back to legacy `survey` flag when pre/post fields are absent. + setPreSurveyCompleted( + cc.preSurvey !== undefined ? Boolean(cc.preSurvey) : Boolean(cc.survey) + ); + setPostSurveyCompleted( + cc.postSurvey !== undefined + ? Boolean(cc.postSurvey) + : Boolean(cc.survey) + ); + setCertificateCompleted(Boolean(cc.certificate)); console.log("progress", response.data); } } catch (error) { @@ -189,7 +201,8 @@ const CoursePage = ({ setCartItemCount }: CatalogProps) => { setHasProgress(false); setProgress(null); setWorkshopCompleted(false); - setSurveyCompleted(false); + setPreSurveyCompleted(false); + setPostSurveyCompleted(false); setCertificateCompleted(false); } catch (error) { console.error("Error dropping course enrollment:", error); @@ -381,9 +394,11 @@ const CoursePage = ({ setCartItemCount }: CatalogProps) => { productType={courseDetailsData.productType} hasProgress={hasProgress} workshop={workshopCompleted} - survey={surveyCompleted} + preSurvey={preSurveyCompleted} + postSurvey={postSurveyCompleted} setWorkshopCompleted={setWorkshopCompleted} - setSurveyCompleted={setSurveyCompleted} + setPreSurveyCompleted={setPreSurveyCompleted} + setPostSurveyCompleted={setPostSurveyCompleted} setCertificateCompleted={setCertificateCompleted} courseName={courseDetailsData.className} certificate={certificateCompleted} @@ -481,9 +496,11 @@ const DisplayBar = ({ productType, hasProgress, workshop, - survey, + preSurvey, + postSurvey, setWorkshopCompleted, - setSurveyCompleted, + setPreSurveyCompleted, + setPostSurveyCompleted, setCertificateCompleted, certificate, courseName, @@ -498,15 +515,18 @@ const DisplayBar = ({ productType: string; hasProgress: boolean | null; workshop: boolean; - survey: boolean; + preSurvey: boolean; + postSurvey: boolean; certificate: boolean; setWorkshopCompleted: any; - setSurveyCompleted: any; + setPreSurveyCompleted: any; + setPostSurveyCompleted: any; setCertificateCompleted: any; courseName: string; surveyId: string | null; }) => { - const [currentPage, setCurrentPage] = useState("Workshop"); + const [currentPage, setCurrentPage] = useState("PreSurvey"); + const [preSurveyColor, setPreSurveyColor] = useState("#F79518"); const [surveyColor, setSurveyColor] = useState("#D9D9D9"); const [certificateColor, setCertificateColor] = useState("#D9D9D9"); const [videoLink, setVideoLink] = useState(""); @@ -738,14 +758,35 @@ const DisplayBar = ({
{/* Workshop -> Survey -> Certificate */}
- {/* Workshop Button */} + {/* Pre-Survey Button */} + {/* Workshop Button */} + - {/* Survey Button */} + {/* Post-Survey Button */} {/* Certificate Button */}
+ {currentPage === "PreSurvey" && ( +
+
+ {!surveyId ? ( +

+ No survey configured for this course. +

+ ) : ( + + )} + setIsSurveyModalOpen(false)} + courseId={courseId} + surveyId={surveyId || null} + setSurveyCompleted={setPreSurveyCompleted} + phase="pre" + /> +
+
+ )} {currentPage === "Workshop" && (
{productType === "Virtual Training - On Demand" ? ( @@ -920,10 +992,12 @@ const DisplayBar = ({

)} @@ -932,7 +1006,8 @@ const DisplayBar = ({ onClose={() => setIsSurveyModalOpen(false)} courseId={courseId} surveyId={surveyId || null} - setSurveyCompleted={setSurveyCompleted} + setSurveyCompleted={setPostSurveyCompleted} + phase="post" >
@@ -942,12 +1017,12 @@ const DisplayBar = ({ Once you have completed the course, your certificate will be accessible here.
-

+

Complete webinar to access certificate