Browse Source

Merge 966bba7d3d into 82cee20f2d

pull/7937/merge
Gabriel Silva 16 hours ago
committed by GitHub
parent
commit
224b096532
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      CHANGELOG.md
  2. 11
      apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts
  3. 61
      libs/common/src/lib/helper.spec.ts
  4. 22
      libs/common/src/lib/helper.ts

6
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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Fixed
- Fixed the alignment of the average price in the chart of the holding detail dialog for custom asset profiles
## 3.74.0 - 2026-09-27 ## 3.74.0 - 2026-09-27
### Added ### Added

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

@ -9,6 +9,7 @@ import {
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { CreateOrderDto } from '@ghostfolio/common/dtos'; import { CreateOrderDto } from '@ghostfolio/common/dtos';
import { import {
alignLineChartItemsToDates,
DATE_FORMAT, DATE_FORMAT,
downloadAsFile, downloadAsFile,
getCountryName getCountryName
@ -68,6 +69,7 @@ import { SortDirection } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import { NavigationStart, Router, RouterModule } from '@angular/router'; import { NavigationStart, Router, RouterModule } from '@angular/router';
import { utc } from '@date-fns/utc';
import { IonIcon } from '@ionic/angular/standalone'; import { IonIcon } from '@ionic/angular/standalone';
import { MarketData, Tag } from '@prisma/client'; import { MarketData, Tag } from '@prisma/client';
import { isUUID } from 'class-validator'; import { isUUID } from 'class-validator';
@ -719,12 +721,19 @@ export class GfHoldingDetailDialogComponent implements OnInit {
this.historicalDataItems = this.marketDataItems.map( this.historicalDataItems = this.marketDataItems.map(
({ date, marketPrice }) => { ({ date, marketPrice }) => {
return { return {
date: format(date, DATE_FORMAT), date: format(date, DATE_FORMAT, { in: utc }),
value: marketPrice value: marketPrice
}; };
} }
); );
this.benchmarkDataItems = alignLineChartItemsToDates({
dates: this.historicalDataItems.map(({ date }) => {
return date;
}),
items: this.benchmarkDataItems
});
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
} }

61
libs/common/src/lib/helper.spec.ts

@ -3,6 +3,7 @@ import {
TAG_ID_EXCLUDE_FROM_ANALYSIS TAG_ID_EXCLUDE_FROM_ANALYSIS
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { import {
alignLineChartItemsToDates,
canApplyFiltersToAccess, canApplyFiltersToAccess,
extractNumberFromString, extractNumberFromString,
getCountryCodeFromCurrency, getCountryCodeFromCurrency,
@ -23,6 +24,66 @@ import {
import { UserSettings } from '@ghostfolio/common/interfaces'; import { UserSettings } from '@ghostfolio/common/interfaces';
describe('Helper', () => { describe('Helper', () => {
describe('Align line chart items to dates', () => {
it('Dates before the first item', () => {
expect(
alignLineChartItemsToDates({
dates: ['2025-01-02', '2025-01-03'],
items: [{ date: '2026-01-09', value: 100 }]
})
).toEqual([
{ date: '2025-01-02', value: null },
{ date: '2025-01-03', value: null }
]);
});
it('Dates on and after the items', () => {
expect(
alignLineChartItemsToDates({
dates: [
'2026-01-08',
'2026-01-09',
'2026-01-10',
'2026-01-12',
'2026-01-13'
],
items: [
{ date: '2026-01-09', value: 100 },
{ date: '2026-01-12', value: 110 }
]
})
).toEqual([
{ date: '2026-01-08', value: null },
{ date: '2026-01-09', value: 100 },
{ date: '2026-01-10', value: 100 },
{ date: '2026-01-12', value: 110 },
{ date: '2026-01-13', value: 110 }
]);
});
it('Null values', () => {
expect(
alignLineChartItemsToDates({
dates: ['2026-01-09', '2026-01-10', '2026-01-11'],
items: [
{ date: '2026-01-09', value: 100 },
{ date: '2026-01-10', value: null }
]
})
).toEqual([
{ date: '2026-01-09', value: 100 },
{ date: '2026-01-10', value: null },
{ date: '2026-01-11', value: null }
]);
});
it('No items', () => {
expect(
alignLineChartItemsToDates({ dates: ['2026-01-09'], items: [] })
).toEqual([{ date: '2026-01-09', value: null }]);
});
});
describe('Can apply filters to access', () => { describe('Can apply filters to access', () => {
it('An access of the model context protocol', () => { it('An access of the model context protocol', () => {
expect(canApplyFiltersToAccess({ type: 'MCP' })).toEqual(false); expect(canApplyFiltersToAccess({ type: 'MCP' })).toEqual(false);

22
libs/common/src/lib/helper.ts

@ -57,6 +57,7 @@ import {
AssetProfileIdentifier, AssetProfileIdentifier,
AssetProfileItem, AssetProfileItem,
Benchmark, Benchmark,
NullableLineChartItem,
PortfolioPosition, PortfolioPosition,
UserSettings UserSettings
} from './interfaces'; } from './interfaces';
@ -91,6 +92,27 @@ const USER_SETTINGS_KEYS_OF_AUTHENTICATED_USER: (keyof UserSettings)[] = [
const VALID_COUNTRY_CODES = new Set([...Object.keys(countries), 'EU']); const VALID_COUNTRY_CODES = new Set([...Object.keys(countries), 'EU']);
export function alignLineChartItemsToDates({
dates,
items
}: {
dates: string[];
items: NullableLineChartItem[];
}): NullableLineChartItem[] {
let index = -1;
return dates.map((date) => {
while (
index + 1 < items.length &&
!isAfter(parseISO(items[index + 1].date), parseISO(date))
) {
index++;
}
return { date, value: items[index]?.value ?? null };
});
}
export function applyAssetProfileOverrides<T extends Partial<SymbolProfile>>( export function applyAssetProfileOverrides<T extends Partial<SymbolProfile>>(
assetProfile: T, assetProfile: T,
assetProfileOverrides: AssetProfileOverrides | null assetProfileOverrides: AssetProfileOverrides | null

Loading…
Cancel
Save