From 33bbf8ccd7b3b383f45ffac6bdd7a6539025e959 Mon Sep 17 00:00:00 2001 From: David Requeno Date: Mon, 15 Sep 2025 23:08:36 -0600 Subject: [PATCH 1/6] Feature/extend public portfolio page with latest activities - Add latestActivities to PublicPortfolioResponse interface - Inject OrderService in PublicController - Query last 10 activities with proper filters (exclude drafts and EXCLUDE_FROM_ANALYSIS) - Apply RedactValuesInResponseInterceptor for data protection - Map Activity[] to public DTO format - Add Latest activities section to public page template - Reuse gf-activities-table component with public mode settings --- .../app/endpoints/public/public.controller.ts | 85 +++++++++++++++---- .../app/pages/public/public-page.component.ts | 5 +- .../src/app/pages/public/public-page.html | 27 ++++++ .../public-portfolio-response.interface.ts | 14 ++- 4 files changed, 111 insertions(+), 20 deletions(-) diff --git a/apps/api/src/app/endpoints/public/public.controller.ts b/apps/api/src/app/endpoints/public/public.controller.ts index 0175b6ce8..1478dd245 100644 --- a/apps/api/src/app/endpoints/public/public.controller.ts +++ b/apps/api/src/app/endpoints/public/public.controller.ts @@ -1,9 +1,3 @@ -import { AccessService } from '@ghostfolio/api/app/access/access.service'; -import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; -import { UserService } from '@ghostfolio/api/app/user/user.service'; -import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; -import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; -import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { getSum } from '@ghostfolio/common/helper'; import { PublicPortfolioResponse } from '@ghostfolio/common/interfaces'; @@ -21,18 +15,29 @@ import { REQUEST } from '@nestjs/core'; import { Big } from 'big.js'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; +import { RedactValuesInResponseInterceptor } from '../../../interceptors/redact-values-in-response/redact-values-in-response.interceptor'; +import { TransformDataSourceInResponseInterceptor } from '../../../interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; +import { ConfigurationService } from '../../../services/configuration/configuration.service'; +import { ExchangeRateDataService } from '../../../services/exchange-rate-data/exchange-rate-data.service'; +import { AccessService } from '../../access/access.service'; +import { OrderService } from '../../order/order.service'; +import { PortfolioService } from '../../portfolio/portfolio.service'; +import { UserService } from '../../user/user.service'; + @Controller('public') export class PublicController { public constructor( private readonly accessService: AccessService, private readonly configurationService: ConfigurationService, private readonly exchangeRateDataService: ExchangeRateDataService, + private readonly _orderService: OrderService, private readonly portfolioService: PortfolioService, @Inject(REQUEST) private readonly request: RequestWithUser, private readonly userService: UserService ) {} @Get(':accessId/portfolio') + @UseInterceptors(RedactValuesInResponseInterceptor) @UseInterceptors(TransformDataSourceInResponseInterceptor) public async getPublicPortfolio( @Param('accessId') accessId @@ -56,26 +61,69 @@ export class PublicController { hasDetails = user.subscription.type === 'Premium'; } + const detailsPromise = this.portfolioService.getDetails({ + impersonationId: access.userId, + userId: user.id, + withMarkets: true + }); + + const performance1dPromise = this.portfolioService.getPerformance({ + dateRange: '1d', + impersonationId: undefined, + userId: user.id + }); + + const performanceMaxPromise = this.portfolioService.getPerformance({ + dateRange: 'max', + impersonationId: undefined, + userId: user.id + }); + + const performanceYtdPromise = this.portfolioService.getPerformance({ + dateRange: 'ytd', + impersonationId: undefined, + userId: user.id + }); + + const latestActivitiesPromise = this._orderService.getOrders({ + includeDrafts: false, + take: 10, + sortColumn: 'date', + sortDirection: 'desc', + userCurrency: + this.request.user?.settings?.settings.baseCurrency ?? DEFAULT_CURRENCY, + userId: user.id, + withExcludedAccountsAndActivities: false + }); + const [ { createdAt, holdings, markets }, { performance: performance1d }, { performance: performanceMax }, { performance: performanceYtd } ] = await Promise.all([ - this.portfolioService.getDetails({ - impersonationId: access.userId, - userId: user.id, - withMarkets: true - }), - ...['1d', 'max', 'ytd'].map((dateRange) => { - return this.portfolioService.getPerformance({ - dateRange, - impersonationId: undefined, - userId: user.id - }); - }) + detailsPromise, + performance1dPromise, + performanceMaxPromise, + performanceYtdPromise ]); + const { activities } = await latestActivitiesPromise; + const latestActivities = activities.map((a) => { + return { + account: a.account + ? { name: a.account.name, currency: a.account.currency } + : undefined, + dataSource: a.SymbolProfile?.dataSource, + date: a.date, + name: a.SymbolProfile?.name ?? '', + quantity: a.quantity, + symbol: a.SymbolProfile?.symbol ?? '', + type: a.type, + unitPrice: a.unitPrice + }; + }); + Object.values(markets ?? {}).forEach((market) => { delete market.valueInBaseCurrency; }); @@ -83,6 +131,7 @@ export class PublicController { const publicPortfolioResponse: PublicPortfolioResponse = { createdAt, hasDetails, + latestActivities, markets, alias: access.alias, holdings: {}, diff --git a/apps/client/src/app/pages/public/public-page.component.ts b/apps/client/src/app/pages/public/public-page.component.ts index ea11dd25f..4a1a18fdb 100644 --- a/apps/client/src/app/pages/public/public-page.component.ts +++ b/apps/client/src/app/pages/public/public-page.component.ts @@ -1,4 +1,3 @@ -import { DataService } from '@ghostfolio/client/services/data.service'; import { UNKNOWN_KEY } from '@ghostfolio/common/config'; import { prettifySymbol } from '@ghostfolio/common/helper'; import { @@ -6,6 +5,7 @@ import { PublicPortfolioResponse } from '@ghostfolio/common/interfaces'; import { Market } from '@ghostfolio/common/types'; +import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table/activities-table.component'; import { GfHoldingsTableComponent } from '@ghostfolio/ui/holdings-table/holdings-table.component'; import { GfPortfolioProportionChartComponent } from '@ghostfolio/ui/portfolio-proportion-chart/portfolio-proportion-chart.component'; import { GfValueComponent } from '@ghostfolio/ui/value'; @@ -28,10 +28,13 @@ import { DeviceDetectorService } from 'ngx-device-detector'; import { EMPTY, Subject } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; +import { DataService } from '../../services/data.service'; + @Component({ host: { class: 'page' }, imports: [ CommonModule, + GfActivitiesTableComponent, GfHoldingsTableComponent, GfPortfolioProportionChartComponent, GfValueComponent, diff --git a/apps/client/src/app/pages/public/public-page.html b/apps/client/src/app/pages/public/public-page.html index 004149ca3..28f7f7a94 100644 --- a/apps/client/src/app/pages/public/public-page.html +++ b/apps/client/src/app/pages/public/public-page.html @@ -220,3 +220,30 @@ +@if (publicPortfolioDetails?.latestActivities?.length > 0) { +
+
+ + + Latest activities + + + + + +
+
+} diff --git a/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts b/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts index 7a98e3c8d..5800b965b 100644 --- a/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts @@ -1,5 +1,7 @@ +import { DataSource } from '@prisma/client'; + import { PortfolioDetails, PortfolioPosition } from '..'; -import { Market } from '../../types'; +import { AccountWithPlatform, Market } from '../../types'; export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 { alias?: string; @@ -23,6 +25,16 @@ export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 { | 'valueInPercentage' >; }; + latestActivities?: { + account?: Pick; + date: Date; + name: string; + quantity: number; + symbol: string; + type: string; + unitPrice: number; + dataSource: DataSource; + }[]; markets: { [key in Market]: Pick< PortfolioDetails['markets'][key], From e5af5f7fcb1ced6e2635b7bcf9e61d28893d7911 Mon Sep 17 00:00:00 2001 From: David Requeno Date: Mon, 15 Sep 2025 23:32:02 -0600 Subject: [PATCH 2/6] Fixes build error in workflow --- .../app/pages/public/public-page.component.ts | 19 +++++++++++++++++++ .../src/app/pages/public/public-page.html | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/apps/client/src/app/pages/public/public-page.component.ts b/apps/client/src/app/pages/public/public-page.component.ts index 4a1a18fdb..802ecec71 100644 --- a/apps/client/src/app/pages/public/public-page.component.ts +++ b/apps/client/src/app/pages/public/public-page.component.ts @@ -20,6 +20,7 @@ import { } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; +import { MatTableDataSource } from '@angular/material/table'; import { ActivatedRoute, Router } from '@angular/router'; import { AssetClass } from '@prisma/client'; import { StatusCodes } from 'http-status-codes'; @@ -66,6 +67,7 @@ export class GfPublicPageComponent implements OnInit { }; }; public publicPortfolioDetails: PublicPortfolioResponse; + public latestActivitiesDataSource: MatTableDataSource; public sectors: { [name: string]: { name: string; value: number }; }; @@ -108,6 +110,23 @@ export class GfPublicPageComponent implements OnInit { .subscribe((portfolioPublicDetails) => { this.publicPortfolioDetails = portfolioPublicDetails; + const latestActivitiesRows = ( + this.publicPortfolioDetails.latestActivities || [] + ).map((a) => { + return { + ...a, + SymbolProfile: { + name: a.name, + symbol: a.symbol, + dataSource: a.dataSource + } + }; + }); + + this.latestActivitiesDataSource = new MatTableDataSource( + latestActivitiesRows + ); + this.initializeAnalysisData(); this.changeDetectorRef.markForCheck(); diff --git a/apps/client/src/app/pages/public/public-page.html b/apps/client/src/app/pages/public/public-page.html index 28f7f7a94..1b3a346df 100644 --- a/apps/client/src/app/pages/public/public-page.html +++ b/apps/client/src/app/pages/public/public-page.html @@ -231,7 +231,7 @@ Date: Tue, 16 Sep 2025 23:22:48 -0600 Subject: [PATCH 3/6] refactoring --- .../app/endpoints/public/public.controller.ts | 84 +++++++------------ .../app/pages/public/public-page.component.ts | 17 +--- .../src/app/pages/public/public-page.html | 41 ++++----- .../public-portfolio-response.interface.ts | 21 ++--- 4 files changed, 61 insertions(+), 102 deletions(-) diff --git a/apps/api/src/app/endpoints/public/public.controller.ts b/apps/api/src/app/endpoints/public/public.controller.ts index 1478dd245..59f3f9732 100644 --- a/apps/api/src/app/endpoints/public/public.controller.ts +++ b/apps/api/src/app/endpoints/public/public.controller.ts @@ -30,7 +30,7 @@ export class PublicController { private readonly accessService: AccessService, private readonly configurationService: ConfigurationService, private readonly exchangeRateDataService: ExchangeRateDataService, - private readonly _orderService: OrderService, + private readonly orderService: OrderService, private readonly portfolioService: PortfolioService, @Inject(REQUEST) private readonly request: RequestWithUser, private readonly userService: UserService @@ -61,68 +61,48 @@ export class PublicController { hasDetails = user.subscription.type === 'Premium'; } - const detailsPromise = this.portfolioService.getDetails({ - impersonationId: access.userId, - userId: user.id, - withMarkets: true - }); - - const performance1dPromise = this.portfolioService.getPerformance({ - dateRange: '1d', - impersonationId: undefined, - userId: user.id - }); - - const performanceMaxPromise = this.portfolioService.getPerformance({ - dateRange: 'max', - impersonationId: undefined, - userId: user.id - }); - - const performanceYtdPromise = this.portfolioService.getPerformance({ - dateRange: 'ytd', - impersonationId: undefined, - userId: user.id - }); - - const latestActivitiesPromise = this._orderService.getOrders({ - includeDrafts: false, - take: 10, - sortColumn: 'date', - sortDirection: 'desc', - userCurrency: - this.request.user?.settings?.settings.baseCurrency ?? DEFAULT_CURRENCY, - userId: user.id, - withExcludedAccountsAndActivities: false - }); - const [ { createdAt, holdings, markets }, { performance: performance1d }, { performance: performanceMax }, { performance: performanceYtd } ] = await Promise.all([ - detailsPromise, - performance1dPromise, - performanceMaxPromise, - performanceYtdPromise + this.portfolioService.getDetails({ + impersonationId: access.userId, + userId: user.id, + withMarkets: true + }), + ...['1d', 'max', 'ytd'].map((dateRange) => { + return this.portfolioService.getPerformance({ + dateRange, + impersonationId: undefined, + userId: user.id + }); + }) ]); + const latestActivitiesPromise = this.orderService.getOrders({ + includeDrafts: false, + sortColumn: 'date', + sortDirection: 'desc', + take: 10, + userCurrency: user.settings?.settings.baseCurrency ?? DEFAULT_CURRENCY, + userId: user.id, + withExcludedAccountsAndActivities: false + }); + const { activities } = await latestActivitiesPromise; - const latestActivities = activities.map((a) => { - return { - account: a.account - ? { name: a.account.name, currency: a.account.currency } - : undefined, + const latestActivities = activities.map((a) => ({ + date: a.date, + quantity: a.quantity, + SymbolProfile: { dataSource: a.SymbolProfile?.dataSource, - date: a.date, name: a.SymbolProfile?.name ?? '', - quantity: a.quantity, - symbol: a.SymbolProfile?.symbol ?? '', - type: a.type, - unitPrice: a.unitPrice - }; - }); + symbol: a.SymbolProfile?.symbol ?? '' + }, + type: a.type, + unitPrice: a.unitPrice + })); Object.values(markets ?? {}).forEach((market) => { delete market.valueInBaseCurrency; diff --git a/apps/client/src/app/pages/public/public-page.component.ts b/apps/client/src/app/pages/public/public-page.component.ts index 802ecec71..4162a903e 100644 --- a/apps/client/src/app/pages/public/public-page.component.ts +++ b/apps/client/src/app/pages/public/public-page.component.ts @@ -57,6 +57,7 @@ export class GfPublicPageComponent implements OnInit { }; public defaultAlias = $localize`someone`; public deviceType: string; + public latestActivitiesDataSource: MatTableDataSource; public holdings: PublicPortfolioResponse['holdings'][string][]; public markets: { [key in Market]: { id: Market; valueInPercentage: number }; @@ -67,7 +68,6 @@ export class GfPublicPageComponent implements OnInit { }; }; public publicPortfolioDetails: PublicPortfolioResponse; - public latestActivitiesDataSource: MatTableDataSource; public sectors: { [name: string]: { name: string; value: number }; }; @@ -110,21 +110,8 @@ export class GfPublicPageComponent implements OnInit { .subscribe((portfolioPublicDetails) => { this.publicPortfolioDetails = portfolioPublicDetails; - const latestActivitiesRows = ( - this.publicPortfolioDetails.latestActivities || [] - ).map((a) => { - return { - ...a, - SymbolProfile: { - name: a.name, - symbol: a.symbol, - dataSource: a.dataSource - } - }; - }); - this.latestActivitiesDataSource = new MatTableDataSource( - latestActivitiesRows + this.publicPortfolioDetails.latestActivities || [] ); this.initializeAnalysisData(); diff --git a/apps/client/src/app/pages/public/public-page.html b/apps/client/src/app/pages/public/public-page.html index 1b3a346df..48719a234 100644 --- a/apps/client/src/app/pages/public/public-page.html +++ b/apps/client/src/app/pages/public/public-page.html @@ -203,24 +203,6 @@ } -
-
-

- Would you like to refine your - personal investment strategy? -

-

- Ghostfolio empowers you to keep track of your wealth. -

- -
-
- -@if (publicPortfolioDetails?.latestActivities?.length > 0) {
@@ -237,13 +219,26 @@ [hasPermissionToDeleteActivity]="false" [hasPermissionToExportActivities]="false" [hasPermissionToOpenDetails]="false" - [pageSize]="10" - [showActions]="false" - [showCheckbox]="false" - [showNameColumn]="true" + [pageSize]="maxSafeInteger" />
-} +
+
+

+ Would you like to refine your + personal investment strategy? +

+

+ Ghostfolio empowers you to keep track of your wealth. +

+ +
+
+ diff --git a/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts b/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts index 5800b965b..42e4081da 100644 --- a/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts @@ -1,7 +1,7 @@ -import { DataSource } from '@prisma/client'; +import type { DataSource, Order } from '@prisma/client'; import { PortfolioDetails, PortfolioPosition } from '..'; -import { AccountWithPlatform, Market } from '../../types'; +import { Market } from '../../types'; export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 { alias?: string; @@ -25,16 +25,13 @@ export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 { | 'valueInPercentage' >; }; - latestActivities?: { - account?: Pick; - date: Date; - name: string; - quantity: number; - symbol: string; - type: string; - unitPrice: number; - dataSource: DataSource; - }[]; + latestActivities: (Pick & { + SymbolProfile: { + dataSource: DataSource; + name: string; + symbol: string; + }; + })[]; markets: { [key in Market]: Pick< PortfolioDetails['markets'][key], From 0310a64948d5a629c5ae369505199fa7b220fbb6 Mon Sep 17 00:00:00 2001 From: David Requeno Date: Tue, 16 Sep 2025 23:50:17 -0600 Subject: [PATCH 4/6] refactoring --- apps/client/src/app/pages/public/public-page.component.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/client/src/app/pages/public/public-page.component.ts b/apps/client/src/app/pages/public/public-page.component.ts index 4162a903e..c7deff59a 100644 --- a/apps/client/src/app/pages/public/public-page.component.ts +++ b/apps/client/src/app/pages/public/public-page.component.ts @@ -1,3 +1,4 @@ +import type { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { UNKNOWN_KEY } from '@ghostfolio/common/config'; import { prettifySymbol } from '@ghostfolio/common/helper'; import { @@ -57,7 +58,8 @@ export class GfPublicPageComponent implements OnInit { }; public defaultAlias = $localize`someone`; public deviceType: string; - public latestActivitiesDataSource: MatTableDataSource; + public maxSafeInteger = Number.MAX_SAFE_INTEGER; + public latestActivitiesDataSource: MatTableDataSource; public holdings: PublicPortfolioResponse['holdings'][string][]; public markets: { [key in Market]: { id: Market; valueInPercentage: number }; @@ -111,7 +113,8 @@ export class GfPublicPageComponent implements OnInit { this.publicPortfolioDetails = portfolioPublicDetails; this.latestActivitiesDataSource = new MatTableDataSource( - this.publicPortfolioDetails.latestActivities || [] + (this.publicPortfolioDetails.latestActivities || + []) as unknown as Activity[] ); this.initializeAnalysisData(); From 4357bab4e9a131dbace8848aa38385fc8562c566 Mon Sep 17 00:00:00 2001 From: David Requeno Date: Thu, 18 Sep 2025 22:26:31 -0600 Subject: [PATCH 5/6] refactoring --- .../app/endpoints/public/public.controller.ts | 18 ++++++++++++------ .../app/pages/public/public-page.component.ts | 4 ++-- .../src/app/pages/public/public-page.html | 1 + .../public-portfolio-response.interface.ts | 19 +++++++++++-------- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/apps/api/src/app/endpoints/public/public.controller.ts b/apps/api/src/app/endpoints/public/public.controller.ts index 59f3f9732..eb7b5238c 100644 --- a/apps/api/src/app/endpoints/public/public.controller.ts +++ b/apps/api/src/app/endpoints/public/public.controller.ts @@ -93,15 +93,21 @@ export class PublicController { const { activities } = await latestActivitiesPromise; const latestActivities = activities.map((a) => ({ + account: a.account + ? { + currency: a.account.currency, + name: a.account.name, + platform: a.account.platform + } + : undefined, + currency: a.currency, date: a.date, quantity: a.quantity, - SymbolProfile: { - dataSource: a.SymbolProfile?.dataSource, - name: a.SymbolProfile?.name ?? '', - symbol: a.SymbolProfile?.symbol ?? '' - }, + SymbolProfile: a.SymbolProfile, type: a.type, - unitPrice: a.unitPrice + unitPrice: a.unitPrice, + value: a.value, + valueInBaseCurrency: a.valueInBaseCurrency })); Object.values(markets ?? {}).forEach((market) => { diff --git a/apps/client/src/app/pages/public/public-page.component.ts b/apps/client/src/app/pages/public/public-page.component.ts index c7deff59a..e7b24c2ab 100644 --- a/apps/client/src/app/pages/public/public-page.component.ts +++ b/apps/client/src/app/pages/public/public-page.component.ts @@ -58,12 +58,12 @@ export class GfPublicPageComponent implements OnInit { }; public defaultAlias = $localize`someone`; public deviceType: string; - public maxSafeInteger = Number.MAX_SAFE_INTEGER; - public latestActivitiesDataSource: MatTableDataSource; public holdings: PublicPortfolioResponse['holdings'][string][]; + public latestActivitiesDataSource: MatTableDataSource; public markets: { [key in Market]: { id: Market; valueInPercentage: number }; }; + public maxSafeInteger = Number.MAX_SAFE_INTEGER; public positions: { [symbol: string]: Pick & { value: number; diff --git a/apps/client/src/app/pages/public/public-page.html b/apps/client/src/app/pages/public/public-page.html index 48719a234..3bf14ae59 100644 --- a/apps/client/src/app/pages/public/public-page.html +++ b/apps/client/src/app/pages/public/public-page.html @@ -220,6 +220,7 @@ [hasPermissionToExportActivities]="false" [hasPermissionToOpenDetails]="false" [pageSize]="maxSafeInteger" + [showActions]="false" />
diff --git a/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts b/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts index 42e4081da..7c1014983 100644 --- a/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts @@ -1,7 +1,8 @@ -import type { DataSource, Order } from '@prisma/client'; +import type { Order } from '@prisma/client'; -import { PortfolioDetails, PortfolioPosition } from '..'; +import { EnhancedSymbolProfile, PortfolioDetails, PortfolioPosition } from '..'; import { Market } from '../../types'; +import type { AccountWithPlatform } from '../../types'; export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 { alias?: string; @@ -25,12 +26,14 @@ export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 { | 'valueInPercentage' >; }; - latestActivities: (Pick & { - SymbolProfile: { - dataSource: DataSource; - name: string; - symbol: string; - }; + latestActivities: (Pick< + Order, + 'currency' | 'date' | 'quantity' | 'type' | 'unitPrice' + > & { + account?: Pick; + SymbolProfile?: EnhancedSymbolProfile; + value: number; + valueInBaseCurrency: number; })[]; markets: { [key in Market]: Pick< From 6e7b7bd9e14d59e4051a62a63fde05e14991ac83 Mon Sep 17 00:00:00 2001 From: David Requeno Date: Thu, 18 Sep 2025 22:29:36 -0600 Subject: [PATCH 6/6] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6215adab..185ba04ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added the latest activities to the public page + ### Changed - Refreshed the cryptocurrencies list