Prettified all the things
This commit is contained in:
parent
073b621494
commit
ac0b901c35
|
@ -0,0 +1,49 @@
|
|||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
dist
|
||||
tmp
|
||||
out-tsc
|
||||
assets
|
||||
src\assets
|
||||
# Only exists if Bazel was run
|
||||
bazel-out
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events*.json
|
||||
speed-measure-plugin*.json
|
||||
|
||||
# IDEs and editors
|
||||
.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
package-lock.json
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode
|
||||
|
||||
# misc
|
||||
.sass-cache
|
||||
connect.lock
|
||||
coverage
|
||||
libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Other ignore files
|
||||
.gitignore
|
||||
.dockerignore
|
||||
.gitlab-ci.yml
|
16
README.md
16
README.md
|
@ -3,6 +3,7 @@
|
|||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.1.7.
|
||||
|
||||
## Angular CLI
|
||||
|
||||
### Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||
|
@ -28,27 +29,32 @@ Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protrac
|
|||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
|
||||
## Deploy with Docker
|
||||
|
||||
> Prerequisite: Docker and/or Docker Compose have to be installed on your system </br>
|
||||
> The Docker installation instructions for most operation systems can be found here: [https://docs.docker.com/get-docker/](https://docs.docker.com/get-docker/) </br>
|
||||
> The Docker Compose installation instructions for most operation systems can be found here: [https://docs.docker.com/compose/install/](https://docs.docker.com/compose/install/)
|
||||
> To use the prebuild images log into the private registry: docker login -u testuser -p LpH2v1mShPpC8Xeimkd2AISA03zaC+vq scrumdev.azurecr.io
|
||||
|
||||
### Quick Deployment with Docker Compose
|
||||
|
||||
1. Clone the git repo
|
||||
2. Open the cloned repo's folder in a shell of your choice (as long as it supports Docker)
|
||||
3. Tell Docker Compose to run the app alongside a database with `docker-compose up`
|
||||
|
||||
Other useful commands:
|
||||
* Run attached (in the background): `docker-compose up -d`
|
||||
* Stop: `docker-compose down`
|
||||
* Restart: `docker-compose restart`
|
||||
|
||||
- Run attached (in the background): `docker-compose up -d`
|
||||
- Stop: `docker-compose down`
|
||||
- Restart: `docker-compose restart`
|
||||
|
||||
### Build the Container yourself (and run it)
|
||||
|
||||
1. Clone the git repo
|
||||
2. Open the cloned repo's folder in a shell of your choice (as long as it supports Docker)
|
||||
3. Tell Docker to build the app under the name "taskboard/frontend" with `docker build -t taskboard/frontend .`
|
||||
4. Tell Docker to run the app on port 8080 with `docker run -p 8080:80 taskboard/frontend`
|
||||
|
||||
Other useful commands:
|
||||
* Run detached: `docker run -d -p 8080:80 taskboard/frontend`
|
||||
* Run under specified name: `docker run -p 8080:80 --name frontend taskboard/frontend`
|
||||
|
||||
- Run detached: `docker run -d -p 8080:80 taskboard/frontend`
|
||||
- Run under specified name: `docker run -p 8080:80 --name frontend taskboard/frontend`
|
||||
|
|
23
angular.json
23
angular.json
|
@ -20,17 +20,13 @@
|
|||
"tsConfig": "tsconfig.app.json",
|
||||
"preserveSymlinks": true,
|
||||
"aot": true,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"assets": ["src/favicon.ico", "src/assets"],
|
||||
"styles": [
|
||||
"node_modules/bootstrap/dist/css/bootstrap.min.css",
|
||||
"src/styles.scss",
|
||||
"src/assets/scss/black-dashboard.scss"
|
||||
],
|
||||
"scripts": [
|
||||
]
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
|
@ -87,13 +83,8 @@
|
|||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"assets": ["src/favicon.ico", "src/assets"],
|
||||
"styles": ["src/styles.scss"],
|
||||
"scripts": []
|
||||
}
|
||||
},
|
||||
|
@ -105,9 +96,7 @@
|
|||
"tsconfig.spec.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
|
@ -126,4 +115,4 @@
|
|||
}
|
||||
},
|
||||
"defaultProject": "frontend"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: scrumdev.azurecr.io/taskboard/frontend:latest
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- default
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
API_URL: http://localhost:5001
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: scrumdev.azurecr.io/taskboard/frontend:latest
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- default
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
API_URL: http://localhost:5001
|
||||
|
|
|
@ -2,31 +2,31 @@
|
|||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
const { SpecReporter } = require("jasmine-spec-reporter");
|
||||
|
||||
/**
|
||||
* @type { import("protractor").Config }
|
||||
*/
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
specs: ["./src/**/*.e2e-spec.ts"],
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
browserName: "chrome",
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
baseUrl: "http://localhost:4200/",
|
||||
framework: "jasmine",
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function() {}
|
||||
print: function () {},
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.json')
|
||||
require("ts-node").register({
|
||||
project: require("path").join(__dirname, "./tsconfig.json"),
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
}
|
||||
};
|
||||
jasmine
|
||||
.getEnv()
|
||||
.addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
},
|
||||
};
|
||||
|
|
|
@ -16,8 +16,10 @@ describe('workspace-project App', () => {
|
|||
afterEach(async () => {
|
||||
// Assert that there are no errors emitted from the browser
|
||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||
expect(logs).not.toContain(jasmine.objectContaining({
|
||||
level: logging.Level.SEVERE,
|
||||
} as logging.Entry));
|
||||
expect(logs).not.toContain(
|
||||
jasmine.objectContaining({
|
||||
level: logging.Level.SEVERE,
|
||||
} as logging.Entry)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,6 +6,8 @@ export class AppPage {
|
|||
}
|
||||
|
||||
getTitleText(): Promise<string> {
|
||||
return element(by.css('app-root .content span')).getText() as Promise<string>;
|
||||
return element(by.css('app-root .content span')).getText() as Promise<
|
||||
string
|
||||
>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,6 @@
|
|||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
"node"
|
||||
]
|
||||
"types": ["jasmine", "jasminewd2", "node"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,30 +3,30 @@
|
|||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
basePath: "",
|
||||
frameworks: ["jasmine", "@angular-devkit/build-angular"],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
require("karma-jasmine"),
|
||||
require("karma-chrome-launcher"),
|
||||
require("karma-jasmine-html-reporter"),
|
||||
require("karma-coverage-istanbul-reporter"),
|
||||
require("@angular-devkit/build-angular/plugins/karma"),
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/frontend'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
dir: require("path").join(__dirname, "./coverage/frontend"),
|
||||
reports: ["html", "lcovonly", "text-summary"],
|
||||
fixWebpackSourcePaths: true,
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
reporters: ["progress", "kjhtml"],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
browsers: ["Chrome"],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
restartOnFileChange: true,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ import { DashboardComponent } from './components/dashboard/dashboard.component';
|
|||
import { UserstoryTableComponent } from './components/tabels/userstory/userstory-table.component';
|
||||
import { TaskTableComponent } from './components/tabels/task/task-table.component';
|
||||
import { SprintTableComponent } from './components/tabels/sprint/sprint-table.component';
|
||||
import {BacklogComponent} from './components/backlog/backlog.component';
|
||||
import { BacklogComponent } from './components/backlog/backlog.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'tasks', component: TaskTableComponent },
|
||||
|
|
|
@ -8,15 +8,15 @@
|
|||
}
|
||||
|
||||
.nav a {
|
||||
font-size: 1em;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.nav a:hover:not(.active) {
|
||||
font-size: 1.15em;
|
||||
}
|
||||
font-size: 1.15em;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
float: right;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
float: right;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
|
|
@ -1,80 +1,87 @@
|
|||
<div class="container-fluid">
|
||||
|
||||
<div class="sidebar">
|
||||
<div class="logo">
|
||||
<a class="simple-text logo-normal">
|
||||
Taskboard
|
||||
</a>
|
||||
</div>
|
||||
<ul class="nav">
|
||||
<li class="active ">
|
||||
<a [routerLink]="['dashboard']">
|
||||
<p>Dashboard</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a [routerLink]="['backlog']">
|
||||
<p>Backlog</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a [routerLink]="['sprints']">
|
||||
<p>Sprints</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a [routerLink]="['userstories']">
|
||||
<p>Userstories</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a [routerLink]="['tasks']">
|
||||
<p>Tasks</p>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<div class="content">
|
||||
<div class=" fixed-plugin">
|
||||
<div class=" show-dropdown" ngbDropdown>
|
||||
<a data-toggle="dropdown" ngbDropdownToggle>
|
||||
<i class="fa fa-cog fa-2x"></i>
|
||||
</a>
|
||||
<ul ngbDropdownMenu>
|
||||
<!--
|
||||
<li class=" header-title">Sidebar Background</li>
|
||||
<li class=" adjustments-line">
|
||||
<a class=" switch-trigger background-color" href="javascript:void(0)">
|
||||
<div class=" badge-colors text-center">
|
||||
<span
|
||||
class=" badge filter badge-danger"
|
||||
[ngClass]="{'active':sidebarColor==='red'}" (click)="changeSidebarColor('red')"
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class=" badge filter badge-primary"
|
||||
[ngClass]="{'active':sidebarColor==='primary'}" (click)="changeSidebarColor('primary')"
|
||||
>
|
||||
</span>
|
||||
<span class=" badge filter badge-info" [ngClass]="{'active':sidebarColor==='blue'}" (click)="changeSidebarColor('blue')"> </span>
|
||||
<span class=" badge filter badge-success" [ngClass]="{'active':sidebarColor==='green'}" (click)="changeSidebarColor('green')">
|
||||
</span>
|
||||
</div>
|
||||
<div class=" clearfix"></div>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
<li class=" adjustments-line text-center color-change">
|
||||
<span class=" color-label"> LIGHT MODE </span>
|
||||
<span class=" badge light-badge mr-2" (click)="changeDashboardColor('white-content')"> </span>
|
||||
<span class=" badge dark-badge ml-2" (click)="changeDashboardColor('black-content')"> </span>
|
||||
<span class=" color-label"> DARK MODE </span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
<div class="sidebar">
|
||||
<div class="logo">
|
||||
<a class="simple-text logo-normal">
|
||||
Taskboard
|
||||
</a>
|
||||
</div>
|
||||
<ul class="nav">
|
||||
<li class="active">
|
||||
<a [routerLink]="['dashboard']">
|
||||
<p>Dashboard</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a [routerLink]="['backlog']">
|
||||
<p>Backlog</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a [routerLink]="['sprints']">
|
||||
<p>Sprints</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a [routerLink]="['userstories']">
|
||||
<p>Userstories</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a [routerLink]="['tasks']">
|
||||
<p>Tasks</p>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<div class="content">
|
||||
<div class="fixed-plugin">
|
||||
<div class="show-dropdown" ngbDropdown>
|
||||
<a data-toggle="dropdown" ngbDropdownToggle>
|
||||
<i class="fa fa-cog fa-2x"></i>
|
||||
</a>
|
||||
<ul ngbDropdownMenu>
|
||||
<!--
|
||||
<li class=" header-title">Sidebar Background</li>
|
||||
<li class=" adjustments-line">
|
||||
<a class=" switch-trigger background-color" href="javascript:void(0)">
|
||||
<div class=" badge-colors text-center">
|
||||
<span
|
||||
class=" badge filter badge-danger"
|
||||
[ngClass]="{'active':sidebarColor==='red'}" (click)="changeSidebarColor('red')"
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class=" badge filter badge-primary"
|
||||
[ngClass]="{'active':sidebarColor==='primary'}" (click)="changeSidebarColor('primary')"
|
||||
>
|
||||
</span>
|
||||
<span class=" badge filter badge-info" [ngClass]="{'active':sidebarColor==='blue'}" (click)="changeSidebarColor('blue')"> </span>
|
||||
<span class=" badge filter badge-success" [ngClass]="{'active':sidebarColor==='green'}" (click)="changeSidebarColor('green')">
|
||||
</span>
|
||||
</div>
|
||||
<div class=" clearfix"></div>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
<li class="adjustments-line text-center color-change">
|
||||
<span class="color-label"> LIGHT MODE </span>
|
||||
<span
|
||||
class="badge light-badge mr-2"
|
||||
(click)="changeDashboardColor('white-content')"
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
class="badge dark-badge ml-2"
|
||||
(click)="changeDashboardColor('black-content')"
|
||||
>
|
||||
</span>
|
||||
<span class="color-label"> DARK MODE </span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,12 +5,8 @@ import { AppComponent } from './app.component';
|
|||
describe('AppComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
imports: [RouterTestingModule],
|
||||
declarations: [AppComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
|
@ -30,6 +26,8 @@ describe('AppComponent', () => {
|
|||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('.content span').textContent).toContain('frontend app is running!');
|
||||
expect(compiled.querySelector('.content span').textContent).toContain(
|
||||
'frontend app is running!'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Component } from '@angular/core';
|
|||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
styleUrls: ['./app.component.css'],
|
||||
})
|
||||
export class AppComponent {
|
||||
/*
|
||||
|
@ -21,12 +21,11 @@ export class AppComponent {
|
|||
}
|
||||
}
|
||||
*/
|
||||
changeDashboardColor(color){
|
||||
changeDashboardColor(color) {
|
||||
var body = document.getElementsByTagName('body')[0];
|
||||
if (body && color === 'white-content') {
|
||||
body.classList.add(color);
|
||||
}
|
||||
else if(body.classList.contains('white-content')) {
|
||||
body.classList.add(color);
|
||||
} else if (body.classList.contains('white-content')) {
|
||||
body.classList.remove('white-content');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,45 +1,45 @@
|
|||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
import { BackendService } from './services/backend.service';
|
||||
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 './components/tabels/userstory/userstory-table.component';
|
||||
import { TaskTableComponent } from './components/tabels/task/task-table.component';
|
||||
import { SprintTableComponent } from './components/tabels/sprint/sprint-table.component';
|
||||
import { DashboardComponent } from './components/dashboard/dashboard.component';
|
||||
import { UserstoryInnerTableComponent } from './components/tabels/userstory-inner-table/userstory-inner-table.component';
|
||||
import { BacklogComponent } from './components/backlog/backlog.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
TaskTableComponent,
|
||||
TaskFormComponent,
|
||||
UserstoryTableComponent,
|
||||
UserstoryFormComponent,
|
||||
UserstoryTableComponent,
|
||||
SprintFormComponent,
|
||||
SprintTableComponent,
|
||||
DashboardComponent,
|
||||
UserstoryInnerTableComponent,
|
||||
BacklogComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
HttpClientModule,
|
||||
FormsModule,
|
||||
NgbModule,
|
||||
],
|
||||
providers: [BackendService],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
import { BackendService } from './services/backend.service';
|
||||
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 './components/tabels/userstory/userstory-table.component';
|
||||
import { TaskTableComponent } from './components/tabels/task/task-table.component';
|
||||
import { SprintTableComponent } from './components/tabels/sprint/sprint-table.component';
|
||||
import { DashboardComponent } from './components/dashboard/dashboard.component';
|
||||
import { UserstoryInnerTableComponent } from './components/tabels/userstory-inner-table/userstory-inner-table.component';
|
||||
import { BacklogComponent } from './components/backlog/backlog.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
TaskTableComponent,
|
||||
TaskFormComponent,
|
||||
UserstoryTableComponent,
|
||||
UserstoryFormComponent,
|
||||
UserstoryTableComponent,
|
||||
SprintFormComponent,
|
||||
SprintTableComponent,
|
||||
DashboardComponent,
|
||||
UserstoryInnerTableComponent,
|
||||
BacklogComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
HttpClientModule,
|
||||
FormsModule,
|
||||
NgbModule,
|
||||
],
|
||||
providers: [BackendService],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
th.sortable:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,97 +1,134 @@
|
|||
<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">
|
||||
<h3>Backlog</h3>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="row px-3 py-2">
|
||||
<ng-container *ngIf="selectedSprint === undefined">
|
||||
<h3>
|
||||
Aktuell läuft kein Sprint.
|
||||
<a [routerLink]="['../sprints']">Zur Sprint Übersicht</a>
|
||||
</h3>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedSprint != undefined">
|
||||
<h3 *ngIf="selectedSprint === currentSprint">Aktueller Sprint:</h3>
|
||||
<h3 *ngIf="selectedSprint !== currentSprint">Sprint:</h3>
|
||||
<div ngbDropdown="ngbDropdown" class="dropdown">
|
||||
<button ngbDropdownToggle="ngbDropdownToggle" class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{{selectedSprint.title}}
|
||||
({{toDateString(selectedSprint.startDate)}}
|
||||
-
|
||||
{{toDateString(selectedSprint.endDate)}})
|
||||
</button>
|
||||
<div ngbDropdownMenu="ngbDropdownMenu" class="dropdown-menu" aria-labelledby="dropdownMenu2">
|
||||
<option ngbDropdownItem="ngbDropdownItem" *ngFor="let s of sprints" (click)="this.selectedSprint = s;">
|
||||
{{s.title}}
|
||||
({{toDateString(selectedSprint.startDate)}}
|
||||
-
|
||||
{{toDateString(selectedSprint.endDate)}})
|
||||
</option>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6 container-fluid">
|
||||
<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;" *ngIf="selectedSprint != undefined">
|
||||
<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 class="col-lg-6 container-fluid">
|
||||
<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 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>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<h3>Backlog</h3>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="row px-3 py-2">
|
||||
<ng-container *ngIf="selectedSprint === undefined">
|
||||
<h3>
|
||||
Aktuell läuft kein Sprint.
|
||||
<a [routerLink]="['../sprints']">Zur Sprint Übersicht</a>
|
||||
</h3>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedSprint != undefined">
|
||||
<h3 *ngIf="selectedSprint === currentSprint">Aktueller Sprint:</h3>
|
||||
<h3 *ngIf="selectedSprint !== currentSprint">Sprint:</h3>
|
||||
<div ngbDropdown="ngbDropdown" class="dropdown">
|
||||
<button
|
||||
ngbDropdownToggle="ngbDropdownToggle"
|
||||
class="btn btn-secondary dropdown-toggle"
|
||||
type="button"
|
||||
id="dropdownMenu2"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
>
|
||||
{{ selectedSprint.title }}
|
||||
({{ toDateString(selectedSprint.startDate) }}
|
||||
-
|
||||
{{ toDateString(selectedSprint.endDate) }})
|
||||
</button>
|
||||
<div
|
||||
ngbDropdownMenu="ngbDropdownMenu"
|
||||
class="dropdown-menu"
|
||||
aria-labelledby="dropdownMenu2"
|
||||
>
|
||||
<option
|
||||
ngbDropdownItem="ngbDropdownItem"
|
||||
*ngFor="let s of sprints"
|
||||
(click)="this.selectedSprint = s"
|
||||
>
|
||||
{{ s.title }}
|
||||
({{ toDateString(selectedSprint.startDate) }}
|
||||
-
|
||||
{{ toDateString(selectedSprint.endDate) }})
|
||||
</option>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6 container-fluid">
|
||||
<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;"
|
||||
*ngIf="selectedSprint != undefined"
|
||||
>
|
||||
<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 class="col-lg-6 container-fluid">
|
||||
<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>
|
||||
|
|
|
@ -9,15 +9,12 @@ import {
|
|||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SprintFormComponent } from '../sprint-form/sprint-form.component';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-backlog',
|
||||
templateUrl: './backlog.component.html',
|
||||
styleUrls: ['./backlog.component.css'],
|
||||
})
|
||||
|
||||
|
||||
export class BacklogComponent{
|
||||
export class BacklogComponent {
|
||||
public status: ScrumStatus[] = [];
|
||||
public categories: ScrumCategory[] = [];
|
||||
public sprints: ScrumSprint[] = [];
|
||||
|
@ -26,12 +23,12 @@ export class BacklogComponent{
|
|||
|
||||
/**
|
||||
* Constructor of the class that initialized the communication with the backend
|
||||
* @param backendService
|
||||
* @param modalService
|
||||
* @param backendService
|
||||
* @param modalService
|
||||
*/
|
||||
constructor(
|
||||
private backendService: BackendService,
|
||||
private modalService: NgbModal,
|
||||
private modalService: NgbModal
|
||||
) {
|
||||
backendService.getSprints().subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
|
@ -80,19 +77,19 @@ export class BacklogComponent{
|
|||
* The relation to the sprint is determined by the userstory's sprintid.
|
||||
* If no sprint is selected it just returns an empty array.
|
||||
*/
|
||||
public get choosen(): ScrumUserstory[]
|
||||
{
|
||||
if(this.selectedSprint === undefined){return null;}
|
||||
return this.storys.filter(u => u.sprintid == this.selectedSprint.id);
|
||||
public get choosen(): ScrumUserstory[] {
|
||||
if (this.selectedSprint === undefined) {
|
||||
return null;
|
||||
}
|
||||
return this.storys.filter((u) => u.sprintid == this.selectedSprint.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter that returns an array with all userstories that aren't in any sprint.
|
||||
* The relation to no sprint is determined by the userstory's sprintid being undefined.
|
||||
*/
|
||||
public get backlog(): ScrumUserstory[]
|
||||
{
|
||||
return this.storys.filter(u => u.sprintid === undefined);
|
||||
public get backlog(): ScrumUserstory[] {
|
||||
return this.storys.filter((u) => u.sprintid === undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,7 +98,9 @@ export class BacklogComponent{
|
|||
*/
|
||||
public get currentSprint(): ScrumSprint {
|
||||
const now = Date.now();
|
||||
return this.sprints.find(s => Date.parse(s.startDate) < now && Date.parse(s.endDate) > now);
|
||||
return this.sprints.find(
|
||||
(s) => Date.parse(s.startDate) < now && Date.parse(s.endDate) > now
|
||||
);
|
||||
}
|
||||
|
||||
//#endregion getters
|
||||
|
@ -121,11 +120,11 @@ export class BacklogComponent{
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Deletes a userstory from the currently selected sprint by changing it's sprintid to undefined
|
||||
* @param userstory userstory object that shall be removed from the selected sprint's backlog
|
||||
*/
|
||||
public deleteFromSprintBacklog(userstory: ScrumUserstory){
|
||||
public deleteFromSprintBacklog(userstory: ScrumUserstory) {
|
||||
userstory.sprintid = undefined;
|
||||
this.backendService.putUserstory(userstory).subscribe((response) => {
|
||||
if (response.status > 399) {
|
||||
|
@ -145,12 +144,12 @@ export class BacklogComponent{
|
|||
const modalRef = this.modalService.open(SprintFormComponent, {
|
||||
backdrop: 'static',
|
||||
keyboard: true,
|
||||
size: "md",
|
||||
size: 'md',
|
||||
});
|
||||
|
||||
modalRef.result.then(result => {
|
||||
modalRef.result.then((result) => {
|
||||
this.sprints.push(result);
|
||||
});
|
||||
}
|
||||
}
|
||||
//#endregion modals
|
||||
}
|
||||
|
|
|
@ -1,36 +1,57 @@
|
|||
<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>
|
||||
<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>
|
||||
<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">
|
||||
<select
|
||||
class="select custom-select custom-text-secondary"
|
||||
[(ngModel)]="selectedSprint"
|
||||
>
|
||||
<option class="bg-secondary text-dark" [ngValue]="currentSprint">
|
||||
{{currentSprint.title}} (aktuell)
|
||||
{{ currentSprint.title }} (aktuell)
|
||||
</option>
|
||||
<option value="" disabled="disabled">─────────────────────────</option>
|
||||
<option class="text-dark" *ngFor="let sprint of sprints" [ngValue]="sprint">
|
||||
<option value="" disabled="disabled"
|
||||
>─────────────────────────</option
|
||||
>
|
||||
<option
|
||||
class="text-dark"
|
||||
*ngFor="let sprint of sprints"
|
||||
[ngValue]="sprint"
|
||||
>
|
||||
<ng-container *ngIf="sprint === currentSprint">
|
||||
{{sprint.title}} (aktuell)
|
||||
{{ sprint.title }} (aktuell)
|
||||
</ng-container>
|
||||
<ng-container *ngIf="sprint !== currentSprint">
|
||||
{{sprint.title}} ({{toDateString(sprint.startDate)}} - {{toDateString(sprint.endDate)}})
|
||||
{{ sprint.title }} ({{ toDateString(sprint.startDate) }} -
|
||||
{{ toDateString(sprint.endDate) }})
|
||||
</ng-container>
|
||||
</option>
|
||||
</select>
|
||||
|
@ -44,43 +65,40 @@
|
|||
[class.text-warning]="getSprintUrgency() === 1"
|
||||
[class.text-danger]="getSprintUrgency() === 0"
|
||||
>
|
||||
Verbleibende Tage: {{getRemainingDaysInSprint()}}
|
||||
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>
|
||||
<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
|
||||
*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 text-center" *ngFor="let status of usedStatus">
|
||||
<div class="card-header">
|
||||
<span class="text-large">
|
||||
{{status.title}}
|
||||
</span>
|
||||
<span class="text-large">
|
||||
{{ status.title }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span class="text-very-large">
|
||||
<b>{{getNumberOfUserstoriesByStatus(status)}}</b>
|
||||
</span>
|
||||
<span class="text-very-large">
|
||||
<b>{{ getNumberOfUserstoriesByStatus(status) }}</b>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -89,13 +107,13 @@
|
|||
<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 [storys]="selectedSprintUserstories"></app-userstory-inner-table>
|
||||
<app-userstory-inner-table
|
||||
[storys]="selectedSprintUserstories"
|
||||
></app-userstory-inner-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,31 +1,44 @@
|
|||
import {Component, OnChanges} from '@angular/core';
|
||||
import {forkJoin} from 'rxjs';
|
||||
import { Component, OnChanges } from '@angular/core';
|
||||
import { forkJoin } from 'rxjs';
|
||||
import Chart from 'chart.js';
|
||||
import {BackendService, ScrumSprint, ScrumStatus, ScrumUserstory} from '../../services/backend.service';
|
||||
import {
|
||||
BackendService,
|
||||
ScrumSprint,
|
||||
ScrumStatus,
|
||||
ScrumUserstory,
|
||||
} from '../../services/backend.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
templateUrl: 'dashboard.component.html',
|
||||
styleUrls: ['./dashboard.component.css']
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
return this.sprints.find(
|
||||
(s) => Date.parse(s.startDate) < now && Date.parse(s.endDate) > now
|
||||
);
|
||||
}
|
||||
|
||||
private _selectedSprint: ScrumSprint;
|
||||
|
@ -53,18 +66,25 @@ export class DashboardComponent {
|
|||
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();
|
||||
}
|
||||
});
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,31 +97,35 @@ export class DashboardComponent {
|
|||
|
||||
private createChart() {
|
||||
// @ts-ignore
|
||||
const context = document.getElementById('done-stories-chart').getContext('2d');
|
||||
const context = document
|
||||
.getElementById('done-stories-chart')
|
||||
.getContext('2d');
|
||||
|
||||
if (this.usedStatus.length === 0) {
|
||||
this.chart.destroy();
|
||||
}
|
||||
|
||||
else {
|
||||
} 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)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
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();
|
||||
|
@ -133,14 +157,18 @@ export class DashboardComponent {
|
|||
}
|
||||
|
||||
public getNumberOfUserstoriesByStatus(status: ScrumStatus): number {
|
||||
return this.selectedSprintUserstories.filter(us => us.statusid === status.id).length;
|
||||
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);
|
||||
return Math.floor(
|
||||
(Date.parse(this.selectedSprint.endDate) - Date.now()) / 86400000
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -154,7 +182,8 @@ export class DashboardComponent {
|
|||
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);
|
||||
const deltaFromStart =
|
||||
Date.parse(sprint.endDate) - Date.parse(sprint.startDate);
|
||||
return Math.floor((3 * deltaFromNow) / deltaFromStart);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.modal-footer {
|
||||
border-top: 0px solid;
|
||||
padding-top: 5%;
|
||||
}
|
||||
border-top: 0px solid;
|
||||
padding-top: 5%;
|
||||
}
|
||||
|
|
|
@ -10,26 +10,56 @@
|
|||
<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">
|
||||
<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">
|
||||
<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">
|
||||
<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
|
||||
(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>
|
||||
</div>
|
||||
|
|
|
@ -1,60 +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';
|
||||
import { BackendService, ScrumSprint } from '../../services/backend.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-task-form',
|
||||
templateUrl: './sprint-form.component.html',
|
||||
styleUrls: ['./sprint-form.component.css']
|
||||
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;
|
||||
@Input() public sprint: ScrumSprint;
|
||||
public editing: Boolean;
|
||||
public sprintid: string;
|
||||
|
||||
constructor(private backendService: BackendService, private activeModalService: NgbActiveModal) { }
|
||||
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();
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
// Closes the popup form window (by clicking "close button").
|
||||
onClose() {
|
||||
this.activeModalService.dismiss(this.sprint);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,40 +4,66 @@
|
|||
Sprints
|
||||
</h3>
|
||||
|
||||
<button class="btn btn-secondary" (click)="openSprintForm()">Neuer Sprint</button>
|
||||
<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 *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 *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 *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 *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>
|
||||
|
@ -45,22 +71,34 @@
|
|||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr *ngFor="let sprint of items" [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>
|
||||
<tr
|
||||
*ngFor="let sprint of items"
|
||||
[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">
|
||||
<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">
|
||||
<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>
|
||||
|
|
|
@ -89,4 +89,4 @@ export class SprintTableComponent extends TableComponentBase<ScrumSprint> {
|
|||
modalRef.componentInstance.sprint = editSprint;
|
||||
}
|
||||
//#endregion modals
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
<div class="container-fluid">
|
||||
<div class="content">
|
||||
<h3>
|
||||
<a *ngIf="filterUserstoryId" [routerLink]="['/userstories', {id: filterUserstoryId}]">
|
||||
Userstory #{{filterUserstoryId}}
|
||||
<a
|
||||
*ngIf="filterUserstoryId"
|
||||
[routerLink]="['/userstories', { id: filterUserstoryId }]"
|
||||
>
|
||||
Userstory #{{ filterUserstoryId }}
|
||||
>
|
||||
</a>
|
||||
Tasks
|
||||
|
@ -11,7 +14,9 @@
|
|||
<a [routerLink]="'/tasks'">Alle Tasks anzeigen</a>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-secondary" (click)="openTaskForm()">Neuer Task</button>
|
||||
<button class="btn btn-secondary" (click)="openTaskForm()">
|
||||
Neuer Task
|
||||
</button>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
|
@ -19,66 +24,118 @@
|
|||
<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 *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 *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>Userstory</span>
|
||||
<span>
|
||||
<span *ngIf="sortBy != 'userstory'"><i class="fa fa-sort fa-lg"></i></span>
|
||||
<span *ngIf="sortDescending && sortBy === 'userstory'"><i class="fa fa-sort-up fa-lg"></i></span>
|
||||
<span *ngIf="sortDescending === false && sortBy === 'userstory'"><i class="fa fa-sort-down fa-lg"></i></span>
|
||||
<span *ngIf="sortBy != 'userstory'"
|
||||
><i class="fa fa-sort fa-lg"></i
|
||||
></span>
|
||||
<span *ngIf="sortDescending && sortBy === 'userstory'"
|
||||
><i class="fa fa-sort-up fa-lg"></i
|
||||
></span>
|
||||
<span *ngIf="sortDescending === false && sortBy === 'userstory'"
|
||||
><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 *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">
|
||||
<span (click)="sortByPrio()">Priorität: </span>
|
||||
<div ngbDropdown class="d-inline-block">
|
||||
<span id="dropdownBasic1" ngbDropdownToggle>{{filterPriority || "All"}}</span>
|
||||
<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>
|
||||
<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 *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>
|
||||
</th>
|
||||
<th (click)="sortByAssigned()" class="sortable">
|
||||
<span>Assigned To</span>
|
||||
<span>
|
||||
<span *ngIf="sortBy != 'assignedtoid'"><i class="fa fa-sort fa-lg"></i></span>
|
||||
<span *ngIf="sortDescending && sortBy === 'assignedtoid'"><i class="fa fa-sort-up fa-lg"></i></span>
|
||||
<span *ngIf="sortDescending === false && sortBy === 'assignedtoid'"><i class="fa fa-sort-down fa-lg"></i></span>
|
||||
<span *ngIf="sortBy != 'assignedtoid'"
|
||||
><i class="fa fa-sort fa-lg"></i
|
||||
></span>
|
||||
<span *ngIf="sortDescending && sortBy === 'assignedtoid'"
|
||||
><i class="fa fa-sort-up fa-lg"></i
|
||||
></span>
|
||||
<span
|
||||
*ngIf="sortDescending === false && sortBy === 'assignedtoid'"
|
||||
><i class="fa fa-sort-down fa-lg"></i
|
||||
></span>
|
||||
</span>
|
||||
</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 *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>
|
||||
|
@ -86,41 +143,53 @@
|
|||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr *ngFor="let task of filteredItems" [class.table-info]="task.id === highlightId">
|
||||
<td>{{task.id}}</td>
|
||||
<td>{{task.title}}</td>
|
||||
<tr
|
||||
*ngFor="let task of filteredItems"
|
||||
[class.table-info]="task.id === highlightId"
|
||||
>
|
||||
<td>{{ task.id }}</td>
|
||||
<td>{{ task.title }}</td>
|
||||
<td>
|
||||
<a [routerLink]="['/userstories', {id: task.userstoryid}]">
|
||||
US #{{task.userstoryid}}
|
||||
<a [routerLink]="['/userstories', { id: task.userstoryid }]">
|
||||
US #{{ task.userstoryid }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a [routerLink]="['/status', {id: task.statusid}]">
|
||||
{{getStatusTitleById(task.statusid)}}
|
||||
<a [routerLink]="['/status', { id: task.statusid }]">
|
||||
{{ getStatusTitleById(task.statusid) }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{task.priority}}</td>
|
||||
<td>{{ task.priority }}</td>
|
||||
<td>
|
||||
<a [routerLink]="['/users', {id: task.assignedtoid}]">
|
||||
{{getUserNameById(task.assignedtoid)}}
|
||||
<a [routerLink]="['/users', { id: task.assignedtoid }]">
|
||||
{{ getUserNameById(task.assignedtoid) }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a [routerLink]="['/categories', {id: task.categoryid}]">
|
||||
{{getCategoryTitleById(task.categoryid)}}
|
||||
<a [routerLink]="['/categories', { id: task.categoryid }]">
|
||||
{{ getCategoryTitleById(task.categoryid) }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" rel="tooltip" (click)="openTaskForm(task)" class="btn btn-success btn-sm btn-icon">
|
||||
<button
|
||||
type="button"
|
||||
rel="tooltip"
|
||||
(click)="openTaskForm(task)"
|
||||
class="btn btn-success btn-sm btn-icon"
|
||||
>
|
||||
<i class="fa fa-pencil-alt"></i>
|
||||
</button>
|
||||
<button type="button" rel="tooltip" (click)="deleteTask(task)" class="btn btn-danger btn-sm btn-icon">
|
||||
<button
|
||||
type="button"
|
||||
rel="tooltip"
|
||||
(click)="deleteTask(task)"
|
||||
class="btn btn-danger btn-sm btn-icon"
|
||||
>
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -195,4 +195,4 @@ export class TaskTableComponent extends TableComponentBase<ScrumTask> {
|
|||
modalRef.componentInstance.task = editTask;
|
||||
}
|
||||
//#endregion modals
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,99 +1,155 @@
|
|||
<button class="btn btn-secondary my-3" (click)="openUserstoryForm()">Neue Userstory</button>
|
||||
<button class="btn btn-secondary my-3" (click)="openUserstoryForm()">
|
||||
Neue Userstory
|
||||
</button>
|
||||
|
||||
<table class="table">
|
||||
|
||||
<thead>
|
||||
<thead>
|
||||
<tr>
|
||||
<th (click)="sortById()" class="sortable">
|
||||
<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 *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 *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">
|
||||
</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 *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">
|
||||
<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">
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
</th>
|
||||
<th (click)="sortByCategory()" class="sortable">
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
</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
|
||||
<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)}}
|
||||
</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)}}
|
||||
</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>
|
||||
</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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
<h3>Userstories</h3>
|
||||
<app-userstory-inner-table [storys]="items"></app-userstory-inner-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
.modal .modal-content {
|
||||
display: contents;
|
||||
}
|
||||
display: contents;
|
||||
}
|
||||
|
|
|
@ -1,118 +1,230 @@
|
|||
<!--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 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 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 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>
|
||||
</div>
|
||||
|
|
|
@ -8,9 +8,8 @@ describe('TaskFormComponent', () => {
|
|||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ TaskFormComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
declarations: [TaskFormComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -2,199 +2,202 @@
|
|||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import {
|
||||
BackendService,
|
||||
ScrumTask,
|
||||
Priority,
|
||||
ScrumStatus,
|
||||
ScrumCategory,
|
||||
ScrumUser,
|
||||
ScrumProject,
|
||||
ScrumUserstory
|
||||
BackendService,
|
||||
ScrumTask,
|
||||
Priority,
|
||||
ScrumStatus,
|
||||
ScrumCategory,
|
||||
ScrumUser,
|
||||
ScrumProject,
|
||||
ScrumUserstory,
|
||||
} from '../../services/backend.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { HttpResponse } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-task-form',
|
||||
templateUrl: './task-form.component.html',
|
||||
styleUrls: ['./task-form.component.css']
|
||||
selector: 'app-task-form',
|
||||
templateUrl: './task-form.component.html',
|
||||
styleUrls: ['./task-form.component.css'],
|
||||
})
|
||||
|
||||
// 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: "" };
|
||||
@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();
|
||||
}
|
||||
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);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
// 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 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 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 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;
|
||||
});
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
.modal .modal-content {
|
||||
display: contents;
|
||||
}
|
||||
display: contents;
|
||||
}
|
||||
|
|
|
@ -1,140 +1,278 @@
|
|||
<!--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 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>
|
||||
</div>
|
||||
|
|
|
@ -1,207 +1,212 @@
|
|||
// 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,
|
||||
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']
|
||||
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;
|
||||
@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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 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;
|
||||
});
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,282 +1,282 @@
|
|||
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) {}
|
||||
|
||||
// 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' });
|
||||
}
|
||||
|
||||
public postTask(task: ScrumTask): Observable<HttpResponse<ScrumTask>> {
|
||||
const url = `${environment.apiUrl}/tasks`;
|
||||
return this.httpClient.post<ScrumTask>(url, task, { 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 deleteTask(task: ScrumTask): Observable<HttpResponse<any>> {
|
||||
const url = `${environment.apiUrl}/tasks/${task.id}`;
|
||||
return this.httpClient.delete(url, { observe: 'response' });
|
||||
}
|
||||
|
||||
// Userstories
|
||||
public getUserstories(): Observable<HttpResponse<ScrumUserstory[]>> {
|
||||
const url = `${environment.apiUrl}/userstories`;
|
||||
return this.httpClient.get<ScrumUserstory[]>(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',
|
||||
});
|
||||
}
|
||||
|
||||
public putUserstory(
|
||||
userstory: ScrumUserstory
|
||||
): Observable<HttpResponse<any>> {
|
||||
const url = `${environment.apiUrl}/userstories/${userstory.id}`;
|
||||
return this.httpClient.put(url, userstory, { observe: 'response' });
|
||||
}
|
||||
|
||||
public deleteUserstory(
|
||||
userstory: ScrumUserstory
|
||||
): Observable<HttpResponse<any>> {
|
||||
const url = `${environment.apiUrl}/userstories/${userstory.id}`;
|
||||
return this.httpClient.delete(url, { observe: 'response' });
|
||||
}
|
||||
|
||||
// Sprints
|
||||
public getSprints(): Observable<HttpResponse<ScrumSprint[]>> {
|
||||
const url = `${environment.apiUrl}/sprints`;
|
||||
return this.httpClient.get<ScrumSprint[]>(url, { observe: 'response' });
|
||||
}
|
||||
|
||||
public getSprint(id: number): Observable<HttpResponse<ScrumSprint>> {
|
||||
const url = `${environment.apiUrl}/sprints/${id}`;
|
||||
return this.httpClient.get<ScrumSprint>(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' });
|
||||
}
|
||||
|
||||
public deleteSprint(sprint: ScrumSprint): Observable<HttpResponse<any>> {
|
||||
const url = `${environment.apiUrl}/sprints/${sprint.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' });
|
||||
}
|
||||
|
||||
public getCategory(id: number): Observable<HttpResponse<ScrumCategory>> {
|
||||
const url = `${environment.apiUrl}/categories/${id}`;
|
||||
return this.httpClient.get<ScrumCategory>(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 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' });
|
||||
}
|
||||
|
||||
// Status
|
||||
public getAllStatus(): Observable<HttpResponse<ScrumStatus[]>> {
|
||||
const url = `${environment.apiUrl}/status`;
|
||||
return this.httpClient.get<ScrumStatus[]>(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 postStatus(
|
||||
status: ScrumStatus
|
||||
): Observable<HttpResponse<ScrumStatus>> {
|
||||
const url = `${environment.apiUrl}/status`;
|
||||
return this.httpClient.post<ScrumStatus>(url, status, {
|
||||
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 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' });
|
||||
}
|
||||
|
||||
public getUser(id: number): Observable<HttpResponse<ScrumUser>> {
|
||||
const url = `${environment.apiUrl}/users/${id}`;
|
||||
return this.httpClient.get<ScrumUser>(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 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' });
|
||||
}
|
||||
}
|
||||
|
||||
export enum Priority {
|
||||
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;
|
||||
}
|
||||
|
||||
export interface ScrumUserstory {
|
||||
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: string;
|
||||
endDate: string;
|
||||
projectid?: number;
|
||||
}
|
||||
|
||||
export interface ScrumCategory {
|
||||
id?: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
color?: string;
|
||||
project?: number;
|
||||
}
|
||||
|
||||
export interface ScrumStatus {
|
||||
id?: number;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface ScrumUser {
|
||||
id?: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ScrumProject {
|
||||
id?: number;
|
||||
title: string;
|
||||
isprivate: boolean;
|
||||
}
|
||||
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) {}
|
||||
|
||||
// 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' });
|
||||
}
|
||||
|
||||
public postTask(task: ScrumTask): Observable<HttpResponse<ScrumTask>> {
|
||||
const url = `${environment.apiUrl}/tasks`;
|
||||
return this.httpClient.post<ScrumTask>(url, task, { 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 deleteTask(task: ScrumTask): Observable<HttpResponse<any>> {
|
||||
const url = `${environment.apiUrl}/tasks/${task.id}`;
|
||||
return this.httpClient.delete(url, { observe: 'response' });
|
||||
}
|
||||
|
||||
// Userstories
|
||||
public getUserstories(): Observable<HttpResponse<ScrumUserstory[]>> {
|
||||
const url = `${environment.apiUrl}/userstories`;
|
||||
return this.httpClient.get<ScrumUserstory[]>(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',
|
||||
});
|
||||
}
|
||||
|
||||
public putUserstory(
|
||||
userstory: ScrumUserstory
|
||||
): Observable<HttpResponse<any>> {
|
||||
const url = `${environment.apiUrl}/userstories/${userstory.id}`;
|
||||
return this.httpClient.put(url, userstory, { observe: 'response' });
|
||||
}
|
||||
|
||||
public deleteUserstory(
|
||||
userstory: ScrumUserstory
|
||||
): Observable<HttpResponse<any>> {
|
||||
const url = `${environment.apiUrl}/userstories/${userstory.id}`;
|
||||
return this.httpClient.delete(url, { observe: 'response' });
|
||||
}
|
||||
|
||||
// Sprints
|
||||
public getSprints(): Observable<HttpResponse<ScrumSprint[]>> {
|
||||
const url = `${environment.apiUrl}/sprints`;
|
||||
return this.httpClient.get<ScrumSprint[]>(url, { observe: 'response' });
|
||||
}
|
||||
|
||||
public getSprint(id: number): Observable<HttpResponse<ScrumSprint>> {
|
||||
const url = `${environment.apiUrl}/sprints/${id}`;
|
||||
return this.httpClient.get<ScrumSprint>(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' });
|
||||
}
|
||||
|
||||
public deleteSprint(sprint: ScrumSprint): Observable<HttpResponse<any>> {
|
||||
const url = `${environment.apiUrl}/sprints/${sprint.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' });
|
||||
}
|
||||
|
||||
public getCategory(id: number): Observable<HttpResponse<ScrumCategory>> {
|
||||
const url = `${environment.apiUrl}/categories/${id}`;
|
||||
return this.httpClient.get<ScrumCategory>(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 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' });
|
||||
}
|
||||
|
||||
// Status
|
||||
public getAllStatus(): Observable<HttpResponse<ScrumStatus[]>> {
|
||||
const url = `${environment.apiUrl}/status`;
|
||||
return this.httpClient.get<ScrumStatus[]>(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 postStatus(
|
||||
status: ScrumStatus
|
||||
): Observable<HttpResponse<ScrumStatus>> {
|
||||
const url = `${environment.apiUrl}/status`;
|
||||
return this.httpClient.post<ScrumStatus>(url, status, {
|
||||
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 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' });
|
||||
}
|
||||
|
||||
public getUser(id: number): Observable<HttpResponse<ScrumUser>> {
|
||||
const url = `${environment.apiUrl}/users/${id}`;
|
||||
return this.httpClient.get<ScrumUser>(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 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' });
|
||||
}
|
||||
}
|
||||
|
||||
export enum Priority {
|
||||
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;
|
||||
}
|
||||
|
||||
export interface ScrumUserstory {
|
||||
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: string;
|
||||
endDate: string;
|
||||
projectid?: number;
|
||||
}
|
||||
|
||||
export interface ScrumCategory {
|
||||
id?: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
color?: string;
|
||||
project?: number;
|
||||
}
|
||||
|
||||
export interface ScrumStatus {
|
||||
id?: number;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface ScrumUser {
|
||||
id?: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ScrumProject {
|
||||
id?: number;
|
||||
title: string;
|
||||
isprivate: boolean;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Priority} from './backend.service';
|
||||
import { Priority } from './backend.service';
|
||||
|
||||
export function sortByNumberAscending<T>(items: T[], key: (T) => number) {
|
||||
return items.sort((a, b) => key(a) - key(b));
|
||||
|
@ -9,7 +9,7 @@ export function sortByStringAscending<T>(items: T[], key: (T) => string) {
|
|||
}
|
||||
|
||||
export function sortByDateAscending<T>(items: T[], key: (T) => Date) {
|
||||
return items.sort((a, b) => <any>key(b) - <any>key(a));
|
||||
return items.sort((a, b) => <any>key(b) - <any>key(a));
|
||||
}
|
||||
|
||||
export function getNumberForPriority(priority: Priority): number {
|
||||
|
@ -21,4 +21,4 @@ export function getNumberForPriority(priority: Priority): number {
|
|||
case Priority.Low:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export const environment = {
|
||||
production: true
|
||||
production: true,
|
||||
};
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
export const environment = {
|
||||
production: false,
|
||||
apiUrl: window["env"]["apiUrl"] || "default",
|
||||
debug: window["env"]["debug"] || false
|
||||
apiUrl: window['env']['apiUrl'] || 'default',
|
||||
debug: window['env']['debug'] || false,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Frontend</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<script src="assets/env.js"></script>
|
||||
<!-- Fonts and icons -->
|
||||
<link href="https://fonts.googleapis.com/css?family=Poppins:200,300,400,600,700,800" rel="stylesheet">
|
||||
<link href="https://use.fontawesome.com/releases/v5.0.6/css/all.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Frontend</title>
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
<script src="assets/env.js"></script>
|
||||
<!-- Fonts and icons -->
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Poppins:200,300,400,600,700,800"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
href="https://use.fontawesome.com/releases/v5.0.6/css/all.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -8,5 +8,6 @@ if (environment.production) {
|
|||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.catch((err) => console.error(err));
|
||||
|
|
|
@ -59,8 +59,7 @@ import '@angular/localize/init';
|
|||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
*/
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
/* 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;
|
||||
}
|
||||
|
||||
color: white;
|
||||
}
|
||||
|
||||
.white-content .custom-text-secondary {
|
||||
color: black !important;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
@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%;
|
||||
}
|
||||
@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%;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'zone.js/dist/zone-testing';
|
|||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
platformBrowserDynamicTesting,
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: any;
|
||||
|
|
|
@ -4,11 +4,6 @@
|
|||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts",
|
||||
"src/polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
"files": ["src/main.ts", "src/polyfills.ts"],
|
||||
"include": ["src/**/*.d.ts"]
|
||||
}
|
||||
|
|
|
@ -11,10 +11,7 @@
|
|||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "es2015",
|
||||
"lib": [
|
||||
"es2018",
|
||||
"dom"
|
||||
]
|
||||
"lib": ["es2018", "dom"]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"fullTemplateTypeCheck": true,
|
||||
|
|
|
@ -2,17 +2,8 @@
|
|||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
"types": ["jasmine", "node"]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts",
|
||||
"src/polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
"files": ["src/test.ts", "src/polyfills.ts"],
|
||||
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
|
||||
}
|
||||
|
|
73
tslint.json
73
tslint.json
|
@ -2,10 +2,7 @@
|
|||
"extends": "tslint:recommended",
|
||||
"rules": {
|
||||
"align": {
|
||||
"options": [
|
||||
"parameters",
|
||||
"statements"
|
||||
]
|
||||
"options": ["parameters", "statements"]
|
||||
},
|
||||
"array-type": false,
|
||||
"arrow-return-shorthand": true,
|
||||
|
@ -16,34 +13,16 @@
|
|||
"component-class-suffix": true,
|
||||
"contextual-lifecycle": true,
|
||||
"directive-class-suffix": true,
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"app",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"app",
|
||||
"kebab-case"
|
||||
],
|
||||
"directive-selector": [true, "attribute", "app", "camelCase"],
|
||||
"component-selector": [true, "element", "app", "kebab-case"],
|
||||
"eofline": true,
|
||||
"import-blacklist": [
|
||||
true,
|
||||
"rxjs/Rx"
|
||||
],
|
||||
"import-blacklist": [true, "rxjs/Rx"],
|
||||
"import-spacing": true,
|
||||
"indent": {
|
||||
"options": [
|
||||
"spaces"
|
||||
]
|
||||
"options": ["spaces"]
|
||||
},
|
||||
"max-classes-per-file": false,
|
||||
"max-line-length": [
|
||||
true,
|
||||
140
|
||||
],
|
||||
"max-line-length": [true, 140],
|
||||
"member-ordering": [
|
||||
true,
|
||||
{
|
||||
|
@ -55,35 +34,17 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
"info",
|
||||
"time",
|
||||
"timeEnd",
|
||||
"trace"
|
||||
],
|
||||
"no-console": [true, "debug", "info", "time", "timeEnd", "trace"],
|
||||
"no-empty": false,
|
||||
"no-inferrable-types": [
|
||||
true,
|
||||
"ignore-params"
|
||||
],
|
||||
"no-inferrable-types": [true, "ignore-params"],
|
||||
"no-non-null-assertion": true,
|
||||
"no-redundant-jsdoc": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-var-requires": false,
|
||||
"object-literal-key-quotes": [
|
||||
true,
|
||||
"as-needed"
|
||||
],
|
||||
"quotemark": [
|
||||
true,
|
||||
"single"
|
||||
],
|
||||
"object-literal-key-quotes": [true, "as-needed"],
|
||||
"quotemark": [true, "single"],
|
||||
"semicolon": {
|
||||
"options": [
|
||||
"always"
|
||||
]
|
||||
"options": ["always"]
|
||||
},
|
||||
"space-before-function-paren": {
|
||||
"options": {
|
||||
|
@ -113,11 +74,7 @@
|
|||
]
|
||||
},
|
||||
"variable-name": {
|
||||
"options": [
|
||||
"ban-keywords",
|
||||
"check-format",
|
||||
"allow-pascal-case"
|
||||
]
|
||||
"options": ["ban-keywords", "check-format", "allow-pascal-case"]
|
||||
},
|
||||
"whitespace": {
|
||||
"options": [
|
||||
|
@ -142,7 +99,5 @@
|
|||
"use-lifecycle-interface": true,
|
||||
"use-pipe-transform-interface": true
|
||||
},
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
]
|
||||
}
|
||||
"rulesDirectory": ["codelyzer"]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue