Browse Source

Merge branch 'feature/move-accounts-table-to-ui' of https://github.com/DavidReque/ghostfolio into feature/move-accounts-table-to-ui

pull/5278/head
David Requeno 3 weeks ago
parent
commit
0e267c2bd3
  1. 84
      apps/client/src/app/components/access-table/access-table.component.html
  2. 11
      apps/client/src/app/components/access-table/access-table.component.scss
  3. 111
      apps/client/src/app/components/access-table/access-table.component.ts
  4. 24
      libs/ui/src/lib/accounts-table/accounts-table.component.stories.ts

84
apps/client/src/app/components/access-table/access-table.component.html

@ -1,84 +0,0 @@
<div class="overflow-x-auto">
<table class="gf-table w-100" mat-table [dataSource]="dataSource">
<ng-container matColumnDef="alias">
<th *matHeaderCellDef class="px-1" i18n mat-header-cell>Alias</th>
<td *matCellDef="let element" class="px-1" mat-cell>
{{ element.alias }}
</td>
</ng-container>
<ng-container matColumnDef="grantee">
<th *matHeaderCellDef class="px-1" i18n mat-header-cell>Grantee</th>
<td *matCellDef="let element" class="px-1" mat-cell>
{{ element.grantee }}
</td>
</ng-container>
<ng-container matColumnDef="type">
<th *matHeaderCellDef class="px-1" i18n mat-header-cell>Permission</th>
<td *matCellDef="let element" class="px-1 text-nowrap" mat-cell>
<div class="align-items-center d-flex">
@if (element.permissions.includes('READ')) {
<ion-icon class="mr-1" name="lock-open-outline" />
<ng-container i18n>View</ng-container>
} @else if (element.permissions.includes('READ_RESTRICTED')) {
<ion-icon class="mr-1" name="lock-closed-outline" />
<ng-container i18n>Restricted view</ng-container>
}
</div>
</td>
</ng-container>
<ng-container matColumnDef="details">
<th *matHeaderCellDef class="px-1" i18n mat-header-cell>Details</th>
<td *matCellDef="let element" class="px-1 text-nowrap" mat-cell>
@if (element.type === 'PUBLIC') {
<div class="align-items-center d-flex">
<ion-icon class="mr-1" name="link-outline" />
<a target="_blank" [href]="getPublicUrl(element.id)">{{
getPublicUrl(element.id)
}}</a>
</div>
@if (user?.settings?.isExperimentalFeatures) {
<div>
<code
>GET {{ baseUrl }}/api/v1/public/{{
element.id
}}/portfolio</code
>
</div>
}
}
</td>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
<th *matHeaderCellDef class="px-1 text-center" mat-header-cell></th>
<td *matCellDef="let element" class="px-1 text-center" mat-cell>
<button
class="mx-1 no-min-width px-2"
mat-button
[matMenuTriggerFor]="transactionMenu"
(click)="$event.stopPropagation()"
>
<ion-icon name="ellipsis-horizontal" />
</button>
<mat-menu #transactionMenu="matMenu" xPosition="before">
@if (element.type === 'PUBLIC') {
<button mat-menu-item (click)="onCopyUrlToClipboard(element.id)">
<ng-container i18n>Copy link to clipboard</ng-container>
</button>
<hr class="my-0" />
}
<button mat-menu-item (click)="onDeleteAccess(element.id)">
<ng-container i18n>Revoke</ng-container>
</button>
</mat-menu>
</td>
</ng-container>
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
<tr *matRowDef="let row; columns: displayedColumns" mat-row></tr>
</table>
</div>

11
apps/client/src/app/components/access-table/access-table.component.scss

@ -1,11 +0,0 @@
:host {
display: block;
a {
color: rgba(var(--palette-primary-500), 1);
&:hover {
color: rgba(var(--palette-primary-300), 1);
}
}
}

111
apps/client/src/app/components/access-table/access-table.component.ts

