Browse Source

Merge branch 'main' into feature/migrate-from-angular-material-design-2-to-3

pull/5311/head
Thomas Kaul 3 weeks ago
parent
commit
a37acaec24
  1. 14
      CHANGELOG.md
  2. 2
      apps/api/src/app/export/export.service.ts
  3. 2
      apps/api/src/app/import/import.service.ts
  4. 4
      apps/api/src/app/order/order.controller.ts
  5. 20
      apps/api/src/app/order/order.service.ts
  6. 10
      apps/api/src/app/portfolio/portfolio.service.ts
  7. 4
      apps/api/src/services/tag/tag.service.ts
  8. 2
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts
  9. 2
      apps/client/src/app/pages/accounts/accounts-page.component.ts
  10. 2
      apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html
  11. 306
      apps/client/src/locales/messages.ca.xlf
  12. 306
      apps/client/src/locales/messages.de.xlf
  13. 306
      apps/client/src/locales/messages.es.xlf
  14. 306
      apps/client/src/locales/messages.fr.xlf
  15. 306
      apps/client/src/locales/messages.it.xlf
  16. 306
      apps/client/src/locales/messages.nl.xlf
  17. 306
      apps/client/src/locales/messages.pl.xlf
  18. 306
      apps/client/src/locales/messages.pt.xlf
  19. 306
      apps/client/src/locales/messages.tr.xlf
  20. 306
      apps/client/src/locales/messages.uk.xlf
  21. 305
      apps/client/src/locales/messages.xlf
  22. 306
      apps/client/src/locales/messages.zh.xlf
  23. 32
      apps/client/src/styles.scss
  24. 2
      libs/common/src/lib/config.ts
  25. 0
      libs/ui/src/lib/accounts-table/accounts-table.component.html
  26. 0
      libs/ui/src/lib/accounts-table/accounts-table.component.scss
  27. 146
      libs/ui/src/lib/accounts-table/accounts-table.component.stories.ts
  28. 10
      libs/ui/src/lib/accounts-table/accounts-table.component.ts
  29. 1
      libs/ui/src/lib/accounts-table/index.ts
  30. 2
      libs/ui/src/lib/activities-table/activities-table.component.html
  31. 28
      libs/ui/src/lib/activities-table/activities-table.component.ts
  32. 1
      libs/ui/src/lib/i18n.ts
  33. 2
      libs/ui/src/lib/tags-selector/tags-selector.component.stories.ts
  34. 4
      package-lock.json
  35. 2
      package.json
  36. 4
      prisma/seed.ts

14
CHANGELOG.md

@ -7,19 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased ## Unreleased
### Changed
- Migrated from _Material Design_ 2 to _Material Design_ 3
## 2.187.0 - 2025-08-02
### Added ### Added
- Added support to exclude an activity from analysis based on tags
- Added a _Storybook_ story for the accounts table component
- Added a _Storybook_ story for the membership card component - Added a _Storybook_ story for the membership card component
### Changed ### Changed
- Moved the support for changing the asset profile identifier (`dataSource` and `symbol`) in the asset profile details dialog of the admin control panel from experimental to general availability - Moved the support for changing the asset profile identifier (`dataSource` and `symbol`) in the asset profile details dialog of the admin control panel from experimental to general availability
- Improved the balance of headings on the landing page - Improved the balance of headings on the landing page
- Migrated from _Material Design_ 2 to _Material Design_ 3 - Improved the language localization for German (`de`)
- Improved the language localization for Spanish (`es`) - Improved the language localization for Spanish (`es`)
- Upgraded `angular` from version `20.0.7` to `20.1.3` - Upgraded `angular` from version `20.0.7` to `20.1.3`
- Upgraded `Nx` from version `21.2.4` to `21.3.9` - Upgraded `Nx` from version `21.2.4` to `21.3.9`
### Fixed
- Fixed the missing localization for "Exclude from Analysis" in the create or update account dialog
## 2.186.0 - 2025-07-30 ## 2.186.0 - 2025-07-30
### Added ### Added

2
apps/api/src/app/export/export.service.ts

