diff --git a/backend/src/activity/activity.controller.ts b/backend/src/activity/activity.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..26fb6becbb1d6812cbd5b5ebc330b690cedcb247 --- /dev/null +++ b/backend/src/activity/activity.controller.ts @@ -0,0 +1,34 @@ +import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common'; +import { ActivityService } from './activity.service'; +import { CreateActivityDto } from './dto/create-activity.dto'; +import { UpdateActivityDto } from './dto/update-activity.dto'; + +@Controller('activity') +export class ActivityController { + constructor(private readonly activityService: ActivityService) {} + + @Post() + create(@Body() createActivityDto: CreateActivityDto) { + return this.activityService.create(createActivityDto); + } + + @Get() + findAll() { + return this.activityService.findAll(); + } + + @Get(':id') + findOne(@Param('id') id: string) { + return this.activityService.findOne(+id); + } + + @Patch(':id') + update(@Param('id') id: string, @Body() updateActivityDto: UpdateActivityDto) { + return this.activityService.update(+id, updateActivityDto); + } + + @Delete(':id') + remove(@Param('id') id: string) { + return this.activityService.remove(+id); + } +} diff --git a/backend/src/activity/activity.module.ts b/backend/src/activity/activity.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..d60b58cec8d42a4ef585ed3331272fbac80b0371 --- /dev/null +++ b/backend/src/activity/activity.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { ActivityService } from './activity.service'; +import { ActivityController } from './activity.controller'; + +@Module({ + controllers: [ActivityController], + providers: [ActivityService], +}) +export class ActivityModule {} diff --git a/backend/src/activity/activity.service.ts b/backend/src/activity/activity.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..4a6486cfe971ac6abb1247c6ed6a7b23c5f6d24d --- /dev/null +++ b/backend/src/activity/activity.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@nestjs/common'; +import { CreateActivityDto } from './dto/create-activity.dto'; +import { UpdateActivityDto } from './dto/update-activity.dto'; + +@Injectable() +export class ActivityService { + create(createActivityDto: CreateActivityDto) { + return 'This action adds a new activity'; + } + + findAll() { + return `This action returns all activity`; + } + + findOne(id: number) { + return `This action returns a #${id} activity`; + } + + update(id: number, updateActivityDto: UpdateActivityDto) { + return `This action updates a #${id} activity`; + } + + remove(id: number) { + return `This action removes a #${id} activity`; + } +} diff --git a/backend/src/activity/dto/create-activity.dto.ts b/backend/src/activity/dto/create-activity.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..b54337e6f900365bca240201169b75480e731456 --- /dev/null +++ b/backend/src/activity/dto/create-activity.dto.ts @@ -0,0 +1,8 @@ +export class CreateActivityDto { + idPlace: number; + name: string; + numberActivity: number; + imageName: string; + descriptionEN: string; + descriptionES: string; +} diff --git a/backend/src/activity/dto/update-activity.dto.ts b/backend/src/activity/dto/update-activity.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..a4b993857397f92a6758ac12bdab27933549e9a4 --- /dev/null +++ b/backend/src/activity/dto/update-activity.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateActivityDto } from './create-activity.dto'; + +export class UpdateActivityDto extends PartialType(CreateActivityDto) {} diff --git a/backend/src/activity/entities/activity.entity.ts b/backend/src/activity/entities/activity.entity.ts new file mode 100644 index 0000000000000000000000000000000000000000..aa9f39c2c508c0d305ad62c63fe44fc1dc53b449 --- /dev/null +++ b/backend/src/activity/entities/activity.entity.ts @@ -0,0 +1,21 @@ +import { Place } from 'src/place/entities/place.entity'; +import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; + +@Entity() +export class Activity { + @PrimaryGeneratedColumn() + idActivity: number; + + @Column({ nullable: false }) + name: string; + + @JoinColumn({ name: 'idPlace' }) + @ManyToOne(() => Place, (place) => place.activities) + idPlace: Place; + + @Column({ nullable: false, primary: true }) + numberActivity: number; + + @Column({ nullable: false }) + imageName: string; +} diff --git a/backend/src/activity/entities/activityTraduction.entity.ts b/backend/src/activity/entities/activityTraduction.entity.ts new file mode 100644 index 0000000000000000000000000000000000000000..e9156a603709365929e2c87681d7f16fb8ac195f --- /dev/null +++ b/backend/src/activity/entities/activityTraduction.entity.ts @@ -0,0 +1,18 @@ +import { Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm'; +import { Activity } from './activity.entity'; +import { LANGUAGES } from 'src/shared/enum/languages.enum'; + +@Entity() +export class ActivityTraduction { + @PrimaryColumn({ name: 'idActivity' }) + @ManyToOne(() => Activity, (activity) => activity.idActivity) + idActivity: number; + @PrimaryColumn() + language: LANGUAGES; + + @Column({ nullable: false }) + content: string; + + @Column() + audioName: string; +} diff --git a/backend/src/activity/enum/available.enum.ts b/backend/src/activity/enum/available.enum.ts new file mode 100644 index 0000000000000000000000000000000000000000..5449782a25960c3b17b2f1ebfa49963bee68d087 --- /dev/null +++ b/backend/src/activity/enum/available.enum.ts @@ -0,0 +1,6 @@ +export enum Available { + WEEKEND = 'weekend', + ALL_DAYS = 'all_days', + WEEKDAYS = 'weekdays', + CUSTOM = 'custom', +} diff --git a/backend/src/admin/dto/create-admin.dto.ts b/backend/src/admin/dto/create-admin.dto.ts index c5a5ca71447f40558381cb8b93996c8aef10460e..b7a54f79390dcb28fe4d371dbf5c5d8875cb8f2a 100644 --- a/backend/src/admin/dto/create-admin.dto.ts +++ b/backend/src/admin/dto/create-admin.dto.ts @@ -6,6 +6,9 @@ export class CreateAdminDto { @ApiProperty() email: string; + @ApiProperty() + idTown: number; + @ApiProperty() password: string; diff --git a/backend/src/admin/entities/admin.entity.ts b/backend/src/admin/entities/admin.entity.ts index b7fc6cbd33be5bee1f6d3d85e037b4de9db1aa73..ec777819bb5748bc8a7153166cd2f407b18d9a48 100644 --- a/backend/src/admin/entities/admin.entity.ts +++ b/backend/src/admin/entities/admin.entity.ts @@ -1,12 +1,17 @@ import { ADMIN_ROLE } from 'src/shared/enum/admin-role.enum'; import { UserStatus } from 'src/shared/enum/user-status.enum'; -import { Entity, Column, PrimaryColumn } from 'typeorm'; +import { Town } from 'src/town/entities/town.entity'; +import { Entity, Column, PrimaryColumn, JoinColumn, ManyToOne } from 'typeorm'; @Entity() export class Admin { @PrimaryColumn() email: string; + @JoinColumn({ name: 'idTown' }) + @ManyToOne(() => Town, { nullable: true }) + idTown: number; + @Column() name: string; diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index befa2d439fcaae4961d1823e2a76e46cff0994df..eaf75daca623a4fb98f05f2de90df0d2f4158616 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -20,6 +20,13 @@ import { TownTraduction } from './town/entities/town-traduction.entity'; import { AuthModule } from './shared/service/auth.module'; import { APP_GUARD } from '@nestjs/core'; import { AuthGuard } from './auth/admin/auth.guard'; +import { PlaceModule } from './place/place.module'; +import { Place } from './place/entities/place.entity'; +import { ActivityModule } from './activity/activity.module'; +import { Activity } from './activity/entities/activity.entity'; +import { AvailableDate } from './place/entities/available-date.entity'; +import { ActivityTraduction } from './activity/entities/activityTraduction.entity'; +import { PlaceTraduction } from './place/entities/place-traduction.entity'; @Module({ imports: [ @@ -30,7 +37,18 @@ import { AuthGuard } from './auth/admin/auth.guard'; username: DbConstants.DB_USER, password: DbConstants.DB_PASSWORD, database: DbConstants.DB_NAME, - entities: [Admin, User, State, Town, TownTraduction], + entities: [ + Admin, + User, + State, + Town, + TownTraduction, + Place, + Activity, + AvailableDate, + ActivityTraduction, + PlaceTraduction, + ], synchronize: DbConstants.DB_SYNC, logging: true, }), @@ -42,9 +60,11 @@ import { AuthGuard } from './auth/admin/auth.guard'; DatabaseSeederModule, TownModule, AuthModule, + PlaceModule, ServeStaticModule.forRoot({ rootPath: join(__dirname, '..', 'static'), }), + ActivityModule, ], controllers: [AppController], providers: [AppService, DatabaseSeederModule, { provide: APP_GUARD, useClass: AuthGuard }], diff --git a/backend/src/constants/server.contants.ts b/backend/src/constants/server.contants.ts index ec37b52a7d38cc9871cfd33aea88c208ba105072..29915945979b52f1e4ae400f0b99b856917740cf 100644 --- a/backend/src/constants/server.contants.ts +++ b/backend/src/constants/server.contants.ts @@ -1,7 +1,20 @@ import * as dotenv from 'dotenv'; + dotenv.config(); +import * as os from 'os'; +let ip = ''; +const interfaces = os.networkInterfaces(); +for (const interfaceName in interfaces) { + const networkInterfaces = interfaces[interfaceName]; + for (const networkInterface of networkInterfaces) { + if (networkInterface.family === 'IPv4' && !networkInterface.internal) { + ip = networkInterface.address; + } + } +} export class ServerConstants { static PORT: number = process.env.SERVER_PORT ? parseInt(process.env.SERVER_PORT) : 3003; - static HOST: string = `${process.env.SERVER_HOST || 'http://localhost'}:${this.PORT}`; + static IP: string = ip; + static HOST: string = `${ip || 'http://localhost'}:${this.PORT}`; } diff --git a/backend/src/database-seeder/database-seeder.module.ts b/backend/src/database-seeder/database-seeder.module.ts index 54d83e1b0e7260afb01dd267c3ec3b9664af15fc..9096a25bd2921515356e45aa31850ede4bce06af 100644 --- a/backend/src/database-seeder/database-seeder.module.ts +++ b/backend/src/database-seeder/database-seeder.module.ts @@ -1,6 +1,7 @@ import { Module } from '@nestjs/common'; -import { DatabaseSeederService } from './database-seeder.service'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { DatabaseSeederService } from './database-seeder.service'; + import { State } from 'src/state/entities/state.entity'; import { StateService } from 'src/state/state.service'; import { Town } from 'src/town/entities/town.entity'; @@ -9,9 +10,39 @@ import { AuthAdminService } from 'src/auth/admin/authAdminservice'; import { AdminService } from 'src/admin/admin.service'; import { JwtService } from '@nestjs/jwt'; import { EncryptionService } from 'src/auth/encryption/encryption.service'; +import { TownService } from 'src/town/town.service'; +import { TownTraduction } from 'src/town/entities/town-traduction.entity'; +import { ActivityService } from 'src/activity/activity.service'; +import { PlaceService } from 'src/place/place.service'; +import { Place } from 'src/place/entities/place.entity'; +import { Activity } from 'src/activity/entities/activity.entity'; +import { AvailableDate } from 'src/place/entities/available-date.entity'; +import { PlaceTraduction } from 'src/place/entities/place-traduction.entity'; @Module({ - providers: [DatabaseSeederService, StateService, AuthAdminService, AdminService, JwtService, EncryptionService], - imports: [TypeOrmModule.forFeature([State, Town, Admin])], + providers: [ + DatabaseSeederService, + StateService, + AuthAdminService, + AdminService, + JwtService, + EncryptionService, + TownService, + ActivityService, + PlaceService, + ], + imports: [ + TypeOrmModule.forFeature([ + State, + Town, + Admin, + Town, + TownTraduction, + Place, + Activity, + AvailableDate, + PlaceTraduction, + ]), + ], }) export class DatabaseSeederModule {} diff --git a/backend/src/database-seeder/database-seeder.service.ts b/backend/src/database-seeder/database-seeder.service.ts index 9a3fd5c9af2f679dd3dea41a929778ff434bdf4c..abdbe6c726fea74337b3e9127f271ae6d0b41bb8 100644 --- a/backend/src/database-seeder/database-seeder.service.ts +++ b/backend/src/database-seeder/database-seeder.service.ts @@ -8,6 +8,10 @@ import { CreateAdminDto } from 'src/admin/dto/create-admin.dto'; import { ADMIN_ROLE } from 'src/shared/enum/admin-role.enum'; import { UserStatus } from 'src/shared/enum/user-status.enum'; import { AuthAdminService } from 'src/auth/admin/authAdminservice'; +import { TownService } from 'src/town/town.service'; +import { CreateTownDto } from 'src/town/dto/create-town.dto'; +import { ActivityService } from 'src/activity/activity.service'; +import { PlaceService } from 'src/place/place.service'; @Injectable() export class DatabaseSeederService implements OnModuleInit { @@ -15,6 +19,9 @@ export class DatabaseSeederService implements OnModuleInit { @InjectRepository(State) private stateRepo: Repository, private readonly stateService: StateService, private readonly authAdminService: AuthAdminService, + private readonly townService: TownService, + private readonly activityService: ActivityService, + private readonly placeService: PlaceService, ) {} async insertStates() { @@ -27,6 +34,7 @@ export class DatabaseSeederService implements OnModuleInit { async insertSuperAdmin() { const createSuperAdmin: CreateAdminDto = { email: 'superadmin@gmail.com', + idTown: null, password: '123', name: 'Super Admin', lastName: 'super', @@ -35,6 +43,7 @@ export class DatabaseSeederService implements OnModuleInit { }; const createAdmin: CreateAdminDto = { email: 'admin@gmail.com', + idTown: null, password: '123', name: 'Admin', lastName: 'admin', @@ -73,8 +82,20 @@ export class DatabaseSeederService implements OnModuleInit { ); } + async insertTowns() { + const town: CreateTownDto = { + state: 1, + name: 'Town', + imageName: 'default.jpg', + descriptionEN: 'Town description', + descriptionES: 'Descripcion del pueblo', + }; + await this.townService.create(town); + } + async onModuleInit() { await this.insertStates(); await this.insertSuperAdmin(); + await this.insertTowns(); } } diff --git a/backend/src/database-seeder/towns.json b/backend/src/database-seeder/towns.json new file mode 100644 index 0000000000000000000000000000000000000000..f3269d60e015615af0df3838346e51b56db4215f --- /dev/null +++ b/backend/src/database-seeder/towns.json @@ -0,0 +1,10 @@ +{ + "towns": [ + { + "town": 1, + "name": "Jerez", + "state": 1, + "imageName": "1.jpg" + } + ] +} diff --git a/backend/src/place/dto/create-date.dto.ts b/backend/src/place/dto/create-date.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..a311bb99bfd223d060c9a0e87b9d936dc55e5d76 --- /dev/null +++ b/backend/src/place/dto/create-date.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class CreateDateDto { + @ApiProperty() + startDate: Date; + + @ApiProperty() + endDate: Date; + + idPlace: number; +} diff --git a/backend/src/place/dto/create-place-date.dto.ts b/backend/src/place/dto/create-place-date.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..9ecc2d8191e4e9e3de451582f2ecd8cf90cbf94b --- /dev/null +++ b/backend/src/place/dto/create-place-date.dto.ts @@ -0,0 +1,36 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Available } from 'src/activity/enum/available.enum'; + +export class CreatePlaceDateTradDto { + @ApiProperty({ enum: Available, enumName: 'Available' }) + available: Available; + + @ApiProperty() + idTown: number; + + @ApiProperty() + name: string; + + @ApiProperty() + descriptionES: string; + @ApiProperty() + descriptionEN: string; + + @ApiProperty({ type: 'string', format: 'binary' }) + image; + + @ApiProperty({ example: '12.3456789-12.3456789' }) + coords: string; + // 24-hour format + @ApiProperty({ maximum: 24, minimum: 0 }) + openAt: number; + // 24-hour format + @ApiProperty({ maximum: 24, minimum: 0 }) + closeAt: number; + + @ApiProperty({ default: new Date() }) + startDate: Date; + + @ApiProperty({ default: new Date() }) + endDate: Date; +} diff --git a/backend/src/place/dto/create-place-trad.dto.ts b/backend/src/place/dto/create-place-trad.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..a4ba67dae589a908643163f75bf78b87b66e973e --- /dev/null +++ b/backend/src/place/dto/create-place-trad.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { LANGUAGES } from 'src/shared/enum/languages.enum'; + +export class CreateTraductionPlaceDto { + @ApiProperty() + language: LANGUAGES = LANGUAGES.ES; + @ApiProperty() + idPlace: number; + + @ApiProperty() + description: string; +} diff --git a/backend/src/place/dto/create-place.dto.ts b/backend/src/place/dto/create-place.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..320b52c663ef5a30599f7ba41cded86c713f1a35 --- /dev/null +++ b/backend/src/place/dto/create-place.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Available } from 'src/activity/enum/available.enum'; +import { Town } from 'src/town/entities/town.entity'; + +export class CreatePlaceDto { + @ApiProperty({ enum: Available, enumName: 'Available' }) + available: Available; + @ApiProperty() + name: string; + @ApiProperty() + imageName: string; + + @ApiProperty({ type: 'number' }) + idTown: Town; + @ApiProperty({ example: '12.3456789-12.3456789' }) + coords: string; + // 24-hour format + @ApiProperty({ maximum: 24, minimum: 0 }) + openAt: number; + // 24-hour format + @ApiProperty({ maximum: 24, minimum: 0 }) + closeAt: number; +} diff --git a/backend/src/place/dto/get-place.dto.ts b/backend/src/place/dto/get-place.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..ee107604de080a59d1798f6adc266e90b542efb9 --- /dev/null +++ b/backend/src/place/dto/get-place.dto.ts @@ -0,0 +1,13 @@ +import { Available } from 'src/activity/enum/available.enum'; +import { Town } from 'src/town/entities/town.entity'; + +export class GetPlaceDto { + available: Available; + name: string; + description: string; + imageName: string; + idTown: Town; + coords: string; + openAt: number; + closeAt: number; +} diff --git a/backend/src/place/dto/update-place.dto.ts b/backend/src/place/dto/update-place.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..5db344e62b005bd3515d79b7d1d77cd55639a95c --- /dev/null +++ b/backend/src/place/dto/update-place.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreatePlaceDateTradDto } from './create-place-date.dto'; + +export class UpdatePlaceDto extends PartialType(CreatePlaceDateTradDto) {} diff --git a/backend/src/place/entities/available-date.entity.ts b/backend/src/place/entities/available-date.entity.ts new file mode 100644 index 0000000000000000000000000000000000000000..f3c8db84d3b21fd6bf834b3c072405f2469395fc --- /dev/null +++ b/backend/src/place/entities/available-date.entity.ts @@ -0,0 +1,17 @@ +import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; +import { Place } from './place.entity'; + +@Entity() +export class AvailableDate { + @PrimaryGeneratedColumn() + idAvailableDate: number; + + @JoinColumn({ name: 'idPlace' }) + @ManyToOne(() => Place, (place) => place.availableDates, { nullable: false }) + idPlace: number; + + @Column() + startDate: Date; + @Column() + endDate: Date; +} diff --git a/backend/src/place/entities/place-traduction.entity.ts b/backend/src/place/entities/place-traduction.entity.ts new file mode 100644 index 0000000000000000000000000000000000000000..f6bbd132e687f2a540333b094e15d76962479d52 --- /dev/null +++ b/backend/src/place/entities/place-traduction.entity.ts @@ -0,0 +1,16 @@ +import { Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm'; +import { Place } from './place.entity'; +import { LANGUAGES } from 'src/shared/enum/languages.enum'; + +@Entity() +export class PlaceTraduction { + @PrimaryColumn() + language: LANGUAGES = LANGUAGES.ES; + + @PrimaryColumn({ name: 'idPlace' }) + @ManyToOne(() => Place, (place) => place.availableDates, { nullable: false }) + idPlace: number; + + @Column() + description: string; +} diff --git a/backend/src/place/entities/place.entity.ts b/backend/src/place/entities/place.entity.ts new file mode 100644 index 0000000000000000000000000000000000000000..047cff095bf4cbca9c53041d714558dec1c643b7 --- /dev/null +++ b/backend/src/place/entities/place.entity.ts @@ -0,0 +1,39 @@ +import { Activity } from 'src/activity/entities/activity.entity'; +import { Available } from 'src/activity/enum/available.enum'; +import { Town } from 'src/town/entities/town.entity'; +import { Column, Entity, JoinColumn, ManyToOne, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; +import { AvailableDate } from './available-date.entity'; + +@Entity() +export class Place { + @PrimaryGeneratedColumn() + idPlace: number; + + @JoinColumn({ name: 'idTown' }) + @ManyToOne(() => Town, (town) => town.townId, { nullable: false }) + idTown: Town; + + @OneToMany(() => Activity, (activity) => activity.idActivity) + activities: Activity[]; + + @OneToMany(() => AvailableDate, (availableDate) => availableDate.idAvailableDate) + availableDates: AvailableDate[]; + + @Column() + available: Available; + + @Column() + name: string; + + @Column() + imageName: string; + + @Column({ nullable: false }) + coords: string; + + @Column({ nullable: false }) + openAt: number; + + @Column({ nullable: false }) + closeAt: number; +} diff --git a/backend/src/place/place.controller.ts b/backend/src/place/place.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..0a7ef30775bf8e0c47dd1022a30c5f540bdd3a8f --- /dev/null +++ b/backend/src/place/place.controller.ts @@ -0,0 +1,64 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, + UseInterceptors, + UploadedFile, + Query, +} from '@nestjs/common'; +import { PlaceService } from './place.service'; +import { CreatePlaceDateTradDto } from './dto/create-place-date.dto'; +import { UpdatePlaceDto } from './dto/update-place.dto'; +import { ApiBearerAuth, ApiBody, ApiConsumes, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; +import { Roles } from 'src/auth/role.decorator'; +import { ADMIN_ROLES } from 'src/shared/enum/admin-role.enum'; +import { fileInterceptor } from 'src/shared/interceptors/file-save.interceptor'; +import { FileValidationPipe } from 'src/shared/pipe/file-validation.pipe'; +import { LANGUAGES } from 'src/shared/enum/languages.enum'; + +@Controller('place') +@ApiTags('Place') +export class PlaceController { + constructor(private readonly placeService: PlaceService) {} + + @ApiBody({ type: CreatePlaceDateTradDto }) + @ApiConsumes('multipart/form-data') + @Roles(ADMIN_ROLES) + @ApiBearerAuth('jwt') + @Post() + @UseInterceptors(fileInterceptor('image', 'static/places/', ['.jpg', '.jpeg', '.png'])) + async create(@UploadedFile(new FileValidationPipe()) file, @Body() createPlaceDto: CreatePlaceDateTradDto) { + createPlaceDto.image = file; + return await this.placeService.create(createPlaceDto); + } + + @ApiQuery({ name: 'lang', type: String }) + @ApiParam({ name: 'idTown', type: Number }) + @Get('/town/:idTown/activity') + async findAll(@Param('idTown') idTown: number, @Query('lang') lang: string) { + try { + return this.placeService.findAllByTown(idTown, lang as LANGUAGES); + } catch (e) { + throw e; + } + } + + @Get(':id') + findOne(@Param('id') id: string) { + return this.placeService.findOne(+id); + } + + @Patch(':id') + update(@Param('id') id: string, @Body() updatePlaceDto: UpdatePlaceDto) { + return this.placeService.update(+id, updatePlaceDto); + } + + @Delete(':id') + remove(@Param('id') id: string) { + return this.placeService.remove(+id); + } +} diff --git a/backend/src/place/place.module.ts b/backend/src/place/place.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..d32cf55e3f1aea5b09d2fc8d9a209a25d59f3fdf --- /dev/null +++ b/backend/src/place/place.module.ts @@ -0,0 +1,22 @@ +import { Module } from '@nestjs/common'; +import { PlaceService } from './place.service'; +import { PlaceController } from './place.controller'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { Place } from './entities/place.entity'; +import { AvailableDate } from './entities/available-date.entity'; +import { PlaceTraduction } from './entities/place-traduction.entity'; +import { Town } from 'src/town/entities/town.entity'; +import { TownService } from 'src/town/town.service'; +import { TownTraduction } from 'src/town/entities/town-traduction.entity'; +import { StateService } from 'src/state/state.service'; +import { State } from 'src/state/entities/state.entity'; + +@Module({ + controllers: [PlaceController], + providers: [PlaceService, TownService, StateService], + imports: [ + TypeOrmModule.forFeature([Place, AvailableDate, PlaceTraduction, Town, TownTraduction, State]), + TypeOrmModule, + ], +}) +export class PlaceModule {} diff --git a/backend/src/place/place.service.ts b/backend/src/place/place.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..ac41e02a61148714fbb2beba7ec07d3b15a276b5 --- /dev/null +++ b/backend/src/place/place.service.ts @@ -0,0 +1,108 @@ +import { BadRequestException, Injectable } from '@nestjs/common'; +import { CreatePlaceDateTradDto } from './dto/create-place-date.dto'; +import { UpdatePlaceDto } from './dto/update-place.dto'; +import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; +import { Place } from './entities/place.entity'; +import { DataSource, Repository } from 'typeorm'; +import { AvailableDate } from './entities/available-date.entity'; +import { CreatePlaceDto } from './dto/create-place.dto'; +import { CreateDateDto } from './dto/create-date.dto'; +import { PlaceTraduction } from './entities/place-traduction.entity'; +import { LANGUAGES } from 'src/shared/enum/languages.enum'; +import { Town } from 'src/town/entities/town.entity'; +import { GetPlaceDto } from './dto/get-place.dto'; +import { ServerConstants } from 'src/constants/server.contants'; + +@Injectable() +export class PlaceService { + constructor( + @InjectRepository(Place) private placeRepository: Repository, + @InjectRepository(AvailableDate) private availableDateRepository: Repository, + @InjectRepository(PlaceTraduction) private placeTraductionRepository: Repository, + @InjectRepository(Town) private townRepository: Repository, + @InjectDataSource() private dataSource: DataSource, + ) {} + + async create(createPlaceDto: CreatePlaceDateTradDto) { + const town: Town = await this.townRepository.findOneBy({ townId: createPlaceDto.idTown }); + const createPlace: CreatePlaceDto = { + available: createPlaceDto.available, + closeAt: createPlaceDto.closeAt, + coords: createPlaceDto.coords, + idTown: town, + name: createPlaceDto.name, + openAt: createPlaceDto.openAt, + imageName: createPlaceDto.image.filename, + }; + + if (!town) { + throw new BadRequestException('Town not found'); + } + + const insertedId = (await this.placeRepository.insert({ ...createPlace })).raw.insertId; + const createDate: CreateDateDto = { + endDate: createPlaceDto.endDate, + startDate: createPlaceDto.startDate, + idPlace: insertedId, + }; + const createTradEn: PlaceTraduction = { + language: LANGUAGES.EN, + idPlace: insertedId, + description: createPlaceDto.descriptionEN, + }; + const createTradEs: PlaceTraduction = { + language: LANGUAGES.ES, + idPlace: insertedId, + description: createPlaceDto.descriptionES, + }; + // TODO: add transaction to rollback if one of the inserts fails + await this.placeTraductionRepository.insert(createTradEs); + await this.placeTraductionRepository.insert(createTradEn); + await this.availableDateRepository.insert(createDate); + } + + async findAllByTown(idTown: number, lang: LANGUAGES) { + const res: GetPlaceDto[] = await this.dataSource + .getRepository(PlaceTraduction) + .createQueryBuilder('placeTrad') + .leftJoin('placeTrad.idPlace', 'place') + .select([ + 'place.idPlace AS idPlace', + 'place.name AS name', + 'place.imageName AS imageName', + 'placeTrad.language AS language', + 'placeTrad.description AS description', + 'place.coords AS coords', + 'place.openAt AS openAt', + 'place.closeAt AS closeAt', + ]) + .where('place.idTown = :idTown', { idTown }) + .andWhere('placeTrad.language = :language', { language: lang }) + .getRawMany(); + const places: GetPlaceDto[] = res.map((place) => { + return { + available: place.available, + description: place.description, + coords: place.coords, + idTown: place.idTown, + imageName: `${ServerConstants.HOST}/places/${place.imageName}`, + name: place.name, + openAt: place.openAt, + closeAt: place.closeAt, + }; + }); + return places; + } + + findOne(id: number) { + return `This action returns a #${id} place`; + } + + update(id: number, updatePlaceDto: UpdatePlaceDto) { + return `This action updates a #${id} place`; + } + + remove(id: number) { + return `This action removes a #${id} place`; + } +} diff --git a/backend/src/state/entities/state.entity.ts b/backend/src/state/entities/state.entity.ts index 22040e87bf79632f9ffee02af2832c10b3ad8bcf..94f883cacbd93cf268def07ae8e871ba9a4f21b6 100644 --- a/backend/src/state/entities/state.entity.ts +++ b/backend/src/state/entities/state.entity.ts @@ -1,5 +1,5 @@ import { Town } from 'src/town/entities/town.entity'; -import { Entity, Column, PrimaryColumn, OneToMany, JoinColumn } from 'typeorm'; +import { Entity, Column, PrimaryColumn, OneToMany } from 'typeorm'; @Entity() export class State { @@ -7,7 +7,6 @@ export class State { stateId: number; @OneToMany(() => Town, (town) => town.townId) - @JoinColumn() towns: Town[]; @Column() diff --git a/backend/src/town/entities/town.entity.ts b/backend/src/town/entities/town.entity.ts index 6001257cf99346c43c4f4e3f15f3fc71c5bd51a6..9e9689ae4485c662c20c2d3dc63fb8f449f915c7 100644 --- a/backend/src/town/entities/town.entity.ts +++ b/backend/src/town/entities/town.entity.ts @@ -1,14 +1,17 @@ +import { Place } from 'src/place/entities/place.entity'; import { State } from 'src/state/entities/state.entity'; -import { Entity, Column, ManyToOne, JoinColumn, PrimaryGeneratedColumn } from 'typeorm'; +import { Entity, Column, ManyToOne, PrimaryGeneratedColumn, OneToMany } from 'typeorm'; @Entity() export class Town { @PrimaryGeneratedColumn() townId: number; @ManyToOne(() => State, (state) => state.stateId, { nullable: false }) - @JoinColumn() state: number; + @OneToMany(() => Place, (place) => place.idTown) + places: Place[]; + @Column() name: string; diff --git a/backend/static/towns/default.jpeg b/backend/static/towns/default.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..4791069031b6fdb3e853904d760969bedf978fed Binary files /dev/null and b/backend/static/towns/default.jpeg differ