Browse Source

refactor and improve UI component

pull/5819/head
HydrallHarsh 2 weeks ago
parent
commit
f6b385cfa1
  1. 5
      CHANGELOG.md
  2. 34
      apps/client/src/app/components/admin-users/admin-users.component.ts
  3. 3
      apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts
  4. 30
      apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts
  5. 18
      apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html

5
CHANGELOG.md

@ -5,12 +5,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
- User detail dialog component to view user information in admin users management
## 2.210.0 - 2025-10-22 ## 2.210.0 - 2025-10-22
### Added ### Added
- Added support for data gathering by date range in the asset profile details dialog of the admin control panel - Added support for data gathering by date range in the asset profile details dialog of the admin control panel
- Added user detail dialog component to view user information in admin users management
### Changed ### Changed

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

@ -77,8 +77,8 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit {
public dataSource = new MatTableDataSource<AdminUsers['users'][0]>(); public dataSource = new MatTableDataSource<AdminUsers['users'][0]>();
public defaultDateFormat: string; public defaultDateFormat: string;
public displayedColumns: string[] = [];
public deviceType: string; public deviceType: string;
public displayedColumns: string[] = [];
public getEmojiFlag = getEmojiFlag; public getEmojiFlag = getEmojiFlag;
public hasPermissionForSubscription: boolean; public hasPermissionForSubscription: boolean;
public hasPermissionToImpersonateAllUsers: boolean; public hasPermissionToImpersonateAllUsers: boolean;
@ -103,6 +103,7 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit {
private tokenStorageService: TokenStorageService, private tokenStorageService: TokenStorageService,
private userService: UserService private userService: UserService
) { ) {
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
this.info = this.dataService.fetchInfo(); this.info = this.dataService.fetchInfo();
this.hasPermissionForSubscription = hasPermission( this.hasPermissionForSubscription = hasPermission(
@ -132,6 +133,14 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit {
]; ];
} }
this.route.queryParams
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((params) => {
if (params['userDetailDialog'] && params['userId']) {
this.openUserDetailDialog(params['userId']);
}
});
this.userService.stateChanged this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe((state) => { .subscribe((state) => {
@ -156,20 +165,12 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit {
personOutline, personOutline,
trashOutline trashOutline
}); });
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
this.route.queryParams
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((params) => {
if (params['userDetailDialog'] && params['userId']) {
this.openUserDetailDialog(params['userId']);
}
});
} }
public ngOnInit() { public ngOnInit() {
this.fetchUsers(); this.fetchUsers();
} }
public onOpenUserDetailDialog(userId: string) { public onOpenUserDetailDialog(userId: string) {
this.router.navigate([], { this.router.navigate([], {
queryParams: { userId, userDetailDialog: true } queryParams: { userId, userDetailDialog: true }
@ -178,14 +179,16 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit {
private openUserDetailDialog(userId: string) { private openUserDetailDialog(userId: string) {
// Find the user data from the current dataSource // Find the user data from the current dataSource
const userData = this.dataSource.data.find((user) => user.id === userId); const userData = this.dataSource.data.find(({ id }) => {
return id === userId;
});
const dialogRef = this.dialog.open(GfUserDetailDialogComponent, { const dialogRef = this.dialog.open(GfUserDetailDialogComponent, {
autoFocus: false, autoFocus: false,
data: { data: {
userData,
userId, userId,
deviceType: this.deviceType, deviceType: this.deviceType
userData: userData // Pass the user data
} as UserDetailDialogParams, } as UserDetailDialogParams,
height: this.deviceType === 'mobile' ? '80vh' : '60vh', height: this.deviceType === 'mobile' ? '80vh' : '60vh',
width: this.deviceType === 'mobile' ? '100vw' : '50rem' width: this.deviceType === 'mobile' ? '100vw' : '50rem'
@ -195,10 +198,11 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit {
.afterClosed() .afterClosed()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => { .subscribe(() => {
this.fetchUsers(); // Refresh the users list this.fetchUsers();
this.router.navigate(['.'], { relativeTo: this.route }); // Clear query params this.router.navigate(['.'], { relativeTo: this.route });
}); });
} }
public formatDistanceToNow(aDateString: string) { public formatDistanceToNow(aDateString: string) {
if (aDateString) { if (aDateString) {
const distanceString = formatDistanceToNowStrict(parseISO(aDateString), { const distanceString = formatDistanceToNowStrict(parseISO(aDateString), {

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

@ -2,6 +2,5 @@ import { AdminUsers } from '@ghostfolio/common/interfaces';
export interface UserDetailDialogParams { export interface UserDetailDialogParams {
deviceType: string; deviceType: string;
userData?: AdminUsers['users'][0]; userData: AdminUsers['users'][0];
userId: string;
} }

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

@ -6,18 +6,16 @@ import { GfValueComponent } from '@ghostfolio/ui/value';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef,
Component, Component,
CUSTOM_ELEMENTS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA,
Inject, Inject,
OnDestroy, OnDestroy,
OnInit, OnInit
ChangeDetectorRef
} from '@angular/core'; } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
import { addIcons } from 'ionicons';
import { personOutline } from 'ionicons/icons';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { UserDetailDialogParams } from './interfaces/interfaces'; import { UserDetailDialogParams } from './interfaces/interfaces';
@ -39,20 +37,15 @@ import { UserDetailDialogParams } from './interfaces/interfaces';
templateUrl: './user-detail-dialog.html' templateUrl: './user-detail-dialog.html'
}) })
export class GfUserDetailDialogComponent implements OnDestroy, OnInit { export class GfUserDetailDialogComponent implements OnDestroy, OnInit {
public isLoading = false; public title: string = 'User Information';
public registrationDate: Date;
public user: User; public user: User;
public userId: string;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
@Inject(MAT_DIALOG_DATA) public data: UserDetailDialogParams, @Inject(MAT_DIALOG_DATA) public data: UserDetailDialogParams,
public dialogRef: MatDialogRef<GfUserDetailDialogComponent> public dialogRef: MatDialogRef<GfUserDetailDialogComponent>
) { ) {}
this.userId = this.data.userId;
addIcons({ personOutline });
}
public ngOnInit() { public ngOnInit() {
this.initialize(); this.initialize();
@ -62,23 +55,12 @@ export class GfUserDetailDialogComponent implements OnDestroy, OnInit {
this.dialogRef.close(); this.dialogRef.close();
} }
private fetchUserDetails(): void { private fetchUserDetails() {
this.isLoading = true;
// Use the user data passed from the admin users list if available
if (this.data.userData) { if (this.data.userData) {
this.userId = this.data.userData.id;
this.registrationDate = this.data.userData.createdAt;
this.isLoading = false;
this.changeDetectorRef.markForCheck();
} else {
// Fallback: use the user ID and set a placeholder registration date
this.userId = this.data.userId;
this.registrationDate = new Date(); // Placeholder
this.isLoading = false;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }
} }
private initialize() { private initialize() {
this.fetchUserDetails(); this.fetchUserDetails();
} }

18
apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html

@ -1,24 +1,28 @@
<gf-dialog-header <gf-dialog-header
position="center" position="center"
[deviceType]="data.deviceType" [deviceType]="data.deviceType"
[title]="userId"
(closeButtonClicked)="onClose()" (closeButtonClicked)="onClose()"
/> />
<div class="flex-grow-1" mat-dialog-content> <div class="flex-grow-1" mat-dialog-content>
<div class="container p-0"> <div class="container p-0">
<div class="row"> <div class="col-12 d-flex justify-content-center mb-3">
<div class="col-12 mb-3"> <gf-value i18n size="medium" [value]="title"></gf-value>
<gf-value i18n size="medium">User Information</gf-value>
</div>
</div> </div>
<div class="mb-3 row"> <div class="mb-3 row">
<div class="col-6 mb-3"> <div class="col-6 mb-3">
<gf-value i18n size="small" [value]="userId">User ID</gf-value> <gf-value i18n size="medium" [value]="data.userData.id"
>User ID</gf-value
>
</div> </div>
<div class="col-6 mb-3"> <div class="col-6 mb-3">
<gf-value i18n size="small" [value]="registrationDate | date: 'medium'" <gf-value
i18n
size="medium"
[isDate]="true"
[locale]="user?.settings?.locale"
[value]="data.userData.createdAt"
>Registration Date</gf-value >Registration Date</gf-value
> >
</div> </div>

Loading…
Cancel
Save