Merge branch 'release/v1.7.7' of hardi/opt-cc into master

pull/44/head v1.7.7
hardi 2018-06-22 09:32:24 +02:00 committed by Gogs
commit de22e744fa
25 changed files with 378 additions and 87 deletions

View File

@ -58,3 +58,16 @@ Create a new awarding proposal, that needs to be approved by higher permission l
+ Response 201 (application/json; charset=utf-8) + Response 201 (application/json; charset=utf-8)
+ Attributes (Awarding, fixed-type) + Attributes (Awarding, fixed-type)
### Get Unprocessed Squad Awardings [GET /awardings/unprocessed/{squadId}]
List all awardings that are requested and in pending decision status
**Permission: 1**
+ Parameters
+ squadId: `5aba54eaeadcce6332c6a774` (string, required) - unique id of the squad in which awardings are requested
+ Response 200 (application/json; charset=utf-8)
+ Attributes (array[AwardingPopulated], fixed-type)

View File

@ -43,6 +43,9 @@
+ `BLUFOR` + `BLUFOR`
+ `OPFOR` + `OPFOR`
+ `NONE` + `NONE`
+ shooterVehicle: `FV-720 Mora` (string, optional) - vehicle in whiock the shooting player sat
+ targetVehicle: `Ifrit-GMG` (string, optional) - vehicle in which the target player sat
+ magazine: `30 mm APFSDS` (string, optional) - magazine name used to execute the kill
#LogRespawn (Log) #LogRespawn (Log)
## Properties ## Properties
@ -84,3 +87,5 @@
+ `HEAVY` + `HEAVY`
+ `AIR` + `AIR`
+ `UNKNOWN` + `UNKNOWN`
+ shooterVehicle: `FV-720 Mora` (string, optional) - vehicle in whiock the shooting player sat
+ magazine: `30 mm APFSDS` (string, optional) - magazine name used to execute the kill

View File

@ -24,6 +24,15 @@ const LogKillSchema = new Schema({
type: Boolean, type: Boolean,
required: true, required: true,
}, },
magazine: {
type: String,
},
shooterVehicle: {
type: String,
},
targetVehicle: {
type: String,
},
fraction: { fraction: {
type: String, type: String,
enum: ['BLUFOR', 'OPFOR', 'NONE'], enum: ['BLUFOR', 'OPFOR', 'NONE'],

View File

@ -33,6 +33,12 @@ const LogVehicleKillSchema = new Schema({
enum: ['LIGHT', 'HEAVY', 'AIR', 'UNKNOWN'], enum: ['LIGHT', 'HEAVY', 'AIR', 'UNKNOWN'],
required: true, required: true,
}, },
magazine: {
type: String,
},
shooterVehicle: {
type: String,
},
}, { }, {
collection: 'logVehicle', collection: 'logVehicle',
}); });

View File

