diff --git a/src/app/admin-view/account-mgmt/account-mgmt.component.html b/src/app/admin-view/account-mgmt/account-mgmt.component.html index 2ff5cb5..e62b531 100644 --- a/src/app/admin-view/account-mgmt/account-mgmt.component.html +++ b/src/app/admin-view/account-mgmt/account-mgmt.component.html @@ -1,37 +1,39 @@
- + Wyszukaj
-@if (loading) { - -} - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - - - -
Imię{{element.fname}}Nazwisko{{element.surname}}Pokój{{element.room}}Nazwa użytkownika{{element.uname}}Karta użytkownika - -
- \ No newline at end of file +
+ @if (ac.state() != STATE.LOADED) { + + } + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + + +
Imię{{element.fname}}Nazwisko{{element.surname}}Pokój{{element.room}}Nazwa użytkownika{{element.uname}}Karta użytkownika + +
+ +
diff --git a/src/app/admin-view/account-mgmt/account-mgmt.component.scss b/src/app/admin-view/account-mgmt/account-mgmt.component.scss index 390db66..5475256 100644 --- a/src/app/admin-view/account-mgmt/account-mgmt.component.scss +++ b/src/app/admin-view/account-mgmt/account-mgmt.component.scss @@ -4,6 +4,13 @@ height: 100%; } +.mainc { + position: relative; + height: 100%; + display: flex; + flex-direction: column; +} + mat-paginator { margin-top: auto; } diff --git a/src/app/admin-view/account-mgmt/account-mgmt.component.spec.ts b/src/app/admin-view/account-mgmt/account-mgmt.component.spec.ts index ec96d0d..81c782a 100644 --- a/src/app/admin-view/account-mgmt/account-mgmt.component.spec.ts +++ b/src/app/admin-view/account-mgmt/account-mgmt.component.spec.ts @@ -6,26 +6,31 @@ import { MatSnackBarModule } from '@angular/material/snack-bar' import { MatFormFieldModule } from '@angular/material/form-field' import { MatIconModule } from '@angular/material/icon' import { MatPaginatorModule } from '@angular/material/paginator' -import { of } from 'rxjs' import { MatTableModule } from '@angular/material/table' import { MatInputModule } from '@angular/material/input' import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { MatProgressSpinnerModule } from '@angular/material/progress-spinner' +import { AccountMgmtService } from './account-mgmt.service' +import { LoadShadeComponent } from 'src/app/commonComponents/load-shade/load-shade.component' +import { of } from 'rxjs' +import { signal } from '@angular/core' +import { STATE } from 'src/app/types/state' -xdescribe('AccountMgmtComponent', () => { +describe('AccountMgmtComponent', () => { let component: AccountMgmtComponent let fixture: ComponentFixture let acMock beforeEach(async () => { acMock = { - accs: { - getAccs: jasmine.createSpy('getAccs').and.returnValue(of()), - }, + accs: of([]), + state: signal(STATE.NOT_LOADED), + refresh: jasmine.createSpy('getAccs'), + error: signal(undefined) } await TestBed.configureTestingModule({ - declarations: [AccountMgmtComponent], - // providers: [{ provide: AdminCommService, useValue: acMock }], + declarations: [AccountMgmtComponent, LoadShadeComponent], + providers: [{ provide: AccountMgmtService, useValue: acMock }], imports: [ MatDialogModule, MatSnackBarModule, 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 7765b3e..94a740f 100644 --- a/src/app/admin-view/account-mgmt/account-mgmt.component.ts +++ b/src/app/admin-view/account-mgmt/account-mgmt.component.ts @@ -1,13 +1,13 @@ -import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core' +import { AfterViewInit, Component, ViewChild } from '@angular/core' import { MatDialog } from '@angular/material/dialog' import { MatTableDataSource } from '@angular/material/table' import { MatPaginator } from '@angular/material/paginator' -import { MatSnackBar } from '@angular/material/snack-bar' 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 { AccountMgmtService } from './account-mgmt.service' +import { STATE } from 'src/app/types/state' @Component({ selector: 'app-account-mgmt', @@ -15,16 +15,14 @@ import { AccountMgmtService } from './account-mgmt.service' styleUrls: ['./account-mgmt.component.scss'], standalone: false, }) -export class AccountMgmtComponent implements OnInit, AfterViewInit { +export class AccountMgmtComponent implements AfterViewInit { protected groups: Group[] = [] users: MatTableDataSource> - loading = false @ViewChild(MatPaginator) paginator!: MatPaginator constructor( - readonly ac: AccountMgmtService, + protected ac: AccountMgmtService, private dialog: MatDialog, - private sb: MatSnackBar, protected readonly ls: LocalStorageService ) { this.users = new MatTableDataSource>() @@ -43,21 +41,20 @@ export class AccountMgmtComponent implements OnInit, AfterViewInit { const filternew = filter.trim().toLowerCase() return dataStr.indexOf(filternew) != -1 } + this.ac.refresh() + this.ac.accs.subscribe(d => { + this.users.data = d ?? [] + }) + } + + protected get STATE(): typeof STATE { + return STATE } ngAfterViewInit() { this.users.paginator = this.paginator } - ngOnInit() { - this.loading = true - this.ac.getAccs().subscribe(data => { - this.loading = false - this.users.data = data.users - this.groups = data.groups - }) - } - filter(event: Event) { const value = (event.target as HTMLInputElement).value this.users.filter = value.toLowerCase().trim() @@ -74,7 +71,7 @@ export class AccountMgmtComponent implements OnInit, AfterViewInit { }) .afterClosed() .subscribe(r => { - if (r) this.ngOnInit() + if (r) this.ac.refresh() }) } 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 0b42ad6..a14b9e0 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 @@ -1,16 +1,43 @@ import { TestBed } from '@angular/core/testing'; 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'; -xdescribe('AccountMgmtService', () => { +describe('AccountMgmtService', () => { let service: AccountMgmtService; + let httpTesting: HttpTestingController; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ + providers: [ + provideHttpClient(), + provideHttpClientTesting() + ] + }); + httpTesting = TestBed.inject(HttpTestingController); service = TestBed.inject(AccountMgmtService); }); it('should be created', () => { expect(service).toBeTruthy(); }); + + it('should get user accounts', () => { + service.refresh() + const req = httpTesting.expectOne(environment.apiEndpoint+"/admin/accs", "Request to load all users") + + expect(req.request.method).toBe("GET") + + req.flush([]) + httpTesting.verify() + }) + + it('should create a user account and refresh list', () => { + service.postAcc({ + uname: "test", + groups: [] + }) + }) }); 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 22c50d1..1e7cb3c 100644 --- a/src/app/admin-view/account-mgmt/account-mgmt.service.ts +++ b/src/app/admin-view/account-mgmt/account-mgmt.service.ts @@ -1,6 +1,8 @@ import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, signal } from '@angular/core'; +import { BehaviorSubject, catchError, of } from 'rxjs'; import { Group } from 'src/app/types/group'; +import { STATE } from 'src/app/types/state'; import { Status } from 'src/app/types/status'; import User from 'src/app/types/user'; import { environment } from 'src/environments/environment'; @@ -12,15 +14,37 @@ export class AccountMgmtService { constructor(private http: HttpClient) { } - getAccs() { - return this.http.get<{ + 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(); + + public refresh() { + this.getAccs() + } + + 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._error.set(undefined) + this._accs.next(v.users ?? []) + this._state.set(STATE.LOADED) + }) } - postAcc(item: any) { - return this.http.post( + postAcc(item: Omit) { + return this.http.post>( environment.apiEndpoint + `/admin/accs`, item, { withCredentials: true } @@ -38,7 +62,7 @@ export class AccountMgmtService { resetPass(id: string) { return this.http.patch( environment.apiEndpoint + `/admin/accs/${id}/reset`, - {}, + undefined, { withCredentials: true } ) } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 084a4c5..80ca0a5 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -85,7 +85,8 @@ import { MessageComponent } from './admin-view/notifications/outbox/message/mess import { NotifDialogComponent } from './app-view/notif-dialog/notif-dialog.component' import { UserSearchComponent } from './commonComponents/user-search/user-search.component' import { StartAdminComponent } from './admin-view/start/start.component' -import { provideLuxonDateAdapter } from '@angular/material-luxon-adapter' +import { provideLuxonDateAdapter } from '@angular/material-luxon-adapter'; +import { LoadShadeComponent } from './commonComponents/load-shade/load-shade.component' @NgModule({ declarations: [ @@ -137,6 +138,7 @@ import { provideLuxonDateAdapter } from '@angular/material-luxon-adapter' NotifDialogComponent, UserSearchComponent, StartAdminComponent, + LoadShadeComponent, ], bootstrap: [AppComponent], imports: [ diff --git a/src/app/commonComponents/load-shade/load-shade.component.html b/src/app/commonComponents/load-shade/load-shade.component.html new file mode 100644 index 0000000..c8f1525 --- /dev/null +++ b/src/app/commonComponents/load-shade/load-shade.component.html @@ -0,0 +1,22 @@ +@switch (state()) { + @case (STATE.PENDING) { + + } + @case (STATE.ERROR) { + + + Błąd wczytywania + Podczas wczytywania treści wystąpił błąd. + + +

{{ error() }}

+
+ + + +
+ } + @default { +

{{state()}}

+ } +} diff --git a/src/app/commonComponents/load-shade/load-shade.component.scss b/src/app/commonComponents/load-shade/load-shade.component.scss new file mode 100644 index 0000000..6efa867 --- /dev/null +++ b/src/app/commonComponents/load-shade/load-shade.component.scss @@ -0,0 +1,11 @@ +:host { + position: absolute; + background-color: hsla(0, 0%, 0%, 0.5); + top: 0; + bottom: 0; + right: 0; + left: 0; + display: flex; + align-items: center; + justify-content: center; +} diff --git a/src/app/commonComponents/load-shade/load-shade.component.spec.ts b/src/app/commonComponents/load-shade/load-shade.component.spec.ts new file mode 100644 index 0000000..bcc5c08 --- /dev/null +++ b/src/app/commonComponents/load-shade/load-shade.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoadShadeComponent } from './load-shade.component'; + +describe('LoadShadeComponent', () => { + let component: LoadShadeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [LoadShadeComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LoadShadeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/commonComponents/load-shade/load-shade.component.ts b/src/app/commonComponents/load-shade/load-shade.component.ts new file mode 100644 index 0000000..c40847c --- /dev/null +++ b/src/app/commonComponents/load-shade/load-shade.component.ts @@ -0,0 +1,18 @@ +import { Component, input, output } from '@angular/core'; +import { STATE } from 'src/app/types/state'; + +@Component({ + selector: 'app-load-shade', + standalone: false, + templateUrl: './load-shade.component.html', + styleUrl: './load-shade.component.scss', +}) +export class LoadShadeComponent { + state = input(STATE.NOT_LOADED) + error = input(undefined) + refresh = output() + + public get STATE(): typeof STATE { + return STATE + } +} diff --git a/src/app/types/state.ts b/src/app/types/state.ts new file mode 100644 index 0000000..986a3c4 --- /dev/null +++ b/src/app/types/state.ts @@ -0,0 +1,6 @@ +export enum STATE { + NOT_LOADED, + PENDING, + LOADED, + ERROR +}