Browse Source
Feature/support calendar year date ranges on activities page (#6597)
* Support calendar year date ranges on activities page
* Update changelog
pull/6599/head
Thomas Kaul
4 days ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with
46 additions and
24 deletions
-
CHANGELOG.md
-
apps/api/src/app/activities/activities.controller.ts
-
apps/api/src/app/admin/admin.controller.ts
-
apps/api/src/app/endpoints/benchmarks/benchmarks.controller.ts
-
apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
-
apps/api/src/app/portfolio/calculator/roai/portfolio-calculator.ts
-
apps/api/src/app/portfolio/portfolio.controller.ts
-
apps/api/src/app/portfolio/portfolio.service.ts
-
apps/client/src/app/pages/portfolio/activities/activities-page.component.ts
-
libs/common/src/lib/calculation-helper.ts
-
libs/ui/src/lib/treemap-chart/treemap-chart.component.ts
|
|
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 |
|
|
|
|
|
|
|
## Unreleased |
|
|
|
|
|
|
|
### Added |
|
|
|
|
|
|
|
- Added support for specific calendar year date ranges (`2025`, `2024`, `2023`, etc.) on the portfolio activities page |
|
|
|
|
|
|
|
### Changed |
|
|
|
|
|
|
|
- Consolidated the sign-out logic within the user service to unify cookie, state and token clearance |
|
|
|
|
|
|
|
@ -126,7 +126,7 @@ export class ActivitiesController { |
|
|
|
let startDate: Date; |
|
|
|
|
|
|
|
if (dateRange) { |
|
|
|
({ endDate, startDate } = getIntervalFromDateRange(dateRange)); |
|
|
|
({ endDate, startDate } = getIntervalFromDateRange({ dateRange })); |
|
|
|
} |
|
|
|
|
|
|
|
const filters = this.apiService.buildFiltersFromQueryParams({ |
|
|
|
|
|
|
|
@ -172,7 +172,7 @@ export class AdminController { |
|
|
|
let date: Date; |
|
|
|
|
|
|
|
if (dateRange) { |
|
|
|
const { startDate } = getIntervalFromDateRange(dateRange); |
|
|
|
const { startDate } = getIntervalFromDateRange({ dateRange }); |
|
|
|
date = startDate; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -126,10 +126,10 @@ export class BenchmarksController { |
|
|
|
@Query('tags') filterByTags?: string, |
|
|
|
@Query('withExcludedAccounts') withExcludedAccountsParam = 'false' |
|
|
|
): Promise<BenchmarkMarketDataDetailsResponse> { |
|
|
|
const { endDate, startDate } = getIntervalFromDateRange( |
|
|
|
const { endDate, startDate } = getIntervalFromDateRange({ |
|
|
|
dateRange, |
|
|
|
new Date(startDateString) |
|
|
|
); |
|
|
|
startDate: new Date(startDateString) |
|
|
|
}); |
|
|
|
|
|
|
|
const filters = this.apiService.buildFiltersFromQueryParams({ |
|
|
|
filterByAccounts, |
|
|
|
|
|
|
|
@ -158,10 +158,10 @@ export abstract class PortfolioCalculator { |
|
|
|
this.redisCacheService = redisCacheService; |
|
|
|
this.userId = userId; |
|
|
|
|
|
|
|
const { endDate, startDate } = getIntervalFromDateRange( |
|
|
|
'max', |
|
|
|
subDays(dateOfFirstActivity, 1) |
|
|
|
); |
|
|
|
const { endDate, startDate } = getIntervalFromDateRange({ |
|
|
|
dateRange: 'max', |
|
|
|
startDate: subDays(dateOfFirstActivity, 1) |
|
|
|
}); |
|
|
|
|
|
|
|
this.endDate = endOfDay(endDate); |
|
|
|
this.startDate = startOfDay(startDate); |
|
|
|
@ -885,7 +885,7 @@ export abstract class PortfolioCalculator { |
|
|
|
// Make sure some key dates are present
|
|
|
|
for (const dateRange of ['1d', '1y', '5y', 'max', 'mtd', 'wtd', 'ytd']) { |
|
|
|
const { endDate: dateRangeEnd, startDate: dateRangeStart } = |
|
|
|
getIntervalFromDateRange(dateRange); |
|
|
|
getIntervalFromDateRange({ dateRange }); |
|
|
|
|
|
|
|
if ( |
|
|
|
!isBefore(dateRangeStart, startDate) && |
|
|
|
|
|
|
|
@ -860,7 +860,7 @@ export class RoaiPortfolioCalculator extends PortfolioCalculator { |
|
|
|
return format(date, 'yyyy'); |
|
|
|
}) |
|
|
|
] as DateRange[]) { |
|
|
|
const dateInterval = getIntervalFromDateRange(dateRange); |
|
|
|
const dateInterval = getIntervalFromDateRange({ dateRange }); |
|
|
|
const endDate = dateInterval.endDate; |
|
|
|
let startDate = dateInterval.startDate; |
|
|
|
|
|
|
|
|
|
|
|
@ -320,7 +320,7 @@ export class PortfolioController { |
|
|
|
await this.impersonationService.validateImpersonationId(impersonationId); |
|
|
|
const userCurrency = this.request.user.settings.settings.baseCurrency; |
|
|
|
|
|
|
|
const { endDate, startDate } = getIntervalFromDateRange(dateRange); |
|
|
|
const { endDate, startDate } = getIntervalFromDateRange({ dateRange }); |
|
|
|
|
|
|
|
const { activities } = await this.activitiesService.getActivities({ |
|
|
|
endDate, |
|
|
|
|
|
|
|
@ -403,7 +403,7 @@ export class PortfolioService { |
|
|
|
const user = await this.userService.user({ id: userId }); |
|
|
|
const userCurrency = this.getUserCurrency(user); |
|
|
|
|
|
|
|
const { endDate, startDate } = getIntervalFromDateRange(dateRange); |
|
|
|
const { endDate, startDate } = getIntervalFromDateRange({ dateRange }); |
|
|
|
|
|
|
|
const { activities } = |
|
|
|
await this.activitiesService.getActivitiesForPortfolioCalculator({ |
|
|
|
@ -1047,7 +1047,7 @@ export class PortfolioService { |
|
|
|
const { errors, hasErrors, historicalData } = |
|
|
|
await portfolioCalculator.getSnapshot(); |
|
|
|
|
|
|
|
const { endDate, startDate } = getIntervalFromDateRange(dateRange); |
|
|
|
const { endDate, startDate } = getIntervalFromDateRange({ dateRange }); |
|
|
|
|
|
|
|
const { chart } = await portfolioCalculator.getPerformance({ |
|
|
|
end: endDate, |
|
|
|
|
|
|
|
@ -10,6 +10,7 @@ import { |
|
|
|
User |
|
|
|
} from '@ghostfolio/common/interfaces'; |
|
|
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; |
|
|
|
import { DateRange } from '@ghostfolio/common/types'; |
|
|
|
import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table'; |
|
|
|
import { DataService } from '@ghostfolio/ui/services'; |
|
|
|
|
|
|
|
@ -129,8 +130,13 @@ export class GfActivitiesPageComponent implements OnDestroy, OnInit { |
|
|
|
} |
|
|
|
|
|
|
|
public fetchActivities() { |
|
|
|
const dateRange = this.user?.settings?.dateRange; |
|
|
|
|
|
|
|
const range = this.isCalendarYear(dateRange) ? dateRange : undefined; |
|
|
|
|
|
|
|
this.dataService |
|
|
|
.fetchActivities({ |
|
|
|
range, |
|
|
|
filters: this.userService.getFilters(), |
|
|
|
skip: this.pageIndex * this.pageSize, |
|
|
|
sortColumn: this.sortColumn, |
|
|
|
@ -352,6 +358,14 @@ export class GfActivitiesPageComponent implements OnDestroy, OnInit { |
|
|
|
this.unsubscribeSubject.complete(); |
|
|
|
} |
|
|
|
|
|
|
|
private isCalendarYear(dateRange: DateRange) { |
|
|
|
if (!dateRange) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
return /^\d{4}$/.test(dateRange); |
|
|
|
} |
|
|
|
|
|
|
|
private openCreateActivityDialog(aActivity?: Activity) { |
|
|
|
this.userService |
|
|
|
.get() |
|
|
|
|
|
|
|
@ -36,14 +36,16 @@ export function getAnnualizedPerformancePercent({ |
|
|
|
return new Big(0); |
|
|
|
} |
|
|
|
|
|
|
|
export function getIntervalFromDateRange( |
|
|
|
aDateRange: DateRange, |
|
|
|
portfolioStart = new Date(0) |
|
|
|
) { |
|
|
|
let endDate = endOfDay(new Date()); |
|
|
|
let startDate = portfolioStart; |
|
|
|
export function getIntervalFromDateRange(params: { |
|
|
|
dateRange: DateRange; |
|
|
|
endDate?: Date; |
|
|
|
startDate?: Date; |
|
|
|
}) { |
|
|
|
const { dateRange } = params; |
|
|
|
let endDate = params.endDate ?? endOfDay(new Date()); |
|
|
|
let startDate = params.startDate ?? new Date(0); |
|
|
|
|
|
|
|
switch (aDateRange) { |
|
|
|
switch (dateRange) { |
|
|
|
case '1d': |
|
|
|
startDate = max([startDate, subDays(resetHours(new Date()), 1)]); |
|
|
|
break; |
|
|
|
@ -75,8 +77,8 @@ export function getIntervalFromDateRange( |
|
|
|
break; |
|
|
|
default: |
|
|
|
// '2024', '2023', '2022', etc.
|
|
|
|
endDate = endOfYear(new Date(aDateRange)); |
|
|
|
startDate = max([startDate, new Date(aDateRange)]); |
|
|
|
endDate = endOfYear(new Date(dateRange)); |
|
|
|
startDate = max([startDate, new Date(dateRange)]); |
|
|
|
} |
|
|
|
|
|
|
|
return { endDate, startDate }; |
|
|
|
|
|
|
|
@ -162,7 +162,9 @@ export class GfTreemapChartComponent |
|
|
|
private initialize() { |
|
|
|
this.isLoading = true; |
|
|
|
|
|
|
|
const { endDate, startDate } = getIntervalFromDateRange(this.dateRange); |
|
|
|
const { endDate, startDate } = getIntervalFromDateRange({ |
|
|
|
dateRange: this.dateRange |
|
|
|
}); |
|
|
|
|
|
|
|
const netPerformancePercentsWithCurrencyEffect = this.holdings.map( |
|
|
|
({ dateOfFirstActivity, netPerformancePercentWithCurrencyEffect }) => { |
|
|
|
|