src/app/tool-page/issue-container/issue-container.component.ts
Container for the display of a single Issue.
| selector | app-issue-container |
| styleUrls | issue-container.component.css |
| templateUrl | ./issue-container.component.html |
Properties |
|
Methods |
Inputs |
constructor(toolService: ToolService, courseService: CourseService)
|
||||||||||||
|
Constructor
Parameters :
|
index
|
The index of the item's issues array the issue is at.
Type: |
issue
|
The issue attached to the display.
Type: |
| buildEditorTabs |
buildEditorTabs()
|
|
Using the {@link Tab}s provided by the Node Tool, builds useable tab objects for each issue.
Returns :
any
The tabs to use to build the editor instance. |
| getButtonClasses | ||||||||||||
getButtonClasses(desiredStatus: string, elType: string)
|
||||||||||||
|
Determines the classes used to style buttons based on the current status of the issue they belong to.
Parameters :
Returns :
string
The classes to apply to the element. |
| isFixed |
isFixed()
|
|
Returns whether or not the item's status is fixed or failed.
Returns :
boolean
Whether or not this issue's status is fixed or failed. |
| ngOnInit |
ngOnInit()
|
|
Fired when the component is initialized, this manages the item's display. It inserts the form for the Issue's FixOptions if available.
Returns :
void
|
| onChange | ||||||
onChange(optionKey: )
|
||||||
|
Manages the validity of the form on each keystroke.
Parameters :
Returns :
void
|
| setIssueStatus | ||||||
setIssueStatus(newStatus: )
|
||||||
|
Sets the status of the issue.
Parameters :
Returns :
void
|
| showEditor |
showEditor()
|
|
Since Object.keys does not work in angular templating, this is a workaround. It checks if the issue has any HTML to display in the editor. Used by the app-code-editor tag to determine if it should display.
Returns :
boolean
|
| codeEditor |
codeEditor:
|
Type : ElementRef
|
Decorators : ViewChild
|
|
A reference to the code editor element for the respective issue. |
| Public courseService |
courseService:
|
Type : CourseService
|
|
Provides information and management for selected courses.
Being used in issue-container.component.html (i.e. DO NOT DELETE)
|
| issueDetails |
issueDetails:
|
Type : ElementRef
|
Decorators : ViewChild
|
|
Element reference to the card containing details about the issue. |
| Public toolService |
toolService:
|
Type : ToolService
|
|
Provides information and management for available tools.
|
import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core';
import { CourseService } from '../../course.service'; // Being used in issue-container.component.html (i.e. DO NOT DELETE)
import { ToolService } from '../../tool.service';
import { Issue } from '../../interfaces';
import { OptionModel } from '../../classes';
/**
* Container for the display of a single {@link Issue}.
*/
@Component({
selector: 'app-issue-container',
templateUrl: './issue-container.component.html',
styleUrls: ['./issue-container.component.css']
})
export class IssueContainerComponent implements OnInit {
/** The issue attached to the display. */
@Input('issue') issue: Issue;
/** The index of the item's issues array the issue is at. */
@Input('index') index: number;
/** Element reference to the card containing details about the issue. */
@ViewChild('issueDetails') issueDetails: ElementRef;
/** A reference to the code editor element for the respective issue. */
@ViewChild('codeEditor') codeEditor: ElementRef;
/**
* Constructor
* @param toolService Provides information and management for available tools.
* @param courseService Provides information and management for selected courses.
* Being used in issue-container.component.html (i.e. DO NOT DELETE)
*/
constructor(public toolService: ToolService,
public courseService: CourseService) { }
/**
* Fired when the component is initialized, this manages the item's display.
* It inserts the form for the {@link Issue}'s {@link FixOption}s if available.
*/
ngOnInit() {
this.issueDetails.nativeElement.innerHTML = this.issue.display;
this.issue.optionModel = new OptionModel(this.toolService.selectedTool.fixOptions);
this.issue.formGroup = this.issue.optionModel.toGroup();
// Update option values if there are values saved for any options
if (this.issue.tempValues) {
Object.keys(this.issue.tempValues).forEach(optionKey => {
const control = this.issue.formGroup.get(optionKey);
control.setValue(this.issue.tempValues[optionKey], { onlySelf: true });
control.updateValueAndValidity();
});
}
}
/**
* Using the {@link Tab}s provided by the Node Tool, builds
* useable tab objects for each issue.
* @returns {Object[]} The tabs to use to build the editor instance.
*/
buildEditorTabs() {
if (!this.toolService.selectedTool.editorTabs) { return; }
return this.toolService.selectedTool.editorTabs.map(editorTab => {
return {
title: editorTab.title,
htmlString: this.issue.html[editorTab.htmlKey],
readOnly: editorTab.readOnly
};
});
}
/**
* Sets the status of the issue.
* @param newStatus The new status to set the issue to.
*/
setIssueStatus(newStatus) {
if (this.issue.status === 'fixed') {
return;
} else if (newStatus === this.issue.status) {
this.issue.status = 'untouched';
} else {
this.issue.status = newStatus;
}
}
/**
* Determines the classes used to style buttons based on the
* current status of the issue they belong to.
* @param {string} desiredStatus What status the issue current is.
* @param {string} elType Either "button" or "icon".
* @returns {string} The classes to apply to the element.
*/
getButtonClasses(desiredStatus: string, elType: string) {
let classes = '';
if (elType === 'button') {
classes += 'waves-effect waves btn white';
}
if (this.issue.status !== 'untouched' && this.issue.status !== desiredStatus) {
classes += ' text-lighten-4';
} else if (desiredStatus === 'approved') {
classes += ' text-accent-4';
} else if (desiredStatus === 'skipped') {
classes += ' text-darken-2';
}
return classes;
}
/**
* Manages the validity of the form on each keystroke.
* @param optionKey The key of the option this came from.
*/
onChange(optionKey) {
if (['untouched', 'fixed', 'failed'].includes(this.issue.status)) {
this.issue.status = 'untouched';
}
if (!this.issue.tempValues) {
this.issue.tempValues = {};
}
this.issue.tempValues[optionKey] = this.issue.formGroup.value[optionKey];
}
/**
* Returns whether or not the item's status is fixed or failed.
* @returns {boolean} Whether or not this issue's status is fixed or failed.
*/
isFixed() {
return this.issue.status === 'fixed' || this.issue.status === 'failed';
}
/**
* Since Object.keys does not work in angular templating, this is
* a workaround. It checks if the issue has any HTML to display in
* the editor. Used by the app-code-editor tag to determine if it
* should display.
*/
showEditor() {
return Object.keys(this.issue.html).length > 0;
}
}
<div [ngClass]="courseService.getBackgroundColorClasses(issue.status)" class="issueContainer card">
<div class="card-content">
<h2 class="cobalt-text">{{issue.title}}</h2>
<div class="issueContainer__issueDetails" #issueDetails></div>
<form materialize [formGroup]="issue.formGroup" #optionsForm="ngForm">
<div *ngFor="let option of issue.optionModel.options" class="form-row">
<h3>{{option.title}}</h3>
<div class="option__description">{{option.description}}</div>
<div [ngSwitch]="option.type" class="option__container" [class.multiselect]="option.type === 'multiselect'" [class.required]="!issue.formGroup.controls[option.key].valid">
<!-- Silly, but [attr.disabled] was the only way I could get it to be disabled -->
<div *ngSwitchCase="'text'" class="option__text">
<input [attr.disabled]="isFixed() ? 'disabled' : null" type="{{option.type}}" id="{{option.key}}" [formControlName]="option.key"
(input)="onChange(option.key)" autocomplete="off" autofocus>
</div>
<div *ngSwitchCase="'dropdown'">
<select [attr.disabled]="isFixed() ? 'disabled' : null" style="display: block" [formControlName]="option.key" (input)="onChange(option.key)">
<option *ngFor="let choice of option.choices">{{choice}}</option>
</select>
</div>
<div *ngSwitchCase="'multiselect'">
<select [attr.disabled]="isFixed() ? 'disabled' : null" style="display: block" [formControlName]="option.key" (input)="onChange(option.key)"
multiple>
<option *ngFor="let choice of option.choices">{{choice}}</option>
</select>
</div>
</div>
</div>
</form>
</div>
<!-- Code Editor - Will only show up if the issue has contents in its "html" property -->
<div *ngIf="showEditor()">
<app-code-editor [issue]="issue" [tabs]="buildEditorTabs()" [highlight]="issue.html.highlight"
#codeEditor></app-code-editor>
</div>
<!-- Card Actions - this section contains the "approve" and "skip" buttons, as well as the fix message container -->
<div class="card-action" *ngIf="toolService.selectedTool.toolType === 'fix'">
<button *ngIf="issue.status !== 'fixed' && issue.status !== 'failed'" (click)="setIssueStatus('approved')" [ngClass]="getButtonClasses('approved', 'button')"
class="green-text" [disabled]="issue.formGroup && !issue.formGroup.valid">
<i *ngIf="!issue.formGroup || issue.formGroup.valid" [ngClass]="getButtonClasses('approved', 'icon')" class="green-text material-icons left">
{{issue.status !== 'approved' ? 'check' : 'check_circle_outline'}}
</i>
<i *ngIf="issue.formGroup && !issue.formGroup.valid" class="material-icons left">
block
</i>
{{issue.status !== 'approved' ? (issue.status !== 'fixed' ? 'approve' : 'fixed') : 'approved'}}
</button>
<button *ngIf="issue.status !== 'fixed' && issue.status !== 'failed'" (click)="setIssueStatus('skipped')" [ngClass]="getButtonClasses('skipped', 'button')"
class="blue-grey-text">
<i [ngClass]="getButtonClasses('skipped', 'icon')" class="blue-grey-text material-icons left">
{{issue.status !== 'skipped' ? 'remove' : 'call_missed_outgoing'}}
</i>
{{issue.status !== 'skipped' ? 'skip' : 'skipped'}}
</button>
<div *ngIf="issue.status === 'fixed'" class="blue-text text-accent-3">
FIXED {{toolService.selectedTool.fixedMessage ? ' - ' + toolService.selectedTool.fixedMessage : ''}}
</div>
<div *ngIf="issue.status === 'failed'" class="red-text text-accent-4">
FAILED - Please contact a Katana developer with the course ID, item name, and issue title.
</div>
</div>
</div>