Add request routes & ui for promotion
parent
609395febc
commit
6b7b90d174
|
@ -13,15 +13,25 @@ const PromotionSchema = new Schema({
|
||||||
ref: 'AppUser',
|
ref: 'AppUser',
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
rankLvl: {
|
oldRankLvl: {
|
||||||
|
type: Number,
|
||||||
|
get: v => Math.round(v),
|
||||||
|
set: v => Math.round(v),
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
newRankLvl: {
|
||||||
type: Number,
|
type: Number,
|
||||||
get: v => Math.round(v),
|
get: v => Math.round(v),
|
||||||
set: v => Math.round(v),
|
set: v => Math.round(v),
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
confirmed: {
|
confirmed: {
|
||||||
type: Boolean,
|
type: Number,
|
||||||
default: true
|
get: v => Math.round(v),
|
||||||
|
set: v => Math.round(v),
|
||||||
|
min: 0,
|
||||||
|
max: 2,
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
collection: 'promotion',
|
collection: 'promotion',
|
||||||
|
|
|
@ -47,7 +47,7 @@ authenticate.route('/')
|
||||||
let authCheck = (username, password, res) => {
|
let authCheck = (username, password, res) => {
|
||||||
const deferred = Q.defer();
|
const deferred = Q.defer();
|
||||||
|
|
||||||
AppUserModel.findOne({username: username}, (err, user) => {
|
AppUserModel.findOne({username: username}).populate('squad').exec((err, user) => {
|
||||||
if (err) deferred.reject(err.name + ': ' + err.message);
|
if (err) deferred.reject(err.name + ': ' + err.message);
|
||||||
|
|
||||||
const diff = 3 * 60 * 24; // time till expiration [minutes]
|
const diff = 3 * 60 * 24; // time till expiration [minutes]
|
||||||
|
|
|
@ -10,7 +10,20 @@ const codes = require('./http-codes');
|
||||||
const routerHandling = require('../middleware/router-handling');
|
const routerHandling = require('../middleware/router-handling');
|
||||||
|
|
||||||
// Mongoose Model using mongoDB
|
// Mongoose Model using mongoDB
|
||||||
|
const UserModel = require('../models/user');
|
||||||
const AwardingModel = require('../models/awarding');
|
const AwardingModel = require('../models/awarding');
|
||||||
|
const PromotionModel = require('../models/promotion');
|
||||||
|
|
||||||
|
// result set for proposer(appUser) population
|
||||||
|
const resultSet = {
|
||||||
|
'__v': 0,
|
||||||
|
'updatedAt': 0,
|
||||||
|
'timestamp': 0,
|
||||||
|
'password': 0,
|
||||||
|
'permission': 0,
|
||||||
|
'secret': 0,
|
||||||
|
'activated': 0
|
||||||
|
};
|
||||||
|
|
||||||
const request = express.Router();
|
const request = express.Router();
|
||||||
|
|
||||||
|
@ -41,6 +54,51 @@ request.route('/award')
|
||||||
|
|
||||||
request.route('/promotion')
|
request.route('/promotion')
|
||||||
|
|
||||||
|
.get((req, res, next) => {
|
||||||
|
const squadFilter = req.query.squadId;
|
||||||
|
let userIds = [];
|
||||||
|
UserModel.find({squadId: squadFilter}, (err, items) => {
|
||||||
|
if (err) {
|
||||||
|
err.status = codes.servererror;
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
for (let item of items) {
|
||||||
|
userIds.push(item._id);
|
||||||
|
}
|
||||||
|
PromotionModel.find({userId: {"$in": userIds}}, {}, {sort: {timestamp: 'desc'}}).populate('userId').populate('proposer', resultSet).exec((err, items) => {
|
||||||
|
if (err) {
|
||||||
|
err.status = codes.servererror;
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items && items.length > 0) {
|
||||||
|
res.locals.items = items;
|
||||||
|
} else {
|
||||||
|
res.locals.items = [];
|
||||||
|
}
|
||||||
|
res.locals.processed = true;
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
.post((req, res, next) => {
|
||||||
|
const promotion = new PromotionModel(req.body);
|
||||||
|
promotion.confirmed = 0;
|
||||||
|
promotion.proposer = req.user._id;
|
||||||
|
// timestamp and default are set automatically by Mongoose Schema Validation
|
||||||
|
promotion.save((err) => {
|
||||||
|
if (err) {
|
||||||
|
err.status = codes.wrongrequest;
|
||||||
|
err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors);
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
res.status(codes.created);
|
||||||
|
res.locals.items = promotion;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
.all(
|
.all(
|
||||||
routerHandling.httpMethodNotAllowed
|
routerHandling.httpMethodNotAllowed
|
||||||
|
|
|
@ -12,6 +12,7 @@ export class AppConfig {
|
||||||
public readonly apiSquadPath = '/squads/';
|
public readonly apiSquadPath = '/squads/';
|
||||||
public readonly apiUserPath = '/users/';
|
public readonly apiUserPath = '/users/';
|
||||||
public readonly apiOverviewPath = '/overview';
|
public readonly apiOverviewPath = '/overview';
|
||||||
public readonly apiRequestAwardPath = '/request/award'
|
public readonly apiRequestAwardPath = '/request/award';
|
||||||
|
public readonly apiPromotionPath = '/request/promotion';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@ import {ArmyService} from "./services/army-service/army.service";
|
||||||
import { ClipboardModule } from 'ngx-clipboard';
|
import { ClipboardModule } from 'ngx-clipboard';
|
||||||
import {AppUserService} from "./services/app-user-service/app-user.service";
|
import {AppUserService} from "./services/app-user-service/app-user.service";
|
||||||
import {AppUserStore} from "./services/stores/app-user.store";
|
import {AppUserStore} from "./services/stores/app-user.store";
|
||||||
|
import {PromotionService} from "./services/promotion-service/promotion.service";
|
||||||
|
import {FilterRankPipe} from "./filter/filter.pipe";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [BrowserModule, FormsModule, ReactiveFormsModule, appRouting, HttpModule, ClipboardModule],
|
imports: [BrowserModule, FormsModule, ReactiveFormsModule, appRouting, HttpModule, ClipboardModule],
|
||||||
|
@ -50,6 +52,7 @@ import {AppUserStore} from "./services/stores/app-user.store";
|
||||||
RankService,
|
RankService,
|
||||||
RankStore,
|
RankStore,
|
||||||
AwardingService,
|
AwardingService,
|
||||||
|
PromotionService,
|
||||||
AppConfig,
|
AppConfig,
|
||||||
Title,
|
Title,
|
||||||
routingProviders,
|
routingProviders,
|
||||||
|
@ -61,6 +64,7 @@ import {AppUserStore} from "./services/stores/app-user.store";
|
||||||
DecorationComponent,
|
DecorationComponent,
|
||||||
DecorationItemComponent,
|
DecorationItemComponent,
|
||||||
RankItemComponent,
|
RankItemComponent,
|
||||||
|
FilterRankPipe,
|
||||||
UserItemComponent,
|
UserItemComponent,
|
||||||
SquadItemComponent,
|
SquadItemComponent,
|
||||||
ShowErrorComponent,
|
ShowErrorComponent,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {armyRoutes, armyRoutingComponents} from "./army/army.routing";
|
||||||
import {SignupComponent} from "./login/signup.component";
|
import {SignupComponent} from "./login/signup.component";
|
||||||
import {AdminComponent} from "./admin/admin.component";
|
import {AdminComponent} from "./admin/admin.component";
|
||||||
import {RequestAwardComponent} from "./request/award/req-award.component";
|
import {RequestAwardComponent} from "./request/award/req-award.component";
|
||||||
|
import {RequestPromotionComponent} from "./request/promotion/req-promotion.component";
|
||||||
|
|
||||||
|
|
||||||
export const appRoutes: Routes = [
|
export const appRoutes: Routes = [
|
||||||
|
@ -21,6 +22,7 @@ export const appRoutes: Routes = [
|
||||||
{path: 'signup', component: SignupComponent},
|
{path: 'signup', component: SignupComponent},
|
||||||
|
|
||||||
{path: 'request-award', component: RequestAwardComponent, canActivate: [LoginGuardSQL]},
|
{path: 'request-award', component: RequestAwardComponent, canActivate: [LoginGuardSQL]},
|
||||||
|
{path: 'request-promotion', component: RequestPromotionComponent, canActivate: [LoginGuardSQL]},
|
||||||
|
|
||||||
{path: 'cc-users', children: usersRoutes, canActivate: [LoginGuardHL]},
|
{path: 'cc-users', children: usersRoutes, canActivate: [LoginGuardHL]},
|
||||||
{path: 'cc-squads', children: squadsRoutes, canActivate: [LoginGuardHL]},
|
{path: 'cc-squads', children: squadsRoutes, canActivate: [LoginGuardHL]},
|
||||||
|
@ -36,7 +38,7 @@ export const appRoutes: Routes = [
|
||||||
|
|
||||||
export const appRouting = RouterModule.forRoot(appRoutes);
|
export const appRouting = RouterModule.forRoot(appRoutes);
|
||||||
|
|
||||||
export const routingComponents = [LoginComponent, SignupComponent, RequestAwardComponent, AdminComponent, ...armyRoutingComponents , NotFoundComponent, ...usersRoutingComponents,
|
export const routingComponents = [LoginComponent, SignupComponent, RequestAwardComponent, RequestPromotionComponent, AdminComponent, ...armyRoutingComponents , NotFoundComponent, ...usersRoutingComponents,
|
||||||
...squadsRoutingComponents, ...decorationsRoutingComponents, ...ranksRoutingComponents];
|
...squadsRoutingComponents, ...decorationsRoutingComponents, ...ranksRoutingComponents];
|
||||||
|
|
||||||
export const routingProviders = [LoginGuardHL];
|
export const routingProviders = [LoginGuardHL];
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import {Pipe, PipeTransform} from "@angular/core";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter Pipe to filter specific rank by level number in template
|
||||||
|
*
|
||||||
|
* @author: HardiReady
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Pipe({name: 'rankfilter'})
|
||||||
|
export class FilterRankPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(items: any[], filter: any): any {
|
||||||
|
// filter items array, items which match and return true will be kept, false will be filtered out
|
||||||
|
let res = items.filter(item => item.level === filter);
|
||||||
|
if (res.length === 0) {
|
||||||
|
return [{name: '-'}];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -40,6 +40,12 @@ export interface Award {
|
||||||
confirmed?: boolean;
|
confirmed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Promotion {
|
||||||
|
userId?: string
|
||||||
|
oldRankLvl: number,
|
||||||
|
newRankLvl: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface Decoration {
|
export interface Decoration {
|
||||||
_id?: string;
|
_id?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
.table-container {
|
.table-container {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
width: 50%;
|
width: 65%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* enable scrolling for long list of awardings */
|
/* enable scrolling for long list of awardings */
|
||||||
|
|
|
@ -8,9 +8,8 @@
|
||||||
id="user"
|
id="user"
|
||||||
[(ngModel)]="user"
|
[(ngModel)]="user"
|
||||||
[compareWith]="equals"
|
[compareWith]="equals"
|
||||||
required
|
(change)="toggleUser(decoPreview, decoDescription)"
|
||||||
(change)="toggleUser()"
|
required>
|
||||||
style="min-width: 200px;">
|
|
||||||
<option *ngFor="let user of users" [ngValue]="user">
|
<option *ngFor="let user of users" [ngValue]="user">
|
||||||
{{user.username}}
|
{{user.username}}
|
||||||
</option>
|
</option>
|
||||||
|
@ -18,22 +17,18 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div *ngIf="showForm">
|
<div class="form-group" *ngIf="showForm">
|
||||||
<div class="form-group">
|
|
||||||
<label for="decoration">Auszeichnung</label>
|
<label for="decoration">Auszeichnung</label>
|
||||||
<select class="form-control"
|
<select class="form-control"
|
||||||
name="decoration"
|
name="decoration"
|
||||||
id="decoration"
|
id="decoration"
|
||||||
#decorationField
|
[(ngModel)]="decoration"
|
||||||
(change)="toggleDecoPreview(decoDescription, decorationField.value, decoPreview)"
|
(change)="toggleDecoPreview(decoDescription, decoPreview)"
|
||||||
[ngModel]="undefined"
|
|
||||||
required>
|
required>
|
||||||
<option *ngFor="let deco of decorations" [value]="deco._id">
|
<option *ngFor="let deco of decorations" [ngValue]="deco">
|
||||||
{{deco.fraction == 'BLUFOR'? 'NATO' : deco.fraction == 'OPFOR'? 'CSAT' : 'Global'}}: {{deco.name}}
|
{{deco.fraction == 'BLUFOR'? 'NATO' : deco.fraction == 'OPFOR'? 'CSAT' : 'Global'}}: {{deco.name}}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<show-error text="Auszeichnung" path="decoration"></show-error>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="div-table-row" [style.display]="decoPreviewDisplay" style="margin-top: 5px; margin-bottom:10px">
|
<div class="div-table-row" [style.display]="decoPreviewDisplay" style="margin-top: 5px; margin-bottom:10px">
|
||||||
|
@ -46,11 +41,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" *ngIf="showForm">
|
||||||
<label for="reason">Begründung</label>
|
<label for="reason">Begründung</label>
|
||||||
<textarea class="form-control center-block" name="reason" [ngModel]="undefined" required
|
<textarea class="form-control center-block" name="reason" [(ngModel)]="reason" required
|
||||||
id="reason" placeholder="Begründung eingeben..." rows="3" #awardTextArea></textarea>
|
id="reason" placeholder="Begründung eingeben..." rows="3" ></textarea>
|
||||||
<show-error text="Begründung" path="reason"></show-error>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="cancel"
|
<button id="cancel"
|
||||||
|
@ -60,8 +54,9 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button id="save"
|
<button id="save"
|
||||||
(click)="addAwarding(decorationField, awardTextArea, decoPreview, decoDescription)"
|
(click)="addAwarding(decoPreview, decoDescription)"
|
||||||
class="btn btn-default"
|
class="btn btn-default"
|
||||||
|
*ngIf="showForm"
|
||||||
[disabled]="!form.valid">
|
[disabled]="!form.valid">
|
||||||
Bestätigen
|
Bestätigen
|
||||||
</button>
|
</button>
|
||||||
|
@ -72,7 +67,7 @@
|
||||||
Erfolgreich gespeichert
|
Erfolgreich gespeichert
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="table-container">
|
<div class="table-container" *ngIf="showForm">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -111,6 +106,5 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -21,6 +21,10 @@ export class RequestAwardComponent {
|
||||||
|
|
||||||
user: User = {};
|
user: User = {};
|
||||||
|
|
||||||
|
decoration: Decoration = null;
|
||||||
|
|
||||||
|
reason: string = '';
|
||||||
|
|
||||||
decorations: Decoration[];
|
decorations: Decoration[];
|
||||||
|
|
||||||
awards: Award[];
|
awards: Award[];
|
||||||
|
@ -39,12 +43,16 @@ export class RequestAwardComponent {
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
|
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
|
||||||
// show only current users squad members
|
// show only current users squad members
|
||||||
this.userService.findUsers('', undefined, currentUser.squad).subscribe(users => {
|
this.userService.findUsers('', undefined, currentUser.squad._id).subscribe(users => {
|
||||||
this.users = users;
|
this.users = users;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleUser() {
|
toggleUser(previewImage, decoDescription) {
|
||||||
|
this.decoration = null;
|
||||||
|
if (previewImage && decoDescription) {
|
||||||
|
previewImage.src = decoDescription.innerHTML = '';
|
||||||
|
}
|
||||||
this.decorationService.findDecorations('', this.user.squad.fraction).subscribe(decorations => {
|
this.decorationService.findDecorations('', this.user.squad.fraction).subscribe(decorations => {
|
||||||
this.decorations = decorations;
|
this.decorations = decorations;
|
||||||
});
|
});
|
||||||
|
@ -57,36 +65,33 @@ export class RequestAwardComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
toggleDecoPreview(descriptionField, decorationId, image) {
|
toggleDecoPreview(descriptionField, image) {
|
||||||
this.decoPreviewDisplay = 'flex'; // visible & keep same height for all children
|
this.decoPreviewDisplay = 'flex'; // visible & keep same height for all children
|
||||||
|
|
||||||
const description = this.decorations.find(
|
const description = this.decorations.find(
|
||||||
decoration => decoration._id === decorationId
|
decoration => decoration._id === this.decoration._id
|
||||||
).description;
|
).description;
|
||||||
|
|
||||||
image.src = 'resource/decoration/' + decorationId + '.png';
|
image.src = 'resource/decoration/' + this.decoration._id + '.png';
|
||||||
descriptionField.innerHTML = description;
|
descriptionField.innerHTML = description;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addAwarding(decorationField, reasonField, previewImage, descriptionField) {
|
addAwarding(previewImage, descriptionField) {
|
||||||
const decorationId = decorationField.value;
|
if (this.decoration._id && this.reason.length > 0) {
|
||||||
const reason = reasonField.value;
|
|
||||||
if (decorationId && reason.length > 0) {
|
|
||||||
const award = {
|
const award = {
|
||||||
"userId": this.user._id,
|
"userId": this.user._id,
|
||||||
"decorationId": decorationId,
|
"decorationId": this.decoration._id,
|
||||||
"reason": reason,
|
"reason": this.reason,
|
||||||
"date": Date.now()
|
"date": Date.now()
|
||||||
};
|
};
|
||||||
this.awardingService.requestAwarding(award).subscribe(() => {
|
this.awardingService.requestAwarding(award).subscribe(() => {
|
||||||
this.awardingService.getUserAwardings(this.user._id)
|
this.awardingService.getUserAwardings(this.user._id)
|
||||||
.subscribe(awards => {
|
.subscribe(awards => {
|
||||||
this.awards = awards;
|
this.awards = awards;
|
||||||
console.log(awards[0])
|
this.decoration = null;
|
||||||
|
this.reason = previewImage.src = descriptionField.innerHTML = '';
|
||||||
this.decoPreviewDisplay = 'none';
|
this.decoPreviewDisplay = 'none';
|
||||||
decorationField.value = undefined;
|
|
||||||
reasonField.value = previewImage.src = descriptionField.innerHTML = '';
|
|
||||||
this.showSuccessLabel = true;
|
this.showSuccessLabel = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.showSuccessLabel = false;
|
this.showSuccessLabel = false;
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
.decoration-preview {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trash {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
margin-top: 40px;
|
||||||
|
overflow-x: auto;
|
||||||
|
width: 65%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable scrolling for long list of awardings */
|
||||||
|
.overview {
|
||||||
|
position: fixed;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
width: 100%;
|
||||||
|
border-left: thin solid lightgrey;
|
||||||
|
padding: 20px 0 0 50px;
|
||||||
|
margin-left: 10px;
|
||||||
|
height: 100vh;
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 80px 0 20px -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
<form #form="ngForm" class="overview">
|
||||||
|
<h3>Beförderung beantragen</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="user">Teilnehmer</label>
|
||||||
|
<select class="form-control"
|
||||||
|
name="user"
|
||||||
|
id="user"
|
||||||
|
[(ngModel)]="user"
|
||||||
|
[compareWith]="equals"
|
||||||
|
required
|
||||||
|
(change)="toggleUser()">
|
||||||
|
<option *ngFor="let user of users" [ngValue]="user">
|
||||||
|
{{user.username}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="showForm">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="user">Aktueller Rang</label>
|
||||||
|
<input class="form-control"
|
||||||
|
[(ngModel)]="user.rank.name"
|
||||||
|
[ngModelOptions]="{standalone: true}"
|
||||||
|
readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="decoration">Neuer Rang</label>
|
||||||
|
<select class="form-control"
|
||||||
|
name="decoration"
|
||||||
|
id="decoration"
|
||||||
|
#decorationField
|
||||||
|
[(ngModel)]="newLevel"
|
||||||
|
required>
|
||||||
|
<option *ngFor="let rank of ranks" [ngValue]="rank.level">
|
||||||
|
{{rank.name}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="cancel"
|
||||||
|
(click)="cancel()"
|
||||||
|
class="btn btn-default">
|
||||||
|
Abbrechen
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button id="save"
|
||||||
|
*ngIf="showForm"
|
||||||
|
(click)="addPromotion()"
|
||||||
|
class="btn btn-default"
|
||||||
|
[disabled]="newLevel === user.rank.level">
|
||||||
|
Bestätigen
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<span *ngIf="showSuccessLabel"
|
||||||
|
class="label label-success label-small"
|
||||||
|
style="margin-left: inherit">
|
||||||
|
Erfolgreich gespeichert
|
||||||
|
</span>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<label>Beförderungsanträge</label>
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="col-sm-1">Teilnehmer</th>
|
||||||
|
<th class="col-sm-1">Alter Rang</th>
|
||||||
|
<th class="col-sm-1">Neuer Rang</th>
|
||||||
|
<th class="col-sm-1 ">Antragsteller</th>
|
||||||
|
<th class="col-sm-1 text-center">Datum</th>
|
||||||
|
<th class="col-sm-1 text-center">Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody *ngFor="let promotion of uncheckedPromotions">
|
||||||
|
<tr>
|
||||||
|
<td class="table-cell-id">
|
||||||
|
{{promotion.userId.username}}
|
||||||
|
</td>
|
||||||
|
<td *ngFor="let rank of (ranks | rankfilter: promotion.oldRankLvl)">
|
||||||
|
{{rank.name}}
|
||||||
|
</td>
|
||||||
|
<td *ngFor="let rank of (ranks | rankfilter: promotion.newRankLvl)">
|
||||||
|
{{rank.name}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{promotion.proposer.username}}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
{{promotion.timestamp | date: 'dd.MM.yyyy'}}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
{{promotion.confirmed === 0? 'In Bearbeitung' : (promotion.confirmed === 1? 'Genehmigt' : 'Abgelehnt')}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</form>
|
|
@ -0,0 +1,96 @@
|
||||||
|
import {Component, ViewChild} from "@angular/core";
|
||||||
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
|
import {Rank, User} from "../../models/model-interfaces";
|
||||||
|
import {NgForm} from "@angular/forms";
|
||||||
|
import {UserService} from "../../services/user-service/user.service";
|
||||||
|
import {RankService} from "../../services/rank-service/rank.service";
|
||||||
|
import {PromotionService} from "../../services/promotion-service/promotion.service";
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: './req-promotion.component.html',
|
||||||
|
styleUrls: ['./req-promotion.component.css'],
|
||||||
|
})
|
||||||
|
export class RequestPromotionComponent {
|
||||||
|
|
||||||
|
@ViewChild(NgForm) form: NgForm;
|
||||||
|
|
||||||
|
showForm = false;
|
||||||
|
|
||||||
|
showSuccessLabel = false;
|
||||||
|
|
||||||
|
user: User = {};
|
||||||
|
|
||||||
|
newLevel: number;
|
||||||
|
|
||||||
|
ranks: Rank[];
|
||||||
|
|
||||||
|
users: User[];
|
||||||
|
|
||||||
|
uncheckedPromotions = [];
|
||||||
|
|
||||||
|
constructor(private router: Router,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private userService: UserService,
|
||||||
|
private rankService: RankService,
|
||||||
|
private promotionService: PromotionService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
|
||||||
|
// show only current users squad members
|
||||||
|
this.userService.findUsers('', undefined, currentUser.squad._id).subscribe(users => {
|
||||||
|
this.users = users;
|
||||||
|
});
|
||||||
|
this.rankService.findRanks('', currentUser.squad.fraction).subscribe(ranks => {
|
||||||
|
this.ranks = ranks;
|
||||||
|
});
|
||||||
|
this.promotionService.getSquadPromotions(currentUser.squad._id).subscribe(promotions => {
|
||||||
|
this.uncheckedPromotions = promotions;
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleUser() {
|
||||||
|
this.showForm = true;
|
||||||
|
this.newLevel = this.user.rank.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
addPromotion() {
|
||||||
|
const promotion = {
|
||||||
|
"userId": this.user._id,
|
||||||
|
"oldRankLvl": this.user.rank.level,
|
||||||
|
"newRankLvl": this.newLevel,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.promotionService.requestPromotion(promotion).subscribe();
|
||||||
|
this.showSuccessLabel = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.showSuccessLabel = false;
|
||||||
|
}, 2000);
|
||||||
|
this.showForm = false;
|
||||||
|
this.user = {};
|
||||||
|
|
||||||
|
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
|
||||||
|
this.promotionService.getSquadPromotions(currentUser.squad._id).subscribe(promotions => {
|
||||||
|
this.uncheckedPromotions = promotions;
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
this.router.navigate(['..'], {relativeTo: this.route});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compare ngValue with ngModel to assign selected element
|
||||||
|
*/
|
||||||
|
equals(o1: User, o2: User) {
|
||||||
|
if (o1 && o2) {
|
||||||
|
return o1._id === o2._id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -8,9 +8,6 @@ import {HttpClient} from "../http-client";
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AwardingService {
|
export class AwardingService {
|
||||||
|
|
||||||
users$: Observable<User[]>;
|
|
||||||
|
|
||||||
|
|
||||||
constructor(private http: HttpClient,
|
constructor(private http: HttpClient,
|
||||||
private config: AppConfig) {
|
private config: AppConfig) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
import {Injectable} from "@angular/core";
|
||||||
|
import {AppConfig} from "../../app.config";
|
||||||
|
import {HttpClient} from "../http-client";
|
||||||
|
import {Promotion} from "../../models/model-interfaces";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PromotionService {
|
||||||
|
|
||||||
|
constructor(private http: HttpClient,
|
||||||
|
private config: AppConfig) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getSquadPromotions(squadId: string) {
|
||||||
|
return this.http.get(this.config.apiUrl + this.config.apiPromotionPath + '?squadId=' + squadId)
|
||||||
|
.map(res => res.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
requestPromotion(promotion: Promotion) {
|
||||||
|
return this.http.post(this.config.apiUrl + this.config.apiPromotionPath, promotion)
|
||||||
|
}
|
||||||
|
|
||||||
|
deletePromotion(promotionId) {
|
||||||
|
return this.http.delete(this.config.apiUrl + this.config.apiPromotionPath + promotionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue