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..29156655 100644 --- a/src/app/quiz/quiz.component.html +++ b/src/app/quiz/quiz.component.html @@ -1,19 +1,99 @@ -
- @if (quizTitle) { {{ quizTitle }}
}
+
+
+
+ @if (quizTitle) {
+ {{ quizTitle }}
+ }
- @if(isPersistent && !loadingQuiz && !currentQuiz) {
-
-
-
- Failed to load quiz!
+ @if (isPersistent && !loadingQuiz && !currentQuiz) {
+
+
+ Failed to load quiz!
+
+
+ }
+
+ @if (isPersistent && loadingQuestions && started) {
+ Loading...
+ Loading Questions...
+ }
+ @if (
+ (!isPersistent || (isPersistent && started)) && questions.length > 0
+ ) {
+
+
+
+
+
+
+ @for (q of questions; track q.id || q.title; let i = $index) {
+
+ @if (q.type === 'checkbox') {
+
+ } @else {
+
+ }
+
+ }
+ }
+
+
+ @if ((!isPersistent || (isPersistent && started)) && questions.length > 0) {
+
+ }
+
+ @if (isPersistent && alreadyPassed) {
-
- You passed this quiz. Great job!
-
- @if(quizIssuer) {
+ You passed this quiz. Great job!
+ @if (quizIssuer) {
-
+
}
- } @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)) {
+ @if (isSubmitted && alreadyPassed === false) {
+
+
+
+ You did not reach the required score. 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 }}
+
}
}
-
+
+
+
+ Incomplete quiz
+
+ Please answer all questions before submitting your quiz.
+
+
+
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..a9cdbb0d 100644
--- a/src/app/quiz/quiz.component.ts
+++ b/src/app/quiz/quiz.component.ts
@@ -75,12 +75,15 @@ export class QuizComponent implements OnInit {
validationType: Validation = 'standard';
isSubmitted = false;
isPersistent = false;
- alreadyPassed = false;
+ alreadyPassed: boolean | null = null;
started = false;
loadingQuestions = true;
loadingQuiz = true;
currentQuiz?: Quiz;
quizIssuer = '';
+ currentIndex = 0;
+ lastPass: boolean | null = null;
+ showIncompleteModal = false;
correctsByQuestionId: Record = {};
@@ -97,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,
@@ -148,6 +152,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 {
@@ -207,17 +213,19 @@ export class QuizComponent implements OnInit {
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);
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;
},
});
});
@@ -260,6 +268,7 @@ export class QuizComponent implements OnInit {
this.isSubmitted = false;
this.loadingQuestions = false;
this.started = true;
+ this.currentIndex = 0;
});
}
@@ -274,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') {
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);
@@ -300,6 +325,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,6 +333,8 @@ export class QuizComponent implements OnInit {
this.rad?.forEach((c) => c.reset());
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));
}
@@ -329,4 +357,27 @@ export class QuizComponent implements OnInit {
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--;
+ }
+ }
}