From 9e115f2b50afbf725ec418524267adbe20b06efc Mon Sep 17 00:00:00 2001 From: Karel De Smet Date: Mon, 26 Jan 2026 22:31:12 +0100 Subject: [PATCH 1/3] Extend user detail dialog --- .../admin-users/admin-users.component.ts | 55 ++++++++++--------- .../interfaces/interfaces.ts | 1 + .../user-detail-dialog.component.ts | 24 ++++++-- .../user-detail-dialog.html | 40 ++++++++++---- 4 files changed, 79 insertions(+), 41 deletions(-) diff --git a/apps/client/src/app/components/admin-users/admin-users.component.ts b/apps/client/src/app/components/admin-users/admin-users.component.ts index 2ae3b1a57..db984392a 100644 --- a/apps/client/src/app/components/admin-users/admin-users.component.ts +++ b/apps/client/src/app/components/admin-users/admin-users.component.ts @@ -57,7 +57,7 @@ import { import { DeviceDetectorService } from 'ngx-device-detector'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { switchMap, takeUntil, tap } from 'rxjs/operators'; @Component({ imports: [ @@ -139,8 +139,25 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { ]; } - this.route.paramMap - .pipe(takeUntil(this.unsubscribeSubject)) + this.userService.stateChanged + .pipe( + takeUntil(this.unsubscribeSubject), + tap((state) => { + if (state?.user) { + this.user = state.user; + + this.defaultDateFormat = getDateFormatString( + this.user.settings.locale + ); + + this.hasPermissionToImpersonateAllUsers = hasPermission( + this.user.permissions, + permissions.impersonateAllUsers + ); + } + }), + switchMap(() => this.route.paramMap) + ) .subscribe((params) => { const userId = params.get('userId'); @@ -149,23 +166,6 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { } }); - this.userService.stateChanged - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe((state) => { - if (state?.user) { - this.user = state.user; - - this.defaultDateFormat = getDateFormatString( - this.user.settings.locale - ); - - this.hasPermissionToImpersonateAllUsers = hasPermission( - this.user.permissions, - permissions.impersonateAllUsers - ); - } - }); - addIcons({ contractOutline, ellipsisHorizontal, @@ -296,7 +296,8 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { deviceType: this.deviceType, hasPermissionForSubscription: this.hasPermissionForSubscription, locale: this.user?.settings?.locale, - userId: aUserId + userId: aUserId, + currentUserId: this.user?.id }, height: this.deviceType === 'mobile' ? '98vh' : '60vh', width: this.deviceType === 'mobile' ? '100vw' : '50rem' @@ -305,10 +306,14 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { dialogRef .afterClosed() .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(() => { - this.router.navigate( - internalRoutes.adminControl.subRoutes.users.routerLink - ); + .subscribe((data) => { + if (data?.userId) { + this.onDeleteUser(data.userId); + } else { + this.router.navigate( + internalRoutes.adminControl.subRoutes.users.routerLink + ); + } }); } } diff --git a/apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts index b922e7a54..c2b3e2ecd 100644 --- a/apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts +++ b/apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts @@ -3,4 +3,5 @@ export interface UserDetailDialogParams { hasPermissionForSubscription: boolean; locale: string; userId: string; + currentUserId: string; } diff --git a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts index cdf977058..60bec57fe 100644 --- a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts +++ b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts @@ -1,6 +1,4 @@ import { AdminUserResponse } from '@ghostfolio/common/interfaces'; -import { GfDialogFooterComponent } from '@ghostfolio/ui/dialog-footer'; -import { GfDialogHeaderComponent } from '@ghostfolio/ui/dialog-header'; import { AdminService } from '@ghostfolio/ui/services'; import { GfValueComponent } from '@ghostfolio/ui/value'; @@ -16,6 +14,10 @@ import { import { MatButtonModule } from '@angular/material/button'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog'; +import { MatMenuModule } from '@angular/material/menu'; +import { IonIcon } from '@ionic/angular/standalone'; +import { addIcons } from 'ionicons'; +import { ellipsisVertical } from 'ionicons/icons'; import { EMPTY, Subject } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; @@ -25,11 +27,11 @@ import { UserDetailDialogParams } from './interfaces/interfaces'; changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'd-flex flex-column h-100' }, imports: [ - GfDialogFooterComponent, - GfDialogHeaderComponent, GfValueComponent, + IonIcon, MatButtonModule, - MatDialogModule + MatDialogModule, + MatMenuModule ], schemas: [CUSTOM_ELEMENTS_SCHEMA], selector: 'gf-user-detail-dialog', @@ -46,7 +48,11 @@ export class GfUserDetailDialogComponent implements OnDestroy, OnInit { private changeDetectorRef: ChangeDetectorRef, @Inject(MAT_DIALOG_DATA) public data: UserDetailDialogParams, public dialogRef: MatDialogRef - ) {} + ) { + addIcons({ + ellipsisVertical + }); + } public ngOnInit() { this.adminService @@ -66,6 +72,12 @@ export class GfUserDetailDialogComponent implements OnDestroy, OnInit { }); } + public deleteUser() { + this.dialogRef.close({ + userId: this.data.userId + }); + } + public onClose() { this.dialogRef.close(); } diff --git a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html index 60f6a2585..570dcf4d6 100644 --- a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html +++ b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html @@ -1,9 +1,28 @@ - - +
+ + + + +
@@ -103,7 +122,8 @@
- +
+ +
From 0f06e0adbf59835bc866f61ee169eec7b52b1554 Mon Sep 17 00:00:00 2001 From: Karel De Smet Date: Sun, 1 Feb 2026 21:58:28 +0100 Subject: [PATCH 2/3] Fix PR comments --- CHANGELOG.md | 1 + .../app/components/admin-users/admin-users.component.ts | 8 ++++---- .../user-detail-dialog/interfaces/interfaces.ts | 2 +- .../user-detail-dialog/user-detail-dialog.component.ts | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7805ad11d..5ed6ae27b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgraded `angular` from version `21.0.6` to `21.1.1` - Upgraded `Nx` from version `22.3.3` to `22.4.1` - Upgraded `prettier` from version `3.8.0` to `3.8.1` +- Extended the user detail dialog with an actions menu ## 2.233.0 - 2026-01-23 diff --git a/apps/client/src/app/components/admin-users/admin-users.component.ts b/apps/client/src/app/components/admin-users/admin-users.component.ts index db984392a..f5333fabf 100644 --- a/apps/client/src/app/components/admin-users/admin-users.component.ts +++ b/apps/client/src/app/components/admin-users/admin-users.component.ts @@ -208,7 +208,7 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { .deleteUser(aId) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe(() => { - this.fetchUsers(); + this.router.navigate(['..'], { relativeTo: this.route }); }); }, confirmType: ConfirmationDialogType.Warn, @@ -293,11 +293,11 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { >(GfUserDetailDialogComponent, { autoFocus: false, data: { + currentUserId: this.user?.id, deviceType: this.deviceType, hasPermissionForSubscription: this.hasPermissionForSubscription, locale: this.user?.settings?.locale, - userId: aUserId, - currentUserId: this.user?.id + userId: aUserId }, height: this.deviceType === 'mobile' ? '98vh' : '60vh', width: this.deviceType === 'mobile' ? '100vw' : '50rem' @@ -307,7 +307,7 @@ export class GfAdminUsersComponent implements OnDestroy, OnInit { .afterClosed() .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((data) => { - if (data?.userId) { + if (data?.action === 'delete' && data?.userId) { this.onDeleteUser(data.userId); } else { this.router.navigate( diff --git a/apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts index c2b3e2ecd..ed46e8a02 100644 --- a/apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts +++ b/apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts @@ -1,7 +1,7 @@ export interface UserDetailDialogParams { + currentUserId: string; deviceType: string; hasPermissionForSubscription: boolean; locale: string; userId: string; - currentUserId: string; } diff --git a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts index 60bec57fe..cf25a7b62 100644 --- a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts +++ b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts @@ -74,7 +74,8 @@ export class GfUserDetailDialogComponent implements OnDestroy, OnInit { public deleteUser() { this.dialogRef.close({ - userId: this.data.userId + userId: this.data.userId, + action: 'delete' }); } From ae9ec2fcf4c4866ae4254385580a2401d0c388d8 Mon Sep 17 00:00:00 2001 From: Karel De Smet Date: Mon, 2 Feb 2026 09:31:16 +0100 Subject: [PATCH 3/3] Order keys alphabetically --- .../user-detail-dialog/user-detail-dialog.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts index cf25a7b62..6f7f4ead6 100644 --- a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts +++ b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts @@ -74,8 +74,8 @@ export class GfUserDetailDialogComponent implements OnDestroy, OnInit { public deleteUser() { this.dialogRef.close({ - userId: this.data.userId, - action: 'delete' + action: 'delete', + userId: this.data.userId }); }