diff --git a/backend/src/route/route.controller.ts b/backend/src/route/route.controller.ts index db4760ed3ce73c34057afc257ea9a994068ccd43..d73c626685cf29fddc298defb68013c8d1d77f03 100644 --- a/backend/src/route/route.controller.ts +++ b/backend/src/route/route.controller.ts @@ -13,6 +13,14 @@ import { UpdateRouteStatusDto } from './dto/updateRouteStatus.dto'; export class RouteController { constructor(private readonly routeService: RouteService) {} + @Get('/info/:idRoute') + @ApiParam({ name: 'idRoute', type: Number }) + @ApiBearerAuth('jwt') + // @UseGuards(AuthUserGuard) + async getRouteInfo(@Param('idRoute') idRoute: number) { + return await this.routeService.getRouteInfoById(idRoute); + } + @Post('/:idTown') @ApiParam({ name: 'idTown', type: Number }) @ApiBody({ type: CreateRouteReq }) @@ -48,8 +56,9 @@ export class RouteController { } } - @Get('/:idRoute') + @Get('/:idRoute/:lang') @ApiParam({ name: 'idRoute', type: Number }) + @ApiParam({ name: 'lang', type: String, enum: Object.values(LANGUAGES) }) @ApiBearerAuth('jwt') @UseGuards(AuthUserGuard) async getRoute(@Param('idRoute') idRoute: number, @Param('lang') lang: LANGUAGES, @Req() req: CustomUserRequest) { @@ -66,12 +75,4 @@ export class RouteController { async updateRoute(@Body() updateRouteStatusDto: UpdateRouteStatusDto, @Param('idRoute') idRoute: number) { return await this.routeService.updateRoute(idRoute, updateRouteStatusDto.status); } - - @Get('/info/:idRoute') - @ApiParam({ name: 'idRoute', type: Number }) - @ApiBearerAuth('jwt') - @UseGuards(AuthUserGuard) - async getRouteInfo(@Param('idRoute') idRoute: number) { - return await this.routeService.getRouteInfoById(idRoute); - } } diff --git a/backend/src/route/route.service.ts b/backend/src/route/route.service.ts index 6ee25f6fc8a8ebfbe8b5f4dae9e27a51f744f2c0..00de4a4383329d95a9db518222568b7860005a9a 100644 --- a/backend/src/route/route.service.ts +++ b/backend/src/route/route.service.ts @@ -150,6 +150,29 @@ export class RouteService { .where('route.idRoute = :idRoute', { idRoute }) .getOne(); + if (res && res.travelPlace) { + res.travelPlace = res.travelPlace.map((travelPlace: TravelPlace) => { + return { + travelPlaceId: travelPlace.travelPlaceId, + idPlace: travelPlace.place.idPlace, + available: travelPlace.place.available, + latitude: travelPlace.place.latitude, + longitude: travelPlace.place.longitude, + imageName: `${ServerConstants.HOST}/places/${travelPlace.place.imageName}`, + name: travelPlace.place.name, + openAt: travelPlace.place.openAt, + closeAt: travelPlace.place.closeAt, + startDate: travelPlace.startDate, + endDate: travelPlace.endDate, + categories: travelPlace.place.categories, + address: travelPlace.place.address, + done: travelPlace.done, + route: travelPlace.route, + place: { ...travelPlace.place, imageName: `${ServerConstants.HOST}/places/${travelPlace.place.imageName}` }, + }; + }); + } + return res; } } diff --git a/backend/src/user/dto/update-user.dto.ts b/backend/src/user/dto/update-user.dto.ts index 78ab602d8ddf6fc7606a9b6abf736549d9e8630d..61fc45980e5e2f8da331db9fc40266fdd2091f8f 100644 --- a/backend/src/user/dto/update-user.dto.ts +++ b/backend/src/user/dto/update-user.dto.ts @@ -1,4 +1,13 @@ -import { PartialType } from '@nestjs/swagger'; -import { CreateUserDto } from './create-user.dto'; +import { ApiProperty } from '@nestjs/swagger'; -export class UpdateUserDto extends PartialType(CreateUserDto) {} +export class UpdateUserDto { + @ApiProperty() + name: string; + @ApiProperty() + lastName: string; +} + +export class UpdateImageUserDto { + @ApiProperty({ type: 'string', format: 'binary' }) + image; +} diff --git a/backend/src/user/dto/user-response.ts b/backend/src/user/dto/user-response.ts new file mode 100644 index 0000000000000000000000000000000000000000..7588283c9ceda338211ed3f145ac439e6b59a08d --- /dev/null +++ b/backend/src/user/dto/user-response.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class UserResponse { + @ApiProperty() + name: string; + @ApiProperty() + lastName: string; + @ApiProperty() + imageUrl: string; + + @ApiProperty() + email: string; + + @ApiProperty() + emailConfirmed: boolean; +} diff --git a/backend/src/user/entities/user.entity.ts b/backend/src/user/entities/user.entity.ts index 9de8695637bfa619e02dceb4c076f030345caf55..6723c568d2efafc3f5844dc52179f612faf59ce1 100644 --- a/backend/src/user/entities/user.entity.ts +++ b/backend/src/user/entities/user.entity.ts @@ -24,6 +24,9 @@ export class User { @Column() password: string; + @Column({ nullable: true }) + imageUrl: string; + @Column({ default: false }) isEmailConfirmed: boolean; diff --git a/backend/src/user/user.controller.ts b/backend/src/user/user.controller.ts index ebb7f80f55d6645ba1ea6efa81a7e438b890ce4d..19cd7bc1954fe64536d11ccac77383f30b2260d8 100644 --- a/backend/src/user/user.controller.ts +++ b/backend/src/user/user.controller.ts @@ -1,10 +1,13 @@ -import { Body, Controller, Get, Patch, Query, Req, UseGuards } from '@nestjs/common'; +import { Body, Controller, Get, Patch, Query, Req, UseGuards, UseInterceptors } from '@nestjs/common'; import { UserService } from './user.service'; import { UpdatePreferedCategoriesDto } from './dto/update-preferedCategories.dto'; import { CustomUserRequest } from 'src/auth/user/interface/customUserReq'; -import { ApiBearerAuth, ApiBody, ApiQuery, ApiTags } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiBody, ApiConsumes, ApiQuery, ApiTags } from '@nestjs/swagger'; import { AuthUserGuard } from 'src/auth/user/authUser.guard'; import { LANGUAGES } from 'src/shared/enum/languages.enum'; +import { UpdateImageUserDto, UpdateUserDto } from './dto/update-user.dto'; +import { fileInterceptor } from 'src/shared/interceptors/file-save.interceptor'; +import { CustomAdminRequest } from 'src/auth/admin/interface/customAdminReq'; @Controller('user') @ApiTags('User') @@ -41,14 +44,44 @@ export class UserController { } } - @Get('is-verfied') + @Patch('info') @ApiBearerAuth('jwt') + @ApiBody({ type: UpdateUserDto }) @UseGuards(AuthUserGuard) - async isVerified(@Req() req: CustomUserRequest) { + async updateInfo(@Body() updateUserDto: UpdateUserDto, @Req() req: CustomUserRequest) { try { const { email } = req.user; - const isVerified = await this.userService.isVerified(email); - return { isVerified }; + await this.userService.updateInfo(email, updateUserDto); + return { message: 'User info updated successfully' }; + } catch (error) { + throw error; + } + } + + @Patch('photo') + @ApiBearerAuth('jwt') + @UseGuards(AuthUserGuard) + @ApiConsumes('multipart/form-data') + @ApiBody({ type: UpdateImageUserDto }) + @UseInterceptors(fileInterceptor('image', 'static/user/', ['.jpg', '.jpeg', '.png'])) + async updatePhoto(@Req() reqImg: CustomAdminRequest, @Req() req: CustomUserRequest) { + try { + const { email } = req.user; + const image = reqImg.file.filename; + await this.userService.updatePhoto(email, image); + return { message: 'User photo updated successfully' }; + } catch (error) { + throw error; + } + } + + @Get('info') + @ApiBearerAuth('jwt') + @UseGuards(AuthUserGuard) + async getInfo(@Req() req: CustomUserRequest) { + try { + const { email } = req.user; + return await this.userService.getUserInfo(email); } catch (error) { throw error; } diff --git a/backend/src/user/user.service.ts b/backend/src/user/user.service.ts index d466391fb4cb2fd482bb680fac4f04e664d35086..bbbf98ab81d497f14421948694059f8af29f0b9a 100644 --- a/backend/src/user/user.service.ts +++ b/backend/src/user/user.service.ts @@ -6,6 +6,9 @@ import { CreateUserDto } from './dto/create-user.dto'; import { Category } from 'src/category/entities/category.entity'; import { CategoryService } from 'src/category/category.service'; import { LANGUAGES } from 'src/shared/enum/languages.enum'; +import { UpdateUserDto } from './dto/update-user.dto'; +import { UserResponse } from './dto/user-response'; +import { ServerConstants } from 'src/constants/server.contants'; @Injectable() /** @@ -77,12 +80,36 @@ export class UserService { } } - async isVerified(email: string): Promise { + async updateInfo(email: string, updateUserDto: UpdateUserDto) { + try { + await this.userRepository.update({ email }, updateUserDto); + } catch (error) { + throw new BadRequestException('Error updating user info'); + } + } + + async updatePhoto(email: string, photo: string) { + try { + await this.userRepository.update({ email }, { imageUrl: photo }); + } catch (error) { + throw new BadRequestException('Error updating user photo'); + } + } + + async getUserInfo(email: string) { try { const user = await this.userRepository.findOneBy({ email }); - return user.isEmailConfirmed; + user.imageUrl = `${ServerConstants.HOST}/user/${user.imageUrl}`; + const res: UserResponse = { + imageUrl: user.imageUrl, + lastName: user.lastName, + email: user.email, + name: user.name, + emailConfirmed: user.isEmailConfirmed, + }; + return res; } catch (error) { - throw new BadRequestException('Error getting email verification status'); + throw new BadRequestException('Error getting user info'); } } } diff --git a/backend/src/visited/visited.module.ts b/backend/src/visited/visited.module.ts index f79ac46ecca8acfe9cb96eb1838a7e496001b956..69bd37103adc479bb28b09c6b6862f496931f87c 100644 --- a/backend/src/visited/visited.module.ts +++ b/backend/src/visited/visited.module.ts @@ -18,6 +18,7 @@ import { EncryptionService } from 'src/auth/encryption/encryption.service'; import { UserResetCode } from 'src/auth/user/entities/user-reset-code.entity'; import { UserConfirmCode } from 'src/auth/user/entities/user-confirm-code.entity'; import { EmailService } from 'src/email/email.service'; +import { TravelPlace } from 'src/travel-place/entities/travel-place.entity'; @Module({ controllers: [VisitedController], @@ -43,6 +44,7 @@ import { EmailService } from 'src/email/email.service'; Visited, UserResetCode, UserConfirmCode, + TravelPlace, ]), ], }) diff --git a/backend/src/visited/visited.service.ts b/backend/src/visited/visited.service.ts index 7f0c17e05c96da0093b408bb5d8631a449620062..b9051ee8b5fe56b60e362c5fd1f3767a27528d9c 100644 --- a/backend/src/visited/visited.service.ts +++ b/backend/src/visited/visited.service.ts @@ -10,6 +10,7 @@ import { PlaceService } from 'src/place/place.service'; import { LANGUAGES } from 'src/shared/enum/languages.enum'; import { VisitedPlacesImageCreator } from './utils/visited_places_image_creator'; import { ServerConstants } from 'src/constants/server.contants'; +import { TravelPlace } from 'src/travel-place/entities/travel-place.entity'; @Injectable() export class VisitedService { @@ -17,6 +18,7 @@ export class VisitedService { @InjectRepository(Visited) private visitedRepository: Repository, private readonly userService: UserService, private readonly placeService: PlaceService, + @InjectRepository(TravelPlace) private travelPlaceRepository: Repository, ) {} async create(createVisitedDto: CreateVisitedDto, email: string) { @@ -24,6 +26,11 @@ export class VisitedService { const user: User = await this.userService.findOne(email); if (!place || !user) throw new BadRequestException('Place or user not found'); + try { + await this.travelPlaceRepository.update({ place }, { done: true }); + } catch (error) { + console.log(error); + } await this.visitedRepository.save({ place, user, rating: createVisitedDto.rating }); } diff --git a/backend/tes.sql b/backend/tes.sql new file mode 100644 index 0000000000000000000000000000000000000000..ff33b2b6bf2f7216e313c698864930abfb14ba36 --- /dev/null +++ b/backend/tes.sql @@ -0,0 +1 @@ +"QueryFailedError: Unknown column 'NaN' in 'where clause'\n at Query.onResult (/home/diego/Desktop/coding/pueblos/backend/src/driver/mysql/MysqlQueryRunner.ts:246:33)\n at Query.execute (/home/diego/Desktop/coding/pueblos/backend/node_modules/mysql2/lib/commands/command.js:36:14)\n at PoolConnection.handlePacket (/home/diego/Desktop/coding/pueblos/backend/node_modules/mysql2/lib/connection.js:481:34)\n at PacketParser.onPacket (/home/diego/Desktop/coding/pueblos/backend/node_modules/mysq…onnection.js:97:12)\n at PacketParser.executeStart (/home/diego/Desktop/coding/pueblos/backend/node_modules/mysql2/lib/packet_parser.js:75:16)\n at Socket. (/home/diego/Desktop/coding/pueblos/backend/node_modules/mysql2/lib/connection.js:104:25)\n at Socket.emit (node:events:520:28)\n at addChunk (node:internal/streams/readable:559:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:510:3)\n at Socket.Readable.push (node:internal/streams/readable:390:5)" \ No newline at end of file