Browse Source

Feature/extend user detail dialog by last request (#7979)

* Include last request in user detail dialog

* Update changelog
pull/7985/head
Thomas Kaul 1 week ago
committed by GitHub
parent
commit
02b11c6547
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 22
      apps/client/src/app/components/admin-overview/admin-overview.component.ts
  3. 25
      apps/client/src/app/components/admin-users/admin-users.component.ts
  4. 22
      apps/client/src/app/components/admin-users/admin-users.html
  5. 1
      apps/client/src/app/components/user-detail-dialog/interfaces/interfaces.ts
  6. 2
      apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts
  7. 17
      apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html
  8. 28
      apps/client/src/app/util/date.util.ts

1
CHANGELOG.md

@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Extended the emergency fund dialog in the portfolio summary to include the currency suffix - Extended the emergency fund dialog in the portfolio summary to include the currency suffix
- Extended the user detail dialog in the users section of the admin control panel to include the last request
- Migrated the client to the code generated by the new `prisma-client` generator of _Prisma_ - Migrated the client to the code generated by the new `prisma-client` generator of _Prisma_
- Migrated from `lodash` to `lodash-es` - Migrated from `lodash` to `lodash-es`

22
apps/client/src/app/components/admin-overview/admin-overview.component.ts

@ -48,12 +48,7 @@ import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
import { import { addMilliseconds, formatDistanceToNowStrict } from 'date-fns';
addMilliseconds,
differenceInSeconds,
formatDistanceToNowStrict,
parseISO
} from 'date-fns';
import { addIcons } from 'ionicons'; import { addIcons } from 'ionicons';
import { import {
closeCircleOutline, closeCircleOutline,
@ -187,21 +182,6 @@ export class GfAdminOverviewComponent implements OnInit {
this.fetchAdminData(); this.fetchAdminData();
} }
protected formatDistanceToNow(aDateString: string) {
if (aDateString) {
const distanceString = formatDistanceToNowStrict(parseISO(aDateString), {
addSuffix: true
});
return Math.abs(differenceInSeconds(parseISO(aDateString), new Date())) <
60
? 'just now'
: distanceString;
}
return '';
}
protected formatStringValue(aStringValue: StringValue) { protected formatStringValue(aStringValue: StringValue) {
return formatDistanceToNowStrict( return formatDistanceToNowStrict(
addMilliseconds(new Date(), ms(aStringValue)), addMilliseconds(new Date(), ms(aStringValue)),

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

@ -5,12 +5,12 @@ import {
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 { formatRelativeTime } from '@ghostfolio/client/util/date.util';
import { DEFAULT_LOCALE, DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; import { DEFAULT_LOCALE, DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config';
import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { ConfirmationDialogType } from '@ghostfolio/common/enums';
import { import {
canDeleteUser, canDeleteUser,
getCountryName, getCountryName,
getDateFnsLocale,
getDateFormatString, getDateFormatString,
getEmojiFlag getEmojiFlag
} from '@ghostfolio/common/helper'; } from '@ghostfolio/common/helper';
@ -49,11 +49,6 @@ import {
import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import { ActivatedRoute, Router, RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
import {
differenceInSeconds,
formatDistanceToNowStrict,
parseISO
} from 'date-fns';
import { addIcons } from 'ionicons'; import { addIcons } from 'ionicons';
import { import {
contractOutline, contractOutline,
@ -93,6 +88,7 @@ export class GfAdminUsersComponent implements OnInit {
protected defaultDateFormat: string; protected defaultDateFormat: string;
protected displayedColumns: string[] = []; protected displayedColumns: string[] = [];
protected readonly canDeleteUser = canDeleteUser; protected readonly canDeleteUser = canDeleteUser;
protected readonly formatRelativeTime = formatRelativeTime;
protected readonly getCountryName = getCountryName; protected readonly getCountryName = getCountryName;
protected readonly getEmojiFlag = getEmojiFlag; protected readonly getEmojiFlag = getEmojiFlag;
protected hasPermissionForSubscription: boolean; protected hasPermissionForSubscription: boolean;
@ -209,22 +205,6 @@ export class GfAdminUsersComponent implements OnInit {
}); });
} }
protected formatDistanceToNow(aDateString: string) {
if (aDateString) {
const distanceString = formatDistanceToNowStrict(parseISO(aDateString), {
addSuffix: true,
locale: getDateFnsLocale(this.user?.settings?.language)
});
return Math.abs(differenceInSeconds(parseISO(aDateString), new Date())) <
60
? $localize`just now`
: distanceString;
}
return '';
}
protected onChangePage(page: PageEvent) { protected onChangePage(page: PageEvent) {
this.fetchUsers({ this.fetchUsers({
pageIndex: page.pageIndex pageIndex: page.pageIndex
@ -332,6 +312,7 @@ export class GfAdminUsersComponent implements OnInit {
currentUserId: this.user?.id, currentUserId: this.user?.id,
deviceType: this.deviceType(), deviceType: this.deviceType(),
hasPermissionForSubscription: this.hasPermissionForSubscription, hasPermissionForSubscription: this.hasPermissionForSubscription,
language: this.user?.settings?.language,
locale: this.user?.settings?.locale ?? DEFAULT_LOCALE, locale: this.user?.settings?.locale ?? DEFAULT_LOCALE,
userId: aUserId userId: aUserId
} satisfies UserDetailDialogParams, } satisfies UserDetailDialogParams,

22
apps/client/src/app/components/admin-users/admin-users.html

@ -32,7 +32,10 @@
class="ml-1" class="ml-1"
i18n-title i18n-title
title="Expires {{ title="Expires {{
formatDistanceToNow(element.subscription.expiresAt) formatRelativeTime({
date: element.subscription.expiresAt,
language: user?.settings?.language
})
}} ({{ }} ({{
element.subscription.expiresAt | date: defaultDateFormat element.subscription.expiresAt | date: defaultDateFormat
}})" }})"
@ -79,7 +82,12 @@
class="mat-mdc-cell px-1 py-2 text-nowrap" class="mat-mdc-cell px-1 py-2 text-nowrap"
mat-cell mat-cell
> >
{{ formatDistanceToNow(element.createdAt) }} {{
formatRelativeTime({
date: element.createdAt,
language: user?.settings?.language
})
}}
</td> </td>
</ng-container> </ng-container>
@ -175,17 +183,21 @@
<th <th
*matHeaderCellDef *matHeaderCellDef
class="mat-mdc-header-cell px-1 py-2" class="mat-mdc-header-cell px-1 py-2"
i18n
mat-header-cell mat-header-cell
> >
Last Request <ng-container i18n>Last Request</ng-container>
</th> </th>
<td <td
*matCellDef="let element" *matCellDef="let element"
class="mat-mdc-cell px-1 py-2 text-nowrap" class="mat-mdc-cell px-1 py-2 text-nowrap"
mat-cell mat-cell
> >
{{ formatDistanceToNow(element.lastActivity) }} {{
formatRelativeTime({
date: element.lastActivity,
language: user?.settings?.language
})
}}
</td> </td>
</ng-container> </ng-container>
} }

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

@ -2,6 +2,7 @@ export interface UserDetailDialogParams {
currentUserId: string; currentUserId: string;
deviceType: string; deviceType: string;
hasPermissionForSubscription: boolean; hasPermissionForSubscription: boolean;
language?: string;
locale: string; locale: string;
userId: string; userId: string;
} }

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

@ -1,3 +1,4 @@
import { formatRelativeTime } from '@ghostfolio/client/util/date.util';
import { import {
canDeleteUser, canDeleteUser,
getCountryName, getCountryName,
@ -55,6 +56,7 @@ import {
export class GfUserDetailDialogComponent implements OnInit { export class GfUserDetailDialogComponent implements OnInit {
protected readonly baseCurrency: string; protected readonly baseCurrency: string;
protected readonly canDeleteUser = canDeleteUser; protected readonly canDeleteUser = canDeleteUser;
protected readonly formatRelativeTime = formatRelativeTime;
protected readonly getCountryName = getCountryName; protected readonly getCountryName = getCountryName;
protected isLoading = true; protected isLoading = true;

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

@ -154,6 +154,23 @@
</div> </div>
</div> </div>
<div class="mb-3 row">
<div class="col-6 mb-3">
<gf-value
i18n
size="medium"
[isLoading]="isLoading"
[value]="
formatRelativeTime({
date: user?.lastActivity,
language: data.language
}) || '-'
"
>Last Request</gf-value
>
</div>
</div>
@if (subscriptionsDataSource.data.length > 0) { @if (subscriptionsDataSource.data.length > 0) {
<div class="row"> <div class="row">
<div class="col"> <div class="col">

28
apps/client/src/app/util/date.util.ts

@ -0,0 +1,28 @@
import { getDateFnsLocale } from '@ghostfolio/common/helper';
import {
differenceInSeconds,
formatDistanceToNowStrict,
parseISO
} from 'date-fns';
export function formatRelativeTime({
date,
language
}: {
date: string;
language?: string;
}) {
if (date) {
const distanceString = formatDistanceToNowStrict(parseISO(date), {
addSuffix: true,
locale: getDateFnsLocale(language)
});
return Math.abs(differenceInSeconds(parseISO(date), new Date())) < 60
? $localize`just now`
: distanceString;
}
return '';
}
Loading…
Cancel
Save