diff --git a/api/middleware/limitoffset-middleware-mongo.js b/api/middleware/limitoffset-middleware-mongo.js index 12eab7e..944ee3a 100644 --- a/api/middleware/limitoffset-middleware-mongo.js +++ b/api/middleware/limitoffset-middleware-mongo.js @@ -33,7 +33,6 @@ router.use((req, res, next) => { if (offsetString) { - console.log(offsetString) if (!isNaN(offsetString)) { offset = parseInt(offsetString); if (offset < 0) { diff --git a/api/routes/users.js b/api/routes/users.js index 23feea4..9554e25 100644 --- a/api/routes/users.js +++ b/api/routes/users.js @@ -12,90 +12,56 @@ const apiAuthenticationMiddleware = require('../middleware/auth-middleware'); const checkHl = require('../middleware/permission-check').checkHl; const sortCollectionBy = require('../middleware/util').sortCollection; const offsetlimitMiddleware = require('../middleware/limitoffset-middleware-mongo'); +const filterHandlerCreator = require('../middleware/filter-handler-mongo'); const routerHandling = require('../middleware/router-handling'); // Mongoose Model using mongoDB const UserModel = require('../models/user'); -const RankModel = require('../models/rank'); +const SquadModel = require('../models/squad'); const AwardingModel = require('../models/awarding'); -const resultSet = {'__v': 0, 'updatedAt': 0, 'timestamp': 0}; - - const users = express.Router(); +users.get('/', filterHandlerCreator(UserModel.schema.paths)); users.get('/', offsetlimitMiddleware); // routes ********************** users.route('/') .get((req, res, next) => { - if (req.query.simple) { - UserModel.find({}, res.locals.filter, res.locals.limitskip, (err, items) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - // if the collection is empty we do not send empty arrays back. - - res.locals.items = items; - res.locals.processed = true; - next(); - }) - } - else { - const nameQuery = req.query.q; - const fractionFilter = req.query.fractFilter; - const squadFilter = req.query.squadId; - - UserModel.find({}, res.locals.filter, res.locals.limitskip, (err, users) => { - if (err) return next(err); - if (users.length === 0) { - res.locals.items = users; - res.locals.processed = true; - next(); - } - let resUsers = []; - let rowsLength = users.length; - users.forEach((user) => { - // filter by name - if (!nameQuery || (nameQuery && user.username.toLowerCase().includes(nameQuery.toLowerCase()))) { - getExtendedUser(user, next, (extUser) => { - - // filter by squad - if (squadFilter) { - if (extUser.squad && extUser.squad._id.toString() === squadFilter) { - resUsers.push(extUser); - } - else { - rowsLength -= 1; - } - } - // filter by fraction - else if (!fractionFilter || - (fractionFilter && extUser.squad && extUser.squad.fraction.toLowerCase() === fractionFilter) || - (fractionFilter && fractionFilter === 'unassigned' && !extUser.squad)) { - resUsers.push(extUser); - } else { - rowsLength -= 1; - } - if (resUsers.length === rowsLength) { - resUsers = sortCollectionBy(resUsers, 'username'); - res.locals.items = resUsers; - res.locals.processed = true; - return next(); - } - }); - } else { - rowsLength -= 1; - // no user matching query - return empty [] - if (rowsLength === 0) { - res.locals.items = resUsers; - res.locals.processed = true; - next(); - } + const userQuery = () => { + UserModel.find(dbFilter, res.locals.filter, res.locals.limitskip) + .populate('squadId') + .exec((err, users) => { + if (err) return next(err); + if (users.length === 0) { + res.locals.items = users; + res.locals.processed = true; + return next(); } + //users = sortCollectionBy(users, 'username'); + UserModel.count(dbFilter, (err, totalCount) => { + res.set('x-total-count', totalCount); + res.locals.items = users; + res.locals.processed = true; + return next(); + }) }) + }; + + if (!req.query.q) req.query.q = '' + const dbFilter = {username: {"$regex": req.query.q, "$options": "i"}}; + if (req.query.squadId) dbFilter["squadId"] = {"$eq": req.query.squadId}; + // squad / fracion filter setup + if (req.query.fractFilter && req.query.fractFilter !== 'UNASSIGNED' && !req.query.squadId) { + SquadModel.find({'fraction': req.query.fractFilter}, {_id: 1}, (err, squads) => { + dbFilter['squadId'] = {$in: squads.map(squad => squad.id)}; + userQuery(); }) + } else { + if (req.query.fractFilter === 'UNASSIGNED') { + dbFilter['squadId'] = {$eq: null}; + } + userQuery(); } }) @@ -108,7 +74,8 @@ users.route('/') return next(err); } res.status(codes.created); - getExtendedUser(user, next, (extUser) => { + + UserModel.populate(user, {path: 'squadId'}, (err, extUser) => { res.locals.items = extUser; res.locals.processed = true; return next(); @@ -121,26 +88,19 @@ users.route('/') users.route('/:id') .get((req, res, next) => { - - UserModel.findById(req.params.id, (err, item) => { + UserModel.findById(req.params.id).populate('squadId').exec((err, user) => { if (err) { err.status = codes.servererror; return next(err); } - else if (!item) { + else if (!user) { err = new Error("item not found"); err.status = codes.notfound; return next(err); - } else if (req.query.simple) { - res.locals.items = item; - next(); } - getExtendedUser(item, next, (extUser) => { - res.locals.items = extUser; - res.locals.processed = true; - return next(); - }) - + res.locals.items = user; + res.locals.processed = true; + return next(); }); }) @@ -165,29 +125,21 @@ users.route('/:id') else if (!item) { err = new Error("item not found"); err.status = codes.notfound; - } else if (req.query.simple) { - res.locals.items = item; + } + UserModel.populate(item, {path: 'squadId'}, (err, extUser) => { + if (err) { + err.status = codes.servererror; + return next(err); + } + if (!user) { + res.locals.items = {}; + res.locals.processed = true; + return next(); + } + res.locals.items = extUser; res.locals.processed = true; return next(); - } - else { - UserModel.findById(item._id, (err, user) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - if (!user) { - res.locals.items = {}; - res.locals.processed = true; - return next(); - } - getExtendedUser(user, next, (extUser) => { - res.locals.items = extUser; - res.locals.processed = true; - return next(); - }) - }) - } + }) }) }) @@ -201,7 +153,7 @@ users.route('/:id') 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 user = new UserModel(req.body); + const user = 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) { @@ -213,7 +165,7 @@ users.route('/:id') err.status = codes.notfound; return next(err); } - // optional task 3b: check that version is still accurate + // check that version is still accurate else if (user.__v !== item.__v) { err = new Error("version outdated. Meanwhile update on item happened. Please GET resource again") err.status = codes.conflict; @@ -227,7 +179,7 @@ users.route('/:id') } } - // optional task 3: update updatedAt and increase version + // update updatedAt and increase version item.updatedAt = new Date(); item.increment(); // this sets __v++ item.save(function (err) { @@ -237,7 +189,8 @@ users.route('/:id') err.status = codes.wrongrequest; err.message += ' in fields: ' + Object.getOwnPropertyNames(err.errors); } - getExtendedUser(item, next, (extUser) => { + + UserModel.populate(item, {path: 'squadId'}, (err, extUser) => { res.locals.items = extUser; res.locals.processed = true; return next(); @@ -286,56 +239,4 @@ users.route('/:id') // it looks for object(s) in res.locals.items and if they exist, they are send to the client as json users.use(routerHandling.emptyResponse); -/** - * Create model for single extended user and - * return via callback - */ -let getExtendedUser = (user, next, callback) => { - let extUser = {}; - UserModel.findById(user._id, resultSet) - .populate('squadId', resultSet).exec((err, member) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - extUser._id = user._id; - extUser.username = user.username; - extUser.squad = member.squadId; - - if (extUser.squad) { - RankModel.findOne({ - level: member.rankLvl, - fraction: member.squadId.fraction - }, resultSet, (err, rank) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - extUser.rank = rank; - }).then(() => { - addAwards(extUser).then(() => { - callback(extUser); - }) - }) - } else { - extUser.rank = {level: user.rankLvl}; - addAwards(extUser).then(() => { - callback(extUser); - }) - } - }) -}; - -let addAwards = (extUser) => { - return AwardingModel.find({userId: extUser._id}, resultSet, {sort: {date: 'desc'}}) - .populate('decorationId', resultSet) - .exec((err, awards) => { - if (err) { - err.status = codes.servererror; - return next(err); - } - extUser.awards = awards; - }) -}; - module.exports = users; diff --git a/static/package.json b/static/package.json index 7791630..b3ceb71 100644 --- a/static/package.json +++ b/static/package.json @@ -33,6 +33,7 @@ "ngx-bootstrap": "^2.0.0-beta.6", "ngx-clipboard": "^8.1.0", "ngx-cookie-service": "^1.0.9", + "ngx-infinite-scroll": "^0.5.2", "rxjs": "^5.2.0", "ts-helpers": "^1.1.1", "typescript": "^2.3.4", diff --git a/static/src/app/app.component.ts b/static/src/app/app.component.ts index 855a37d..6f12cc5 100644 --- a/static/src/app/app.component.ts +++ b/static/src/app/app.component.ts @@ -26,7 +26,9 @@ export class AppComponent { } if (event instanceof NavigationEnd) { this.loading = false; - window.scrollTo(0, 0); + // if (!router.url.includes('right')) { + // window.scrollTo(0, 0); + // } } }); } diff --git a/static/src/app/models/model-interfaces.ts b/static/src/app/models/model-interfaces.ts index d5b536f..1b46f7d 100644 --- a/static/src/app/models/model-interfaces.ts +++ b/static/src/app/models/model-interfaces.ts @@ -11,7 +11,8 @@ export interface User { _id?: string; boardUserId?: number; username?: string; - squad?: any; //Squad or id-string + squadId?: any; //Squad or id-string + rankLvl?: number; rank?: Rank; awards?: Award[]; } diff --git a/static/src/app/services/stores/user.store.ts b/static/src/app/services/stores/user.store.ts index 1abef63..ce8ac10 100644 --- a/static/src/app/services/stores/user.store.ts +++ b/static/src/app/services/stores/user.store.ts @@ -22,6 +22,9 @@ export class UserStore { case LOAD: return [...action.data]; case ADD: + if (action.data instanceof Array) { + return users.concat(action.data); + } return [...users, action.data]; case EDIT: return users.map(user => { diff --git a/static/src/app/services/user-service/user.service.ts b/static/src/app/services/user-service/user.service.ts index b378d5d..7a91bb9 100644 --- a/static/src/app/services/user-service/user.service.ts +++ b/static/src/app/services/user-service/user.service.ts @@ -11,13 +11,15 @@ export class UserService { users$: Observable; + totalCount = 0; + constructor(private http: HttpClient, private userStore: UserStore, private config: AppConfig) { this.users$ = userStore.items$; } - findUsers(query = '', fractionFilter?, squadFilter?) { + findUsers(query = '', fractionFilter?, squadFilter?, limit?, offset?, action?) { const searchParams = new URLSearchParams(); searchParams.append('q', query); if (fractionFilter) { @@ -26,10 +28,17 @@ export class UserService { if (squadFilter) { searchParams.append('squadId', squadFilter); } + searchParams.append('limit', limit); + searchParams.append('offset', offset); this.http.get(this.config.apiUserPath, searchParams) - .map(res => res.json()) - .do((users) => { - this.userStore.dispatch({type: LOAD, data: users}); + .do((res) => { + let headerCount = parseInt(res.headers.get('x-total-count')); + console.log(headerCount) + if (headerCount) { + this.totalCount = headerCount; + } + let users = res.json(); + this.userStore.dispatch({type: action, data: users}); }).subscribe(_ => { }); diff --git a/static/src/app/users/edit-user/edit-user.component.html b/static/src/app/users/edit-user/edit-user.component.html index b052f21..42dc09e 100644 --- a/static/src/app/users/edit-user/edit-user.component.html +++ b/static/src/app/users/edit-user/edit-user.component.html @@ -19,7 +19,7 @@ diff --git a/static/src/app/users/edit-user/edit-user.component.ts b/static/src/app/users/edit-user/edit-user.component.ts index 8b495ce..8d3bc3c 100644 --- a/static/src/app/users/edit-user/edit-user.component.ts +++ b/static/src/app/users/edit-user/edit-user.component.ts @@ -18,7 +18,7 @@ export class EditUserComponent { subscription: Subscription; - user: User = {username: '', squad: '0', rank: {level: 0}}; + user: User = {username: '', squadId: '0', rankLvl: 0}; squads: Squad[] = []; @@ -46,11 +46,11 @@ export class EditUserComponent { .filter(id => id != undefined) .flatMap(id => this.userService.getUser(id)) .subscribe(user => { - if (!user.squad) { - user.squad = "0"; + if (!user.squadId) { + user.squadId = "0"; this.ranksDisplay = 'none'; } else { - this.rankService.findRanks('', user.squad.fraction).subscribe(ranks => { + this.rankService.findRanks('', user.squadId.fraction).subscribe(ranks => { this.ranks = ranks; this.ranksDisplay = 'block'; }); @@ -64,8 +64,8 @@ export class EditUserComponent { } toggleRanks() { - if (this.user.squad != '0') { - this.rankService.findRanks('', this.user.squad.fraction).subscribe( + if (this.user.squadId != '0') { + this.rankService.findRanks('', this.user.squadId.fraction).subscribe( ranks => { this.ranks = ranks; this.ranksDisplay = 'block'; @@ -83,8 +83,8 @@ export class EditUserComponent { rankLvl: rankLevel, squadId: null }; - if (this.user.squad._id !== '0') { - updateObject.squadId = this.user.squad._id + if (this.user.squadId._id !== '0') { + updateObject.squadId = this.user.squadId._id } if (this.user._id) { @@ -107,7 +107,7 @@ export class EditUserComponent { }, error => { // duplicated user error message - if (error._body.indexOf('duplicate') >= 0) { + if (error._body.includes('duplicate')) { this.error = "Benutzername existiert bereits"; this.showErrorLabel = true; setTimeout(() => { 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 a3dbaa6..4e4ad63 100644 --- a/static/src/app/users/user-list/user-item.component.html +++ b/static/src/app/users/user-list/user-item.component.html @@ -6,9 +6,9 @@ {{user.username}}
- CSAT - {{user.squad.name}} - NATO - {{user.squad.name}} - ohne Squad/Fraktion + CSAT - {{user.squadId.name}} + NATO - {{user.squadId.name}} + ohne Squad/Fraktion
diff --git a/static/src/app/users/user-list/user-list.component.html b/static/src/app/users/user-list/user-list.component.html index 89fd620..1f55f83 100644 --- a/static/src/app/users/user-list/user-list.component.html +++ b/static/src/app/users/user-list/user-list.component.html @@ -1,9 +1,9 @@
- - - + + +
+
@@ -21,7 +21,12 @@
-
+
this.userService.findUsers(query, this.radioModel)) + .switchMap(query => this.filterUsers()) .subscribe(); - } openNewUserForm() { @@ -70,8 +78,24 @@ export class UserListComponent implements OnInit { } } - filterUsers() { - this.users$ = this.userService.findUsers(this.searchTerm.value, this.radioModel); + filterUsers(action?) { + if (!action || (action && action === LOAD)) { + action = LOAD; + this.offset = 0; + this.limit = 20; + } + return this.users$ = this.userService.findUsers(this.searchTerm.value, this.radioModel, + null, this.limit, this.offset, action); + } + + onScrollDown() { + if (this.offset + this.limit > this.userService.totalCount) { + this.limit = this.userService.totalCount - this.offset; + } + if (this.limit != 0) { + this.offset += this.limit; + this.filterUsers(ADD); + } } adjustBrowserUrl(queryString = '') { diff --git a/static/src/app/users/users.module.ts b/static/src/app/users/users.module.ts index dbce84f..9e3a8bb 100644 --- a/static/src/app/users/users.module.ts +++ b/static/src/app/users/users.module.ts @@ -3,10 +3,11 @@ import {usersRouterModule, usersRoutingComponents} from './users.routing'; import {CommonModule} from "@angular/common"; import {SharedModule} from "../shared.module"; import {ButtonsModule} from "ngx-bootstrap"; +import {InfiniteScrollModule} from "ngx-infinite-scroll"; @NgModule({ declarations: usersRoutingComponents, - imports: [CommonModule, SharedModule, ButtonsModule.forRoot(), usersRouterModule], + imports: [CommonModule, SharedModule, ButtonsModule.forRoot(), InfiniteScrollModule, usersRouterModule], }) export class UsersModule { static routes = usersRouterModule;