diff --git a/apps/api/src/app/activities/delete-activities.dto.ts b/apps/api/src/app/activities/activities-filter.dto.ts similarity index 96% rename from apps/api/src/app/activities/delete-activities.dto.ts rename to apps/api/src/app/activities/activities-filter.dto.ts index cc5ac017c0..e5a22c0205 100644 --- a/apps/api/src/app/activities/delete-activities.dto.ts +++ b/apps/api/src/app/activities/activities-filter.dto.ts @@ -9,7 +9,7 @@ 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 DeleteActivitiesDto { +export class ActivitiesFilterDto { @IsOptional() @IsString() accounts?: string; diff --git a/apps/api/src/app/activities/activities.controller.ts b/apps/api/src/app/activities/activities.controller.ts index b74a4d9815..daade2a59a 100644 --- a/apps/api/src/app/activities/activities.controller.ts +++ b/apps/api/src/app/activities/activities.controller.ts @@ -41,8 +41,8 @@ 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 { DeleteActivitiesDto } from './delete-activities.dto'; import { GetActivitiesDto } from './get-activities.dto'; @Controller('activities') @@ -62,7 +62,7 @@ export class ActivitiesController { @UseInterceptors(TransformDataSourceInRequestInterceptor) public async deleteActivities( @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, - @Query() query: DeleteActivitiesDto + @Query() query: ActivitiesFilterDto ): Promise { if (impersonationId) { throw new HttpException( diff --git a/apps/api/src/app/activities/get-activities.dto.ts b/apps/api/src/app/activities/get-activities.dto.ts index cf5e064f18..0430a89c1d 100644 --- a/apps/api/src/app/activities/get-activities.dto.ts +++ b/apps/api/src/app/activities/get-activities.dto.ts @@ -2,9 +2,9 @@ import { Prisma } from '@prisma/client'; import { Type } from 'class-transformer'; import { IsIn, IsInt, IsOptional, Min } from 'class-validator'; -import { DeleteActivitiesDto } from './delete-activities.dto'; +import { ActivitiesFilterDto } from './activities-filter.dto'; -export class GetActivitiesDto extends DeleteActivitiesDto { +export class GetActivitiesDto extends ActivitiesFilterDto { @IsInt() @IsOptional() @Min(0) diff --git a/apps/api/src/app/export/export.controller.ts b/apps/api/src/app/export/export.controller.ts index c9a54cb5a0..f503088d42 100644 --- a/apps/api/src/app/export/export.controller.ts +++ b/apps/api/src/app/export/export.controller.ts @@ -4,7 +4,7 @@ import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interc import { ApiService } from '@ghostfolio/api/services/api/api.service'; import { getIntervalFromDateRange } from '@ghostfolio/common/calculation-helper'; import { ExportResponse } from '@ghostfolio/common/interfaces'; -import type { DateRange, RequestWithUser } from '@ghostfolio/common/types'; +import type { RequestWithUser } from '@ghostfolio/common/types'; import { Controller, @@ -16,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 { @@ -32,40 +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('range') dateRange?: DateRange, - @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 (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 }); 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/get-export.dto.ts b/apps/api/src/app/export/get-export.dto.ts new file mode 100644 index 0000000000..5fc3c81baa --- /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/libs/ui/src/lib/activities-table/activities-table.component.html b/libs/ui/src/lib/activities-table/activities-table.component.html index 1ff8496a9b..ffcc778321 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 a4e7403a7b..6fac6d1625 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)) ??