Commit ac8e062d authored by Simone Vuotto's avatar Simone Vuotto

Update requirements list tab

parent 88d3010b
This diff is collapsed.
......@@ -22,6 +22,8 @@
"@angular/platform-browser-dynamic": "^5.0.0",
"@angular/router": "^5.0.0",
"@auth0/angular-jwt": "^1.1.0",
"@ng-bootstrap/ng-bootstrap": "^2.0.0",
"@swimlane/ngx-datatable": "^13.0.0",
"bootstrap": "^3.3.7",
"bootswatch": "^4.0.0-beta.2",
"core-js": "^2.4.1",
......@@ -47,7 +49,7 @@
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"protractor": "^5.3.2",
"ts-node": "~3.2.0",
"tslint": "~5.7.0",
"typescript": "~2.4.2"
......
......@@ -4,6 +4,8 @@ import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
// Components
import { AppComponent } from './app.component';
......@@ -48,6 +50,8 @@ import { JwtInterceptor } from './jwt.interceptor.js';
ReactiveFormsModule,
FormsModule,
HttpClientModule,
NgxDatatableModule,
NgbModule.forRoot(),
],
providers: [
AuthenticationService,
......
......@@ -11,6 +11,7 @@ export class Requirement {
project: number;
errorDescription: string;
state: ReqState;
disabled: boolean;
clone(req: Requirement): Requirement {
this.id = req.id;
......@@ -18,6 +19,7 @@ export class Requirement {
this.project = req.project;
this.errorDescription = req.errorDescription;
this.state = req.state;
this.disabled = req.disabled;
return this;
}
......
.tab-header {
margin-top: 1em;
margin-bottom: 1em;
margin: 1em 0;
display: flex;
height: 2.5em;
width: 100%;
}
.btn {
......@@ -11,3 +13,15 @@
display: none !important;
visibility: hidden !important;
}
.search-field {
width:100%;
margin: 0;
}
.tab-progress {
margin-top: 1em;
}
<div class="tab-header clearfix">
<div class="hidden"><input type="file" #fileInput (change)="fileChange($event)"/></div>
<button type="button" class="btn btn-primary pull-right" (click)="fileInput.click()"
[disabled]="uploadLoading">
<i class="fa fa-upload" aria-hidden="true" *ngIf="!uploadLoading"></i>
<i class='fa fa-spinner fa-spin' *ngIf="uploadLoading"></i>
&nbsp;Upload File
</button>
<div class="tab-progress" *ngIf="uploadLoading">
<ngb-progressbar type="primary" [value]="100" [striped]="true" [animated]="true">
<span>{{ progressMessage }}</span>
</ngb-progressbar>
</div>
<table class="table table-hover">
<thead>
<tr>
<th class="col-md-1">Id</th>
<th class="col-md-9">Requirement</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let req of requirements"
[ngClass]="{'table-success': req.compliant,
'table-primary': req.error,
'table-dange': req.warning,
'table-light': req.notChecked}"
(click)="requirementDetails(req)">
<td scope="row">{{ req.id }}</td>
<td>{{ req.text }}</td>
</tr>
</tbody>
</table>
<div class="row tab-header">
<div class="col-10 pl-0">
<div class="hidden"><input type="file" #fileInput (change)="fileChange($event)"/></div>
<div class="btn-group" role="group" aria-label="Basic example">
<button type="button" class="btn btn-secondary" (click)="disableSelected(false)"><i class="fa fa-check"></i>&nbsp;Enable</button>
<button type="button" class="btn btn-secondary" (click)="disableSelected(true)"><i class="fa fa-minus"></i>&nbsp;Disable</button>
<button type="button" class="btn btn-secondary" (click)="deleteSelected()"><i class="fa fa-trash"></i>&nbsp;Delete</button>
</div>
<button type="button" class="btn btn-outline-primary" (click)="fileInput.click()"
[disabled]="uploadLoading">
<i class="fa fa-plus" aria-hidden="true" *ngIf="!uploadLoading"></i>
<i class='fa fa-spinner fa-spin' *ngIf="uploadLoading"></i>
&nbsp;Add Requirement
</button>
<button type="button" class="btn btn-outline-primary" (click)="fileInput.click()"
[disabled]="uploadLoading">
<i class="fa fa-upload" aria-hidden="true" *ngIf="!uploadLoading"></i>
<i class='fa fa-spinner fa-spin' *ngIf="uploadLoading"></i>
&nbsp;Upload File
</button>
</div>
<div class="col-2 pr-0">
<input
type='text'
class="form-control search-field pull-right"
placeholder='Search...'
(keyup)='updateFilter($event)'
[(ngModel)]="searchValue"
/>
</div>
</div>
<ngx-datatable #table
class="material"
[columnMode]="'standard'"
[headerHeight]="50"
[footerHeight]="50"
[rowHeight]="'auto'"
[limit]="10"
[selected]="selected"
[selectionType]="'checkbox'"
[rows]='rows'
[rowClass]="getRowClass"
[reorderable]="true"
(activate)="requirementDetails($event)">
<ngx-datatable-column
[width]="30"
[sortable]="false"
[canAutoResize]="false"
[draggable]="false"
[resizeable]="false"
[headerCheckboxable]="false"
[checkboxable]="true">
</ngx-datatable-column>
<ngx-datatable-column name="Id" [width]="50"></ngx-datatable-column>
<ngx-datatable-column name="Requirement" prop="text" [width]="1000"></ngx-datatable-column>
</ngx-datatable>
<app-requirement-details [req]="selectedRequirement" (update)="onChange($event)"></app-requirement-details>
import { Component, Input, OnInit } from '@angular/core';
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { Requirement } from '../../models/requirement';
import { ActivatedRoute } from '@angular/router';
import { RequirementService } from '../../services/requirement.service';
import { AlertService } from '../../alert/alert.service';
import { DatatableComponent } from '@swimlane/ngx-datatable';
@Component({
selector: 'app-requirements-tab',
templateUrl: './requirements-tab.component.html',
styleUrls: ['./requirements-tab.component.css']
styleUrls: ['./requirements-tab.component.css' ]
})
export class RequirementsTabComponent implements OnInit {
......@@ -17,6 +17,14 @@ export class RequirementsTabComponent implements OnInit {
selectedRequirement = new Requirement();
uploadLoading = false;
progressMessage = 'Loading...';
searchValue = '';
rows = [];
selected = [];
@ViewChild(DatatableComponent) table: DatatableComponent;
constructor(private requirementService: RequirementService,
private alertService: AlertService) { }
......@@ -25,8 +33,10 @@ export class RequirementsTabComponent implements OnInit {
this.getRequirements();
}
requirementDetails(req: Requirement) {
this.selectedRequirement = new Requirement().clone(req);
requirementDetails(event) {
if (event.type === 'dblclick') {
this.selectedRequirement = new Requirement().clone(event.row);
}
}
onChange(req: Requirement) {
......@@ -34,11 +44,16 @@ export class RequirementsTabComponent implements OnInit {
if (index >= 0) {
this.requirements[index] = req;
}
this.updateFilter(null);
}
getRequirements() {
this.requirementService.getRequirements(this.projectId).subscribe(
requirements => this.requirements = requirements.map(req => new Requirement().clone(req)));
requirements => {
this.requirements = requirements.map(req => new Requirement().clone(req));
this.rows = this.requirements;
});
}
fileChange(event) {
......@@ -62,4 +77,57 @@ export class RequirementsTabComponent implements OnInit {
}
}
getRowClass(req) {
return {
'row-green': req.compliant,
'row-red': req.error,
'row-yellow': req.waning,
'row-disabled': req.disabled
};
}
disableSelected(disabled: boolean) {
this.uploadLoading = true;
const reqs = this.selected;
let loaded = 0;
reqs.forEach(req => {
req.disabled = disabled;
this.requirementService.updateRequirement(req).subscribe(
data => {
loaded++;
if (loaded === reqs.length) {
this.uploadLoading = false;
this.onChange(new Requirement().clone(data));
}
},
error => { this.alertService.error(error); });
});
}
deleteSelected() {
}
updateFilter(event) {
const val = this.searchValue.toLowerCase();
// filter our data
const temp = this.requirements.filter(function(req) {
return req.text.toLowerCase().indexOf(val) !== -1 || !val;
});
// update the rows
this.rows = temp;
if (event != null) {
// Whenever the filter changes, always go back to the first page
this.table.offset = 0;
}
// Remove previously selected items
this.selected = [];
}
}
@import '~@swimlane/ngx-datatable/release/index.css';
@import '~@swimlane/ngx-datatable/release/themes/material.css';
@import '~@swimlane/ngx-datatable/release/assets/icons.css';
/* You can add global styles to this file, and also import other style files */
.modal-backdrop {
opacity: .5;
}
.row-green {
background-color: #77B300;
}
.row-red {
background-color: #CC0000;
}
.row-yellow {
background-color: #FF8800;
}
.row-disabled {
background-color: #555;
}
.ngx-datatable.material .datatable-body .datatable-body-row .datatable-body-cell {
color: #ffffff;
}
.ngx-datatable.material .datatable-body .datatable-body-row :hover .datatable-body-cell :hover{
color: #373a3c;
}
ngb-progressbar .progress-bar {
color: white;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment