src/app/core/user/user-list/user-list.component.ts

Implements

OnInit

Metadata

Relationships

Used by

No results matching.

Depends on

Index

Properties
Methods

Methods

addNewAccount
addNewAccount()
Returns : void
getRoleNames
getRoleNames(userAccount: UserAccount)
Parameters :
Name Type Optional
userAccount UserAccount No
Returns : string
loadUsers
loadUsers()
Returns : void
onPageChange
onPageChange(event: PageEvent)
Parameters :
Name Type Optional
event PageEvent No
Returns : void
openUserDetails
openUserDetails(user: UserAccount | null)
Parameters :
Name Type Optional
user UserAccount | null No
Returns : void

Properties

displayedColumns
Type : string[]
Default value : [ "email", "userEntityId", "enabled", "emailVerified", "roles", ]
hasUserManagementRole
Type : unknown
Default value : this.sessionInfo.value?.roles.includes( UserAdminService.ACCOUNT_MANAGER_ROLE, )
pagedUsers
Type : unknown
Default value : computed(() => { const start = this.pageIndex() * this.pageSize(); const end = start + this.pageSize(); return this.users().slice(start, end); })
pageIndex
Type : unknown
Default value : signal(0)
pageSize
Type : unknown
Default value : signal(10)
Readonly pageSizeOptions
Type : []
Default value : [10, 25, 50, 100]
users
Type : unknown
Default value : signal<UserAccount[]>([])
import {
  ChangeDetectionStrategy,
  Component,
  computed,
  inject,
  OnInit,
  signal,
} from "@angular/core";
import { UserAdminService } from "../user-admin-service/user-admin.service";
import { MatDialog } from "@angular/material/dialog";
import { SessionSubject } from "../../session/auth/session-info";
import { UserAccount } from "../user-admin-service/user-account";
import { Logging } from "../../logging/logging.service";
import {
  UserDetailsAction,
  UserDetailsComponent,
  UserDetailsDialogData,
} from "../user-details/user-details.component";
import { ViewTitleComponent } from "../../common-components/view-title/view-title.component";
import { MatTableModule } from "@angular/material/table";
import { MatButtonModule } from "@angular/material/button";
import { FaIconComponent } from "@fortawesome/angular-fontawesome";
import { EntityBlockComponent } from "../../basic-datatypes/entity/entity-block/entity-block.component";
import { AlertService } from "../../alerts/alert.service";
import { MatPaginatorModule, PageEvent } from "@angular/material/paginator";

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "app-user-list",
  imports: [
    ViewTitleComponent,
    MatTableModule,
    MatButtonModule,
    FaIconComponent,
    EntityBlockComponent,
    MatPaginatorModule,
  ],

  templateUrl: "./user-list.component.html",
  styleUrl: "./user-list.component.scss",
})
export class UserListComponent implements OnInit {
  private readonly userAdminService = inject(UserAdminService);
  private readonly dialog = inject(MatDialog);
  private readonly sessionInfo = inject(SessionSubject);
  private readonly alertService = inject(AlertService);

  hasUserManagementRole = this.sessionInfo.value?.roles.includes(
    UserAdminService.ACCOUNT_MANAGER_ROLE,
  );

  users = signal<UserAccount[]>([]);
  pageIndex = signal(0);
  pageSize = signal(10);
  readonly pageSizeOptions = [10, 25, 50, 100];
  pagedUsers = computed(() => {
    const start = this.pageIndex() * this.pageSize();
    const end = start + this.pageSize();
    return this.users().slice(start, end);
  });

  displayedColumns: string[] = [
    "email",
    "userEntityId",
    "enabled",
    "emailVerified",
    "roles",
  ];

  ngOnInit() {
    const isOnline = typeof navigator !== "undefined" ? navigator.onLine : true;

    if (this.hasUserManagementRole && isOnline) {
      this.loadUsers();
    } else if (!isOnline) {
      this.alertService.addInfo(
        $localize`User accounts cannot be loaded while offline.`,
      );
    }
  }

  loadUsers() {
    this.userAdminService.getAllUsers().subscribe({
      next: (users) => {
        this.users.set(users);
        this.pageIndex.set(0);
      },
      error: (err) => {
        Logging.error("Failed to load users:", err);
        this.alertService.addWarning(
          $localize`Failed to load users. Please try again later or contact your server administrator.`,
        );
      },
    });
  }

