fix: Redesigned user cards

This commit is contained in:
2025-05-21 19:56:25 +02:00
parent 45fb44712e
commit cf2fa0b607
7 changed files with 247 additions and 156 deletions

View File

@@ -3,7 +3,7 @@
<mat-label>Wyszukaj</mat-label> <mat-label>Wyszukaj</mat-label>
<input matInput (keyup)="filter($event)"> <input matInput (keyup)="filter($event)">
</mat-form-field> </mat-form-field>
<button mat-icon-button (click)="new()"><mat-icon>add</mat-icon></button> <button mat-icon-button (click)="openUserCard()"><mat-icon>add</mat-icon></button>
</div> </div>
<mat-spinner *ngIf="loading"></mat-spinner> <mat-spinner *ngIf="loading"></mat-spinner>
<table mat-table [dataSource]="users"> <table mat-table [dataSource]="users">
@@ -24,17 +24,9 @@
<td mat-cell *matCellDef="let element">{{element.uname}}</td> <td mat-cell *matCellDef="let element">{{element.uname}}</td>
</div> </div>
<div matColumnDef="actions"> <div matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>Akcje</th> <th mat-header-cell *matHeaderCellDef>Karta użytkownika</th>
<td mat-cell *matCellDef="let element"> <td mat-cell *matCellDef="let element">
<button mat-mini-fab (click)="resetPass(element._id)"><mat-icon>lock_reset</mat-icon></button> <button mat-mini-fab (click)="openUserCard(element._id)"><mat-icon>manage_accounts</mat-icon></button>
<button mat-mini-fab (click)="edit(element)"><mat-icon>edit</mat-icon></button>
<button mat-mini-fab (click)="toggleLock(element)">
<div [ngSwitch]="element.locked">
<mat-icon *ngSwitchCase="true">lock</mat-icon>
<mat-icon *ngSwitchDefault>lock_open</mat-icon>
</div>
</button>
<button mat-mini-fab (click)="delete(element._id)"><mat-icon>delete_forever</mat-icon></button>
</td> </td>
</div> </div>
<tr mat-header-row *matHeaderRowDef="collumns"></tr> <tr mat-header-row *matHeaderRowDef="collumns"></tr>

View File

@@ -4,10 +4,7 @@ import { MatDialog } from '@angular/material/dialog';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
import { UserDeleteComponent } from './user-delete/user-delete.component';
import { UserEditComponent } from './user-edit/user-edit.component'; import { UserEditComponent } from './user-edit/user-edit.component';
import { catchError, throwError } from 'rxjs';
import { UserResetComponent } from './user-reset/user-reset.component';
import { LocalStorageService } from 'src/app/services/local-storage.service'; import { LocalStorageService } from 'src/app/services/local-storage.service';
import { Group } from 'src/app/types/group'; import { Group } from 'src/app/types/group';
import User from 'src/app/types/user'; import User from 'src/app/types/user';
@@ -57,75 +54,9 @@ export class AccountMgmtComponent implements OnInit, AfterViewInit {
this.users.filter = value.toLowerCase().trim() this.users.filter = value.toLowerCase().trim()
} }
edit(item: any) { openUserCard(id?: string) {
this.dialog.open(UserEditComponent, {data: {user: item, groups: this.groups}}).afterClosed().subscribe(reply => { this.dialog.open(UserEditComponent, {data: {id: id, type: id ? "edit" : "new", groups: this.groups}}).afterClosed().subscribe(r => {
if (reply) { if (r) this.ngOnInit()
this.ac.accs.putAcc(item._id, reply).pipe(catchError((err)=>{
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.")
return throwError(()=> new Error(err.message))
})).subscribe((data)=> {
if (data.status == 200) {
this.sb.open("Użytkownik został zmodyfikowany.", undefined, {duration: 2500})
this.ngOnInit()
} else {
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.")
}
})
}
})
}
new() {
this.dialog.open(UserEditComponent, {data: {groups: this.groups}}).afterClosed().subscribe(reply => {
if (reply) {
this.ac.accs.postAcc(reply).pipe(catchError((err)=>{
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.")
return throwError(()=> new Error(err.message))
})).subscribe((data)=> {
if (data.status == 201) {
this.sb.open("Użytkownik został utworzony.", undefined, {duration: 2500})
this.ngOnInit()
} else {
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.")
}
})
}
})
}
delete(id: string) {
this.dialog.open(UserDeleteComponent).afterClosed().subscribe(reply => {
if (reply) {
this.ac.accs.deleteAcc(id).subscribe((res) => {
if (res.status == 200) {
this.sb.open("Użytkownik został usunięty.", undefined, {duration: 2500})
this.ngOnInit()
} else {
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.")
console.error(res);
}
})
}
})
}
resetPass(id: string) {
this.dialog.open(UserResetComponent).afterClosed().subscribe((res) => {
if (res == true) {
this.ac.accs.resetPass(id).subscribe((patch)=>{
if (patch.status == 200) {
this.sb.open("Hasło zostało zresetowane", undefined, {duration: 2500})
}
})
}
})
}
toggleLock(item: any) {
this.ac.accs.putAcc(item._id, {locked: !item.locked}).subscribe((res) => {
if (res.status == 200) {
item.locked = !item.locked
}
}) })
} }

View File

@@ -1,21 +1,20 @@
<form [formGroup]="form" (ngSubmit)="editUser()"> <h1 mat-dialog-title>Karta użytkownika</h1>
<mat-form-field appearance="outline"> <mat-dialog-content>
<form [formGroup]="form">
<div>
<mat-form-field appearance="outline" color="accent">
<mat-label>Imię</mat-label> <mat-label>Imię</mat-label>
<input type="text" matInput formControlName="fname"> <input type="text" matInput formControlName="fname">
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline"> <mat-form-field appearance="outline" color="accent">
<mat-label>Nazwisko</mat-label> <mat-label>Nazwisko</mat-label>
<input type="text" matInput formControlName="surname"> <input type="text" matInput formControlName="surname">
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline"> <mat-form-field appearance="outline" color="accent">
<mat-label>Pokój</mat-label> <mat-label>Pokój</mat-label>
<input type="text" matInput formControlName="room"> <input type="text" matInput formControlName="room">
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline"> <mat-form-field appearance="outline" color="accent">
<mat-label>Nazwa użytkownika</mat-label>
<input type="text" matInput required formControlName="uname">
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Grupy</mat-label> <mat-label>Grupy</mat-label>
<mat-select multiple formControlName="groups"> <mat-select multiple formControlName="groups">
@for (item of groups; track $index) { @for (item of groups; track $index) {
@@ -23,7 +22,26 @@
} }
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="this.ls.permChecker(32)"> <span *ngIf="data.type == 'edit'">Data rejestracji:<br>{{regDate?.format('DD.MM.YYYY')}}</span>
</div>
<div>
<mat-form-field appearance="outline" color="accent">
<mat-label>Nazwa użytkownika</mat-label>
<input type="text" matInput required formControlName="uname">
</mat-form-field>
@if (data.type == "edit") {
<button mat-stroked-button color="accent" (click)="resetPass()">Resetuj hasło</button>
@if (locked) {
<button mat-stroked-button color="warn" (click)="toggleLock(false)"><mat-icon>lock</mat-icon>Blokada ręczna</button>
} @else {
<button mat-stroked-button color="accent" (click)="toggleLock(true)">Zablokuj konto</button>
}
@if (lockout) {
<button mat-stroked-button color="warn" (click)="disableLockout()"><mat-icon>lock_clock</mat-icon>Auto-Blokada</button>
} @else {
<button mat-stroked-button disabled>Auto-Blokada nieczynna</button>
}
<mat-form-field *ngIf="ls.permChecker(32)" color="accent">
<mat-label>Uprawnienia</mat-label> <mat-label>Uprawnienia</mat-label>
<mat-select multiple formControlName="flags"> <mat-select multiple formControlName="flags">
<mat-option [value]="1" *ngIf="ls.capCheck(1)">Wiadomości</mat-option> <mat-option [value]="1" *ngIf="ls.capCheck(1)">Wiadomości</mat-option>
@@ -35,5 +53,15 @@
<mat-option [value]="128" *ngIf="ls.capCheck(16)">Czystość</mat-option> <mat-option [value]="128" *ngIf="ls.capCheck(16)">Czystość</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<button mat-stroked-button>Wyślij</button> }
</form> </div>
</form>
</mat-dialog-content>
<mat-dialog-actions>
@if (data.type == "edit") {
<button mat-stroked-button color="warn" style="margin-right: auto;">Usuń konto</button>
}
<button mat-stroked-button mat-dialog-close>Zamknij</button>
<button mat-flat-button color="accent" (click)="submit()">Zapisz</button>
<mat-spinner diameter="32" color="accent" *ngIf="loading"></mat-spinner>
</mat-dialog-actions>

View File

@@ -4,7 +4,29 @@
} }
form { form {
margin-top: 1ch !important;
display: flex; display: flex;
flex-direction: column; grid-auto-flow: column;
flex-direction: row;
flex-wrap: wrap;
align-items: center; align-items: center;
justify-content: center;
column-gap: 3ch;
div {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: repeat(5, 1fr);
align-items: center;
button {
align-self: stretch;
justify-self: stretch;
height: auto;
margin-bottom: 1lh;
}
}
}
mat-dialog-actions {
display: flex;
justify-content: flex-end;
} }

View File

@@ -1,8 +1,15 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { LocalStorageService } from 'src/app/services/local-storage.service'; import { LocalStorageService } from 'src/app/services/local-storage.service';
import { Group } from 'src/app/types/group'; import { Group } from 'src/app/types/group';
import { AdminCommService } from '../../admin-comm.service';
import { UserDeleteComponent } from '../user-delete/user-delete.component';
import { MatSnackBar } from '@angular/material/snack-bar';
import { UserResetComponent } from '../user-reset/user-reset.component';
import { catchError, throwError } from 'rxjs';
import { Moment } from 'moment';
import * as moment from 'moment';
@Component({ @Component({
selector: 'app-user-edit', selector: 'app-user-edit',
@@ -10,43 +17,105 @@ import { Group } from 'src/app/types/group';
styleUrls: ['./user-edit.component.scss'] styleUrls: ['./user-edit.component.scss']
}) })
export class UserEditComponent { export class UserEditComponent {
form: FormGroup lockout = false;
locked = false;
loading = false;
form: FormGroup = new FormGroup({
fname: new FormControl<string>(""),
surname: new FormControl<string>(""),
room: new FormControl<string>(""),
uname: new FormControl<string>(""),
groups: new FormControl<Array<string>>([]),
flags: new FormControl<Array<number>>([]),
})
groups: Group[] groups: Group[]
constructor (public dialogRef: MatDialogRef<UserEditComponent>, @Inject(MAT_DIALOG_DATA) public data: any, readonly ls: LocalStorageService) { id?: string
if (data.user == null) { regDate?: Moment;
data.user = { constructor (
fname: "", public dialogRef: MatDialogRef<UserEditComponent>,
surname: "", @Inject(MAT_DIALOG_DATA) public data: ({type: "edit", id: string} | {type: "new"}) & {groups: Group[]},
room: "", readonly ls: LocalStorageService,
uname: "", readonly acu: AdminCommService,
groups: [], private dialog: MatDialog,
admin: 0 private sb: MatSnackBar
} ) {
} this.groups = data.groups
this.groups = data.groups ? data.groups : [] if (data.type == "edit") {
this.id = data.id
this.acu.accs.getUser(data.id).subscribe((r) => {
this.regDate = moment(r.regDate)
var flags: Array<number> = [] var flags: Array<number> = []
if (data.user.admin) { if (r.admin) {
if ((data.user.admin & 1) == 1) flags.push(1) if ((r.admin & 1) == 1) flags.push(1)
if ((data.user.admin & 2) == 2) flags.push(2) if ((r.admin & 2) == 2) flags.push(2)
if ((data.user.admin & 4) == 4) flags.push(4) if ((r.admin & 4) == 4) flags.push(4)
if ((data.user.admin & 8) == 8) flags.push(8) if ((r.admin & 8) == 8) flags.push(8)
if ((data.user.admin & 16) == 16) flags.push(16) if ((r.admin & 16) == 16) flags.push(16)
if ((data.user.admin & 32) == 32) flags.push(32) if ((r.admin & 32) == 32) flags.push(32)
if ((data.user.admin & 64) == 64) flags.push(64) if ((r.admin & 64) == 64) flags.push(64)
if ((data.user.admin & 128) == 128) flags.push(128) if ((r.admin & 128) == 128) flags.push(128)
}
this.locked = r.locked ? true : false
this.lockout = r.lockout
this.form.get("fname")?.setValue(r.fname)
this.form.get("surname")?.setValue(r.surname)
this.form.get("room")?.setValue(r.room)
this.form.get("uname")?.setValue(r.uname)
this.form.get("groups")?.setValue(r.groups)
this.form.get("flags")?.setValue(flags)
})
}
}
protected submit() {
this.loading = true
if (this.data.type == "edit") {
this.acu.accs.putAcc(this.id!, this.getForm()).pipe(catchError((err)=>{
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.")
return throwError(()=> new Error(err.message))
})).subscribe((data)=> {
if (data.status == 200) {
this.sb.open("Użytkownik został zmodyfikowany.", undefined, {duration: 2500})
this.dialogRef.close(true)
} else {
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.")
this.loading = false
}
})
} else {
this.acu.accs.postAcc(this.getForm()).pipe(catchError((err)=>{
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.")
return throwError(()=> new Error(err.message))
})).subscribe((data)=> {
if (data.status == 201) {
this.sb.open("Użytkownik został utworzony.", undefined, {duration: 2500})
this.dialogRef.close(true)
} else {
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.")
this.loading = false
}
})
}
}
protected disableLockout() {
this.loading = true
this.acu.accs.clearLockout(this.id!).pipe(catchError((err)=>{
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.")
return throwError(()=> new Error(err.message))
})).subscribe((s) => {
if (s.status == 200) {
this.loading = false
this.lockout = false
} else {
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.")
this.loading = false
} }
this.form = new FormGroup({
fname: new FormControl(data.user.fname),
surname: new FormControl(data.user.surname),
room: new FormControl(data.user.room),
uname: new FormControl<string>(data.user.uname),
groups: new FormControl<Array<string>>(data.user.groups),
flags: new FormControl<Array<number>>(flags),
}) })
} }
protected editUser() { protected getForm() {
this.dialogRef.close({ return {
fname: this.form.get('fname')?.value, fname: this.form.get('fname')?.value,
surname: this.form.get('surname')?.value, surname: this.form.get('surname')?.value,
room: this.form.get('room')?.value, room: this.form.get('room')?.value,
@@ -60,6 +129,44 @@ export class UserEditComponent {
return undefined return undefined
} }
})() })()
}
}
protected delete() {
this.dialog.open(UserDeleteComponent).afterClosed().subscribe(reply => {
if (reply) {
this.acu.accs.deleteAcc(this.id!).subscribe((res) => {
if (res.status == 200) {
this.sb.open("Użytkownik został usunięty.", undefined, {duration: 2500})
this.dialogRef.close()
} else {
this.sb.open("Wystąpił błąd. Skontaktuj się z obsługą programu.")
console.error(res);
}
})
}
})
}
protected resetPass() {
this.loading = true
this.dialog.open(UserResetComponent).afterClosed().subscribe((res) => {
if (res == true) {
this.acu.accs.resetPass(this.id!).subscribe((patch)=>{
if (patch.status == 200) {
this.sb.open("Hasło zostało zresetowane", undefined, {duration: 2500})
this.loading = false
}
})
}
})
}
protected toggleLock(state: boolean) {
this.acu.accs.putAcc(this.id!, {locked: state}).subscribe((res) => {
if (res.status == 200) {
this.locked = state
}
}) })
} }
} }

