Merge master into feature/dashboard
This commit is contained in:
178 files changed
+16540
-322
No files matched your search
@@ -1,20 +1,21 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
import { TaskListComponent } from './task-list/task-list.component';
|
||||
import { UserstoryListComponent } from './userstory-list/userstory-list.component';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { UserstoryTableComponent } from './userstory-table/userstory-table.component';
|
||||
import { TaskTableComponent } from './task-table/task-table.component';
|
||||
import { SprintTableComponent } from './sprint-table/sprint-table.component';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'tasks', component: TaskListComponent },
|
||||
{ path: 'userstories', component: UserstoryListComponent },
|
||||
{ path: 'tasks', component: TaskTableComponent },
|
||||
{ path: 'userstories', component: UserstoryTableComponent },
|
||||
{ path: 'dashboard', component: DashboardComponent },
|
||||
{ path: 'sprints', component: SprintTableComponent },
|
||||
{ path: '', redirectTo: '/tasks', pathMatch: 'full' },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
imports: [ RouterModule.forRoot(routes) ],
|
||||
exports: [ RouterModule ]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
export class AppRoutingModule {}
|
||||
@@ -1 +1,39 @@
|
||||
<router-outlet></router-outlet>
|
||||
<router-outlet></router-outlet>
|
||||
<div class=" fixed-plugin">
|
||||
<div class=" show-dropdown" ngbDropdown>
|
||||
<a data-toggle="dropdown" ngbDropdownToggle>
|
||||
<i class="fa fa-cog fa-2x"></i>
|
||||
</a>
|
||||
<ul ngbDropdownMenu>
|
||||
<!--
|
||||
<li class=" header-title">Sidebar Background</li>
|
||||
<li class=" adjustments-line">
|
||||
<a class=" switch-trigger background-color" href="javascript:void(0)">
|
||||
<div class=" badge-colors text-center">
|
||||
<span
|
||||
class=" badge filter badge-danger"
|
||||
[ngClass]="{'active':sidebarColor==='red'}" (click)="changeSidebarColor('red')"
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class=" badge filter badge-primary"
|
||||
[ngClass]="{'active':sidebarColor==='primary'}" (click)="changeSidebarColor('primary')"
|
||||
>
|
||||
</span>
|
||||
<span class=" badge filter badge-info" [ngClass]="{'active':sidebarColor==='blue'}" (click)="changeSidebarColor('blue')"> </span>
|
||||
<span class=" badge filter badge-success" [ngClass]="{'active':sidebarColor==='green'}" (click)="changeSidebarColor('green')">
|
||||
</span>
|
||||
</div>
|
||||
<div class=" clearfix"></div>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
<li class=" adjustments-line text-center color-change">
|
||||
<span class=" color-label"> LIGHT MODE </span>
|
||||
<span class=" badge light-badge mr-2" (click)="changeDashboardColor('white-content')"> </span>
|
||||
<span class=" badge dark-badge ml-2" (click)="changeDashboardColor('black-content')"> </span>
|
||||
<span class=" color-label"> DARK MODE </span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -5,4 +5,42 @@ import { Component } from '@angular/core';
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {}
|
||||
export class AppComponent {
|
||||
/*
|
||||
changeSidebarColor(color){
|
||||
var sidebar = document.getElementsByClassName('sidebar')[0];
|
||||
var mainPanel = document.getElementsByClassName('main-panel')[0];
|
||||
|
||||
this.sidebarColor = color;
|
||||
|
||||
if(sidebar != undefined){
|
||||
sidebar.setAttribute('data',color);
|
||||
}
|
||||
if(mainPanel != undefined){
|
||||
mainPanel.setAttribute('data',color);
|
||||
}
|
||||
}
|
||||
*/
|
||||
changeDashboardColor(color){
|
||||
var body = document.getElementsByTagName('body')[0];
|
||||
if (body && color === 'white-content') {
|
||||
body.classList.add(color);
|
||||
}
|
||||
else if(body.classList.contains('white-content')) {
|
||||
body.classList.remove('white-content');
|
||||
}
|
||||
}
|
||||
// function that adds color white/transparent to the navbar on resize (this is for the collapse)
|
||||
/*
|
||||
updateColor = () => {
|
||||
var navbar = document.getElementsByClassName('navbar')[0];
|
||||
if (window.innerWidth < 993 && !this.isCollapsed) {
|
||||
navbar.classList.add('bg-white');
|
||||
navbar.classList.remove('navbar-transparent');
|
||||
} else {
|
||||
navbar.classList.remove('bg-white');
|
||||
navbar.classList.add('navbar-transparent');
|
||||
}
|
||||
};
|
||||
*/
|
||||
}
|
||||
+11
-6
@@ -7,21 +7,26 @@ import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
import { BackendService } from './services/backend.service';
|
||||
import { TaskListComponent } from './task-list/task-list.component';
|
||||
import { TaskFormComponent } from './task-form/task-form.component';
|
||||
import { UserstoryListComponent } from './userstory-list/userstory-list.component';
|
||||
import { UserstoryFormComponent } from './userstory-form/userstory-form.component';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SprintFormComponent } from './sprint-form/sprint-form.component';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { UserstoryTableComponent } from './userstory-table/userstory-table.component';
|
||||
import { TaskTableComponent } from './task-table/task-table.component';
|
||||
import { SprintTableComponent } from './sprint-table/sprint-table.component';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
TaskListComponent,
|
||||
TaskTableComponent,
|
||||
TaskFormComponent,
|
||||
UserstoryListComponent,
|
||||
UserstoryTableComponent,
|
||||
UserstoryFormComponent,
|
||||
DashboardComponent
|
||||
UserstoryTableComponent,
|
||||
SprintFormComponent,
|
||||
SprintTableComponent,
|
||||
DashboardComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@@ -11,8 +11,8 @@ export class DashboardComponent implements OnInit {
|
||||
/**
|
||||
* Returns the status that are used by at least one userstory.
|
||||
*/
|
||||
private get usedStatus(): ScrumStatus[] {
|
||||
return this.status.filter(s => this.userstories.find(us => us.status === s.id) !== undefined);
|
||||
public get usedStatus(): ScrumStatus[] {
|
||||
return this.status.filter(s => this.userstories.find(us => us.statusid === s.id) !== undefined);
|
||||
}
|
||||
|
||||
private status: ScrumStatus[];
|
||||
@@ -47,11 +47,11 @@ export class DashboardComponent implements OnInit {
|
||||
{id: 1, title: "Done", description:""},
|
||||
];
|
||||
this.userstories = [
|
||||
{status: 0, title:""},
|
||||
{status: 0, title:""},
|
||||
{status: 0, title:""},
|
||||
{status: 1, title:""},
|
||||
{status: 1, title:""},
|
||||
{statusid: 0, title:""},
|
||||
{statusid: 0, title:""},
|
||||
{statusid: 0, title:""},
|
||||
{statusid: 1, title:""},
|
||||
{statusid: 1, title:""},
|
||||
];
|
||||
this.sprints = [
|
||||
{description:"", title:"", project: 0, startDate: new Date(2020, 5, 22), endDate: new Date(2020, 5, 28)},
|
||||
@@ -101,7 +101,7 @@ export class DashboardComponent implements OnInit {
|
||||
}
|
||||
|
||||
public getNumberOfUserstoriesByStatus(status: ScrumStatus): number {
|
||||
return this.userstories.filter(us => us.status === status.id).length;
|
||||
return this.userstories.filter(us => us.statusid === status.id).length;
|
||||
}
|
||||
|
||||
public getRemainingDaysInSprint(): number {
|
||||
|
||||
@@ -9,7 +9,7 @@ export class BackendService {
|
||||
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
|
||||
|
||||
|
||||
// Tasks
|
||||
public getTasks(): Observable<HttpResponse<ScrumTask[]>> {
|
||||
const url = `${environment.apiUrl}/tasks`;
|
||||
@@ -117,7 +117,7 @@ export class BackendService {
|
||||
return this.httpClient.delete(url, { observe: 'response' });
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Status
|
||||
public getAllStatus(): Observable<HttpResponse<ScrumStatus[]>> {
|
||||
const url = `${environment.apiUrl}/status`;
|
||||
@@ -143,8 +143,8 @@ export class BackendService {
|
||||
const url = `${environment.apiUrl}/status/${status.id}`;
|
||||
return this.httpClient.delete(url, { observe: 'response' });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Users
|
||||
public getUsers(): Observable<HttpResponse<ScrumUser[]>> {
|
||||
const url = `${environment.apiUrl}/users`;
|
||||
@@ -171,7 +171,7 @@ export class BackendService {
|
||||
return this.httpClient.delete(url, { observe: 'response' });
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Projects
|
||||
public getProjects(): Observable<HttpResponse<ScrumProject[]>> {
|
||||
const url = `${environment.apiUrl}/projects`;
|
||||
@@ -209,12 +209,12 @@ export interface ScrumTask {
|
||||
id?: number;
|
||||
title: string;
|
||||
content?: string;
|
||||
status?: number;
|
||||
category?: number;
|
||||
assignedto?: number;
|
||||
sprint?: number;
|
||||
project?: number;
|
||||
userstory?: number;
|
||||
statusid?: number;
|
||||
categoryid?: number;
|
||||
assignedtoid?: number;
|
||||
sprintid?: number;
|
||||
projectid?: number;
|
||||
userstoryid?: number;
|
||||
priority?: Priority;
|
||||
}
|
||||
|
||||
@@ -223,16 +223,16 @@ export interface ScrumUserstory {
|
||||
title: string;
|
||||
content?: string;
|
||||
priority?: Priority;
|
||||
status?: number;
|
||||
category?: number;
|
||||
createdby?: number;
|
||||
project?: number;
|
||||
statusid?: number;
|
||||
categoryid?: number;
|
||||
createdbyid?: number;
|
||||
projectid?: number;
|
||||
}
|
||||
|
||||
export interface ScrumSprint{
|
||||
id?: number;
|
||||
title: string;
|
||||
description: string;
|
||||
description?: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
project: number;
|
||||
@@ -262,4 +262,4 @@ export interface ScrumProject {
|
||||
id?: number;
|
||||
title: string;
|
||||
isprivate: boolean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import {Priority} from './backend.service';
|
||||
|
||||
export function sortByNumberAscending<T>(items: T[], key: (T) => number) {
|
||||
return items.sort((a, b) => key(a) - key(b));
|
||||
}
|
||||
|
||||
export function sortByStringAscending<T>(items: T[], key: (T) => string) {
|
||||
return items.sort((a, b) => key(a).localeCompare(key(b)));
|
||||
}
|
||||
|
||||
export function sortByDateAscending<T>(items: T[], key: (T) => Date) {
|
||||
return items.sort((a, b) => <any>key(b) - <any>key(a));
|
||||
}
|
||||
|
||||
export function getNumberForPriority(priority: Priority): number {
|
||||
switch (priority) {
|
||||
case Priority.High:
|
||||
return 2;
|
||||
case Priority.Medium:
|
||||
return 1;
|
||||
case Priority.Low:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
import {sortByNumberAscending, sortByStringAscending, sortByDateAscending} from './sorting.service';
|
||||
import {Priority} from './backend.service';
|
||||
|
||||
export abstract class TableComponentBase<T> {
|
||||
public sortBy: string;
|
||||
public sortDescending = false;
|
||||
public items: T[] = [];
|
||||
|
||||
protected doNumericSort(by: string, key: (item: T) => number) {
|
||||
if (this.sortBy === by) {
|
||||
this.sortDescending = !this.sortDescending;
|
||||
} else {
|
||||
this.sortBy = by;
|
||||
}
|
||||
|
||||
this.items = sortByNumberAscending(this.items, key);
|
||||
if (this.sortDescending) {
|
||||
this.items = this.items.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
protected doStringSort(by: string, key: (item: T) => string) {
|
||||
if (this.sortBy === by) {
|
||||
this.sortDescending = !this.sortDescending;
|
||||
} else {
|
||||
this.sortBy = by;
|
||||
}
|
||||
|
||||
this.items = sortByStringAscending(this.items, key);
|
||||
if (this.sortDescending) {
|
||||
this.items = this.items.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
protected doDateSort(by: string, key: (item: T) => Date) {
|
||||
if (this.sortBy === by) {
|
||||
this.sortDescending = !this.sortDescending;
|
||||
} else {
|
||||
this.sortBy = by;
|
||||
}
|
||||
|
||||
this.items = sortByDateAscending(this.items, key);
|
||||
if (this.sortDescending) {
|
||||
this.items = this.items.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
public getAllPriorities(): string[] {
|
||||
return Object.values(Priority);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
.modal-footer {
|
||||
border-top: 0px solid;
|
||||
padding-top: 5%;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<div class="modal-content p-3">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Neuen Sprint anlegen</h4>
|
||||
<button (click)="onClose()" type="button" class="close" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form (ngSubmit)="onSubmit()">
|
||||
<div class="form-group">
|
||||
<label for="Title">Titel</label>
|
||||
<input type="text" class="form-control" id="Title" required name="title" [(ngModel)]="sprint.title"
|
||||
id="titleField">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="date">Startdatum</label>
|
||||
<input type="Date" class="form-control" id="Date" required name="date" [(ngModel)]="sprint.startDate"
|
||||
id="titleField">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="Date">Enddatum</label>
|
||||
<input type="date" class="form-control" id="Date" required name="date" [(ngModel)]="sprint.endDate"
|
||||
id="titleField">
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button (click)="onClose()" type="dismiss" class="btn btn-secondary"
|
||||
data-dismiss="modal">Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary">Sprint starten</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import {
|
||||
BackendService,
|
||||
ScrumTask,
|
||||
Priority,
|
||||
ScrumStatus,
|
||||
ScrumCategory,
|
||||
ScrumUser,
|
||||
ScrumProject,
|
||||
ScrumUserstory,
|
||||
ScrumSprint
|
||||
} from '../services/backend.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { HttpResponse } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-task-form',
|
||||
templateUrl: './sprint-form.component.html',
|
||||
styleUrls: ['./sprint-form.component.css'],
|
||||
})
|
||||
export class SprintFormComponent implements OnInit {
|
||||
@Input() public sprint: ScrumSprint;
|
||||
public editing: Boolean;
|
||||
public sprintid: string;
|
||||
|
||||
constructor(
|
||||
private backendService: BackendService,
|
||||
private activeModalService: NgbActiveModal
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.sprint === null || this.sprint === undefined) {
|
||||
this.sprint = { title: '', startDate: new Date(), endDate: new Date(), project: 0 }; //project id: static counter?
|
||||
this.editing = false;
|
||||
} else {
|
||||
this.editing = true;
|
||||
}
|
||||
document.getElementById('titleField').focus();
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
if (this.editing) {
|
||||
this.backendService.putSprint(this.sprint).subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.backendService.postSprint(this.sprint).subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
});
|
||||
}
|
||||
this.activeModalService.close(this.sprint);
|
||||
}
|
||||
|
||||
onClose() {
|
||||
this.activeModalService.dismiss(this.sprint);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
th.sortable:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<div class="mx-5 my-3">
|
||||
|
||||
<h3 class="my-1">
|
||||
Sprints
|
||||
</h3>
|
||||
|
||||
<button class="btn btn-secondary my-3" (click)="openSprintForm()">Neuer Sprint</button>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th (click)="sortById()" class="sortable">
|
||||
<span>ID</span>
|
||||
<span *ngIf="sortBy === 'id'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th (click)="sortByTitle()" class="sortable">
|
||||
<span>Titel</span>
|
||||
<span *ngIf="sortBy === 'title'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th (click)="sortByStartDate()" class="sortable">
|
||||
<span>Start</span>
|
||||
<span *ngIf="sortBy === 'startDate'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th (click)="sortByEndDate()" class="endDate">
|
||||
<span>End</span>
|
||||
<span *ngIf="sortBy === 'endDate'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr *ngFor="let sprint of filteredItems" [class.table-info]="sprint.id === highlightId">
|
||||
<td>{{sprint.id}}</td>
|
||||
<td>{{sprint.title}}</td>
|
||||
<td>{{sprint.startDate | date:'dd.MM.yyyy'}}</td>
|
||||
<td>{{sprint.endDate | date:'dd.MM.yyyy'}}</td>
|
||||
<td>
|
||||
<button type="button" rel="tooltip" (click)="openSprintForm(sprint)" class="btn btn-success btn-sm btn-icon">
|
||||
<i class="fa fa-pencil-alt"></i>
|
||||
</button>
|
||||
<button type="button" rel="tooltip" (click)="deleteSprint(sprint)" class="btn btn-danger btn-sm btn-icon">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {BackendService, ScrumSprint} from '../services/backend.service';
|
||||
import {TableComponentBase} from '../services/table-component.base';
|
||||
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {ActivatedRoute, ParamMap, Router} from '@angular/router';
|
||||
import { SprintFormComponent } from '../sprint-form/sprint-form.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sprint',
|
||||
templateUrl: './sprint-table.component.html',
|
||||
styleUrls: ['./sprint-table.component.css']
|
||||
})
|
||||
export class SprintTableComponent extends TableComponentBase<ScrumSprint> {
|
||||
public filterSprintId: number | null = null;
|
||||
public highlightId: number;
|
||||
|
||||
public get filteredItems() {
|
||||
return this.items.filter(sprint =>
|
||||
(this.filterSprintId === null || sprint.id === this.filterSprintId)
|
||||
);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private backendService: BackendService, private modalService: NgbModal,
|
||||
private route: ActivatedRoute, private router: Router
|
||||
) {
|
||||
super();
|
||||
|
||||
this.applyFilterParameters(route.snapshot.paramMap);
|
||||
route.paramMap.subscribe(map => this.applyFilterParameters(map));
|
||||
|
||||
backendService.getSprints().subscribe(response => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
} else {
|
||||
this.items.push(...response.body);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private applyFilterParameters(params: ParamMap) {
|
||||
if (params.has('id')) {
|
||||
this.highlightId = parseInt(params.get('id'));
|
||||
}
|
||||
}
|
||||
|
||||
public deleteSprint(sprint: ScrumSprint) {
|
||||
this.backendService.deleteSprint(sprint).subscribe(response => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
});
|
||||
const index = this.items.indexOf(sprint);
|
||||
if (index !== -1) {
|
||||
this.items.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public openSprintForm(editSprint?: ScrumSprint) {
|
||||
const modalRef = this.modalService.open(SprintFormComponent, {
|
||||
backdrop: 'static',
|
||||
keyboard: true,
|
||||
});
|
||||
if (editSprint === null) {
|
||||
modalRef.result.then(result => {
|
||||
this.items.push(result);
|
||||
});
|
||||
}
|
||||
modalRef.componentInstance.sprint = editSprint;
|
||||
}
|
||||
|
||||
sortById() {
|
||||
this.doNumericSort('id', sprint => sprint.id);
|
||||
}
|
||||
|
||||
sortByTitle() {
|
||||
this.doStringSort('title', sprint => sprint.title);
|
||||
}
|
||||
|
||||
sortByStartDate() {
|
||||
this.doDateSort('startDate', sprint => sprint.startDate);
|
||||
}
|
||||
|
||||
sortByEndDate() {
|
||||
this.doDateSort('endDate', sprint => sprint.endDate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
.modal-footer {
|
||||
border-top: 0px solid;
|
||||
padding-top: 5%;
|
||||
}
|
||||
.modal-content {
|
||||
width: 1040px;
|
||||
right: 55%;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.modal {
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -1,30 +1,68 @@
|
||||
<div class="modal-content p-3">
|
||||
<div>
|
||||
<button (click) ="onClose()" type="button" class="close" aria-label="Close">
|
||||
<div class="modal-header">
|
||||
<table>
|
||||
<tr>
|
||||
<h4 class="modal-title">Neuen Task anlegen</h4>
|
||||
</tr>
|
||||
<tr>
|
||||
<h6 class="modal-caption text-muted"> Gehört zu Story: <a href="#" id="userstoryTitle">{{this.userstoryId}}</a></h6>
|
||||
</tr>
|
||||
<!--getUserstory fehlt noch-->
|
||||
</table>
|
||||
<button (click)="onClose()" type="button" class="close" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form (ngSubmit)="onSubmit()">
|
||||
<div class="form-group">
|
||||
<label for="Title">Titel</label>
|
||||
<input type="text" class="form-control" id="Title" required name="title" [(ngModel)]="task.title">
|
||||
<div class="modal-body">
|
||||
<form (ngSubmit)="onSubmit()">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="form-group">
|
||||
<label for="Title">Titel</label>
|
||||
<input type="text" class="form-control" id="Title" required name="title"
|
||||
[(ngModel)]="task.title" id="titleField">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1"></div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<label for="Prio">Prio</label>
|
||||
<select class="form-control custom-select mr-sm-2" id="prio" required name="prio"
|
||||
[(ngModel)]="task.priority">
|
||||
<option value="low">Low</option>
|
||||
<option value="medium">Medium</option>
|
||||
<option value="high">High</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="form-group">
|
||||
<label for="Inhalt">What to do?</label>
|
||||
<textarea type="text" class="form-control" id="Story" required name="story" rows="5"
|
||||
[(ngModel)]="task.content"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1"></div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<label for="Inhalt">Status</label>
|
||||
<input type="text" class="form-control" id="Status" required name="status"
|
||||
[(ngModel)]="task.statusid">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="Inhalt">Assigned User</label>
|
||||
<input type="text" class="form-control" id="Author" required name="author">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button (click)="onClose()" type="dismiss" class="btn btn-secondary" data-dismiss="modal">Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary">Erstellen</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="Inhalt">Inhalt</label>
|
||||
<input type="text" class="form-control" id="Content" required name="content" [(ngModel)]="task.content">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="Prio">Prio</label>
|
||||
<select class="form-control" id="prio" required name="prio" [(ngModel)]="task.priority">
|
||||
<option value="low">Low</option>
|
||||
<option value="medium">Medium</option>
|
||||
<option value="high">High</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success">Submit</button>
|
||||
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,50 +1,72 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { BackendService, ScrumTask, Priority, ScrumStatus, ScrumCategory, ScrumUser, ScrumProject } from '../services/backend.service';
|
||||
import {
|
||||
BackendService,
|
||||
ScrumTask,
|
||||
Priority,
|
||||
ScrumStatus,
|
||||
ScrumCategory,
|
||||
ScrumUser,
|
||||
ScrumProject,
|
||||
ScrumUserstory,
|
||||
} from '../services/backend.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { HttpResponse } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-task-form',
|
||||
templateUrl: './task-form.component.html',
|
||||
styleUrls: ['./task-form.component.css']
|
||||
selector: 'app-task-form',
|
||||
templateUrl: './task-form.component.html',
|
||||
styleUrls: ['./task-form.component.css'],
|
||||
})
|
||||
export class TaskFormComponent implements OnInit {
|
||||
@Input() public task: ScrumTask;
|
||||
public editing: Boolean;
|
||||
public userstoryId: string;
|
||||
|
||||
@Input()
|
||||
public task: ScrumTask;
|
||||
public editing: Boolean;
|
||||
constructor(
|
||||
private backendService: BackendService,
|
||||
private activeModalService: NgbActiveModal
|
||||
) {}
|
||||
|
||||
constructor(private backendService: BackendService, private activeModalService: NgbActiveModal) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.task === null || this.task === undefined) {
|
||||
this.task = {title: ""};
|
||||
this.editing = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.editing = true;
|
||||
}
|
||||
ngOnInit(): void {
|
||||
if (this.task === null || this.task === undefined) {
|
||||
this.task = { title: '' };
|
||||
this.editing = false;
|
||||
} else {
|
||||
this.editing = true;
|
||||
}
|
||||
document.getElementById('titleField').focus();
|
||||
this.getRelatedStory();
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
if (this.editing) {
|
||||
this.backendService.putTask(this.task).subscribe(response => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
});
|
||||
onSubmit() {
|
||||
if (this.editing) {
|
||||
this.backendService.putTask(this.task).subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
else {
|
||||
this.backendService.postTask(this.task).subscribe(response => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.backendService.postTask(this.task).subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
this.activeModalService.close(this.task);
|
||||
});
|
||||
}
|
||||
this.activeModalService.close(this.task);
|
||||
}
|
||||
|
||||
onClose(){
|
||||
this.activeModalService.dismiss(this.task);
|
||||
}
|
||||
onClose() {
|
||||
this.activeModalService.dismiss(this.task);
|
||||
}
|
||||
|
||||
getRelatedStory() {
|
||||
this.backendService.getUserstory(2).subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
} else {
|
||||
this.userstoryId = response.body.title;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
@@ -1,14 +0,0 @@
|
||||
<button class="btn btn-dark m-3" (click)="openTaskForm(null)">Neu</button>
|
||||
|
||||
<ul class="list-group m-3">
|
||||
<li *ngFor="let task of tasks" class="list-group-item">
|
||||
<span>Titel: {{task.title}}</span>
|
||||
<br/>
|
||||
<span>Inhalt: {{task.content}}</span>
|
||||
<br/>
|
||||
<span>Priotität: {{task.priority}}</span>
|
||||
<br>
|
||||
<button class="btn btn-secondary m-2" (click)="openTaskForm(task)">Bearbeiten</button>
|
||||
<button class="btn btn-secondary m-2" (click)="deleteTask(task)">Löschen</button>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -1,54 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { BackendService, ScrumTask } from '../services/backend.service';
|
||||
import { TaskFormComponent } from '../task-form/task-form.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-task-list',
|
||||
templateUrl: './task-list.component.html',
|
||||
styleUrls: ['./task-list.component.css']
|
||||
})
|
||||
export class TaskListComponent implements OnInit {
|
||||
|
||||
public tasks: ScrumTask[] = [];
|
||||
|
||||
constructor(private backendService: BackendService, private modalService: NgbModal) {
|
||||
backendService.getTasks().subscribe(response => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
else {
|
||||
this.tasks.push(...response.body);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
public deleteTask(task: ScrumTask) {
|
||||
this.backendService.deleteTask(task).subscribe(response => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
});
|
||||
const index = this.tasks.indexOf(task);
|
||||
if (index !== -1) {
|
||||
this.tasks.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public openTaskForm(editTask: ScrumTask) {
|
||||
const modalRef = this.modalService.open(TaskFormComponent, {
|
||||
backdrop: 'static',
|
||||
keyboard: true,
|
||||
});
|
||||
if (editTask === null) {
|
||||
modalRef.result.then(result => {
|
||||
this.tasks.push(result);
|
||||
});
|
||||
}
|
||||
modalRef.componentInstance.task = editTask;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
th.sortable:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<div class="mx-5 my-3">
|
||||
|
||||
<h3 class="my-1">
|
||||
<a *ngIf="filterUserstoryId" [routerLink]="['/userstories', {id: filterUserstoryId}]">
|
||||
Userstory #{{filterUserstoryId}}
|
||||
>
|
||||
</a>
|
||||
Tasks
|
||||
</h3>
|
||||
<div *ngIf="filterUserstoryId">
|
||||
<a [routerLink]="'/tasks'">Alle Tasks anzeigen</a>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-secondary my-3" (click)="openTaskForm()">Neuer Task</button>
|
||||
|
||||
<table class="table">
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th (click)="sortById()" class="sortable">
|
||||
<span>ID</span>
|
||||
<span *ngIf="sortBy === 'id'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th (click)="sortByTitle()" class="sortable">
|
||||
<span>Titel</span>
|
||||
<span *ngIf="sortBy === 'title'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th (click)="sortByTasks()" class="sortable">
|
||||
<span>Userstory</span>
|
||||
<span *ngIf="sortBy === 'userstory'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th (click)="sortByStatus()" class="sortable">
|
||||
<span>Status</span>
|
||||
<span *ngIf="sortBy === 'statusid'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th class="sortable">
|
||||
<div class="d-inline-block">
|
||||
<div ngbDropdown class="d-inline-block">
|
||||
<span id="dropdownBasic1" ngbDropdownToggle>Priotität</span>
|
||||
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
|
||||
<option (click)="filterPriority=null">All</option>
|
||||
<option *ngFor="let p of getAllPriorities()" (click)="filterPriority=p">{{p}}</option>
|
||||
</div>
|
||||
</div>
|
||||
<div (click)="sortByPrio()">
|
||||
<i class="fa fa-sort"></i>
|
||||
<span *ngIf="sortBy === 'priority'" (click)="sortByPrio()" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<th (click)="sortByAssigned()" class="sortable">
|
||||
<span>Assigned To</span>
|
||||
<span *ngIf="sortBy === 'assignedtoid'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th (click)="sortByCategory()" class="sortable">
|
||||
<span>Category</span>
|
||||
<span *ngIf="sortBy === 'categoryid'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr *ngFor="let task of filteredItems" [class.table-info]="task.id === highlightId">
|
||||
<td>{{task.id}}</td>
|
||||
<td>{{task.title}}</td>
|
||||
<td>
|
||||
<a [routerLink]="['/userstories', {id: task.userstoryid}]">
|
||||
US #{{task.userstoryid}}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a [routerLink]="['/status', {id: task.statusid}]">
|
||||
{{getStatusTitleById(task.statusid)}}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{task.priority}}</td>
|
||||
<td>
|
||||
<a [routerLink]="['/users', {id: task.assignedtoid}]">
|
||||
{{getUserNameById(task.assignedtoid)}}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a [routerLink]="['/categories', {id: task.categoryid}]">
|
||||
{{getCategoryTitleById(task.categoryid)}}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" rel="tooltip" (click)="openTaskForm(task)" class="btn btn-success btn-sm btn-icon">
|
||||
<i class="fa fa-pencil-alt"></i>
|
||||
</button>
|
||||
<button type="button" rel="tooltip" (click)="deleteTask(task)" class="btn btn-danger btn-sm btn-icon">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
BackendService,
|
||||
ScrumTask,
|
||||
ScrumStatus,
|
||||
ScrumUser,
|
||||
ScrumCategory,
|
||||
} from '../services/backend.service';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TaskFormComponent } from '../task-form/task-form.component';
|
||||
import { TableComponentBase } from '../services/table-component.base';
|
||||
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
|
||||
import { getNumberForPriority } from '../services/sorting.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-userstory-table',
|
||||
templateUrl: './task-table.component.html',
|
||||
styleUrls: ['./task-table.component.css'],
|
||||
})
|
||||
export class TaskTableComponent extends TableComponentBase<ScrumTask> {
|
||||
public filterUserstoryId: number | null = null;
|
||||
public filterPriority: string | null = null;
|
||||
public highlightId: number;
|
||||
public status: ScrumStatus[] = [];
|
||||
public users: ScrumUser[] = [];
|
||||
public categories: ScrumCategory[] = [];
|
||||
|
||||
public get filteredItems() {
|
||||
return this.items.filter(
|
||||
(task) =>
|
||||
(this.filterUserstoryId === null ||
|
||||
task.userstoryid === this.filterUserstoryId) &&
|
||||
(this.filterPriority === null || task.priority === this.filterPriority)
|
||||
);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private backendService: BackendService,
|
||||
private modalService: NgbModal,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router
|
||||
) {
|
||||
super();
|
||||
|
||||
this.applyFilterParameters(route.snapshot.paramMap);
|
||||
route.paramMap.subscribe((map) => this.applyFilterParameters(map));
|
||||
|
||||
backendService.getTasks().subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
} else {
|
||||
this.items.push(...response.body);
|
||||
}
|
||||
});
|
||||
backendService.getAllStatus().subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
} else {
|
||||
this.status.push(...response.body);
|
||||
}
|
||||
});
|
||||
backendService.getUsers().subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
} else {
|
||||
this.users.push(...response.body);
|
||||
}
|
||||
});
|
||||
backendService.getCategories().subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
} else {
|
||||
this.categories.push(...response.body);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private applyFilterParameters(params: ParamMap) {
|
||||
if (params.has('userstoryId')) {
|
||||
this.filterUserstoryId = parseInt(params.get('userstoryId'));
|
||||
} else {
|
||||
this.filterUserstoryId = null;
|
||||
}
|
||||
if (params.has('id')) {
|
||||
this.highlightId = parseInt(params.get('id'));
|
||||
}
|
||||
}
|
||||
|
||||
public deleteTask(task: ScrumTask) {
|
||||
this.backendService.deleteTask(task).subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
});
|
||||
const index = this.items.indexOf(task);
|
||||
if (index !== -1) {
|
||||
this.items.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public openTaskForm(editTask?: ScrumTask) {
|
||||
const modalRef = this.modalService.open(TaskFormComponent, {
|
||||
backdrop: 'static',
|
||||
keyboard: true,
|
||||
});
|
||||
if (editTask === null) {
|
||||
modalRef.result.then((result) => {
|
||||
this.items.push(result);
|
||||
});
|
||||
}
|
||||
modalRef.componentInstance.task = editTask;
|
||||
}
|
||||
|
||||
sortById() {
|
||||
this.doNumericSort('id', (task) => task.id);
|
||||
}
|
||||
|
||||
sortByTitle() {
|
||||
this.doStringSort('title', (task) => task.title);
|
||||
}
|
||||
|
||||
sortByPrio() {
|
||||
this.doNumericSort('priority', (task) =>
|
||||
getNumberForPriority(task.priority)
|
||||
);
|
||||
}
|
||||
|
||||
sortByTasks() {
|
||||
this.doNumericSort('userstory', (task) => task.userstoryid);
|
||||
}
|
||||
|
||||
sortByStatus() {
|
||||
this.doNumericSort('statusid', (task) => task.statusid);
|
||||
}
|
||||
sortByAssigned() {
|
||||
this.doNumericSort('assignedtoid', (task) => task.assignedtoid);
|
||||
}
|
||||
sortByCategory() {
|
||||
this.doNumericSort('categoryid', (task) => task.categoryid);
|
||||
}
|
||||
|
||||
private findTaskById(id: number): ScrumTask {
|
||||
return this.items.find((t) => t.id === id);
|
||||
}
|
||||
|
||||
getStatusTitleById(id) {
|
||||
var status = this.status.find((x) => x.id === id);
|
||||
if (!status) {
|
||||
return 'N/A';
|
||||
}
|
||||
return status.title;
|
||||
}
|
||||
|
||||
getUserNameById(id) {
|
||||
var user = this.users.find((x) => x.id === id);
|
||||
if (!user) {
|
||||
return 'N/A';
|
||||
}
|
||||
return user.name;
|
||||
}
|
||||
|
||||
getCategoryTitleById(id) {
|
||||
var category = this.categories.find((x) => x.id === id);
|
||||
if (!category) {
|
||||
return 'N/A';
|
||||
}
|
||||
return category.title;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
.modal-footer {
|
||||
border-top: 0px solid;
|
||||
padding-top: 5%;
|
||||
}
|
||||
.modal-content {
|
||||
width: 1040px;
|
||||
right: 55%;
|
||||
}
|
||||
|
||||
.modal {
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -1,30 +1,63 @@
|
||||
<div class="modal-content p-3">
|
||||
<div>
|
||||
<button (click) ="onClose()" type="button" class="close" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<!-- <div class="modal-lg"> -->
|
||||
<div class="modal-content p-3 text-dark">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Neue Userstory anlegen</h4>
|
||||
<button (click)="onClose()" type="button" class="close" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form (ngSubmit)="onSubmit()">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="form-group">
|
||||
<label for="Title">Titel</label>
|
||||
<input type="text" class="form-control" id="Title" required name="title"
|
||||
[(ngModel)]="userstory.title" id="titleField">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1"></div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<label for="Prio">Prio</label>
|
||||
<select class="custom-select mr-sm-2" id="prio" required name="prio"
|
||||
[(ngModel)]="userstory.priority">
|
||||
<option value="low">Low</option>
|
||||
<option value="medium">Medium</option>
|
||||
<option value="high">High</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="form-group">
|
||||
<label for="Inhalt">Story</label>
|
||||
<textarea type="text" class="form-control" id="Story" required name="story" rows="5"
|
||||
[(ngModel)]="userstory.content"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1"></div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<label for="Inhalt">Status</label>
|
||||
<input type="text" class="form-control" id="Status" required name="status"
|
||||
[(ngModel)]="userstory.statusid">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="Inhalt">Autor</label>
|
||||
<input type="text" class="form-control" id="Author" required name="author"
|
||||
[(ngModel)]="userstory.createdbyid">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button (click)="onClose()" type="dismiss" class="btn btn-secondary"
|
||||
data-dismiss="modal">Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary">Erstellen</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<form (ngSubmit)="onSubmit()">
|
||||
<div class="form-group">
|
||||
<label for="Title">Titel</label>
|
||||
<input type="text" class="form-control" id="Title" required name="title" [(ngModel)]="userstory.title">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="Inhalt">Inhalt</label>
|
||||
<input type="text" class="form-control" id="Content" required name="content" [(ngModel)]="userstory.content">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="Prio">Prio</label>
|
||||
<select class="form-control" id="prio" required name="prio" [(ngModel)]="userstory.priority">
|
||||
<option value="low">Low</option>
|
||||
<option value="medium">Medium</option>
|
||||
<option value="high">High</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success">Submit</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<!-- </div> -->
|
||||
@@ -3,48 +3,47 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { BackendService, ScrumUserstory, Priority } from '../services/backend.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-userstory-form',
|
||||
templateUrl: './userstory-form.component.html',
|
||||
styleUrls: ['./userstory-form.component.css']
|
||||
selector: 'app-userstory-form',
|
||||
templateUrl: './userstory-form.component.html',
|
||||
styleUrls: [ './userstory-form.component.css' ]
|
||||
})
|
||||
export class UserstoryFormComponent implements OnInit {
|
||||
@Input() public userstory: ScrumUserstory;
|
||||
private editing: boolean;
|
||||
|
||||
constructor(private backendService: BackendService, private activeModalService: NgbActiveModal) {}
|
||||
|
||||
@Input()
|
||||
public userstory: ScrumUserstory;
|
||||
private editing: boolean;
|
||||
ngOnInit(): void {
|
||||
if (this.userstory === null || this.userstory === undefined) {
|
||||
this.userstory = { title: '' };
|
||||
this.editing = false;
|
||||
} else {
|
||||
this.editing = true;
|
||||
}
|
||||
document.getElementById('titleField').focus();
|
||||
}
|
||||
|
||||
constructor(private backendService: BackendService, private activeModalService: NgbActiveModal) { }
|
||||
onSubmit() {
|
||||
if (this.editing) {
|
||||
this.backendService.putUserstory(this.userstory).subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.backendService.postUserstory(this.userstory).subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
});
|
||||
}
|
||||
this.activeModalService.close(this.userstory);
|
||||
}
|
||||
onClose() {
|
||||
this.activeModalService.dismiss(this.userstory);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.userstory === null || this.userstory === undefined) {
|
||||
this.userstory = {title: ""};
|
||||
this.editing = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.editing = true;
|
||||
}
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
if (this.editing) {
|
||||
this.backendService.putUserstory(this.userstory).subscribe(response => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.backendService.postUserstory(this.userstory).subscribe(response => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
});
|
||||
}
|
||||
this.activeModalService.close(this.userstory);
|
||||
}
|
||||
onClose(){
|
||||
this.activeModalService.dismiss(this.userstory);
|
||||
}
|
||||
//focusTitleField() {
|
||||
// document.getElementById('titleField').focus();
|
||||
//}
|
||||
}
|
||||
Whitespace-only changes.
@@ -1,14 +0,0 @@
|
||||
<button class="btn btn-dark m-3" (click)="openUserstoryForm(null)">Neu</button>
|
||||
|
||||
<ul class="list-group m-3">
|
||||
<li *ngFor="let userstory of userstories" class="list-group-item">
|
||||
<span>Titel: {{userstory.title}}</span>
|
||||
<br/>
|
||||
<span>Inhalt: {{userstory.content}}</span>
|
||||
<br/>
|
||||
<span>Priotität: {{userstory.priority}}</span>
|
||||
<br>
|
||||
<button class="btn btn-secondary m-2" (click)="openUserstoryForm(userstory)">Bearbeiten</button>
|
||||
<button class="btn btn-secondary m-2" (click)="deleteUserstory(userstory)">Löschen</button>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -1,54 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { BackendService, ScrumUserstory } from '../services/backend.service';
|
||||
import { UserstoryFormComponent } from '../userstory-form/userstory-form.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-userstory-list',
|
||||
templateUrl: './userstory-list.component.html',
|
||||
styleUrls: ['./userstory-list.component.css']
|
||||
})
|
||||
export class UserstoryListComponent implements OnInit {
|
||||
|
||||
public userstories: ScrumUserstory[] = [];
|
||||
|
||||
constructor(private backendService: BackendService, private modalService: NgbModal) {
|
||||
backendService.getUserstories().subscribe(response => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
else {
|
||||
this.userstories.push(...response.body);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
public deleteUserstory(userstory: ScrumUserstory) {
|
||||
this.backendService.deleteUserstory(userstory).subscribe(response => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
});
|
||||
const index = this.userstories.indexOf(userstory);
|
||||
if (index !== -1) {
|
||||
this.userstories.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public openUserstoryForm(editUserstory: ScrumUserstory) {
|
||||
const modalRef = this.modalService.open(UserstoryFormComponent, {
|
||||
backdrop: 'static',
|
||||
keyboard: true,
|
||||
});
|
||||
if (editUserstory === null) {
|
||||
modalRef.result.then(result => {
|
||||
this.userstories.push(result);
|
||||
});
|
||||
}
|
||||
modalRef.componentInstance.userstory = editUserstory;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
th.sortable:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<div class="mx-5 my-3">
|
||||
|
||||
<h3 class="my-1">Userstories</h3>
|
||||
|
||||
<button class="btn btn-secondary my-3" (click)="openUserstoryForm()">Neue Userstory</button>
|
||||
|
||||
<table class="table">
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th (click)="sortById()" class="sortable">
|
||||
<span>ID</span>
|
||||
<span *ngIf="sortBy === 'id'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th (click)="sortByTitle()" class="sortable">
|
||||
<span>Titel</span>
|
||||
<span *ngIf="sortBy === 'title'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th (click)="sortByTasks()" class="sortable">
|
||||
<span>Tasks</span>
|
||||
<span *ngIf="sortBy === 'tasks'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th (click)="sortByStatus()" class="sortable">
|
||||
<span>Status</span>
|
||||
<span *ngIf="sortBy === 'statusid'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th (click)="sortByPrio()" class="sortable">
|
||||
<span>Priorität</span>
|
||||
<label class="pl-3" (click)="$event.stopPropagation()">
|
||||
<select [(ngModel)]="filterPriority">
|
||||
<option [ngValue]="null" selected></option>
|
||||
<option *ngFor="let p of getAllPriorities()" [ngValue]="p">{{p}}</option>
|
||||
</select>
|
||||
</label>
|
||||
<span *ngIf="sortBy === 'priority'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th (click)="sortByCategory()" class="sortable">
|
||||
<span>Category</span>
|
||||
<span *ngIf="sortBy === 'categoryid'" class="pl-3">
|
||||
<span *ngIf="sortDescending"><i class="fa fa-sort-up"></i></span>
|
||||
<span *ngIf="sortDescending === false"><i class="fa fa-sort-down"></i></span>
|
||||
</span>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr *ngFor="let userstory of filteredItems" [class.table-info]="userstory.id === highlightId">
|
||||
<td>{{userstory.id}}</td>
|
||||
<td>{{userstory.title}}</td>
|
||||
<td>
|
||||
<a [routerLink]="['/tasks', {userstoryId: userstory.id}]">
|
||||
{{getNumberOfTasks(userstory)}} Tasks
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a [routerLink]="['/status', {id: userstory.statusid}]">
|
||||
{{getStatusTitleById(userstory.statusid)}}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{userstory.priority}}</td>
|
||||
<td>
|
||||
<a [routerLink]="['/categories', {id: userstory.categoryid}]">
|
||||
{{getCategoryTitleById(userstory.categoryid)}}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" rel="tooltip" (click)="openUserstoryForm(userstory)" class="btn btn-success btn-sm btn-icon">
|
||||
<i class="fa fa-pencil-alt"></i>
|
||||
</button>
|
||||
<button type="button" rel="tooltip" (click)="deleteUserstory(userstory)" class="btn btn-danger btn-sm btn-icon">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,150 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
BackendService,
|
||||
ScrumTask,
|
||||
ScrumUserstory,
|
||||
ScrumStatus,
|
||||
ScrumCategory,
|
||||
} from '../services/backend.service';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TableComponentBase } from '../services/table-component.base';
|
||||
import { getNumberForPriority } from '../services/sorting.service';
|
||||
import { UserstoryFormComponent } from '../userstory-form/userstory-form.component';
|
||||
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-userstory-table',
|
||||
templateUrl: './userstory-table.component.html',
|
||||
styleUrls: ['./userstory-table.component.css'],
|
||||
})
|
||||
export class UserstoryTableComponent extends TableComponentBase<
|
||||
ScrumUserstory
|
||||
> {
|
||||
public tasks: ScrumTask[] = [];
|
||||
public filterPriority: string | null = null;
|
||||
public highlightId: number;
|
||||
public status: ScrumStatus[] = [];
|
||||
public categories: ScrumCategory[] = [];
|
||||
|
||||
public get filteredItems() {
|
||||
return this.items.filter(
|
||||
(task) =>
|
||||
this.filterPriority === null || task.priority === this.filterPriority
|
||||
);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private backendService: BackendService,
|
||||
private modalService: NgbModal,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router
|
||||
) {
|
||||
super();
|
||||
|
||||
this.applyFilterParameters(this.route.snapshot.paramMap);
|
||||
this.route.paramMap.subscribe((map) => this.applyFilterParameters(map));
|
||||
|
||||
backendService.getUserstories().subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
} else {
|
||||
this.items.push(...response.body);
|
||||
}
|
||||
});
|
||||
backendService.getTasks().subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
} else {
|
||||
this.tasks.push(...response.body);
|
||||
}
|
||||
});
|
||||
backendService.getAllStatus().subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
} else {
|
||||
this.status.push(...response.body);
|
||||
}
|
||||
});
|
||||
backendService.getCategories().subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
} else {
|
||||
this.categories.push(...response.body);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private applyFilterParameters(params: ParamMap) {
|
||||
if (params.has('id')) {
|
||||
this.highlightId = parseInt(params.get('id'));
|
||||
}
|
||||
}
|
||||
|
||||
public deleteUserstory(userstory: ScrumUserstory) {
|
||||
this.backendService.deleteUserstory(userstory).subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
alert('Fehler');
|
||||
}
|
||||
});
|
||||
const index = this.items.indexOf(userstory);
|
||||
if (index !== -1) {
|
||||
this.items.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public openUserstoryForm(editUserstory?: ScrumUserstory) {
|
||||
const modalRef = this.modalService.open(UserstoryFormComponent, {
|
||||
backdrop: 'static',
|
||||
keyboard: true,
|
||||
});
|
||||
if (editUserstory === null) {
|
||||
modalRef.result.then((result) => {
|
||||
this.items.push(result);
|
||||
});
|
||||
}
|
||||
modalRef.componentInstance.userstory = editUserstory;
|
||||
}
|
||||
|
||||
public getNumberOfTasks(userstory: ScrumUserstory) {
|
||||
return this.tasks.filter((t) => t.userstoryid === userstory.id).length;
|
||||
}
|
||||
|
||||
public sortById() {
|
||||
this.doNumericSort('id', (us) => us.id);
|
||||
}
|
||||
|
||||
public sortByTitle() {
|
||||
this.doStringSort('title', (us) => us.title);
|
||||
}
|
||||
|
||||
public sortByPrio() {
|
||||
this.doNumericSort('priority', (us) => getNumberForPriority(us.priority));
|
||||
}
|
||||
|
||||
public sortByTasks() {
|
||||
this.doNumericSort('tasks', (us) => this.getNumberOfTasks(us));
|
||||
}
|
||||
|
||||
sortByStatus() {
|
||||
this.doNumericSort('statusid', (us) => us.statusid);
|
||||
}
|
||||
sortByCategory() {
|
||||
this.doNumericSort('categoryid', (us) => us.categoryid);
|
||||
}
|
||||
|
||||
getStatusTitleById(id) {
|
||||
var status = this.status.find((x) => x.id === id);
|
||||
if (!status) {
|
||||
return 'N/A';
|
||||
}
|
||||
return status.title;
|
||||
}
|
||||
|
||||
getCategoryTitleById(id) {
|
||||
var category = this.categories.find((x) => x.id === id);
|
||||
if (!category) {
|
||||
return 'N/A';
|
||||
}
|
||||
return category.title;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user