diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 529e65b..19e6b43 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -1,3 +1,6 @@ +import { EditUserComponent } from './pages/edit-user/edit-user.component'; +import { AdminGuard } from './services/admin-guard.service'; +import { UsersComponent } from './pages/users/users.component'; import { CreateAuthComponent } from './pages/create-auth/create-auth.component'; import { CreateSlaveComponent } from './pages/create-slave/create-slave.component'; import { EditSlaveComponent } from './pages/edit-slave/edit-slave.component'; @@ -57,6 +60,20 @@ const routes: Routes = [ component: CreateAuthComponent, data: { type: 'NATIVE' } }, + { + path: '', + canActivate: [AdminGuard], + children: [ + { + path: 'users', + component: UsersComponent + }, + { + path: 'users/:userId', + component: EditUserComponent + } + ] + }, { path: 'password', component: PasswordComponent diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index fe6b759..f17999b 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -1,5 +1,6 @@ Domains + Users Password Logout diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 04e29ce..9ee199e 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -1,3 +1,6 @@ +import { EditUserComponent } from './pages/edit-user/edit-user.component'; +import { UsersOperation } from './operations/users.operations'; +import { AdminGuard } from './services/admin-guard.service'; import { CreateAuthComponent } from './pages/create-auth/create-auth.component'; import { StopPropagateClickDirective } from './utils/stop-propagate-click.directive'; import { PagesizeComponent } from './partials/pagesize/pagesize.component'; @@ -30,6 +33,7 @@ import { EditSlaveComponent } from './pages/edit-slave/edit-slave.component'; import { EditAuthComponent } from './pages/edit-auth/edit-auth.component'; import { SelectComponent } from './partials/select/select.component'; import { CreateSlaveComponent } from './pages/create-slave/create-slave.component'; +import { UsersComponent } from './pages/users/users.component'; @NgModule({ declarations: [ @@ -52,7 +56,9 @@ import { CreateSlaveComponent } from './pages/create-slave/create-slave.componen SelectComponent, StopPropagateClickDirective, CreateSlaveComponent, - CreateAuthComponent + CreateAuthComponent, + UsersComponent, + EditUserComponent ], imports: [ BrowserModule, @@ -66,7 +72,9 @@ import { CreateSlaveComponent } from './pages/create-slave/create-slave.componen SessionOperation, PasswordOperation, DomainsOperation, - AuthGuard + UsersOperation, + AuthGuard, + AdminGuard ], bootstrap: [AppComponent] }) diff --git a/frontend/src/app/operations/users.operations.ts b/frontend/src/app/operations/users.operations.ts new file mode 100644 index 0000000..a315217 --- /dev/null +++ b/frontend/src/app/operations/users.operations.ts @@ -0,0 +1,88 @@ +import { UserApitype } from './../apitypes/User.apitype'; +import { ListApitype } from './../apitypes/List.apitype'; +import { Injectable } from '@angular/core'; +import { HttpService } from '../services/http.service'; +import { StateService } from '../services/state.service'; +import { SessionApitype } from '../apitypes/Session.apitype'; + +@Injectable() +export class UsersOperation { + + constructor(private http: HttpService, private gs: StateService) { } + + public async getList(page?: number, pageSize?: number, query?: string, + sort?: Array | string, type?: string): Promise> { + try { + return new ListApitype(await this.http.get('/users', { + page: page, + pagesize: pageSize, + query: query, + sort: sort, + type: type + })); + } catch (e) { + console.error(e); + return new ListApitype({ paging: {}, results: [] }); + } + } + + public async delete(userId: number): Promise { + try { + await this.http.delete(['/users', userId.toString()]); + return true; + } catch (e) { + console.error(e); + return false; + } + } + + public async getSingle(userId: number): Promise { + try { + return new UserApitype(await this.http.get(['/users', userId.toString()])); + } catch (e) { + console.error(e); + return new UserApitype({}); + } + } + + public async updateUser(userId: number, name?: string, type?: string, password?: string): Promise { + const data = {}; + if (name !== null && name !== undefined) { + data['name'] = name; + } + if (type !== null && type !== undefined) { + data['type'] = type; + } + if (password !== null && password !== undefined) { + data['password'] = password; + } + + try { + await this.http.put(['/users', userId.toString()], data); + + return true; + } catch (e) { + console.error(e); + return false; + } + } + + public async create(name: string, type: string, password: string): Promise { + try { + const result = new UserApitype(await this.http.post('/users', { + name: name, + type: type, + password: password + })); + + return result; + } catch (e) { + if (e.response.status || e.response.status === 409) { + throw new Error('User already exists!'); + } else { + console.error(e); + return new UserApitype({}); + } + } + } +} diff --git a/frontend/src/app/pages/domains/domains.component.html b/frontend/src/app/pages/domains/domains.component.html index 55d6890..499ec34 100644 --- a/frontend/src/app/pages/domains/domains.component.html +++ b/frontend/src/app/pages/domains/domains.component.html @@ -30,7 +30,7 @@
- Name + Type
diff --git a/frontend/src/app/pages/edit-user/edit-user.component.html b/frontend/src/app/pages/edit-user/edit-user.component.html new file mode 100644 index 0000000..ae99287 --- /dev/null +++ b/frontend/src/app/pages/edit-user/edit-user.component.html @@ -0,0 +1,3 @@ +

+ Edit user works! +

\ No newline at end of file diff --git a/frontend/src/app/pages/edit-user/edit-user.component.scss b/frontend/src/app/pages/edit-user/edit-user.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/pages/edit-user/edit-user.component.ts b/frontend/src/app/pages/edit-user/edit-user.component.ts new file mode 100644 index 0000000..f1f5102 --- /dev/null +++ b/frontend/src/app/pages/edit-user/edit-user.component.ts @@ -0,0 +1,19 @@ +import { ActivatedRoute } from '@angular/router'; +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-edit-user', + templateUrl: './edit-user.component.html', + styleUrls: ['./edit-user.component.scss'] +}) +export class EditUserComponent implements OnInit { + + public type: string; + + constructor(private route: ActivatedRoute) { } + + ngOnInit() { + + } + +} diff --git a/frontend/src/app/pages/users/users.component.html b/frontend/src/app/pages/users/users.component.html new file mode 100644 index 0000000..2170a37 --- /dev/null +++ b/frontend/src/app/pages/users/users.component.html @@ -0,0 +1,50 @@ +
+
+ +
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + +
+ ID + + +
+ Name + + +
+
+
+ Type + + +
+
{{ user.id }}{{ user.name }}{{ user.type }} + +
+
+
+
+ \ No newline at end of file diff --git a/frontend/src/app/pages/users/users.component.scss b/frontend/src/app/pages/users/users.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/pages/users/users.component.ts b/frontend/src/app/pages/users/users.component.ts new file mode 100644 index 0000000..715bc91 --- /dev/null +++ b/frontend/src/app/pages/users/users.component.ts @@ -0,0 +1,99 @@ +import { UsersOperation } from './../../operations/users.operations'; +import { UserApitype } from './../../apitypes/User.apitype'; +import { SortEventDatatype } from './../../datatypes/sort-event.datatype'; +import { ModalOptionsDatatype } from './../../datatypes/modal-options.datatype'; +import { ModalService } from './../../services/modal.service'; +import { StateService } from './../../services/state.service'; +import { PagingApitype } from './../../apitypes/Paging.apitype'; +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { FormControl } from '@angular/forms'; + +import 'rxjs/add/operator/debounceTime'; + +@Component({ + selector: 'app-users', + templateUrl: './users.component.html', + styleUrls: ['./users.component.scss'] +}) +export class UsersComponent implements OnInit { + + public pagingInfo = new PagingApitype({}); + public pageRequested = 1; + + public userList: UserApitype[] = []; + + public sortField = ''; + public sortOrder = 'asc'; + + public searchInput: FormControl; + public typeFilter: FormControl; + public typeFilterOptions = ['admin', 'user']; + + constructor(private users: UsersOperation, public gs: StateService, private modal: ModalService, private router: Router) { } + + public ngOnInit() { + this.searchInput = new FormControl(''); + this.searchInput.valueChanges.debounceTime(500).subscribe(() => this.loadData()); + + this.typeFilter = new FormControl(null); + this.typeFilter.valueChanges.subscribe(() => this.loadData()); + + this.loadData(); + } + + public async loadData() { + const sortStr = this.sortField !== '' ? this.sortField + '-' + this.sortOrder : null; + const searchStr = this.searchInput.value !== '' ? this.searchInput.value : null; + const typeFilter = this.typeFilter.value; + + const res = await this.users.getList(this.pageRequested, this.gs.pageSize, searchStr, sortStr, typeFilter); + + this.pagingInfo = res.paging; + this.userList = res.results; + } + + public async onDeleteUser(user: UserApitype) { + try { + await this.modal.showMessage(new ModalOptionsDatatype({ + heading: 'Confirm deletion', + body: 'Are you shure you want to delete ' + user.name + '?', + acceptText: 'Delete', + dismisText: 'Cancel', + acceptClass: 'danger' + })); + + await this.users.delete(user.id); + + await this.loadData(); + } catch (e) { + } + } + + public async onPageChange(newPage: number) { + this.pageRequested = newPage; + await this.loadData(); + } + + public async onPagesizeChange(pagesize: number) { + this.gs.pageSize = pagesize; + this.pageRequested = 1; + await this.loadData(); + } + + public async onUserClick(domain: UserApitype) { + this.router.navigate(['/users', domain.id.toString()]); + } + + public async onSortEvent(sortEvent: SortEventDatatype) { + if (sortEvent.order === 0) { + this.sortField = ''; + this.sortOrder = 'asc'; + } else { + this.sortField = sortEvent.field; + this.sortOrder = sortEvent.order === 1 ? 'asc' : 'desc'; + } + + await this.loadData(); + } +} diff --git a/frontend/src/app/services/admin-guard.service.ts b/frontend/src/app/services/admin-guard.service.ts new file mode 100644 index 0000000..866606c --- /dev/null +++ b/frontend/src/app/services/admin-guard.service.ts @@ -0,0 +1,18 @@ +import { StateService } from './state.service'; +import { Injectable } from '@angular/core'; +import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; + +@Injectable() +export class AdminGuard implements CanActivate { + + constructor(private gs: StateService, private router: Router) { } + + canActivate(): boolean { + if (!this.gs.isAdmin) { + this.router.navigate(['/']); + return false; + } else { + return true; + } + } +}