Merge branch 'master' into feature/sidebar
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -22,6 +22,7 @@ speed-measure-plugin*.json
 | 
			
		||||
*.launch
 | 
			
		||||
.settings/
 | 
			
		||||
*.sublime-workspace
 | 
			
		||||
package-lock.json
 | 
			
		||||
 | 
			
		||||
# IDE - VSCode
 | 
			
		||||
.vscode/*
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ WORKDIR /build
 | 
			
		||||
 | 
			
		||||
## Install app dependencies
 | 
			
		||||
COPY package*.json ./
 | 
			
		||||
RUN npm install
 | 
			
		||||
RUN npm install --unsafe-perm
 | 
			
		||||
 | 
			
		||||
## Bundle app source
 | 
			
		||||
COPY . .
 | 
			
		||||
 
 | 
			
		||||
@@ -26,10 +26,11 @@
 | 
			
		||||
            ],
 | 
			
		||||
            "styles": [
 | 
			
		||||
              "node_modules/bootstrap/dist/css/bootstrap.min.css",
 | 
			
		||||
              "src/styles.css",
 | 
			
		||||
              "src/styles.scss",
 | 
			
		||||
              "src/assets/scss/black-dashboard.scss"
 | 
			
		||||
            ],
 | 
			
		||||
            "scripts": []
 | 
			
		||||
            "scripts": [
 | 
			
		||||
            ]
 | 
			
		||||
          },
 | 
			
		||||
          "configurations": {
 | 
			
		||||
            "production": {
 | 
			
		||||
@@ -91,7 +92,7 @@
 | 
			
		||||
              "src/assets"
 | 
			
		||||
            ],
 | 
			
		||||
            "styles": [
 | 
			
		||||
              "src/styles.css"
 | 
			
		||||
              "src/styles.scss"
 | 
			
		||||
            ],
 | 
			
		||||
            "scripts": []
 | 
			
		||||
          }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13471
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13471
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -23,6 +23,7 @@
 | 
			
		||||
    "@ng-bootstrap/ng-bootstrap": "^6.0.0",
 | 
			
		||||
    "bootstrap": "^4.4.0",
 | 
			
		||||
    "chart.js": "^2.9.3",
 | 
			
		||||
    "component": "^1.1.0",
 | 
			
		||||
    "rxjs": "~6.5.4",
 | 
			
		||||
    "tslib": "^1.10.0",
 | 
			
		||||
    "zone.js": "~0.10.2"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,22 @@
 | 
			
		||||
import { NgModule } from '@angular/core';
 | 
			
		||||
import { Routes, RouterModule } from '@angular/router';
 | 
			
		||||
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';
 | 
			
		||||
 | 
			
		||||
import { DashboardComponent } from './components/dashboard/dashboard.component';
 | 
			
		||||
import { UserstoryTableComponent } from './components/userstory-table/userstory-table.component';
 | 
			
		||||
import { TaskTableComponent } from './components/task-table/task-table.component';
 | 
			
		||||
import { SprintTableComponent } from './components/sprint-table/sprint-table.component';
 | 
			
		||||
import {BacklogComponent} from './components/backlog-table/backlog.component';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
  { path: 'tasks', component: TaskTableComponent },
 | 
			
		||||
  { path: 'userstories', component: UserstoryTableComponent },
 | 
			
		||||
  { path: 'dashboard', component: DashboardComponent },
 | 
			
		||||
  { path: 'sprints', component: SprintTableComponent },
 | 
			
		||||
  { path: 'backlog', component: BacklogComponent },
 | 
			
		||||
  { path: '', redirectTo: '/tasks', pathMatch: 'full' },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
	imports: [ RouterModule.forRoot(routes) ],
 | 
			
		||||
	exports: [ RouterModule ]
 | 
			
		||||
  imports: [RouterModule.forRoot(routes)],
 | 
			
		||||
  exports: [RouterModule],
 | 
			
		||||
})
 | 
			
		||||
export class AppRoutingModule {}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,14 +7,16 @@ import { AppRoutingModule } from './app-routing.module';
 | 
			
		||||
import { AppComponent } from './app.component';
 | 
			
		||||
 | 
			
		||||
import { BackendService } from './services/backend.service';
 | 
			
		||||
import { TaskFormComponent } from './task-form/task-form.component';
 | 
			
		||||
import { UserstoryFormComponent } from './userstory-form/userstory-form.component';
 | 
			
		||||
import { SprintFormComponent } from './sprint-form/sprint-form.component';
 | 
			
		||||
import { TaskFormComponent } from './components/task-form/task-form.component';
 | 
			
		||||
import { UserstoryFormComponent } from './components/userstory-form/userstory-form.component';
 | 
			
		||||
import { SprintFormComponent } from './components/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';
 | 
			
		||||
import { UserstoryTableComponent } from './components/userstory-table/userstory-table.component';
 | 
			
		||||
import { TaskTableComponent } from './components/task-table/task-table.component';
 | 
			
		||||
import { SprintTableComponent } from './components/sprint-table/sprint-table.component';
 | 
			
		||||
import { DashboardComponent } from './components/dashboard/dashboard.component';
 | 
			
		||||
import { UserstoryInnerTableComponent } from './components/userstory-inner-table/userstory-inner-table.component';
 | 
			
		||||
import { BacklogComponent } from './components/backlog-table/backlog.component';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [
 | 
			
		||||
@@ -27,17 +29,17 @@ import { DashboardComponent } from './dashboard/dashboard.component';
 | 
			
		||||
    SprintFormComponent,
 | 
			
		||||
    SprintTableComponent,
 | 
			
		||||
    DashboardComponent,
 | 
			
		||||
    UserstoryInnerTableComponent,
 | 
			
		||||
    BacklogComponent
 | 
			
		||||
  ],
 | 
			
		||||
  imports: [
 | 
			
		||||
    BrowserModule,
 | 
			
		||||
    AppRoutingModule,
 | 
			
		||||
    HttpClientModule,
 | 
			
		||||
    FormsModule,
 | 
			
		||||
    NgbModule
 | 
			
		||||
    NgbModule,
 | 
			
		||||
  ],
 | 
			
		||||
  providers: [
 | 
			
		||||
    BackendService,
 | 
			
		||||
  ],
 | 
			
		||||
  bootstrap: [AppComponent]
 | 
			
		||||
  providers: [BackendService],
 | 
			
		||||
  bootstrap: [AppComponent],
 | 
			
		||||
})
 | 
			
		||||
export class AppModule { }
 | 
			
		||||
export class AppModule {}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								src/app/components/backlog-table/backlog.component.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/app/components/backlog-table/backlog.component.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
th.sortable:hover {
 | 
			
		||||
  text-decoration: underline;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										62
									
								
								src/app/components/backlog-table/backlog.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/app/components/backlog-table/backlog.component.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
<div class="container-fluid">
 | 
			
		||||
  <div class="content">
 | 
			
		||||
 | 
			
		||||
  <h3>Backlog</h3>
 | 
			
		||||
<div class="row">
 | 
			
		||||
  <div class="col-lg-6 container-fluid">
 | 
			
		||||
</div>
 | 
			
		||||
<div align="right" class="col-lg-6 container-fluid">
 | 
			
		||||
  <button class="btn btn-secondary" (click)="openSprintForm()">Neuer Sprint</button>
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="col-lg-6 container-fluid">
 | 
			
		||||
    <h4>Backlog</h4>
 | 
			
		||||
    <div *ngFor="let story of backlog" class="col-lg-6 container-fluid">
 | 
			
		||||
      <div class="card" style="width: 150%;">
 | 
			
		||||
        <div class="card-body">
 | 
			
		||||
          <h4 class="card-title">{{story.title}}</h4>
 | 
			
		||||
          <h6 class="card-subtitle mb-2 text-muted">Prio: {{story.priority}}</h6>
 | 
			
		||||
          <p class="card-text">{{story.content}}</p>
 | 
			
		||||
          <div title="Badges">
 | 
			
		||||
            <span class="badge badge-primary">Category: {{story.categoryid || "N/A"}}</span>
 | 
			
		||||
            <span class="badge badge-info">Status: {{story.statusid || "N/A"}}</span>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div style="text-align: right;">
 | 
			
		||||
          <button type="button" rel="tooltip" (click)="addToSprintBacklog(story)"
 | 
			
		||||
                class="btn btn-sm btn-success btn-icon">
 | 
			
		||||
                <i class="fas fa-plus-square"></i>
 | 
			
		||||
          </button>
 | 
			
		||||
        </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div></div>
 | 
			
		||||
 | 
			
		||||
    <div class="col-lg-6 container-fluid">
 | 
			
		||||
 | 
			
		||||
      <h4>Sprint-Backlog - Aktueller Sprint: {{this.currentSprint.title}}</h4>
 | 
			
		||||
      <div *ngFor="let story of choosen" class="col-lg-6 container-fluid">
 | 
			
		||||
        <div class="card" style="width: 150%;">
 | 
			
		||||
          <div class="card-body">
 | 
			
		||||
            <h4 class="card-title">{{story.title}}</h4>
 | 
			
		||||
            <h6 class="card-subtitle mb-2 text-muted">Prio: {{story.priority}}</h6>
 | 
			
		||||
            <p class="card-text">{{story.content}}</p>
 | 
			
		||||
            <div title="Badges">
 | 
			
		||||
              <span class="badge badge-primary">Category: {{story.categoryid || "N/A"}}</span>
 | 
			
		||||
              <span class="badge badge-info">Status: {{story.statusid || "N/A"}}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div style="text-align: right;">
 | 
			
		||||
              <button type="button" rel="tooltip" (click)="deleteFromSprintBacklog(story)"
 | 
			
		||||
              class="btn btn-danger btn-sm btn-icon">
 | 
			
		||||
              <i class="fa fa-trash"></i>
 | 
			
		||||
            </button>
 | 
			
		||||
          </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										160
									
								
								src/app/components/backlog-table/backlog.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								src/app/components/backlog-table/backlog.component.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,160 @@
 | 
			
		||||
import { Component } from '@angular/core';
 | 
			
		||||
import {
 | 
			
		||||
  BackendService,
 | 
			
		||||
  ScrumTask,
 | 
			
		||||
  ScrumUserstory,
 | 
			
		||||
  ScrumStatus,
 | 
			
		||||
  ScrumCategory,
 | 
			
		||||
  ScrumSprint,
 | 
			
		||||
} 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 } from '@angular/router';
 | 
			
		||||
import { SprintFormComponent } from '../sprint-form/sprint-form.component';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-backlog',
 | 
			
		||||
  templateUrl: './backlog.component.html',
 | 
			
		||||
  styleUrls: ['./backlog.component.css'],
 | 
			
		||||
})
 | 
			
		||||
export class BacklogComponent extends TableComponentBase<
 | 
			
		||||
  ScrumUserstory
 | 
			
		||||
> {
 | 
			
		||||
  public tasks: ScrumTask[] = [];
 | 
			
		||||
  public filterPriority: string | null = null;
 | 
			
		||||
  public status: ScrumStatus[] = [];
 | 
			
		||||
  public categories: ScrumCategory[] = [];
 | 
			
		||||
  public sprints: ScrumSprint[] = [];
 | 
			
		||||
 | 
			
		||||
  public backlog: ScrumUserstory[] = [];
 | 
			
		||||
  public choosen: ScrumUserstory[] = [];
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private backendService: BackendService,
 | 
			
		||||
    private modalService: NgbModal,
 | 
			
		||||
    private route: ActivatedRoute,
 | 
			
		||||
  ) {
 | 
			
		||||
    super();
 | 
			
		||||
    backendService.getSprints().subscribe((response) => {
 | 
			
		||||
      if (response.status > 399) {
 | 
			
		||||
        alert('Fehler');
 | 
			
		||||
      } else {
 | 
			
		||||
        this.sprints.push(...response.body);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    backendService.getUserstories().subscribe((response) => {
 | 
			
		||||
      if (response.status > 399) {
 | 
			
		||||
        alert('Fehler');
 | 
			
		||||
      } else {
 | 
			
		||||
        this.backlog = response.body.filter(u => u.sprintid == null);
 | 
			
		||||
        this.choosen = response.body.filter(u => u.sprintid == this.currentSprint.id);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    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);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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,
 | 
			
		||||
      size: 'lg'
 | 
			
		||||
    });
 | 
			
		||||
    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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getCategoryTitleById(id) {
 | 
			
		||||
    var category = this.categories.find((x) => x.id === id);
 | 
			
		||||
    if (!category) {
 | 
			
		||||
      return 'N/A';
 | 
			
		||||
    }
 | 
			
		||||
    return category.title;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Sprint-Backlog
 | 
			
		||||
 | 
			
		||||
  public addToSprintBacklog(userstory: ScrumUserstory) {
 | 
			
		||||
    this.choosen.push(userstory);
 | 
			
		||||
    const index = this.backlog.indexOf(userstory);
 | 
			
		||||
    this.backlog.splice(index, 1);
 | 
			
		||||
    userstory.sprintid = this.currentSprint.id;
 | 
			
		||||
    this.backendService.putUserstory(userstory).subscribe((response) => {
 | 
			
		||||
      if (response.status > 399) {
 | 
			
		||||
        alert('Fehler');
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public deleteFromSprintBacklog(userstory: ScrumUserstory){
 | 
			
		||||
    const index = this.choosen.indexOf(userstory);
 | 
			
		||||
    this.choosen.splice(index, 1);
 | 
			
		||||
    this.backlog.push(userstory)
 | 
			
		||||
    userstory.sprintid = null;
 | 
			
		||||
    this.backendService.putUserstory(userstory).subscribe((response) => {
 | 
			
		||||
      if (response.status > 399) {
 | 
			
		||||
        alert('Fehler');
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public get currentSprint(): ScrumSprint {
 | 
			
		||||
    const now = Date.now();
 | 
			
		||||
    return this.sprints.find(s => Date.parse(s.startDate) < now && Date.parse(s.endDate) > now);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								src/app/components/dashboard/dashboard.component.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/app/components/dashboard/dashboard.component.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
.text-large {
 | 
			
		||||
  font-size: 1.2rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.text-very-large {
 | 
			
		||||
  font-size: 2.4rem;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										101
									
								
								src/app/components/dashboard/dashboard.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/app/components/dashboard/dashboard.component.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
			
		||||
<div class="container-fluid">
 | 
			
		||||
  <div class="content">
 | 
			
		||||
 | 
			
		||||
    <h3>Dashboard</h3>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
 | 
			
		||||
      <div class="row px-3 py-2">
 | 
			
		||||
 | 
			
		||||
        <ng-container *ngIf="selectedSprint === undefined">
 | 
			
		||||
          <h3 class="mr-3 text-primary">Alle Userstories</h3>
 | 
			
		||||
          <a [routerLink]="['sprints']">Lege einen Sprint an, um Userstories zu organisieren.</a>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
 | 
			
		||||
        <ng-container *ngIf="selectedSprint !== undefined">
 | 
			
		||||
 | 
			
		||||
          <h3 class="mr-3 text-primary" *ngIf="selectedSprint === currentSprint">Aktueller Sprint:</h3>
 | 
			
		||||
          <h3 class="mr-3 text-primary" *ngIf="selectedSprint !== currentSprint">Sprint:</h3>
 | 
			
		||||
          <h3 class="mr-3 custom-text-secondary">{{selectedSprint.title}}</h3>
 | 
			
		||||
          <h3 class="mr-3">{{toDateString(selectedSprint.startDate)}} - {{toDateString(selectedSprint.endDate)}}</h3>
 | 
			
		||||
 | 
			
		||||
          <label class="mr-3">
 | 
			
		||||
            <select class="select custom-select custom-text-secondary" [(ngModel)]="selectedSprint">
 | 
			
		||||
              <option class="bg-secondary text-dark" [ngValue]="currentSprint">
 | 
			
		||||
                {{currentSprint.title}} (aktuell)
 | 
			
		||||
              </option>
 | 
			
		||||
              <option value="" disabled="disabled">─────────────────────────</option>
 | 
			
		||||
              <option class="text-dark" *ngFor="let sprint of sprints" [ngValue]="sprint">
 | 
			
		||||
                <ng-container *ngIf="sprint === currentSprint">
 | 
			
		||||
                  {{sprint.title}} (aktuell)
 | 
			
		||||
                </ng-container>
 | 
			
		||||
                <ng-container *ngIf="sprint !== currentSprint">
 | 
			
		||||
                  {{sprint.title}} ({{toDateString(sprint.startDate)}} - {{toDateString(sprint.endDate)}})
 | 
			
		||||
                </ng-container>
 | 
			
		||||
              </option>
 | 
			
		||||
            </select>
 | 
			
		||||
          </label>
 | 
			
		||||
 | 
			
		||||
          <span class="mr-5"></span>
 | 
			
		||||
          <h3
 | 
			
		||||
            *ngIf="selectedSprint === currentSprint"
 | 
			
		||||
            class="mr-3 custom-text-secondary"
 | 
			
		||||
            [class.text-success]="getSprintUrgency() === 2"
 | 
			
		||||
            [class.text-warning]="getSprintUrgency() === 1"
 | 
			
		||||
            [class.text-danger]="getSprintUrgency() === 0"
 | 
			
		||||
          >
 | 
			
		||||
            Verbleibende Tage: {{getRemainingDaysInSprint()}}
 | 
			
		||||
          </h3>
 | 
			
		||||
 | 
			
		||||
        </ng-container>
 | 
			
		||||
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="row">
 | 
			
		||||
 | 
			
		||||
        <div class="p-3 col-12 col-xl-6 col-lg-6 col-md-12 col-sm-12">
 | 
			
		||||
          <div class="card">
 | 
			
		||||
            <div class="card-header">
 | 
			
		||||
          <span class="text-large">
 | 
			
		||||
            Userstories
 | 
			
		||||
          </span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="card-body">
 | 
			
		||||
              <div *ngIf="selectedSprint !== undefined && usedStatus.length === 0">
 | 
			
		||||
                Zum Sprint "{{selectedSprint.title}}" sind aktuell keine Userstories vorhanden.
 | 
			
		||||
              </div>
 | 
			
		||||
              <canvas id="done-stories-chart"></canvas>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="card-deck">
 | 
			
		||||
            <div
 | 
			
		||||
              class="card text-center"
 | 
			
		||||
              *ngFor="let status of usedStatus"
 | 
			
		||||
            >
 | 
			
		||||
              <div class="card-header">
 | 
			
		||||
            <span class="text-large">
 | 
			
		||||
              {{status.title}}
 | 
			
		||||
            </span>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="card-body">
 | 
			
		||||
            <span class="text-very-large">
 | 
			
		||||
              <b>{{getNumberOfUserstoriesByStatus(status)}}</b>
 | 
			
		||||
            </span>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="p-3 col-12 col-xl-6 col-lg-6 col-md-12 col-sm-12">
 | 
			
		||||
          <div class="card">
 | 
			
		||||
            <div class="card-body">
 | 
			
		||||
              <app-userstory-inner-table [items]="selectedSprintUserstories"></app-userstory-inner-table>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										160
									
								
								src/app/components/dashboard/dashboard.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								src/app/components/dashboard/dashboard.component.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,160 @@
 | 
			
		||||
import {Component, OnChanges} from '@angular/core';
 | 
			
		||||
import {forkJoin} from 'rxjs';
 | 
			
		||||
import Chart from 'chart.js';
 | 
			
		||||
import {BackendService, ScrumSprint, ScrumStatus, ScrumUserstory} from '../../services/backend.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-dashboard',
 | 
			
		||||
  templateUrl: 'dashboard.component.html',
 | 
			
		||||
  styleUrls: ['./dashboard.component.css']
 | 
			
		||||
})
 | 
			
		||||
export class DashboardComponent {
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns the status that are used by at least one userstory.
 | 
			
		||||
   */
 | 
			
		||||
  public get usedStatus(): ScrumStatus[] {
 | 
			
		||||
    return this.status.filter(s => this.selectedSprintUserstories.find(us => us.statusid === s.id) !== undefined);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public get selectedSprintUserstories(): ScrumUserstory[] {
 | 
			
		||||
    if (this.selectedSprint === undefined) {
 | 
			
		||||
      return this.userstories;
 | 
			
		||||
    }
 | 
			
		||||
    return this.userstories.filter(us => us.sprintid === this.selectedSprint.id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public get currentSprint(): ScrumSprint {
 | 
			
		||||
    const now = Date.now();
 | 
			
		||||
    return this.sprints.find(s => Date.parse(s.startDate) < now && Date.parse(s.endDate) > now);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _selectedSprint: ScrumSprint;
 | 
			
		||||
  public get selectedSprint(): ScrumSprint {
 | 
			
		||||
    if (this._selectedSprint === undefined) {
 | 
			
		||||
      if (this.currentSprint === undefined) {
 | 
			
		||||
        return this.sprints[0];
 | 
			
		||||
      }
 | 
			
		||||
      return this.currentSprint;
 | 
			
		||||
    }
 | 
			
		||||
    return this._selectedSprint;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public set selectedSprint(value) {
 | 
			
		||||
    this._selectedSprint = value;
 | 
			
		||||
    this.createChart();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public status: ScrumStatus[] = [];
 | 
			
		||||
  public userstories: ScrumUserstory[] = [];
 | 
			
		||||
  public sprints: ScrumSprint[] = [];
 | 
			
		||||
 | 
			
		||||
  public chart: Chart;
 | 
			
		||||
 | 
			
		||||
  constructor(private backendService: BackendService) {
 | 
			
		||||
    // download userstories, status and sprints and update
 | 
			
		||||
    // the chart whenever a new response is there
 | 
			
		||||
    forkJoin([backendService.getUserstories(), backendService.getAllStatus(), backendService.getSprints()])
 | 
			
		||||
      .subscribe(results => {
 | 
			
		||||
        const [userstoryResponse, statusResponse, sprintResponse] = results;
 | 
			
		||||
        if (userstoryResponse.status > 399 || statusResponse.status > 399 || sprintResponse.status > 399) {
 | 
			
		||||
          alert('Fehler');
 | 
			
		||||
        } else {
 | 
			
		||||
          this.userstories.push(...userstoryResponse.body);
 | 
			
		||||
          this.status.push(...statusResponse.body);
 | 
			
		||||
          this.sprints.push(...sprintResponse.body);
 | 
			
		||||
          this.createChart();
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns the date in the following format: 1.7.2020
 | 
			
		||||
   */
 | 
			
		||||
  public toDateString(isoFormatString) {
 | 
			
		||||
    const date = new Date(isoFormatString);
 | 
			
		||||
    return `${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private createChart() {
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    const context = document.getElementById('done-stories-chart').getContext('2d');
 | 
			
		||||
 | 
			
		||||
    if (this.usedStatus.length === 0) {
 | 
			
		||||
      this.chart.destroy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    else {
 | 
			
		||||
      this.chart = new Chart(context, {
 | 
			
		||||
        type: 'pie',
 | 
			
		||||
        data: {
 | 
			
		||||
          labels: this.usedStatus.map(s => s.title),
 | 
			
		||||
          datasets: [{
 | 
			
		||||
            data: this.usedStatus.map(s => this.getNumberOfUserstoriesByStatus(s)),
 | 
			
		||||
            backgroundColor: this.getBackgroundColors(),
 | 
			
		||||
            options: {
 | 
			
		||||
              legend: {
 | 
			
		||||
                display: true,
 | 
			
		||||
                position: 'right',
 | 
			
		||||
                labels: {
 | 
			
		||||
                  fontColor: 'rgba(255, 255, 255, 0.8)'
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }]
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      this.chart.update();
 | 
			
		||||
      this.chart.render();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private getBackgroundColors(): string[] {
 | 
			
		||||
    const baseColors = [
 | 
			
		||||
      'rgb(255, 153, 102)',
 | 
			
		||||
      'rgb(255, 102, 102)',
 | 
			
		||||
      'rgb(153, 204, 255)',
 | 
			
		||||
      'rgb(102, 153, 102)',
 | 
			
		||||
      'rgb(204, 204, 153)',
 | 
			
		||||
      'rgb(153, 102, 204)',
 | 
			
		||||
      'rgb(204, 102, 102)',
 | 
			
		||||
      'rgb(255, 204, 153)',
 | 
			
		||||
      'rgb(153, 102, 255)',
 | 
			
		||||
      'rgb(204, 204, 204)',
 | 
			
		||||
      'rgb(102, 255, 204)',
 | 
			
		||||
      'rgb(102, 153, 255)',
 | 
			
		||||
      'rgb(153, 102, 153)',
 | 
			
		||||
      'rgb(204, 204, 255)',
 | 
			
		||||
    ];
 | 
			
		||||
    const colors = [];
 | 
			
		||||
    while (colors.length < this.usedStatus.length) {
 | 
			
		||||
      colors.push(...baseColors);
 | 
			
		||||
    }
 | 
			
		||||
    return colors;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getNumberOfUserstoriesByStatus(status: ScrumStatus): number {
 | 
			
		||||
    return this.selectedSprintUserstories.filter(us => us.statusid === status.id).length;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getRemainingDaysInSprint(): number {
 | 
			
		||||
    if (this.selectedSprint === undefined) {
 | 
			
		||||
      return undefined;
 | 
			
		||||
    }
 | 
			
		||||
    return Math.floor((Date.parse(this.selectedSprint.endDate) - Date.now()) / 86400000);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns the "urgency" of the current sprint (ie what percentage of the sprint is remaining)
 | 
			
		||||
   * as an integer from 0 (most urgent) to 2 (least urgent).
 | 
			
		||||
   */
 | 
			
		||||
  public getSprintUrgency(): number {
 | 
			
		||||
    const now = Date.now();
 | 
			
		||||
    const sprint = this.selectedSprint;
 | 
			
		||||
    if (sprint === undefined) {
 | 
			
		||||
      return undefined;
 | 
			
		||||
    }
 | 
			
		||||
    const deltaFromNow = Date.parse(sprint.endDate) - now;
 | 
			
		||||
    const deltaFromStart = Date.parse(sprint.endDate) - Date.parse(sprint.startDate);
 | 
			
		||||
    return Math.floor(3 * deltaFromNow / deltaFromStart);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								src/app/components/sprint-form/sprint-form.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/app/components/sprint-form/sprint-form.component.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
<!--Popup form to create an modify sprints-->
 | 
			
		||||
 | 
			
		||||
<div class="card" style="width: 100%;">
 | 
			
		||||
  <div class="container">
 | 
			
		||||
    <div class="card-body">
 | 
			
		||||
      <div style="text-align: right;">
 | 
			
		||||
        <i class="fa fa-times fa-2x" (click)="onClose()"></i>
 | 
			
		||||
      </div>
 | 
			
		||||
      <h4 class="card-title">Neuen Sprint anlegen</h4>
 | 
			
		||||
      <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 #startDate type="Date" class="form-control" id="Date" required name="date"
 | 
			
		||||
            [value]="sprint.startDate | date: 'yyyy-MM-dd'" (change)="sprint.startDate=startDate.value"
 | 
			
		||||
            id="startDateField">
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="form-group">
 | 
			
		||||
          <label for="Date">Enddatum</label>
 | 
			
		||||
          <input #endDate type="date" class="form-control" id="Date" required name="date"
 | 
			
		||||
            [value]="sprint.endDate | date: 'yyyy-MM-dd'" (change)="sprint.endDate=endDate.value" id="endDateField">
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
          <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>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										60
									
								
								src/app/components/sprint-form/sprint-form.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/app/components/sprint-form/sprint-form.component.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
// Importing necessary components and interfaces.
 | 
			
		||||
import { Component, OnInit, Input } from '@angular/core';
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 | 
			
		||||
import {
 | 
			
		||||
	BackendService,
 | 
			
		||||
	ScrumSprint
 | 
			
		||||
} from '../../services/backend.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
	selector: 'app-task-form',
 | 
			
		||||
	templateUrl: './sprint-form.component.html',
 | 
			
		||||
	styleUrls: ['./sprint-form.component.css']
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// Class implements the logic for a popup window form to create and modify sprints.
 | 
			
		||||
export class SprintFormComponent implements OnInit {
 | 
			
		||||
	@Input() public sprint: ScrumSprint;
 | 
			
		||||
	public editing: Boolean;
 | 
			
		||||
	public sprintid: string;
 | 
			
		||||
 | 
			
		||||
	constructor(private backendService: BackendService, private activeModalService: NgbActiveModal) { }
 | 
			
		||||
 | 
			
		||||
	// If no sprint exists a new one will be created.
 | 
			
		||||
	// In other cases the sprint exists and gets modifiable.
 | 
			
		||||
	ngOnInit(): void {
 | 
			
		||||
		if (this.sprint === null || this.sprint === undefined) {
 | 
			
		||||
			this.sprint = { title: '', startDate: '', endDate: '' };
 | 
			
		||||
			this.editing = false;
 | 
			
		||||
		} else {
 | 
			
		||||
			this.editing = true;
 | 
			
		||||
		}
 | 
			
		||||
		document.getElementById('titleField').focus();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// A new created sprint will be saved in backend (POST).
 | 
			
		||||
	// If a sprint already exists, modifying results an update (PUT) to the backend.
 | 
			
		||||
	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) => {
 | 
			
		||||
				console.log('Sprint gespeichert!');
 | 
			
		||||
				if (response.status > 399) {
 | 
			
		||||
					alert('Fehler');
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
		// Closes the popup window after submitting/canceling.
 | 
			
		||||
		this.activeModalService.close(this.sprint);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Closes the popup form window (by clicking "close button").
 | 
			
		||||
	onClose() {
 | 
			
		||||
		this.activeModalService.dismiss(this.sprint);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,66 +1,66 @@
 | 
			
		||||
<div class="container-fluid">
 | 
			
		||||
  <div class="content">
 | 
			
		||||
    <h3>
 | 
			
		||||
      Sprints
 | 
			
		||||
    </h3>
 | 
			
		||||
 | 
			
		||||
    <button class="btn btn-secondary" (click)="openSprintForm()">Neuer Sprint</button>
 | 
			
		||||
    <table class="table">
 | 
			
		||||
      <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
          <th (click)="sortById()" class="sortable">
 | 
			
		||||
            <span>ID</span>
 | 
			
		||||
            <span>
 | 
			
		||||
              <span *ngIf="sortBy != 'id'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending && sortBy === 'id'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending === false && sortBy === 'id'"><i class="fa fa-sort-down fa-lg"></i></span>
 | 
			
		||||
            </span>
 | 
			
		||||
          </th>
 | 
			
		||||
          <th (click)="sortByTitle()" class="sortable">
 | 
			
		||||
            <span>Titel</span>
 | 
			
		||||
            <span>
 | 
			
		||||
              <span *ngIf="sortBy != 'title'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending && sortBy === 'title'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending === false && sortBy === 'title'"><i class="fa fa-sort-down fa-lg"></i></span>
 | 
			
		||||
            </span>
 | 
			
		||||
          </th>
 | 
			
		||||
          <th (click)="sortByStartDate()" class="sortable">
 | 
			
		||||
            <span>Start</span>
 | 
			
		||||
            <span>
 | 
			
		||||
              <span *ngIf="sortBy != 'startDate'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending && sortBy === 'startDate'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending === false && sortBy === 'startDate'"><i class="fa fa-sort-down fa-lg"></i></span>
 | 
			
		||||
            </span>
 | 
			
		||||
          </th>
 | 
			
		||||
          <th (click)="sortByEndDate()" class="sortable">
 | 
			
		||||
            <span>End</span>
 | 
			
		||||
            <span>
 | 
			
		||||
              <span *ngIf="sortBy != 'endDate'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending && sortBy === 'endDate'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending === false && sortBy === 'endDate'"><i class="fa fa-sort-down fa-lg"></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>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="container-fluid">
 | 
			
		||||
  <div class="content">
 | 
			
		||||
    <h3>
 | 
			
		||||
      Sprints
 | 
			
		||||
    </h3>
 | 
			
		||||
 | 
			
		||||
    <button class="btn btn-secondary" (click)="openSprintForm()">Neuer Sprint</button>
 | 
			
		||||
    <table class="table">
 | 
			
		||||
      <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
          <th (click)="sortById()" class="sortable">
 | 
			
		||||
            <span>ID</span>
 | 
			
		||||
            <span>
 | 
			
		||||
              <span *ngIf="sortBy != 'id'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending && sortBy === 'id'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending === false && sortBy === 'id'"><i class="fa fa-sort-down fa-lg"></i></span>
 | 
			
		||||
            </span>
 | 
			
		||||
          </th>
 | 
			
		||||
          <th (click)="sortByTitle()" class="sortable">
 | 
			
		||||
            <span>Titel</span>
 | 
			
		||||
            <span>
 | 
			
		||||
              <span *ngIf="sortBy != 'title'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending && sortBy === 'title'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending === false && sortBy === 'title'"><i class="fa fa-sort-down fa-lg"></i></span>
 | 
			
		||||
            </span>
 | 
			
		||||
          </th>
 | 
			
		||||
          <th (click)="sortByStartDate()" class="sortable">
 | 
			
		||||
            <span>Start</span>
 | 
			
		||||
            <span>
 | 
			
		||||
              <span *ngIf="sortBy != 'startDate'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending && sortBy === 'startDate'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending === false && sortBy === 'startDate'"><i class="fa fa-sort-down fa-lg"></i></span>
 | 
			
		||||
            </span>
 | 
			
		||||
          </th>
 | 
			
		||||
          <th (click)="sortByEndDate()" class="sortable">
 | 
			
		||||
            <span>End</span>
 | 
			
		||||
            <span>
 | 
			
		||||
              <span *ngIf="sortBy != 'endDate'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending && sortBy === 'endDate'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending === false && sortBy === 'endDate'"><i class="fa fa-sort-down fa-lg"></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>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -1,87 +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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
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.doStringSort('startDate', sprint => sprint.startDate);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sortByEndDate() {
 | 
			
		||||
    this.doStringSort('endDate', sprint => sprint.endDate);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								src/app/components/task-form/task-form.component.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/app/components/task-form/task-form.component.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
.modal .modal-content {
 | 
			
		||||
	display: contents;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										118
									
								
								src/app/components/task-form/task-form.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/app/components/task-form/task-form.component.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
			
		||||
<!--Popup form to create and modify a task-->
 | 
			
		||||
 | 
			
		||||
<div class="card" style="width: 100%;">
 | 
			
		||||
    <div class="container">
 | 
			
		||||
        <div class="card-body">
 | 
			
		||||
            <div style="text-align: right;">
 | 
			
		||||
                <i class="fa fa-times fa-2x" (click)="onClose()"></i>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="row">
 | 
			
		||||
                <div class="col-8" style="text-align: left;">
 | 
			
		||||
                    <h4 class="card-title">Neuen Task anlegen</h4>
 | 
			
		||||
                    <div ngbDropdown class="dropdown card-text">
 | 
			
		||||
                        <span ngbDropdownToggle class="dropdown" id="dropdownMenuUserstory" data-toggle="dropdown"
 | 
			
		||||
                            aria-haspopup="true" aria-expanded="false">
 | 
			
		||||
                            Gehört zu Story: {{getUserstoryTitleById(task.userstoryid) || "Keine"}}
 | 
			
		||||
                            <i class="fa fa-caret-down"></i>
 | 
			
		||||
                        </span>
 | 
			
		||||
                        <div ngbDropdownMenu class="dropdown-menu" aria-labelledby="dropdownMenuUserstory">
 | 
			
		||||
                            <option ngbDropdownItem *ngFor="let userstory of userstories"
 | 
			
		||||
                                (click)="task.userstoryid = userstory.id">{{ userstory.title }}</option>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="col-4"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <form (ngSubmit)="onSubmit()">
 | 
			
		||||
                <div class="row">
 | 
			
		||||
                    <div class="col-8">
 | 
			
		||||
                        <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 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-4">
 | 
			
		||||
                        <div ngbDropdown class="dropdown">
 | 
			
		||||
                            <button ngbDropdownToggle class="btn btn-secondary dropdown-toggle" type="button"
 | 
			
		||||
                                id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
 | 
			
		||||
                                Prio: {{task.priority}}
 | 
			
		||||
                            </button>
 | 
			
		||||
                            <div ngbDropdownMenu class="dropdown-menu" aria-labelledby="dropdownMenu2">
 | 
			
		||||
                                <option ngbDropdownItem *ngFor="let p of getAllPriorities()" (click)="task.priority=p">
 | 
			
		||||
                                    {{p}}</option>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="form-group">
 | 
			
		||||
                            <div ngbDropdown class="dropdown" [autoClose]="false">
 | 
			
		||||
                                <button ngbDropdownToggle class="btn btn-secondary dropdown-toggle" type="button"
 | 
			
		||||
                                    id="dropdownMenu3" data-toggle="dropdown" aria-haspopup="true"
 | 
			
		||||
                                    aria-expanded="false">
 | 
			
		||||
                                    Status: {{getStatusTitleById(task.statusid)}}
 | 
			
		||||
                                </button>
 | 
			
		||||
                                <div ngbDropdownMenu class="dropdown-menu" aria-labelledby="dropdownMenu2">
 | 
			
		||||
                                    <div class="card-text" for="Inhalt">Status wählen</div>
 | 
			
		||||
                                    <option disable-auto-close ngbDropdownItem *ngFor="let status of allStatus"
 | 
			
		||||
                                        (click)="task.statusid = status.id">{{ status.title }}</option>
 | 
			
		||||
 | 
			
		||||
                                    <div class="dropdown-divider"></div>
 | 
			
		||||
                                    <div class="card-text" for="Inhalt">Neuer Status</div>
 | 
			
		||||
                                    <input #statusname type="text" id="statusname" class="dropdown-item"
 | 
			
		||||
                                        (change)="status.title=statusname.value" placeholder="New Title..."
 | 
			
		||||
                                        style="background-color: rgba(211, 211, 211, 0.342);">
 | 
			
		||||
                                    <button disable-auto-close ngbDropdownItem class="dropdown-item" type="button"
 | 
			
		||||
                                        (click)="createTaskStatus(status)">Status anlegen</button>
 | 
			
		||||
                                    <button disable-auto-close ngbDropdownItem class="dropdown-item" type="button"
 | 
			
		||||
                                        (click)="deleteStatus(task.statusid)">Status löschen</button>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="dropdown-menu">
 | 
			
		||||
                                <select class="form-control custom-select mr-sm-2" id="status" required name="status"
 | 
			
		||||
                                    [(ngModel)]="task.statusid">
 | 
			
		||||
                                    <option *ngFor="let status of allStatus" [value]="status.id">{{
 | 
			
		||||
                status.title
 | 
			
		||||
              }}</option>
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="form-group">
 | 
			
		||||
                            <div ngbDropdown class="dropdown" [autoClose]="true">
 | 
			
		||||
                                <button ngbDropdownToggle class="btn btn-secondary dropdown-toggle" type="button"
 | 
			
		||||
                                    id="dropdownMenu4" data-toggle="dropdown" aria-haspopup="true"
 | 
			
		||||
                                    aria-expanded="false">
 | 
			
		||||
                                    Bearbeiter: {{getAuthorById(task.assignedtoid)}}
 | 
			
		||||
                                </button>
 | 
			
		||||
                                <div ngbDropdownMenu class="dropdown-menu" aria-labelledby="dropdownMenu2">
 | 
			
		||||
                                    <div class="card-text" for="Inhalt">User wählen</div>
 | 
			
		||||
                                    <option disable-auto-close ngbDropdownItem *ngFor="let user of allUser"
 | 
			
		||||
                                        (click)="task.assignedtoid = user.id">{{ user.name }}</option>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="dropdown-menu">
 | 
			
		||||
                                <select class="form-control custom-select mr-sm-2" id="assignedto" required name="assignedto"
 | 
			
		||||
                                    [(ngModel)]="task.assignedtoid">
 | 
			
		||||
                                    <option *ngFor="let user of allUser" [value]="user.id">{{
 | 
			
		||||
                user.name
 | 
			
		||||
              }}</option>
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="row">
 | 
			
		||||
                    <div class="col-6">
 | 
			
		||||
                        <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>
 | 
			
		||||
            </form>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										200
									
								
								src/app/components/task-form/task-form.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								src/app/components/task-form/task-form.component.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,200 @@
 | 
			
		||||
// Importing necessary components and interfaces.
 | 
			
		||||
import { Component, OnInit, Input } from '@angular/core';
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 | 
			
		||||
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']
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// Class implements the logic for a popup window form to create and modify tasks.
 | 
			
		||||
export class TaskFormComponent implements OnInit {
 | 
			
		||||
	@Input() public task: ScrumTask;
 | 
			
		||||
	public editing: boolean;
 | 
			
		||||
	public creating: boolean;
 | 
			
		||||
	public userstoryId: string;
 | 
			
		||||
	public userstories: any[] = [];
 | 
			
		||||
	public allStatus: any[] = [];
 | 
			
		||||
	public status: ScrumStatus = { title: "", description: "" };
 | 
			
		||||
	public allUser: any[] = [];
 | 
			
		||||
	public user: ScrumUser = { name: "" };
 | 
			
		||||
 | 
			
		||||
	constructor(private backendService: BackendService, private activeModalService: NgbActiveModal) {
 | 
			
		||||
		this.getUserStories();
 | 
			
		||||
		this.getTaskStatus();
 | 
			
		||||
		this.getAllUsers();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If no task exists a new one will be created.
 | 
			
		||||
	// In other cases the task exists and gets modifiable.
 | 
			
		||||
	ngOnInit(): void {
 | 
			
		||||
		if (this.task === null || this.task === undefined) {
 | 
			
		||||
			this.task = { title: '' };
 | 
			
		||||
			this.editing = false;
 | 
			
		||||
			this.creating = false;
 | 
			
		||||
		} else if (this.task.userstoryid) {
 | 
			
		||||
			this.editing = true;
 | 
			
		||||
		} else {
 | 
			
		||||
			this.creating = true;
 | 
			
		||||
		}
 | 
			
		||||
		document.getElementById('titleField').focus();
 | 
			
		||||
		this.getRelatedStory();
 | 
			
		||||
	}
 | 
			
		||||
	// A new created task will be saved in backend (POST).
 | 
			
		||||
	// If a task already exists, modifying results an update (PUT) to the backend.
 | 
			
		||||
	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');
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
		// Closes the popup window after submitting/canceling.
 | 
			
		||||
		this.activeModalService.close(this.task);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Closes the popup form window (by clicking "close button").
 | 
			
		||||
	onClose() {
 | 
			
		||||
		this.activeModalService.dismiss(this.task);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Getting the userstory which is related to a task.
 | 
			
		||||
	// The related story will be shown in popup window of a task.
 | 
			
		||||
	getRelatedStory() {
 | 
			
		||||
		if (!this.task.userstoryid) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		this.backendService.getUserstory(this.task.userstoryid).subscribe((response) => {
 | 
			
		||||
			if (response.status > 399) {
 | 
			
		||||
				alert('Fehler');
 | 
			
		||||
			} else {
 | 
			
		||||
				this.userstoryId = response.body.title;
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Getting all userstories from backend to show in a dropdown in popup window.
 | 
			
		||||
	getUserStories() {
 | 
			
		||||
		this.backendService.getUserstories().subscribe((response) => {
 | 
			
		||||
			if (response.status > 399) {
 | 
			
		||||
				alert('Fehler');
 | 
			
		||||
			} else {
 | 
			
		||||
				this.userstories.push(...response.body);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Getting all available status from backend to list it in status-dropdown in popup window.
 | 
			
		||||
	getTaskStatus() {
 | 
			
		||||
		this.backendService.getAllStatus().subscribe((response) => {
 | 
			
		||||
			if (response.status > 399) {
 | 
			
		||||
				alert('Fehler');
 | 
			
		||||
			} else {
 | 
			
		||||
				this.allStatus.push(...response.body);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If desired a new arbitrary status (such as "Waiting") can be created, which will be stored in an array.
 | 
			
		||||
	// The new status is available to all tasks.
 | 
			
		||||
	createTaskStatus(status: ScrumStatus) {
 | 
			
		||||
		this.backendService.postStatus(status).subscribe((response) => {
 | 
			
		||||
			if (response.status > 399) {
 | 
			
		||||
				alert('Fehler');
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				this.allStatus.push(response.body);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// A custom status can even be deleted if not used anymore.
 | 
			
		||||
	// This will remove the status from status-array.
 | 
			
		||||
	deleteStatus(id: number) {
 | 
			
		||||
		var status = this.allStatus.find((x) => x.id === id);
 | 
			
		||||
		this.backendService.deleteStatus(status).subscribe((response) => {
 | 
			
		||||
			if (response.status > 399) {
 | 
			
		||||
				alert('Fehler');
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				const index = this.allStatus.indexOf(status);
 | 
			
		||||
				if (index !== -1) {
 | 
			
		||||
					this.allStatus.splice(index, 1);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			this.task.statusid = null;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Getting the values of the Priority enum to be shown in a dropdown in popup window.
 | 
			
		||||
	getAllPriorities(): Priority[] {
 | 
			
		||||
		return Object.values(Priority);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// necessary?????????????????????????????????????????????????????
 | 
			
		||||
	getUserstoryTitleById(id: number): string {
 | 
			
		||||
		if (!id) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		var story = this.userstories.find((x) => x.id === id);
 | 
			
		||||
		if (!story) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		return story.title;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Shows the before choosen status in the status-field in the popup window.
 | 
			
		||||
	getStatusTitleById(id: number): string {
 | 
			
		||||
		if (!id) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		var status = this.allStatus.find((x) => x.id === id);
 | 
			
		||||
		if (!status) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		return status.title;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Getting all taskboard users from backend to show in a dropdown in popup window.
 | 
			
		||||
	getAllUsers() {
 | 
			
		||||
		this.backendService.getUsers().subscribe((response) => {
 | 
			
		||||
			if (response.status > 399) {
 | 
			
		||||
				alert('Fehler');
 | 
			
		||||
			} else {
 | 
			
		||||
				this.allUser.push(...response.body);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Shows the before assigned user in the author-field in the popup window.
 | 
			
		||||
	getAuthorById(id: number): string {
 | 
			
		||||
		if (!id) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		var user = this.allUser.find((x) => x.id === id);
 | 
			
		||||
		if (!user) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		return user.name;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -5,15 +5,16 @@ import {
 | 
			
		||||
  ScrumStatus,
 | 
			
		||||
  ScrumUser,
 | 
			
		||||
  ScrumCategory,
 | 
			
		||||
} from '../services/backend.service';
 | 
			
		||||
} 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 { TableComponentBase } from '../../services/table-component.base';
 | 
			
		||||
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
 | 
			
		||||
import { getNumberForPriority } from '../services/sorting.service';
 | 
			
		||||
import { getNumberForPriority } from '../../services/sorting.service';
 | 
			
		||||
import { NONE_TYPE } from '@angular/compiler';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-userstory-table',
 | 
			
		||||
  selector: 'app-task-table',
 | 
			
		||||
  templateUrl: './task-table.component.html',
 | 
			
		||||
  styleUrls: ['./task-table.component.css'],
 | 
			
		||||
})
 | 
			
		||||
@@ -102,6 +103,7 @@ export class TaskTableComponent extends TableComponentBase<ScrumTask> {
 | 
			
		||||
    const modalRef = this.modalService.open(TaskFormComponent, {
 | 
			
		||||
      backdrop: 'static',
 | 
			
		||||
      keyboard: true,
 | 
			
		||||
      size: 'lg',
 | 
			
		||||
    });
 | 
			
		||||
    if (editTask === null) {
 | 
			
		||||
      modalRef.result.then((result) => {
 | 
			
		||||
@@ -0,0 +1,3 @@
 | 
			
		||||
.modal .modal-content {
 | 
			
		||||
	display: contents;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										140
									
								
								src/app/components/userstory-form/userstory-form.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								src/app/components/userstory-form/userstory-form.component.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,140 @@
 | 
			
		||||
<!--Popup form to create and modify a task-->
 | 
			
		||||
 | 
			
		||||
<div class="card" style="width: 100%;">
 | 
			
		||||
    <div class="container">
 | 
			
		||||
        <div class="card-body">
 | 
			
		||||
            <div style="text-align: right;">
 | 
			
		||||
                <i class="fa fa-times fa-2x" (click)="onClose()"></i>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="row">
 | 
			
		||||
                <div class="col-8" style="text-align: left;">
 | 
			
		||||
                    <h4 class="card-title">Neue Userstory anlegen</h4>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="col-4"></div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <form (ngSubmit)="onSubmit()">
 | 
			
		||||
                <div class="row">
 | 
			
		||||
                    <div class="col-8">
 | 
			
		||||
                        <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 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-4">
 | 
			
		||||
                        <div ngbDropdown class="dropdown">
 | 
			
		||||
                            <button ngbDropdownToggle class="btn btn-secondary dropdown-toggle" type="button"
 | 
			
		||||
                                id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
 | 
			
		||||
                                Prio: {{userstory.priority}}
 | 
			
		||||
                            </button>
 | 
			
		||||
                            <div ngbDropdownMenu class="dropdown-menu" aria-labelledby="dropdownMenu2">
 | 
			
		||||
                                <option ngbDropdownItem *ngFor="let p of getAllPriorities()"
 | 
			
		||||
                                    (click)="userstory.priority=p">
 | 
			
		||||
                                    {{p}}</option>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="form-group">
 | 
			
		||||
                            <div ngbDropdown class="dropdown" [autoClose]="false">
 | 
			
		||||
                                <button ngbDropdownToggle class="btn btn-secondary dropdown-toggle" type="button"
 | 
			
		||||
                                    id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true"
 | 
			
		||||
                                    aria-expanded="false">
 | 
			
		||||
                                    Status: {{getStatusTitleById(userstory.statusid)}}
 | 
			
		||||
                                </button>
 | 
			
		||||
                                <div ngbDropdownMenu class="dropdown-menu" aria-labelledby="dropdownMenu2">
 | 
			
		||||
                                    <div class="card-text" for="Inhalt">Status wählen</div>
 | 
			
		||||
                                    <option disable-auto-close ngbDropdownItem *ngFor="let status of allStatus"
 | 
			
		||||
                                        (click)="userstory.statusid = status.id">{{ status.title }}</option>
 | 
			
		||||
 | 
			
		||||
                                    <div class="dropdown-divider"></div>
 | 
			
		||||
                                    <div class="card-text" for="Inhalt">Neuer Status</div>
 | 
			
		||||
                                    <input #statusname type="text" id="statusname" class="dropdown-item"
 | 
			
		||||
                                        (change)="status.title=statusname.value" placeholder="New Title..."
 | 
			
		||||
                                        style="background-color: rgba(211, 211, 211, 0.342);">
 | 
			
		||||
                                    <button disable-auto-close ngbDropdownItem class="dropdown-item" type="button"
 | 
			
		||||
                                        (click)="createUserstoryStatus(status)">Status anlegen</button>
 | 
			
		||||
                                    <button disable-auto-close ngbDropdownItem class="dropdown-item" type="button"
 | 
			
		||||
                                        (click)="deleteStatus(userstory.statusid)">Status löschen</button>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="dropdown-menu">
 | 
			
		||||
                                <select class="form-control custom-select mr-sm-2" id="prio" required name="prio"
 | 
			
		||||
                                    [(ngModel)]="userstory.statusid">
 | 
			
		||||
                                    <option *ngFor="let status of allStatus" [value]="status.id">{{
 | 
			
		||||
                status.title
 | 
			
		||||
              }}</option>
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="form-group">
 | 
			
		||||
                            <div ngbDropdown class="dropdown" [autoClose]="false">
 | 
			
		||||
                                <button ngbDropdownToggle class="btn btn-secondary dropdown-toggle" type="button"
 | 
			
		||||
                                    id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true"
 | 
			
		||||
                                    aria-expanded="false">
 | 
			
		||||
                                    Kategorie: {{getCategoryById(userstory.categoryid)}}
 | 
			
		||||
                                </button>
 | 
			
		||||
                                <div ngbDropdownMenu class="dropdown-menu" aria-labelledby="dropdownMenu2">
 | 
			
		||||
                                    <div class="card-text" for="Inhalt">Kategorie wählen</div>
 | 
			
		||||
                                    <option disable-auto-close ngbDropdownItem *ngFor="let category of allCategories"
 | 
			
		||||
                                        (click)="userstory.categoryid = category.id">{{ category.title }}</option>
 | 
			
		||||
 | 
			
		||||
                                    <div class="dropdown-divider"></div>
 | 
			
		||||
                                    <div class="card-text" for="Inhalt">Neue Kategorie</div>
 | 
			
		||||
                                    <input #categoryname type="text" id="categoryname" class="dropdown-item"
 | 
			
		||||
                                        (change)="category.title=categoryname.value" placeholder="New Title..."
 | 
			
		||||
                                        style="background-color: rgba(211, 211, 211, 0.342);">
 | 
			
		||||
                                    <button disable-auto-close ngbDropdownItem class="dropdown-item" type="button"
 | 
			
		||||
                                        (click)="createUserstoryCategory(category)">Kategorie anlegen</button>
 | 
			
		||||
                                    <button disable-auto-close ngbDropdownItem class="dropdown-item" type="button"
 | 
			
		||||
                                        (click)="deleteCategory(userstory.categoryid)">Kategorie löschen</button>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="dropdown-menu">
 | 
			
		||||
                                <select class="form-control custom-select mr-sm-2" id="prio" required name="prio"
 | 
			
		||||
                                    [(ngModel)]="userstory.categoryid">
 | 
			
		||||
                                    <option *ngFor="let category of allCategories" [value]="category.id">{{
 | 
			
		||||
                category.title
 | 
			
		||||
              }}</option>
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="form-group">
 | 
			
		||||
                            <div ngbDropdown class="dropdown" [autoClose]="true">
 | 
			
		||||
                                <button ngbDropdownToggle class="btn btn-secondary dropdown-toggle" type="button"
 | 
			
		||||
                                    id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true"
 | 
			
		||||
                                    aria-expanded="false">
 | 
			
		||||
                                    Autor: {{getAuthorById(userstory.createdbyid)}}
 | 
			
		||||
                                </button>
 | 
			
		||||
                                <div ngbDropdownMenu class="dropdown-menu" aria-labelledby="dropdownMenu2">
 | 
			
		||||
                                    <div class="card-text" for="Inhalt">User wählen</div>
 | 
			
		||||
                                    <option disable-auto-close ngbDropdownItem *ngFor="let user of allUser"
 | 
			
		||||
                                        (click)="userstory.createdbyid = user.id">{{ user.name }}</option>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="dropdown-menu">
 | 
			
		||||
                                <select class="form-control custom-select mr-sm-2" id="prio" required name="prio"
 | 
			
		||||
                                    [(ngModel)]="userstory.createdbyid">
 | 
			
		||||
                                    <option *ngFor="let user of allUser" [value]="user.id">{{
 | 
			
		||||
                user.name
 | 
			
		||||
              }}</option>
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="row">
 | 
			
		||||
                    <div class="col-6">
 | 
			
		||||
                        <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>
 | 
			
		||||
            </form>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										207
									
								
								src/app/components/userstory-form/userstory-form.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								src/app/components/userstory-form/userstory-form.component.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,207 @@
 | 
			
		||||
// Importing necessary components and interfaces.
 | 
			
		||||
import { Component, OnInit, Input } from '@angular/core';
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 | 
			
		||||
import { BackendService, ScrumUserstory, Priority } from '../../services/backend.service';
 | 
			
		||||
import {
 | 
			
		||||
	ScrumTask,
 | 
			
		||||
	ScrumStatus,
 | 
			
		||||
	ScrumCategory,
 | 
			
		||||
	ScrumUser,
 | 
			
		||||
	ScrumProject,
 | 
			
		||||
} from '../../services/backend.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
	selector: 'app-userstory-form',
 | 
			
		||||
	templateUrl: './userstory-form.component.html',
 | 
			
		||||
	styleUrls: ['./userstory-form.component.css']
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// Class implements the logic for a popup window form to create and modify userstories.
 | 
			
		||||
export class UserstoryFormComponent implements OnInit {
 | 
			
		||||
	@Input() public userstory: ScrumUserstory;
 | 
			
		||||
	public allStatus: any[] = [];
 | 
			
		||||
	public status: ScrumStatus = { title: "", description: "" };
 | 
			
		||||
	public allUser: any[] = [];
 | 
			
		||||
	public user: ScrumUser = { name: "" };
 | 
			
		||||
	public allCategories: any[] = [];
 | 
			
		||||
	public category: ScrumCategory = { title: "" };
 | 
			
		||||
	private editing: boolean;
 | 
			
		||||
 | 
			
		||||
	constructor(private backendService: BackendService, private activeModalService: NgbActiveModal) {
 | 
			
		||||
		this.getUserstoryStatus();
 | 
			
		||||
		this.getAllUsers();
 | 
			
		||||
		this.getUserstoryCategory();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If no userstory exists a new one will be created.
 | 
			
		||||
	// In other cases the userstory exists and gets modifiable.
 | 
			
		||||
	ngOnInit(): void {
 | 
			
		||||
		if (this.userstory === null || this.userstory === undefined) {
 | 
			
		||||
			this.userstory = { title: '' };
 | 
			
		||||
			this.editing = false;
 | 
			
		||||
		} else {
 | 
			
		||||
			this.editing = true;
 | 
			
		||||
		}
 | 
			
		||||
		document.getElementById('titleField').focus();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// A new created userstory will be saved in backend (POST).
 | 
			
		||||
	// If a userstory already exists, modifying results an update (PUT) to the backend.
 | 
			
		||||
	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');
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
		// Closes the popup window after submitting/canceling.
 | 
			
		||||
		this.activeModalService.close(this.userstory);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Closes the popup form window (by clicking "close button").
 | 
			
		||||
	onClose() {
 | 
			
		||||
		this.activeModalService.dismiss(this.userstory);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Getting all available status from backend to list it in status-dropdown in popup window.
 | 
			
		||||
	getUserstoryStatus() {
 | 
			
		||||
		this.backendService.getAllStatus().subscribe((response) => {
 | 
			
		||||
			if (response.status > 399) {
 | 
			
		||||
				alert('Fehler');
 | 
			
		||||
			} else {
 | 
			
		||||
				this.allStatus.push(...response.body);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If desired a new arbitrary status (such as "Waiting") can be created, which will be stored in an array.
 | 
			
		||||
	// The new status is available to all userstories.
 | 
			
		||||
	createUserstoryStatus(status: ScrumStatus) {
 | 
			
		||||
		this.backendService.postStatus(status).subscribe((response) => {
 | 
			
		||||
			if (response.status > 399) {
 | 
			
		||||
				alert('Fehler');
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				this.allStatus.push(response.body);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// A custom status can even be deleted if not used anymore.
 | 
			
		||||
	// This will remove the status from status-array.
 | 
			
		||||
	deleteStatus(id: number) {
 | 
			
		||||
		var status = this.allStatus.find((x) => x.id === id);
 | 
			
		||||
		this.backendService.deleteStatus(status).subscribe((response) => {
 | 
			
		||||
			if (response.status > 399) {
 | 
			
		||||
				alert('Fehler');
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				const index = this.allStatus.indexOf(status);
 | 
			
		||||
				if (index !== -1) {
 | 
			
		||||
					this.allStatus.splice(index, 1);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			this.userstory.statusid = null;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Getting the values of the Priority enum to be shown in a dropdown in popup window.
 | 
			
		||||
	getAllPriorities(): Priority[] {
 | 
			
		||||
		return Object.values(Priority);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Shows the before choosen status in the status-field in the popup window.
 | 
			
		||||
	getStatusTitleById(id: number): string {
 | 
			
		||||
		if (!id) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		var status = this.allStatus.find((x) => x.id === id);
 | 
			
		||||
		if (!status) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		return status.title;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Getting all taskboard users from backend to show in a dropdown in popup window.
 | 
			
		||||
	getAllUsers() {
 | 
			
		||||
		this.backendService.getUsers().subscribe((response) => {
 | 
			
		||||
			if (response.status > 399) {
 | 
			
		||||
				alert('Fehler');
 | 
			
		||||
			} else {
 | 
			
		||||
				this.allUser.push(...response.body);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Shows the before assigned user in the author-field in the popup window.
 | 
			
		||||
	getAuthorById(id: number): string {
 | 
			
		||||
		if (!id) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		var user = this.allUser.find((x) => x.id === id);
 | 
			
		||||
		if (!user) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		return user.name;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Getting all available categories from backend to list it in status-dropdown in popup window.
 | 
			
		||||
	getUserstoryCategory() {
 | 
			
		||||
		this.backendService.getCategories().subscribe((response) => {
 | 
			
		||||
			if (response.status > 399) {
 | 
			
		||||
				alert('Fehler');
 | 
			
		||||
			} else {
 | 
			
		||||
				this.allCategories.push(...response.body);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If desired a new arbitrary category can be created, which will be stored in an array.
 | 
			
		||||
	// The new category is available to all userstories.
 | 
			
		||||
	createUserstoryCategory(category: ScrumCategory) {
 | 
			
		||||
		this.backendService.postCategory(category).subscribe((response) => {
 | 
			
		||||
			if (response.status > 399) {
 | 
			
		||||
				alert('Fehler');
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				this.allCategories.push(response.body);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// A custom category can even be deleted if not used anymore.
 | 
			
		||||
	// This will remove the category from category-array.
 | 
			
		||||
	deleteCategory(id: number) {
 | 
			
		||||
		var category = this.allCategories.find((x) => x.id === id);
 | 
			
		||||
		this.backendService.deleteCategory(category).subscribe((response) => {
 | 
			
		||||
			if (response.status > 399) {
 | 
			
		||||
				alert('Fehler');
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				const index = this.allCategories.indexOf(category);
 | 
			
		||||
				if (index !== -1) {
 | 
			
		||||
					this.allCategories.splice(index, 1);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			this.userstory.categoryid = null;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
	// Shows the before choosen category in the category-field in the popup window.
 | 
			
		||||
	getCategoryById(id: number): string {
 | 
			
		||||
		if (!id) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		var category = this.allCategories.find((x) => x.id === id);
 | 
			
		||||
		if (!category) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		return category.title;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,90 @@
 | 
			
		||||
<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>
 | 
			
		||||
@@ -1,31 +1,31 @@
 | 
			
		||||
import { Component } from '@angular/core';
 | 
			
		||||
import { Component, Input } from '@angular/core';
 | 
			
		||||
import {
 | 
			
		||||
  BackendService,
 | 
			
		||||
  ScrumTask,
 | 
			
		||||
  ScrumUserstory,
 | 
			
		||||
  ScrumStatus,
 | 
			
		||||
  ScrumCategory,
 | 
			
		||||
} from '../services/backend.service';
 | 
			
		||||
} 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 { 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'],
 | 
			
		||||
  selector: 'app-userstory-inner-table',
 | 
			
		||||
  templateUrl: './userstory-inner-table.component.html',
 | 
			
		||||
  styleUrls: ['./userstory-inner-table.component.css']
 | 
			
		||||
})
 | 
			
		||||
export class UserstoryTableComponent extends TableComponentBase<
 | 
			
		||||
  ScrumUserstory
 | 
			
		||||
> {
 | 
			
		||||
export class UserstoryInnerTableComponent extends TableComponentBase<ScrumUserstory> {
 | 
			
		||||
  public tasks: ScrumTask[] = [];
 | 
			
		||||
  public filterPriority: string | null = null;
 | 
			
		||||
  public highlightId: number;
 | 
			
		||||
  public status: ScrumStatus[] = [];
 | 
			
		||||
  public categories: ScrumCategory[] = [];
 | 
			
		||||
 | 
			
		||||
  @Input() public items: ScrumUserstory[] = [];
 | 
			
		||||
 | 
			
		||||
  public get filteredItems() {
 | 
			
		||||
    return this.items.filter(
 | 
			
		||||
      (task) =>
 | 
			
		||||
@@ -44,13 +44,6 @@ export class UserstoryTableComponent extends TableComponentBase<
 | 
			
		||||
    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');
 | 
			
		||||
@@ -0,0 +1,6 @@
 | 
			
		||||
<div class="container-fluid">
 | 
			
		||||
  <div class="content">
 | 
			
		||||
    <h3>Userstories</h3>
 | 
			
		||||
    <app-userstory-inner-table [items]="items"></app-userstory-inner-table>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
import { Component } from '@angular/core';
 | 
			
		||||
import {
 | 
			
		||||
  BackendService,
 | 
			
		||||
  ScrumUserstory,
 | 
			
		||||
} from '../../services/backend.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-userstory-table',
 | 
			
		||||
  templateUrl: './userstory-table.component.html',
 | 
			
		||||
  styleUrls: ['./userstory-table.component.css'],
 | 
			
		||||
})
 | 
			
		||||
export class UserstoryTableComponent {
 | 
			
		||||
  public items: ScrumUserstory[] = [];
 | 
			
		||||
 | 
			
		||||
  constructor(private backendService: BackendService) {
 | 
			
		||||
    backendService.getUserstories().subscribe((response) => {
 | 
			
		||||
      if (response.status > 399) {
 | 
			
		||||
        alert('Fehler');
 | 
			
		||||
      } else {
 | 
			
		||||
        this.items.push(...response.body);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
.text-2em {
 | 
			
		||||
  font-size: 2rem;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,55 +0,0 @@
 | 
			
		||||
 | 
			
		||||
<div class="row px-3 py-2">
 | 
			
		||||
  <h1>Dashboard</h1>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="row px-3 py-2">
 | 
			
		||||
  <h2>Current sprint</h2>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="row">
 | 
			
		||||
 | 
			
		||||
  <div class="p-3 xl-col-6 lg-col-6 md-col-12 sm-col-12 xs-col-12">
 | 
			
		||||
    <div class="card">
 | 
			
		||||
      <div class="card-header">
 | 
			
		||||
        Userstories
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="card-body">
 | 
			
		||||
        <canvas id="done-stories-chart"></canvas>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="p-3 xl-col-6 lg-col-6 md-col-12 sm-col-12 xs-col-12">
 | 
			
		||||
    <div class="card-deck">
 | 
			
		||||
 | 
			
		||||
      <div class="card"
 | 
			
		||||
            [class.border-danger]="getSprintUrgency() == 0"
 | 
			
		||||
            [class.border-warning]="getSprintUrgency() == 1"
 | 
			
		||||
            [class.border-success]="getSprintUrgency() == 2"
 | 
			
		||||
            *ngIf="getRemainingDaysInSprint() !== undefined">
 | 
			
		||||
        <div class="card-header">
 | 
			
		||||
          Days remaining
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="card-body text-center">
 | 
			
		||||
          <span class="text-2em">
 | 
			
		||||
            {{getRemainingDaysInSprint()}}
 | 
			
		||||
          </span>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="card" *ngFor="let status of usedStatus">
 | 
			
		||||
        <div class="card-header">
 | 
			
		||||
          Userstories: {{status.title}}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="card-body text-center">
 | 
			
		||||
          <span class="text-2em">
 | 
			
		||||
            {{getNumberOfUserstoriesByStatus(status)}}
 | 
			
		||||
          </span>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
@@ -1,127 +0,0 @@
 | 
			
		||||
import {Component, OnInit} from '@angular/core';
 | 
			
		||||
import Chart from 'chart.js';
 | 
			
		||||
import {BackendService, ScrumStatus, ScrumUser, ScrumUserstory, ScrumSprint} from '../services/backend.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-dashboard',
 | 
			
		||||
  templateUrl: 'dashboard.component.html',
 | 
			
		||||
  styleUrls: ['./dashboard.component.css']
 | 
			
		||||
})
 | 
			
		||||
export class DashboardComponent implements OnInit {
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns the status that are used by at least one userstory.
 | 
			
		||||
   */
 | 
			
		||||
  public get usedStatus(): ScrumStatus[] {
 | 
			
		||||
    return this.status.filter(s => this.userstories.find(us => us.statusid === s.id) !== undefined);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private status: ScrumStatus[];
 | 
			
		||||
  private userstories: ScrumUserstory[];
 | 
			
		||||
  private sprints: ScrumSprint[];
 | 
			
		||||
 | 
			
		||||
  constructor(private backendService: BackendService) {
 | 
			
		||||
    // backendService.getUserstories().subscribe(response => {
 | 
			
		||||
    //   if (response.status > 399) {
 | 
			
		||||
    //     alert('Fehler');
 | 
			
		||||
    //   } else {
 | 
			
		||||
    //     this.userstories.push(...response.body);
 | 
			
		||||
    //   }
 | 
			
		||||
    // });
 | 
			
		||||
    // backendService.getAllStatus().subscribe(response => {
 | 
			
		||||
    //   if (response.status > 399) {
 | 
			
		||||
    //     alert('Fehler');
 | 
			
		||||
    //   } else {
 | 
			
		||||
    //     this.status.push(...response.body);
 | 
			
		||||
    //   }
 | 
			
		||||
    // });
 | 
			
		||||
    // backendService.getSprints().subscribe(response => {
 | 
			
		||||
    //   if (response.status > 399) {
 | 
			
		||||
    //     alert('Fehler');
 | 
			
		||||
    //   } else {
 | 
			
		||||
    //     this.sprints.push(...response.body);
 | 
			
		||||
    //   }
 | 
			
		||||
    // });
 | 
			
		||||
 | 
			
		||||
    this.status = [
 | 
			
		||||
      {id: 0, title: "In progress", description:""},
 | 
			
		||||
      {id: 1, title: "Done", description:""},
 | 
			
		||||
    ];
 | 
			
		||||
    this.userstories = [
 | 
			
		||||
      {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)},
 | 
			
		||||
      {description:"", title:"", project: 0, startDate: new Date(2020, 5, 29), endDate: new Date(2020, 6, 5)},
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    const context = document.getElementById('done-stories-chart').getContext('2d');
 | 
			
		||||
    const chart = new Chart(context, {
 | 
			
		||||
      type: 'pie',
 | 
			
		||||
      data: {
 | 
			
		||||
        labels: this.usedStatus.map(s => s.title),
 | 
			
		||||
        datasets: [{
 | 
			
		||||
          label: 'Done stories',
 | 
			
		||||
          data: this.usedStatus.map(s => this.getNumberOfUserstoriesByStatus(s)),
 | 
			
		||||
          backgroundColor: this.getBackgroundColors(),
 | 
			
		||||
        }]
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private getBackgroundColors(): string[] {
 | 
			
		||||
    const baseColors = [
 | 
			
		||||
      'rgb(255, 153, 102)',
 | 
			
		||||
      'rgb(255, 102, 102)',
 | 
			
		||||
      'rgb(153, 204, 255)',
 | 
			
		||||
      'rgb(102, 153, 102)',
 | 
			
		||||
      'rgb(204, 204, 153)',
 | 
			
		||||
      'rgb(153, 102, 204)',
 | 
			
		||||
      'rgb(204, 102, 102)',
 | 
			
		||||
      'rgb(255, 204, 153)',
 | 
			
		||||
      'rgb(153, 102, 255)',
 | 
			
		||||
      'rgb(204, 204, 204)',
 | 
			
		||||
      'rgb(102, 255, 204)',
 | 
			
		||||
      'rgb(102, 153, 255)',
 | 
			
		||||
      'rgb(153, 102, 153)',
 | 
			
		||||
      'rgb(204, 204, 255)',
 | 
			
		||||
    ];
 | 
			
		||||
    const colors = [];
 | 
			
		||||
    while (colors.length < this.usedStatus.length) {
 | 
			
		||||
      colors.push(...baseColors);
 | 
			
		||||
    }
 | 
			
		||||
    return colors;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getNumberOfUserstoriesByStatus(status: ScrumStatus): number {
 | 
			
		||||
    return this.userstories.filter(us => us.statusid === status.id).length;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getRemainingDaysInSprint(): number {
 | 
			
		||||
    const now = new Date();
 | 
			
		||||
    const currentSprint = this.sprints.find(s => s.endDate > now && s.startDate < now);
 | 
			
		||||
    if (currentSprint === undefined) {
 | 
			
		||||
      return undefined;
 | 
			
		||||
    }
 | 
			
		||||
    const daysDelta = Math.floor((currentSprint.endDate.getTime() - now.getTime()) / 86400000);
 | 
			
		||||
    return daysDelta;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getSprintUrgency(): number {
 | 
			
		||||
    const now = new Date();
 | 
			
		||||
    const currentSprint = this.sprints.find(s => s.endDate > now && s.startDate < now);
 | 
			
		||||
    if (currentSprint === undefined) {
 | 
			
		||||
      return undefined;
 | 
			
		||||
    }
 | 
			
		||||
    const deltaFromNow = currentSprint.endDate.getTime() - now.getTime();
 | 
			
		||||
    const deltaFromStart = currentSprint.endDate.getTime() - currentSprint.startDate.getTime();
 | 
			
		||||
    return Math.floor(3 * deltaFromNow / deltaFromStart);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,265 +1,282 @@
 | 
			
		||||
import {Injectable} from '@angular/core';
 | 
			
		||||
import {HttpClient, HttpResponse} from '@angular/common/http';
 | 
			
		||||
import {Observable} from 'rxjs';
 | 
			
		||||
import {environment} from '../../environments/environment';
 | 
			
		||||
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { HttpClient, HttpResponse } from '@angular/common/http';
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
import { environment } from '../../environments/environment';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class BackendService {
 | 
			
		||||
  constructor(private httpClient: HttpClient) {}
 | 
			
		||||
 | 
			
		||||
    constructor(private httpClient: HttpClient) {}
 | 
			
		||||
  // Tasks
 | 
			
		||||
  public getTasks(): Observable<HttpResponse<ScrumTask[]>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/tasks`;
 | 
			
		||||
    return this.httpClient.get<ScrumTask[]>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getTask(id: number): Observable<HttpResponse<ScrumTask>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/tasks/${id}`;
 | 
			
		||||
    return this.httpClient.get<ScrumTask>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    // Tasks
 | 
			
		||||
    public getTasks(): Observable<HttpResponse<ScrumTask[]>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/tasks`;
 | 
			
		||||
        return this.httpClient.get<ScrumTask[]>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public postTask(task: ScrumTask): Observable<HttpResponse<ScrumTask>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/tasks`;
 | 
			
		||||
    return this.httpClient.post<ScrumTask>(url, task, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public getTask(id: number): Observable<HttpResponse<ScrumTask>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/tasks/${id}`;
 | 
			
		||||
        return this.httpClient.get<ScrumTask>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public putTask(task: ScrumTask): Observable<HttpResponse<any>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/tasks/${task.id}`;
 | 
			
		||||
    return this.httpClient.put(url, task, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public postTask(task: ScrumTask): Observable<HttpResponse<ScrumTask>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/tasks`;
 | 
			
		||||
        return this.httpClient.post<ScrumTask>(url, task, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public deleteTask(task: ScrumTask): Observable<HttpResponse<any>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/tasks/${task.id}`;
 | 
			
		||||
    return this.httpClient.delete(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public putTask(task: ScrumTask): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/tasks/${task.id}`;
 | 
			
		||||
        return this.httpClient.put(url, task, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  // Userstories
 | 
			
		||||
  public getUserstories(): Observable<HttpResponse<ScrumUserstory[]>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/userstories`;
 | 
			
		||||
    return this.httpClient.get<ScrumUserstory[]>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public deleteTask(task: ScrumTask): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/tasks/${task.id}`;
 | 
			
		||||
        return this.httpClient.delete(url, {observe: 'response'});
 | 
			
		||||
    }
 | 
			
		||||
  public getUserstory(id: number): Observable<HttpResponse<ScrumUserstory>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/userstories/${id}`;
 | 
			
		||||
    return this.httpClient.get<ScrumUserstory>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public postUserstory(
 | 
			
		||||
    userstory: ScrumUserstory
 | 
			
		||||
  ): Observable<HttpResponse<ScrumUserstory>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/userstories`;
 | 
			
		||||
    return this.httpClient.post<ScrumUserstory>(url, userstory, {
 | 
			
		||||
      observe: 'response',
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    // Userstories
 | 
			
		||||
    public getUserstories(): Observable<HttpResponse<ScrumUserstory[]>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/userstories`;
 | 
			
		||||
        return this.httpClient.get<ScrumUserstory[]>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public putUserstory(
 | 
			
		||||
    userstory: ScrumUserstory
 | 
			
		||||
  ): Observable<HttpResponse<any>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/userstories/${userstory.id}`;
 | 
			
		||||
    return this.httpClient.put(url, userstory, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public getUserstory(id: number): Observable<HttpResponse<ScrumUserstory>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/userstories/${id}`;
 | 
			
		||||
        return this.httpClient.get<ScrumUserstory>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public deleteUserstory(
 | 
			
		||||
    userstory: ScrumUserstory
 | 
			
		||||
  ): Observable<HttpResponse<any>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/userstories/${userstory.id}`;
 | 
			
		||||
    return this.httpClient.delete(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public postUserstory(userstory: ScrumUserstory): Observable<HttpResponse<ScrumUserstory>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/userstories`;
 | 
			
		||||
        return this.httpClient.post<ScrumUserstory>(url, userstory, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  // Sprints
 | 
			
		||||
  public getSprints(): Observable<HttpResponse<ScrumSprint[]>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/sprints`;
 | 
			
		||||
    return this.httpClient.get<ScrumSprint[]>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public putUserstory(userstory: ScrumUserstory): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/userstories/${userstory.id}`;
 | 
			
		||||
        return this.httpClient.put(url, userstory, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public getSprint(id: number): Observable<HttpResponse<ScrumSprint>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/sprints/${id}`;
 | 
			
		||||
    return this.httpClient.get<ScrumSprint>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public deleteUserstory(userstory: ScrumUserstory): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/userstories/${userstory.id}`;
 | 
			
		||||
        return this.httpClient.delete(url, {observe: 'response'});
 | 
			
		||||
    }
 | 
			
		||||
  public postSprint(
 | 
			
		||||
    sprint: ScrumSprint
 | 
			
		||||
  ): Observable<HttpResponse<ScrumSprint>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/sprints`;
 | 
			
		||||
    return this.httpClient.post<ScrumSprint>(url, sprint, {
 | 
			
		||||
      observe: 'response',
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public putSprint(sprint: ScrumSprint): Observable<HttpResponse<any>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/sprints/${sprint.id}`;
 | 
			
		||||
    return this.httpClient.put(url, sprint, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    // Sprints
 | 
			
		||||
    public getSprints(): Observable<HttpResponse<ScrumSprint[]>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/sprints`;
 | 
			
		||||
        return this.httpClient.get<ScrumSprint[]>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public deleteSprint(sprint: ScrumSprint): Observable<HttpResponse<any>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/sprints/${sprint.id}`;
 | 
			
		||||
    return this.httpClient.delete(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public getSprint(id: number): Observable<HttpResponse<ScrumSprint>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/sprints/${id}`;
 | 
			
		||||
        return this.httpClient.get<ScrumSprint>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  // Categories
 | 
			
		||||
  public getCategories(): Observable<HttpResponse<ScrumCategory[]>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/categories`;
 | 
			
		||||
    return this.httpClient.get<ScrumCategory[]>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public postSprint(sprint: ScrumSprint): Observable<HttpResponse<ScrumSprint>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/sprints`;
 | 
			
		||||
        return this.httpClient.post<ScrumSprint>(url, sprint, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public getCategory(id: number): Observable<HttpResponse<ScrumCategory>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/categories/${id}`;
 | 
			
		||||
    return this.httpClient.get<ScrumCategory>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public putSprint(sprint: ScrumSprint): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/sprints/${sprint.id}`;
 | 
			
		||||
        return this.httpClient.put(url, sprint, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public postCategory(
 | 
			
		||||
    category: ScrumCategory
 | 
			
		||||
  ): Observable<HttpResponse<ScrumCategory>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/categories`;
 | 
			
		||||
    return this.httpClient.post<ScrumCategory>(url, category, {
 | 
			
		||||
      observe: 'response',
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public deleteSprint(sprint: ScrumSprint): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/sprints/${sprint.id}`;
 | 
			
		||||
        return this.httpClient.delete(url, {observe: 'response'});
 | 
			
		||||
    }
 | 
			
		||||
  public putCategory(category: ScrumCategory): Observable<HttpResponse<any>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/categories/${category.id}`;
 | 
			
		||||
    return this.httpClient.put(url, category, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public deleteCategory(
 | 
			
		||||
    category: ScrumCategory
 | 
			
		||||
  ): Observable<HttpResponse<any>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/categories/${category.id}`;
 | 
			
		||||
    return this.httpClient.delete(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    // Categories
 | 
			
		||||
    public getCategories(): Observable<HttpResponse<ScrumCategory[]>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/categories`;
 | 
			
		||||
        return this.httpClient.get<ScrumCategory[]>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  // Status
 | 
			
		||||
  public getAllStatus(): Observable<HttpResponse<ScrumStatus[]>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/status`;
 | 
			
		||||
    return this.httpClient.get<ScrumStatus[]>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public getCategory(id: number): Observable<HttpResponse<ScrumCategory>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/categories/${id}`;
 | 
			
		||||
        return this.httpClient.get<ScrumCategory>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public getStatus(id: number): Observable<HttpResponse<ScrumStatus>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/status/${id}`;
 | 
			
		||||
    return this.httpClient.get<ScrumStatus>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public postCategory(category: ScrumCategory): Observable<HttpResponse<ScrumCategory>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/categories`;
 | 
			
		||||
        return this.httpClient.post<ScrumCategory>(url, category, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public postStatus(
 | 
			
		||||
    status: ScrumStatus
 | 
			
		||||
  ): Observable<HttpResponse<ScrumStatus>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/status`;
 | 
			
		||||
    return this.httpClient.post<ScrumStatus>(url, status, {
 | 
			
		||||
      observe: 'response',
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public putCategory(category: ScrumCategory): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/categories/${category.id}`;
 | 
			
		||||
        return this.httpClient.put(url, category, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public putStatus(status: ScrumStatus): Observable<HttpResponse<any>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/status/${status.id}`;
 | 
			
		||||
    return this.httpClient.put(url, status, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public deleteCategory(category: ScrumCategory): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/categories/${category.id}`;
 | 
			
		||||
        return this.httpClient.delete(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public deleteStatus(status: ScrumStatus): Observable<HttpResponse<any>> {
 | 
			
		||||
    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`;
 | 
			
		||||
    return this.httpClient.get<ScrumUser[]>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    // Status
 | 
			
		||||
    public getAllStatus(): Observable<HttpResponse<ScrumStatus[]>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/status`;
 | 
			
		||||
        return this.httpClient.get<ScrumStatus[]>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public getUser(id: number): Observable<HttpResponse<ScrumUser>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/users/${id}`;
 | 
			
		||||
    return this.httpClient.get<ScrumUser>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public getStatus(id: number): Observable<HttpResponse<ScrumStatus>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/status/${id}`;
 | 
			
		||||
        return this.httpClient.get<ScrumStatus>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public postUser(user: ScrumUser): Observable<HttpResponse<ScrumUser>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/users`;
 | 
			
		||||
    return this.httpClient.post<ScrumUser>(url, user, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public postStatus(status: ScrumStatus): Observable<HttpResponse<ScrumStatus>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/status`;
 | 
			
		||||
        return this.httpClient.post<ScrumStatus>(url, status, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public putUser(user: ScrumUser): Observable<HttpResponse<any>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/users/${user.id}`;
 | 
			
		||||
    return this.httpClient.put(url, user, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public putStatus(status: ScrumStatus): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/status/${status.id}`;
 | 
			
		||||
        return this.httpClient.put(url, status, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public deleteUser(user: ScrumUser): Observable<HttpResponse<any>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/users/${user.id}`;
 | 
			
		||||
    return this.httpClient.delete(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public deleteStatus(status: ScrumStatus): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/status/${status.id}`;
 | 
			
		||||
        return this.httpClient.delete(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  // Projects
 | 
			
		||||
  public getProjects(): Observable<HttpResponse<ScrumProject[]>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/projects`;
 | 
			
		||||
    return this.httpClient.get<ScrumProject[]>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getProject(id: number): Observable<HttpResponse<ScrumProject>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/projects/${id}`;
 | 
			
		||||
    return this.httpClient.get<ScrumProject>(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    // Users
 | 
			
		||||
    public getUsers(): Observable<HttpResponse<ScrumUser[]>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/users`;
 | 
			
		||||
        return this.httpClient.get<ScrumUser[]>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public postProject(
 | 
			
		||||
    project: ScrumProject
 | 
			
		||||
  ): Observable<HttpResponse<ScrumProject>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/projects`;
 | 
			
		||||
    return this.httpClient.post<ScrumProject>(url, project, {
 | 
			
		||||
      observe: 'response',
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public getUser(id: number): Observable<HttpResponse<ScrumUser>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/users/${id}`;
 | 
			
		||||
        return this.httpClient.get<ScrumUser>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public putProject(project: ScrumProject): Observable<HttpResponse<any>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/projects/${project.id}`;
 | 
			
		||||
    return this.httpClient.put(url, project, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    public postUser(user: ScrumUser): Observable<HttpResponse<ScrumUser>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/users`;
 | 
			
		||||
        return this.httpClient.post<ScrumUser>(url, user, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public putUser(user: ScrumUser): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/users/${user.id}`;
 | 
			
		||||
        return this.httpClient.put(url, user, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public deleteUser(user: ScrumUser): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/users/${user.id}`;
 | 
			
		||||
        return this.httpClient.delete(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Projects
 | 
			
		||||
    public getProjects(): Observable<HttpResponse<ScrumProject[]>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/projects`;
 | 
			
		||||
        return this.httpClient.get<ScrumProject[]>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public getProject(id: number): Observable<HttpResponse<ScrumProject>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/projects/${id}`;
 | 
			
		||||
        return this.httpClient.get<ScrumProject>(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public postProject(project: ScrumProject): Observable<HttpResponse<ScrumProject>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/projects`;
 | 
			
		||||
        return this.httpClient.post<ScrumProject>(url, project, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public putProject(project: ScrumProject): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/projects/${project.id}`;
 | 
			
		||||
        return this.httpClient.put(url, project, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public deleteProject(project: ScrumProject): Observable<HttpResponse<any>> {
 | 
			
		||||
        const url = `${environment.apiUrl}/projects/${project.id}`;
 | 
			
		||||
        return this.httpClient.delete(url, { observe: 'response' });
 | 
			
		||||
    }
 | 
			
		||||
  public deleteProject(project: ScrumProject): Observable<HttpResponse<any>> {
 | 
			
		||||
    const url = `${environment.apiUrl}/projects/${project.id}`;
 | 
			
		||||
    return this.httpClient.delete(url, { observe: 'response' });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum Priority {
 | 
			
		||||
    High="high",
 | 
			
		||||
    Medium="medium",
 | 
			
		||||
    Low="low"
 | 
			
		||||
  High = 'high',
 | 
			
		||||
  Medium = 'medium',
 | 
			
		||||
  Low = 'low',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ScrumTask {
 | 
			
		||||
    id?: number;
 | 
			
		||||
    title: string;
 | 
			
		||||
    content?: string;
 | 
			
		||||
    statusid?: number;
 | 
			
		||||
    categoryid?: number;
 | 
			
		||||
    assignedtoid?: number;
 | 
			
		||||
    sprintid?: number;
 | 
			
		||||
    projectid?: number;
 | 
			
		||||
    userstoryid?: number;
 | 
			
		||||
    priority?: Priority;
 | 
			
		||||
  id?: number;
 | 
			
		||||
  title: string;
 | 
			
		||||
  content?: string;
 | 
			
		||||
  statusid?: number;
 | 
			
		||||
  categoryid?: number;
 | 
			
		||||
  assignedtoid?: number;
 | 
			
		||||
  sprintid?: number;
 | 
			
		||||
  projectid?: number;
 | 
			
		||||
  userstoryid?: number;
 | 
			
		||||
  priority?: Priority;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ScrumUserstory {
 | 
			
		||||
    id?: number;
 | 
			
		||||
    title: string;
 | 
			
		||||
    content?: string;
 | 
			
		||||
    priority?: Priority;
 | 
			
		||||
    statusid?: number;
 | 
			
		||||
    categoryid?: number;
 | 
			
		||||
    createdbyid?: number;
 | 
			
		||||
    projectid?: number;
 | 
			
		||||
  id?: number;
 | 
			
		||||
  title: string;
 | 
			
		||||
  content?: string;
 | 
			
		||||
  priority?: Priority;
 | 
			
		||||
  statusid?: number;
 | 
			
		||||
  categoryid?: number;
 | 
			
		||||
  sprintid?: number;
 | 
			
		||||
  createdbyid?: number;
 | 
			
		||||
  projectid?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ScrumSprint{
 | 
			
		||||
    id?: number;
 | 
			
		||||
    title: string;
 | 
			
		||||
    description?: string;
 | 
			
		||||
    startDate: Date;
 | 
			
		||||
    endDate: Date;
 | 
			
		||||
    project: number;
 | 
			
		||||
    startDate: string;
 | 
			
		||||
    endDate: string;
 | 
			
		||||
    projectid?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ScrumCategory {
 | 
			
		||||
    id?: number;
 | 
			
		||||
    title: string;
 | 
			
		||||
    description?: string;
 | 
			
		||||
    color?: string;
 | 
			
		||||
    project: number;
 | 
			
		||||
  id?: number;
 | 
			
		||||
  title: string;
 | 
			
		||||
  description?: string;
 | 
			
		||||
  color?: string;
 | 
			
		||||
  project?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ScrumStatus {
 | 
			
		||||
    id? : number;
 | 
			
		||||
    title: string;
 | 
			
		||||
    description: string;
 | 
			
		||||
  id?: number;
 | 
			
		||||
  title: string;
 | 
			
		||||
  description: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export interface ScrumUser {
 | 
			
		||||
    id?: number;
 | 
			
		||||
    name: string;
 | 
			
		||||
  id?: number;
 | 
			
		||||
  name: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ScrumProject {
 | 
			
		||||
    id?: number;
 | 
			
		||||
    title: string;
 | 
			
		||||
    isprivate: boolean;
 | 
			
		||||
  id?: number;
 | 
			
		||||
  title: string;
 | 
			
		||||
  isprivate: boolean;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
    <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>
 | 
			
		||||
@@ -1,62 +0,0 @@
 | 
			
		||||
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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
th.sortable:hover {
 | 
			
		||||
    text-decoration: underline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.content {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    float: left;
 | 
			
		||||
    margin-top: 10px;
 | 
			
		||||
    margin-left: 20px;
 | 
			
		||||
    width: 80%;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
.modal-footer {
 | 
			
		||||
	border-top: 0px solid;
 | 
			
		||||
	padding-top: 5%;
 | 
			
		||||
}
 | 
			
		||||
.modal-content {
 | 
			
		||||
	width: 1040px;
 | 
			
		||||
	right: 55%;
 | 
			
		||||
	border: 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.modal {
 | 
			
		||||
	margin: 0 auto;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,68 +0,0 @@
 | 
			
		||||
<div class="modal-content p-3">
 | 
			
		||||
    <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>
 | 
			
		||||
    <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>
 | 
			
		||||
        </form>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
import { Component, OnInit, Input } from '@angular/core';
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 | 
			
		||||
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'],
 | 
			
		||||
})
 | 
			
		||||
export class TaskFormComponent implements OnInit {
 | 
			
		||||
  @Input() public task: ScrumTask;
 | 
			
		||||
  public editing: Boolean;
 | 
			
		||||
  public userstoryId: string;
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
    }
 | 
			
		||||
    document.getElementById('titleField').focus();
 | 
			
		||||
    this.getRelatedStory();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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');
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    this.activeModalService.close(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;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
th.sortable:hover {
 | 
			
		||||
  text-decoration: underline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.content {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  float: left;
 | 
			
		||||
  margin-top: 10px;
 | 
			
		||||
  margin-left: 20px;
 | 
			
		||||
  width: 80%;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
.modal-footer {
 | 
			
		||||
	border-top: 0px solid;
 | 
			
		||||
	padding-top: 5%;
 | 
			
		||||
}
 | 
			
		||||
.modal-content {
 | 
			
		||||
	width: 1040px;
 | 
			
		||||
	right: 55%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.modal {
 | 
			
		||||
	margin: 0 auto;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,63 +0,0 @@
 | 
			
		||||
<!-- <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>
 | 
			
		||||
<!-- </div> -->
 | 
			
		||||
@@ -1,49 +0,0 @@
 | 
			
		||||
import { Component, OnInit, Input } from '@angular/core';
 | 
			
		||||
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' ]
 | 
			
		||||
})
 | 
			
		||||
export class UserstoryFormComponent implements OnInit {
 | 
			
		||||
	@Input() public userstory: ScrumUserstory;
 | 
			
		||||
	private editing: boolean;
 | 
			
		||||
 | 
			
		||||
	constructor(private backendService: BackendService, private activeModalService: NgbActiveModal) {}
 | 
			
		||||
 | 
			
		||||
	ngOnInit(): void {
 | 
			
		||||
		if (this.userstory === null || this.userstory === undefined) {
 | 
			
		||||
			this.userstory = { title: '' };
 | 
			
		||||
			this.editing = false;
 | 
			
		||||
		} else {
 | 
			
		||||
			this.editing = true;
 | 
			
		||||
		}
 | 
			
		||||
		document.getElementById('titleField').focus();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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();
 | 
			
		||||
	//}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,107 +0,0 @@
 | 
			
		||||
<div class="container-fluid">
 | 
			
		||||
  <div class="content">
 | 
			
		||||
    <h3>Userstories</h3>
 | 
			
		||||
 | 
			
		||||
    <button class="btn btn-secondary" (click)="openUserstoryForm()">Neue Userstory</button>
 | 
			
		||||
 | 
			
		||||
    <table class="table">
 | 
			
		||||
 | 
			
		||||
      <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
          <th (click)="sortById()" class="sortable">
 | 
			
		||||
            <span>ID</span>
 | 
			
		||||
            <span>
 | 
			
		||||
              <span *ngIf="sortBy != 'id'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending && sortBy === 'id'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending === false && sortBy === 'id'"><i class="fa fa-sort-down fa-lg"></i></span>
 | 
			
		||||
            </span>
 | 
			
		||||
          </th>
 | 
			
		||||
          <th (click)="sortByTitle()" class="sortable">
 | 
			
		||||
            <span>Titel</span>
 | 
			
		||||
            <span>
 | 
			
		||||
              <span *ngIf="sortBy != 'title'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending && sortBy === 'title'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending === false && sortBy === 'title'"><i class="fa fa-sort-down fa-lg"></i></span>
 | 
			
		||||
            </span>
 | 
			
		||||
          </th>
 | 
			
		||||
          <th (click)="sortByTasks()" class="sortable">
 | 
			
		||||
            <span>Tasks</span>
 | 
			
		||||
            <span>
 | 
			
		||||
              <span *ngIf="sortBy != 'tasks'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending && sortBy === 'tasks'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending === false && sortBy === 'tasks'"><i class="fa fa-sort-down fa-lg"></i></span>
 | 
			
		||||
            </span>
 | 
			
		||||
          </th>
 | 
			
		||||
          <th (click)="sortByStatus()" class="sortable">
 | 
			
		||||
            <span>Status</span>
 | 
			
		||||
            <span>
 | 
			
		||||
              <span *ngIf="sortBy != 'statusid'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending && sortBy === 'statusid'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending === false && sortBy === 'statusid'"><i class="fa fa-sort-down fa-lg"></i></span>
 | 
			
		||||
            </span>
 | 
			
		||||
          </th>
 | 
			
		||||
          <th class="sortable">
 | 
			
		||||
            <div class="d-inline-block">
 | 
			
		||||
              <div class="d-inline-block">
 | 
			
		||||
                <span (click)="sortByPrio()">Priorität: </span>
 | 
			
		||||
                <div ngbDropdown class="d-inline-block">
 | 
			
		||||
                  <span id="dropdownBasic1" ngbDropdownToggle>{{filterPriority || "All"}}</span>
 | 
			
		||||
                  <div ngbDropdownMenu aria-labelledby="dropdownBasic1">
 | 
			
		||||
                    <option ngbDropdownItem (click)="filterPriority=null">All</option>
 | 
			
		||||
                    <option ngbDropdownItem *ngFor="let p of getAllPriorities()" (click)="filterPriority=p">{{p}}</option>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <span (click)="sortByPrio()">
 | 
			
		||||
                  <span *ngIf="sortBy != 'priority'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
                  <span *ngIf="sortDescending && sortBy === 'priority'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
                  <span *ngIf="sortDescending === false && sortBy === 'priority'"><i class="fa fa-sort-down fa-lg"></i></span>
 | 
			
		||||
                </span>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </th>
 | 
			
		||||
          <th (click)="sortByCategory()" class="sortable">
 | 
			
		||||
            <span>Category</span>
 | 
			
		||||
            <span>
 | 
			
		||||
              <span *ngIf="sortBy != 'categoryid'"><i class="fa fa-sort fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending && sortBy === 'categoryid'"><i class="fa fa-sort-up fa-lg"></i></span>
 | 
			
		||||
              <span *ngIf="sortDescending === false && sortBy === 'categoryid'"><i class="fa fa-sort-down fa-lg"></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>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -1 +1,9 @@
 | 
			
		||||
/* You can add global styles to this file, and also import other style files */
 | 
			
		||||
.custom-text-secondary {
 | 
			
		||||
    color: white;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  .white-content .custom-text-secondary {
 | 
			
		||||
    color: black !important;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
@@ -1,11 +1,17 @@
 | 
			
		||||
th.sortable:hover {
 | 
			
		||||
  text-decoration: underline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.content {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  float: left;
 | 
			
		||||
  margin-top: 10px;
 | 
			
		||||
  margin-left: 20px;
 | 
			
		||||
  width: 80%;
 | 
			
		||||
}
 | 
			
		||||
@import "~bootstrap/scss/bootstrap";
 | 
			
		||||
 | 
			
		||||
.modal .modal-content {
 | 
			
		||||
	display: contents;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
th.sortable:hover {
 | 
			
		||||
  text-decoration: underline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.content {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  float: left;
 | 
			
		||||
  margin-top: 10px;
 | 
			
		||||
  margin-left: 20px;
 | 
			
		||||
  width: 80%;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user