@ -3,6 +3,8 @@
// modules // modules
const express = require('express'); const express = require('express');
const mongoose = require('mongoose');
// HTTP status codes by name // HTTP status codes by name
const codes = require('./http-codes'); const codes = require('./http-codes');
@ -10,9 +12,11 @@ const routerHandling = require('../middleware/router-handling');
const apiAuthenticationMiddleware = require('../middleware/auth-middleware'); const apiAuthenticationMiddleware = require('../middleware/auth-middleware');
const checkHl = require('../middleware/permission-check').checkHl; const checkHl = require('../middleware/permission-check').checkHl;
const checkSql = require('../middleware/permission-check').checkSql;
// Mongoose Model using mongoDB // Mongoose Model using mongoDB
const AwardingModel = require('../models/awarding'); const AwardingModel = require('../models/awarding');
const UserModel = require('../models/user');
// result set for proposer(appUser) population // result set for proposer(appUser) population
const resultSet = { const resultSet = {
@ -77,8 +81,26 @@ awarding.route('/')
.all(routerHandling.httpMethodNotAllowed); .all(routerHandling.httpMethodNotAllowed);
awarding.route('/:id') awarding.route('/unprocessed/:squadId')
.get(apiAuthenticationMiddleware, checkSql, (req, res, next) => {
const filter = {squadId: req.params.squadId};
UserModel.find(filter, (err, users) => {
if (!users || users.length === 0) {
return next();
}
const squadUserIds = users.map((user) => new mongoose.Types.ObjectId(user._id));
AwardingModel.find({userId: {$in: squadUserIds}, confirmed: 0})
.populate('decorationId')
.populate('proposer', resultSet)
.populate('userId')
.exec((err, awards) => {
res.locals.items = awards;
next();
});
});
});
awarding.route('/:id')
.patch(apiAuthenticationMiddleware, checkHl, (req, res, next) => { .patch(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
if (!req.body || (req.body._id && req.body._id !== req.params.id)) { 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 // little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match

View File

@ -17,9 +17,9 @@ const vehicleNameEndRegex = /\s\(\w+:/;
const sideRegex = /(side:\s(.*?)\))/; const sideRegex = /(side:\s(.*?)\))/;
// const magazineRegex = /(magazine:\s(.*?)\))/; const magazineRegex = /(magazine:\s(.*?)\))/;
// const vehicleRegex = /(vehicle:\s(.*?)\))/; const vehicleRegex = /(vehicle:\s(.*?)\))/;
const categoryRegex = /(category:\s(.*?)\))/; const categoryRegex = /(category:\s(.*?)\))/;
@ -51,7 +51,7 @@ const parseWarLog = (lineArray, war) => {
]; ];
const addPlayerIfNotExists = (inputPlayer, steamUUID) => { const addPlayerIfNotExists = (inputPlayer, steamUUID) => {
const player = getPlayerAndFractionFromString(inputPlayer); const player = getPlayerInfoFromString(inputPlayer);
if (player && player.name && player.fraction && !playerArrayContains(stats.players, player)) { if (player && player.name && player.fraction && !playerArrayContains(stats.players, player)) {
player['warId'] = war._id; player['warId'] = war._id;
player['steamUUID'] = steamUUID; player['steamUUID'] = steamUUID;
@ -74,7 +74,7 @@ const parseWarLog = (lineArray, war) => {
stats.clean.push(line); stats.clean.push(line);
const shooterString = line.substring(line.lastIndexOf(' von: ') + 6, line.search(/$/) - 1); const shooterString = line.substring(line.lastIndexOf(' von: ') + 6, line.search(/$/) - 1);
const shooter = getPlayerAndFractionFromString(shooterString); const shooter = getPlayerInfoFromString(shooterString);
if (line.includes('Fahrzeug:')) { if (line.includes('Fahrzeug:')) {
const targetString = line.substring(line.lastIndexOf(' --- Fahrzeug: ') + 15, line.lastIndexOf(' von:')); const targetString = line.substring(line.lastIndexOf(' --- Fahrzeug: ') + 15, line.lastIndexOf(' von:'));
@ -93,19 +93,36 @@ const parseWarLog = (lineArray, war) => {
} else { } else {
vehicleKill.shooter = shooter ? shooter.name : null; vehicleKill.shooter = shooter ? shooter.name : null;
} }
if (shooter.magazine) {
vehicleKill.magazine = shooter.magazine;
}
if (shooter.vehicle) {
vehicleKill.shooterVehicle = shooter.vehicle;
}
stats.vehicles.push(vehicleKill); stats.vehicles.push(vehicleKill);
} }
} else { } else {
const targetString = line.substring(line.lastIndexOf(' --- Einheit: ') + 14, line.lastIndexOf(' von:')); const targetString = line.substring(line.lastIndexOf(' --- Einheit: ') + 14, line.lastIndexOf(' von:'));
const target = getPlayerAndFractionFromString(targetString); const target = getPlayerInfoFromString(targetString);
stats.kills.push({ const kill = {
war: war._id, war: war._id,
time: getFullTimeDate(war.date, line.split(WHITESPACE)[5]), time: getFullTimeDate(war.date, line.split(WHITESPACE)[5]),
shooter: shooter ? shooter.name : null, shooter: shooter ? shooter.name : null,
target: target ? target.name : null, target: target ? target.name : null,
friendlyFire: shooter ? target.fraction === shooter.fraction : false, friendlyFire: shooter ? target.fraction === shooter.fraction : false,
fraction: shooter ? shooter.fraction : 'NONE', fraction: shooter ? shooter.fraction : 'NONE',
}); };
if (shooter.magazine) {
kill.magazine = shooter.magazine;
}
if (shooter.vehicle) {
kill.shooterVehicle = shooter.vehicle;
}
if (target.vehicle) {
kill.targetVehicle = target.vehicle;
}
stats.kills.push(kill);
} }
} else if (line.includes('(Budget)')) { } else if (line.includes('(Budget)')) {
/** /**
@ -173,9 +190,9 @@ const parseWarLog = (lineArray, war) => {
const stabilized = !!line.includes('stabilisiert."'); const stabilized = !!line.includes('stabilisiert."');
const medicName = line.substring(line.lastIndexOf('wurde von ') + 10, const medicName = line.substring(line.lastIndexOf('wurde von ') + 10,
line.lastIndexOf(stabilized ? ' stabilisiert' : ' wiederbelebt')); line.lastIndexOf(stabilized ? ' stabilisiert' : ' wiederbelebt'));
const medic = getPlayerAndFractionFromString(medicName); const medic = getPlayerInfoFromString(medicName);
const patientName = line.substring(line.lastIndexOf('--- ') + 4, line.lastIndexOf(' wurde von')); const patientName = line.substring(line.lastIndexOf('--- ') + 4, line.lastIndexOf(' wurde von'));
const patient = getPlayerAndFractionFromString(patientName); const patient = getPlayerInfoFromString(patientName);
stats.revive.push({ stats.revive.push({
war: war._id, war: war._id,
@ -191,9 +208,9 @@ const parseWarLog = (lineArray, war) => {
*/ */
stats.clean.push(line); stats.clean.push(line);
const driverString = line.substring(line.lastIndexOf('wurde von ') + 10, line.lastIndexOf(' eingeflogen')); const driverString = line.substring(line.lastIndexOf('wurde von ') + 10, line.lastIndexOf(' eingeflogen'));
const driver = getPlayerAndFractionFromString(driverString); const driver = getPlayerInfoFromString(driverString);
const passengerString = line.substring(line.lastIndexOf('--- ') + 3, line.lastIndexOf(' wurde von')); const passengerString = line.substring(line.lastIndexOf('--- ') + 3, line.lastIndexOf(' wurde von'));
const passenger = getPlayerAndFractionFromString(passengerString); const passenger = getPlayerInfoFromString(passengerString);
const distance = parseInt(line.substring(line.lastIndexOf('eingeflogen (') + 13, line.lastIndexOf('m)') - 1)); const distance = parseInt(line.substring(line.lastIndexOf('eingeflogen (') + 13, line.lastIndexOf('m)') - 1));
stats.transport.push({ stats.transport.push({
@ -285,15 +302,20 @@ const getBudgetEntry = (budg, warId, warDate) => {
}; };
}; };
const getPlayerAndFractionFromString = (inputString) => { const getPlayerInfoFromString = (inputString) => {
const resPlayer = {};
const playerNameRegexMatch = playerNameRegex.exec(inputString); const playerNameRegexMatch = playerNameRegex.exec(inputString);
const sideMatch = sideRegex.exec(inputString); const sideMatch = sideRegex.exec(inputString);
// SINGLE PLAYER NAME // SINGLE PLAYER NAME
let name; let name;
if (playerNameRegexMatch && playerNameRegexMatch.length >= 2) { if (playerNameRegexMatch && playerNameRegexMatch.length >= 2) {
// NAME
name = playerNameRegexMatch[2].trim(); name = playerNameRegexMatch[2].trim();
// do not return player for 'Error: No unit'
if (!name && name === 'Error: No unit') {
return;
}
resPlayer.name = name;
} }
// ADDITIONAL PLAYER NAMES // ADDITIONAL PLAYER NAMES
let additionalPlayerMatch; let additionalPlayerMatch;
@ -317,34 +339,31 @@ const getPlayerAndFractionFromString = (inputString) => {
} }
} }
// const magazineMatch = magazineRegex.exec(inputString); if (side && side !== 'ENEMY') {
// const vehicleMatch = vehicleRegex.exec(inputString); resPlayer.fraction = side === 'WEST' ? 'BLUFOR' : 'OPFOR';
}
// if (magazineMatch && magazineMatch.length >= 3) {
// MAGAZINE // MAGAZINE
// console.log(magazineMatch[2]) const magazineMatch = magazineRegex.exec(inputString);
// } if (magazineMatch && magazineMatch.length >= 3) {
let magazine = magazineMatch[2];
if (new RegExp('\\(.*$').test(magazine)) {
magazine = magazine.concat(')');
}
resPlayer.magazine = magazine;
}
// if (vehicleMatch && vehicleMatch.length >= 3 && vehicleMatch[2]) {
// let vehicle = vehicleMatch[2];
// if (new RegExp("\\(.*$").test(vehicle)) {
// vehicle = vehicle.concat(")");
// }
// VEHICLE // VEHICLE
// console.log(vehicle) const vehicleMatch = vehicleRegex.exec(inputString);
// } if (vehicleMatch && vehicleMatch.length >= 3 && vehicleMatch[2]) {
let vehicle = vehicleMatch[2];
let fraction; if (new RegExp('\\(.*$').test(vehicle)) {
if (side) { vehicle = vehicle.concat(')');
fraction = side !== 'ENEMY' ? side === 'WEST' ? }
'BLUFOR' : 'OPFOR' : resPlayer.vehicle = vehicle;
undefined;
} }
// do not return player for 'Error: No unit' return resPlayer;
if (name && name !== 'Error: No unit') {
return {name: name, fraction: fraction};
}
}; };
const getVehicleAndFractionFromString = (nameClassFractionString) => { const getVehicleAndFractionFromString = (nameClassFractionString) => {

View File

@ -1,6 +1,6 @@
{ {
"name": "opt-cc", "name": "opt-cc",
"version": "1.7.6", "version": "1.7.7",
"author": "Florian Hartwich <hardi@noarch.de>", "author": "Florian Hartwich <hardi@noarch.de>",
"private": true, "private": true,
"scripts": { "scripts": {

View File

@ -19,9 +19,6 @@
<li routerLinkActive="active"> <li routerLinkActive="active">
<a href="https://www.opt4.net/dashboard" class="link">Zum Forum</a> <a href="https://www.opt4.net/dashboard" class="link">Zum Forum</a>
</li> </li>
<li *ngIf="!loginService.isLoggedIn()" routerLinkActive="active">
<a routerLink='{{config.loginPath}}' class="link">Login</a>
</li>
<li routerLinkActive="active"> <li routerLinkActive="active">
<a routerLink='{{config.overviewPath}}' class="link">Armeeübersicht</a> <a routerLink='{{config.overviewPath}}' class="link">Armeeübersicht</a>
</li> </li>
@ -48,6 +45,9 @@
<span class="caret"></span> <span class="caret"></span>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li>
<a routerLink="{{config.request}}/{{config.sqlDashboardPath}}">Offene Anträge</a>
</li>
<li> <li>
<a routerLink="{{config.request}}/{{config.requestPromotionPath}}">Beförderung</a> <a routerLink="{{config.request}}/{{config.requestPromotionPath}}">Beförderung</a>
</li> </li>
@ -84,6 +84,9 @@
<li *ngIf="loginService.isLoggedIn()" class="link" style="cursor: pointer"> <li *ngIf="loginService.isLoggedIn()" class="link" style="cursor: pointer">
<a (click)="logout()">Abmelden</a> <a (click)="logout()">Abmelden</a>
</li> </li>
<li *ngIf="!loginService.isLoggedIn()" routerLinkActive="active">
<a routerLink='{{config.loginPath}}' class="link">Login</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -5,6 +5,7 @@ export class AppConfig {
public readonly apiAppUserPath = this.apiUrl + '/account/'; public readonly apiAppUserPath = this.apiUrl + '/account/';
public readonly apiAuthenticationPath = this.apiUrl + '/authenticate'; public readonly apiAuthenticationPath = this.apiUrl + '/authenticate';
public readonly apiAwardPath = this.apiUrl + '/awardings'; public readonly apiAwardPath = this.apiUrl + '/awardings';
public readonly apiAwardSquadPath = this.apiUrl + '/awardings/unprocessed';
public readonly apiCampaignPath = this.apiUrl + '/campaigns'; public readonly apiCampaignPath = this.apiUrl + '/campaigns';
public readonly apiDecorationPath = this.apiUrl + '/decorations/'; public readonly apiDecorationPath = this.apiUrl + '/decorations/';
public readonly apiLogsPath = this.apiUrl + '/logs'; public readonly apiLogsPath = this.apiUrl + '/logs';
@ -17,7 +18,6 @@ export class AppConfig {
public readonly apiSquadPath = this.apiUrl + '/squads/'; public readonly apiSquadPath = this.apiUrl + '/squads/';
public readonly apiUserPath = this.apiUrl + '/users/'; public readonly apiUserPath = this.apiUrl + '/users/';
public readonly apiWarPath = this.apiUrl + '/wars'; public readonly apiWarPath = this.apiUrl + '/wars';
} }
export const RouteConfig = { export const RouteConfig = {
@ -34,5 +34,6 @@ export const RouteConfig = {
requestAwardPath: 'award', requestAwardPath: 'award',
requestPromotionPath: 'promotion', requestPromotionPath: 'promotion',
confirmAwardPath: 'confirm-award', confirmAwardPath: 'confirm-award',
confirmPromotionPath: 'confirm-promotion' confirmPromotionPath: 'confirm-promotion',
sqlDashboardPath: 'sql-dashboard',
}; };

View File

@ -1,6 +1,6 @@
<form class="form-signin" (ngSubmit)="login(userName.value, password.value)"> <form class="form-signin" (ngSubmit)="login(userName.value, password.value)">
<div class="row"> <div class="row" style="position: absolute;width: 400px;left: 40%;">
<h2 style="text-align: center;" class="form-signin-heading">Anmelden</h2> <h2 style="text-align: center;" class="form-signin-heading">Anmelden</h2>
<label for="inputEmail" class="sr-only">Benutzername</label> <label for="inputEmail" class="sr-only">Benutzername</label>
@ -23,7 +23,5 @@
</div> </div>
</form> </form>

View File

@ -94,6 +94,7 @@ export interface Promotion {
oldRankLvl: number; oldRankLvl: number;
newRankLvl: number; newRankLvl: number;
rejectReason?: string; rejectReason?: string;
confirmed?: number;
} }
export interface Decoration { export interface Decoration {

View File

@ -8,10 +8,11 @@ import {ConfirmAwardComponent} from './confirm-award/confirm-award.component';
import {ConfirmPromotionComponent} from './confirm-promotion/confirm-promotion.component'; import {ConfirmPromotionComponent} from './confirm-promotion/confirm-promotion.component';
import {RequestAwardComponent} from './award/req-award.component'; import {RequestAwardComponent} from './award/req-award.component';
import {RequestPromotionComponent} from './promotion/req-promotion.component'; import {RequestPromotionComponent} from './promotion/req-promotion.component';
import {SqlDashboardComponent} from './sql-dashboard/sql-dashboard.component';
@NgModule({ @NgModule({
declarations: [RequestComponent, RequestPromotionComponent, RequestAwardComponent, ConfirmPromotionComponent, declarations: [RequestComponent, RequestPromotionComponent, RequestAwardComponent, ConfirmPromotionComponent,
ConfirmAwardComponent, FilterRankPipe], ConfirmAwardComponent, SqlDashboardComponent, FilterRankPipe],
imports: [CommonModule, SharedModule, requestRouterModule] imports: [CommonModule, SharedModule, requestRouterModule]
}) })
export class RequestModule { export class RequestModule {

View File

@ -8,6 +8,7 @@ import {RequestPromotionComponent} from './promotion/req-promotion.component';
import {RequestComponent} from './request.component'; import {RequestComponent} from './request.component';
import {RouteConfig} from '../app.config'; import {RouteConfig} from '../app.config';
import {LoginGuardHL, LoginGuardSQL} from '../login'; import {LoginGuardHL, LoginGuardSQL} from '../login';
import {SqlDashboardComponent} from './sql-dashboard/sql-dashboard.component';
export const requestRoutes: Routes = [{ export const requestRoutes: Routes = [{
@ -23,6 +24,11 @@ export const requestRoutes: Routes = [{
component: RequestPromotionComponent, component: RequestPromotionComponent,
canActivate: [LoginGuardSQL] canActivate: [LoginGuardSQL]
}, },
{
path: RouteConfig.sqlDashboardPath,
component: SqlDashboardComponent,
canActivate: [LoginGuardSQL]
},
{ {
path: RouteConfig.confirmAwardPath, path: RouteConfig.confirmAwardPath,
component: ConfirmAwardComponent, component: ConfirmAwardComponent,
@ -32,7 +38,7 @@ export const requestRoutes: Routes = [{
path: RouteConfig.confirmPromotionPath, path: RouteConfig.confirmPromotionPath,
component: ConfirmPromotionComponent, component: ConfirmPromotionComponent,
canActivate: [LoginGuardHL] canActivate: [LoginGuardHL]
} },
]; ];
export const requestRouterModule: ModuleWithProviders = RouterModule.forChild(requestRoutes); export const requestRouterModule: ModuleWithProviders = RouterModule.forChild(requestRoutes);

View File

@ -0,0 +1,24 @@
.overview {
margin-left: 25px !important;
}
.decoration-preview {
padding: 5px;
}
.table {
overflow-wrap: break-word;
table-layout: fixed;
}
.table-container {
margin-top: 40px;
overflow-x: auto;
width: 90%;
min-width: 800px;
padding: 5px;
}
h3 {
margin: 80px 0 20px -20px;
}

View File

@ -0,0 +1,78 @@
<div class="overview">
<h3>SQL Dashboard</h3>
<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>
</tr>
</thead>
<tbody *ngFor="let promotion of promotions">
<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>
</tr>
</tbody>
</table>
</div>
<div class="table-container">
<label>Anträge für Orden/ Auszeichnungen</label>
<table class="table table-hover">
<thead>
<tr>
<th class="col-sm-1">Teilnehmer</th>
<th class="col-sm-1">Bild</th>
<th class="col-sm-2">Bezeichnung</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>
</tr>
</thead>
<tbody *ngFor="let award of awards">
<tr>
<td class="table-cell-id">
{{award.userId.username}}
</td>
<td class="table-cell-id" *ngIf="award.decorationId.isMedal">
<img height="40px" src="resource/decoration/{{award.decorationId._id}}.png">
</td>
<td class="table-cell-id" *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>
</tr>
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,50 @@
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {Award, Promotion, Rank} from '../../models/model-interfaces';
import {RankService} from '../../services/army-management/rank.service';
import {PromotionService} from '../../services/army-management/promotion.service';
import {LoginService} from '../../services/app-user-service/login-service';
import {AwardingService} from '../../services/army-management/awarding.service';
@Component({
templateUrl: './sql-dashboard.component.html',
styleUrls: ['./sql-dashboard.component.css', '../../style/overview.css'],
})
export class SqlDashboardComponent implements OnInit {
ranks: Rank[];
promotions: Promotion[];
awards: Award[];
constructor(private router: Router,
private route: ActivatedRoute,
private rankService: RankService,
private promotionService: PromotionService,
private awardingService: AwardingService,
private loginService: LoginService) {
}
ngOnInit() {
const currentUser = this.loginService.getCurrentUser();
this.promotionService.getSquadPromotions(currentUser.squad._id).subscribe(promotions => {
this.promotions = promotions.filter(promotion => promotion.confirmed === 0);
});
this.awardingService.getUnprocessedSquadAwards(currentUser.squad._id).subscribe(awards => {
this.awards = awards;
});
this.rankService.findRanks('', currentUser.squad.fraction).subscribe(ranks => {
this.ranks = ranks;
});
}
cancel() {
this.router.navigate(['..'], {relativeTo: this.route});
return false;
}
}

View File

@ -25,6 +25,11 @@ export class AwardingService {
}); });
} }
getUnprocessedSquadAwards(squadId?: string) {
return this.http.get(this.config.apiAwardSquadPath.concat('/').concat(squadId))
.map(res => res.json());
}
/** /**
* get awards array with populated decorations * get awards array with populated decorations
*/ */

View File

@ -40,7 +40,8 @@ export class FractionStatsComponent implements OnInit, OnChanges {
tmpFlagCaptureData; tmpFlagCaptureData;
colorScheme = { colorScheme = {
domain: [Fraction.COLOR_BLUFOR, Fraction.COLOR_OPFOR] domain: [Fraction.COLOR_BLUFOR, Fraction.COLOR_OPFOR, Fraction.COLOR_BLUFOR_LIGHT, Fraction.COLOR_OPFOR_LIGHT,
Fraction.COLOR_BLUFOR_DARK, Fraction.COLOR_OPFOR_DARK, Fraction.COLOR_BLUFOR_GREY, Fraction.COLOR_OPFOR_GREY]
}; };
labelPoints = 'Punkte'; labelPoints = 'Punkte';
@ -259,7 +260,8 @@ export class FractionStatsComponent implements OnInit, OnChanges {
if (this.initialized.vehicle) { if (this.initialized.vehicle) {
return; return;
} }
let vehicleKillCountBlufor = 0, vehicleKillCountOpfor = 0; let vehicleKillCountBlufor = 0, vehicleKillCountOpfor = 0, vehicleLightCountBlufor = 0, vehicleHeavyCountBlufor = 0,
vehicleAirCountBlufor = 0, vehicleLightCountOpfor = 0, vehicleHeavyCountOpfor = 0, vehicleAirCountOpfor = 0;
for (const {transportEntry: vehicleEntry, index} of this.logData.vehicle.map((transport, pos) => ({ for (const {transportEntry: vehicleEntry, index} of this.logData.vehicle.map((transport, pos) => ({
transportEntry: transport, transportEntry: transport,
index: pos index: pos
@ -267,12 +269,40 @@ export class FractionStatsComponent implements OnInit, OnChanges {
const vehicleEntryDate = new Date(vehicleEntry.time); const vehicleEntryDate = new Date(vehicleEntry.time);
if (vehicleEntry.fraction === 'BLUFOR') { if (vehicleEntry.fraction === 'BLUFOR') {
vehicleKillCountBlufor++; vehicleKillCountBlufor++;
switch (vehicleEntry.vehicleClass) {
case 'LIGHT':
vehicleLightCountBlufor++;
break;
case 'HEAVY':
vehicleHeavyCountBlufor++;
break;
case 'AIR':
vehicleAirCountBlufor++;
break;
}
} else { } else {
vehicleKillCountOpfor++; vehicleKillCountOpfor++;
switch (vehicleEntry.vehicleClass) {
case 'LIGHT':
vehicleLightCountOpfor++;
break;
case 'HEAVY':
vehicleHeavyCountOpfor++;
break;
case 'AIR':
vehicleAirCountOpfor++;
break;
}
} }
if (this.isTwoMinutesAhead(vehicleEntryDate, this.tmpVehicleData) || index === this.logData.vehicle.length - 1) { if (this.isTwoMinutesAhead(vehicleEntryDate, this.tmpVehicleData) || index === this.logData.vehicle.length - 1) {
this.tmpVehicleData[0].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleKillCountBlufor)); this.tmpVehicleData[0].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleKillCountBlufor));
this.tmpVehicleData[1].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleKillCountOpfor)); this.tmpVehicleData[1].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleKillCountOpfor));
this.tmpVehicleData[2].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleLightCountBlufor));
this.tmpVehicleData[3].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleLightCountOpfor));
this.tmpVehicleData[4].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleHeavyCountBlufor));
this.tmpVehicleData[5].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleHeavyCountOpfor));
this.tmpVehicleData[6].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleAirCountBlufor));
this.tmpVehicleData[7].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleAirCountOpfor));
} }
} }
this.addFinalTimeData(this.tmpVehicleData); this.addFinalTimeData(this.tmpVehicleData);
@ -336,7 +366,9 @@ export class FractionStatsComponent implements OnInit, OnChanges {
this.tmpBudgetData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); this.tmpBudgetData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR);
this.tmpKillData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); this.tmpKillData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR);
this.tmpFrienlyFireData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); this.tmpFrienlyFireData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR);
this.tmpVehicleData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); this.tmpVehicleData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR,
Fraction.BLUFOR.concat(' Leicht'), Fraction.OPFOR.concat(' Leicht'), Fraction.BLUFOR.concat(' Schwer'),
Fraction.OPFOR.concat(' Schwer'), Fraction.BLUFOR.concat(' Luft'), Fraction.OPFOR.concat(' Luft'));
this.tmpTransportData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); this.tmpTransportData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR);
this.tmpReviveData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); this.tmpReviveData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR);
this.tmpStabilizeData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); this.tmpStabilizeData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR);
@ -358,7 +390,7 @@ export class FractionStatsComponent implements OnInit, OnChanges {
this.tmpBudgetData[0].series.push(ChartUtils.getSeriesEntry(endDate, this.war.endBudgetBlufor)); this.tmpBudgetData[0].series.push(ChartUtils.getSeriesEntry(endDate, this.war.endBudgetBlufor));
this.tmpBudgetData[1].series.push(ChartUtils.getSeriesEntry(endDate, this.war.endBudgetOpfor)); this.tmpBudgetData[1].series.push(ChartUtils.getSeriesEntry(endDate, this.war.endBudgetOpfor));
} else { } else {
for (const j in [0, 1]) { for (let j = 0; j < tmpCollection.length; j++) {
if (tmpCollection[j].series[tmpCollection[j].series.length - 1].name < endDate) { if (tmpCollection[j].series[tmpCollection[j].series.length - 1].name < endDate) {
tmpCollection[j].series.push( tmpCollection[j].series.push(
ChartUtils.getSeriesEntry(endDate, tmpCollection[j].series[tmpCollection[j].series.length - 1].value) ChartUtils.getSeriesEntry(endDate, tmpCollection[j].series[tmpCollection[j].series.length - 1].value)
@ -367,5 +399,4 @@ export class FractionStatsComponent implements OnInit, OnChanges {
} }
} }
} }
} }

