Add hl confirmation menus
parent
6b7b90d174
commit
720df45cfd
|
@ -5,10 +5,7 @@ const async = require('async');
|
||||||
const UserModel = require('../models/user');
|
const UserModel = require('../models/user');
|
||||||
const signatureTool = require('../signature-tool/signature-tool');
|
const signatureTool = require('../signature-tool/signature-tool');
|
||||||
|
|
||||||
|
const createAllSignatures = () => {
|
||||||
// Execute daily @ 02:30 AM
|
|
||||||
const cronJob = cron.job('00 30 02 * * *', () => {
|
|
||||||
|
|
||||||
console.log('\x1b[35m%s\x1b[0m', new Date().toLocaleString()
|
console.log('\x1b[35m%s\x1b[0m', new Date().toLocaleString()
|
||||||
+ ': cron job started - UPDATE SIGNATURES');
|
+ ': cron job started - UPDATE SIGNATURES');
|
||||||
|
|
||||||
|
@ -42,6 +39,12 @@ const cronJob = cron.job('00 30 02 * * *', () => {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
};
|
||||||
|
|
||||||
module.exports = cronJob;
|
// Execute daily @ 02:30 AM
|
||||||
|
const cronJob = cron.job('00 30 02 * * *', createAllSignatures);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
cronJob: cronJob,
|
||||||
|
createAllSignatures: createAllSignatures
|
||||||
|
};
|
||||||
|
|
|
@ -22,8 +22,12 @@ const AwardingSchema = new Schema({
|
||||||
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,
|
||||||
|
default: 0
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
type: Date,
|
type: Date,
|
||||||
|
|
|
@ -36,6 +36,9 @@ awarding.route('/')
|
||||||
if (req.query.userId) {
|
if (req.query.userId) {
|
||||||
filter.userId = req.query.userId;
|
filter.userId = req.query.userId;
|
||||||
}
|
}
|
||||||
|
if (req.query.inProgress) {
|
||||||
|
filter.confirmed = 0;
|
||||||
|
}
|
||||||
if (req.query.simple) {
|
if (req.query.simple) {
|
||||||
AwardingModel.find(filter, {}, {sort: {date: 'desc'}}, (err, items) => {
|
AwardingModel.find(filter, {}, {sort: {date: 'desc'}}, (err, items) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -53,17 +56,26 @@ awarding.route('/')
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
AwardingModel.find(filter, {}, {sort: {date: 'desc'}})
|
AwardingModel.find(filter, {}, {sort: {date: 'desc'}})
|
||||||
.populate('decorationId').populate('proposer', resultSet).exec((err, items) => {
|
.populate('decorationId').populate('proposer', resultSet).populate('userId').exec((err, items) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
err.status = codes.servererror;
|
err.status = codes.servererror;
|
||||||
return next(err);
|
return next(err);
|
||||||
// with return before (or after) the next(err) we prevent that the code continues here after next(err) has finished.
|
// with return before (or after) the next(err) we prevent that the code continues here after next(err) has finished.
|
||||||
// this saves an extra else {..}
|
// this saves an extra else {..}
|
||||||
}
|
}
|
||||||
// if the collection is empty we do not send empty arrays back.
|
let results = [];
|
||||||
if (items && items.length > 0) {
|
if (req.query.fractFilter) {
|
||||||
|
for (let item of items) {
|
||||||
|
if (item.decorationId.fraction === req.query.fractFilter) {
|
||||||
|
results.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.locals.items = results;
|
||||||
|
|
||||||
|
} else {
|
||||||
res.locals.items = items;
|
res.locals.items = items;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.locals.processed = true;
|
res.locals.processed = true;
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
@ -72,6 +84,7 @@ awarding.route('/')
|
||||||
|
|
||||||
.post(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
|
.post(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
|
||||||
const award = new AwardingModel(req.body);
|
const award = new AwardingModel(req.body);
|
||||||
|
award.confirmed = 1;
|
||||||
award.proposer = req.user._id;
|
award.proposer = req.user._id;
|
||||||
// timestamp and default are set automatically by Mongoose Schema Validation
|
// timestamp and default are set automatically by Mongoose Schema Validation
|
||||||
award.save((err) => {
|
award.save((err) => {
|
||||||
|
@ -91,6 +104,36 @@ awarding.route('/')
|
||||||
);
|
);
|
||||||
|
|
||||||
awarding.route('/:id')
|
awarding.route('/:id')
|
||||||
|
|
||||||
|
.patch(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
|
||||||
|
if (!req.body || (req.body._id && req.body._id !== req.params.id)) {
|
||||||
|
// little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match
|
||||||
|
const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + " " + req.body._id);
|
||||||
|
err.status = codes.notfound;
|
||||||
|
next(err);
|
||||||
|
return; // prevent node to process this function further after next() has finished.
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional task 3: increment version manually as we do not use .save(.)
|
||||||
|
req.body.updatedAt = new Date();
|
||||||
|
req.body.$inc = {__v: 1};
|
||||||
|
|
||||||
|
// PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need to reset attributes that are missing.
|
||||||
|
AwardingModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => {
|
||||||
|
if (err) {
|
||||||
|
err.status = codes.wrongrequest;
|
||||||
|
}
|
||||||
|
else if (!item) {
|
||||||
|
err = new Error("item not found");
|
||||||
|
err.status = codes.notfound;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.locals.items = item;
|
||||||
|
}
|
||||||
|
next(err);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
.delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
|
.delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
|
||||||
AwardingModel.findByIdAndRemove(req.params.id, (err, item) => {
|
AwardingModel.findByIdAndRemove(req.params.id, (err, item) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
|
@ -8,11 +8,19 @@ const logger = require('debug')('cc:command');
|
||||||
const codes = require('./http-codes');
|
const codes = require('./http-codes');
|
||||||
|
|
||||||
const routerHandling = require('../middleware/router-handling');
|
const routerHandling = require('../middleware/router-handling');
|
||||||
|
const createAllSignatures = require('../cron-job/update-signatures').createAllSignatures;
|
||||||
const createSignature = require('../signature-tool/signature-tool');
|
const createSignature = require('../signature-tool/signature-tool');
|
||||||
|
|
||||||
const command = express.Router();
|
const command = express.Router();
|
||||||
|
|
||||||
// add middleware for bonus tasks 4 and 5 to find filter and offset/limit params for GET / and GET /:id
|
command.route('/createSignature')
|
||||||
|
.post((req, res, next) => {
|
||||||
|
createAllSignatures();
|
||||||
|
})
|
||||||
|
|
||||||
|
.all(
|
||||||
|
routerHandling.httpMethodNotAllowed
|
||||||
|
);
|
||||||
|
|
||||||
command.route('/createSignature/:id')
|
command.route('/createSignature/:id')
|
||||||
.post((req, res, next) => {
|
.post((req, res, next) => {
|
||||||
|
|
|
@ -33,7 +33,7 @@ request.route('/award')
|
||||||
|
|
||||||
.post((req, res, next) => {
|
.post((req, res, next) => {
|
||||||
const award = new AwardingModel(req.body);
|
const award = new AwardingModel(req.body);
|
||||||
award.confirmed = false;
|
award.confirmed = 0;
|
||||||
award.proposer = req.user._id;
|
award.proposer = req.user._id;
|
||||||
// timestamp and default are set automatically by Mongoose Schema Validation
|
// timestamp and default are set automatically by Mongoose Schema Validation
|
||||||
award.save((err) => {
|
award.save((err) => {
|
||||||
|
|
|
@ -17,7 +17,7 @@ const errorResponseWare = require('./middleware/error-response');
|
||||||
const apiAuthenticationMiddleware = require('./middleware/auth-middleware');
|
const apiAuthenticationMiddleware = require('./middleware/auth-middleware');
|
||||||
const checkSql = require('./middleware/permission-check').checkSql;
|
const checkSql = require('./middleware/permission-check').checkSql;
|
||||||
const checkAdmin = require('./middleware/permission-check').checkAdmin;
|
const checkAdmin = require('./middleware/permission-check').checkAdmin;
|
||||||
const signatureCronJob = require('./cron-job/update-signatures');
|
const signatureCronJob = require('./cron-job/update-signatures').cronJob;
|
||||||
|
|
||||||
// router modules
|
// router modules
|
||||||
const authenticateRouter = require('./routes/authenticate');
|
const authenticateRouter = require('./routes/authenticate');
|
||||||
|
|
|
@ -209,6 +209,8 @@ let saveJimpImageAndCompress = (image, userId, res, next) => {
|
||||||
}).then((files) => {
|
}).then((files) => {
|
||||||
res.locals.items = {status: 'success'};
|
res.locals.items = {status: 'success'};
|
||||||
return next();
|
return next();
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error)
|
||||||
})
|
})
|
||||||
}, 3000);
|
}, 3000);
|
||||||
};
|
};
|
||||||
|
|
|
@ -48,6 +48,21 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
<li *ngIf="loginService.hasPermission(2)" class="dropdown">
|
||||||
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
|
||||||
|
aria-expanded="false">
|
||||||
|
Anträge
|
||||||
|
<span class="caret"></span>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li>
|
||||||
|
<a [routerLink]="['/confirm-promotion']">Beförderung</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a [routerLink]="['/confirm-award']">Orden/ Auszeichnung</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="nav navbar-nav" style="float: right">
|
<ul class="nav navbar-nav" style="float: right">
|
||||||
|
|
|
@ -4,7 +4,7 @@ export class AppConfig {
|
||||||
public readonly apiUrl = '';
|
public readonly apiUrl = '';
|
||||||
|
|
||||||
public readonly apiAppUserPath = '/account/';
|
public readonly apiAppUserPath = '/account/';
|
||||||
public readonly apiAwardPath = '/awardings/';
|
public readonly apiAwardPath = '/awardings';
|
||||||
public readonly apiDecorationPath = '/decorations/';
|
public readonly apiDecorationPath = '/decorations/';
|
||||||
public readonly apiAuthenticationPath = '/authenticate';
|
public readonly apiAuthenticationPath = '/authenticate';
|
||||||
public readonly apiSignupPath = '/authenticate/signup';
|
public readonly apiSignupPath = '/authenticate/signup';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {Routes, RouterModule} from '@angular/router';
|
import {RouterModule, Routes} from "@angular/router";
|
||||||
import {LoginComponent} from './login/index';
|
import {LoginComponent} from "./login/index";
|
||||||
import {NotFoundComponent} from './not-found/not-found.component';
|
import {NotFoundComponent} from "./not-found/not-found.component";
|
||||||
import {LoginGuardAdmin, LoginGuardHL, LoginGuardSQL} from './login/login.guard';
|
import {LoginGuardAdmin, LoginGuardHL, LoginGuardSQL} from "./login/login.guard";
|
||||||
import {usersRoutes, usersRoutingComponents} from "./users/users.routing";
|
import {usersRoutes, usersRoutingComponents} from "./users/users.routing";
|
||||||
import {squadsRoutes, squadsRoutingComponents} from "./squads/squads.routing";
|
import {squadsRoutes, squadsRoutingComponents} from "./squads/squads.routing";
|
||||||
import {decorationsRoutes, decorationsRoutingComponents} from "./decorations/decoration.routing";
|
import {decorationsRoutes, decorationsRoutingComponents} from "./decorations/decoration.routing";
|
||||||
|
@ -11,6 +11,8 @@ 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";
|
import {RequestPromotionComponent} from "./request/promotion/req-promotion.component";
|
||||||
|
import {ConfirmPromotionComponent} from "./request/confirm-promotion/confirm-promotion.component";
|
||||||
|
import {ConfirmAwardComponent} from "./request/confirm-award/confirm-award.component";
|
||||||
|
|
||||||
|
|
||||||
export const appRoutes: Routes = [
|
export const appRoutes: Routes = [
|
||||||
|
@ -23,6 +25,8 @@ export const appRoutes: Routes = [
|
||||||
|
|
||||||
{path: 'request-award', component: RequestAwardComponent, canActivate: [LoginGuardSQL]},
|
{path: 'request-award', component: RequestAwardComponent, canActivate: [LoginGuardSQL]},
|
||||||
{path: 'request-promotion', component: RequestPromotionComponent, canActivate: [LoginGuardSQL]},
|
{path: 'request-promotion', component: RequestPromotionComponent, canActivate: [LoginGuardSQL]},
|
||||||
|
{path: 'confirm-award', component: ConfirmAwardComponent, canActivate: [LoginGuardHL]},
|
||||||
|
{path: 'confirm-promotion', component: ConfirmPromotionComponent, canActivate: [LoginGuardHL]},
|
||||||
|
|
||||||
{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]},
|
||||||
|
@ -38,7 +42,8 @@ export const appRoutes: Routes = [
|
||||||
|
|
||||||
export const appRouting = RouterModule.forRoot(appRoutes);
|
export const appRouting = RouterModule.forRoot(appRoutes);
|
||||||
|
|
||||||
export const routingComponents = [LoginComponent, SignupComponent, RequestAwardComponent, RequestPromotionComponent, AdminComponent, ...armyRoutingComponents , NotFoundComponent, ...usersRoutingComponents,
|
export const routingComponents = [LoginComponent, SignupComponent, RequestAwardComponent, RequestPromotionComponent, ConfirmAwardComponent,
|
||||||
|
ConfirmPromotionComponent, AdminComponent, ...armyRoutingComponents, NotFoundComponent, ...usersRoutingComponents,
|
||||||
...squadsRoutingComponents, ...decorationsRoutingComponents, ...ranksRoutingComponents];
|
...squadsRoutingComponents, ...decorationsRoutingComponents, ...ranksRoutingComponents];
|
||||||
|
|
||||||
export const routingProviders = [LoginGuardHL];
|
export const routingProviders = [LoginGuardHL];
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody *ngFor="let award of user.awards">
|
<tbody *ngFor="let award of user.awards">
|
||||||
<tr *ngIf="award.confirmed" class="cell-outline">
|
<tr *ngIf="award.confirmed === 1" class="cell-outline">
|
||||||
<td class="text-center" *ngIf="award.decorationId.isMedal">
|
<td class="text-center" *ngIf="award.decorationId.isMedal">
|
||||||
<img height="90px" src="resource/decoration/{{award.decorationId._id}}.png">
|
<img height="90px" src="resource/decoration/{{award.decorationId._id}}.png">
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -37,7 +37,7 @@ export interface Award {
|
||||||
reason?: string;
|
reason?: string;
|
||||||
proposer?: AppUser;
|
proposer?: AppUser;
|
||||||
date?: number; // since Date.now() returns a number
|
date?: number; // since Date.now() returns a number
|
||||||
confirmed?: boolean;
|
confirmed?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Promotion {
|
export interface Promotion {
|
||||||
|
|
|
@ -97,10 +97,10 @@
|
||||||
{{award.proposer?.username}}
|
{{award.proposer?.username}}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<a class="small text-nowrap">{{award.date | date: 'dd.MM.yyyy'}}</a>
|
{{award.date | date: 'dd.MM.yyyy'}}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
{{award.confirmed? 'Bestätigt' : 'In Bearbeitung'}}
|
{{award.confirmed === 0? 'In Bearbeitung' : (award.confirmed === 1? 'Genehmigt' : 'Abgelehnt')}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
.decoration-preview {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
margin-top: 40px;
|
||||||
|
overflow-x: auto;
|
||||||
|
width: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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,49 @@
|
||||||
|
<form #form="ngForm" class="overview">
|
||||||
|
<h3>Offene Anträge - Auszeichnungen</h3>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="col-sm-1">Teilnehmer</th>
|
||||||
|
<th class="col-sm-1"></th>
|
||||||
|
<th class="col-sm-2">Auszeichnung</th>
|
||||||
|
<th class="col-sm-2">Begründung</th>
|
||||||
|
<th class="col-sm-1 ">Antragsteller</th>
|
||||||
|
<th class="col-sm-1 text-right">Datum</th>
|
||||||
|
<th class="col-sm-1 text-right">Aktion</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody *ngFor="let award of awards">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{award.userId.username}}
|
||||||
|
</td>
|
||||||
|
<td *ngIf="award.decorationId.isMedal">
|
||||||
|
<img height="40px" src="resource/decoration/{{award.decorationId._id}}.png">
|
||||||
|
</td>
|
||||||
|
<td *ngIf="!award.decorationId.isMedal">
|
||||||
|
<img width="60px" src="resource/decoration/{{award.decorationId._id}}.png">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{award.decorationId.name}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{award.reason}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{award.proposer?.username}}
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{{award.date | date: 'dd.MM.yyyy'}}
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<a class="action" (click)="confirm(award, true)">Bestätigen</a><br>
|
||||||
|
<a class="action" (click)="confirm(award, false)">Ablehnen</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
|
@ -0,0 +1,42 @@
|
||||||
|
import {Component} from "@angular/core";
|
||||||
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
|
import {Award} from "../../models/model-interfaces";
|
||||||
|
import {AwardingService} from "../../services/awarding-service/awarding.service";
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: './confirm-award.component.html',
|
||||||
|
styleUrls: ['./confirm-award.component.css'],
|
||||||
|
})
|
||||||
|
export class ConfirmAwardComponent {
|
||||||
|
awards: Award[];
|
||||||
|
|
||||||
|
constructor(private router: Router,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private awardingService: AwardingService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
|
||||||
|
|
||||||
|
this.awardingService.getUnconfirmedAwards(currentUser.squad.fraction).subscribe(awards => {
|
||||||
|
this.awards = awards;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm(award: Award, decision: boolean) {
|
||||||
|
const updateObject = {
|
||||||
|
_id: award._id,
|
||||||
|
confirmed: decision ? 1 : 2
|
||||||
|
};
|
||||||
|
|
||||||
|
this.awardingService.updateAward(updateObject).subscribe(res => {
|
||||||
|
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
|
||||||
|
this.awardingService.getUnconfirmedAwards(currentUser.squad.fraction).subscribe(awards => {
|
||||||
|
this.awards = awards;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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>
|
||||||
|
{{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: './confirm-promotion.component.html',
|
||||||
|
styleUrls: ['./confirm-promotion.component.css'],
|
||||||
|
})
|
||||||
|
export class ConfirmPromotionComponent {
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import {Injectable} from "@angular/core";
|
import {Injectable} from "@angular/core";
|
||||||
import {User} from "../../models/model-interfaces";
|
import {Award, User} from "../../models/model-interfaces";
|
||||||
import {Headers, Http} from "@angular/http";
|
import {Headers, Http} from "@angular/http";
|
||||||
import {Observable} from "rxjs/Observable";
|
import {Observable} from "rxjs/Observable";
|
||||||
import {AppConfig} from "../../app.config";
|
import {AppConfig} from "../../app.config";
|
||||||
|
@ -12,6 +12,11 @@ export class AwardingService {
|
||||||
private config: AppConfig) {
|
private config: AppConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUnconfirmedAwards(fraction?: string) {
|
||||||
|
return this.http.get(this.config.apiUrl + this.config.apiAwardPath + '?inProgress=true&fractFilter=' + fraction)
|
||||||
|
.map(res => res.json())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get awards array with populated decorations
|
* get awards array with populated decorations
|
||||||
*/
|
*/
|
||||||
|
@ -20,16 +25,21 @@ export class AwardingService {
|
||||||
.map(res => res.json())
|
.map(res => res.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
addAwarding(award) {
|
addAwarding(award: Award) {
|
||||||
return this.http.post(this.config.apiUrl + this.config.apiAwardPath, award)
|
return this.http.post(this.config.apiUrl + this.config.apiAwardPath, award)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAwarding(award) {
|
updateAward(award) {
|
||||||
|
return this.http.patch(this.config.apiUrl + this.config.apiAwardPath + '/' + award._id, award)
|
||||||
|
.map(res => res.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAwarding(award: Award) {
|
||||||
return this.http.post(this.config.apiUrl + this.config.apiRequestAwardPath, award)
|
return this.http.post(this.config.apiUrl + this.config.apiRequestAwardPath, award)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteAwarding(awardingId) {
|
deleteAwarding(awardingId) {
|
||||||
return this.http.delete(this.config.apiUrl + this.config.apiAwardPath + awardingId)
|
return this.http.delete(this.config.apiUrl + this.config.apiAwardPath + '/' + awardingId)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue