import { ChangeDetectionStrategy, Component, inject, OnInit, ViewChild } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { MatDialog } from '@angular/material/dialog'; import { MatPaginator } from '@angular/material/paginator'; import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSort } from '@angular/material/sort'; import { MatTableDataSource } from '@angular/material/table'; import { httpErrorMessage } from 'src/app/helpers/http-error'; import { Role, roleLabel } from 'src/app/models/role'; import { User } from 'src/app/models/user.model'; import { RolesService } from 'src/app/services/roles.service'; import { TokenStorageService } from 'src/app/services/token-storage.service'; import { UserService } from 'src/app/services/user.service'; import { RolesDialogComponent, RolesDialogData } from '../roles-dialog/roles-dialog.component'; @Component({ selector: 'app-board-admin', templateUrl: './board-admin.component.html', styleUrls: ['./board-admin.component.scss'], changeDetection: ChangeDetectionStrategy.Eager, standalone: false }) export class BoardAdminComponent implements OnInit { private readonly userService = inject(UserService); private readonly rolesService = inject(RolesService); private readonly tokenStorage = inject(TokenStorageService); private readonly dialog = inject(MatDialog); private readonly snackBar = inject(MatSnackBar); readonly displayedColumns = ['username', 'email', 'roles', 'actions']; readonly dataSource = new MatTableDataSource([]); readonly roleLabel = roleLabel; readonly currentUserId = this.tokenStorage.getUser().id; private allRoles: Role[] = []; @ViewChild(MatSort) set sort(sort: MatSort) { this.dataSource.sort = sort; } @ViewChild(MatPaginator) set paginator(paginator: MatPaginator) { this.dataSource.paginator = paginator; } ngOnInit(): void { this.dataSource.sortingDataAccessor = (user, column) => column === 'roles' ? (user.roles ?? []).length : String((user as any)[column] ?? '').toLowerCase(); this.dataSource.filterPredicate = (user, filter) => [user.username, user.email, ...(user.roles ?? []).map(role => roleLabel(role.name))] .some(value => value?.toLowerCase().includes(filter)); this.userService.getAll().subscribe({ // На пустой результат сервер отвечает 204 без тела next: users => this.dataSource.data = users ?? [], error: err => this.showError(err) }); this.rolesService.getAllRoles().subscribe({ next: roles => this.allRoles = roles ?? [], error: err => this.showError(err) }); } applyFilter(value: string): void { this.dataSource.filter = value.trim().toLowerCase(); this.dataSource.paginator?.firstPage(); } initials(user: User): string { return (user.username ?? '?').slice(0, 2); } editRoles(user: User): void { const data: RolesDialogData = { user, allRoles: this.allRoles }; this.dialog.open(RolesDialogComponent, { width: '360px', data }) .afterClosed() .subscribe((roles?: string[]) => { if (roles) { this.saveRoles(user, roles); } }); } private saveRoles(user: User, roles: string[]): void { this.userService.updateRoles(user.id, roles).subscribe({ next: updated => { this.dataSource.data = this.dataSource.data.map(u => u.id === updated.id ? updated : u); if (updated.id === this.currentUserId) { // Собственные роли хранятся в сессии — обновляем, чтобы меню и доступы совпадали с сервером this.tokenStorage.saveUser({ ...this.tokenStorage.getUser(), roles }); } this.snackBar.open(`Roles of ${updated.username} were updated`, 'OK', { duration: 3000 }); }, error: err => this.showError(err) }); } private showError(err: HttpErrorResponse): void { this.snackBar.open(httpErrorMessage(err), 'OK', { duration: 5000 }); } }