diff --git a/src/app-building/app-building.controller.ts b/src/app-building/app-building.controller.ts index 4465c5a..54fbb2e 100644 --- a/src/app-building/app-building.controller.ts +++ b/src/app-building/app-building.controller.ts @@ -20,12 +20,16 @@ import { ApiNotFoundResponse, ApiParam, ApiNoContentResponse, + ApiBadRequestResponse, } from '@nestjs/swagger'; import { User } from 'src/objects/user/user.entity'; import { LoggedInUser } from 'src/shared/decorators/user.decorator'; import { AuthGuard } from 'src/shared/guards/auth.guard'; import { AppBuildingService } from './app-building.service'; -import { BuildingFloorUpdateRequestDto } from './dto/buildings-floor-request.dto'; +import { + BuildingFloorUpdateRequestDto, + CreateFloorRequestDto, +} from './dto/buildings-floor-request.dto'; import { CreateBuildingRequestDto } from './dto/buildings-create-request.dto'; import { BuildingFloorResponseDto, @@ -108,6 +112,19 @@ export class AppBuildingController { return this.service.getBuildingFloors(user, id); } + @Post(':id/floors') + @ApiParam({ name: 'id', description: 'Building ID' }) + @ApiBody({ type: CreateFloorRequestDto }) + @ApiOperation({ summary: 'Create building floors' }) + @ApiOkResponse({ type: BuildingFloorResponseDto, isArray: true }) + async createBuildingFloor( + @Param('id', ParseIntPipe) id: number, + @Body() body: CreateFloorRequestDto, + @LoggedInUser() user: User, + ): Promise { + return this.service.createBuildingFloor(user, id, body); + } + @Get(':id/floors/:number') @ApiParam({ name: 'id', description: 'Building ID' }) @ApiParam({ name: 'number', description: 'Floor number' }) @@ -136,6 +153,22 @@ export class AppBuildingController { return this.service.updateBuildingFloor(user, body, id, floor); } + @Delete(':id/floors/:number') + @ApiParam({ name: 'id', description: 'Building ID' }) + @ApiParam({ name: 'number', description: 'Floor number' }) + @ApiOperation({ summary: 'Delete building floor by floor number' }) + @ApiBadRequestResponse({ + description: 'A floor cannot be deleted if it has defined rooms', + }) + @ApiOkResponse({ type: BuildingFloorResponseDto }) + async deleteBuildingFloorByIdByNumber( + @Param('id', ParseIntPipe) id: number, + @Param('number', ParseIntPipe) floor: number, + @LoggedInUser() user: User, + ): Promise { + return this.service.deleteBuildingFloor(user, id, floor); + } + @Post(':id/floors/:number/rooms') @ApiParam({ name: 'id', description: 'Building ID' }) @ApiParam({ name: 'number', description: 'Floor number' }) diff --git a/src/app-building/app-building.service.ts b/src/app-building/app-building.service.ts index 6110d2f..4fd2175 100644 --- a/src/app-building/app-building.service.ts +++ b/src/app-building/app-building.service.ts @@ -1,11 +1,18 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; +import { + BadRequestException, + Injectable, + NotFoundException, +} from '@nestjs/common'; import omit from 'lodash.omit'; import pick from 'lodash.pick'; import { BuildingService } from 'src/objects/building/building.service'; import { GroupService } from 'src/objects/group/group.service'; import { User } from 'src/objects/user/user.entity'; import { UserService } from 'src/objects/user/user.service'; -import { BuildingFloorUpdateRequestDto } from './dto/buildings-floor-request.dto'; +import { + BuildingFloorUpdateRequestDto, + CreateFloorRequestDto, +} from './dto/buildings-floor-request.dto'; import { CreateBuildingRequestDto } from './dto/buildings-create-request.dto'; import { BuildingsUpdateRequestDto } from './dto/buildings-update-request.dto'; import { @@ -56,6 +63,30 @@ export class AppBuildingService { return omit(newBuilding, ['groups']); } + async createBuildingFloor( + user: User, + id: number, + body: CreateFloorRequestDto, + ) { + const building = await this.buildingService.getBuildingByIdAndUserSub( + id, + user.sub, + ); + + if (!building) { + throw new NotFoundException('Building not found'); + } + + const newFloor = await this.buildingService.saveFloor({ + displayName: body.displayName, + number: body.number, + plan: '{}', + building, + }); + + return omit(newFloor, ['building']); + } + async updateBuilding( user: User, id: number, @@ -116,6 +147,34 @@ export class AppBuildingService { return omit(floor, ['building']); } + async deleteBuildingFloor( + user: User, + buildingId: number, + floorNumber: number, + ) { + const floor = await this.buildingService.getFloorByBuildingAndUserSub( + buildingId, + floorNumber, + user.sub, + ['rooms'], + ); + + if (!floor) { + throw new NotFoundException('Floor not found'); + } + + if (floor.rooms.length) { + throw new BadRequestException( + 'A floor cannot be deleted if it has defined rooms', + ); + } + + await this.planRenderService.deletePlanImage(floor); + await this.buildingService.deleteFloor(floor); + + return omit(floor, ['building']); + } + async updateBuildingFloor( user: User, body: BuildingFloorUpdateRequestDto, diff --git a/src/app-building/dto/buildings-floor-request.dto.ts b/src/app-building/dto/buildings-floor-request.dto.ts index 977b107..63ea10b 100644 --- a/src/app-building/dto/buildings-floor-request.dto.ts +++ b/src/app-building/dto/buildings-floor-request.dto.ts @@ -10,3 +10,8 @@ export class BuildingFloorRequestDto extends PickType(Floor, [ export class BuildingFloorUpdateRequestDto extends PartialType( BuildingFloorRequestDto, ) {} + +export class CreateFloorRequestDto extends PickType(Floor, [ + 'displayName', + 'number', +]) {} diff --git a/src/app-building/plan-renderer/plan-renderer.service.ts b/src/app-building/plan-renderer/plan-renderer.service.ts index 6b79d91..174995d 100644 --- a/src/app-building/plan-renderer/plan-renderer.service.ts +++ b/src/app-building/plan-renderer/plan-renderer.service.ts @@ -42,4 +42,14 @@ export class PlanRendererService { ); floor.planImage = filename; } + + public async deletePlanImage(floor: Floor) { + if (!floor.planImage) return; + const filePath = join(process.cwd(), 'usercontent', floor.planImage); + try { + await fs.promises.unlink(filePath); + } catch (e) { + Logger.error(`Could not unlink floor plan ${filePath}`, e.stack); + } + } } diff --git a/src/app-storage/app-storage.controller.ts b/src/app-storage/app-storage.controller.ts index 79ab87b..1dab8df 100644 --- a/src/app-storage/app-storage.controller.ts +++ b/src/app-storage/app-storage.controller.ts @@ -58,10 +58,7 @@ import { StorageUpdateRequestDto, StorageWithSetsQueryDto, } from './dto/storage-request.dto'; -import { - StorageResponseDto, - StorageResponseWithItemCountDto, -} from './dto/storage-response.dto'; +import { StorageResponseDto } from './dto/storage-response.dto'; import { StorageSetCreateRequestDto, StorageSetUpdateRequestDto, @@ -82,10 +79,10 @@ export class AppStorageController { @Get('storages/:storageId') @ApiParam({ name: 'storageId', description: 'Storage ID' }) @ApiOperation({ summary: 'Get storage by ID' }) - @ApiOkResponse({ type: StorageResponseWithItemCountDto }) + @ApiOkResponse({ type: StorageResponseDto }) async getStorage( @CurrentStorage() storage: Storage, - ): Promise { + ): Promise { return this.service.getStorageWithItems(storage); } @@ -101,6 +98,27 @@ export class AppStorageController { return this.service.updateStorage(storage, body); } + @Get('set/room/:roomId') + @ApiParam({ name: 'roomId', description: 'Room ID' }) + @ApiOperation({ summary: 'Get storage sets in room' }) + @ApiOkResponse({ type: StorageSetResponseDto, isArray: true }) + async getStorageSets(@CurrentRoom() room: Room) { + return this.service.getStorageSetsInRoom(room.id); + } + + @Post('set/room/:roomId') + @ApiParam({ name: 'roomId', description: 'Room ID' }) + @ApiBody({ type: StorageCreateRequestDto }) + @ApiOperation({ summary: 'Create storage sets in room' }) + @ApiOkResponse({ type: StorageSetResponseDto, isArray: true }) + async createStorageSet( + @LoggedInUser() user: User, + @Body() body: StorageSetCreateRequestDto, + @CurrentRoom() room: Room, + ) { + return this.service.createStorageSet(user, room, body); + } + @UseGuards(StorageSetGuard) @Get('set/:storageSetId') @ApiParam({ name: 'storageSetId', description: 'Storage set ID' }) @@ -149,27 +167,6 @@ export class AppStorageController { return this.service.removeFromSet(set, storage); } - @Get('set/room/:roomId') - @ApiParam({ name: 'roomId', description: 'Room ID' }) - @ApiOperation({ summary: 'Get storage sets in room' }) - @ApiOkResponse({ type: StorageSetResponseDto, isArray: true }) - async getStorageSets(@CurrentRoom() room: Room) { - return this.service.getStorageSetsInRoom(room.id); - } - - @Post('set/room/:roomId') - @ApiParam({ name: 'roomId', description: 'Room ID' }) - @ApiBody({ type: StorageCreateRequestDto }) - @ApiOperation({ summary: 'Create storage sets in room' }) - @ApiOkResponse({ type: StorageSetResponseDto, isArray: true }) - async createStorageSet( - @LoggedInUser() user: User, - @Body() body: StorageSetCreateRequestDto, - @CurrentRoom() room: Room, - ) { - return this.service.createStorageSet(user, room, body); - } - @Get('room/:roomId') @ApiParam({ name: 'roomId', description: 'Room ID' }) @ApiOperation({ summary: 'Get storages in room' }) diff --git a/src/app-storage/app-storage.service.ts b/src/app-storage/app-storage.service.ts index 02ce327..0033827 100644 --- a/src/app-storage/app-storage.service.ts +++ b/src/app-storage/app-storage.service.ts @@ -39,7 +39,6 @@ import { import { StorageActorResponse, StorageResponseDto, - StorageResponseWithItemCountDto, } from './dto/storage-response.dto'; import { StorageSetCreateRequestDto, @@ -397,13 +396,13 @@ export class AppStorageService { } async getStorageWithItems(storage: Storage) { - const withItemCount = await this.storageService.getStorageWithItemCount( - storage.id, - ); + const withItemCount = await this.storageService.getStorageById(storage.id, [ + 'items', + 'items.item', + 'items.addedBy', + ]); - return this.formatStorageNoItems( - withItemCount, - ) as StorageResponseWithItemCountDto; + return this.formatStorageWithItems(withItemCount) as StorageResponseDto; } private formatActor(input: User): StorageActorResponse { diff --git a/src/objects/building/building.service.ts b/src/objects/building/building.service.ts index 661170e..67c6df0 100644 --- a/src/objects/building/building.service.ts +++ b/src/objects/building/building.service.ts @@ -218,4 +218,8 @@ export class BuildingService { async deleteRoom(room: Room) { return this.roomRepository.remove(room); } + + async deleteFloor(floor: Floor) { + return this.floorRepository.remove(floor); + } }