Skip to content
Merged
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "planet",
"license": "AGPL-3.0",
"version": "0.22.89",
"version": "0.22.90",
"myplanet": {
"latest": "v0.55.91",
"min": "v0.53.91"
Expand Down
2 changes: 1 addition & 1 deletion src/app/exams/exams-add.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<button mat-menu-item type="button" (click)="addQuestion('textarea')" i18n>Text - Long answer</button>
<button mat-menu-item type="button" (click)="addQuestion('select')" i18n>Multiple Choice - single answer</button>
<button mat-menu-item type="button" (click)="addQuestion('selectMultiple')" i18n>Multiple Choice - multiple answer</button>
<button *ngIf="examType !== 'exam'" mat-menu-item type="button" (click)="addQuestion('ratingScale')" i18n>Rating Scale - 1 to 9</button>
<button *ngIf="examType !== 'exam'" mat-menu-item type="button" (click)="addQuestion('ratingScale')" i18n>Rating Scale</button>
</mat-menu>
<mat-accordion class="exam-inputs" *ngIf="!isCourseContent">
<mat-expansion-panel [expanded]="!isQuestionsActive">
Expand Down
8 changes: 7 additions & 1 deletion src/app/exams/exams-question.component.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
<form class="vertical-form" [formGroup]="questionForm">
<div class="type-title-container">
<div class="scale-max-field" *ngIf="questionForm.controls.type.value === 'ratingScale'">
<span class="scale-max-label" i18n>Scale</span>
<mat-button-toggle-group formControlName="scaleMax">
<mat-button-toggle *ngFor="let n of scaleMaxOptions" [value]="n">{{n}}</mat-button-toggle>
</mat-button-toggle-group>
</div>
<mat-form-field>
<mat-label i18n>Type</mat-label>
<mat-select formControlName="type" (selectionChange)="clearChoices()" panelClass="exam-question-select">
<mat-option value="input" i18n>Text - Short answer</mat-option>
<mat-option value="textarea" i18n>Text - Long answer</mat-option>
<mat-option value="select" i18n>Multiple Choice - single answer</mat-option>
<mat-option value="selectMultiple" i18n>Multiple Choice - multiple answer</mat-option>
<mat-option *ngIf="examType !== 'exam'" value="ratingScale" i18n>Rating Scale - 1 to 9</mat-option>
<mat-option *ngIf="examType !== 'exam'" value="ratingScale" i18n>Rating Scale</mat-option>
</mat-select>
</mat-form-field>
</div>
Expand Down
4 changes: 3 additions & 1 deletion src/app/exams/exams-question.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { NgIf, NgFor } from '@angular/common';
import { PlanetMarkdownTextboxComponent } from '../shared/forms/planet-markdown-textbox.component';
import { FormErrorMessagesComponent } from '../shared/forms/form-error-messages.component';
import { MatButton, MatIconButton } from '@angular/material/button';
import { MatButtonToggle, MatButtonToggleGroup } from '@angular/material/button-toggle';
import { MatCheckbox } from '@angular/material/checkbox';
import { MatInput } from '@angular/material/input';
import { MatIcon } from '@angular/material/icon';
Expand All @@ -27,7 +28,7 @@ import { MatIcon } from '@angular/material/icon';
imports: [
FormsModule, ReactiveFormsModule, MatFormField, MatLabel, MatSelect, MatOption, NgIf,
PlanetMarkdownTextboxComponent, MatError, FormErrorMessagesComponent, MatButton, NgFor, MatCheckbox,
MatInput, MatIconButton, MatSuffix, MatIcon
MatInput, MatIconButton, MatSuffix, MatIcon, MatButtonToggle, MatButtonToggleGroup
]
})
export class ExamsQuestionComponent implements OnInit, OnChanges, OnDestroy, AfterViewChecked {
Expand All @@ -38,6 +39,7 @@ export class ExamsQuestionComponent implements OnInit, OnChanges, OnDestroy, Aft
@Output() questionRemove = new EventEmitter<any>();
@ViewChildren('choiceInput') choiceInputs: QueryList<ElementRef>;
correctCheckboxes: any = {};
scaleMaxOptions = [ 5, 6, 7, 8, 9 ];
questionForm: QuestionFormGroup = this.examsService.newQuestionForm(this.examType === 'courses');
initializing = true;
choiceAdded = false;
Expand Down
18 changes: 16 additions & 2 deletions src/app/exams/exams-question.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
form {
display: grid;
grid-template-rows: 56px auto 56px;
grid-row-gap: 1rem;
}

.question-choices {
Expand All @@ -20,8 +21,21 @@

.type-title-container {
display: grid;
grid-template-columns: 1fr v.$form-width-1;
grid-column-gap: 0.5rem;
grid-template-columns: 2fr 8fr;
grid-column-gap: 1rem;
align-items: center;
}

.scale-max-field {
display: flex;
align-items: center;
gap: 0.75rem;
flex-wrap: wrap;
}

.scale-max-label {
font-size: 14px;
color: rgba(0, 0, 0, 0.6);
}

}
172 changes: 100 additions & 72 deletions src/app/exams/exams-view.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,86 +32,114 @@
<button mat-icon-button [disabled]="isLoading || questionNum === 1" (click)="moveQuestion(-1)"><mat-icon>navigate_before</mat-icon></button>
<button mat-icon-button [disabled]="isLoading || questionNum === maxQuestions" (click)="nextQuestion({ nextClicked: true })"><mat-icon>navigate_next</mat-icon></button>
</mat-toolbar>
<div class="progress-bar-track" *ngIf="!isLoading && maxQuestions > 0">
<div class="progress-bar-fill" [style.width.%]="progressPercent"></div>
</div>
<div class="view-container" [ngClass]="{ 'view-full-height': !isDialog }">
<ng-container *ngIf="!isLoading; else LoadingContent">
<td-markdown [content]="question?.body"></td-markdown>
<div [ngSwitch]="mode">
<ng-container *ngSwitchCase="'take'">
<ng-container [ngSwitch]="question?.type">
<mat-form-field class="full-width" *ngSwitchCase="'input'">
<mat-label i18n>Enter answer here</mat-label>
<input matInput [formControl]="answer">
</mat-form-field>
<mat-form-field *ngSwitchCase="'textarea'" class="full-width mat-form-field-type-no-underline">
<planet-markdown-textbox [formControl]="answer"></planet-markdown-textbox>
</mat-form-field>
<mat-radio-group *ngSwitchCase="'select'" class="question-list" [formControl]="answer">
<mat-radio-button [value]="option" *ngFor="let option of question?.choices">
<span class="multiple-choice-text">{{option.text}}</span>
</mat-radio-button>
<ng-container *ngIf="question?.hasOtherOption">
<div class="other-option-flex">
<mat-radio-button [value]="currentOtherOption">
<span class="multiple-choice-text" i18n>Other: </span>
<div [ngClass]="slideDirection === 'right'
? (slideAnimationVariant === 'a' ? 'slide-in-right-a' : 'slide-in-right-b')
: (slideAnimationVariant === 'a' ? 'slide-in-left-a' : 'slide-in-left-b')">
<td-markdown [content]="question?.body"></td-markdown>
<div [ngSwitch]="mode">
<ng-container *ngSwitchCase="'take'">
<ng-container [ngSwitch]="question?.type">
<mat-form-field class="full-width" *ngSwitchCase="'input'">
<mat-label i18n>Enter answer here</mat-label>
<input matInput [formControl]="answer">
</mat-form-field>
<mat-form-field *ngSwitchCase="'textarea'" class="full-width mat-form-field-type-no-underline">
<planet-markdown-textbox [formControl]="answer"></planet-markdown-textbox>
</mat-form-field>
<mat-radio-group *ngSwitchCase="'select'" class="question-list" [formControl]="answer">
<div class="option-card" *ngFor="let option of question?.choices" [class.selected]="isSelectOptionSelected(option)" (click)="selectOption(option)">
<mat-radio-button [value]="option">
<span class="multiple-choice-text">{{option.text}}</span>
</mat-radio-button>
<input matInput
class="other-option-input"
[disabled]="!isOtherSelected()"
[(ngModel)]="currentOtherOption.text"
(ngModelChange)="updateOtherText()">
<mat-icon class="option-check" *ngIf="isSelectOptionSelected(option)">check</mat-icon>
</div>
</ng-container>
</mat-radio-group>
<div *ngSwitchCase="'selectMultiple'" class="question-list">
<span class="mat-caption" i18n>
{examType, select, survey {You can choose one or more answers.} exam {There are one or more correct answers. Please choose all correct answers.}}
</span>
<mat-checkbox *ngFor="let option of question?.choices" [value]="option" (change)="setAnswer($event, option)" [checked]="checkboxState[option.id]">
<span class="multiple-choice-text">{{option.text}}</span>
</mat-checkbox>
<ng-container *ngIf="question?.hasOtherOption">
<div class="other-option-flex">
<mat-checkbox [checked]="checkboxState['other']" (change)="toggleOtherMultiple($event)">
<span class="multiple-choice-text" i18n>Other: </span>
<ng-container *ngIf="question?.hasOtherOption">
<div class="option-card" [class.selected]="isOtherSelected()" (click)="selectOtherRadio()">
<mat-radio-button [value]="currentOtherOption">
<span class="multiple-choice-text" i18n>Other: </span>
</mat-radio-button>
<div class="other-option-flex">
<input matInput
#singleOtherInput
class="other-option-input"
[(ngModel)]="currentOtherOption.text"
(ngModelChange)="updateOtherText()"
(focus)="selectOtherRadio()"
(click)="$event.stopPropagation()">
</div>
<mat-icon class="option-check" *ngIf="isOtherSelected()">check</mat-icon>
</div>
</ng-container>
</mat-radio-group>
<div *ngSwitchCase="'selectMultiple'" class="question-list">
<span i18n>
{examType, select, survey {You can choose one or more answers.} exam {There are one or more correct answers. Please choose all correct answers.}}
</span>
<div class="option-card" *ngFor="let option of question?.choices" [class.selected]="checkboxState[option.id]" (click)="toggleMultipleOption(option)">
<mat-checkbox [value]="option" (change)="setAnswer($event, option)" [checked]="checkboxState[option.id]" (click)="$event.stopPropagation()">
<span class="multiple-choice-text">{{option.text}}</span>
</mat-checkbox>
<input matInput class="other-option-input" [disabled]="!checkboxState['other']" [(ngModel)]="currentOtherOption.text" (ngModelChange)="updateOtherText()">
<mat-icon class="option-check" *ngIf="checkboxState[option.id]">check</mat-icon>
</div>
</ng-container>
</div>
<div *ngSwitchCase="'ratingScale'" class="rating-scale-keypad">
<div class="rating-scale-grid">
<button type="button" mat-raised-button class="rating-scale-button" *ngFor="let num of [1,2,3,4,5,6,7,8,9]" [class.selected]="answer.value === num.toString()" [attr.data-rating]="num" (click)="setRatingScaleAnswer(num)">
{{num}}
</button>
<ng-container *ngIf="question?.hasOtherOption">
<div class="option-card" [class.selected]="checkboxState['other']" (click)="toggleOtherCheckbox()">
<mat-checkbox [checked]="checkboxState['other']" (change)="toggleOtherMultiple($event)" (click)="$event.stopPropagation()">
<span class="multiple-choice-text" i18n>Other: </span>
</mat-checkbox>
<div class="other-option-flex">
<input matInput #multipleOtherInput class="other-option-input" [(ngModel)]="currentOtherOption.text" (ngModelChange)="updateOtherText()" (focus)="ensureOtherCheckboxSelected()" (click)="$event.stopPropagation()">
</div>
<mat-icon class="option-check" *ngIf="checkboxState['other']">check</mat-icon>
</div>
</ng-container>
</div>
</div>
<ng-container *ngSwitchCase="'ratingScale'">
<div class="rating-scale-keypad rating-scale-keypad--grid" *ngIf="(question?.scaleMax ?? 9) === 9; else ratingScaleRow">
<button type="button" mat-raised-button class="rating-scale-button rating-scale-button--phone" *ngFor="let num of ratingScaleNumbers" [class.selected]="answer.value === num.toString()" [attr.data-rating]="num" (click)="setRatingScaleAnswer(num)">
{{num}}
</button>
</div>
<ng-template #ratingScaleRow>
<div class="rating-scale-keypad rating-scale-keypad--row">
<button type="button" class="rating-scale-button rating-scale-button--line" *ngFor="let num of ratingScaleNumbers" [class.selected]="answer.value === num.toString()" (click)="setRatingScaleAnswer(num)">
{{num}}
</button>
</div>
</ng-template>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
<ng-container *ngSwitchCase="'grade'">
<p><b i18n>Submitted answer:</b></p>
<td-markdown [content]="answer?.value?.text || answer?.value"></td-markdown>
<mat-radio-group [(ngModel)]="grade" [disabled]="question?.type === 'select' || question?.type === 'selectMultiple'">
<mat-radio-button [value]="1" class="planet-radio-button" i18n>Correct</mat-radio-button>
<mat-radio-button [value]="0" class="planet-radio-button" i18n>Incorrect</mat-radio-button>
</mat-radio-group>
<mat-form-field class="full-width mat-form-field-type-no-underline">
<mat-label i18n>Comment</mat-label>
<planet-markdown-textbox class="full-width" [(ngModel)]="comment"></planet-markdown-textbox>
</mat-form-field>
</ng-container>
<ng-container *ngSwitchCase="'view'">
<p><b i18n>Response:</b></p>
<td-markdown [content]="answer?.value?.text || answer?.value"></td-markdown>
<ng-container *ngIf="grade>=0">
<p><b i18n>Grade:</b></p>
<p *ngIf="grade===1" i18n>Correct</p>
<p *ngIf="grade===0" i18n>Incorrect</p>
<ng-container *ngSwitchCase="'grade'">
<p><b i18n>Submitted answer:</b></p>
<td-markdown [content]="answer?.value?.text || answer?.value"></td-markdown>
<mat-radio-group [(ngModel)]="grade" [disabled]="question?.type === 'select' || question?.type === 'selectMultiple'">
<mat-radio-button [value]="1" class="planet-radio-button" i18n>Correct</mat-radio-button>
<mat-radio-button [value]="0" class="planet-radio-button" i18n>Incorrect</mat-radio-button>
</mat-radio-group>
<mat-form-field class="full-width mat-form-field-type-no-underline">
<mat-label i18n>Comment</mat-label>
<planet-markdown-textbox class="full-width" [(ngModel)]="comment"></planet-markdown-textbox>
</mat-form-field>
</ng-container>
<ng-container *ngIf="comment">
<p><b i18n>Feedback:</b></p>
<td-markdown [content]="comment"></td-markdown>
<ng-container *ngSwitchCase="'view'">
<p><b i18n>Response:</b></p>
<td-markdown [content]="answer?.value?.text || answer?.value"></td-markdown>
<ng-container *ngIf="grade>=0">
<p><b i18n>Grade:</b></p>
<p *ngIf="grade===1" i18n>Correct</p>
<p *ngIf="grade===0" i18n>Incorrect</p>
</ng-container>
<ng-container *ngIf="comment">
<p><b i18n>Feedback:</b></p>
<td-markdown [content]="comment"></td-markdown>
</ng-container>
</ng-container>
</ng-container>
</div>
</div>
<div class="v-align-center action-buttons">
<button
Expand All @@ -124,8 +152,8 @@
</button>
<button
*ngIf="mode === 'take'"
mat-raised-button
color="accent"
mat-stroked-button
color="primary"
(click)="nextQuestion({ isFinish: true })"
[disabled]="!isComplete || !answer.valid || grade === undefined || grade === null"
i18n>
Expand Down
Loading
Loading