View File

@ -71,7 +71,7 @@ export class ScoreboardComponent implements OnChanges {
exportCSV() { exportCSV() {
let csvOut = ''; let csvOut = '';
for (let i = 0; i < this.tableHead.length; i++) { for (let i = 0; i < this.tableHead.length; i++) {
csvOut += this.tableHead[i]; csvOut += this.tableHead[i].head;
if (i !== this.tableHead.length - 1) { if (i !== this.tableHead.length - 1) {
csvOut += ','; csvOut += ',';
} }

View File

@ -73,7 +73,9 @@
<th class="col-sm-2">Begründung</th> <th class="col-sm-2">Begründung</th>
<th class="col-sm-1 text-right">Datum</th> <th class="col-sm-1 text-right">Datum</th>
<th class="col-sm-1 text-center">Status</th> <th class="col-sm-1 text-center">Status</th>
<th class="col-sm-1 text-center"></th> <th class="col-sm-1 text-center">
<span class="btn btn-default" (click)="deleteAwarding()">Löschen</span>
</th>
</tr> </tr>
</thead> </thead>
<tbody *ngFor="let award of awards"> <tbody *ngFor="let award of awards">
@ -94,10 +96,15 @@
<a class="small text-nowrap">{{award.date | date: 'dd.MM.yyyy'}}</a> <a class="small text-nowrap">{{award.date | date: 'dd.MM.yyyy'}}</a>
</td> </td>
<td class="text-center"> <td class="text-center">
{{award.confirmed === 0? 'In Bearbeitung' : (award.confirmed === 1? 'Genehmigt' : 'Abgelehnt')}} {{award.confirmed === 0 ? 'In Bearbeitung' : (award.confirmed === 1 ? 'Genehmigt' : 'Abgelehnt')}}
</td> </td>
<td class="text-center"> <td class="text-center">
<span class="glyphicon glyphicon-trash trash" title="Löschen" (click)="deleteAwarding(award._id)"></span> <label>
<input name="deleteAward"
type="checkbox"
value="{{award._id}}"
[(ngModel)]="award.checked">
</label>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View File

@ -51,7 +51,6 @@ export class AwardUserComponent implements OnInit {
.subscribe(id => this.userId = id); .subscribe(id => this.userId = id);
} }
toggleDecoPreview(descriptionField, decorationId, image) { toggleDecoPreview(descriptionField, decorationId, image) {
if (decorationId !== '0') { if (decorationId !== '0') {
this.decoPreviewDisplay = 'flex'; // visible & keep same height for all children this.decoPreviewDisplay = 'flex'; // visible & keep same height for all children
@ -72,10 +71,10 @@ export class AwardUserComponent implements OnInit {
const reason = reasonField.value; const reason = reasonField.value;
if (decorationId && reason.length > 0) { if (decorationId && reason.length > 0) {
const award = { const award = {
'userId': this.userId, userId: this.userId,
'decorationId': decorationId, decorationId: decorationId,
'reason': reason, reason: reason,
'date': Date.now() date: Date.now()
}; };
this.awardingService.addAwarding(award).subscribe(() => { this.awardingService.addAwarding(award).subscribe(() => {
this.awardingService.getUserAwardings(this.userId) this.awardingService.getUserAwardings(this.userId)
@ -93,22 +92,28 @@ export class AwardUserComponent implements OnInit {
} }
} }
deleteAwarding(awardingId) { deleteAwarding() {
this.awardingService.deleteAwarding(awardingId).subscribe((res) => { const checkedAwardings = this.awards.filter(award => award['checked'] === true);
if (checkedAwardings.length > 0) {
checkedAwardings.forEach(awarding => {
this.awardingService.deleteAwarding(awarding._id).subscribe((res) => {
this.awardingService.getUserAwardings(this.userId) this.awardingService.getUserAwardings(this.userId)
.subscribe((awards) => { .subscribe((awards) => {
this.awards = awards; this.awards = awards;
});
});
});
this.showSuccessLabel = true; this.showSuccessLabel = true;
setTimeout(() => { setTimeout(() => {
this.showSuccessLabel = false; this.showSuccessLabel = false;
}, 2000); }, 4000);
}); }
});
} }
cancel() { cancel() {
this.router.navigate(['../..'], {relativeTo: this.route}); this.router.navigate(['../..'], {relativeTo: this.route});
return false; return false;
} }
} }

View File

@ -53,7 +53,9 @@ export class EditUserComponent implements OnInit {
user.squadId = '0'; user.squadId = '0';
this.ranksDisplay = 'none'; this.ranksDisplay = 'none';
} else { } else {
this.rankService.findRanks('', user.squadId.fraction).subscribe(ranks => { this.rankService
.findRanks('', user.squadId.fraction)
.subscribe(ranks => {
this.ranks = ranks; this.ranks = ranks;
this.ranksDisplay = 'block'; this.ranksDisplay = 'block';
}); });
@ -68,7 +70,9 @@ export class EditUserComponent implements OnInit {
toggleRanks() { toggleRanks() {
if (this.user.squadId !== '0') { if (this.user.squadId !== '0') {
this.rankService.findRanks('', this.user.squadId.fraction).subscribe( this.rankService
.findRanks('', this.user.squadId.fraction)
.subscribe(
ranks => { ranks => {
this.ranks = ranks; this.ranks = ranks;
this.ranksDisplay = 'block'; this.ranksDisplay = 'block';

View File

@ -34,6 +34,5 @@ export class UserItemComponent {
delete() { delete() {
this.userDelete.emit(this.user); this.userDelete.emit(this.user);
} }
} }

View File

@ -41,7 +41,6 @@ export class UserListComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
this.users$ = this.userService.users$; this.users$ = this.userService.users$;
const paramsStream = this.route.queryParams const paramsStream = this.route.queryParams
@ -106,5 +105,4 @@ export class UserListComponent implements OnInit {
this.location.replaceState(absoluteUrl, queryPart); this.location.replaceState(absoluteUrl, queryPart);
} }
} }

View File

@ -2,5 +2,11 @@ export enum Fraction {
BLUFOR = 'NATO', BLUFOR = 'NATO',
OPFOR = 'CSAT', OPFOR = 'CSAT',
COLOR_BLUFOR = '#0000FF', COLOR_BLUFOR = '#0000FF',
COLOR_OPFOR = '#B22222' COLOR_BLUFOR_LIGHT = '#6666dd',
COLOR_BLUFOR_DARK = '#0C0CA6',
COLOR_BLUFOR_GREY = '#515179',
COLOR_OPFOR = '#B22222',
COLOR_OPFOR_DARK = '#890F0F',
COLOR_OPFOR_LIGHT = '#fb5555',
COLOR_OPFOR_GREY = '#955c5f'
} }