diff --git a/api/routes/users.js b/api/routes/users.js index 95f7694..6e98986 100644 --- a/api/routes/users.js +++ b/api/routes/users.js @@ -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) => { UserModel.findByIdAndRemove(req.params.id, (err, item) => { if (err) { @@ -241,7 +296,7 @@ let getExtendedUser = (user, next, callback) => { }) }) } else { - extUser.rank = null; + extUser.rank = {level: user.rankLvl}; addAwards(extUser).then(() => { callback(extUser); }) diff --git a/static/src/app/app.component.css b/static/src/app/app.component.css index fcdca7f..156bb18 100644 --- a/static/src/app/app.component.css +++ b/static/src/app/app.component.css @@ -20,5 +20,5 @@ li { .left { float: left; - width: calc(100% - 300px); + width: calc(100% - 400px); } diff --git a/static/src/app/services/http-client.ts b/static/src/app/services/http-client.ts index 0fa536e..088ad03 100644 --- a/static/src/app/services/http-client.ts +++ b/static/src/app/services/http-client.ts @@ -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) { let headers = this.createAuthorizationHeader(); return this.http.patch(url, data, { diff --git a/static/src/app/services/user-service/user.service.ts b/static/src/app/services/user-service/user.service.ts index 0692199..cb212f0 100644 --- a/static/src/app/services/user-service/user.service.ts +++ b/static/src/app/services/user-service/user.service.ts @@ -48,7 +48,7 @@ export class UserService { } 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()) .do(savedUser => { const action = {type: EDIT, data: savedUser}; diff --git a/static/src/app/users/user-award/user-award.component.css b/static/src/app/users/user-award/user-award.component.css new file mode 100644 index 0000000..789b2f5 --- /dev/null +++ b/static/src/app/users/user-award/user-award.component.css @@ -0,0 +1,4 @@ +.decoration-preview { + background-color: white; + padding: 5px; +} diff --git a/static/src/app/users/user-award/user-award.component.html b/static/src/app/users/user-award/user-award.component.html new file mode 100644 index 0000000..006fe4c --- /dev/null +++ b/static/src/app/users/user-award/user-award.component.html @@ -0,0 +1,127 @@ +
diff --git a/static/src/app/users/user-award/user-award.component.ts b/static/src/app/users/user-award/user-award.component.ts new file mode 100644 index 0000000..dd0fb61 --- /dev/null +++ b/static/src/app/users/user-award/user-award.component.ts @@ -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?`); + } + +} diff --git a/static/src/app/users/user-list/user-item.component.css b/static/src/app/users/user-list/user-item.component.css index ca97142..57348aa 100644 --- a/static/src/app/users/user-list/user-item.component.css +++ b/static/src/app/users/user-list/user-item.component.css @@ -3,7 +3,6 @@ div.user-list-entry, a.user-list-entry { width: 475px; border-radius: 2px; border: lightgrey solid 1px; - cursor: pointer; margin-bottom: -1px; } @@ -11,23 +10,36 @@ div.user-list-entry, a.user-list-entry { background: lightgrey; } +.icon-award { + background: url(../../../assets/award.png); + width: 18px; + height: 29px; + display: block; + margin-left: 12px; +} + span { cursor: pointer; } a { - font-size: x-large; - font-weight: 700; + font-size: large; + font-weight: 600; } small { color: grey; + font-size: x-small; } .trash { - padding-top: 18px; - font-size: 17px; - margin-left: -10px; + font-size: 19px; + margin-left: 12px; + margin-top: 2px; +} + +.edit { + font-size: 22px; } .selected { diff --git a/static/src/app/users/user-list/user-item.component.html b/static/src/app/users/user-list/user-item.component.html index 41c414b..2ad44e0 100644 --- a/static/src/app/users/user-list/user-item.component.html +++ b/static/src/app/users/user-list/user-item.component.html @@ -1,14 +1,20 @@ -