Browse Source

Task/fetch user data on demand in user detail dialog (#5923)

* Fetch user data on demand in user detail dialog

* Update changelog
pull/5925/head^2
David Requeno 1 month ago
committed by GitHub
parent
commit
8d2fde35da
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 30
      apps/client/src/app/components/admin-users/admin-users.component.ts
  3. 4
      apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts
  4. 33
      apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts
  5. 39
      apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html
  6. 7
      apps/client/src/app/services/admin.service.ts

1
CHANGELOG.md

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Refactored the get holding functionality in the portfolio service
- Changed the user data loading in the user detail dialog of the admin control panel’s users section to fetch data on demand
- Improved the language localization for German (`de`)
- Upgraded `prisma` from version `6.18.0` to `6.19.0`

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

@ -1,4 +1,12 @@
import { UserDetailDialogParams } from '@ghostfolio/client/components/user-detail-dialog/interfaces/interfaces';
import { GfUserDetailDialogComponent } from '@ghostfolio/client/components/user-detail-dialog/user-detail-dialog.component';
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { AdminService } from '@ghostfolio/client/services/admin.service';
import { DataService } from '@ghostfolio/client/services/data.service';
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config';
import {
getDateFnsLocale,
@ -51,15 +59,6 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { ConfirmationDialogType } from '../../core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '../../core/notification/notification.service';
import { AdminService } from '../../services/admin.service';
import { DataService } from '../../services/data.service';
import { ImpersonationStorageService } from '../../services/impersonation-storage.service';
import { UserService } from '../../services/user/user.service';
import { UserDetailDialogParams } from '../user-detail-dialog/interfaces/interfaces';
import { GfUserDetailDialogComponent } from '../user-detail-dialog/user-detail-dialog.component';
@Component({
imports: [
CommonModule,
@ -283,25 +282,16 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit {
}
private openUserDetailDialog(aUserId: string) {
const userData = this.dataSource.data.find(({ id }) => {
return id === aUserId;
});
if (!userData) {
this.router.navigate(['.'], { relativeTo: this.route });
return;
}
const dialogRef = this.dialog.open<
GfUserDetailDialogComponent,
UserDetailDialogParams
>(GfUserDetailDialogComponent, {
autoFocus: false,
data: {
userData,
deviceType: this.deviceType,
hasPermissionForSubscription: this.hasPermissionForSubscription,
locale: this.user?.settings?.locale
locale: this.user?.settings?.locale,
userId: aUserId
},
height: this.deviceType === 'mobile' ? '98vh' : '60vh',
width: this.deviceType === 'mobile' ? '100vw' : '50rem'

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

@ -1,8 +1,6 @@
import { AdminUsersResponse } from '@ghostfolio/common/interfaces';
export interface UserDetailDialogParams {
deviceType: string;
hasPermissionForSubscription: boolean;
locale: string;
userData: AdminUsersResponse['users'][0];
userId: string;
}

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

@ -1,19 +1,24 @@
import { GfDialogFooterComponent } from '@ghostfolio/client/components/dialog-footer/dialog-footer.component';
import { GfDialogHeaderComponent } from '@ghostfolio/client/components/dialog-header/dialog-header.component';
import { AdminService } from '@ghostfolio/client/services/admin.service';
import { AdminUserResponse } from '@ghostfolio/common/interfaces';
import { GfValueComponent } from '@ghostfolio/ui/value';
import { CommonModule } from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
CUSTOM_ELEMENTS_SCHEMA,
Inject,
OnDestroy
OnDestroy,
OnInit
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatDialogModule } from '@angular/material/dialog';
import { Subject } from 'rxjs';
import { EMPTY, Subject } from 'rxjs';
import { catchError, takeUntil } from 'rxjs/operators';
import { UserDetailDialogParams } from './interfaces/interfaces';
@ -33,14 +38,36 @@ import { UserDetailDialogParams } from './interfaces/interfaces';
styleUrls: ['./user-detail-dialog.component.scss'],
templateUrl: './user-detail-dialog.html'
})
export class GfUserDetailDialogComponent implements OnDestroy {
export class GfUserDetailDialogComponent implements OnDestroy, OnInit {
public user: AdminUserResponse;
private unsubscribeSubject = new Subject<void>();
public constructor(
private adminService: AdminService,
private changeDetectorRef: ChangeDetectorRef,
@Inject(MAT_DIALOG_DATA) public data: UserDetailDialogParams,
public dialogRef: MatDialogRef<GfUserDetailDialogComponent>
) {}
public ngOnInit() {
this.adminService
.fetchUserById(this.data.userId)
.pipe(
takeUntil(this.unsubscribeSubject),
catchError(() => {
this.dialogRef.close();
return EMPTY;
})
)
.subscribe((user) => {
this.user = user;
this.changeDetectorRef.markForCheck();
});
}
public onClose() {
this.dialogRef.close();
}

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

@ -8,9 +8,7 @@
<div class="container p-0">
<div class="mb-3 row">
<div class="col-6 mb-3">
<gf-value i18n size="medium" [value]="data.userData.id">
User ID
</gf-value>
<gf-value i18n size="medium" [value]="user?.id">User ID</gf-value>
</div>
<div class="col-6 mb-3">
<gf-value
@ -18,24 +16,21 @@
size="medium"
[isDate]="true"
[locale]="data.locale"
[value]="data.userData.createdAt"
[value]="user?.createdAt"
>Registration Date</gf-value
>
Registration Date
</gf-value>
</div>
</div>
<div class="mb-3 row">
<div class="col-6 mb-3">
<gf-value i18n size="medium" [value]="data.userData.role">
Role
</gf-value>
<gf-value i18n size="medium" [value]="user?.role">Role</gf-value>
</div>
@if (data.hasPermissionForSubscription) {
<div class="col-6 mb-3">
<gf-value i18n size="medium" [value]="data.userData.country">
Country
</gf-value>
<gf-value i18n size="medium" [value]="user?.country"
>Country</gf-value
>
</div>
}
</div>
@ -46,20 +41,18 @@
i18n
size="medium"
[locale]="data.locale"
[value]="data.userData.accountCount"
[value]="user?.accountCount"
>Accounts</gf-value
>
Accounts
</gf-value>
</div>
<div class="col-6 mb-3">
<gf-value
i18n
size="medium"
[locale]="data.locale"
[value]="data.userData.activityCount"
[value]="user?.activityCount"
>Activities</gf-value
>
Activities
</gf-value>
</div>
</div>
@ -71,20 +64,18 @@
size="medium"
[locale]="data.locale"
[precision]="0"
[value]="data.userData.engagement"
[value]="user?.engagement"
>Engagement per Day</gf-value
>
Engagement per Day
</gf-value>
</div>
<div class="col-6 mb-3">
<gf-value
i18n
size="medium"
[locale]="data.locale"
[value]="data.userData.dailyApiRequests"
[value]="user?.dailyApiRequests"
>API Requests Today</gf-value
>
API Requests Today
</gf-value>
</div>
</div>
}

7
apps/client/src/app/services/admin.service.ts

@ -8,11 +8,12 @@ import {
} from '@ghostfolio/common/config';
import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config';
import {
AssetProfileIdentifier,
AdminData,
AdminJobs,
AdminMarketData,
AdminUserResponse,
AdminUsersResponse,
AssetProfileIdentifier,
DataProviderGhostfolioStatusResponse,
EnhancedSymbolProfile,
Filter
@ -142,6 +143,10 @@ export class AdminService {
return this.http.get<Platform[]>('/api/v1/platform');
}
public fetchUserById(id: string) {
return this.http.get<AdminUserResponse>(`/api/v1/admin/user/${id}`);
}
public fetchUsers({
skip,
take = DEFAULT_PAGE_SIZE

Loading…
Cancel
Save