mirror of https://github.com/ghostfolio/ghostfolio
committed by
GitHub
298 changed files with 35561 additions and 117107 deletions
@ -1,4 +1,5 @@ |
|||
/.nx/cache |
|||
/.nx/workspace-data |
|||
/apps/client/src/polyfills.ts |
|||
/dist |
|||
/test/import |
|||
|
@ -0,0 +1,29 @@ |
|||
import { AdminService } from '@ghostfolio/api/app/admin/admin.service'; |
|||
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor'; |
|||
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; |
|||
import type { AdminMarketDataDetails } from '@ghostfolio/common/interfaces'; |
|||
|
|||
import { Controller, Get, Param, UseInterceptors } from '@nestjs/common'; |
|||
import { DataSource } from '@prisma/client'; |
|||
import { pick } from 'lodash'; |
|||
|
|||
@Controller('asset') |
|||
export class AssetController { |
|||
public constructor(private readonly adminService: AdminService) {} |
|||
|
|||
@Get(':dataSource/:symbol') |
|||
@UseInterceptors(TransformDataSourceInRequestInterceptor) |
|||
@UseInterceptors(TransformDataSourceInResponseInterceptor) |
|||
public async getAsset( |
|||
@Param('dataSource') dataSource: DataSource, |
|||
@Param('symbol') symbol: string |
|||
): Promise<AdminMarketDataDetails> { |
|||
const { assetProfile, marketData } = |
|||
await this.adminService.getMarketDataBySymbol({ dataSource, symbol }); |
|||
|
|||
return { |
|||
marketData, |
|||
assetProfile: pick(assetProfile, ['dataSource', 'name', 'symbol']) |
|||
}; |
|||
} |
|||
} |
@ -0,0 +1,17 @@ |
|||
import { AdminModule } from '@ghostfolio/api/app/admin/admin.module'; |
|||
import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; |
|||
import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; |
|||
|
|||
import { Module } from '@nestjs/common'; |
|||
|
|||
import { AssetController } from './asset.controller'; |
|||
|
|||
@Module({ |
|||
controllers: [AssetController], |
|||
imports: [ |
|||
AdminModule, |
|||
TransformDataSourceInRequestModule, |
|||
TransformDataSourceInResponseModule |
|||
] |
|||
}) |
|||
export class AssetModule {} |
@ -0,0 +1,6 @@ |
|||
import { IsString } from 'class-validator'; |
|||
|
|||
export class DeleteOwnUserDto { |
|||
@IsString() |
|||
accessToken: string; |
|||
} |
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,4 @@ |
|||
import { Module } from '@nestjs/common'; |
|||
|
|||
@Module({}) |
|||
export class RedactValuesInResponseModule {} |
@ -0,0 +1,11 @@ |
|||
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; |
|||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; |
|||
|
|||
import { Module } from '@nestjs/common'; |
|||
|
|||
@Module({ |
|||
exports: [ConfigurationService], |
|||
imports: [ConfigurationModule], |
|||
providers: [ConfigurationService] |
|||
}) |
|||
export class TransformDataSourceInRequestModule {} |
@ -0,0 +1,11 @@ |
|||
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; |
|||
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; |
|||
|
|||
import { Module } from '@nestjs/common'; |
|||
|
|||
@Module({ |
|||
exports: [ConfigurationService], |
|||
imports: [ConfigurationModule], |
|||
providers: [ConfigurationService] |
|||
}) |
|||
export class TransformDataSourceInResponseModule {} |
@ -0,0 +1,44 @@ |
|||
import { DERIVED_CURRENCIES } from '@ghostfolio/common/config'; |
|||
|
|||
import { |
|||
registerDecorator, |
|||
ValidationOptions, |
|||
ValidatorConstraint, |
|||
ValidatorConstraintInterface, |
|||
ValidationArguments |
|||
} from 'class-validator'; |
|||
import { isISO4217CurrencyCode } from 'class-validator'; |
|||
|
|||
export function IsCurrencyCode(validationOptions?: ValidationOptions) { |
|||
return function (object: Object, propertyName: string) { |
|||
registerDecorator({ |
|||
propertyName, |
|||
constraints: [], |
|||
options: validationOptions, |
|||
target: object.constructor, |
|||
validator: IsExtendedCurrencyConstraint |
|||
}); |
|||
}; |
|||
} |
|||
|
|||
@ValidatorConstraint({ async: false }) |
|||
export class IsExtendedCurrencyConstraint |
|||
implements ValidatorConstraintInterface |
|||
{ |
|||
public defaultMessage(args: ValidationArguments) { |
|||
return '$value must be a valid ISO4217 currency code'; |
|||
} |
|||
|
|||
public validate(currency: any) { |
|||
// Return true if currency is a standard ISO 4217 code or a derived currency
|
|||
return ( |
|||
isISO4217CurrencyCode(currency) || |
|||
[ |
|||
...DERIVED_CURRENCIES.map((derivedCurrency) => { |
|||
return derivedCurrency.currency; |
|||
}), |
|||
'USX' |
|||
].includes(currency) |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
-----BEGIN CERTIFICATE----- |
|||
MIIC5TCCAc2gAwIBAgIJAJAMHOFnJ6oyMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV |
|||
BAMMCWxvY2FsaG9zdDAeFw0yNDAyMjcxNTQ2MzFaFw0yNDAzMjgxNTQ2MzFaMBQx |
|||
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC |
|||
ggEBAJ0hRjViikEKVIyukXR4CfuYVvFEFzB6AwAQ9Jrz2MseqpLacLTXFFAS54mp |
|||
iDuqPBzs9ta40mlSrqSBuAWKikpW5kTNnmqUnDZ6iSJezbYWx9YyULGqqb1q3C4/ |
|||
5pH9m6NHJ+2uaUNKlDxYNKbntqs3drQEdxH9yv672Z53nvogTcf9jz6zjutEQGSV |
|||
HgVkCTTQmzf3+3st+VJ5D8JeYFR+tpZ6yleqgXFaTMtPZRfKLvTkQ+KeyCJLnsUJ |
|||
BQvdCKI0PGsG6d6ygXFmSePolD9KW3VTKKDPCsndID89vAnRWDj9UhzvycxmKpcF |
|||
GrUPH5+Pis1PM1R7OnAvnFygnyECAwEAAaM6MDgwFAYDVR0RBA0wC4IJbG9jYWxo |
|||
b3N0MAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0B |
|||
AQsFAAOCAQEAOdzrY3RTAPnBVAd3/GKIEkielYyOgKPnvd+RcOB+je8cXZY+vaxX |
|||
uEFP0526G00+kRZ2Tx9t0SmjoSpwg3lHUPMko0dIxgRlqISDAohdrEptHpcVujsD |
|||
ak88LLnAurr60JNjWX2wbEoJ18KLtqGSnATdmCgKwDPIN5a7+ssp44BGyzH6VYCg |
|||
wV3VjJl0pp4C5M0Jfu0p1FrQjzIVhgqR7JFYmvqIogWrGwYAQK/3XRXq8t48J5X3 |
|||
IsfWiLAA2ZdCoWAnZ6PAGBOoGivtkJm967pHjd/28qYY6mQo4sN2ksEOjx6/YslF |
|||
2mOJdLV/DzqoggUsTpPEG0dRhzQLTGHKDQ== |
|||
-----END CERTIFICATE----- |
@ -0,0 +1,28 @@ |
|||
-----BEGIN PRIVATE KEY----- |
|||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCdIUY1YopBClSM |
|||
rpF0eAn7mFbxRBcwegMAEPSa89jLHqqS2nC01xRQEueJqYg7qjwc7PbWuNJpUq6k |
|||
gbgFiopKVuZEzZ5qlJw2eokiXs22FsfWMlCxqqm9atwuP+aR/ZujRyftrmlDSpQ8 |
|||
WDSm57arN3a0BHcR/cr+u9med576IE3H/Y8+s47rREBklR4FZAk00Js39/t7LflS |
|||
eQ/CXmBUfraWespXqoFxWkzLT2UXyi705EPinsgiS57FCQUL3QiiNDxrBunesoFx |
|||
Zknj6JQ/Slt1UyigzwrJ3SA/PbwJ0Vg4/VIc78nMZiqXBRq1Dx+fj4rNTzNUezpw |
|||
L5xcoJ8hAgMBAAECggEAPU5YOEgELTA8oM8TjV+wdWuQsH2ilpVkSkhTR4nQkh+a |
|||
6cU0qDoqgLt/fySYNL9MyPRjso9V+SX7YdAC3paZMjwJh9q57lehQ1g33SMkG+Fz |
|||
gs0K0ucFZxQkaB8idN+ANAp1N7UO+ORGRe0cTeqmSNNRCxea5XgiFZVxaPS/IFOR |
|||
vVdXS1DulgwHh4H0ljKmkj7jc9zPBSc9ccW5Ml2q4a26Atu4IC/Mlp/DF4GNELbD |
|||
ebi9ZOZG33ip2bdhj3WX7NW9GJaaViKtVUpcpR6u+6BfvTXQ6xrqdoxXk9fnPzzf |
|||
sSoLPTt8yO4RavP1zQU22PhgIcWudhCiy/2Nv+uLqQKBgQDMPh1/xwdFl8yES8dy |
|||
f0xJyCDWPiB+xzGwcOAC90FrNZaqG0WWs3VHE4cilaWjCflvdR6aAEDEY68sZJhl |
|||
h+ChT/g61QLMOI+VIDQ1kJXKYgfS/B+BE1PZ0EkuymKFOvbNO8agHodB8CqnZaQh |
|||
bLuZaDnZ0JLK4im3KPt70+aKYwKBgQDE8s6xl0SLu+yJLo3VklCRD67Z8/jlvx2t |
|||
h3DF6NG8pB3QmvKdJWFKuLAWLGflJwbUW9Pt3hXkc0649KQrtiIYC0ZMh9KMaVCk |
|||
WmjS/n2eIUQZ7ZUlwFesi4p4iGynVBavIY8TJ6Y+K3TvsJgXP3IZ96r689PQJo8E |
|||
KbSeyYzFqwKBgGQTS4EAlJ+U8bEhMGj51veQCAbyChoUoFRD+n95h6RwbZKMKlzd |
|||
MenRt7VKfg6VJJNoX8Y1uYaBEaQ+5i1Zlsdz1718AhLu4+u+C9bzMXIo9ox63TTx |
|||
s3RWioVSxVNiwOtvDrQGQWAdvcioFPQLwyA34aDIgiTHDImimxbhjWThAoGAWOqW |
|||
Tq9QjxWk0Lpn5ohMP3GpK1VuhasnJvUDARb/uf8ORuPtrOz3Y9jGBvy9W0OnXbCn |
|||
mbiugZldbTtl8yYjdl+AuYSIlkPl2I3IzZl/9Shnqp0MvSJ9crT9KzXMeC8Knr6z |
|||
7Z30/BR6ksxTngtS5E5grzPt6Qe/gc2ich3kpEkCgYBfBHUhVIKVrDW/8a7U2679 |
|||
Wj4i9us/M3iPMxcTv7/ZAg08TEvNBQYtvVQLu0NfDKXx8iGKJJ6evIYgNXVm2sPq |
|||
VzSgwGCALi1X0443amZU+mnX+/9RnBqyM+PJU8570mM83jqKlY/BEsi0aqxTioFG |
|||
an3xbjjN+Rq9iKLzmPxIMg== |
|||
-----END PRIVATE KEY----- |
@ -1,67 +1,71 @@ |
|||
<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> |
|||
<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="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="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> |
|||
<div *ngIf="element.type === 'PUBLIC'" class="align-items-center d-flex"> |
|||
<ion-icon class="mr-1" name="link-outline" /> |
|||
<a |
|||
href="{{ baseUrl }}/{{ defaultLanguageCode }}/p/{{ element.id }}" |
|||
target="_blank" |
|||
>{{ baseUrl }}/{{ defaultLanguageCode }}/p/{{ element.id }}</a |
|||
> |
|||
</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 |
|||
href="{{ baseUrl }}/{{ defaultLanguageCode }}/p/{{ element.id }}" |
|||
target="_blank" |
|||
>{{ baseUrl }}/{{ defaultLanguageCode }}/p/{{ element.id }}</a |
|||
> |
|||
</div> |
|||
} |
|||
</td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="actions" stickyEnd> |
|||
<th *matHeaderCellDef class="px-1 text-center" mat-header-cell></th> |
|||
<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"> |
|||
<button mat-menu-item (click)="onDeleteAccess(element.id)"> |
|||
<ng-container i18n>Revoke</ng-container> |
|||
<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> |
|||
</td> |
|||
</ng-container> |
|||
<mat-menu #transactionMenu="matMenu" xPosition="before"> |
|||
<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> |
|||
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr> |
|||
<tr *matRowDef="let row; columns: displayedColumns" mat-row></tr> |
|||
</table> |
|||
</div> |
|||
|
@ -1,296 +1,322 @@ |
|||
<div *ngIf="showActions" class="d-flex justify-content-end"> |
|||
<button |
|||
class="align-items-center d-flex" |
|||
mat-stroked-button |
|||
[disabled]="dataSource?.data.length < 2" |
|||
(click)="onTransferBalance()" |
|||
> |
|||
<ion-icon class="mr-2" name="arrow-redo-outline" /> |
|||
<ng-container i18n>Transfer Cash Balance</ng-container>... |
|||
</button> |
|||
</div> |
|||
|
|||
<table class="gf-table w-100" mat-table matSort [dataSource]="dataSource"> |
|||
<ng-container matColumnDef="status"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-header-cell |
|||
></th> |
|||
<td *matCellDef="let element" class="d-none d-lg-table-cell px-1" mat-cell> |
|||
<div class="d-flex justify-content-center"> |
|||
<ion-icon *ngIf="element.isExcluded" name="eye-off-outline" /> |
|||
</div> |
|||
</td> |
|||
<td |
|||
*matFooterCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-footer-cell |
|||
></td> |
|||
</ng-container> |
|||
@if (showActions) { |
|||
<div class="d-flex justify-content-end"> |
|||
<button |
|||
class="align-items-center d-flex" |
|||
mat-stroked-button |
|||
[disabled]="dataSource?.data.length < 2" |
|||
(click)="onTransferBalance()" |
|||
> |
|||
<ion-icon class="mr-2" name="arrow-redo-outline" /> |
|||
<ng-container i18n>Transfer Cash Balance</ng-container>... |
|||
</button> |
|||
</div> |
|||
} |
|||
|
|||
<ng-container matColumnDef="account"> |
|||
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header="name"> |
|||
<ng-container i18n>Name</ng-container> |
|||
</th> |
|||
<td *matCellDef="let element" class="px-1" mat-cell> |
|||
<gf-asset-profile-icon |
|||
*ngIf="element.Platform?.url" |
|||
class="d-inline d-sm-none mr-1" |
|||
[tooltip]="element.Platform?.name" |
|||
[url]="element.Platform?.url" |
|||
/> |
|||
<span>{{ element.name }}</span> |
|||
</td> |
|||
<td *matFooterCellDef class="px-1" i18n mat-footer-cell>Total</td> |
|||
</ng-container> |
|||
<div class="overflow-x-auto"> |
|||
<table class="gf-table w-100" mat-table matSort [dataSource]="dataSource"> |
|||
<ng-container matColumnDef="status"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-header-cell |
|||
></th> |
|||
<td |
|||
*matCellDef="let element" |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-cell |
|||
> |
|||
<div class="d-flex justify-content-center"> |
|||
@if (element.isExcluded) { |
|||
<ion-icon name="eye-off-outline" /> |
|||
} |
|||
</div> |
|||
</td> |
|||
<td |
|||
*matFooterCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-footer-cell |
|||
></td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="currency"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-header-cell |
|||
mat-sort-header |
|||
> |
|||
<ng-container i18n>Currency</ng-container> |
|||
</th> |
|||
<td *matCellDef="let element" class="d-none d-lg-table-cell px-1" mat-cell> |
|||
{{ element.currency }} |
|||
</td> |
|||
<td *matFooterCellDef class="d-none d-lg-table-cell px-1" mat-footer-cell> |
|||
{{ baseCurrency }} |
|||
</td> |
|||
</ng-container> |
|||
<ng-container matColumnDef="account"> |
|||
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header="name"> |
|||
<ng-container i18n>Name</ng-container> |
|||
</th> |
|||
<td *matCellDef="let element" class="px-1" mat-cell> |
|||
@if (element.Platform?.url) { |
|||
<gf-asset-profile-icon |
|||
class="d-inline d-sm-none mr-1" |
|||
[tooltip]="element.Platform?.name" |
|||
[url]="element.Platform?.url" |
|||
/> |
|||
} |
|||
<span>{{ element.name }}</span> |
|||
</td> |
|||
<td *matFooterCellDef class="px-1" i18n mat-footer-cell>Total</td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="platform"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-header-cell |
|||
mat-sort-header="Platform.name" |
|||
> |
|||
<ng-container i18n>Platform</ng-container> |
|||
</th> |
|||
<td *matCellDef="let element" class="d-none d-lg-table-cell px-1" mat-cell> |
|||
<div class="d-flex"> |
|||
<gf-asset-profile-icon |
|||
*ngIf="element.Platform?.url" |
|||
class="mr-1" |
|||
[tooltip]="element.Platform?.name" |
|||
[url]="element.Platform?.url" |
|||
/> |
|||
<span>{{ element.Platform?.name }}</span> |
|||
</div> |
|||
</td> |
|||
<td |
|||
*matFooterCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-footer-cell |
|||
></td> |
|||
</ng-container> |
|||
<ng-container matColumnDef="currency"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-header-cell |
|||
mat-sort-header |
|||
> |
|||
<ng-container i18n>Currency</ng-container> |
|||
</th> |
|||
<td |
|||
*matCellDef="let element" |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-cell |
|||
> |
|||
{{ element.currency }} |
|||
</td> |
|||
<td *matFooterCellDef class="d-none d-lg-table-cell px-1" mat-footer-cell> |
|||
{{ baseCurrency }} |
|||
</td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="transactions"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="justify-content-end px-1" |
|||
mat-header-cell |
|||
mat-sort-header="transactionCount" |
|||
> |
|||
<span class="d-block d-sm-none">#</span> |
|||
<span class="d-none d-sm-block" i18n>Activities</span> |
|||
</th> |
|||
<td *matCellDef="let element" class="px-1 text-right" mat-cell> |
|||
{{ element.transactionCount }} |
|||
</td> |
|||
<td *matFooterCellDef class="px-1 text-right" mat-footer-cell> |
|||
{{ transactionCount }} |
|||
</td> |
|||
</ng-container> |
|||
<ng-container matColumnDef="platform"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-header-cell |
|||
mat-sort-header="Platform.name" |
|||
> |
|||
<ng-container i18n>Platform</ng-container> |
|||
</th> |
|||
<td |
|||
*matCellDef="let element" |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-cell |
|||
> |
|||
<div class="d-flex"> |
|||
@if (element.Platform?.url) { |
|||
<gf-asset-profile-icon |
|||
class="mr-1" |
|||
[tooltip]="element.Platform?.name" |
|||
[url]="element.Platform?.url" |
|||
/> |
|||
} |
|||
<span>{{ element.Platform?.name }}</span> |
|||
</div> |
|||
</td> |
|||
<td |
|||
*matFooterCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-footer-cell |
|||
></td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="balance"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-none d-lg-table-cell justify-content-end px-1" |
|||
mat-header-cell |
|||
mat-sort-header |
|||
> |
|||
<ng-container i18n>Cash Balance</ng-container> |
|||
</th> |
|||
<td |
|||
*matCellDef="let element" |
|||
class="d-none d-lg-table-cell px-1 text-right" |
|||
mat-cell |
|||
> |
|||
<gf-value |
|||
class="d-inline-block justify-content-end" |
|||
[isCurrency]="true" |
|||
[locale]="locale" |
|||
[value]="element.balance" |
|||
/> |
|||
</td> |
|||
<td |
|||
*matFooterCellDef |
|||
class="d-none d-lg-table-cell px-1 text-right" |
|||
mat-footer-cell |
|||
> |
|||
<gf-value |
|||
class="d-inline-block justify-content-end" |
|||
[isCurrency]="true" |
|||
[locale]="locale" |
|||
[value]="totalBalanceInBaseCurrency" |
|||
/> |
|||
</td> |
|||
</ng-container> |
|||
<ng-container matColumnDef="transactions"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="justify-content-end px-1" |
|||
mat-header-cell |
|||
mat-sort-header="transactionCount" |
|||
> |
|||
<span class="d-block d-sm-none">#</span> |
|||
<span class="d-none d-sm-block" i18n>Activities</span> |
|||
</th> |
|||
<td *matCellDef="let element" class="px-1 text-right" mat-cell> |
|||
{{ element.transactionCount }} |
|||
</td> |
|||
<td *matFooterCellDef class="px-1 text-right" mat-footer-cell> |
|||
{{ transactionCount }} |
|||
</td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="value"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-none d-lg-table-cell justify-content-end px-1" |
|||
mat-header-cell |
|||
mat-sort-header |
|||
> |
|||
<ng-container i18n>Value</ng-container> |
|||
</th> |
|||
<td |
|||
*matCellDef="let element" |
|||
class="d-none d-lg-table-cell px-1 text-right" |
|||
mat-cell |
|||
> |
|||
<gf-value |
|||
class="d-inline-block justify-content-end" |
|||
[isCurrency]="true" |
|||
[locale]="locale" |
|||
[value]="element.value" |
|||
/> |
|||
</td> |
|||
<td |
|||
*matFooterCellDef |
|||
class="d-none d-lg-table-cell px-1 text-right" |
|||
mat-footer-cell |
|||
> |
|||
<gf-value |
|||
class="d-inline-block justify-content-end" |
|||
[isCurrency]="true" |
|||
[locale]="locale" |
|||
[value]="totalValueInBaseCurrency" |
|||
/> |
|||
</td> |
|||
</ng-container> |
|||
<ng-container matColumnDef="balance"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-none d-lg-table-cell justify-content-end px-1" |
|||
mat-header-cell |
|||
mat-sort-header |
|||
> |
|||
<ng-container i18n>Cash Balance</ng-container> |
|||
</th> |
|||
<td |
|||
*matCellDef="let element" |
|||
class="d-none d-lg-table-cell px-1 text-right" |
|||
mat-cell |
|||
> |
|||
<gf-value |
|||
class="d-inline-block justify-content-end" |
|||
[isCurrency]="true" |
|||
[locale]="locale" |
|||
[value]="element.balance" |
|||
/> |
|||
</td> |
|||
<td |
|||
*matFooterCellDef |
|||
class="d-none d-lg-table-cell px-1 text-right" |
|||
mat-footer-cell |
|||
> |
|||
<gf-value |
|||
class="d-inline-block justify-content-end" |
|||
[isCurrency]="true" |
|||
[locale]="locale" |
|||
[value]="totalBalanceInBaseCurrency" |
|||
/> |
|||
</td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="valueInBaseCurrency"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-lg-none d-xl-none px-1 text-right" |
|||
mat-header-cell |
|||
mat-sort-header |
|||
> |
|||
<ng-container i18n>Value</ng-container> |
|||
</th> |
|||
<td |
|||
*matCellDef="let element" |
|||
class="d-lg-none d-xl-none px-1 text-right" |
|||
mat-cell |
|||
> |
|||
<gf-value |
|||
class="d-inline-block justify-content-end" |
|||
[isCurrency]="true" |
|||
[locale]="locale" |
|||
[value]="element.valueInBaseCurrency" |
|||
/> |
|||
</td> |
|||
<td |
|||
*matFooterCellDef |
|||
class="d-lg-none d-xl-none px-1 text-right" |
|||
mat-footer-cell |
|||
> |
|||
<gf-value |
|||
class="d-inline-block justify-content-end" |
|||
[isCurrency]="true" |
|||
[locale]="locale" |
|||
[value]="totalValueInBaseCurrency" |
|||
/> |
|||
</td> |
|||
</ng-container> |
|||
<ng-container matColumnDef="value"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-none d-lg-table-cell justify-content-end px-1" |
|||
mat-header-cell |
|||
mat-sort-header |
|||
> |
|||
<ng-container i18n>Value</ng-container> |
|||
</th> |
|||
<td |
|||
*matCellDef="let element" |
|||
class="d-none d-lg-table-cell px-1 text-right" |
|||
mat-cell |
|||
> |
|||
<gf-value |
|||
class="d-inline-block justify-content-end" |
|||
[isCurrency]="true" |
|||
[locale]="locale" |
|||
[value]="element.value" |
|||
/> |
|||
</td> |
|||
<td |
|||
*matFooterCellDef |
|||
class="d-none d-lg-table-cell px-1 text-right" |
|||
mat-footer-cell |
|||
> |
|||
<gf-value |
|||
class="d-inline-block justify-content-end" |
|||
[isCurrency]="true" |
|||
[locale]="locale" |
|||
[value]="totalValueInBaseCurrency" |
|||
/> |
|||
</td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="comment"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-header-cell |
|||
></th> |
|||
<td *matCellDef="let element" class="d-none d-lg-table-cell px-1" mat-cell> |
|||
<button |
|||
*ngIf="element.comment" |
|||
class="mx-1 no-min-width px-2" |
|||
mat-button |
|||
title="Note" |
|||
(click)="onOpenComment(element.comment); $event.stopPropagation()" |
|||
<ng-container matColumnDef="valueInBaseCurrency"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-lg-none d-xl-none px-1 text-right" |
|||
mat-header-cell |
|||
mat-sort-header |
|||
> |
|||
<ng-container i18n>Value</ng-container> |
|||
</th> |
|||
<td |
|||
*matCellDef="let element" |
|||
class="d-lg-none d-xl-none px-1 text-right" |
|||
mat-cell |
|||
> |
|||
<ion-icon name="document-text-outline" /> |
|||
</button> |
|||
</td> |
|||
<td |
|||
*matFooterCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-footer-cell |
|||
></td> |
|||
</ng-container> |
|||
<gf-value |
|||
class="d-inline-block justify-content-end" |
|||
[isCurrency]="true" |
|||
[locale]="locale" |
|||
[value]="element.valueInBaseCurrency" |
|||
/> |
|||
</td> |
|||
<td |
|||
*matFooterCellDef |
|||
class="d-lg-none d-xl-none px-1 text-right" |
|||
mat-footer-cell |
|||
> |
|||
<gf-value |
|||
class="d-inline-block justify-content-end" |
|||
[isCurrency]="true" |
|||
[locale]="locale" |
|||
[value]="totalValueInBaseCurrency" |
|||
/> |
|||
</td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="actions" stickyEnd> |
|||
<th *matHeaderCellDef class="px-1 text-center" i18n 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]="accountMenu" |
|||
(click)="$event.stopPropagation()" |
|||
<ng-container matColumnDef="comment"> |
|||
<th |
|||
*matHeaderCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-header-cell |
|||
></th> |
|||
<td |
|||
*matCellDef="let element" |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-cell |
|||
> |
|||
<ion-icon name="ellipsis-horizontal" /> |
|||
</button> |
|||
<mat-menu #accountMenu="matMenu" xPosition="before"> |
|||
<button mat-menu-item (click)="onUpdateAccount(element)"> |
|||
<span class="align-items-center d-flex"> |
|||
<ion-icon class="mr-2" name="create-outline" /> |
|||
<span i18n>Edit</span> |
|||
</span> |
|||
</button> |
|||
@if (element.comment) { |
|||
<button |
|||
class="mx-1 no-min-width px-2" |
|||
mat-button |
|||
title="Note" |
|||
(click)="onOpenComment(element.comment); $event.stopPropagation()" |
|||
> |
|||
<ion-icon name="document-text-outline" /> |
|||
</button> |
|||
} |
|||
</td> |
|||
<td |
|||
*matFooterCellDef |
|||
class="d-none d-lg-table-cell px-1" |
|||
mat-footer-cell |
|||
></td> |
|||
</ng-container> |
|||
|
|||
<ng-container matColumnDef="actions" stickyEnd> |
|||
<th *matHeaderCellDef class="px-1 text-center" i18n mat-header-cell></th> |
|||
<td *matCellDef="let element" class="px-1 text-center" mat-cell> |
|||
<button |
|||
mat-menu-item |
|||
[disabled]="element.transactionCount > 0" |
|||
(click)="onDeleteAccount(element.id)" |
|||
class="mx-1 no-min-width px-2" |
|||
mat-button |
|||
[matMenuTriggerFor]="accountMenu" |
|||
(click)="$event.stopPropagation()" |
|||
> |
|||
<span class="align-items-center d-flex"> |
|||
<ion-icon class="mr-2" name="trash-outline" /> |
|||
<span i18n>Delete</span> |
|||
</span> |
|||
<ion-icon name="ellipsis-horizontal" /> |
|||
</button> |
|||
</mat-menu> |
|||
</td> |
|||
<td *matFooterCellDef class="px-1" mat-footer-cell></td> |
|||
</ng-container> |
|||
<mat-menu #accountMenu="matMenu" xPosition="before"> |
|||
<button mat-menu-item (click)="onUpdateAccount(element)"> |
|||
<span class="align-items-center d-flex"> |
|||
<ion-icon class="mr-2" name="create-outline" /> |
|||
<span i18n>Edit</span> |
|||
</span> |
|||
</button> |
|||
<button |
|||
mat-menu-item |
|||
[disabled]="element.transactionCount > 0" |
|||
(click)="onDeleteAccount(element.id)" |
|||
> |
|||
<span class="align-items-center d-flex"> |
|||
<ion-icon class="mr-2" name="trash-outline" /> |
|||
<span i18n>Delete</span> |
|||
</span> |
|||
</button> |
|||
</mat-menu> |
|||
</td> |
|||
<td *matFooterCellDef class="px-1" mat-footer-cell></td> |
|||
</ng-container> |
|||
|
|||
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr> |
|||
<tr |
|||
*matRowDef="let row; columns: displayedColumns" |
|||
mat-row |
|||
[ngClass]="{ |
|||
'cursor-pointer': hasPermissionToOpenDetails |
|||
}" |
|||
(click)="onOpenAccountDetailDialog(row.id)" |
|||
></tr> |
|||
<tr |
|||
*matFooterRowDef="displayedColumns" |
|||
mat-footer-row |
|||
[ngClass]="{ 'd-none': isLoading || !showFooter }" |
|||
></tr> |
|||
</table> |
|||
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr> |
|||
<tr |
|||
*matRowDef="let row; columns: displayedColumns" |
|||
mat-row |
|||
[ngClass]="{ |
|||
'cursor-pointer': hasPermissionToOpenDetails |
|||
}" |
|||
(click)="onOpenAccountDetailDialog(row.id)" |
|||
></tr> |
|||
<tr |
|||
*matFooterRowDef="displayedColumns" |
|||
mat-footer-row |
|||
[ngClass]="{ 'd-none': isLoading || !showFooter }" |
|||
></tr> |
|||
</table> |
|||
</div> |
|||
|
|||
<ngx-skeleton-loader |
|||
*ngIf="isLoading" |
|||
animation="pulse" |
|||
class="px-4 py-3" |
|||
[theme]="{ |
|||
height: '1.5rem', |
|||
width: '100%' |
|||
}" |
|||
/> |
|||
@if (isLoading) { |
|||
<ngx-skeleton-loader |
|||
animation="pulse" |
|||
class="px-4 py-3" |
|||
[theme]="{ |
|||
height: '1.5rem', |
|||
width: '100%' |
|||
}" |
|||
/> |
|||
} |
|||
|
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue