From 1f9fff9616c7d123e586df7eb0f36229ca52ecf0 Mon Sep 17 00:00:00 2001 From: Tanja Ulmen Date: Wed, 26 Nov 2025 11:15:44 +0100 Subject: [PATCH 1/2] feat(): change ux/ui of quiz component into cards --- src/app/quiz/quiz-checkbox.component.scss | 2 - src/app/quiz/quiz.component.html | 238 ++++++++++++++-------- src/app/quiz/quiz.component.scss | 46 ++++- src/app/quiz/quiz.component.ts | 117 +++++++---- 4 files changed, 277 insertions(+), 126 deletions(-) diff --git a/src/app/quiz/quiz-checkbox.component.scss b/src/app/quiz/quiz-checkbox.component.scss index a429ab23..6902e5ad 100644 --- a/src/app/quiz/quiz-checkbox.component.scss +++ b/src/app/quiz/quiz-checkbox.component.scss @@ -14,8 +14,6 @@ clr-checkbox-container { .quiz-question-title { white-space: wrap; width: calc(100% - 40px) !important; - margin-left: 20px; - margin-right: 20px; margin-bottom: 10px; max-width: 100%; } diff --git a/src/app/quiz/quiz.component.html b/src/app/quiz/quiz.component.html index be6b9893..c3dc50af 100644 --- a/src/app/quiz/quiz.component.html +++ b/src/app/quiz/quiz.component.html @@ -1,83 +1,136 @@ -
-  @if (quizTitle) { 
{{ quizTitle }}
} +
+
+
+ @if (quizTitle) { +
{{ quizTitle }}
+ } - @if(isPersistent && !loadingQuiz && !currentQuiz) { - - - - Failed to load quiz! - - - - } - @else if (isPersistent && alreadyPassed) { - - - - - You passed this quiz. Great job! - - @if(quizIssuer) { -
- + @if (isPersistent && !loadingQuiz && !currentQuiz) { + + + Failed to load quiz! + + + } @else if (isPersistent && alreadyPassed) { + + + + You passed this quiz. Great job! + @if (quizIssuer) { +
+ +
+ } +
+
+ } @else if (isPersistent && loadingQuestions && started) { + Loading... + Loading Questions... + } @else if ( + !isPersistent || (isPersistent && !alreadyPassed && started) + ) { + + @if (questions.length > 0) { +
+
+ +
+ + @for (q of questions; track q.id || q.title; let i = $index) { +
+ @if (q.type === 'checkbox') { + + } @else { + + } +
+ } } - - - } @else if(isPersistent && loadingQuestions && started) { - Loading... - Loading Questions... - } @else if (!isPersistent || isPersistent && !alreadyPassed && started) { - @for (q of questions; track q.id || q.title; let i = $index) { - @if (q.type === 'checkbox') { - - } @else { - } +
+ + @if ( + (!isPersistent || (isPersistent && !alreadyPassed && started)) && + questions.length > 0 + ) { + + } +
+ + @if (!isPersistent || (isPersistent && !alreadyPassed && started)) { + @if (isSubmitted && !alreadyPassed) { + + + + There are wrong answers. Please try again. + + + }
- +
- @if(allowedAtts <= 100) { - Allowed attempts left: {{ allowedAtts }} + + @if (allowedAtts <= 100) { + + Allowed attempts left: {{ allowedAtts }} + } } - @if(isPersistent && loadingQuestions && !started && !alreadyPassed && !loadingQuiz) { + + @if ( + isPersistent && + loadingQuestions && + !started && + !alreadyPassed && + !loadingQuiz + ) {
- @if(allowedAtts <= 100) { - Allowed attempts left: {{ allowedAtts }} + @if (allowedAtts <= 100) { + + Allowed attempts left: {{ allowedAtts }} + } } -
+ diff --git a/src/app/quiz/quiz.component.scss b/src/app/quiz/quiz.component.scss index 7ed0cf9a..c3c18ca4 100644 --- a/src/app/quiz/quiz.component.scss +++ b/src/app/quiz/quiz.component.scss @@ -1,10 +1,15 @@ .quiz-container { display: flex; flex-direction: column; + border: none; + padding: 20px; + margin-top: 20px; + border-radius: var(--cds-alias-object-border-radius-100); } .quiz-title { - margin-bottom: 1.2rem; + margin-bottom: 1rem; + margin-top: 0; font-weight: bold; } @@ -12,6 +17,9 @@ display: flex; width: 100%; flex-direction: row; + padding: 20px; + justify-content: flex-end; + gap: 5px; } .submit-button { @@ -24,3 +32,39 @@ .centered { align-items: center; } + +.attempts { + align-self: flex-end; +} + +.nav-container { + display: flex; + align-items: center; + margin: 1rem 0; + + .nav-label { + margin: 0 1rem; + } +} + +.quiz-progress { + margin-bottom: 1rem; +} + +.quiz-card { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + border: 1px; + border-radius: var(--cds-alias-object-border-radius-100); +} + +.quiz-footer { + display: flex; + align-items: center; + gap: 1rem; + + &-label { + font-size: var(--cds-alias-typography-caption-font-size); + opacity: 0.8; + } +} diff --git a/src/app/quiz/quiz.component.ts b/src/app/quiz/quiz.component.ts index 25694b03..4b2ad050 100644 --- a/src/app/quiz/quiz.component.ts +++ b/src/app/quiz/quiz.component.ts @@ -5,18 +5,18 @@ import { QueryList, ViewChild, ViewChildren, -} from '@angular/core'; -import { Validation } from './Validation'; -import { shuffleArray, shuffleStringArray } from '../utils'; +} from "@angular/core"; +import { Validation } from "./Validation"; +import { shuffleArray, shuffleStringArray } from "../utils"; import { Quiz, QuizService, PreparedQuizEvaluation, -} from '../services/quiz.service'; -import { QuizCheckboxComponent } from './quiz-checkbox.component'; -import { QuizRadioComponent } from './quiz-radio.component'; -import { QuestionAnswer } from './QuestionAnswer'; -import { QuestionType } from './QuestionType'; +} from "../services/quiz.service"; +import { QuizCheckboxComponent } from "./quiz-checkbox.component"; +import { QuizRadioComponent } from "./quiz-radio.component"; +import { QuestionAnswer } from "./QuestionAnswer"; +import { QuestionType } from "./QuestionType"; import { catchError, map, @@ -25,11 +25,11 @@ import { shareReplay, Subject, switchMap, -} from 'rxjs'; -import { UserService } from '../services/user.service'; -import { PdfService } from '../services/pdf.service'; -import { AlertComponent } from '../alert/alert.component'; -import { ClrAlertType } from '../alert/clr-alert-type'; +} from "rxjs"; +import { UserService } from "../services/user.service"; +import { PdfService } from "../services/pdf.service"; +import { AlertComponent } from "../alert/alert.component"; +import { ClrAlertType } from "../alert/clr-alert-type"; type Question = { id?: string; @@ -45,34 +45,34 @@ type Question = { @Component({ // eslint-disable-next-line @angular-eslint/component-selector - selector: 'quiz', - templateUrl: 'quiz.component.html', - styleUrls: ['quiz.component.scss'], + selector: "quiz", + templateUrl: "quiz.component.html", + styleUrls: ["quiz.component.scss"], standalone: false, }) export class QuizComponent implements OnInit { - @ViewChild('failedDownloadAlert') failedDownloadAlert: AlertComponent; + @ViewChild("failedDownloadAlert") failedDownloadAlert: AlertComponent; @ViewChildren(QuizCheckboxComponent) private chk!: QueryList; @ViewChildren(QuizRadioComponent) private rad!: QueryList; /** Local props */ - @Input() quizTitle = ''; - @Input() questionsRaw = ''; + @Input() quizTitle = ""; + @Input() questionsRaw = ""; @Input() allowedAtts = 1; @Input() questionCount = 0; @Input() shuffle = false; /** Persistent */ - @Input() quizId = ''; - @Input() scenarioId = ''; - @Input() scenarioName = ''; - @Input() courseName = ''; + @Input() quizId = ""; + @Input() scenarioId = ""; + @Input() scenarioName = ""; + @Input() courseName = ""; private timeStamp?: string; questions: Question[] = []; - validationType: Validation = 'standard'; + validationType: Validation = "standard"; isSubmitted = false; isPersistent = false; alreadyPassed = false; @@ -80,7 +80,8 @@ export class QuizComponent implements OnInit { loadingQuestions = true; loadingQuiz = true; currentQuiz?: Quiz; - quizIssuer = ''; + quizIssuer = ""; + currentIndex = 0; correctsByQuestionId: Record = {}; @@ -139,7 +140,7 @@ export class QuizComponent implements OnInit { /** ---------- Local ---------- */ private initLocal() { - const blocks = (this.questionsRaw || '').split('\n---\n').filter(Boolean); + const blocks = (this.questionsRaw || "").split("\n---\n").filter(Boolean); const total = blocks.length; if (this.questionCount <= 0 || this.questionCount > total) this.questionCount = total; @@ -148,6 +149,8 @@ export class QuizComponent implements OnInit { if (this.shuffle) shuffleStringArray(pool); const selected = pool.slice(0, this.questionCount); this.questions = selected.map((raw) => this.fromRaw(raw)); + + this.currentIndex = 0; } private fromRaw(raw: string): Question { @@ -162,19 +165,19 @@ export class QuizComponent implements OnInit { )[0]; }; - const title = getParam('title') ?? ''; - const helperText = getParam('info') ?? ''; - const type = (getParam('type') ?? 'checkbox') as 'checkbox' | 'radio'; - const validation = (getParam('validation') ?? 'standard') as Validation; - const successMsg = getParam('successMsg') ?? 'Correct!'; - const errorMsg = getParam('errorMsg') ?? 'Incorrect!'; - const qShuffle = (getParam('shuffle') ?? 'false') === 'true'; + const title = getParam("title") ?? ""; + const helperText = getParam("info") ?? ""; + const type = (getParam("type") ?? "checkbox") as "checkbox" | "radio"; + const validation = (getParam("validation") ?? "standard") as Validation; + const successMsg = getParam("successMsg") ?? "Correct!"; + const errorMsg = getParam("errorMsg") ?? "Incorrect!"; + const qShuffle = (getParam("shuffle") ?? "false") === "true"; - const optionsRaw = raw.split(/\n- (.*)/s)[1] ?? ''; - const options = optionsRaw.split('\n- ').filter(Boolean); + const optionsRaw = raw.split(/\n- (.*)/s)[1] ?? ""; + const options = optionsRaw.split("\n- ").filter(Boolean); let answers: QuestionAnswer[] = options.map((o) => ({ - title: o.split(':(')[0], - correct: o.split(':(')[1]?.toLowerCase() === 'x)', + title: o.split(":(")[0], + correct: o.split(":(")[1]?.toLowerCase() === "x)", })); if (qShuffle) answers = shuffleArray(answers); @@ -199,8 +202,8 @@ export class QuizComponent implements OnInit { quiz.questions.forEach((question) => { this.correctIdsByQid.set( - question.id ?? '', - this.correctIds$(question.id ?? ''), + question.id ?? "", + this.correctIds$(question.id ?? ""), ); }); @@ -213,11 +216,13 @@ export class QuizComponent implements OnInit { this.allowedAtts = Math.max(0, (quiz.max_attempts ?? 1) - used); this.currentQuiz = quiz; this.loadingQuiz = false; + this.currentIndex = 0; }, error: () => { this.allowedAtts = quiz.max_attempts ?? 1; this.currentQuiz = quiz; this.loadingQuiz = false; + this.currentIndex = 0; }, }); }); @@ -247,7 +252,7 @@ export class QuizComponent implements OnInit { id: q.id, title: q.title, helperText: q.description, - type: q.type === 'radio' ? 'radio' : 'checkbox', + type: q.type === "radio" ? "radio" : "checkbox", validation: quiz.validation_type as Validation, successMsg: q.success_message, errorMsg: q.failure_message, @@ -260,6 +265,7 @@ export class QuizComponent implements OnInit { this.isSubmitted = false; this.loadingQuestions = false; this.started = true; + this.currentIndex = 0; }); } @@ -280,7 +286,7 @@ export class QuizComponent implements OnInit { c = 0; this.questions.forEach((q) => { if (!q.id) return; - if (q.type === 'radio') { + if (q.type === "radio") { const comp = this.rad.get(r++); answers[q.id] = comp?.getSelectedAnswerIds() ?? []; } else { @@ -300,6 +306,7 @@ export class QuizComponent implements OnInit { this.rad?.forEach((c) => c.reset()); this.chk?.forEach((c) => c.reset()); this.isSubmitted = false; + this.currentIndex = 0; return; } if (this.allowedAtts < 1) return; @@ -307,13 +314,14 @@ export class QuizComponent implements OnInit { this.rad?.forEach((c) => c.reset()); this.chk?.forEach((c) => c.reset()); this.isSubmitted = false; + this.currentIndex = 0; this.qs.getUserQuiz(this.quizId).subscribe((quiz) => this.start(quiz)); } download() { if (!this.timeStamp) { this.failedDownloadAlert.doAlert( - 'Failed to issue certificate. Unable to retrieve completion timestamp!', + "Failed to issue certificate. Unable to retrieve completion timestamp!", ClrAlertType.Danger, true, 3000, @@ -325,8 +333,31 @@ export class QuizComponent implements OnInit { description: `Congratulations! We hereby certify that the following user has successfully completed the course "${this.courseName ? this.courseName : this.scenarioName}" by fully attending all sessions and fulfilling the requirements, including the successful completion of the mandatory final test:`, personName: `${this.us.getEmail().value}`, title: `${this.courseName ? this.courseName : this.scenarioName}`, - fileName: `${this.scenarioName ? `certificate-${this.scenarioName}.pdf` : 'certificate.pdf'}`, + fileName: `${this.scenarioName ? `certificate-${this.scenarioName}.pdf` : "certificate.pdf"}`, issuer: `${this.quizIssuer}`, }); } + + get totalQuestions(): number { + return this.questions.length; + } + + get progress(): number { + if (!this.totalQuestions) { + return 0; + } + return ((this.currentIndex + 1) / this.totalQuestions) * 100; + } + + nextQuestion(): void { + if (this.currentIndex < this.questions.length - 1) { + this.currentIndex++; + } + } + + prevQuestion(): void { + if (this.currentIndex > 0) { + this.currentIndex--; + } + } } From 0dec2fde638774ce73a2b4c5495f91e3155ed1d4 Mon Sep 17 00:00:00 2001 From: Tanja Ulmen Date: Wed, 26 Nov 2025 15:20:23 +0100 Subject: [PATCH 2/2] feat(): show modal if not all questions are answered --- src/app/quiz/quiz.component.html | 151 ++++++++++++++++--------------- src/app/quiz/quiz.component.ts | 116 ++++++++++++++---------- 2 files changed, 146 insertions(+), 121 deletions(-) diff --git a/src/app/quiz/quiz.component.html b/src/app/quiz/quiz.component.html index c3dc50af..29156655 100644 --- a/src/app/quiz/quiz.component.html +++ b/src/app/quiz/quiz.component.html @@ -11,85 +11,60 @@
{{ quizTitle }}
Failed to load quiz! - } @else if (isPersistent && alreadyPassed) { - - - - You passed this quiz. Great job! - @if (quizIssuer) { -
- -
- } -
-
- } @else if (isPersistent && loadingQuestions && started) { + } + + @if (isPersistent && loadingQuestions && started) { Loading... Loading Questions... - } @else if ( - !isPersistent || (isPersistent && !alreadyPassed && started) + } + @if ( + (!isPersistent || (isPersistent && started)) && questions.length > 0 ) { - - @if (questions.length > 0) { -
-
- -
+
+
+
+
- @for (q of questions; track q.id || q.title; let i = $index) { -
- @if (q.type === 'checkbox') { - - } @else { - - } -
- } + @for (q of questions; track q.id || q.title; let i = $index) { +
+ @if (q.type === 'checkbox') { + + } @else { + + } +
} }
- @if ( - (!isPersistent || (isPersistent && !alreadyPassed && started)) && - questions.length > 0 - ) { + @if ((!isPersistent || (isPersistent && started)) && questions.length > 0) { } - + @if (isPersistent && alreadyPassed) { + + + + You passed this quiz. Great job! + @if (quizIssuer) { +
+ +
+ } +
+
+ } @if (!isPersistent || (isPersistent && !alreadyPassed && started)) { - @if (isSubmitted && !alreadyPassed) { + @if (isSubmitted && alreadyPassed === false) { - There are wrong answers. Please try again. + You did not reach the required score. Please try again. @@ -183,3 +176,15 @@
{{ quizTitle }}
} } + + + + + + diff --git a/src/app/quiz/quiz.component.ts b/src/app/quiz/quiz.component.ts index 4b2ad050..a9cdbb0d 100644 --- a/src/app/quiz/quiz.component.ts +++ b/src/app/quiz/quiz.component.ts @@ -5,18 +5,18 @@ import { QueryList, ViewChild, ViewChildren, -} from "@angular/core"; -import { Validation } from "./Validation"; -import { shuffleArray, shuffleStringArray } from "../utils"; +} from '@angular/core'; +import { Validation } from './Validation'; +import { shuffleArray, shuffleStringArray } from '../utils'; import { Quiz, QuizService, PreparedQuizEvaluation, -} from "../services/quiz.service"; -import { QuizCheckboxComponent } from "./quiz-checkbox.component"; -import { QuizRadioComponent } from "./quiz-radio.component"; -import { QuestionAnswer } from "./QuestionAnswer"; -import { QuestionType } from "./QuestionType"; +} from '../services/quiz.service'; +import { QuizCheckboxComponent } from './quiz-checkbox.component'; +import { QuizRadioComponent } from './quiz-radio.component'; +import { QuestionAnswer } from './QuestionAnswer'; +import { QuestionType } from './QuestionType'; import { catchError, map, @@ -25,11 +25,11 @@ import { shareReplay, Subject, switchMap, -} from "rxjs"; -import { UserService } from "../services/user.service"; -import { PdfService } from "../services/pdf.service"; -import { AlertComponent } from "../alert/alert.component"; -import { ClrAlertType } from "../alert/clr-alert-type"; +} from 'rxjs'; +import { UserService } from '../services/user.service'; +import { PdfService } from '../services/pdf.service'; +import { AlertComponent } from '../alert/alert.component'; +import { ClrAlertType } from '../alert/clr-alert-type'; type Question = { id?: string; @@ -45,43 +45,45 @@ type Question = { @Component({ // eslint-disable-next-line @angular-eslint/component-selector - selector: "quiz", - templateUrl: "quiz.component.html", - styleUrls: ["quiz.component.scss"], + selector: 'quiz', + templateUrl: 'quiz.component.html', + styleUrls: ['quiz.component.scss'], standalone: false, }) export class QuizComponent implements OnInit { - @ViewChild("failedDownloadAlert") failedDownloadAlert: AlertComponent; + @ViewChild('failedDownloadAlert') failedDownloadAlert: AlertComponent; @ViewChildren(QuizCheckboxComponent) private chk!: QueryList; @ViewChildren(QuizRadioComponent) private rad!: QueryList; /** Local props */ - @Input() quizTitle = ""; - @Input() questionsRaw = ""; + @Input() quizTitle = ''; + @Input() questionsRaw = ''; @Input() allowedAtts = 1; @Input() questionCount = 0; @Input() shuffle = false; /** Persistent */ - @Input() quizId = ""; - @Input() scenarioId = ""; - @Input() scenarioName = ""; - @Input() courseName = ""; + @Input() quizId = ''; + @Input() scenarioId = ''; + @Input() scenarioName = ''; + @Input() courseName = ''; private timeStamp?: string; questions: Question[] = []; - validationType: Validation = "standard"; + validationType: Validation = 'standard'; isSubmitted = false; isPersistent = false; - alreadyPassed = false; + alreadyPassed: boolean | null = null; started = false; loadingQuestions = true; loadingQuiz = true; currentQuiz?: Quiz; - quizIssuer = ""; + quizIssuer = ''; currentIndex = 0; + lastPass: boolean | null = null; + showIncompleteModal = false; correctsByQuestionId: Record = {}; @@ -98,6 +100,7 @@ export class QuizComponent implements OnInit { this.qs.recordEvaluation(this.quizId, this.scenarioId, answers).pipe( map((res) => { this.alreadyPassed = res.attempt.pass; + this.timeStamp = res.attempt.timestamp; return { pass: res.attempt.pass, @@ -140,7 +143,7 @@ export class QuizComponent implements OnInit { /** ---------- Local ---------- */ private initLocal() { - const blocks = (this.questionsRaw || "").split("\n---\n").filter(Boolean); + const blocks = (this.questionsRaw || '').split('\n---\n').filter(Boolean); const total = blocks.length; if (this.questionCount <= 0 || this.questionCount > total) this.questionCount = total; @@ -165,19 +168,19 @@ export class QuizComponent implements OnInit { )[0]; }; - const title = getParam("title") ?? ""; - const helperText = getParam("info") ?? ""; - const type = (getParam("type") ?? "checkbox") as "checkbox" | "radio"; - const validation = (getParam("validation") ?? "standard") as Validation; - const successMsg = getParam("successMsg") ?? "Correct!"; - const errorMsg = getParam("errorMsg") ?? "Incorrect!"; - const qShuffle = (getParam("shuffle") ?? "false") === "true"; + const title = getParam('title') ?? ''; + const helperText = getParam('info') ?? ''; + const type = (getParam('type') ?? 'checkbox') as 'checkbox' | 'radio'; + const validation = (getParam('validation') ?? 'standard') as Validation; + const successMsg = getParam('successMsg') ?? 'Correct!'; + const errorMsg = getParam('errorMsg') ?? 'Incorrect!'; + const qShuffle = (getParam('shuffle') ?? 'false') === 'true'; - const optionsRaw = raw.split(/\n- (.*)/s)[1] ?? ""; - const options = optionsRaw.split("\n- ").filter(Boolean); + const optionsRaw = raw.split(/\n- (.*)/s)[1] ?? ''; + const options = optionsRaw.split('\n- ').filter(Boolean); let answers: QuestionAnswer[] = options.map((o) => ({ - title: o.split(":(")[0], - correct: o.split(":(")[1]?.toLowerCase() === "x)", + title: o.split(':(')[0], + correct: o.split(':(')[1]?.toLowerCase() === 'x)', })); if (qShuffle) answers = shuffleArray(answers); @@ -202,15 +205,15 @@ export class QuizComponent implements OnInit { quiz.questions.forEach((question) => { this.correctIdsByQid.set( - question.id ?? "", - this.correctIds$(question.id ?? ""), + question.id ?? '', + this.correctIds$(question.id ?? ''), ); }); this.qs.getEvaluationForUser(this.quizId, this.scenarioId).subscribe({ next: (ev: PreparedQuizEvaluation) => { const last = ev.attempts?.[ev.attempts.length - 1]; - this.alreadyPassed = !!last?.pass; + this.alreadyPassed = last ? last.pass : null; this.timeStamp = last.timestamp; const used = ev.attempts?.length ?? 0; this.allowedAtts = Math.max(0, (quiz.max_attempts ?? 1) - used); @@ -252,7 +255,7 @@ export class QuizComponent implements OnInit { id: q.id, title: q.title, helperText: q.description, - type: q.type === "radio" ? "radio" : "checkbox", + type: q.type === 'radio' ? 'radio' : 'checkbox', validation: quiz.validation_type as Validation, successMsg: q.success_message, errorMsg: q.failure_message, @@ -280,21 +283,37 @@ export class QuizComponent implements OnInit { return; } + this.alreadyPassed = null; + // Build answers from children (answer IDs only) const answers: Record = {}; let r = 0, - c = 0; + c = 0, + hasIncomplete = false; this.questions.forEach((q) => { if (!q.id) return; - if (q.type === "radio") { + if (q.type === 'radio') { const comp = this.rad.get(r++); - answers[q.id] = comp?.getSelectedAnswerIds() ?? []; + const sel = comp?.getSelectedAnswerIds() ?? []; + answers[q.id] = sel; + if (sel.length === 0) { + hasIncomplete = true; + } } else { const comp = this.chk.get(c++); - answers[q.id] = comp?.getSelectedAnswerIds() ?? []; + const sel = comp?.getSelectedAnswerIds() ?? []; + answers[q.id] = sel; + if (sel.length === 0) { + hasIncomplete = true; + } } }); + if (hasIncomplete) { + this.showIncompleteModal = true; + return; + } + this.attempt.next(answers); this.isSubmitted = true; this.allowedAtts = Math.max(0, this.allowedAtts - 1); @@ -315,13 +334,14 @@ export class QuizComponent implements OnInit { this.chk?.forEach((c) => c.reset()); this.isSubmitted = false; this.currentIndex = 0; + this.alreadyPassed = null; this.qs.getUserQuiz(this.quizId).subscribe((quiz) => this.start(quiz)); } download() { if (!this.timeStamp) { this.failedDownloadAlert.doAlert( - "Failed to issue certificate. Unable to retrieve completion timestamp!", + 'Failed to issue certificate. Unable to retrieve completion timestamp!', ClrAlertType.Danger, true, 3000, @@ -333,7 +353,7 @@ export class QuizComponent implements OnInit { description: `Congratulations! We hereby certify that the following user has successfully completed the course "${this.courseName ? this.courseName : this.scenarioName}" by fully attending all sessions and fulfilling the requirements, including the successful completion of the mandatory final test:`, personName: `${this.us.getEmail().value}`, title: `${this.courseName ? this.courseName : this.scenarioName}`, - fileName: `${this.scenarioName ? `certificate-${this.scenarioName}.pdf` : "certificate.pdf"}`, + fileName: `${this.scenarioName ? `certificate-${this.scenarioName}.pdf` : 'certificate.pdf'}`, issuer: `${this.quizIssuer}`, }); }