From 4256cdee34fdb17cff146cb95edd2ead5e78bb9a Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Sat, 4 Oct 2025 09:51:51 +0200
Subject: [PATCH 1/7] Bugfix/add missing CommonModule import in import
activities dialog (#5674)
* Add missing CommonModule import
* Update changelog
---
CHANGELOG.md | 1 +
.../import-activities-dialog.component.ts | 2 ++
2 files changed, 3 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 524dcc47b..9ad8f2267 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Handled an exception in the get asset profile functionality of the _Financial Modeling Prep_ service
+- Added the missing `CommonModule` import in the import activities dialog
## 2.205.0 - 2025-10-01
diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts
index d1eaf0fe1..2439a4b65 100644
--- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts
+++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts
@@ -15,6 +15,7 @@ import {
StepperOrientation,
StepperSelectionEvent
} from '@angular/cdk/stepper';
+import { CommonModule } from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
@@ -59,6 +60,7 @@ import { ImportActivitiesDialogParams } from './interfaces/interfaces';
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'd-flex flex-column h-100' },
imports: [
+ CommonModule,
GfActivitiesTableComponent,
GfDialogFooterComponent,
GfDialogHeaderComponent,
From d715143299354272459a4e6949e3575aaac630e3 Mon Sep 17 00:00:00 2001
From: Malay Kumar <91375797+Malay-dev@users.noreply.github.com>
Date: Sat, 4 Oct 2025 13:58:08 +0530
Subject: [PATCH 2/7] Task/add value components to rule settings dialog (#5653)
* Add value components to rule settings dialog
* Update changelog
---
CHANGELOG.md | 1 +
.../interfaces/interfaces.ts | 1 +
.../rule-settings-dialog.component.ts | 2 +
.../rule-settings-dialog.html | 133 +++++++++---------
4 files changed, 71 insertions(+), 66 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9ad8f2267..4a58a725c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
+- Localized the number formatting in the settings dialog to customize the rule thresholds of the _X-ray_ page
- Improved the usability of the _Cancel_ / _Close_ buttons in the create watchlist item dialog
- Refactored the `fireWealth` from `number` type to a structured object in the summary of the portfolio details endpoint
- Refactored the _Open Startup_ (`/open`) page to standalone
diff --git a/apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts b/apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts
index 9811a6564..51c2b8951 100644
--- a/apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts
+++ b/apps/client/src/app/components/rule/rule-settings-dialog/interfaces/interfaces.ts
@@ -5,6 +5,7 @@ import {
export interface IRuleSettingsDialogParams {
categoryName: string;
+ locale: string;
rule: PortfolioReportRule;
settings: XRayRulesSettings['AccountClusterRiskCurrentInvestment'];
}
diff --git a/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts
index 7ee9c66cf..f8ce13e0d 100644
--- a/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts
+++ b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts
@@ -1,4 +1,5 @@
import { XRayRulesSettings } from '@ghostfolio/common/interfaces';
+import { GfValueComponent } from '@ghostfolio/ui/value';
import { CommonModule } from '@angular/common';
import { Component, Inject } from '@angular/core';
@@ -17,6 +18,7 @@ import { IRuleSettingsDialogParams } from './interfaces/interfaces';
imports: [
CommonModule,
FormsModule,
+ GfValueComponent,
MatButtonModule,
MatDialogModule,
MatSliderModule
diff --git a/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html
index 9fdc0cd57..83d4e2d19 100644
--- a/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html
+++ b/apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html
@@ -5,28 +5,30 @@
data.rule.configuration.thresholdMin && data.rule.configuration.thresholdMax
) {
} @else {
@@ -50,22 +51,23 @@
class="w-100"
[ngClass]="{ 'd-none': !data.rule.configuration.thresholdMin }"
>
-
+
Threshold Min:
- @if (data.rule.configuration.threshold.unit === '%') {
- {{ data.settings.thresholdMin | percent: '1.2-2' }}
- } @else {
- {{ data.settings.thresholdMin }}
- }
+
- @if (data.rule.configuration.threshold.unit === '%') {
-
- } @else {
-
- }
+
- @if (data.rule.configuration.threshold.unit === '%') {
-
- } @else {
-
- }
+
}
From 2248eb77f9ba7e1102ac068276e4841bd9cbab53 Mon Sep 17 00:00:00 2001
From: Aditya Garud <153842990+yashranaway@users.noreply.github.com>
Date: Sat, 4 Oct 2025 23:29:28 +0530
Subject: [PATCH 3/7] Feature/preselect first search result item in assistant
(#5656)
* Preselect first search result item in assistant
* Update changelog
---
CHANGELOG.md | 1 +
.../src/lib/assistant/assistant.component.ts | 63 +++++++++++++++++++
2 files changed, 64 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4a58a725c..008ec3055 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Localized the number formatting in the settings dialog to customize the rule thresholds of the _X-ray_ page
+- Improved the usability of the assistant by preselecting the first search result
- Improved the usability of the _Cancel_ / _Close_ buttons in the create watchlist item dialog
- Refactored the `fireWealth` from `number` type to a structured object in the summary of the portfolio details endpoint
- Refactored the _Open Startup_ (`/open`) page to standalone
diff --git a/libs/ui/src/lib/assistant/assistant.component.ts b/libs/ui/src/lib/assistant/assistant.component.ts
index 57c440bdb..3fc1cc232 100644
--- a/libs/ui/src/lib/assistant/assistant.component.ts
+++ b/libs/ui/src/lib/assistant/assistant.component.ts
@@ -169,6 +169,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
};
public tags: Filter[] = [];
+ private readonly PRESELECTION_DELAY = 100;
+
private filterTypes: Filter['type'][] = [
'ACCOUNT',
'ASSET_CLASS',
@@ -176,7 +178,9 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
'SYMBOL',
'TAG'
];
+
private keyManager: FocusKeyManager;
+ private preselectionTimeout: ReturnType;
private unsubscribeSubject = new Subject();
public constructor(
@@ -344,6 +348,9 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
.subscribe({
next: (searchResults) => {
this.searchResults = searchResults;
+
+ this.preselectFirstItem();
+
this.changeDetectorRef.markForCheck();
},
error: (error) => {
@@ -585,6 +592,10 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
}
public ngOnDestroy() {
+ if (this.preselectionTimeout) {
+ clearTimeout(this.preselectionTimeout);
+ }
+
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
@@ -595,6 +606,58 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit {
});
}
+ private getFirstSearchResultItem() {
+ if (this.searchResults.quickLinks?.length > 0) {
+ return this.searchResults.quickLinks[0];
+ }
+
+ if (this.searchResults.accounts?.length > 0) {
+ return this.searchResults.accounts[0];
+ }
+
+ if (this.searchResults.holdings?.length > 0) {
+ return this.searchResults.holdings[0];
+ }
+
+ if (this.searchResults.assetProfiles?.length > 0) {
+ return this.searchResults.assetProfiles[0];
+ }
+
+ return null;
+ }
+
+ private preselectFirstItem() {
+ if (this.preselectionTimeout) {
+ clearTimeout(this.preselectionTimeout);
+ }
+
+ this.preselectionTimeout = setTimeout(() => {
+ if (!this.isOpen || !this.searchFormControl.value) {
+ return;
+ }
+
+ const firstItem = this.getFirstSearchResultItem();
+
+ if (!firstItem) {
+ return;
+ }
+
+ for (const item of this.assistantListItems) {
+ item.removeFocus();
+ }
+
+ this.keyManager.setFirstItemActive();
+
+ const currentFocusedItem = this.getCurrentAssistantListItem();
+
+ if (currentFocusedItem) {
+ currentFocusedItem.focus();
+ }
+
+ this.changeDetectorRef.markForCheck();
+ }, this.PRESELECTION_DELAY);
+ }
+
private searchAccounts(aSearchTerm: string): Observable {
return this.dataService
.fetchAccounts({
From 6dc7edb7b57a57efc9465221d0751ed9100a9996 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Sat, 4 Oct 2025 20:11:48 +0200
Subject: [PATCH 4/7] Task/clean up unused import in holdings table component
(#5682)
* Clean up
---
libs/ui/src/lib/holdings-table/holdings-table.component.ts | 2 --
1 file changed, 2 deletions(-)
diff --git a/libs/ui/src/lib/holdings-table/holdings-table.component.ts b/libs/ui/src/lib/holdings-table/holdings-table.component.ts
index 83faf7621..1c46e18db 100644
--- a/libs/ui/src/lib/holdings-table/holdings-table.component.ts
+++ b/libs/ui/src/lib/holdings-table/holdings-table.component.ts
@@ -1,4 +1,3 @@
-import { GfSymbolPipe } from '@ghostfolio/client/pipes/symbol/symbol.pipe';
import { getLocale } from '@ghostfolio/common/helper';
import {
AssetProfileIdentifier,
@@ -34,7 +33,6 @@ import { GfValueComponent } from '../value/value.component';
imports: [
CommonModule,
GfEntityLogoComponent,
- GfSymbolPipe,
GfValueComponent,
MatButtonModule,
MatDialogModule,
From 400d4ae7b49bb5a78ec290a531cfc258be5a0731 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Sat, 4 Oct 2025 20:14:19 +0200
Subject: [PATCH 5/7] Release 2.206.0 (#5683)
---
CHANGELOG.md | 2 +-
package-lock.json | 4 ++--
package.json | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 008ec3055..5583fd59d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## Unreleased
+## 2.206.0 - 2025-10-04
### Changed
diff --git a/package-lock.json b/package-lock.json
index 8f8676556..2ed25d7c2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "ghostfolio",
- "version": "2.205.0",
+ "version": "2.206.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ghostfolio",
- "version": "2.205.0",
+ "version": "2.206.0",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
diff --git a/package.json b/package.json
index 28881f546..8717f58df 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ghostfolio",
- "version": "2.205.0",
+ "version": "2.206.0",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"repository": "https://github.com/ghostfolio/ghostfolio",
From 180e0f3c817d4f0fd607c8bc6fa60c49a6449787 Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Sun, 5 Oct 2025 11:17:15 +0200
Subject: [PATCH 6/7] Feature/add date range query parameter to data gathering
endpoint (#5684)
* Add date range
* Update changelog
---
CHANGELOG.md | 6 ++++++
apps/api/src/app/admin/admin.controller.ts | 18 ++++++++++++++++--
.../data-gathering/data-gathering.service.ts | 14 +++++++++-----
3 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5583fd59d..fecc9758e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## Unreleased
+
+### Added
+
+- Added support for a date range query parameter in the data gathering endpoint
+
## 2.206.0 - 2025-10-04
### Changed
diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts
index 27cc088d1..66f8483b4 100644
--- a/apps/api/src/app/admin/admin.controller.ts
+++ b/apps/api/src/app/admin/admin.controller.ts
@@ -6,6 +6,7 @@ import { ManualService } from '@ghostfolio/api/services/data-provider/manual/man
import { DemoService } from '@ghostfolio/api/services/demo/demo.service';
import { PropertyDto } from '@ghostfolio/api/services/property/property.dto';
import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service';
+import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper';
import {
DATA_GATHERING_QUEUE_PRIORITY_HIGH,
DATA_GATHERING_QUEUE_PRIORITY_MEDIUM,
@@ -22,6 +23,7 @@ import {
} from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions';
import type {
+ DateRange,
MarketDataPreset,
RequestWithUser
} from '@ghostfolio/common/types';
@@ -161,9 +163,21 @@ export class AdminController {
@HasPermission(permissions.accessAdminControl)
public async gatherSymbol(
@Param('dataSource') dataSource: DataSource,
- @Param('symbol') symbol: string
+ @Param('symbol') symbol: string,
+ @Query('range') dateRange: DateRange
): Promise {
- this.dataGatheringService.gatherSymbol({ dataSource, symbol });
+ let date: Date;
+
+ if (dateRange) {
+ const { startDate } = getIntervalFromDateRange(dateRange, new Date());
+ date = startDate;
+ }
+
+ this.dataGatheringService.gatherSymbol({
+ dataSource,
+ date,
+ symbol
+ });
return;
}
diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts
index 31edf6ffc..dd93e3e47 100644
--- a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts
+++ b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts
@@ -94,17 +94,21 @@ export class DataGatheringService {
});
}
- public async gatherSymbol({ dataSource, symbol }: AssetProfileIdentifier) {
+ public async gatherSymbol({ dataSource, date, symbol }: IDataGatheringItem) {
await this.marketDataService.deleteMany({ dataSource, symbol });
- const dataGatheringItems = (await this.getSymbolsMax()).filter(
- (dataGatheringItem) => {
+ const dataGatheringItems = (await this.getSymbolsMax())
+ .filter((dataGatheringItem) => {
return (
dataGatheringItem.dataSource === dataSource &&
dataGatheringItem.symbol === symbol
);
- }
- );
+ })
+ .map((item) => ({
+ ...item,
+ date: date ?? item.date
+ }));
+
await this.gatherSymbols({
dataGatheringItems,
priority: DATA_GATHERING_QUEUE_PRIORITY_HIGH
From 5d65906722fa060f5b038005cd93e7ef4be7d8ae Mon Sep 17 00:00:00 2001
From: David Requeno <108202767+DavidReque@users.noreply.github.com>
Date: Sun, 5 Oct 2025 08:44:23 -0600
Subject: [PATCH 7/7] Task/set up Storybook stories for activities table
component (#5610)
* Set up Storybook stories for activities table component
* Update changelog
---
CHANGELOG.md | 1 +
.../activities-table.component.stories.ts | 471 ++++++++++++++++++
2 files changed, 472 insertions(+)
create mode 100644 libs/ui/src/lib/activities-table/activities-table.component.stories.ts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fecc9758e..ecb75a2a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added support for a date range query parameter in the data gathering endpoint
+- Added a _Storybook_ story for the activities table component
## 2.206.0 - 2025-10-04
diff --git a/libs/ui/src/lib/activities-table/activities-table.component.stories.ts b/libs/ui/src/lib/activities-table/activities-table.component.stories.ts
new file mode 100644
index 000000000..5e774730b
--- /dev/null
+++ b/libs/ui/src/lib/activities-table/activities-table.component.stories.ts
@@ -0,0 +1,471 @@
+import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
+import { GfSymbolPipe } from '@ghostfolio/client/pipes/symbol/symbol.pipe';
+
+import { CommonModule } from '@angular/common';
+import { MatButtonModule } from '@angular/material/button';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatMenuModule } from '@angular/material/menu';
+import { MatPaginatorModule } from '@angular/material/paginator';
+import { MatSortModule } from '@angular/material/sort';
+import { MatTableDataSource, MatTableModule } from '@angular/material/table';
+import { MatTooltipModule } from '@angular/material/tooltip';
+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 { GfActivityTypeComponent } from '../activity-type/activity-type.component';
+import { GfEntityLogoComponent } from '../entity-logo';
+import { GfNoTransactionsInfoComponent } from '../no-transactions-info/no-transactions-info.component';
+import { GfValueComponent } from '../value';
+import { GfActivitiesTableComponent } from './activities-table.component';
+
+const activities: Activity[] = [
+ {
+ accountId: '776bd1e9-b2f6-4f7e-933d-18756c2f0625',
+ accountUserId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ comment: null,
+ createdAt: new Date('2025-04-09T13:47:33.133Z'),
+ currency: 'USD',
+ date: new Date('2025-04-09T13:45:45.504Z'),
+ fee: 1,
+ id: 'a76968ff-80a4-4453-81ed-c3627dea3919',
+ isDraft: false,
+ quantity: 115,
+ symbolProfileId: '21746431-d612-4298-911c-3099b2a43003',
+ type: 'BUY',
+ unitPrice: 103.543,
+ updatedAt: new Date('2025-05-31T18:43:01.840Z'),
+ userId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ account: {
+ balance: 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',
+ platformId: '9da3a8a7-4795-43e3-a6db-ccb914189737',
+ updatedAt: new Date('2025-06-01T06:53:10.569Z'),
+ userId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ platform: {
+ id: '9da3a8a7-4795-43e3-a6db-ccb914189737',
+ name: 'Interactive Brokers',
+ url: 'https://interactivebrokers.com'
+ }
+ },
+ SymbolProfile: {
+ assetClass: 'EQUITY',
+ assetSubClass: 'ETF',
+ comment: null,
+ countries: [],
+ createdAt: new Date('2021-06-06T16:12:20.982Z'),
+ currency: 'USD',
+ cusip: '922042742',
+ dataSource: 'YAHOO',
+ figi: 'BBG000GM5FZ6',
+ figiComposite: 'BBG000GM5FZ6',
+ figiShareClass: 'BBG001T2YZG9',
+ holdings: [],
+ id: '21746431-d612-4298-911c-3099b2a43003',
+ isActive: true,
+ isin: 'US9220427424',
+ name: 'Vanguard Total World Stock Index Fund ETF Shares',
+ updatedAt: new Date('2025-10-01T20:09:39.500Z'),
+ scraperConfiguration: null,
+ sectors: [],
+ symbol: 'VT',
+ symbolMapping: {},
+ url: 'https://www.vanguard.com',
+ userId: null,
+ activitiesCount: 267,
+ dateOfFirstActivity: new Date('2018-05-31T16:00:00.000Z')
+ },
+ tags: [],
+ feeInAssetProfileCurrency: 1,
+ feeInBaseCurrency: 1,
+ unitPriceInAssetProfileCurrency: 103.543,
+ value: 11907.445,
+ valueInBaseCurrency: 11907.445
+ },
+ {
+ accountId: '776bd1e9-b2f6-4f7e-933d-18756c2f0625',
+ accountUserId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ comment: null,
+ createdAt: new Date('2024-08-07T13:40:39.103Z'),
+ currency: 'USD',
+ date: new Date('2024-08-07T13:38:06.289Z'),
+ fee: 2.97,
+ id: '0c2f4fbf-6edc-4adc-8f83-abf8148500ec',
+ isDraft: false,
+ quantity: 105,
+ symbolProfileId: '21746431-d612-4298-911c-3099b2a43003',
+ type: 'BUY',
+ unitPrice: 110.24,
+ updatedAt: new Date('2025-05-31T18:46:14.175Z'),
+ userId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ account: {
+ balance: 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',
+ platformId: '9da3a8a7-4795-43e3-a6db-ccb914189737',
+ updatedAt: new Date('2025-06-01T06:53:10.569Z'),
+ userId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ platform: {
+ id: '9da3a8a7-4795-43e3-a6db-ccb914189737',
+ name: 'Interactive Brokers',
+ url: 'https://interactivebrokers.com'
+ }
+ },
+ SymbolProfile: {
+ assetClass: 'EQUITY',
+ assetSubClass: 'ETF',
+ comment: null,
+ countries: [],
+ createdAt: new Date('2021-06-06T16:12:20.982Z'),
+ currency: 'USD',
+ cusip: '922042742',
+ dataSource: 'YAHOO',
+ figi: 'BBG000GM5FZ6',
+ figiComposite: 'BBG000GM5FZ6',
+ figiShareClass: 'BBG001T2YZG9',
+ holdings: [],
+ id: '21746431-d612-4298-911c-3099b2a43003',
+ isActive: true,
+ isin: 'US9220427424',
+ name: 'Vanguard Total World Stock Index Fund ETF Shares',
+ updatedAt: new Date('2025-10-01T20:09:39.500Z'),
+ scraperConfiguration: null,
+ sectors: [],
+ symbol: 'VT',
+ symbolMapping: {},
+ url: 'https://www.vanguard.com',
+ userId: null,
+ activitiesCount: 267,
+ dateOfFirstActivity: new Date('2018-05-31T16:00:00.000Z')
+ },
+ tags: [],
+ feeInAssetProfileCurrency: 2.97,
+ feeInBaseCurrency: 2.97,
+ unitPriceInAssetProfileCurrency: 110.24,
+ value: 11575.2,
+ valueInBaseCurrency: 11575.2
+ },
+ {
+ accountId: '776bd1e9-b2f6-4f7e-933d-18756c2f0625',
+ accountUserId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ comment: null,
+ createdAt: new Date('2024-03-12T15:15:21.217Z'),
+ currency: 'USD',
+ date: new Date('2024-03-12T15:14:38.597Z'),
+ fee: 45.29,
+ id: 'bfc92677-faf4-4d4f-9762-e0ec056525c2',
+ isDraft: false,
+ quantity: 167,
+ symbolProfileId: '888d4123-db9a-42f3-9775-01b1ae6f9092',
+ type: 'BUY',
+ unitPrice: 41.0596,
+ updatedAt: new Date('2025-05-31T18:49:54.064Z'),
+ userId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ account: {
+ balance: 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',
+ platformId: '9da3a8a7-4795-43e3-a6db-ccb914189737',
+ updatedAt: new Date('2025-06-01T06:53:10.569Z'),
+ userId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ platform: {
+ id: '9da3a8a7-4795-43e3-a6db-ccb914189737',
+ name: 'Interactive Brokers',
+ url: 'https://interactivebrokers.com'
+ }
+ },
+ SymbolProfile: {
+ assetClass: 'LIQUIDITY',
+ assetSubClass: 'CRYPTOCURRENCY',
+ comment: null,
+ countries: [],
+ createdAt: new Date('2024-03-12T15:15:21.217Z'),
+ currency: 'USD',
+ cusip: '463918102',
+ dataSource: 'YAHOO',
+ figi: 'BBG01KYQ6PV3',
+ figiComposite: 'BBG01KYQ6PV3',
+ figiShareClass: 'BBG01KYQ6QS5',
+ holdings: [],
+ id: '888d4123-db9a-42f3-9775-01b1ae6f9092',
+ isActive: true,
+ isin: 'CA4639181029',
+ name: 'iShares Bitcoin Trust',
+ updatedAt: new Date('2025-09-29T03:14:07.742Z'),
+ scraperConfiguration: null,
+ sectors: [],
+ symbol: 'IBIT',
+ symbolMapping: {},
+ url: 'https://www.ishares.com',
+ userId: null,
+ activitiesCount: 6,
+ dateOfFirstActivity: new Date('2024-01-01T08:00:00.000Z')
+ },
+ tags: [],
+ feeInAssetProfileCurrency: 45.29,
+ feeInBaseCurrency: 45.29,
+ unitPriceInAssetProfileCurrency: 41.0596,
+ value: 6856.9532,
+ valueInBaseCurrency: 6856.9532
+ },
+ {
+ accountId: '776bd1e9-b2f6-4f7e-933d-18756c2f0625',
+ accountUserId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ comment: null,
+ createdAt: new Date('2024-02-23T15:53:46.907Z'),
+ currency: 'USD',
+ date: new Date('2024-02-23T15:53:15.745Z'),
+ fee: 3,
+ id: '7c9ceb54-acb1-4850-bfb1-adb41c29fd6a',
+ isDraft: false,
+ quantity: 81,
+ symbolProfileId: '36effe43-7cb4-4e8b-b7ac-03ff65702cb9',
+ type: 'BUY',
+ unitPrice: 67.995,
+ updatedAt: new Date('2025-05-31T18:48:48.209Z'),
+ userId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ account: {
+ balance: 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',
+ platformId: '9da3a8a7-4795-43e3-a6db-ccb914189737',
+ updatedAt: new Date('2025-06-01T06:53:10.569Z'),
+ userId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ platform: {
+ id: '9da3a8a7-4795-43e3-a6db-ccb914189737',
+ name: 'Interactive Brokers',
+ url: 'https://interactivebrokers.com'
+ }
+ },
+ SymbolProfile: {
+ assetClass: 'FIXED_INCOME',
+ assetSubClass: 'BOND',
+ comment: 'No data',
+ countries: [],
+ createdAt: new Date('2022-04-13T20:05:47.301Z'),
+ currency: 'USD',
+ cusip: '92206C565',
+ dataSource: 'YAHOO',
+ figi: 'BBG00LWSF7T3',
+ figiComposite: 'BBG00LWSF7T3',
+ figiShareClass: 'BBG00LWSF8K0',
+ holdings: [],
+ id: '36effe43-7cb4-4e8b-b7ac-03ff65702cb9',
+ isActive: true,
+ isin: 'US92206C5655',
+ name: 'Vanguard Total World Bond ETF',
+ updatedAt: new Date('2025-10-02T06:02:56.314Z'),
+
+ sectors: [],
+ symbol: 'BNDW',
+ symbolMapping: {},
+ url: 'https://vanguard.com',
+ userId: null,
+ activitiesCount: 38,
+ dateOfFirstActivity: new Date('2022-04-13T20:05:48.742Z')
+ },
+ tags: [],
+ feeInAssetProfileCurrency: 3,
+ feeInBaseCurrency: 3,
+ unitPriceInAssetProfileCurrency: 67.995,
+ value: 5507.595,
+ valueInBaseCurrency: 5507.595
+ },
+ {
+ accountId: '776bd1e9-b2f6-4f7e-933d-18756c2f0625',
+ accountUserId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ comment: null,
+ createdAt: new Date('2023-01-11T14:35:22.325Z'),
+ currency: 'USD',
+ date: new Date('2023-01-11T14:34:55.174Z'),
+ fee: 7.38,
+ id: '3fe87b3f-78de-407a-bc02-4189b221051f',
+ isDraft: false,
+ quantity: 55,
+ symbolProfileId: '21746431-d612-4298-911c-3099b2a43003',
+ type: 'BUY',
+ unitPrice: 89.48,
+ updatedAt: new Date('2025-05-31T18:46:44.616Z'),
+ userId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ account: {
+ balance: 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',
+ platformId: '9da3a8a7-4795-43e3-a6db-ccb914189737',
+ updatedAt: new Date('2025-06-01T06:53:10.569Z'),
+ userId: '081aa387-487d-4438-83a4-3060eb2a016e',
+ platform: {
+ id: '9da3a8a7-4795-43e3-a6db-ccb914189737',
+ name: 'Interactive Brokers',
+ url: 'https://interactivebrokers.com'
+ }
+ },
+ SymbolProfile: {
+ assetClass: 'EQUITY',
+ assetSubClass: 'ETF',
+ comment: null,
+ countries: [],
+ createdAt: new Date('2021-06-06T16:12:20.982Z'),
+ currency: 'USD',
+ cusip: '922042742',
+ dataSource: 'YAHOO',
+ figi: 'BBG000GM5FZ6',
+ figiComposite: 'BBG000GM5FZ6',
+ figiShareClass: 'BBG001T2YZG9',
+ holdings: [],
+ id: '21746431-d612-4298-911c-3099b2a43003',
+ isActive: true,
+ isin: 'US9220427424',
+ name: 'Vanguard Total World Stock Index Fund ETF Shares',
+ updatedAt: new Date('2025-10-01T20:09:39.500Z'),
+ scraperConfiguration: null,
+ sectors: [],
+ symbol: 'VT',
+ symbolMapping: {},
+ url: 'https://www.vanguard.com',
+ userId: null,
+ activitiesCount: 267,
+ dateOfFirstActivity: new Date('2018-05-31T16:00:00.000Z')
+ },
+ tags: [],
+ feeInAssetProfileCurrency: 7.38,
+ feeInBaseCurrency: 7.38,
+ unitPriceInAssetProfileCurrency: 89.48,
+ value: 4921.4,
+ valueInBaseCurrency: 4921.4
+ }
+];
+
+const dataSource = new MatTableDataSource(activities);
+
+export default {
+ title: 'Activities Table',
+ component: GfActivitiesTableComponent,
+ decorators: [
+ moduleMetadata({
+ imports: [
+ CommonModule,
+ GfActivityTypeComponent,
+ GfEntityLogoComponent,
+ GfNoTransactionsInfoComponent,
+ GfSymbolPipe,
+ GfValueComponent,
+ IonIcon,
+ MatButtonModule,
+ MatCheckboxModule,
+ MatMenuModule,
+ MatPaginatorModule,
+ MatSortModule,
+ MatTableModule,
+ MatTooltipModule,
+ NgxSkeletonLoaderModule,
+ RouterModule.forChild([])
+ ],
+ providers: [NotificationService]
+ })
+ ]
+} as Meta;
+
+type Story = StoryObj;
+
+export const Loading: Story = {
+ args: {
+ baseCurrency: 'USD',
+ dataSource: undefined,
+ deviceType: 'desktop',
+ hasActivities: true,
+ hasPermissionToCreateActivity: false,
+ hasPermissionToDeleteActivity: false,
+ hasPermissionToExportActivities: false,
+ hasPermissionToOpenDetails: false,
+ locale: 'en-US',
+ pageIndex: 0,
+ pageSize: 10,
+ showAccountColumn: true,
+ showActions: false,
+ showCheckbox: false,
+ showNameColumn: true,
+ sortColumn: 'date',
+ sortDirection: 'desc',
+ sortDisabled: false,
+ totalItems: 0
+ }
+};
+
+export const Default: Story = {
+ args: {
+ baseCurrency: 'USD',
+ dataSource,
+ deviceType: 'desktop',
+ hasActivities: true,
+ hasPermissionToCreateActivity: false,
+ hasPermissionToDeleteActivity: false,
+ hasPermissionToExportActivities: false,
+ hasPermissionToOpenDetails: false,
+ locale: 'en-US',
+ pageIndex: 0,
+ pageSize: 10,
+ showAccountColumn: true,
+ showActions: false,
+ showCheckbox: false,
+ showNameColumn: true,
+ sortColumn: 'date',
+ sortDirection: 'desc',
+ sortDisabled: false,
+ totalItems: activities.length
+ }
+};
+
+export const Pagination: Story = {
+ args: {
+ baseCurrency: 'USD',
+ dataSource: new MatTableDataSource(
+ Array.from({ length: 50 }).map((_, i) => ({
+ ...(activities[i % activities.length] as Activity),
+ date: new Date(2025, 5, (i % 28) + 1),
+ id: `${i}`
+ }))
+ ),
+ deviceType: 'desktop',
+ hasActivities: true,
+ hasPermissionToCreateActivity: false,
+ hasPermissionToDeleteActivity: false,
+ hasPermissionToExportActivities: false,
+ hasPermissionToOpenDetails: false,
+ locale: 'en-US',
+ pageIndex: 0,
+ pageSize: 10,
+ showAccountColumn: true,
+ showActions: false,
+ showCheckbox: false,
+ showNameColumn: true,
+ sortColumn: 'date',
+ sortDirection: 'desc',
+ sortDisabled: false,
+ totalItems: 50
+ }
+};