Skip to content

feat: [TEO-454, TEO-508] download event RSVP #119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import {
IsDate,
IsEnum,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
import { IsDate, IsOptional, IsString } from 'class-validator';
import { BasePaginationFilterDto } from 'src/infrastructure/base/base-pagination-filter.dto';

export class GetManyActionsArchiveDto extends BasePaginationFilterDto {
Expand Down
9 changes: 4 additions & 5 deletions backend/src/api/event/dto/get-paginated-event-rsvp.dto.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -16,7 +16,6 @@ export class GetPaginatedEventRSVPsDto extends BasePaginationFilterDto {
roleId?: string;

@IsOptional()
@IsBoolean()
@Transform(({ value }) => value === 'true')
going?: boolean;
@IsEnum(RSVPGoingEnum)
going?: RSVPGoingEnum;
}
28 changes: 27 additions & 1 deletion backend/src/api/event/event.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ 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';
import { RSVPGoingEnum } from 'src/modules/event/enums/rsvp-going.enum';

@ApiBearerAuth()
@UseGuards(WebJwtAuthGuard, EventGuard)
Expand All @@ -55,6 +58,7 @@ export class EventController {
private readonly getManyEventRSVPUsecase: GetManyEventRSVPUseCase,
private readonly getManyEventUseCase: GetManyEventUseCase,
private readonly getManyForDownloadEventUseCase: GetManyForDownloadEventUseCase,
private readonly getManyForDownloadEventRSVPUseCase: GetManyForDownloadEventRSVPUseCase,
) {}

@Get()
Expand All @@ -80,7 +84,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,
Expand Down Expand Up @@ -170,6 +174,7 @@ export class EventController {
): Promise<PaginatedPresenter<EventRSVPPresenter>> {
const rsvps = await this.getManyEventRSVPUsecase.execute({
...filters,
going: filters.going ? filters.going === RSVPGoingEnum.YES : undefined,
eventId,
});

Expand All @@ -178,4 +183,25 @@ 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<void> {
const data = await this.getManyForDownloadEventRSVPUseCase.execute({
...filters,
going: filters.going ? filters.going === RSVPGoingEnum.YES : undefined,
eventId,
});

res.end(jsonToExcelBuffer<IEventRSVPDownload>(data, 'Lista raspunsuri'));
}
}
4 changes: 4 additions & 0 deletions backend/src/modules/event/enums/rsvp-going.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum RSVPGoingEnum {
YES = 'yes',
NO = 'no',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface IEventRSVPDownload {
Nume: string;
Raspuns: string;
'Voluntar in organizatie': string;
Mentiune?: string;
}
Original file line number Diff line number Diff line change
@@ -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<IEventRSVPDownload[]>
{
constructor(
private readonly getManyEventRSVPUseCase: GetManyEventRSVPUseCase,
) {}

public async execute(
findOptions: FindManyEventRSVPOptions,
): Promise<IEventRSVPDownload[]> {
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,
};
});
}
}
11 changes: 7 additions & 4 deletions backend/src/usecases/use-case.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/assets/locales/ro/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,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",
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/common/enums/rsvp.enum.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export enum RsvpEnum {
GOING = 'going',
NOT_GOING = 'not_going',
export enum RSVPGoingEnum {
YES = 'yes',
NO = 'no',
}
26 changes: 21 additions & 5 deletions frontend/src/pages/Event.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -43,7 +43,8 @@ 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 {
EVENT = 'event',
Expand Down Expand Up @@ -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 (
<PageLayout>
<PageHeader onBackButtonPress={navigateBack}>{i18n.t('general:view')}</PageHeader>
Expand Down Expand Up @@ -433,8 +449,8 @@ const Event = () => {
onChange={(item: SelectItem<string>) => 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') },
]}
/>
</DataTableFilters>
Expand All @@ -445,7 +461,7 @@ const Event = () => {
label={i18n.t('general:download_table')}
icon={<ArrowDownTrayIcon className="h-5 w-5 text-cool-gray-600" />}
className="btn-outline-secondary ml-auto"
onClick={() => alert('Not implemented')}
onClick={onExportRSVPs}
/>
</CardHeader>
<CardBody>
Expand Down
29 changes: 27 additions & 2 deletions frontend/src/services/event/event.api.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -83,11 +82,37 @@ export const getRsvps = async (
branchId,
departmentId,
roleId,
going: going === undefined ? going : going === RsvpEnum.GOING,
going,
},
}).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,
},
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 {
Expand Down