  onPageChange(event: PageEvent) {
    this.pageIndex.set(event.pageIndex);
    this.pageSize.set(event.pageSize);
  }

  getRoleNames(userAccount: UserAccount): string {
    return userAccount.roles?.map((r) => r.name).join(", ") || "-";
  }

  addNewAccount() {
    this.openUserDetails(null);
  }

  openUserDetails(user: UserAccount | null) {
    const dialogData: UserDetailsDialogData = {
      userAccount: user,
    };

    const dialogRef = this.dialog.open(UserDetailsComponent, {
      data: dialogData,
      width: "99%",
    });

    dialogRef.afterClosed().subscribe((action: UserDetailsAction) => {
      if (!action) {
        // Dialog was closed without an action (e.g., clicked outside or ESC)
        return;
      }

      switch (action.type) {
        case "accountUpdated": {
          const updatedUsers = this.users().map((u) =>
            u.id === action.data.user.id ? action.data.user : u,
          );
          this.users.set(updatedUsers);
          break;
        }
        case "accountCreated":
          this.loadUsers();
          break;
      }
    });
  }
}
<div class="flex-row flex-wrap">
  <div class="flex-grow">
    <app-view-title i18n>User Management</app-view-title>
  </div>
  @if (hasUserManagementRole) {
    <button
      mat-stroked-button
      color="accent"
      class="standard-add-button"
      (click)="addNewAccount()"
    >
      <fa-icon icon="plus-circle" class="standard-icon-with-text"></fa-icon>
      <span i18n>Add New Account</span>
    </button>
  }
</div>

@if (!hasUserManagementRole) {
  <div i18n>
    Your account does not have the required permissions to manage user accounts.
  </div>
} @else {
  <p i18n class="margin-bottom-regular">
    This page displays all user accounts registered for your system.
  </p>

  <div class="table-container">
    <table mat-table [dataSource]="pagedUsers()" class="full-width table">
      <ng-container matColumnDef="email">
        <th mat-header-cell *matHeaderCellDef i18n>Email</th>
        <td mat-cell *matCellDef="let user">{{ user.email || "-" }}</td>
      </ng-container>

      <ng-container matColumnDef="userEntityId">
        <th mat-header-cell *matHeaderCellDef i18n>Profile</th>
        <td mat-cell *matCellDef="let user">
          @if (user.userEntityId) {
            <app-entity-block [entityId]="user.userEntityId"></app-entity-block>
          } @else {
            <span>-</span>
          }
        </td>
      </ng-container>

      <ng-container matColumnDef="enabled">
        <th mat-header-cell *matHeaderCellDef i18n>Enabled</th>
        <td mat-cell *matCellDef="let user">
          @if (user.enabled) {
            <span i18n>Yes</span>
          } @else {
            <span i18n>No</span>
          }
        </td>
      </ng-container>

      <ng-container matColumnDef="emailVerified">
        <th mat-header-cell *matHeaderCellDef i18n>Email Verified</th>
        <td mat-cell *matCellDef="let user">
          @if (user.emailVerified) {
            <span i18n>Yes</span>
          } @else {
            <span i18n>No</span>
          }
        </td>
      </ng-container>

      <ng-container matColumnDef="roles">
        <th mat-header-cell *matHeaderCellDef i18n>Roles</th>
        <td mat-cell *matCellDef="let user">{{ getRoleNames(user) }}</td>
      </ng-container>

      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr
        mat-row
        *matRowDef="let row; columns: displayedColumns"
        (click)="openUserDetails(row)"
        class="clickable-row"
      ></tr>
    </table>

    <mat-paginator
      class="table-footer"
      [length]="users().length"
      [pageIndex]="pageIndex()"
      [pageSize]="pageSize()"
      [pageSizeOptions]="pageSizeOptions"
      [showFirstLastButtons]="true"
      (page)="onPageChange($event)"
      aria-label="User accounts pagination"
      i18n-aria-label
    ></mat-paginator>
  </div>
}

./user-list.component.scss

@use "mixins/list-table";

@include list-table.container-and-table;
@include list-table.footer;
@include list-table.clickable-row;

:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-container {
  justify-content: flex-start;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""