Skip to content

Roko/fly talks be #533

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 13 commits into from
May 6, 2025
44 changes: 42 additions & 2 deletions apps/api/src/event/event.controller.ts
Copy link
Member

@ToniGrbic ToniGrbic May 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jel bi tribalo zaštitt rute koje nemaj nikakav Guard, ili User ili Admin pa provjeri.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doda san na skoro sve sta user koristi userGuard

Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {
EventDto,
EventModifyDto,
EventWithCompanyDto,
EventWithSpeakerDto,
UserToEventDto,
} from '@ddays-app/types';
import { UserToEventDto } from '@ddays-app/types/src/dto/user';
import {
BadRequestException,
Body,
Controller,
Delete,
Expand All @@ -15,8 +17,11 @@ import {
Post,
Req,
Res,
UploadedFile,
UseGuards,
UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { UserToEvent } from '@prisma/client';
import { Response } from 'express';
import { AdminGuard } from 'src/auth/admin.guard';
Expand All @@ -34,11 +39,34 @@ export class EventController {
return await this.eventService.create(dto);
}

@Post('apply-to-flytalk')
@UseGuards(UserGuard)
async applyToFlyTalk(@Body() dto: UserToEventDto): Promise<UserToEventDto> {
return await this.eventService.applyToFlyTalk(dto);
}

@Post('upload-cv')
@UseGuards(UserGuard)
@UseInterceptors(FileInterceptor('file'))
async uploadCV(@UploadedFile() file: Express.Multer.File): Promise<string> {
if (!file) {
throw new BadRequestException('File is required');
}
return await this.eventService.uploadCV(file);
}

@Get('with-speaker')
async getAllWithSpeakerAnd(): Promise<EventWithSpeakerDto[]> {
@UseGuards(UserGuard)
async getAllWithSpeaker(): Promise<EventWithSpeakerDto[]> {
return await this.eventService.getAllWithSpeaker();
}

@Get('fly-talks-with-company')
@UseGuards(UserGuard)
async GetFlyTalksWithCompany(): Promise<EventWithCompanyDto[]> {
return await this.eventService.getFlyTalksWithCompany();
}

@UseGuards(UserGuard)
@Get('my-schedule')
async getEventsInMySchedule(
Expand All @@ -53,6 +81,7 @@ export class EventController {
}

@Get('schedule-ical/:userId.ics')
@UseGuards(UserGuard)
async generateIcal(
@Param('userId', ParseIntPipe) userId: number,
@Res() res: Response,
Expand All @@ -71,6 +100,15 @@ export class EventController {
return await this.eventService.getAll();
}

@UseGuards(UserGuard)
@Delete('delete-flytalk-application')
async deleteFlyTalkApplication(
@Body() { eventId: eventId }: { eventId: number },
@Req() { user }: AuthenticatedRequest,
): Promise<UserToEventDto> {
return await this.eventService.deleteFlyTalkApplication(user.id, eventId);
}

@UseGuards(AdminGuard)
@Delete(':id')
async remove(@Param('id', ParseIntPipe) id: number): Promise<EventDto> {
Expand All @@ -87,6 +125,7 @@ export class EventController {
}

@Post(':id/join')
@UseGuards(UserGuard)
async joinEvent(
@Param('id', ParseIntPipe) eventId: number,
@Body() dto: UserToEventDto,
Expand All @@ -95,6 +134,7 @@ export class EventController {
}

@Delete(':id/leave')
@UseGuards(UserGuard)
async leaveEvent(
@Param('id', ParseIntPipe) eventId: number,
@Body() dto: UserToEventDto,
Expand Down
3 changes: 2 additions & 1 deletion apps/api/src/event/event.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Module } from '@nestjs/common';
import { BlobService } from 'src/blob/blob.service';
import { PrismaService } from 'src/prisma.service';

import { EventController } from './event.controller';
import { EventService } from './event.service';

@Module({
controllers: [EventController],
providers: [EventService, PrismaService],
providers: [EventService, PrismaService, BlobService],
})
export class EventModule {}
102 changes: 100 additions & 2 deletions apps/api/src/event/event.service.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {
EventDto,
EventModifyDto,
EventWithCompanyDto,
EventWithSpeakerDto,
UserToEventDto,
} from '@ddays-app/types';
import { UserToEventDto } from '@ddays-app/types/src/dto/user';
import {
HttpException,
HttpStatus,
Expand All @@ -12,6 +13,7 @@ import {
} from '@nestjs/common';
import { EventType, UserToEvent } from '@prisma/client';
import ical from 'ical-generator';
import { BlobService } from 'src/blob/blob.service';
import { PrismaService } from 'src/prisma.service';

export class AlreadyJoinedEventException extends HttpException {
Expand All @@ -22,7 +24,10 @@ export class AlreadyJoinedEventException extends HttpException {

@Injectable()
export class EventService {
constructor(private readonly prisma: PrismaService) {}
constructor(
private readonly prisma: PrismaService,
private readonly blobService: BlobService,
) {}

async create(dto: EventModifyDto): Promise<EventDto> {
const createdEvent = await this.prisma.event.create({
Expand All @@ -32,6 +37,32 @@ export class EventService {
return createdEvent;
}

async applyToFlyTalk(dto: UserToEventDto): Promise<UserToEventDto> {
const appliedFlyTalk = await this.prisma.userToEvent.create({
data: {
userId: dto.userId,
eventId: dto.eventId,
linkedinProfile: dto.linkedinProfile,
githubProfile: dto.githubProfile,
portfolioProfile: dto.portfolioProfile,
cv: dto.cv,
description: dto.description,
},
});

return appliedFlyTalk;
}

async uploadCV(file: Express.Multer.File): Promise<string> {
const cv = await this.blobService.upload(
'user-cv',
file.buffer,
file.mimetype,
);
console.log(cv);
return cv;
}

async getAll(): Promise<EventDto[]> {
const events = await this.prisma.event.findMany({
orderBy: { name: 'asc' },
Expand Down Expand Up @@ -131,6 +162,57 @@ export class EventService {
}));
}

async getFlyTalksWithCompany(): Promise<EventWithCompanyDto[]> {
const events = await this.prisma.event.findMany({
where: { type: EventType.FLY_TALK },
include: {
companyToFlyTalk: {
include: {
company: {
select: {
id: true,
name: true,
logoImage: true,
},
},
},
},
userToEvent: {
include: {
user: {
select: {
id: true,
},
},
},
},
},
});

return events.map((event) => ({
id: event.id,
name: event.name,
description: event.description,
startsAt: event.startsAt,
endsAt: event.endsAt,
maxParticipants: event.maxParticipants,
requirements: event.requirements,
footageLink: event.footageLink,
type: event.type,
theme: event.theme,
codeId: event.codeId,
isOnEnglish: event.isOnEnglish,
companies: event.companyToFlyTalk.map((relation) => ({
id: relation.company.id,
name: relation.company.name,
logoImage: relation.company.logoImage,
})),
users: event.userToEvent.map((relation) => ({
id: relation.user.id,
})),
}));
}

async remove(id: number): Promise<EventDto> {
const deletedEvent = await this.prisma.event.delete({
where: { id },
Expand All @@ -139,6 +221,22 @@ export class EventService {
return deletedEvent;
}

async deleteFlyTalkApplication(
userId: number,
eventId: number,
): Promise<UserToEventDto> {
const deletedApplication = await this.prisma.userToEvent.delete({
where: {
userId_eventId: {
userId,
eventId,
},
},
});

return deletedApplication;
}

async update(id: number, dto: EventModifyDto): Promise<EventDto> {
const updatedEvent = await this.prisma.event.update({
where: { id },
Expand Down
32 changes: 32 additions & 0 deletions apps/app/src/api/flyTalks/useDeleteFlyTalkApplication.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useMutation, useQueryClient } from 'react-query';
import axios from '../base';
import toast from 'react-hot-toast';
import { QUERY_KEYS } from '@/constants/queryKeys';

const deleteFlyTalkApplication = async ({
eventId,
}: {
eventId: number;
}): Promise<{ eventId: number }> => {
return axios.delete('/event/delete-flytalk-application', {
data: { eventId },
});
};

export const useDeleteFlyTalkApplication = () => {
const queryClient = useQueryClient();
return useMutation(deleteFlyTalkApplication, {
onSuccess: () => {
queryClient.refetchQueries([QUERY_KEYS.flyTalkGroups]);
toast.success('Termin je uspješno odjavljen.');
},
onError: (error: import('axios').AxiosError<{ message?: string }>) => {
console.error('Došlo je do greške: ', error);
const errorMessage =
error?.response?.data?.message ||
error?.message ||
'Došlo je do greške.';
toast.error(errorMessage);
},
});
};
15 changes: 15 additions & 0 deletions apps/app/src/api/flyTalks/useGetGroupCompanies.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import axios from '../base';
import { useQuery } from 'react-query';
import { QUERY_KEYS } from '@/constants/queryKeys';
import { EventWithCompanyDto } from '@ddays-app/types';

const getAllFlyTalkGroups = async (): Promise<EventWithCompanyDto[]> => {
return axios.get('/event/fly-talks-with-company');
};

export const useGetAllFlyTalkGroups = () => {
return useQuery<EventWithCompanyDto[]>(
[QUERY_KEYS.flyTalkGroups],
getAllFlyTalkGroups,
);
};
28 changes: 28 additions & 0 deletions apps/app/src/api/flyTalks/usePostApplyToFlyTalks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { QUERY_KEYS } from "@/constants/queryKeys";
import { UserToEventDto } from "@ddays-app/types";
import axios from "../base";
import toast from "react-hot-toast";
import { useMutation, QueryClient } from "react-query";

const queryClient = new QueryClient();


const postApplyToFlyTalks = async (data: UserToEventDto): Promise<UserToEventDto> => {
return axios.post<UserToEventDto, UserToEventDto>('/event/apply-to-flytalk', data);
}

export const usePostApplyToFlyTalks = () => {
return useMutation(
postApplyToFlyTalks, {
onSuccess: () => {
queryClient.invalidateQueries([QUERY_KEYS.applyFlyTalk]);
},
onError: (error: import("axios").AxiosError<{ message?: string }>) => {
console.error("Error applying to FlyTalk:", error);
const errorMessage = error?.response?.data?.message || error?.message || "An unexpected error occurred.";
toast.error(errorMessage);
},
}

);
};
23 changes: 23 additions & 0 deletions apps/app/src/api/flyTalks/usePostUploadCV.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useMutation } from 'react-query';
import axios from '../base';
import toast from 'react-hot-toast';

const uploadCV = async (file: File): Promise<string> => {
const formData = new FormData();
formData.append('file', file);

return await axios.post('event/upload-cv', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
responseType: 'text',
});
};

export const useUploadCV = () => {
return useMutation((file: File) => uploadCV(file), {
onError: () => {
toast.error('Greška prilikom slanja CV-a');
},
});
};
Loading
Loading