@ -1,111 +0,0 @@
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { Access, User } from '@ghostfolio/common/interfaces';
import { publicRoutes } from '@ghostfolio/common/routes/routes';
import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard';
import { CommonModule } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
CUSTOM_ELEMENTS_SCHEMA,
EventEmitter,
Input,
OnChanges,
Output
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatMenuModule } from '@angular/material/menu';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons';
import {
ellipsisHorizontal,
linkOutline,
lockClosedOutline,
lockOpenOutline
} from 'ionicons/icons';
import ms from 'ms';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
ClipboardModule,
CommonModule,
IonIcon,
MatButtonModule,
MatMenuModule,
MatTableModule,
RouterModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
selector: 'gf-access-table',
templateUrl: './access-table.component.html',
styleUrls: ['./access-table.component.scss']
})
export class GfAccessTableComponent implements OnChanges {
@Input() accesses: Access[];
@Input() showActions: boolean;
@Input() user: User;
@Output() accessDeleted = new EventEmitter<string>();
public baseUrl = window.location.origin;
public dataSource: MatTableDataSource<Access>;
public displayedColumns = [];
public constructor(
private clipboard: Clipboard,
private notificationService: NotificationService,
private snackBar: MatSnackBar
) {
addIcons({
ellipsisHorizontal,
linkOutline,
lockClosedOutline,
lockOpenOutline
});
}
public ngOnChanges() {
this.displayedColumns = ['alias', 'grantee', 'type', 'details'];
if (this.showActions) {
this.displayedColumns.push('actions');
}
if (this.accesses) {
this.dataSource = new MatTableDataSource(this.accesses);
}
}
public getPublicUrl(aId: string): string {
const languageCode = this.user.settings.language;
return `${this.baseUrl}/${languageCode}/${publicRoutes.public.path}/${aId}`;
}
public onCopyUrlToClipboard(aId: string): void {
this.clipboard.copy(this.getPublicUrl(aId));
this.snackBar.open(
'✅ ' + $localize`Link has been copied to the clipboard`,
undefined,
{
duration: ms('3 seconds')
}
);
}
public onDeleteAccess(aId: string) {
this.notificationService.confirm({
confirmFn: () => {
this.accessDeleted.emit(aId);
},
confirmType: ConfirmationDialogType.Warn,
title: $localize`Do you really want to revoke this granted access?`
});
}
}

24
libs/ui/src/lib/accounts-table/accounts-table.component.stories.ts

@ -28,7 +28,11 @@ const mockAccounts = [
platform: { platform: {
name: 'Bank of America', name: 'Bank of America',
url: 'https://www.bankofamerica.com' url: 'https://www.bankofamerica.com'
} },
createdAt: new Date(),
updatedAt: new Date(),
platformId: 'bofa',
userId: 'user1'
}, },
{ {
id: '2', id: '2',
@ -44,7 +48,11 @@ const mockAccounts = [
platform: { platform: {
name: 'Interactive Brokers', name: 'Interactive Brokers',
url: 'https://www.interactivebrokers.com' url: 'https://www.interactivebrokers.com'
} },
createdAt: new Date(),
updatedAt: new Date(),
platformId: 'ibkr',
userId: 'user1'
}, },
{ {
id: '3', id: '3',
@ -60,7 +68,11 @@ const mockAccounts = [
platform: { platform: {
name: 'Deutsche Bank', name: 'Deutsche Bank',
url: 'https://www.deutsche-bank.de' url: 'https://www.deutsche-bank.de'
} },
createdAt: new Date(),
updatedAt: new Date(),
platformId: 'db',
userId: 'user1'
}, },
{ {
id: '4', id: '4',
@ -76,7 +88,11 @@ const mockAccounts = [
platform: { platform: {
name: 'Local Credit Union', name: 'Local Credit Union',
url: null url: null
} },
createdAt: new Date(),
updatedAt: new Date(),
platformId: 'lcu',
userId: 'user1'
} }
]; ];

Loading…
Cancel
Save