diff --git a/src/app/admin-view/account-mgmt/account-mgmt.component.ts b/src/app/admin-view/account-mgmt/account-mgmt.component.ts index 94a740f..c131192 100644 --- a/src/app/admin-view/account-mgmt/account-mgmt.component.ts +++ b/src/app/admin-view/account-mgmt/account-mgmt.component.ts @@ -5,7 +5,7 @@ import { MatPaginator } from '@angular/material/paginator' import { UserEditComponent } from './user-edit/user-edit.component' import { LocalStorageService } from 'src/app/services/local-storage.service' import { Group } from 'src/app/types/group' -import User from 'src/app/types/user' +import { User } from 'src/app/admin-view/account-mgmt/account.model' import { AccountMgmtService } from './account-mgmt.service' import { STATE } from 'src/app/types/state' @@ -17,7 +17,7 @@ import { STATE } from 'src/app/types/state' }) export class AccountMgmtComponent implements AfterViewInit { protected groups: Group[] = [] - users: MatTableDataSource> + users: MatTableDataSource @ViewChild(MatPaginator) paginator!: MatPaginator constructor( @@ -25,7 +25,7 @@ export class AccountMgmtComponent implements AfterViewInit { private dialog: MatDialog, protected readonly ls: LocalStorageService ) { - this.users = new MatTableDataSource>() + this.users = new MatTableDataSource() this.users.filterPredicate = ( data: Record, filter: string diff --git a/src/app/admin-view/account-mgmt/account-mgmt.service.spec.ts b/src/app/admin-view/account-mgmt/account-mgmt.service.spec.ts index a14b9e0..8094939 100644 --- a/src/app/admin-view/account-mgmt/account-mgmt.service.spec.ts +++ b/src/app/admin-view/account-mgmt/account-mgmt.service.spec.ts @@ -4,6 +4,7 @@ import { AccountMgmtService } from './account-mgmt.service'; import { provideHttpClient } from '@angular/common/http'; import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; import { environment } from 'src/environments/environment'; +import { firstValueFrom, skip } from 'rxjs'; describe('AccountMgmtService', () => { let service: AccountMgmtService; @@ -26,7 +27,7 @@ describe('AccountMgmtService', () => { it('should get user accounts', () => { service.refresh() - const req = httpTesting.expectOne(environment.apiEndpoint+"/admin/accs", "Request to load all users") + const req = httpTesting.expectOne(environment.apiEndpoint + "/admin/accs", "Request to load all users") expect(req.request.method).toBe("GET") @@ -34,10 +35,62 @@ describe('AccountMgmtService', () => { httpTesting.verify() }) - it('should create a user account and refresh list', () => { - service.postAcc({ - uname: "test", - groups: [] + describe('create user', () => { + xit('should create a user account and refresh list', () => { + const test_user = { + uname: "test", + groups: [] + } + service.postAcc(test_user).subscribe(v => { + expect(v).toEqual(jasmine.objectContaining(test_user)) + }) + const req = httpTesting.expectOne(environment.apiEndpoint + "/admin/accs", "Request new user") + + expect(req.request.method).toBe("POST") + + req.flush({ + ...test_user, + _id: "test_id" + }) + + const req2 = httpTesting.expectOne(environment.apiEndpoint + "/admin/accs", "Request to load all users") + + expect(req2.request.method).toBe("GET") + + // service.accs.pipe(skip(1)).subscribe(v => { + // expect(v).toContain(createdUser) + // }) + + + req2.flush([ + { + ...test_user, + _id: "test_id" + } + ]) + httpTesting.verify() + }) + }) + + describe("delete user", () => { + it('should refresh accounts and not to contain deleted user', async () => { + service.deleteAcc("test").subscribe() + const req = httpTesting.expectOne(environment.apiEndpoint + "/admin/accs/test", "Request delete user") + + expect(req.request.method).toBe("DELETE") + + req.flush({ status: 200 }) + + const req2 = httpTesting.expectOne(environment.apiEndpoint + "/admin/accs", "Request to load all users") + + expect(req2.request.method).toBe("GET") + service.accs.pipe(skip(1)).subscribe(v => { + expect(v).not.toContain(jasmine.objectContaining({ _id: "test" })) + }) + + req2.flush([]) + + httpTesting.verify() }) }) }); diff --git a/src/app/admin-view/account-mgmt/account-mgmt.service.ts b/src/app/admin-view/account-mgmt/account-mgmt.service.ts index 1e7cb3c..4ea9e79 100644 --- a/src/app/admin-view/account-mgmt/account-mgmt.service.ts +++ b/src/app/admin-view/account-mgmt/account-mgmt.service.ts @@ -1,11 +1,11 @@ import { HttpClient } from '@angular/common/http'; import { Injectable, signal } from '@angular/core'; -import { BehaviorSubject, catchError, of } from 'rxjs'; -import { Group } from 'src/app/types/group'; +import { BehaviorSubject, catchError, map, of, tap } from 'rxjs'; import { STATE } from 'src/app/types/state'; import { Status } from 'src/app/types/status'; -import User from 'src/app/types/user'; +import { User, UserAPI } from 'src/app/admin-view/account-mgmt/account.model'; import { environment } from 'src/environments/environment'; +import { DateTime } from 'luxon'; @Injectable({ providedIn: 'root' @@ -14,12 +14,14 @@ export class AccountMgmtService { constructor(private http: HttpClient) { } - private _accs = new BehaviorSubject[]>([]) + private _accs = new BehaviorSubject([]) public readonly accs = this._accs.asObservable() private _state = signal(STATE.NOT_LOADED); public readonly state = this._state.asReadonly(); private _error = signal(undefined); public readonly error = this._error.asReadonly(); + private _selAcc = new BehaviorSubject(null) + public readonly selAcc = this._selAcc.asObservable() public refresh() { this.getAccs() @@ -27,28 +29,41 @@ export class AccountMgmtService { private getAccs() { this._state.set(STATE.PENDING) - this.http.get<{ - users: Omit[] - groups: Group[] - }>(environment.apiEndpoint + `/admin/accs`, { withCredentials: true }) - .pipe(catchError((err: Error) => { - this._state.set(STATE.ERROR) - this._error.set(err.message) - return of() - })) - .subscribe(v => { + this.http.get + + (environment.apiEndpoint + `/admin/accs`, { withCredentials: true }) + .pipe( + catchError((err: Error) => { + this._state.set(STATE.ERROR) + this._error.set(err.message) + return of() + }), + map(v => { + return v.map(i => ({ + ...i, + regDate: DateTime.fromISO(i.regDate) + })) + }) + ).subscribe(v => { this._error.set(undefined) - this._accs.next(v.users ?? []) + this._accs.next(v ?? []) this._state.set(STATE.LOADED) }) } - postAcc(item: Omit) { - return this.http.post>( + selectAccount(acc: User) { + this._selAcc.next(acc) + } + + //#region legacy + postAcc(item: Omit) { + return this.http.post( environment.apiEndpoint + `/admin/accs`, item, { withCredentials: true } - ) + ).pipe(tap(v => { + if (v instanceof Array) this.refresh() + })) } putAcc(id: string, update: Partial) { @@ -72,11 +87,14 @@ export class AccountMgmtService { environment.apiEndpoint + `/admin/accs/${id}`, { withCredentials: true } ) + .pipe(tap(v => { + if (v.status == 200) this.refresh() + })) } getUser(id: string) { return this.http.get< - Omit & { lockout: boolean; regDate: string } + Omit & { lockout: boolean; regDate: string } >(environment.apiEndpoint + `/admin/accs/${id}`, { withCredentials: true, }) @@ -88,5 +106,5 @@ export class AccountMgmtService { { withCredentials: true } ) } - + //#endregion } diff --git a/src/app/types/user.ts b/src/app/admin-view/account-mgmt/account.model.ts similarity index 70% rename from src/app/types/user.ts rename to src/app/admin-view/account-mgmt/account.model.ts index ebecf77..5eae42f 100644 --- a/src/app/types/user.ts +++ b/src/app/admin-view/account-mgmt/account.model.ts @@ -1,9 +1,8 @@ import { DateTime } from 'luxon' -export default interface User { +export interface User { _id: string uname: string - pass: string room?: string admin?: number locked?: boolean @@ -13,3 +12,5 @@ export default interface User { regDate: DateTime defaultPage?: string } + +export type UserAPI = Omit & {regDate: "string"} diff --git a/src/app/admin-view/account-mgmt/user-edit/user-edit.component.html b/src/app/admin-view/account-mgmt/user-edit/user-edit.component.html index d3b761c..5bdd74c 100644 --- a/src/app/admin-view/account-mgmt/user-edit/user-edit.component.html +++ b/src/app/admin-view/account-mgmt/user-edit/user-edit.component.html @@ -17,7 +17,7 @@ Grupy - @for (item of groups; track $index) { + @for (item of adsyn.groups; track $index) { {{item.name}} } diff --git a/src/app/admin-view/account-mgmt/user-edit/user-edit.component.ts b/src/app/admin-view/account-mgmt/user-edit/user-edit.component.ts index a65c205..ce41754 100644 --- a/src/app/admin-view/account-mgmt/user-edit/user-edit.component.ts +++ b/src/app/admin-view/account-mgmt/user-edit/user-edit.component.ts @@ -13,6 +13,7 @@ import { UserResetComponent } from '../user-reset/user-reset.component' import { catchError, throwError } from 'rxjs' import { DateTime } from 'luxon' import { AccountMgmtService } from '../account-mgmt.service' +import { AdminSyncService } from '../../admin-sync.service' export namespace UserEditComponent { export type InputData = { type: 'new' | 'edit'; id?: string; groups: Group[] } @@ -37,7 +38,6 @@ export class UserEditComponent { groups: new FormControl>([]), flags: new FormControl>([]), }) - groups: Group[] id?: string regDate?: DateTime constructor( @@ -46,9 +46,9 @@ export class UserEditComponent { readonly ls: LocalStorageService, readonly acu: AccountMgmtService, private dialog: MatDialog, - private sb: MatSnackBar + private sb: MatSnackBar, + protected adsyn: AdminSyncService ) { - this.groups = data.groups if (data.type == 'edit') { this.id = data.id this.acu.getUser(data.id!).subscribe(r => { diff --git a/src/app/admin-view/admin-sync.service.spec.ts b/src/app/admin-view/admin-sync.service.spec.ts new file mode 100644 index 0000000..9ee7784 --- /dev/null +++ b/src/app/admin-view/admin-sync.service.spec.ts @@ -0,0 +1,23 @@ +import { TestBed } from '@angular/core/testing'; + +import { AdminSyncService } from './admin-sync.service'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; + +describe('AdminSyncService', () => { + let service: AdminSyncService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + provideHttpClient(), + provideHttpClientTesting() + ] + }); + service = TestBed.inject(AdminSyncService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/admin-view/admin-sync.service.ts b/src/app/admin-view/admin-sync.service.ts new file mode 100644 index 0000000..2b4a7d3 --- /dev/null +++ b/src/app/admin-view/admin-sync.service.ts @@ -0,0 +1,27 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Group } from '../types/group'; +import { environment } from 'src/environments/environment'; + +@Injectable({ + providedIn: 'root' +}) +export class AdminSyncService { + + constructor(private http: HttpClient) { } + + private _data: any + + private sync() { + this.http.get(environment.apiEndpoint + `/admin/sync`, { withCredentials: true }).subscribe(v => { + this._data = v + }) + } + + public get groups(): Group[] { + var groups = this._data?.groups + if (!groups) this.sync() + return groups + } + +}