Redesign user editing
parent
7c55d14538
commit
5201463751
|
@ -171,6 +171,61 @@ users.route('/:id')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
.put(apiAuthenticationMiddleware, (req, res,next) => {
|
||||||
|
// first check that the given element id is the same as the URL id
|
||||||
|
if (!req.body || req.body._id !== req.params.id) {
|
||||||
|
// the URL does not fit the given element
|
||||||
|
var 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.
|
||||||
|
}
|
||||||
|
// main difference of PUT and PATCH is that PUT expects all data in request: checked by using the schema
|
||||||
|
var video = new UserModel(req.body);
|
||||||
|
UserModel.findById(req.params.id, req.body, {new: true}, function (err, item) {
|
||||||
|
// with parameter {new: true} the TweetNModel will return the new and changed object from the DB and not the old one.
|
||||||
|
if (err) {
|
||||||
|
err.status = codes.wrongrequest;
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
else if (!item) {
|
||||||
|
err = new Error("item not found");
|
||||||
|
err.status = codes.notfound;
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
// optional task 3b: check that version is still accurate
|
||||||
|
else if (video.__v !== item.__v) {
|
||||||
|
err = new Error("version outdated. Meanwhile update on item happened. Please GET resource again")
|
||||||
|
err.status = codes.conflict;
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
// now update all fields in DB item with body data in variable video
|
||||||
|
for (var field in UserModel.schema.paths) {
|
||||||
|
if ((field !== '_id') && (field !== '__v')) {
|
||||||
|
// this includes undefined. is important to reset attributes that are missing in req.body
|
||||||
|
item.set(field, video[field]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional task 3: update updatedAt and increase version
|
||||||
|
item.updatedAt = new Date();
|
||||||
|
item.increment(); // this sets __v++
|
||||||
|
item.save(function (err) {
|
||||||
|
if (!err) {
|
||||||
|
res.locals.items = item;
|
||||||
|
} else {
|
||||||
|
err.status = codes.wrongrequest;
|
||||||
|
err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors);
|
||||||
|
}
|
||||||
|
getExtendedUser(item, next, (extUser) => {
|
||||||
|
res.locals.items = extUser;
|
||||||
|
res.locals.processed = true;
|
||||||
|
return next();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
.delete(apiAuthenticationMiddleware, (req, res, next) => {
|
.delete(apiAuthenticationMiddleware, (req, res, next) => {
|
||||||
UserModel.findByIdAndRemove(req.params.id, (err, item) => {
|
UserModel.findByIdAndRemove(req.params.id, (err, item) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -241,7 +296,7 @@ let getExtendedUser = (user, next, callback) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
extUser.rank = null;
|
extUser.rank = {level: user.rankLvl};
|
||||||
addAwards(extUser).then(() => {
|
addAwards(extUser).then(() => {
|
||||||
callback(extUser);
|
callback(extUser);
|
||||||
})
|
})
|
||||||
|
|
|
@ -20,5 +20,5 @@ li {
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
float: left;
|
float: left;
|
||||||
width: calc(100% - 300px);
|
width: calc(100% - 400px);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,13 @@ export class HttpClient {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
put(url, data) {
|
||||||
|
let headers = this.createAuthorizationHeader();
|
||||||
|
return this.http.put(url, data, {
|
||||||
|
headers: headers
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
patch(url, data) {
|
patch(url, data) {
|
||||||
let headers = this.createAuthorizationHeader();
|
let headers = this.createAuthorizationHeader();
|
||||||
return this.http.patch(url, data, {
|
return this.http.patch(url, data, {
|
||||||
|
|
|
@ -48,7 +48,7 @@ export class UserService {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUser(user) {
|
updateUser(user) {
|
||||||
return this.http.patch(this.config.apiUrl + this.config.apiUserPath + user._id, user)
|
return this.http.put(this.config.apiUrl + this.config.apiUserPath + user._id, user)
|
||||||
.map(res => res.json())
|
.map(res => res.json())
|
||||||
.do(savedUser => {
|
.do(savedUser => {
|
||||||
const action = {type: EDIT, data: savedUser};
|
const action = {type: EDIT, data: savedUser};
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
.decoration-preview {
|
||||||
|
background-color: white;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
<form #form="ngForm" class="overview">
|
||||||
|
<h3>Teilnehmer auszeichnen</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="title">Name</label>
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
[(ngModel)]="user.username"
|
||||||
|
name="title"
|
||||||
|
id="title"
|
||||||
|
required maxlength="50"/>
|
||||||
|
|
||||||
|
<show-error text="Name" path="title"></show-error>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="squad">Squad</label>
|
||||||
|
<select class="form-control"
|
||||||
|
name="squad"
|
||||||
|
id="squad"
|
||||||
|
[(ngModel)]="user.squad"
|
||||||
|
[compareWith]="equals"
|
||||||
|
(change)="toggleRanks()">
|
||||||
|
<option [value]="0">Ohne Fraktion/ Squad</option>
|
||||||
|
<option *ngFor="let squad of squads" [ngValue]="squad">
|
||||||
|
{{squad.fraction == 'BLUFOR'? 'NATO' : 'CSAT'}}: {{squad.name}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<show-error text="Squad" path="squad"></show-error>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" [style.display]="ranksDisplay">
|
||||||
|
<label for="rank">Rang</label>
|
||||||
|
<select class="form-control"
|
||||||
|
name="rank"
|
||||||
|
id="rank" [ngModel]="user.rank?.level"
|
||||||
|
#rankLevel
|
||||||
|
style="min-width: 200px;">
|
||||||
|
<option *ngFor="let rank of ranks" [value]="rank.level">{{rank.name}}</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<show-error text="Squad" path="squad"></show-error>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="cancel"
|
||||||
|
(click)="cancel()"
|
||||||
|
class="btn btn-default">
|
||||||
|
Abbrechen
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button id="save"
|
||||||
|
(click)="saveUser(rankLevel.value)"
|
||||||
|
class="btn btn-default"
|
||||||
|
[disabled]="!form.valid">
|
||||||
|
Bestätigen
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="section-content">
|
||||||
|
<div class="container">
|
||||||
|
<div class="table-responsive table-responsive-dropdowns">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="col-sm-2">Grafik</th>
|
||||||
|
<th class="col-sm-3">Bezeichnung</th>
|
||||||
|
<th class="col-sm-3">Begründung</th>
|
||||||
|
<th class="col-sm-3 text-right">Datum</th>
|
||||||
|
<th class="col-sm-2 text-center">LÖschen</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!--begin forEach-->
|
||||||
|
<tr>
|
||||||
|
<td class="table-cell-id">
|
||||||
|
1
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a class="small text-nowrap">2016-04-16 12:02:12</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
Herman
|
||||||
|
Melville
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
222
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="label label-default">Paid online</span>
|
||||||
|
</td>
|
||||||
|
<td class="table-cell-actions">
|
||||||
|
<a href="/orders/edit/1" class="item-action" title="Edit">
|
||||||
|
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
|
||||||
|
</a>
|
||||||
|
<a href="#" class="item-action" title="Mark as delivered">
|
||||||
|
<span class="glyphicon glyphicon-ok-sign" aria-hidden="true"></span>
|
||||||
|
</a>
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="item-action" type="button" id="itemOptions1" data-toggle="dropdown" aria-haspopup="true"
|
||||||
|
aria-expanded="false">
|
||||||
|
<span class="glyphicon glyphicon-option-vertical" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="itemOptions1">
|
||||||
|
<li>
|
||||||
|
<button type="button" class="btn btn-dropdown" data-toggle="confirm"
|
||||||
|
data-confirm-message="Do you really want to cancel this order?" data-confirm-yes="Yes"
|
||||||
|
data-confirm-no="No">
|
||||||
|
<span class="glyphicon glyphicon-remove-sign offset-right" aria-hidden="true"></span>
|
||||||
|
Cancel order
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!--end forEach-->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</form>
|
|
@ -0,0 +1,134 @@
|
||||||
|
import {Component, ViewChild} from "@angular/core";
|
||||||
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
|
import * as model from "../../models/model-interfaces";
|
||||||
|
import {Rank, Squad, User} from "../../models/model-interfaces";
|
||||||
|
import {UserService} from "../../services/user-service/user.service";
|
||||||
|
import {SquadService} from "../../services/squad-service/squad.service";
|
||||||
|
import {RankService} from "../../services/rank-service/rank.service";
|
||||||
|
import {Subscription} from "rxjs";
|
||||||
|
import {NgForm} from "@angular/forms";
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: './user-award.component.html',
|
||||||
|
styleUrls: ['./user-award.component.css', '../../style/new-entry-form.css'],
|
||||||
|
})
|
||||||
|
export class UserAwardComponent {
|
||||||
|
|
||||||
|
@ViewChild(NgForm) form: NgForm;
|
||||||
|
|
||||||
|
subscription: Subscription;
|
||||||
|
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
model = model;
|
||||||
|
|
||||||
|
showSuccessLabel = false;
|
||||||
|
|
||||||
|
ranksDisplay = 'none';
|
||||||
|
|
||||||
|
user: User = {squad: {}};
|
||||||
|
|
||||||
|
squads: Squad[] = [];
|
||||||
|
|
||||||
|
ranks: Rank[] = [];
|
||||||
|
|
||||||
|
saved = false;
|
||||||
|
|
||||||
|
constructor(private router: Router,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private userService: UserService,
|
||||||
|
private squadService: SquadService,
|
||||||
|
private rankService: RankService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
|
||||||
|
// this.subscription = this.route.params
|
||||||
|
// .map(params => params['id'])
|
||||||
|
// .filter(id => id != undefined)
|
||||||
|
// .flatMap(id => this.userService.getUser(id))
|
||||||
|
// .subscribe(user => {
|
||||||
|
// if (user.squad === null) {
|
||||||
|
// user.squad = "0";
|
||||||
|
// this.ranksDisplay = 'none';
|
||||||
|
// } else {
|
||||||
|
// this.rankService.findRanks('', user.squad.fraction).subscribe(ranks => {
|
||||||
|
// this.ranks = ranks;
|
||||||
|
// this.ranksDisplay = 'block';
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// this.user = user;
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// this.squadService.findSquads().subscribe(squads => {
|
||||||
|
// this.squads = squads;
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
// this.subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleRanks() {
|
||||||
|
if (this.user.squad != '0') {
|
||||||
|
this.rankService.findRanks('', this.user.squad.fraction).subscribe(
|
||||||
|
ranks => {
|
||||||
|
this.ranks = ranks;
|
||||||
|
this.ranksDisplay = 'block';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.ranksDisplay = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveUser(rankLevel) {
|
||||||
|
const updateObject = {
|
||||||
|
_id: this.user._id,
|
||||||
|
username: this.user.username,
|
||||||
|
squadId: this.user.squad._id,
|
||||||
|
rankLvl: rankLevel
|
||||||
|
};
|
||||||
|
this.userService.updateUser(updateObject)
|
||||||
|
.subscribe(user => {
|
||||||
|
this.user = user;
|
||||||
|
this.showSuccessLabel = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.showSuccessLabel = false;
|
||||||
|
}, 2000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
this.router.navigate(['../..'], {relativeTo: this.route});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compare ngValue with ngModel to assign selected element
|
||||||
|
*/
|
||||||
|
equals(o1: Squad, o2: Squad) {
|
||||||
|
if (o1 && o2) {
|
||||||
|
return o1._id === o2._id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteUser(confirm) {
|
||||||
|
if (confirm.toLowerCase() === this.user.username.toLocaleLowerCase()) {
|
||||||
|
this.userService.deleteUser(this.user)
|
||||||
|
.subscribe((res) => {
|
||||||
|
this.router.navigate(['../..'], {relativeTo: this.route});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
canDeactivate(): boolean {
|
||||||
|
if (this.saved || !this.form.dirty) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return window.confirm(`Ihr Formular besitzt ungespeicherte Änderungen, möchten Sie die Seite wirklich verlassen?`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ div.user-list-entry, a.user-list-entry {
|
||||||
width: 475px;
|
width: 475px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
border: lightgrey solid 1px;
|
border: lightgrey solid 1px;
|
||||||
cursor: pointer;
|
|
||||||
margin-bottom: -1px;
|
margin-bottom: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,23 +10,36 @@ div.user-list-entry, a.user-list-entry {
|
||||||
background: lightgrey;
|
background: lightgrey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-award {
|
||||||
|
background: url(../../../assets/award.png);
|
||||||
|
width: 18px;
|
||||||
|
height: 29px;
|
||||||
|
display: block;
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
font-size: x-large;
|
font-size: large;
|
||||||
font-weight: 700;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
small {
|
small {
|
||||||
color: grey;
|
color: grey;
|
||||||
|
font-size: x-small;
|
||||||
}
|
}
|
||||||
|
|
||||||
.trash {
|
.trash {
|
||||||
padding-top: 18px;
|
font-size: 19px;
|
||||||
font-size: 17px;
|
margin-left: 12px;
|
||||||
margin-left: -10px;
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit {
|
||||||
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected {
|
.selected {
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
<div class="fade-in user-list-entry" [ngClass]="{selected : selected}" (click)="select()">
|
<div class="fade-in user-list-entry" [ngClass]="{selected : selected}">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-11">
|
<div class="col-xs-9">
|
||||||
<span>
|
<span>
|
||||||
<a>{{user.username}}</a>
|
<a (click)="select()">{{user.username}}</a>
|
||||||
</span>
|
</span>
|
||||||
<br>
|
<br>
|
||||||
<small *ngIf="user.squad && user.squad.fraction == 'OPFOR'">CSAT - {{user.squad.name}}</small>
|
<small *ngIf="user.squad && user.squad.fraction == 'OPFOR'">CSAT - {{user.squad.name}}</small>
|
||||||
<small *ngIf="user.squad && user.squad.fraction == 'BLUFOR'">NATO - {{user.squad.name}}</small>
|
<small *ngIf="user.squad && user.squad.fraction == 'BLUFOR'">NATO - {{user.squad.name}}</small>
|
||||||
<small *ngIf="!user.squad">ohne Squad/Fraktion</small>
|
<small *ngIf="!user.squad">ohne Squad/Fraktion</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<span (click)="select()" class="glyphicon glyphicon-pencil pull-left edit"></span>
|
||||||
|
<span (click)="award()" class="icon-award pull-left"></span>
|
||||||
|
<span (click)="delete(); $event.stopPropagation()" class="glyphicon glyphicon-trash trash"></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {User} from "../../models/model-interfaces";
|
||||||
styleUrls: ['./user-item.component.css'],
|
styleUrls: ['./user-item.component.css'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
inputs: ['user', 'selected'],
|
inputs: ['user', 'selected'],
|
||||||
outputs: ['userSelected', 'userDelete']
|
outputs: ['userSelected', 'userAward', 'userDelete']
|
||||||
})
|
})
|
||||||
export class UserItemComponent {
|
export class UserItemComponent {
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ export class UserItemComponent {
|
||||||
user: User;
|
user: User;
|
||||||
|
|
||||||
userSelected = new EventEmitter();
|
userSelected = new EventEmitter();
|
||||||
|
userAward = new EventEmitter();
|
||||||
userDelete = new EventEmitter();
|
userDelete = new EventEmitter();
|
||||||
|
|
||||||
constructor(private router: Router) {
|
constructor(private router: Router) {
|
||||||
|
@ -26,6 +27,10 @@ export class UserItemComponent {
|
||||||
this.userSelected.emit(this.user._id)
|
this.userSelected.emit(this.user._id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
award() {
|
||||||
|
this.userAward.emit(this.user._id)
|
||||||
|
}
|
||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
this.userDelete.emit(this.user);
|
this.userDelete.emit(this.user);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
[user]="user"
|
[user]="user"
|
||||||
(userDelete)="deleteUser(user)"
|
(userDelete)="deleteUser(user)"
|
||||||
(userSelected)="selectUser($event)"
|
(userSelected)="selectUser($event)"
|
||||||
|
(userAward)="awardUser($event)"
|
||||||
[selected]="user._id == selectedUserId">
|
[selected]="user._id == selectedUserId">
|
||||||
</pjm-user-item>
|
</pjm-user-item>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -51,11 +51,16 @@ export class UserListComponent implements OnInit {
|
||||||
this.router.navigate([{outlets: {'right': ['new']}}], {relativeTo: this.route});
|
this.router.navigate([{outlets: {'right': ['new']}}], {relativeTo: this.route});
|
||||||
}
|
}
|
||||||
|
|
||||||
selectUser(userId: string | number) {
|
selectUser(userId: string) {
|
||||||
this.selectedUserId = userId;
|
this.selectedUserId = userId;
|
||||||
this.router.navigate([{outlets: {'right': ['overview', userId]}}], {relativeTo: this.route});
|
this.router.navigate([{outlets: {'right': ['overview', userId]}}], {relativeTo: this.route});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
awardUser(userId: string) {
|
||||||
|
this.selectedUserId = userId;
|
||||||
|
this.router.navigate([{outlets: {'right': ['award', userId]}}], {relativeTo: this.route});
|
||||||
|
}
|
||||||
|
|
||||||
filterUsersByFraction(query = '', fractionFilter) {
|
filterUsersByFraction(query = '', fractionFilter) {
|
||||||
this.users$ = this.userService.findUsers(query, fractionFilter);
|
this.users$ = this.userService.findUsers(query, fractionFilter);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,200 +1,57 @@
|
||||||
<div class="overview">
|
<form #form="ngForm" class="overview">
|
||||||
<h3 style="margin-bottom: 25px">Teilnehmer-Details
|
<h3>Teilnehmer editieren</h3>
|
||||||
<span *ngIf="showSuccessLabel"
|
|
||||||
class="label label-success label-small"
|
|
||||||
style="margin-left: inherit">
|
|
||||||
Erfolgreich gespeichert
|
|
||||||
</span>
|
|
||||||
</h3>
|
|
||||||
<div *ngIf="user">
|
|
||||||
<div class="col-xs-12">
|
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="title">Name</label>
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
[(ngModel)]="user.username"
|
||||||
|
name="title"
|
||||||
|
id="title"
|
||||||
|
required maxlength="50"/>
|
||||||
|
|
||||||
<div class="div-table">
|
<show-error text="Name" path="title"></show-error>
|
||||||
<div class="div-table-row">
|
|
||||||
<div class="div-table-col content-s">
|
|
||||||
<label>Name:</label>
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content content-m" >
|
|
||||||
{{user.username}}
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-l">
|
|
||||||
<input class="form-control" width="250px" placeholder="Neuer Name" #newNameInput>
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-s">
|
|
||||||
<a class="pull-right btn btn-sm btn-block btn-default" (click)="update('username',newNameInput.value, newNameInput)" >Bestätigen</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="div-table-row">
|
|
||||||
<div class="div-table-col content-s">
|
|
||||||
<label>Fraktion:</label>
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-m" *ngIf="!user.squad">
|
|
||||||
Ohne Fraktion
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col fraction-opfor content-m" *ngIf="user.squad && user.squad.fraction == 'OPFOR'">
|
|
||||||
CSAT
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col fraction-blufor content-m" *ngIf="user.squad && user.squad.fraction == 'BLUFOR'">
|
|
||||||
NATO
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<div class="form-group">
|
||||||
<div class="div-table">
|
<label for="squad">Squad</label>
|
||||||
<div class="div-table-row">
|
<select class="form-control"
|
||||||
<div class="div-table-col content-s">
|
name="squad"
|
||||||
<label>Squad:</label>
|
id="squad"
|
||||||
</div>
|
[(ngModel)]="user.squad"
|
||||||
<div class="div-table-col content-m" *ngIf="!user.squad">Ohne Squad</div>
|
[compareWith]="equals"
|
||||||
<div class="div-table-col content content-m" *ngIf="user.squad">{{user.squad.name}}</div>
|
(change)="toggleRanks()">
|
||||||
<div class="div-table-col content content-l">
|
<option [value]="0">Ohne Fraktion/ Squad</option>
|
||||||
<select class="form-control" [ngModel]="user.squad?._id" style="min-width: 200px;" #squadSelector>
|
<option *ngFor="let squad of squads" [ngValue]="squad">
|
||||||
<option [value]="">---</option>
|
{{squad.fraction == 'BLUFOR'? 'NATO' : 'CSAT'}}: {{squad.name}}
|
||||||
<option *ngFor="let squad of squads" [value]="squad._id">{{squad.fraction}}: {{squad.name}}</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-s">
|
<show-error text="Squad" path="squad"></show-error>
|
||||||
<a class="pull-right btn btn-sm btn-default btn-block" (click)="update('squadId', squadSelector.value)">Bestätigen</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<div class="form-group" [style.display]="ranksDisplay">
|
||||||
<div class="div-table">
|
<label for="rank">Rang</label>
|
||||||
<div class="div-table-row">
|
<select class="form-control"
|
||||||
<div class="div-table-col content-s">
|
name="rank"
|
||||||
<label>Rang:</label>
|
id="rank" [ngModel]="user.rank?.level"
|
||||||
</div>
|
#rankLevel
|
||||||
<div class="div-table-col content-m" *ngIf="!user.rank">Ohne Rang</div>
|
style="min-width: 200px;">
|
||||||
<div class="div-table-col content-m content" *ngIf="user.rank">{{user.rank.name}}</div>
|
<option *ngFor="let rank of ranks" [value]="rank.level">{{rank.name}}</option>
|
||||||
<div class="div-table-col content-l" *ngIf="user.rank"><img src="resource/rank/{{user.rank._id}}.png" class="center-block"></div>
|
|
||||||
<div class="div-table-col content-s" *ngIf="user.rank">
|
|
||||||
<a class="btn btn-sm btn-default btn-block pull-right" (click)="update('rankLvl', user.rank.level + 1)">
|
|
||||||
Befördern
|
|
||||||
</a>
|
|
||||||
<br><br>
|
|
||||||
<a class="btn btn-sm btn-default btn-block pull-right" (click)="update('rankLvl', user.rank.level - 1)">
|
|
||||||
Degradieren
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<div class="div-table">
|
|
||||||
<!--row 1 - label and dropdown-->
|
|
||||||
<div class="div-table-row">
|
|
||||||
<div class="div-table-col content-s">
|
|
||||||
<label style="white-space: pre-wrap">Auszeichnung
|
|
||||||
verleihen:</label>
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-m">
|
|
||||||
<select #decoSelector class="form-control" [ngModel]="undefined" style="min-width: 200px"
|
|
||||||
(change)="toggleDecoPreview(decoDescription, decoSelector.value, decoPreview)">
|
|
||||||
<option *ngFor="let deco of decorations" [value]="deco._id">{{deco.fraction}}: {{deco.name}}</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-l">
|
<show-error text="Squad" path="squad"></show-error>
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--row 2 - decoration preview-->
|
|
||||||
<div class="div-table-row" [style.display]="decoPreviewDisplay">
|
|
||||||
<div class="div-table-col content-s">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-m-flex decoration-preview" style="border-radius: 15px 0px 0px 15px;">
|
|
||||||
<img class="center-block" #decoPreview>
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-l decoration-preview" style="border-radius: 0px 15px 15px 0px; font-style: oblique" #decoDescription>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-s">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--row 3 - input and submit-->
|
|
||||||
<div class="div-table-row">
|
|
||||||
<div class="div-table-col content-s">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-xxl">
|
|
||||||
<textarea class="form-control center-block" placeholder="Begründung eingeben..." rows="3" #awardTextArea></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-s">
|
|
||||||
<a class="pull-right btn btn-sm btn-default btn-block" style="position: relative; bottom: -40px;"
|
|
||||||
(click)="addAwarding(decoSelector, awardTextArea, decoPreview, decoDescription)">
|
|
||||||
Bestätigen</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<button id="cancel"
|
||||||
<label style="margin-left: calc(8% + 15px);">Auszeichnungen:</label>
|
(click)="cancel()"
|
||||||
<div class="div-table table-bordered" style="min-width: 852px; border-collapse: collapse;">
|
class="btn btn-default">
|
||||||
<div class="div-table-row table-bordered">
|
Abbrechen
|
||||||
<div class="div-table-col div-table-head content-xs">
|
</button>
|
||||||
Bild
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col div-table-head content-m">
|
|
||||||
Bezeichnung
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col div-table-head content-xl">
|
|
||||||
Begründung
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col div-table-head content-xs">
|
|
||||||
Datum
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-xs">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="div-table-row table-bordered"*ngFor="let award of user.awards">
|
|
||||||
<div class="div-table-col content-xs" *ngIf="award.decorationId.isMedal">
|
|
||||||
<img height="40px" src="resource/decoration/{{award.decorationId._id}}.png">
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-xs" *ngIf="!award.decorationId.isMedal">
|
|
||||||
<img width="60px" src="resource/decoration/{{award.decorationId._id}}.png">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="div-table-col content-m">
|
<button id="save"
|
||||||
{{award.decorationId.name}}
|
(click)="saveUser(rankLevel.value)"
|
||||||
</div>
|
class="btn btn-default"
|
||||||
<div class="div-table-col content-xl">
|
[disabled]="!form.valid">
|
||||||
{{award.reason}}
|
Bestätigen
|
||||||
</div>
|
</button>
|
||||||
<div class="div-table-col content-xs">
|
</form>
|
||||||
{{award.date | date: 'dd.MM.yyyy'}}
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-xs">
|
|
||||||
<span title="Löschen" (click)="deleteAwarding(award._id)" class="pull-right btn glyphicon glyphicon-trash trash"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<div class="div-table">
|
|
||||||
<div class="div-table-row">
|
|
||||||
<div class="div-table-col content-s">
|
|
||||||
<label>Löschen:</label>
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-l" align="center">
|
|
||||||
<input class="form-control" #confirmInput placeholder="Teilnehmernamen zur Bestätigung eingeben" style="width:320px">
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-s">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="div-table-col content-m">
|
|
||||||
<a class="pull-right btn btn-sm btn-block btn-default" (click)="deleteUser(confirmInput.value)" >Teilnehmer Löschen</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,88 +1,126 @@
|
||||||
import {Component} from "@angular/core";
|
import {Component, ViewChild} from "@angular/core";
|
||||||
import {ActivatedRoute, Router} from "@angular/router";
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
import * as model from "../../models/model-interfaces";
|
import * as model from "../../models/model-interfaces";
|
||||||
import {Decoration, Squad, User} from "../../models/model-interfaces";
|
import {Rank, Squad, User} from "../../models/model-interfaces";
|
||||||
import {UserService} from "../../services/user-service/user.service";
|
import {UserService} from "../../services/user-service/user.service";
|
||||||
import {SquadService} from "../../services/squad-service/squad.service";
|
import {SquadService} from "../../services/squad-service/squad.service";
|
||||||
import {DecorationService} from "../../services/decoration-service/decoration.service";
|
import {RankService} from "../../services/rank-service/rank.service";
|
||||||
import {AwardingService} from "../../services/awarding-service/awarding.service";
|
import {Subscription} from "rxjs";
|
||||||
|
import {NgForm} from "@angular/forms";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './user-overview.component.html',
|
templateUrl: './user-overview.component.html',
|
||||||
styleUrls: ['./user-overview.component.css', '../../style/overview.css'],
|
styleUrls: ['./user-overview.component.css', '../../style/new-entry-form.css'],
|
||||||
})
|
})
|
||||||
export class UserOverviewComponent {
|
export class UserOverviewComponent {
|
||||||
|
|
||||||
|
@ViewChild(NgForm) form: NgForm;
|
||||||
|
|
||||||
|
subscription: Subscription;
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
model = model;
|
model = model;
|
||||||
|
|
||||||
showSuccessLabel = false;
|
showSuccessLabel = false;
|
||||||
|
|
||||||
decoPreviewDisplay = 'none';
|
ranksDisplay = 'none';
|
||||||
|
|
||||||
user: User;
|
user: User = {squad: {}};
|
||||||
|
|
||||||
squads: Squad[];
|
squads: Squad[] = [];
|
||||||
|
|
||||||
decorations: Decoration[];
|
ranks: Rank[] = [];
|
||||||
|
|
||||||
|
saved = false;
|
||||||
|
|
||||||
constructor(private router: Router,
|
constructor(private router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private userService: UserService,
|
private userService: UserService,
|
||||||
private squadService: SquadService,
|
private squadService: SquadService,
|
||||||
private decorationService: DecorationService,
|
private rankService: RankService) {
|
||||||
private awardingService: AwardingService) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.route.params.subscribe((params) => {
|
|
||||||
this.userService.getUser(params['id']).subscribe(user => {
|
this.subscription = this.route.params
|
||||||
this.user = user;
|
.map(params => params['id'])
|
||||||
|
.filter(id => id != undefined)
|
||||||
|
.flatMap(id => this.userService.getUser(id))
|
||||||
|
.subscribe(user => {
|
||||||
|
console.log(user.squad)
|
||||||
|
if (!user.squad) {
|
||||||
|
user.squad = "0";
|
||||||
|
this.ranksDisplay = 'none';
|
||||||
|
} else {
|
||||||
|
this.rankService.findRanks('', user.squad.fraction).subscribe(ranks => {
|
||||||
|
this.ranks = ranks;
|
||||||
|
this.ranksDisplay = 'block';
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
this.user = user;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.squadService.findSquads().subscribe(squads => {
|
this.squadService.findSquads().subscribe(squads => {
|
||||||
this.squads = squads;
|
this.squads = squads;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.decorationService.findDecorations().subscribe(decorations => {
|
|
||||||
this.decorations = decorations;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleDecoPreview(descriptionField, decorationId, image) {
|
ngOnDestroy() {
|
||||||
this.decoPreviewDisplay = 'flex'; // visible & keep same height for all children
|
this.subscription.unsubscribe();
|
||||||
|
|
||||||
const description = this.decorations.find(
|
|
||||||
decoration => decoration._id === decorationId
|
|
||||||
).description;
|
|
||||||
|
|
||||||
image.src = 'resource/decoration/' + decorationId + '.png';
|
|
||||||
descriptionField.innerHTML = description;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(attrName, value, inputField?) {
|
toggleRanks() {
|
||||||
if (attrName === 'squadId' && value === '---') {
|
if (this.user.squad != '0') {
|
||||||
value = null;
|
this.rankService.findRanks('', this.user.squad.fraction).subscribe(
|
||||||
|
ranks => {
|
||||||
|
this.ranks = ranks;
|
||||||
|
this.ranksDisplay = 'block';
|
||||||
}
|
}
|
||||||
if (value !== '' && (attrName !== 'rankLvl' || attrName === 'rankLvl' && value >= 0 && value <= 22)) {
|
);
|
||||||
const updateObject = {_id: this.user._id};
|
} else {
|
||||||
updateObject[attrName] = value;
|
this.ranksDisplay = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveUser(rankLevel) {
|
||||||
|
const updateObject = {
|
||||||
|
_id: this.user._id,
|
||||||
|
username: this.user.username,
|
||||||
|
rankLvl: rankLevel,
|
||||||
|
squadId: null
|
||||||
|
};
|
||||||
|
if (this.user.squad._id !== '0') {
|
||||||
|
updateObject.squadId = this.user.squad._id
|
||||||
|
}
|
||||||
|
console.log(updateObject)
|
||||||
this.userService.updateUser(updateObject)
|
this.userService.updateUser(updateObject)
|
||||||
.subscribe(user => {
|
.subscribe(user => {
|
||||||
this.user = user;
|
if (!user.squad) {
|
||||||
if (inputField) {
|
user.squad = '0';
|
||||||
inputField.value = '';
|
|
||||||
}
|
}
|
||||||
|
this.user = user;
|
||||||
this.showSuccessLabel = true;
|
this.showSuccessLabel = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.showSuccessLabel = false;
|
this.showSuccessLabel = false;
|
||||||
}, 2000)
|
}, 2000)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
this.router.navigate(['../..'], {relativeTo: this.route});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compare ngValue with ngModel to assign selected element
|
||||||
|
*/
|
||||||
|
equals(o1: Squad, o2: Squad) {
|
||||||
|
if (o1 && o2) {
|
||||||
|
return o1._id === o2._id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteUser(confirm) {
|
deleteUser(confirm) {
|
||||||
|
@ -94,47 +132,11 @@ export class UserOverviewComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteAwarding(awardingId) {
|
canDeactivate(): boolean {
|
||||||
this.awardingService.deleteAwarding(awardingId).subscribe((res) => {
|
if (this.saved || !this.form.dirty) {
|
||||||
this.awardingService.getUserAwardings(this.user._id)
|
return true;
|
||||||
.map((res) => res.json())
|
|
||||||
.subscribe((awards) => {
|
|
||||||
this.user.awards = awards;
|
|
||||||
this.showSuccessLabel = true;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.showSuccessLabel = false;
|
|
||||||
}, 2000)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
addAwarding(decorationField, reasonField, previewImage, descriptionField) {
|
|
||||||
const decorationId = decorationField.value;
|
|
||||||
const reason = reasonField.value;
|
|
||||||
if (decorationId && reason.length > 0) {
|
|
||||||
const award = {
|
|
||||||
"userId": this.user._id,
|
|
||||||
"decorationId": decorationId,
|
|
||||||
"reason": reason,
|
|
||||||
"date": Date.now()
|
|
||||||
};
|
|
||||||
this.awardingService.addAwarding(award).subscribe(() => {
|
|
||||||
this.awardingService.getUserAwardings(this.user._id)
|
|
||||||
.map((res) => res.json())
|
|
||||||
.subscribe(awards => {
|
|
||||||
this.user.awards = awards;
|
|
||||||
this.decoPreviewDisplay = 'none';
|
|
||||||
decorationField.value = undefined;
|
|
||||||
reasonField.value = '';
|
|
||||||
previewImage.src = '';
|
|
||||||
descriptionField.innerHTML = '';
|
|
||||||
this.showSuccessLabel = true;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.showSuccessLabel = false;
|
|
||||||
}, 2000)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
return window.confirm(`Ihr Formular besitzt ungespeicherte Änderungen, möchten Sie die Seite wirklich verlassen?`);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {UsersComponent} from "./users.component";
|
||||||
import {UserOverviewComponent} from "./user-overview/user-overview.component";
|
import {UserOverviewComponent} from "./user-overview/user-overview.component";
|
||||||
import {UserListComponent} from "./user-list/user-list.component";
|
import {UserListComponent} from "./user-list/user-list.component";
|
||||||
import {CreateUserComponent} from "./new-user/new-user.component";
|
import {CreateUserComponent} from "./new-user/new-user.component";
|
||||||
|
import {UserAwardComponent} from "./user-award/user-award.component";
|
||||||
|
|
||||||
export const usersRoutes: Routes = [{
|
export const usersRoutes: Routes = [{
|
||||||
path: '', component: UsersComponent,
|
path: '', component: UsersComponent,
|
||||||
|
@ -22,6 +23,12 @@ export const usersRoutes: Routes = [{
|
||||||
path: 'overview/:id',
|
path: 'overview/:id',
|
||||||
component: UserOverviewComponent,
|
component: UserOverviewComponent,
|
||||||
outlet: 'right'
|
outlet: 'right'
|
||||||
}];
|
},
|
||||||
|
{
|
||||||
|
path: 'award/:id',
|
||||||
|
component: UserAwardComponent,
|
||||||
|
outlet: 'right'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export const usersRoutingComponents = [UsersComponent, UserListComponent, UserOverviewComponent, CreateUserComponent];
|
export const usersRoutingComponents = [UsersComponent, UserListComponent, UserOverviewComponent, CreateUserComponent, UserAwardComponent];
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
Loading…
Reference in New Issue