View File

@@ -131,7 +131,7 @@ export class AdminCommService {
return this.http.post<Status>(environment.apiEndpoint+`/admin/accs`, item, {withCredentials: true}) return this.http.post<Status>(environment.apiEndpoint+`/admin/accs`, item, {withCredentials: true})
}, },
putAcc: (id: string, update: object) => { putAcc: (id: string, update: Partial<User>) => {
return this.http.put<Status>(environment.apiEndpoint+`/admin/accs/${id}`, update, {withCredentials: true}) return this.http.put<Status>(environment.apiEndpoint+`/admin/accs/${id}`, update, {withCredentials: true})
}, },
@@ -141,6 +141,14 @@ export class AdminCommService {
deleteAcc: (id: string) => { deleteAcc: (id: string) => {
return this.http.delete<Status>(environment.apiEndpoint+`/admin/accs/${id}`, {withCredentials: true}) return this.http.delete<Status>(environment.apiEndpoint+`/admin/accs/${id}`, {withCredentials: true})
},
getUser: (id: string) => {
return this.http.get<Omit<User, "pass"> & {lockout: boolean}>(environment.apiEndpoint+`/admin/accs/${id}`, {withCredentials: true})
},
clearLockout: (id: string) => {
return this.http.delete<Status>(environment.apiEndpoint+`/admin/accs/${id}/lockout`, {withCredentials: true})
} }
} }
//#endregion //#endregion

View File

@@ -1,3 +1,5 @@
import { Moment } from "moment";
export default interface User { export default interface User {
_id: string; _id: string;
uname: string; uname: string;
@@ -8,4 +10,5 @@ export default interface User {
fname?: string; fname?: string;
surname?: string; surname?: string;
groups: string[]; groups: string[];
regDate: Moment;
} }