From 031b3b68c7493383c7dcf27a68e40cc159c97a99 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 31 Jul 2026 21:38:03 +0200 Subject: [PATCH] Bugfix/deletion of activities to respect activity type and date range filter (#7492) * Respect activity type and date range filter * Update changelog --- CHANGELOG.md | 9 ++ .../app/activities/activities-filter.dto.ts | 43 ++++++++++ .../app/activities/activities.controller.ts | 83 ++++++++++--------- .../src/app/activities/activities.service.ts | 9 ++ .../src/app/activities/get-activities.dto.ts | 27 ++++++ apps/api/src/app/export/export.controller.ts | 39 ++++----- apps/api/src/app/export/export.service.ts | 6 ++ apps/api/src/app/export/get-export.dto.ts | 14 ++++ apps/client/src/app/app.component.ts | 5 +- .../activities/activities-page.component.ts | 33 +++++--- .../common/src/lib/calculation-helper.spec.ts | 24 +++++- libs/common/src/lib/calculation-helper.ts | 13 ++- libs/common/src/lib/config.ts | 14 ++++ libs/common/src/lib/types/date-range.type.ts | 12 +-- .../activities-table.component.html | 11 ++- .../activities-table.component.ts | 7 ++ libs/ui/src/lib/services/data.service.ts | 34 ++++++-- 17 files changed, 292 insertions(+), 91 deletions(-) create mode 100644 apps/api/src/app/activities/activities-filter.dto.ts create mode 100644 apps/api/src/app/activities/get-activities.dto.ts create mode 100644 apps/api/src/app/export/get-export.dto.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 83ff99150..1843d4627 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added support for the date range filter in the export functionality +- Added support for the date range filter on the portfolio activities page + ### Changed - Improved the style of the tabs in the account detail dialog on mobile @@ -19,10 +24,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the search functionality by trimming the query - Improved the log output in the search functionality of the _Yahoo Finance_ service for unsupported queries - Improved the performance of the property service by caching the properties in memory +- Improved the validation of the query parameters in the activities endpoints - Improved the language localization for German (`de`) ### Fixed +- Fixed the calendar year date range in time zones with a negative _UTC_ offset +- Fixed the deletion of activities to respect the activity type filter on the activities page (experimental) +- Fixed the deletion of activities to respect the date range filter on the activities page - Fixed the static portfolio analysis rule for a portfolio with no holdings: _Asset Class Cluster Risks_ (Equity) - Fixed the static portfolio analysis rule for a portfolio with no holdings: _Asset Class Cluster Risks_ (Fixed Income) - Fixed the static portfolio analysis rule for a portfolio with no holdings: _Currency Cluster Risks_ (Investment) diff --git a/apps/api/src/app/activities/activities-filter.dto.ts b/apps/api/src/app/activities/activities-filter.dto.ts new file mode 100644 index 000000000..e5a22c020 --- /dev/null +++ b/apps/api/src/app/activities/activities-filter.dto.ts @@ -0,0 +1,43 @@ +import { DATE_RANGES } from '@ghostfolio/common/config'; +import { DateRange } from '@ghostfolio/common/types'; + +import { Type as ActivityType } from '@prisma/client'; +import { Transform, TransformFnParams } from 'class-transformer'; +import { IsEnum, IsOptional, IsString, Matches } from 'class-validator'; +import { isString } from 'lodash'; + +// A named date range or a calendar year like '2024', '2023', '2022', etc. +const DATE_RANGE_PATTERN = new RegExp(`^(${DATE_RANGES.join('|')}|\\d{4})$`); + +export class ActivitiesFilterDto { + @IsOptional() + @IsString() + accounts?: string; + + @IsEnum(ActivityType, { each: true }) + @IsOptional() + @Transform(({ value }: TransformFnParams) => { + return isString(value) ? value.split(',') : value; + }) + activityTypes?: ActivityType[]; + + @IsOptional() + @IsString() + assetClasses?: string; + + @IsOptional() + @IsString() + dataSource?: string; + + @IsOptional() + @Matches(DATE_RANGE_PATTERN) + range?: DateRange; + + @IsOptional() + @IsString() + symbol?: string; + + @IsOptional() + @IsString() + tags?: string; +} diff --git a/apps/api/src/app/activities/activities.controller.ts b/apps/api/src/app/activities/activities.controller.ts index 21dabc639..daade2a59 100644 --- a/apps/api/src/app/activities/activities.controller.ts +++ b/apps/api/src/app/activities/activities.controller.ts @@ -18,7 +18,7 @@ import { ActivityResponse } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; -import type { DateRange, RequestWithUser } from '@ghostfolio/common/types'; +import type { RequestWithUser } from '@ghostfolio/common/types'; import { Body, @@ -29,7 +29,6 @@ import { HttpException, Inject, Param, - ParseIntPipe, Post, Put, Query, @@ -38,11 +37,13 @@ import { } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; -import { Order, Prisma, Type as ActivityType } from '@prisma/client'; +import { Order } from '@prisma/client'; import { parseISO } from 'date-fns'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; +import { ActivitiesFilterDto } from './activities-filter.dto'; import { ActivitiesService } from './activities.service'; +import { GetActivitiesDto } from './get-activities.dto'; @Controller('activities') export class ActivitiesController { @@ -60,22 +61,38 @@ export class ActivitiesController { @UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseInterceptors(TransformDataSourceInRequestInterceptor) public async deleteActivities( - @Query('accounts') filterByAccounts?: string, - @Query('assetClasses') filterByAssetClasses?: string, - @Query('dataSource') filterByDataSource?: string, - @Query('symbol') filterBySymbol?: string, - @Query('tags') filterByTags?: string + @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, + @Query() query: ActivitiesFilterDto ): Promise { + if (impersonationId) { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + + let endDate: Date; + let startDate: Date; + + if (query.range) { + ({ endDate, startDate } = getIntervalFromDateRange({ + dateRange: query.range + })); + } + const filters = this.apiService.buildFiltersFromQueryParams({ - filterByAccounts, - filterByAssetClasses, - filterByDataSource, - filterBySymbol, - filterByTags + filterByAccounts: query.accounts, + filterByAssetClasses: query.assetClasses, + filterByDataSource: query.dataSource, + filterBySymbol: query.symbol, + filterByTags: query.tags }); return this.activitiesService.deleteActivities({ + endDate, filters, + startDate, + types: query.activityTypes, userId: this.request.user.id }); } @@ -108,51 +125,41 @@ export class ActivitiesController { @UseInterceptors(TransformDataSourceInResponseInterceptor) public async getAllActivities( @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, - @Query('accounts') filterByAccounts?: string, - @Query('activityTypes') filterByTypes?: string, - @Query('assetClasses') filterByAssetClasses?: string, - @Query('dataSource') filterByDataSource?: string, - @Query('range') dateRange?: DateRange, - @Query('skip', new ParseIntPipe({ optional: true })) skip?: number, - @Query('sortColumn') sortColumn?: string, - @Query('sortDirection') sortDirection?: Prisma.SortOrder, - @Query('symbol') filterBySymbol?: string, - @Query('tags') filterByTags?: string, - @Query('take', new ParseIntPipe({ optional: true })) take?: number + @Query() query: GetActivitiesDto ): Promise { let endDate: Date; let startDate: Date; - if (dateRange) { - ({ endDate, startDate } = getIntervalFromDateRange({ dateRange })); + if (query.range) { + ({ endDate, startDate } = getIntervalFromDateRange({ + dateRange: query.range + })); } const filters = this.apiService.buildFiltersFromQueryParams({ - filterByAccounts, - filterByAssetClasses, - filterByDataSource, - filterBySymbol, - filterByTags + filterByAccounts: query.accounts, + filterByAssetClasses: query.assetClasses, + filterByDataSource: query.dataSource, + filterBySymbol: query.symbol, + filterByTags: query.tags }); const impersonationUserId = await this.impersonationService.validateImpersonationId(impersonationId); - const types = (filterByTypes?.split(',') as ActivityType[]) ?? []; - const userCurrency = this.request.user.settings.settings.baseCurrency; const { activities, count } = await this.activitiesService.getActivities({ endDate, filters, - skip, - sortColumn, - sortDirection, startDate, - take, - types, userCurrency, includeDrafts: true, + skip: query.skip, + sortColumn: query.sortColumn, + sortDirection: query.sortDirection, + take: query.take, + types: query.activityTypes, userId: impersonationUserId || this.request.user.id, withExcludedAccountsAndActivities: true }); diff --git a/apps/api/src/app/activities/activities.service.ts b/apps/api/src/app/activities/activities.service.ts index 7d6e822c5..da3e99312 100644 --- a/apps/api/src/app/activities/activities.service.ts +++ b/apps/api/src/app/activities/activities.service.ts @@ -361,14 +361,23 @@ export class ActivitiesService { } public async deleteActivities({ + endDate, filters, + startDate, + types, userId }: { + endDate?: Date; filters?: Filter[]; + startDate?: Date; + types?: ActivityType[]; userId: string; }): Promise { const { activities } = await this.getActivities({ + endDate, filters, + startDate, + types, userId, includeDrafts: true, userCurrency: undefined, diff --git a/apps/api/src/app/activities/get-activities.dto.ts b/apps/api/src/app/activities/get-activities.dto.ts new file mode 100644 index 000000000..0430a89c1 --- /dev/null +++ b/apps/api/src/app/activities/get-activities.dto.ts @@ -0,0 +1,27 @@ +import { Prisma } from '@prisma/client'; +import { Type } from 'class-transformer'; +import { IsIn, IsInt, IsOptional, Min } from 'class-validator'; + +import { ActivitiesFilterDto } from './activities-filter.dto'; + +export class GetActivitiesDto extends ActivitiesFilterDto { + @IsInt() + @IsOptional() + @Min(0) + @Type(() => Number) + skip?: number; + + @IsIn(Object.values(Prisma.OrderScalarFieldEnum)) + @IsOptional() + sortColumn?: keyof typeof Prisma.OrderScalarFieldEnum; + + @IsIn(['asc', 'desc'] as Prisma.SortOrder[]) + @IsOptional() + sortDirection?: Prisma.SortOrder; + + @IsInt() + @IsOptional() + @Min(0) + @Type(() => Number) + take?: number; +} diff --git a/apps/api/src/app/export/export.controller.ts b/apps/api/src/app/export/export.controller.ts index 4f4f4e6dd..f503088d4 100644 --- a/apps/api/src/app/export/export.controller.ts +++ b/apps/api/src/app/export/export.controller.ts @@ -2,6 +2,7 @@ import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard' import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor'; import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; import { ApiService } from '@ghostfolio/api/services/api/api.service'; +import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { ExportResponse } from '@ghostfolio/common/interfaces'; import type { RequestWithUser } from '@ghostfolio/common/types'; @@ -15,9 +16,9 @@ import { } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; -import { Type as ActivityType } from '@prisma/client'; import { ExportService } from './export.service'; +import { GetExportDto } from './get-export.dto'; @Controller('export') export class ExportController { @@ -31,30 +32,30 @@ export class ExportController { @UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseInterceptors(TransformDataSourceInRequestInterceptor) @UseInterceptors(TransformDataSourceInResponseInterceptor) - public async export( - @Query('accounts') filterByAccounts?: string, - @Query('activityIds') filterByActivityIds?: string, - @Query('activityTypes') filterByTypes?: string, - @Query('assetClasses') filterByAssetClasses?: string, - @Query('dataSource') filterByDataSource?: string, - @Query('symbol') filterBySymbol?: string, - @Query('tags') filterByTags?: string - ): Promise { - const activityIds = filterByActivityIds?.split(',') ?? []; - const activityTypes = (filterByTypes?.split(',') as ActivityType[]) ?? []; + public async export(@Query() query: GetExportDto): Promise { + let endDate: Date; + let startDate: Date; + + if (query.range) { + ({ endDate, startDate } = getIntervalFromDateRange({ + dateRange: query.range + })); + } const filters = this.apiService.buildFiltersFromQueryParams({ - filterByAccounts, - filterByAssetClasses, - filterByDataSource, - filterBySymbol, - filterByTags + filterByAccounts: query.accounts, + filterByAssetClasses: query.assetClasses, + filterByDataSource: query.dataSource, + filterBySymbol: query.symbol, + filterByTags: query.tags }); return this.exportService.export({ - activityIds, - activityTypes, + endDate, filters, + startDate, + activityIds: query.activityIds, + activityTypes: query.activityTypes, userId: this.request.user.id, userSettings: this.request.user.settings.settings }); diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts index a94479562..6e920553b 100644 --- a/apps/api/src/app/export/export.service.ts +++ b/apps/api/src/app/export/export.service.ts @@ -25,13 +25,17 @@ export class ExportService { public async export({ activityIds, activityTypes, + endDate, filters, + startDate, userId, userSettings }: { activityIds?: string[]; activityTypes?: ActivityType[]; + endDate?: Date; filters?: Filter[]; + startDate?: Date; userId: string; userSettings: UserSettings; }): Promise { @@ -41,7 +45,9 @@ export class ExportService { const platformsMap: { [platformId: string]: Platform } = {}; let { activities } = await this.activitiesService.getActivities({ + endDate, filters, + startDate, userId, includeDrafts: true, sortColumn: 'date', diff --git a/apps/api/src/app/export/get-export.dto.ts b/apps/api/src/app/export/get-export.dto.ts new file mode 100644 index 000000000..5fc3c81ba --- /dev/null +++ b/apps/api/src/app/export/get-export.dto.ts @@ -0,0 +1,14 @@ +import { ActivitiesFilterDto } from '@ghostfolio/api/app/activities/activities-filter.dto'; + +import { Transform, TransformFnParams } from 'class-transformer'; +import { IsOptional, IsUUID } from 'class-validator'; +import { isString } from 'lodash'; + +export class GetExportDto extends ActivitiesFilterDto { + @IsOptional() + @IsUUID(undefined, { each: true }) + @Transform(({ value }: TransformFnParams) => { + return isString(value) ? value.split(',') : value; + }) + activityIds?: string[]; +} diff --git a/apps/client/src/app/app.component.ts b/apps/client/src/app/app.component.ts index ff67daf5e..fd17bcd6e 100644 --- a/apps/client/src/app/app.component.ts +++ b/apps/client/src/app/app.component.ts @@ -135,7 +135,10 @@ export class GfAppComponent implements OnInit { this.currentSubRoute === internalRoutes.home.subRoutes?.holdings.path) || (this.currentRoute === internalRoutes.portfolio.path && - !this.currentSubRoute)) && + !this.currentSubRoute) || + (this.currentRoute === internalRoutes.portfolio.path && + this.currentSubRoute === + internalRoutes.portfolio.subRoutes?.activities.path)) && this.user?.settings?.viewMode !== 'ZEN' ) { this.hasPermissionToChangeDateRange = true; diff --git a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts index 3362dc5d5..87da2fb84 100644 --- a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts @@ -10,7 +10,6 @@ import { } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { internalRoutes } from '@ghostfolio/common/routes/routes'; -import { DateRange } from '@ghostfolio/common/types'; import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table'; import { GfFabComponent } from '@ghostfolio/ui/fab'; import { DataService } from '@ghostfolio/ui/services'; @@ -92,8 +91,14 @@ export class GfActivitiesPageComponent implements OnInit { .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((state) => { if (state?.user) { + const previousDateRange = this.getDateRange(); + this.updateUser(state.user); + if (previousDateRange !== this.getDateRange()) { + this.pageIndex = 0; + } + this.fetchActivities(); this.changeDetectorRef.markForCheck(); @@ -120,7 +125,11 @@ export class GfActivitiesPageComponent implements OnInit { protected onDeleteActivities() { this.dataService .deleteActivities({ - filters: this.userService.getFilters() + activityTypes: this.activityTypesFilter.length + ? this.activityTypesFilter + : undefined, + filters: this.userService.getFilters(), + range: this.getDateRange() }) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { @@ -159,7 +168,8 @@ export class GfActivitiesPageComponent implements OnInit { activityTypes: this.activityTypesFilter.length ? this.activityTypesFilter : undefined, - filters: this.userService.getFilters() + filters: this.userService.getFilters(), + range: this.getDateRange() }; } @@ -277,16 +287,13 @@ export class GfActivitiesPageComponent implements OnInit { this.dataSource = undefined; this.totalItems = undefined; - const dateRange = this.user?.settings?.dateRange; - const range = this.isCalendarYear(dateRange) ? dateRange : undefined; - this.dataService .fetchActivities({ - range, activityTypes: this.activityTypesFilter.length ? this.activityTypesFilter : undefined, filters: this.userService.getFilters(), + range: this.getDateRange(), skip: this.pageIndex * this.pageSize, sortColumn: this.sortColumn, sortDirection: this.sortDirection, @@ -311,12 +318,16 @@ export class GfActivitiesPageComponent implements OnInit { }); } - private isCalendarYear(dateRange?: DateRange) { - if (!dateRange) { - return false; + private getDateRange() { + const dateRange = this.user?.settings?.dateRange; + + // Omit the date ranges which do not apply to activities: '1d' spans today + // only, while 'max' would exclude drafts dated in the future + if (!dateRange || ['1d', 'max'].includes(dateRange)) { + return undefined; } - return /^\d{4}$/.test(dateRange); + return dateRange; } private updateUser(aUser: User) { diff --git a/libs/common/src/lib/calculation-helper.spec.ts b/libs/common/src/lib/calculation-helper.spec.ts index 69621ec0a..75a24edba 100644 --- a/libs/common/src/lib/calculation-helper.spec.ts +++ b/libs/common/src/lib/calculation-helper.spec.ts @@ -1,8 +1,30 @@ import { Big } from 'big.js'; +import { format } from 'date-fns'; -import { getAnnualizedPerformancePercent } from './calculation-helper'; +import { + getAnnualizedPerformancePercent, + getIntervalFromDateRange +} from './calculation-helper'; +import { DATE_FORMAT } from './helper'; describe('CalculationHelper', () => { + describe('interval from date range', () => { + it('Get interval of a calendar year', async () => { + const { endDate, startDate } = getIntervalFromDateRange({ + dateRange: '2024' + }); + + // The boundaries are expressed in the local time zone, therefore the + // calendar days must hold independently of the time zone the tests run in + expect(format(startDate, DATE_FORMAT)).toEqual('2023-12-31'); + expect(format(endDate, DATE_FORMAT)).toEqual('2024-12-31'); + + // The start date is exclusive, hence the first instant of the year is + // part of the interval + expect(startDate.getTime()).toEqual(new Date(2024, 0, 1).getTime() - 1); + }); + }); + describe('annualized performance percentage', () => { it('Get annualized performance', async () => { expect( diff --git a/libs/common/src/lib/calculation-helper.ts b/libs/common/src/lib/calculation-helper.ts index 2097fa52a..d49663d6c 100644 --- a/libs/common/src/lib/calculation-helper.ts +++ b/libs/common/src/lib/calculation-helper.ts @@ -7,6 +7,7 @@ import { startOfWeek, startOfYear, subDays, + subMilliseconds, subYears } from 'date-fns'; import { isFinite, isNumber } from 'lodash'; @@ -75,10 +76,16 @@ export function getIntervalFromDateRange(params: { break; case 'max': break; - default: + default: { // '2024', '2023', '2022', etc. - endDate = endOfYear(new Date(dateRange)); - startDate = max([startDate, new Date(dateRange)]); + const yearStartDate = new Date(Number(dateRange), 0, 1); + + // Derive the boundaries of the calendar year in the local time zone, as + // the consumers apply local time zone semantics. As the start date is + // exclusive, the last millisecond of the preceding year is used. + endDate = endOfYear(yearStartDate); + startDate = max([startDate, subMilliseconds(yearStartDate, 1)]); + } } return { endDate, startDate }; diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index 08fb99e28..bb4ace0c5 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -67,6 +67,20 @@ export const DATA_GATHERING_QUEUE_PRIORITY_MEDIUM = Math.round( DATA_GATHERING_QUEUE_PRIORITY_LOW / 2 ); +/** + * The named date ranges, complemented by the calendar years like '2024', + * '2023', '2022', etc. + */ +export const DATE_RANGES = [ + '1d', + '1y', + '5y', + 'max', + 'mtd', + 'wtd', + 'ytd' +] as const; + export const PORTFOLIO_SNAPSHOT_COMPUTATION_QUEUE = 'PORTFOLIO_SNAPSHOT_COMPUTATION_QUEUE'; export const PORTFOLIO_SNAPSHOT_COMPUTATION_QUEUE_PRIORITY_HIGH = 1; diff --git a/libs/common/src/lib/types/date-range.type.ts b/libs/common/src/lib/types/date-range.type.ts index 09fa3c15b..5c25cfe39 100644 --- a/libs/common/src/lib/types/date-range.type.ts +++ b/libs/common/src/lib/types/date-range.type.ts @@ -1,9 +1,3 @@ -export type DateRange = - | '1d' - | '1y' - | '5y' - | 'max' - | 'mtd' - | 'wtd' - | 'ytd' - | string; // '2024', '2023', '2022', etc. +import type { DATE_RANGES } from '../config'; + +export type DateRange = (typeof DATE_RANGES)[number] | string; // '2024', '2023', '2022', etc. diff --git a/libs/ui/src/lib/activities-table/activities-table.component.html b/libs/ui/src/lib/activities-table/activities-table.component.html index 1ff8496a9..ffcc77832 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.html +++ b/libs/ui/src/lib/activities-table/activities-table.component.html @@ -58,12 +58,19 @@ } diff --git a/libs/ui/src/lib/activities-table/activities-table.component.ts b/libs/ui/src/lib/activities-table/activities-table.component.ts index a4e7403a7..6fac6d162 100644 --- a/libs/ui/src/lib/activities-table/activities-table.component.ts +++ b/libs/ui/src/lib/activities-table/activities-table.component.ts @@ -289,6 +289,13 @@ export class GfActivitiesTableComponent implements AfterViewInit, OnInit { ); } + public canExportActivities() { + return ( + (this.dataSource()?.data.length ?? 0) > 0 && + this.hasPermissionToExportActivities + ); + } + public isExcludedFromAnalysis(activity: Activity) { return ( (activity.account && isAccountExcluded(activity.account)) ?? diff --git a/libs/ui/src/lib/services/data.service.ts b/libs/ui/src/lib/services/data.service.ts index 23c654ae6..870719702 100644 --- a/libs/ui/src/lib/services/data.service.ts +++ b/libs/ui/src/lib/services/data.service.ts @@ -329,8 +329,24 @@ export class DataService { return this.http.delete(`/api/v1/account-balance/${aId}`); } - public deleteActivities({ filters }: { filters?: Filter[] }) { - const params = this.buildFiltersAsQueryParams({ filters }); + public deleteActivities({ + activityTypes, + filters, + range + }: { + activityTypes?: string[]; + filters?: Filter[]; + range?: DateRange; + }) { + let params = this.buildFiltersAsQueryParams({ filters }); + + if (activityTypes?.length) { + params = params.append('activityTypes', activityTypes.join(',')); + } + + if (range) { + params = params.append('range', range); + } return this.http.delete('/api/v1/activities', { params }); } @@ -459,11 +475,13 @@ export class DataService { public fetchExport({ activityIds, activityTypes, - filters + filters, + range }: { activityIds?: string[]; activityTypes?: string[]; filters?: Filter[]; + range?: DateRange; } = {}) { let params = this.buildFiltersAsQueryParams({ filters }); @@ -475,6 +493,10 @@ export class DataService { params = params.append('activityTypes', activityTypes.join(',')); } + if (range) { + params = params.append('range', range); + } + return this.http.get('/api/v1/export', { params }); @@ -509,8 +531,7 @@ export class DataService { public fetchInfo(): InfoItem { const info = cloneDeep((window as any).info); const utmSource = window.localStorage.getItem('utm_source') as - | 'ios' - | 'trusted-web-activity'; + 'ios' | 'trusted-web-activity'; info.globalPermissions = filterGlobalPermissions( info.globalPermissions, @@ -949,8 +970,7 @@ export class DataService { public updateInfo() { this.http.get('/api/v1/info').subscribe((info) => { const utmSource = window.localStorage.getItem('utm_source') as - | 'ios' - | 'trusted-web-activity'; + 'ios' | 'trusted-web-activity'; info.globalPermissions = filterGlobalPermissions( info.globalPermissions,