From 827d0fe6c5878c59dc9523453f652b8ed14be6e1 Mon Sep 17 00:00:00 2001 From: OlteanuCosmin Date: Fri, 31 Mar 2023 13:09:00 +0300 Subject: [PATCH 1/3] feat: [TEO-454] download event RSVPs --- .../event/dto/get-paginated-event-rsvp.dto.ts | 4 ++- backend/src/api/event/event.controller.ts | 25 +++++++++++++- .../event-rsvp-download.interface.ts | 6 ++++ .../get-many-for-download-rsvp.usecase.ts | 33 +++++++++++++++++++ backend/src/usecases/use-case.module.ts | 11 ++++--- .../src/assets/locales/ro/translation.json | 4 +-- frontend/src/pages/Event.tsx | 20 +++++++++-- frontend/src/services/event/event.api.ts | 26 +++++++++++++++ 8 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 backend/src/modules/event/interfaces/event-rsvp-download.interface.ts create mode 100644 backend/src/usecases/event/RSVP/get-many-for-download-rsvp.usecase.ts diff --git a/backend/src/api/event/dto/get-paginated-event-rsvp.dto.ts b/backend/src/api/event/dto/get-paginated-event-rsvp.dto.ts index c07efeb5..a4bcbe22 100644 --- a/backend/src/api/event/dto/get-paginated-event-rsvp.dto.ts +++ b/backend/src/api/event/dto/get-paginated-event-rsvp.dto.ts @@ -17,6 +17,8 @@ export class GetPaginatedEventRSVPsDto extends BasePaginationFilterDto { @IsOptional() @IsBoolean() - @Transform(({ value }) => value === 'true') + @Transform(({ value }) => { + return value === 'true' || value === true || value === 1 || value === '1'; + }) going?: boolean; } diff --git a/backend/src/api/event/event.controller.ts b/backend/src/api/event/event.controller.ts index 3cae532f..1627e910 100644 --- a/backend/src/api/event/event.controller.ts +++ b/backend/src/api/event/event.controller.ts @@ -40,6 +40,8 @@ import { EventListItemPresenter } from './presenters/event-list-item.presenter'; import { EventPresenter } from './presenters/event.presenter'; import { Response } from 'express'; import { GetManyForDownloadEventUseCase } from 'src/usecases/event/get-many-for-download-event.usecase'; +import { GetManyForDownloadEventRSVPUseCase } from 'src/usecases/event/RSVP/get-many-for-download-rsvp.usecase'; +import { IEventRSVPDownload } from 'src/modules/event/interfaces/event-rsvp-download.interface'; @ApiBearerAuth() @UseGuards(WebJwtAuthGuard, EventGuard) @@ -55,6 +57,7 @@ export class EventController { private readonly getManyEventRSVPUsecase: GetManyEventRSVPUseCase, private readonly getManyEventUseCase: GetManyEventUseCase, private readonly getManyForDownloadEventUseCase: GetManyForDownloadEventUseCase, + private readonly getManyForDownloadEventRSVPUseCase: GetManyForDownloadEventRSVPUseCase, ) {} @Get() @@ -80,7 +83,7 @@ export class EventController { 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ) @Header('Content-Disposition', 'attachment; filename="Evenimente.xlsx"') - async downloadAccessRequests( + async downloadEvents( @Res({ passthrough: true }) res: Response, @ExtractUser() user: IAdminUserModel, @Query() filters: GetManyEventDto, @@ -178,4 +181,24 @@ export class EventController { items: rsvps.items.map((rsvp) => new EventRSVPPresenter(rsvp)), }); } + + @ApiParam({ name: 'id', type: 'string' }) + @Get(':id/rsvp/download') + @Header( + 'Content-Type', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + ) + @Header('Content-Disposition', 'attachment; filename="Lista raspunsuri.xlsx"') + async downloadEventRSVPs( + @Param('id', UuidValidationPipe) eventId: string, + @Res({ passthrough: true }) res: Response, + @Query() filters: GetPaginatedEventRSVPsDto, + ): Promise { + const data = await this.getManyForDownloadEventRSVPUseCase.execute({ + ...filters, + eventId, + }); + + res.end(jsonToExcelBuffer(data, 'Lista raspunsuri')); + } } diff --git a/backend/src/modules/event/interfaces/event-rsvp-download.interface.ts b/backend/src/modules/event/interfaces/event-rsvp-download.interface.ts new file mode 100644 index 00000000..b7fe4c2f --- /dev/null +++ b/backend/src/modules/event/interfaces/event-rsvp-download.interface.ts @@ -0,0 +1,6 @@ +export interface IEventRSVPDownload { + Nume: string; + Raspuns: string; + 'Voluntar in organizatie': string; + Mentiune?: string; +} diff --git a/backend/src/usecases/event/RSVP/get-many-for-download-rsvp.usecase.ts b/backend/src/usecases/event/RSVP/get-many-for-download-rsvp.usecase.ts new file mode 100644 index 00000000..6bd3403f --- /dev/null +++ b/backend/src/usecases/event/RSVP/get-many-for-download-rsvp.usecase.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { IUseCaseService } from 'src/common/interfaces/use-case-service.interface'; +import { IEventRSVPDownload } from 'src/modules/event/interfaces/event-rsvp-download.interface'; +import { GetManyEventRSVPUseCase } from './get-many-rsvp.usecase'; +import { FindManyEventRSVPOptions } from 'src/modules/event/models/event-rsvp.model'; + +@Injectable() +export class GetManyForDownloadEventRSVPUseCase + implements IUseCaseService +{ + constructor( + private readonly getManyEventRSVPUseCase: GetManyEventRSVPUseCase, + ) {} + + public async execute( + findOptions: FindManyEventRSVPOptions, + ): Promise { + const eventRSVPs = await this.getManyEventRSVPUseCase.execute({ + ...findOptions, + limit: 0, + page: 0, + }); + + return eventRSVPs.items.map((rsvp): IEventRSVPDownload => { + return { + Nume: rsvp.user.name, + Raspuns: rsvp.going ? 'Participa' : 'Nu participa', + 'Voluntar in organizatie': rsvp.volunteerId ? 'Da' : 'Nu', + Mentiune: rsvp.mention, + }; + }); + } +} diff --git a/backend/src/usecases/use-case.module.ts b/backend/src/usecases/use-case.module.ts index 917b2449..94a83820 100644 --- a/backend/src/usecases/use-case.module.ts +++ b/backend/src/usecases/use-case.module.ts @@ -78,6 +78,7 @@ import { GetManyAdminUsersUseCase } from './user/get-many-admin-users.usecase'; import { ActionsArchiveModule } from 'src/modules/actions-archive/actions-archive.module'; import { GetManyActionsArchiveUseCase } from './actions-archive/get-many-actions-archive.usecase'; import { GetActivityLogCountersUsecase } from './activity-log/get-activity-log-counters.usecase'; +import { GetManyForDownloadEventRSVPUseCase } from './event/RSVP/get-many-for-download-rsvp.usecase'; @Module({ imports: [ @@ -158,13 +159,14 @@ import { GetActivityLogCountersUsecase } from './activity-log/get-activity-log-c DeleteEventUseCase, PublishEventUseCase, ArchiveEventUseCase, + GetManyForDownloadEventUseCase, + GetManyEventUseCase, // Events RSVP CreateEventRSVPUseCase, GetOneEventRSVPUseCase, DeleteEventRSVPUseCase, GetManyEventRSVPUseCase, - GetManyEventUseCase, - GetManyForDownloadEventUseCase, + GetManyForDownloadEventRSVPUseCase, // Activity Log CreateActivityLogByAdmin, GetOneActivityLogUsecase, @@ -241,13 +243,14 @@ import { GetActivityLogCountersUsecase } from './activity-log/get-activity-log-c DeleteEventUseCase, PublishEventUseCase, ArchiveEventUseCase, + GetManyEventUseCase, + GetManyForDownloadEventUseCase, // Events RSVP CreateEventRSVPUseCase, GetOneEventRSVPUseCase, DeleteEventRSVPUseCase, GetManyEventRSVPUseCase, - GetManyEventUseCase, - GetManyForDownloadEventUseCase, + GetManyForDownloadEventRSVPUseCase, // Activity Log CreateActivityLogByAdmin, GetOneActivityLogUsecase, diff --git a/frontend/src/assets/locales/ro/translation.json b/frontend/src/assets/locales/ro/translation.json index 97350d4b..9a4ce9d4 100644 --- a/frontend/src/assets/locales/ro/translation.json +++ b/frontend/src/assets/locales/ro/translation.json @@ -8,7 +8,6 @@ "save": "Salveaza", "close": "Inchide", "name": "Nume", - "save": "Salvează", "people": "Persoane", "age": "Vârsta", "hours": "Ore", @@ -488,7 +487,8 @@ "EVENT_006": "Doar evenimentele publicate pot fi arhivate" }, "download_open": "Evenimente active.xlsx", - "download_past": "Evenimente incheiate.xlsx" + "download_past": "Evenimente incheiate.xlsx", + "download_rsvp": "Lista raspunsuri.xlsx" }, "announcement": { "title": "Istoric anunturi", diff --git a/frontend/src/pages/Event.tsx b/frontend/src/pages/Event.tsx index 77749871..0a848aad 100644 --- a/frontend/src/pages/Event.tsx +++ b/frontend/src/pages/Event.tsx @@ -26,7 +26,7 @@ import CardBody from '../components/CardBody'; import FormLayout from '../layouts/FormLayout'; import Paragraph from '../components/Paragraph'; import FormReadOnlyElement from '../components/FormReadOnlyElement'; -import { formatDateWithTime } from '../common/utils/utils'; +import { downloadExcel, formatDateWithTime } from '../common/utils/utils'; import LoadingContent from '../components/LoadingContent'; import EmptyContent from '../components/EmptyContent'; import { useErrorToast, useSuccessToast } from '../hooks/useToast'; @@ -44,6 +44,7 @@ import DataTableFilters from '../components/DataTableFilters'; import OrganizationStructureSelect from '../containers/OrganizationStructureSelect'; import { DivisionType } from '../common/enums/division-type.enum'; import { RsvpEnum } from '../common/enums/rsvp.enum'; +import { getEventRSVPsForDownload } from '../services/event/event.api'; enum EventTab { EVENT = 'event', @@ -348,6 +349,21 @@ const Event = () => { setGoing(undefined); }; + const onExportRSVPs = async () => { + const { data: eventRSVPsData } = await getEventRSVPsForDownload( + id as string, + orderByColumn, + orderDirection, + search, + branch?.key, + department?.key, + role?.key, + going?.key, + ); + + downloadExcel(eventRSVPsData as BlobPart, i18n.t('events:download_rsvp')); + }; + return ( {i18n.t('general:view')} @@ -445,7 +461,7 @@ const Event = () => { label={i18n.t('general:download_table')} icon={} className="btn-outline-secondary ml-auto" - onClick={() => alert('Not implemented')} + onClick={onExportRSVPs} /> diff --git a/frontend/src/services/event/event.api.ts b/frontend/src/services/event/event.api.ts index e808867d..df6478ad 100644 --- a/frontend/src/services/event/event.api.ts +++ b/frontend/src/services/event/event.api.ts @@ -88,6 +88,32 @@ export const getRsvps = async ( }).then((res) => res.data); }; +export const getEventRSVPsForDownload = async ( + id: string, + orderBy?: string, + orderDirection?: OrderDirection, + search?: string, + branchId?: string, + departmentId?: string, + roleId?: string, + going?: string, +): Promise<{ data: unknown; headers: AxiosResponseHeaders }> => { + return API.get(`event/${id}/rsvp/download`, { + params: { + orderBy, + orderDirection, + search, + branchId, + departmentId, + roleId, + going: going === undefined ? going : going === RsvpEnum.GOING, + }, + responseType: 'arraybuffer', + }).then((res) => { + return { data: res.data, headers: res.headers as AxiosResponseHeaders }; + }); +}; + const formatAddEventPayload = (data: EventFormTypes): object => { const { targets, tasks, targetType, ...payload } = data; return { From 6ad31e93c9196a5761226f075d2f02c1d24c16b6 Mon Sep 17 00:00:00 2001 From: OlteanuCosmin Date: Fri, 31 Mar 2023 17:02:29 +0300 Subject: [PATCH 2/3] feat: [TEO-454] disable implicit type conversion, add @Type decorators in DTOs --- backend/src/api/_mobile/event/dto/create-rsvp.dto.ts | 2 ++ .../api/_mobile/user/dto/create-regular-user.dto.ts | 3 +++ .../volunteer/dto/create-volunteer-profile.dto.ts | 2 ++ .../api/access-request/dto/get-access-requests.dto.ts | 6 ++++++ .../actions-archive/dto/get-actions-history.dto.ts | 11 ++++------- .../dto/create-activity-log-by-admin.dto.ts | 3 +++ .../activity-log/dto/get-many-activity-logs.dto.ts | 5 +++++ backend/src/api/event/dto/create-event.dto.ts | 4 ++++ .../src/api/event/dto/get-paginated-event-rsvp.dto.ts | 4 +--- .../api/organization/dto/create-access-code.dto.ts | 3 +++ .../api/organization/dto/update-access-code.dto.ts | 2 ++ backend/src/api/volunteer/dto/get-volunteers.dto.ts | 4 ++++ backend/src/main.ts | 2 +- 13 files changed, 40 insertions(+), 11 deletions(-) diff --git a/backend/src/api/_mobile/event/dto/create-rsvp.dto.ts b/backend/src/api/_mobile/event/dto/create-rsvp.dto.ts index 9981eaa2..834f0e97 100644 --- a/backend/src/api/_mobile/event/dto/create-rsvp.dto.ts +++ b/backend/src/api/_mobile/event/dto/create-rsvp.dto.ts @@ -1,3 +1,4 @@ +import { Type } from 'class-transformer'; import { IsBoolean, IsOptional, @@ -9,6 +10,7 @@ import { export class EventRSVPDto { @IsBoolean() + @Type(() => Boolean) going: boolean; @IsString() diff --git a/backend/src/api/_mobile/user/dto/create-regular-user.dto.ts b/backend/src/api/_mobile/user/dto/create-regular-user.dto.ts index 8409b36a..f960d709 100644 --- a/backend/src/api/_mobile/user/dto/create-regular-user.dto.ts +++ b/backend/src/api/_mobile/user/dto/create-regular-user.dto.ts @@ -1,3 +1,4 @@ +import { Type } from 'class-transformer'; import { IsDate, IsEmail, @@ -26,11 +27,13 @@ export class CreateRegularUserDto { cognitoId: string; @IsDate() + @Type(() => Date) birthday: Date; @IsEnum(SEX) sex: SEX; @IsNumber() + @Type(() => Number) locationId: number; } diff --git a/backend/src/api/_mobile/volunteer/dto/create-volunteer-profile.dto.ts b/backend/src/api/_mobile/volunteer/dto/create-volunteer-profile.dto.ts index 9737d69b..b7d99dca 100644 --- a/backend/src/api/_mobile/volunteer/dto/create-volunteer-profile.dto.ts +++ b/backend/src/api/_mobile/volunteer/dto/create-volunteer-profile.dto.ts @@ -1,3 +1,4 @@ +import { Type } from 'class-transformer'; import { IsDate, IsEmail, @@ -18,6 +19,7 @@ export class CreateVolunteerProfileDto { @IsDate() @IsOptional() + @Type(() => Date) activeSince: Date; @IsUUID() diff --git a/backend/src/api/access-request/dto/get-access-requests.dto.ts b/backend/src/api/access-request/dto/get-access-requests.dto.ts index a33231e2..b997fe3b 100644 --- a/backend/src/api/access-request/dto/get-access-requests.dto.ts +++ b/backend/src/api/access-request/dto/get-access-requests.dto.ts @@ -1,3 +1,4 @@ +import { Type } from 'class-transformer'; import { IsDate, IsEnum, IsNumber, IsOptional } from 'class-validator'; import { BasePaginationFilterDto } from 'src/infrastructure/base/base-pagination-filter.dto'; import { AccessRequestStatus } from 'src/modules/access-request/enums/access-request-status.enum'; @@ -5,22 +6,27 @@ import { AccessRequestStatus } from 'src/modules/access-request/enums/access-req export class GetAccessRequestsDto extends BasePaginationFilterDto { @IsNumber() @IsOptional() + @Type(() => Number) locationId?: number; @IsOptional() @IsDate() + @Type(() => Date) createdOnStart?: Date; @IsOptional() @IsDate() + @Type(() => Date) createdOnEnd?: Date; @IsOptional() @IsDate() + @Type(() => Date) rejectedOnStart?: Date; @IsOptional() @IsDate() + @Type(() => Date) rejectedOnEnd?: Date; @IsOptional() diff --git a/backend/src/api/actions-archive/dto/get-actions-history.dto.ts b/backend/src/api/actions-archive/dto/get-actions-history.dto.ts index cdd8395d..61d8279b 100644 --- a/backend/src/api/actions-archive/dto/get-actions-history.dto.ts +++ b/backend/src/api/actions-archive/dto/get-actions-history.dto.ts @@ -1,10 +1,5 @@ -import { - IsDate, - IsEnum, - IsNumber, - IsOptional, - IsString, -} from 'class-validator'; +import { Type } from 'class-transformer'; +import { IsDate, IsOptional, IsString } from 'class-validator'; import { BasePaginationFilterDto } from 'src/infrastructure/base/base-pagination-filter.dto'; export class GetManyActionsArchiveDto extends BasePaginationFilterDto { @@ -14,9 +9,11 @@ export class GetManyActionsArchiveDto extends BasePaginationFilterDto { @IsDate() @IsOptional() + @Type(() => Date) actionStartDate?: Date; @IsDate() @IsOptional() + @Type(() => Date) actionEndDate?: Date; } diff --git a/backend/src/api/activity-log/dto/create-activity-log-by-admin.dto.ts b/backend/src/api/activity-log/dto/create-activity-log-by-admin.dto.ts index 5b16558a..64e1571e 100644 --- a/backend/src/api/activity-log/dto/create-activity-log-by-admin.dto.ts +++ b/backend/src/api/activity-log/dto/create-activity-log-by-admin.dto.ts @@ -1,3 +1,4 @@ +import { Type } from 'class-transformer'; import { IsDate, IsNumber, @@ -11,11 +12,13 @@ import { export class CreateActivityLogByAdminDto { @IsDate() + @Type(() => Date) date: Date; @IsNumber() @Min(1) @Max(1000) + @Type(() => Number) hours: number; @IsString() diff --git a/backend/src/api/activity-log/dto/get-many-activity-logs.dto.ts b/backend/src/api/activity-log/dto/get-many-activity-logs.dto.ts index d2692d1e..f44ec68d 100644 --- a/backend/src/api/activity-log/dto/get-many-activity-logs.dto.ts +++ b/backend/src/api/activity-log/dto/get-many-activity-logs.dto.ts @@ -1,3 +1,4 @@ +import { Type } from 'class-transformer'; import { IsDate, IsEnum, IsOptional, IsString } from 'class-validator'; import { BasePaginationFilterDto } from 'src/infrastructure/base/base-pagination-filter.dto'; import { ActivityLogResolutionStatus } from 'src/modules/activity-log/enums/activity-log-resolution-status.enum'; @@ -21,18 +22,22 @@ export class GetManyActivityLogsDto extends BasePaginationFilterDto { @IsDate() @IsOptional() + @Type(() => Date) executionDateStart?: Date; @IsDate() @IsOptional() + @Type(() => Date) executionDateEnd?: Date; @IsDate() @IsOptional() + @Type(() => Date) registrationDateStart?: Date; @IsDate() @IsOptional() + @Type(() => Date) registrationDateEnd?: Date; @IsString() diff --git a/backend/src/api/event/dto/create-event.dto.ts b/backend/src/api/event/dto/create-event.dto.ts index 245899b8..d8cfe568 100644 --- a/backend/src/api/event/dto/create-event.dto.ts +++ b/backend/src/api/event/dto/create-event.dto.ts @@ -1,3 +1,4 @@ +import { Type } from 'class-transformer'; import { IsArray, IsBoolean, @@ -31,16 +32,19 @@ export class CreateEventDto { location: string; @IsDate() + @Type(() => Date) startDate: Date; @IsDate() @IsOptional() + @Type(() => Date) endDate: Date; @IsIn([EventStatus.DRAFT, EventStatus.PUBLISHED]) status: EventStatus.DRAFT | EventStatus.PUBLISHED; @IsBoolean() + @Type(() => Boolean) isPublic: boolean; @IsEnum(EventAttendOptions) diff --git a/backend/src/api/event/dto/get-paginated-event-rsvp.dto.ts b/backend/src/api/event/dto/get-paginated-event-rsvp.dto.ts index a4bcbe22..c07efeb5 100644 --- a/backend/src/api/event/dto/get-paginated-event-rsvp.dto.ts +++ b/backend/src/api/event/dto/get-paginated-event-rsvp.dto.ts @@ -17,8 +17,6 @@ export class GetPaginatedEventRSVPsDto extends BasePaginationFilterDto { @IsOptional() @IsBoolean() - @Transform(({ value }) => { - return value === 'true' || value === true || value === 1 || value === '1'; - }) + @Transform(({ value }) => value === 'true') going?: boolean; } diff --git a/backend/src/api/organization/dto/create-access-code.dto.ts b/backend/src/api/organization/dto/create-access-code.dto.ts index 730ee5c4..65e846ea 100644 --- a/backend/src/api/organization/dto/create-access-code.dto.ts +++ b/backend/src/api/organization/dto/create-access-code.dto.ts @@ -1,3 +1,4 @@ +import { Type } from 'class-transformer'; import { IsDate, IsOptional, @@ -13,9 +14,11 @@ export class CreateAccessCodeDto { code: string; @IsDate() + @Type(() => Date) startDate: Date; @IsDate() @IsOptional() + @Type(() => Date) endDate?: Date; } diff --git a/backend/src/api/organization/dto/update-access-code.dto.ts b/backend/src/api/organization/dto/update-access-code.dto.ts index 734f03fd..cc77da81 100644 --- a/backend/src/api/organization/dto/update-access-code.dto.ts +++ b/backend/src/api/organization/dto/update-access-code.dto.ts @@ -1,7 +1,9 @@ +import { Type } from 'class-transformer'; import { IsDate, ValidateIf } from 'class-validator'; export class UpdateAccessCodeDto { @IsDate() @ValidateIf((_, value) => value !== null) + @Type(() => Date) endDate: Date | null; } diff --git a/backend/src/api/volunteer/dto/get-volunteers.dto.ts b/backend/src/api/volunteer/dto/get-volunteers.dto.ts index bd4786ab..2c54410c 100644 --- a/backend/src/api/volunteer/dto/get-volunteers.dto.ts +++ b/backend/src/api/volunteer/dto/get-volunteers.dto.ts @@ -1,3 +1,4 @@ +import { Type } from 'class-transformer'; import { IsDate, IsEnum, @@ -31,13 +32,16 @@ export class GetVolunteersDto extends BasePaginationFilterDto { @IsNumber() @IsOptional() + @Type(() => Number) locationId?: number; @IsDate() @IsOptional() + @Type(() => Date) activeSinceStart?: Date; @IsDate() @IsOptional() + @Type(() => Date) activeSinceEnd?: Date; } diff --git a/backend/src/main.ts b/backend/src/main.ts index 1d0cd620..350bbba8 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -22,7 +22,7 @@ async function bootstrap(): Promise { new ValidationPipe({ whitelist: true, transform: true, - transformOptions: { enableImplicitConversion: true }, + transformOptions: { enableImplicitConversion: false }, forbidNonWhitelisted: false, }), ); From 9393e19af1a00aca123ae60ad828e38ca7407132 Mon Sep 17 00:00:00 2001 From: OlteanuCosmin Date: Mon, 3 Apr 2023 16:30:20 +0300 Subject: [PATCH 3/3] feat: [TEO-508] revert @type additions to DTOs and change going from boolean to enum --- backend/src/api/_mobile/event/dto/create-rsvp.dto.ts | 2 -- .../src/api/_mobile/user/dto/create-regular-user.dto.ts | 3 --- .../volunteer/dto/create-volunteer-profile.dto.ts | 2 -- .../api/access-request/dto/get-access-requests.dto.ts | 6 ------ .../api/actions-archive/dto/get-actions-history.dto.ts | 3 --- .../activity-log/dto/create-activity-log-by-admin.dto.ts | 3 --- .../api/activity-log/dto/get-many-activity-logs.dto.ts | 5 ----- backend/src/api/event/dto/create-event.dto.ts | 4 ---- .../src/api/event/dto/get-paginated-event-rsvp.dto.ts | 9 ++++----- backend/src/api/event/event.controller.ts | 3 +++ .../src/api/organization/dto/create-access-code.dto.ts | 3 --- .../src/api/organization/dto/update-access-code.dto.ts | 2 -- backend/src/api/volunteer/dto/get-volunteers.dto.ts | 4 ---- backend/src/main.ts | 2 +- backend/src/modules/event/enums/rsvp-going.enum.ts | 4 ++++ frontend/src/common/enums/rsvp.enum.ts | 6 +++--- frontend/src/pages/Event.tsx | 6 +++--- frontend/src/services/event/event.api.ts | 5 ++--- 18 files changed, 20 insertions(+), 52 deletions(-) create mode 100644 backend/src/modules/event/enums/rsvp-going.enum.ts diff --git a/backend/src/api/_mobile/event/dto/create-rsvp.dto.ts b/backend/src/api/_mobile/event/dto/create-rsvp.dto.ts index 834f0e97..9981eaa2 100644 --- a/backend/src/api/_mobile/event/dto/create-rsvp.dto.ts +++ b/backend/src/api/_mobile/event/dto/create-rsvp.dto.ts @@ -1,4 +1,3 @@ -import { Type } from 'class-transformer'; import { IsBoolean, IsOptional, @@ -10,7 +9,6 @@ import { export class EventRSVPDto { @IsBoolean() - @Type(() => Boolean) going: boolean; @IsString() diff --git a/backend/src/api/_mobile/user/dto/create-regular-user.dto.ts b/backend/src/api/_mobile/user/dto/create-regular-user.dto.ts index f960d709..8409b36a 100644 --- a/backend/src/api/_mobile/user/dto/create-regular-user.dto.ts +++ b/backend/src/api/_mobile/user/dto/create-regular-user.dto.ts @@ -1,4 +1,3 @@ -import { Type } from 'class-transformer'; import { IsDate, IsEmail, @@ -27,13 +26,11 @@ export class CreateRegularUserDto { cognitoId: string; @IsDate() - @Type(() => Date) birthday: Date; @IsEnum(SEX) sex: SEX; @IsNumber() - @Type(() => Number) locationId: number; } diff --git a/backend/src/api/_mobile/volunteer/dto/create-volunteer-profile.dto.ts b/backend/src/api/_mobile/volunteer/dto/create-volunteer-profile.dto.ts index b7d99dca..9737d69b 100644 --- a/backend/src/api/_mobile/volunteer/dto/create-volunteer-profile.dto.ts +++ b/backend/src/api/_mobile/volunteer/dto/create-volunteer-profile.dto.ts @@ -1,4 +1,3 @@ -import { Type } from 'class-transformer'; import { IsDate, IsEmail, @@ -19,7 +18,6 @@ export class CreateVolunteerProfileDto { @IsDate() @IsOptional() - @Type(() => Date) activeSince: Date; @IsUUID() diff --git a/backend/src/api/access-request/dto/get-access-requests.dto.ts b/backend/src/api/access-request/dto/get-access-requests.dto.ts index b997fe3b..a33231e2 100644 --- a/backend/src/api/access-request/dto/get-access-requests.dto.ts +++ b/backend/src/api/access-request/dto/get-access-requests.dto.ts @@ -1,4 +1,3 @@ -import { Type } from 'class-transformer'; import { IsDate, IsEnum, IsNumber, IsOptional } from 'class-validator'; import { BasePaginationFilterDto } from 'src/infrastructure/base/base-pagination-filter.dto'; import { AccessRequestStatus } from 'src/modules/access-request/enums/access-request-status.enum'; @@ -6,27 +5,22 @@ import { AccessRequestStatus } from 'src/modules/access-request/enums/access-req export class GetAccessRequestsDto extends BasePaginationFilterDto { @IsNumber() @IsOptional() - @Type(() => Number) locationId?: number; @IsOptional() @IsDate() - @Type(() => Date) createdOnStart?: Date; @IsOptional() @IsDate() - @Type(() => Date) createdOnEnd?: Date; @IsOptional() @IsDate() - @Type(() => Date) rejectedOnStart?: Date; @IsOptional() @IsDate() - @Type(() => Date) rejectedOnEnd?: Date; @IsOptional() diff --git a/backend/src/api/actions-archive/dto/get-actions-history.dto.ts b/backend/src/api/actions-archive/dto/get-actions-history.dto.ts index 61d8279b..dbd5249a 100644 --- a/backend/src/api/actions-archive/dto/get-actions-history.dto.ts +++ b/backend/src/api/actions-archive/dto/get-actions-history.dto.ts @@ -1,4 +1,3 @@ -import { Type } from 'class-transformer'; import { IsDate, IsOptional, IsString } from 'class-validator'; import { BasePaginationFilterDto } from 'src/infrastructure/base/base-pagination-filter.dto'; @@ -9,11 +8,9 @@ export class GetManyActionsArchiveDto extends BasePaginationFilterDto { @IsDate() @IsOptional() - @Type(() => Date) actionStartDate?: Date; @IsDate() @IsOptional() - @Type(() => Date) actionEndDate?: Date; } diff --git a/backend/src/api/activity-log/dto/create-activity-log-by-admin.dto.ts b/backend/src/api/activity-log/dto/create-activity-log-by-admin.dto.ts index 273b1447..fe92682b 100644 --- a/backend/src/api/activity-log/dto/create-activity-log-by-admin.dto.ts +++ b/backend/src/api/activity-log/dto/create-activity-log-by-admin.dto.ts @@ -1,4 +1,3 @@ -import { Type } from 'class-transformer'; import { IsDate, IsNumber, @@ -12,13 +11,11 @@ import { export class CreateActivityLogByAdminDto { @IsDate() - @Type(() => Date) date: Date; @IsNumber() @Min(1) @Max(1000) - @Type(() => Number) hours: number; @IsString() diff --git a/backend/src/api/activity-log/dto/get-many-activity-logs.dto.ts b/backend/src/api/activity-log/dto/get-many-activity-logs.dto.ts index f44ec68d..d2692d1e 100644 --- a/backend/src/api/activity-log/dto/get-many-activity-logs.dto.ts +++ b/backend/src/api/activity-log/dto/get-many-activity-logs.dto.ts @@ -1,4 +1,3 @@ -import { Type } from 'class-transformer'; import { IsDate, IsEnum, IsOptional, IsString } from 'class-validator'; import { BasePaginationFilterDto } from 'src/infrastructure/base/base-pagination-filter.dto'; import { ActivityLogResolutionStatus } from 'src/modules/activity-log/enums/activity-log-resolution-status.enum'; @@ -22,22 +21,18 @@ export class GetManyActivityLogsDto extends BasePaginationFilterDto { @IsDate() @IsOptional() - @Type(() => Date) executionDateStart?: Date; @IsDate() @IsOptional() - @Type(() => Date) executionDateEnd?: Date; @IsDate() @IsOptional() - @Type(() => Date) registrationDateStart?: Date; @IsDate() @IsOptional() - @Type(() => Date) registrationDateEnd?: Date; @IsString() diff --git a/backend/src/api/event/dto/create-event.dto.ts b/backend/src/api/event/dto/create-event.dto.ts index d8cfe568..245899b8 100644 --- a/backend/src/api/event/dto/create-event.dto.ts +++ b/backend/src/api/event/dto/create-event.dto.ts @@ -1,4 +1,3 @@ -import { Type } from 'class-transformer'; import { IsArray, IsBoolean, @@ -32,19 +31,16 @@ export class CreateEventDto { location: string; @IsDate() - @Type(() => Date) startDate: Date; @IsDate() @IsOptional() - @Type(() => Date) endDate: Date; @IsIn([EventStatus.DRAFT, EventStatus.PUBLISHED]) status: EventStatus.DRAFT | EventStatus.PUBLISHED; @IsBoolean() - @Type(() => Boolean) isPublic: boolean; @IsEnum(EventAttendOptions) diff --git a/backend/src/api/event/dto/get-paginated-event-rsvp.dto.ts b/backend/src/api/event/dto/get-paginated-event-rsvp.dto.ts index c07efeb5..1b0d9edd 100644 --- a/backend/src/api/event/dto/get-paginated-event-rsvp.dto.ts +++ b/backend/src/api/event/dto/get-paginated-event-rsvp.dto.ts @@ -1,5 +1,5 @@ -import { Transform } from 'class-transformer'; -import { IsBoolean, IsOptional, IsUUID } from 'class-validator'; +import { IsEnum, IsOptional, IsUUID } from 'class-validator'; +import { RSVPGoingEnum } from 'src/modules/event/enums/rsvp-going.enum'; import { BasePaginationFilterDto } from 'src/infrastructure/base/base-pagination-filter.dto'; export class GetPaginatedEventRSVPsDto extends BasePaginationFilterDto { @@ -16,7 +16,6 @@ export class GetPaginatedEventRSVPsDto extends BasePaginationFilterDto { roleId?: string; @IsOptional() - @IsBoolean() - @Transform(({ value }) => value === 'true') - going?: boolean; + @IsEnum(RSVPGoingEnum) + going?: RSVPGoingEnum; } diff --git a/backend/src/api/event/event.controller.ts b/backend/src/api/event/event.controller.ts index 1627e910..3e21eb4a 100644 --- a/backend/src/api/event/event.controller.ts +++ b/backend/src/api/event/event.controller.ts @@ -42,6 +42,7 @@ import { Response } from 'express'; import { GetManyForDownloadEventUseCase } from 'src/usecases/event/get-many-for-download-event.usecase'; import { GetManyForDownloadEventRSVPUseCase } from 'src/usecases/event/RSVP/get-many-for-download-rsvp.usecase'; import { IEventRSVPDownload } from 'src/modules/event/interfaces/event-rsvp-download.interface'; +import { RSVPGoingEnum } from 'src/modules/event/enums/rsvp-going.enum'; @ApiBearerAuth() @UseGuards(WebJwtAuthGuard, EventGuard) @@ -173,6 +174,7 @@ export class EventController { ): Promise> { const rsvps = await this.getManyEventRSVPUsecase.execute({ ...filters, + going: filters.going ? filters.going === RSVPGoingEnum.YES : undefined, eventId, }); @@ -196,6 +198,7 @@ export class EventController { ): Promise { const data = await this.getManyForDownloadEventRSVPUseCase.execute({ ...filters, + going: filters.going ? filters.going === RSVPGoingEnum.YES : undefined, eventId, }); diff --git a/backend/src/api/organization/dto/create-access-code.dto.ts b/backend/src/api/organization/dto/create-access-code.dto.ts index 65e846ea..730ee5c4 100644 --- a/backend/src/api/organization/dto/create-access-code.dto.ts +++ b/backend/src/api/organization/dto/create-access-code.dto.ts @@ -1,4 +1,3 @@ -import { Type } from 'class-transformer'; import { IsDate, IsOptional, @@ -14,11 +13,9 @@ export class CreateAccessCodeDto { code: string; @IsDate() - @Type(() => Date) startDate: Date; @IsDate() @IsOptional() - @Type(() => Date) endDate?: Date; } diff --git a/backend/src/api/organization/dto/update-access-code.dto.ts b/backend/src/api/organization/dto/update-access-code.dto.ts index cc77da81..734f03fd 100644 --- a/backend/src/api/organization/dto/update-access-code.dto.ts +++ b/backend/src/api/organization/dto/update-access-code.dto.ts @@ -1,9 +1,7 @@ -import { Type } from 'class-transformer'; import { IsDate, ValidateIf } from 'class-validator'; export class UpdateAccessCodeDto { @IsDate() @ValidateIf((_, value) => value !== null) - @Type(() => Date) endDate: Date | null; } diff --git a/backend/src/api/volunteer/dto/get-volunteers.dto.ts b/backend/src/api/volunteer/dto/get-volunteers.dto.ts index 2c54410c..bd4786ab 100644 --- a/backend/src/api/volunteer/dto/get-volunteers.dto.ts +++ b/backend/src/api/volunteer/dto/get-volunteers.dto.ts @@ -1,4 +1,3 @@ -import { Type } from 'class-transformer'; import { IsDate, IsEnum, @@ -32,16 +31,13 @@ export class GetVolunteersDto extends BasePaginationFilterDto { @IsNumber() @IsOptional() - @Type(() => Number) locationId?: number; @IsDate() @IsOptional() - @Type(() => Date) activeSinceStart?: Date; @IsDate() @IsOptional() - @Type(() => Date) activeSinceEnd?: Date; } diff --git a/backend/src/main.ts b/backend/src/main.ts index 350bbba8..1d0cd620 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -22,7 +22,7 @@ async function bootstrap(): Promise { new ValidationPipe({ whitelist: true, transform: true, - transformOptions: { enableImplicitConversion: false }, + transformOptions: { enableImplicitConversion: true }, forbidNonWhitelisted: false, }), ); diff --git a/backend/src/modules/event/enums/rsvp-going.enum.ts b/backend/src/modules/event/enums/rsvp-going.enum.ts new file mode 100644 index 00000000..9792e8c7 --- /dev/null +++ b/backend/src/modules/event/enums/rsvp-going.enum.ts @@ -0,0 +1,4 @@ +export enum RSVPGoingEnum { + YES = 'yes', + NO = 'no', +} diff --git a/frontend/src/common/enums/rsvp.enum.ts b/frontend/src/common/enums/rsvp.enum.ts index 1356fb51..9792e8c7 100644 --- a/frontend/src/common/enums/rsvp.enum.ts +++ b/frontend/src/common/enums/rsvp.enum.ts @@ -1,4 +1,4 @@ -export enum RsvpEnum { - GOING = 'going', - NOT_GOING = 'not_going', +export enum RSVPGoingEnum { + YES = 'yes', + NO = 'no', } diff --git a/frontend/src/pages/Event.tsx b/frontend/src/pages/Event.tsx index 0a848aad..d625c0c3 100644 --- a/frontend/src/pages/Event.tsx +++ b/frontend/src/pages/Event.tsx @@ -43,7 +43,7 @@ import { IRsvp } from '../common/interfaces/rsvp.interface'; import DataTableFilters from '../components/DataTableFilters'; import OrganizationStructureSelect from '../containers/OrganizationStructureSelect'; import { DivisionType } from '../common/enums/division-type.enum'; -import { RsvpEnum } from '../common/enums/rsvp.enum'; +import { RSVPGoingEnum } from '../common/enums/rsvp.enum'; import { getEventRSVPsForDownload } from '../services/event/event.api'; enum EventTab { @@ -449,8 +449,8 @@ const Event = () => { onChange={(item: SelectItem) => setGoing(item)} selected={going} options={[ - { key: RsvpEnum.GOING, value: i18n.t('events:participate') }, - { key: RsvpEnum.NOT_GOING, value: i18n.t('events:not_participate') }, + { key: RSVPGoingEnum.YES, value: i18n.t('events:participate') }, + { key: RSVPGoingEnum.NO, value: i18n.t('events:not_participate') }, ]} /> diff --git a/frontend/src/services/event/event.api.ts b/frontend/src/services/event/event.api.ts index df6478ad..3ee1a1ce 100644 --- a/frontend/src/services/event/event.api.ts +++ b/frontend/src/services/event/event.api.ts @@ -1,7 +1,6 @@ import { AxiosResponseHeaders } from 'axios'; import { EventState } from '../../common/enums/event-state.enum'; import { EventStatus } from '../../common/enums/event-status'; -import { RsvpEnum } from '../../common/enums/rsvp.enum'; import { OrderDirection } from '../../common/enums/order-direction.enum'; import { IEvent } from '../../common/interfaces/event.interface'; import { IPaginatedEntity } from '../../common/interfaces/paginated-entity.interface'; @@ -83,7 +82,7 @@ export const getRsvps = async ( branchId, departmentId, roleId, - going: going === undefined ? going : going === RsvpEnum.GOING, + going, }, }).then((res) => res.data); }; @@ -106,7 +105,7 @@ export const getEventRSVPsForDownload = async ( branchId, departmentId, roleId, - going: going === undefined ? going : going === RsvpEnum.GOING, + going, }, responseType: 'arraybuffer', }).then((res) => {