Browse Source

Task/improve type safety for admin users component (#6903)

* feat(client): resolve errors

* feat(client): migrate constructor based DI to inject function

* feat(client): implement view child signal

* feat(client): replace deprecated getDeviceInfo

* fix(client): enforce encapsulation and immutability
pull/6908/head
Kenrick Tandrian 5 days ago
committed by GitHub
parent
commit
83ff3caf42
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 103
      apps/client/src/app/components/admin-users/admin-users.component.ts
  2. 5
      apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts
  3. 10
      apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts

103
apps/client/src/app/components/admin-users/admin-users.component.ts

@ -1,8 +1,11 @@
import { UserDetailDialogParams } from '@ghostfolio/client/components/user-detail-dialog/interfaces/interfaces'; import {
UserDetailDialogParams,
UserDetailDialogResult
} from '@ghostfolio/client/components/user-detail-dialog/interfaces/interfaces';
import { GfUserDetailDialogComponent } from '@ghostfolio/client/components/user-detail-dialog/user-detail-dialog.component'; import { GfUserDetailDialogComponent } from '@ghostfolio/client/components/user-detail-dialog/user-detail-dialog.component';
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; import { DEFAULT_PAGE_SIZE, locale } from '@ghostfolio/common/config';
import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { import {
getDateFnsLocale, getDateFnsLocale,
@ -25,9 +28,11 @@ import { CommonModule } from '@angular/common';
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
computed,
DestroyRef, DestroyRef,
inject,
OnInit, OnInit,
ViewChild viewChild
} from '@angular/core'; } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -76,37 +81,42 @@ import { switchMap, tap } from 'rxjs/operators';
templateUrl: './admin-users.html' templateUrl: './admin-users.html'
}) })
export class GfAdminUsersComponent implements OnInit { export class GfAdminUsersComponent implements OnInit {
@ViewChild(MatPaginator) paginator: MatPaginator; protected dataSource = new MatTableDataSource<
AdminUsersResponse['users'][0]
public dataSource = new MatTableDataSource<AdminUsersResponse['users'][0]>(); >();
public defaultDateFormat: string; protected defaultDateFormat: string;
public deviceType: string; protected displayedColumns: string[] = [];
public displayedColumns: string[] = []; protected readonly getEmojiFlag = getEmojiFlag;
public getEmojiFlag = getEmojiFlag; protected hasPermissionForSubscription: boolean;
public hasPermissionForSubscription: boolean; protected hasPermissionToImpersonateAllUsers: boolean;
public hasPermissionToImpersonateAllUsers: boolean; protected info: InfoItem;
public info: InfoItem; protected isLoading = false;
public isLoading = false; protected readonly pageSize = DEFAULT_PAGE_SIZE;
public pageSize = DEFAULT_PAGE_SIZE; protected readonly routerLinkAdminControlUsers =
public routerLinkAdminControlUsers =
internalRoutes.adminControl.subRoutes.users.routerLink; internalRoutes.adminControl.subRoutes.users.routerLink;
public totalItems = 0; protected totalItems = 0;
public user: User; protected user: User;
private readonly deviceType = computed(
() => this.deviceDetectorService.deviceInfo().deviceType
);
private readonly paginator = viewChild.required(MatPaginator);
public constructor( private readonly adminService = inject(AdminService);
private adminService: AdminService, private readonly changeDetectorRef = inject(ChangeDetectorRef);
private changeDetectorRef: ChangeDetectorRef, private readonly dataService = inject(DataService);
private dataService: DataService, private readonly destroyRef = inject(DestroyRef);
private destroyRef: DestroyRef, private readonly deviceDetectorService = inject(DeviceDetectorService);
private deviceDetectorService: DeviceDetectorService, private readonly dialog = inject(MatDialog);
private dialog: MatDialog, private readonly impersonationStorageService = inject(
private impersonationStorageService: ImpersonationStorageService, ImpersonationStorageService
private notificationService: NotificationService, );
private route: ActivatedRoute, private readonly notificationService = inject(NotificationService);
private router: Router, private readonly route = inject(ActivatedRoute);
private userService: UserService private readonly router = inject(Router);
) { private readonly userService = inject(UserService);
this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
public constructor() {
this.info = this.dataService.fetchInfo(); this.info = this.dataService.fetchInfo();
this.hasPermissionForSubscription = hasPermission( this.hasPermissionForSubscription = hasPermission(
@ -176,7 +186,7 @@ export class GfAdminUsersComponent implements OnInit {
this.fetchUsers(); this.fetchUsers();
} }
public formatDistanceToNow(aDateString: string) { protected formatDistanceToNow(aDateString: string) {
if (aDateString) { if (aDateString) {
const distanceString = formatDistanceToNowStrict(parseISO(aDateString), { const distanceString = formatDistanceToNowStrict(parseISO(aDateString), {
addSuffix: true, addSuffix: true,
@ -192,13 +202,13 @@ export class GfAdminUsersComponent implements OnInit {
return ''; return '';
} }
public onChangePage(page: PageEvent) { protected onChangePage(page: PageEvent) {
this.fetchUsers({ this.fetchUsers({
pageIndex: page.pageIndex pageIndex: page.pageIndex
}); });
} }
public onDeleteUser(aId: string) { protected onDeleteUser(aId: string) {
this.notificationService.confirm({ this.notificationService.confirm({
confirmFn: () => { confirmFn: () => {
this.dataService this.dataService
@ -216,7 +226,7 @@ export class GfAdminUsersComponent implements OnInit {
}); });
} }
public onGenerateAccessToken(aUserId: string) { protected onGenerateAccessToken(aUserId: string) {
this.notificationService.confirm({ this.notificationService.confirm({
confirmFn: () => { confirmFn: () => {
this.dataService this.dataService
@ -241,7 +251,7 @@ export class GfAdminUsersComponent implements OnInit {
}); });
} }
public onImpersonateUser(aId: string) { protected onImpersonateUser(aId: string) {
if (aId) { if (aId) {
this.impersonationStorageService.setId(aId); this.impersonationStorageService.setId(aId);
} else { } else {
@ -251,7 +261,7 @@ export class GfAdminUsersComponent implements OnInit {
window.location.reload(); window.location.reload();
} }
public onOpenUserDetailDialog(userId: string) { protected onOpenUserDetailDialog(userId: string) {
this.router.navigate( this.router.navigate(
internalRoutes.adminControl.subRoutes.users.routerLink.concat(userId) internalRoutes.adminControl.subRoutes.users.routerLink.concat(userId)
); );
@ -260,8 +270,8 @@ export class GfAdminUsersComponent implements OnInit {
private fetchUsers({ pageIndex }: { pageIndex: number } = { pageIndex: 0 }) { private fetchUsers({ pageIndex }: { pageIndex: number } = { pageIndex: 0 }) {
this.isLoading = true; this.isLoading = true;
if (pageIndex === 0 && this.paginator) { if (pageIndex === 0 && this.paginator()) {
this.paginator.pageIndex = 0; this.paginator().pageIndex = 0;
} }
this.adminService this.adminService
@ -283,18 +293,19 @@ export class GfAdminUsersComponent implements OnInit {
private openUserDetailDialog(aUserId: string) { private openUserDetailDialog(aUserId: string) {
const dialogRef = this.dialog.open< const dialogRef = this.dialog.open<
GfUserDetailDialogComponent, GfUserDetailDialogComponent,
UserDetailDialogParams UserDetailDialogParams,
UserDetailDialogResult
>(GfUserDetailDialogComponent, { >(GfUserDetailDialogComponent, {
autoFocus: false, autoFocus: false,
data: { data: {
currentUserId: this.user?.id, currentUserId: this.user?.id,
deviceType: this.deviceType, deviceType: this.deviceType(),
hasPermissionForSubscription: this.hasPermissionForSubscription, hasPermissionForSubscription: this.hasPermissionForSubscription,
locale: this.user?.settings?.locale, locale: this.user?.settings?.locale ?? locale,
userId: aUserId userId: aUserId
}, } satisfies UserDetailDialogParams,
height: this.deviceType === 'mobile' ? '98vh' : '60vh', height: this.deviceType() === 'mobile' ? '98vh' : '60vh',
width: this.deviceType === 'mobile' ? '100vw' : '50rem' width: this.deviceType() === 'mobile' ? '100vw' : '50rem'
}); });
dialogRef dialogRef

5
apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts

@ -5,3 +5,8 @@ export interface UserDetailDialogParams {
locale: string; locale: string;
userId: string; userId: string;
} }
export interface UserDetailDialogResult {
action: 'delete';
userId: string;
}

10
apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts

@ -22,7 +22,10 @@ import { ellipsisVertical } from 'ionicons/icons';
import { EMPTY } from 'rxjs'; import { EMPTY } from 'rxjs';
import { catchError } from 'rxjs/operators'; import { catchError } from 'rxjs/operators';
import { UserDetailDialogParams } from './interfaces/interfaces'; import {
UserDetailDialogParams,
UserDetailDialogResult
} from './interfaces/interfaces';
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
@ -47,7 +50,10 @@ export class GfUserDetailDialogComponent implements OnInit {
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
@Inject(MAT_DIALOG_DATA) public data: UserDetailDialogParams, @Inject(MAT_DIALOG_DATA) public data: UserDetailDialogParams,
private destroyRef: DestroyRef, private destroyRef: DestroyRef,
public dialogRef: MatDialogRef<GfUserDetailDialogComponent> public dialogRef: MatDialogRef<
GfUserDetailDialogComponent,
UserDetailDialogResult
>
) { ) {
addIcons({ addIcons({
ellipsisVertical ellipsisVertical

Loading…
Cancel
Save