@ -41,7 +41,7 @@ export class ExportService {
includeDrafts: true, includeDrafts: true,
sortColumn: 'date', sortColumn: 'date',
sortDirection: 'asc', sortDirection: 'asc',
withExcludedAccounts: true withExcludedAccountsAndActivities: true
}); });
if (activityIds?.length > 0) { if (activityIds?.length > 0) {

2
apps/api/src/app/import/import.service.ts

@ -533,7 +533,7 @@ export class ImportService {
userCurrency, userCurrency,
userId, userId,
includeDrafts: true, includeDrafts: true,
withExcludedAccounts: true withExcludedAccountsAndActivities: true
}); });
return activitiesDto.map( return activitiesDto.map(

4
apps/api/src/app/order/order.controller.ts

@ -144,7 +144,7 @@ export class OrderController {
skip: isNaN(skip) ? undefined : skip, skip: isNaN(skip) ? undefined : skip,
take: isNaN(take) ? undefined : take, take: isNaN(take) ? undefined : take,
userId: impersonationUserId || this.request.user.id, userId: impersonationUserId || this.request.user.id,
withExcludedAccounts: true withExcludedAccountsAndActivities: true
}); });
return { activities, count }; return { activities, count };
@ -165,7 +165,7 @@ export class OrderController {
const { activities } = await this.orderService.getOrders({ const { activities } = await this.orderService.getOrders({
userCurrency, userCurrency,
userId: impersonationUserId || this.request.user.id, userId: impersonationUserId || this.request.user.id,
withExcludedAccounts: true withExcludedAccountsAndActivities: true
}); });
const activity = activities.find((activity) => { const activity = activities.find((activity) => {

20
apps/api/src/app/order/order.service.ts

@ -9,7 +9,8 @@ import {
DATA_GATHERING_QUEUE_PRIORITY_HIGH, DATA_GATHERING_QUEUE_PRIORITY_HIGH,
GATHER_ASSET_PROFILE_PROCESS_JOB_NAME, GATHER_ASSET_PROFILE_PROCESS_JOB_NAME,
GATHER_ASSET_PROFILE_PROCESS_JOB_OPTIONS, GATHER_ASSET_PROFILE_PROCESS_JOB_OPTIONS,
ghostfolioPrefix ghostfolioPrefix,
TAG_ID_EXCLUDE_FROM_ANALYSIS
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; import { getAssetProfileIdentifier } from '@ghostfolio/common/helper';
import { import {
@ -275,7 +276,7 @@ export class OrderService {
userId, userId,
includeDrafts: true, includeDrafts: true,
userCurrency: undefined, userCurrency: undefined,
withExcludedAccounts: true withExcludedAccountsAndActivities: true
}); });
const { count } = await this.prismaService.order.deleteMany({ const { count } = await this.prismaService.order.deleteMany({
@ -332,7 +333,7 @@ export class OrderService {
types, types,
userCurrency, userCurrency,
userId, userId,
withExcludedAccounts = false withExcludedAccountsAndActivities = false
}: { }: {
endDate?: Date; endDate?: Date;
filters?: Filter[]; filters?: Filter[];
@ -345,7 +346,7 @@ export class OrderService {
types?: ActivityType[]; types?: ActivityType[];
userCurrency: string; userCurrency: string;
userId: string; userId: string;
withExcludedAccounts?: boolean; withExcludedAccountsAndActivities?: boolean;
}): Promise<Activities> { }): Promise<Activities> {
let orderBy: Prisma.Enumerable<Prisma.OrderOrderByWithRelationInput> = [ let orderBy: Prisma.Enumerable<Prisma.OrderOrderByWithRelationInput> = [
{ date: 'asc' }, { date: 'asc' },
@ -491,11 +492,18 @@ export class OrderService {
where.type = { in: types }; where.type = { in: types };
} }
if (withExcludedAccounts === false) { if (withExcludedAccountsAndActivities === false) {
where.OR = [ where.OR = [
{ account: null }, { account: null },
{ account: { NOT: { isExcluded: true } } } { account: { NOT: { isExcluded: true } } }
]; ];
where.tags = {
...where.tags,
none: {
id: TAG_ID_EXCLUDE_FROM_ANALYSIS
}
};
} }
const [orders, count] = await Promise.all([ const [orders, count] = await Promise.all([
@ -609,7 +617,7 @@ export class OrderService {
filters, filters,
userCurrency, userCurrency,
userId, userId,
withExcludedAccounts: false // TODO withExcludedAccountsAndActivities: false // TODO
}); });
} }

10
apps/api/src/app/portfolio/portfolio.service.ts

@ -33,6 +33,7 @@ import {
import { import {
DEFAULT_CURRENCY, DEFAULT_CURRENCY,
TAG_ID_EMERGENCY_FUND, TAG_ID_EMERGENCY_FUND,
TAG_ID_EXCLUDE_FROM_ANALYSIS,
UNKNOWN_KEY UNKNOWN_KEY
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { DATE_FORMAT, getSum, parseDate } from '@ghostfolio/common/helper'; import { DATE_FORMAT, getSum, parseDate } from '@ghostfolio/common/helper';
@ -1799,14 +1800,19 @@ export class PortfolioService {
const { activities } = await this.orderService.getOrders({ const { activities } = await this.orderService.getOrders({
userCurrency, userCurrency,
userId, userId,
withExcludedAccounts: true withExcludedAccountsAndActivities: true
}); });
const excludedActivities: Activity[] = []; const excludedActivities: Activity[] = [];
const nonExcludedActivities: Activity[] = []; const nonExcludedActivities: Activity[] = [];
for (const activity of activities) { for (const activity of activities) {
if (activity.account?.isExcluded) { if (
activity.account?.isExcluded ||
activity.tags?.some(({ id }) => {
return id === TAG_ID_EXCLUDE_FROM_ANALYSIS;
})
) {
excludedActivities.push(activity); excludedActivities.push(activity);
} else { } else {
nonExcludedActivities.push(activity); nonExcludedActivities.push(activity);

4
apps/api/src/services/tag/tag.service.ts

@ -1,4 +1,5 @@
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { TAG_ID_EXCLUDE_FROM_ANALYSIS } from '@ghostfolio/common/config';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Prisma, Tag } from '@prisma/client'; import { Prisma, Tag } from '@prisma/client';
@ -79,7 +80,8 @@ export class TagService {
id, id,
name, name,
userId, userId,
isUsed: _count.activities > 0 isUsed:
_count.activities > 0 && ![TAG_ID_EXCLUDE_FROM_ANALYSIS].includes(id)
})); }));
} }

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

@ -1,5 +1,4 @@
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
import { GfAccountsTableComponent } from '@ghostfolio/client/components/accounts-table/accounts-table.component';
import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module'; import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module';
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module'; import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
@ -15,6 +14,7 @@ import {
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { internalRoutes } from '@ghostfolio/common/routes/routes'; import { internalRoutes } from '@ghostfolio/common/routes/routes';
import { GfAccountsTableComponent } from '@ghostfolio/ui/accounts-table';
import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table'; import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table';
import { GfDataProviderCreditsComponent } from '@ghostfolio/ui/data-provider-credits'; import { GfDataProviderCreditsComponent } from '@ghostfolio/ui/data-provider-credits';
import { GfHistoricalMarketDataEditorComponent } from '@ghostfolio/ui/historical-market-data-editor'; import { GfHistoricalMarketDataEditorComponent } from '@ghostfolio/ui/historical-market-data-editor';

2
apps/client/src/app/pages/accounts/accounts-page.component.ts

@ -4,13 +4,13 @@ import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto
import { AccountDetailDialog } from '@ghostfolio/client/components/account-detail-dialog/account-detail-dialog.component'; import { AccountDetailDialog } from '@ghostfolio/client/components/account-detail-dialog/account-detail-dialog.component';
import { GfAccountDetailDialogModule } from '@ghostfolio/client/components/account-detail-dialog/account-detail-dialog.module'; import { GfAccountDetailDialogModule } from '@ghostfolio/client/components/account-detail-dialog/account-detail-dialog.module';
import { AccountDetailDialogParams } from '@ghostfolio/client/components/account-detail-dialog/interfaces/interfaces'; import { AccountDetailDialogParams } from '@ghostfolio/client/components/account-detail-dialog/interfaces/interfaces';
import { GfAccountsTableComponent } from '@ghostfolio/client/components/accounts-table/accounts-table.component';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
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 { User } from '@ghostfolio/common/interfaces'; import { User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { GfAccountsTableComponent } from '@ghostfolio/ui/accounts-table';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';

2
apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html

@ -86,7 +86,7 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div class="mb-3 px-2"> <div class="mb-3 px-2">
<mat-checkbox color="primary" formControlName="isExcluded" <mat-checkbox color="primary" formControlName="isExcluded" i18n
>Exclude from Analysis</mat-checkbox >Exclude from Analysis</mat-checkbox
> >
</div> </div>

306
apps/client/src/locales/messages.ca.xlf

File diff suppressed because it is too large

306
apps/client/src/locales/messages.de.xlf

File diff suppressed because it is too large

306
apps/client/src/locales/messages.es.xlf

File diff suppressed because it is too large

306
apps/client/src/locales/messages.fr.xlf

File diff suppressed because it is too large

306
apps/client/src/locales/messages.it.xlf

File diff suppressed because it is too large

306
apps/client/src/locales/messages.nl.xlf

File diff suppressed because it is too large

306
apps/client/src/locales/messages.pl.xlf

File diff suppressed because it is too large

306
apps/client/src/locales/messages.pt.xlf

File diff suppressed because it is too large

306
apps/client/src/locales/messages.tr.xlf

File diff suppressed because it is too large

306
apps/client/src/locales/messages.uk.xlf

File diff suppressed because it is too large

305
apps/client/src/locales/messages.xlf

File diff suppressed because it is too large

306
apps/client/src/locales/messages.zh.xlf

File diff suppressed because it is too large

32
apps/client/src/styles.scss

@ -267,6 +267,30 @@ body {
background-color: rgba(var(--palette-foreground-base-dark), 0.02); background-color: rgba(var(--palette-foreground-base-dark), 0.02);
} }
.mat-mdc-slide-toggle {
.mdc-switch__track {
--mat-slide-toggle-selected-focus-track-color: rgba(
255,
255,
255,
0.12
);
--mat-slide-toggle-selected-hover-track-color: rgba(
255,
255,
255,
0.12
);
--mat-slide-toggle-selected-pressed-track-color: rgba(
255,
255,
255,
0.12
);
--mat-slide-toggle-selected-track-color: rgba(255, 255, 255, 0.12);
}
}
.mdc-button { .mdc-button {
&.mat-accent, &.mat-accent,
&.mat-primary { &.mat-primary {
@ -488,12 +512,12 @@ ngx-skeleton-loader {
} }
} }
/**
* Fix for https://github.com/angular/components/issues/26818
*/
.mat-mdc-slide-toggle { .mat-mdc-slide-toggle {
.mdc-switch__track { .mdc-switch__track {
background-color: rgba(var(--palette-primary-500), 1); --mat-slide-toggle-selected-focus-track-color: rgba(0, 0, 0, 0.12);
--mat-slide-toggle-selected-hover-track-color: rgba(0, 0, 0, 0.12);
--mat-slide-toggle-selected-pressed-track-color: rgba(0, 0, 0, 0.12);
--mat-slide-toggle-selected-track-color: rgba(0, 0, 0, 0.12);
} }
} }

2
libs/common/src/lib/config.ts

@ -200,6 +200,8 @@ export const SUPPORTED_LANGUAGE_CODES = [
]; ];
export const TAG_ID_EMERGENCY_FUND = '4452656d-9fa4-4bd0-ba38-70492e31d180'; export const TAG_ID_EMERGENCY_FUND = '4452656d-9fa4-4bd0-ba38-70492e31d180';
export const TAG_ID_EXCLUDE_FROM_ANALYSIS =
'f2e868af-8333-459f-b161-cbc6544c24bd';
export const TAG_ID_DEMO = 'efa08cb3-9b9d-4974-ac68-db13a19c4874'; export const TAG_ID_DEMO = 'efa08cb3-9b9d-4974-ac68-db13a19c4874';
export const UNKNOWN_KEY = 'UNKNOWN'; export const UNKNOWN_KEY = 'UNKNOWN';

0
apps/client/src/app/components/accounts-table/accounts-table.component.html → libs/ui/src/lib/accounts-table/accounts-table.component.html

0
apps/client/src/app/components/accounts-table/accounts-table.component.scss → libs/ui/src/lib/accounts-table/accounts-table.component.scss

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

@ -0,0 +1,146 @@
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatMenuModule } from '@angular/material/menu';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone';
import { moduleMetadata } from '@storybook/angular';
import type { Meta, StoryObj } from '@storybook/angular';
import { NotificationService } from 'apps/client/src/app/core/notification/notification.service';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { GfEntityLogoComponent } from '../entity-logo';
import { GfValueComponent } from '../value';
import { GfAccountsTableComponent } from './accounts-table.component';
const accounts = [
{
allocationInPercentage: null,
balance: 278,
balanceInBaseCurrency: 278,
comment: null,
createdAt: new Date('2025-06-01T06:52:49.063Z'),
currency: 'USD',
id: '460d7401-ca43-4ed4-b08e-349f1822e9db',
isExcluded: false,
name: 'Coinbase Account',
platform: {
id: '8dc24b88-bb92-4152-af25-fe6a31643e26',
name: 'Coinbase',
url: 'https://www.coinbase.com'
},
platformId: '8dc24b88-bb92-4152-af25-fe6a31643e26',
transactionCount: 0,
updatedAt: new Date('2025-06-01T06:52:49.063Z'),
userId: '081aa387-487d-4438-83a4-3060eb2a016e',
value: 278,
valueInBaseCurrency: 278
},
{
allocationInPercentage: null,
balance: 12000,
balanceInBaseCurrency: 12000,
comment: null,
createdAt: new Date('2025-06-01T06:48:53.055Z'),
currency: 'USD',
id: '6d773e31-0583-4c85-a247-e69870b4f1ee',
isExcluded: false,
name: 'Private Banking Account',
platform: {
id: '43e8fcd1-5b79-4100-b678-d2229bd1660d',
name: 'J.P. Morgan',
url: 'https://www.jpmorgan.com'
},
platformId: '43e8fcd1-5b79-4100-b678-d2229bd1660d',
transactionCount: 0,
updatedAt: new Date('2025-06-01T06:48:53.055Z'),
userId: '081aa387-487d-4438-83a4-3060eb2a016e',
value: 12000,
valueInBaseCurrency: 12000
},
{
allocationInPercentage: null,
balance: 150.2,
balanceInBaseCurrency: 150.2,
comment: null,
createdAt: new Date('2025-05-31T13:00:13.940Z'),
currency: 'USD',
id: '776bd1e9-b2f6-4f7e-933d-18756c2f0625',
isExcluded: false,
name: 'Trading Account',
platform: {
id: '9da3a8a7-4795-43e3-a6db-ccb914189737',
name: 'Interactive Brokers',
url: 'https://interactivebrokers.com'
},
platformId: '9da3a8a7-4795-43e3-a6db-ccb914189737',
transactionCount: 12,
valueInBaseCurrency: 95693.70321466809,
updatedAt: new Date('2025-06-01T06:53:10.569Z'),
userId: '081aa387-487d-4438-83a4-3060eb2a016e',
value: 95693.70321466809
}
];
export default {
title: 'Accounts Table',
component: GfAccountsTableComponent,
decorators: [
moduleMetadata({
imports: [
CommonModule,
GfEntityLogoComponent,
GfValueComponent,
IonIcon,
MatButtonModule,
MatMenuModule,
MatSortModule,
MatTableModule,
NgxSkeletonLoaderModule,
RouterModule.forChild([])
],
providers: [NotificationService]
})
]
} as Meta<GfAccountsTableComponent>;
type Story = StoryObj<GfAccountsTableComponent>;
export const Default: Story = {
args: {
accounts,
baseCurrency: 'USD',
deviceType: 'desktop',
locale: 'en-US',
showActions: false,
showAllocationInPercentage: false,
showBalance: true,
showFooter: true,
showTransactions: true,
showValue: true,
showValueInBaseCurrency: true,
totalBalanceInBaseCurrency: 12428.2,
totalValueInBaseCurrency: 107971.70321466809,
transactionCount: 12
}
};
export const WithoutFooter: Story = {
args: {
accounts,
baseCurrency: 'USD',
deviceType: 'desktop',
locale: 'en-US',
showActions: false,
showAllocationInPercentage: false,
showBalance: true,
showFooter: false,
showTransactions: true,
showValue: true,
showValueInBaseCurrency: true,
totalBalanceInBaseCurrency: 12428.2,
totalValueInBaseCurrency: 107971.70321466809,
transactionCount: 12
}
};

10
apps/client/src/app/components/accounts-table/accounts-table.component.ts → libs/ui/src/lib/accounts-table/accounts-table.component.ts

@ -21,7 +21,7 @@ import { MatSort, MatSortModule } from '@angular/material/sort';
import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { Router, RouterModule } from '@angular/router'; import { Router, RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
import { Account as AccountModel } from '@prisma/client'; import { Account } from '@prisma/client';
import { addIcons } from 'ionicons'; import { addIcons } from 'ionicons';
import { import {
arrowRedoOutline, arrowRedoOutline,
@ -54,7 +54,7 @@ import { Subject, Subscription } from 'rxjs';
templateUrl: './accounts-table.component.html' templateUrl: './accounts-table.component.html'
}) })
export class GfAccountsTableComponent implements OnChanges, OnDestroy { export class GfAccountsTableComponent implements OnChanges, OnDestroy {
@Input() accounts: AccountModel[]; @Input() accounts: Account[];
@Input() baseCurrency: string; @Input() baseCurrency: string;
@Input() deviceType: string; @Input() deviceType: string;
@Input() hasPermissionToOpenDetails = true; @Input() hasPermissionToOpenDetails = true;
@ -71,12 +71,12 @@ export class GfAccountsTableComponent implements OnChanges, OnDestroy {
@Input() transactionCount: number; @Input() transactionCount: number;
@Output() accountDeleted = new EventEmitter<string>(); @Output() accountDeleted = new EventEmitter<string>();
@Output() accountToUpdate = new EventEmitter<AccountModel>(); @Output() accountToUpdate = new EventEmitter<Account>();
@Output() transferBalance = new EventEmitter<void>(); @Output() transferBalance = new EventEmitter<void>();
@ViewChild(MatSort) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
public dataSource = new MatTableDataSource<AccountModel>(); public dataSource = new MatTableDataSource<Account>();
public displayedColumns = []; public displayedColumns = [];
public isLoading = true; public isLoading = true;
public routeQueryParams: Subscription; public routeQueryParams: Subscription;
@ -167,7 +167,7 @@ export class GfAccountsTableComponent implements OnChanges, OnDestroy {
this.transferBalance.emit(); this.transferBalance.emit();
} }
public onUpdateAccount(aAccount: AccountModel) { public onUpdateAccount(aAccount: Account) {
this.accountToUpdate.emit(aAccount); this.accountToUpdate.emit(aAccount);
} }

1
libs/ui/src/lib/accounts-table/index.ts

@ -0,0 +1 @@
export * from './accounts-table.component';

2
libs/ui/src/lib/activities-table/activities-table.component.html

@ -468,7 +468,7 @@
[ngClass]="{ [ngClass]="{
'cursor-pointer': 'cursor-pointer':
hasPermissionToOpenDetails && hasPermissionToOpenDetails &&
row.account?.isExcluded !== true && isExcludedFromAnalysis(row) === false &&
row.isDraft === false && row.isDraft === false &&
['BUY', 'DIVIDEND', 'SELL'].includes(row.type) ['BUY', 'DIVIDEND', 'SELL'].includes(row.type)
}" }"

28
libs/ui/src/lib/activities-table/activities-table.component.ts

@ -2,7 +2,10 @@ import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interf
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type'; import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module';
import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; import {
DEFAULT_PAGE_SIZE,
TAG_ID_EXCLUDE_FROM_ANALYSIS
} from '@ghostfolio/common/config';
import { getDateFormatString, getLocale } from '@ghostfolio/common/helper'; import { getDateFormatString, getLocale } from '@ghostfolio/common/helper';
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
import { OrderWithAccount } from '@ghostfolio/common/types'; import { OrderWithAccount } from '@ghostfolio/common/types';
@ -171,12 +174,6 @@ export class GfActivitiesTableComponent
}); });
} }
public areAllRowsSelected() {
const numSelectedRows = this.selectedRows.selected.length;
const numTotalRows = this.dataSource.data.length;
return numSelectedRows === numTotalRows;
}
public ngOnChanges() { public ngOnChanges() {
this.defaultDateFormat = getDateFormatString(this.locale); this.defaultDateFormat = getDateFormatString(this.locale);
@ -215,6 +212,21 @@ export class GfActivitiesTableComponent
} }
} }
public areAllRowsSelected() {
const numSelectedRows = this.selectedRows.selected.length;
const numTotalRows = this.dataSource.data.length;
return numSelectedRows === numTotalRows;
}
public isExcludedFromAnalysis(activity: Activity) {
return (
activity.account?.isExcluded ||
activity.tags?.some(({ id }) => {
return id === TAG_ID_EXCLUDE_FROM_ANALYSIS;
})
);
}
public onChangePage(page: PageEvent) { public onChangePage(page: PageEvent) {
this.pageChanged.emit(page); this.pageChanged.emit(page);
} }
@ -226,7 +238,7 @@ export class GfActivitiesTableComponent
} }
} else if ( } else if (
this.hasPermissionToOpenDetails && this.hasPermissionToOpenDetails &&
activity.account?.isExcluded !== true && this.isExcludedFromAnalysis(activity) === false &&
activity.isDraft === false && activity.isDraft === false &&
['BUY', 'DIVIDEND', 'SELL'].includes(activity.type) ['BUY', 'DIVIDEND', 'SELL'].includes(activity.type)
) { ) {

1
libs/ui/src/lib/i18n.ts

@ -13,6 +13,7 @@ const locales = {
DATA_IMPORT_AND_EXPORT_TOOLTIP_OSS: $localize`Switch to Ghostfolio Premium easily`, DATA_IMPORT_AND_EXPORT_TOOLTIP_OSS: $localize`Switch to Ghostfolio Premium easily`,
DATA_IMPORT_AND_EXPORT_TOOLTIP_PREMIUM: $localize`Switch to Ghostfolio Open Source or Ghostfolio Basic easily`, DATA_IMPORT_AND_EXPORT_TOOLTIP_PREMIUM: $localize`Switch to Ghostfolio Open Source or Ghostfolio Basic easily`,
EMERGENCY_FUND: $localize`Emergency Fund`, EMERGENCY_FUND: $localize`Emergency Fund`,
EXCLUDE_FROM_ANALYSIS: $localize`Exclude from Analysis`,
Global: $localize`Global`, Global: $localize`Global`,
GRANT: $localize`Grant`, GRANT: $localize`Grant`,
HIGHER_RISK: $localize`Higher Risk`, HIGHER_RISK: $localize`Higher Risk`,

2
libs/ui/src/lib/tags-selector/tags-selector.component.stories.ts

@ -21,7 +21,7 @@ const OPTIONS = [
{ {
id: '3ef7e6d9-4598-4eb2-b0e8-00e61cfc0ea6', id: '3ef7e6d9-4598-4eb2-b0e8-00e61cfc0ea6',
name: 'Gambling', name: 'Gambling',
userId: 'c6a71541-d0e3-4e22-ae83-b5e5611b6695' userId: '081aa387-487d-4438-83a4-3060eb2a016e'
}, },
{ {
id: 'EMERGENCY_FUND', id: 'EMERGENCY_FUND',

4
package-lock.json

@ -1,12 +1,12 @@
{ {
"name": "ghostfolio", "name": "ghostfolio",
"version": "2.186.0", "version": "2.187.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ghostfolio", "name": "ghostfolio",
"version": "2.186.0", "version": "2.187.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {

2
package.json

@ -1,6 +1,6 @@
{ {
"name": "ghostfolio", "name": "ghostfolio",
"version": "2.186.0", "version": "2.187.0",
"homepage": "https://ghostfol.io", "homepage": "https://ghostfol.io",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio", "repository": "https://github.com/ghostfolio/ghostfolio",

4
prisma/seed.ts

@ -8,6 +8,10 @@ async function main() {
{ {
id: '4452656d-9fa4-4bd0-ba38-70492e31d180', id: '4452656d-9fa4-4bd0-ba38-70492e31d180',
name: 'EMERGENCY_FUND' name: 'EMERGENCY_FUND'
},
{
id: 'f2e868af-8333-459f-b161-cbc6544c24bd',
name: 'EXCLUDE_FROM_ANALYSIS'
} }
], ],
skipDuplicates: true skipDuplicates: true

Loading…
Cancel
Save