From 83be80d9f6c94b5e690071e7b88db1035b569a07 Mon Sep 17 00:00:00 2001 From: ZaydSkaff Date: Wed, 9 Jul 2025 11:44:14 +0300 Subject: [PATCH] add order space API (#459) --- libs/common/src/constants/controller-route.ts | 5 +++ .../modules/space/entities/space.entity.ts | 10 ++++- src/space/controllers/space.controller.ts | 33 ++++++++++++--- src/space/dtos/order.spaces.dto.ts | 13 ++++++ src/space/services/space.service.ts | 40 ++++++++++++++++++- 5 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 src/space/dtos/order.spaces.dto.ts diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index 1c7ee7c..af7bda4 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -220,6 +220,11 @@ export class ControllerRoute { public static readonly UPDATE_SPACE_DESCRIPTION = 'Updates a space by its UUID and community ID. You can update the name, parent space, and other properties. If a parent space is provided and not already a parent, its `isParent` flag will be set to true.'; + public static readonly UPDATE_CHILDREN_SPACES_ORDER_OF_A_SPACE_SUMMARY = + 'Update the order of child spaces under a specific parent space'; + public static readonly UPDATE_CHILDREN_SPACES_ORDER_OF_A_SPACE_DESCRIPTION = + 'Updates the order of child spaces under a specific parent space. You can provide a new order for the child spaces.'; + public static readonly GET_HEIRARCHY_SUMMARY = 'Get space hierarchy'; public static readonly GET_HEIRARCHY_DESCRIPTION = 'This endpoint retrieves the hierarchical structure of spaces under a given space ID. It returns all the child spaces nested within the specified space, organized by their parent-child relationships. '; diff --git a/libs/common/src/modules/space/entities/space.entity.ts b/libs/common/src/modules/space/entities/space.entity.ts index 763ffec..6a04865 100644 --- a/libs/common/src/modules/space/entities/space.entity.ts +++ b/libs/common/src/modules/space/entities/space.entity.ts @@ -6,9 +6,9 @@ import { OneToMany, OneToOne, } from 'typeorm'; -import { SpaceDto } from '../dtos'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { AqiSpaceDailyPollutantStatsEntity } from '../../aqi/entities'; +import { BookableSpaceEntity } from '../../booking/entities'; import { CommunityEntity } from '../../community/entities'; import { DeviceEntity } from '../../device/entities'; import { InviteUserSpaceEntity } from '../../Invite-user/entities'; @@ -17,9 +17,9 @@ import { PresenceSensorDailySpaceEntity } from '../../presence-sensor/entities'; import { SceneEntity } from '../../scene/entities'; import { SpaceModelEntity } from '../../space-model'; import { UserSpaceEntity } from '../../user/entities'; +import { SpaceDto } from '../dtos'; import { SpaceProductAllocationEntity } from './space-product-allocation.entity'; import { SubspaceEntity } from './subspace/subspace.entity'; -import { BookableSpaceEntity } from '../../booking/entities'; @Entity({ name: 'space' }) export class SpaceEntity extends AbstractEntity { @@ -64,6 +64,12 @@ export class SpaceEntity extends AbstractEntity { }) public disabled: boolean; + @Column({ + nullable: true, + type: Number, + }) + public order?: number; + @OneToMany(() => SubspaceEntity, (subspace) => subspace.space, { nullable: true, }) diff --git a/src/space/controllers/space.controller.ts b/src/space/controllers/space.controller.ts index ca47488..20660ca 100644 --- a/src/space/controllers/space.controller.ts +++ b/src/space/controllers/space.controller.ts @@ -1,6 +1,5 @@ -import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { SpaceService } from '../services'; import { ControllerRoute } from '@app/common/constants/controller-route'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { Body, Controller, @@ -12,12 +11,14 @@ import { Query, UseGuards, } from '@nestjs/common'; -import { AddSpaceDto, CommunitySpaceParam, UpdateSpaceDto } from '../dtos'; -import { BaseResponseDto } from '@app/common/dto/base.response.dto'; -import { GetSpaceParam } from '../dtos/get.space.param'; -import { PermissionsGuard } from 'src/guards/permissions.guard'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; import { Permissions } from 'src/decorators/permissions.decorator'; +import { PermissionsGuard } from 'src/guards/permissions.guard'; +import { AddSpaceDto, CommunitySpaceParam, UpdateSpaceDto } from '../dtos'; import { GetSpaceDto } from '../dtos/get.space.dto'; +import { GetSpaceParam } from '../dtos/get.space.param'; +import { OrderSpacesDto } from '../dtos/order.spaces.dto'; +import { SpaceService } from '../services'; @ApiTags('Space Module') @Controller({ @@ -65,6 +66,26 @@ export class SpaceController { ); } + @ApiBearerAuth() + @UseGuards(PermissionsGuard) + @Permissions('SPACE_UPDATE') + @ApiOperation({ + summary: + ControllerRoute.SPACE.ACTIONS + .UPDATE_CHILDREN_SPACES_ORDER_OF_A_SPACE_SUMMARY, + description: + ControllerRoute.SPACE.ACTIONS + .UPDATE_CHILDREN_SPACES_ORDER_OF_A_SPACE_DESCRIPTION, + }) + @Post(':parentSpaceUuid/spaces/order') + async updateSpacesOrder( + @Body() orderSpacesDto: OrderSpacesDto, + @Param() communitySpaceParam: CommunitySpaceParam, + @Param('parentSpaceUuid') parentSpaceUuid: string, + ) { + return this.spaceService.updateSpacesOrder(parentSpaceUuid, orderSpacesDto); + } + @ApiBearerAuth() @UseGuards(PermissionsGuard) @Permissions('SPACE_DELETE') diff --git a/src/space/dtos/order.spaces.dto.ts b/src/space/dtos/order.spaces.dto.ts new file mode 100644 index 0000000..275af9f --- /dev/null +++ b/src/space/dtos/order.spaces.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { ArrayUnique, IsNotEmpty, IsUUID } from 'class-validator'; + +export class OrderSpacesDto { + @ApiProperty({ + description: 'List of children spaces associated with the space', + type: [String], + }) + @IsNotEmpty() + @ArrayUnique() + @IsUUID('4', { each: true, message: 'Invalid space UUID provided' }) + spacesUuids: string[]; +} diff --git a/src/space/services/space.service.ts b/src/space/services/space.service.ts index f0ea822..5ea39cd 100644 --- a/src/space/services/space.service.ts +++ b/src/space/services/space.service.ts @@ -33,6 +33,7 @@ import { } from '../dtos'; import { CreateProductAllocationDto } from '../dtos/create-product-allocation.dto'; import { GetSpaceDto } from '../dtos/get.space.dto'; +import { OrderSpacesDto } from '../dtos/order.spaces.dto'; import { SpaceWithParentsDto } from '../dtos/space.parents.dto'; import { SpaceProductAllocationService } from './space-product-allocation.service'; import { ValidationService } from './space-validation.service'; @@ -355,6 +356,32 @@ export class SpaceService { } } + async updateSpacesOrder( + parentSpaceUuid: string, + { spacesUuids }: OrderSpacesDto, + ) { + try { + await this.spaceRepository.update( + { uuid: In(spacesUuids), parent: { uuid: parentSpaceUuid } }, + { + order: () => + 'CASE ' + + spacesUuids + .map((s, index) => `WHEN uuid = '${s}' THEN ${index + 1}`) + .join(' ') + + ' END', + }, + ); + return true; + } catch (error) { + console.error('Error updating spaces order:', error); + throw new HttpException( + 'An error occurred while updating spaces order', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async delete(params: GetSpaceParam): Promise { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); @@ -709,10 +736,21 @@ export class SpaceService { rootSpaces.push(map.get(space.uuid)!); // Push only root spaces } }); - + rootSpaces.forEach(this.sortSpaceChildren.bind(this)); return rootSpaces; } + private sortSpaceChildren(space: SpaceEntity) { + if (space.children && space.children.length > 0) { + space.children.sort((a, b) => { + const aOrder = a.order ?? Infinity; + const bOrder = b.order ?? Infinity; + return aOrder - bOrder; + }); + space.children.forEach(this.sortSpaceChildren.bind(this)); // Recursively sort children of children + } + } + private validateSpaceCreationCriteria({ spaceModelUuid, productAllocations,