add order space API (#459)

This commit is contained in:
ZaydSkaff
2025-07-09 11:44:14 +03:00
committed by GitHub
parent 2589e391ed
commit 83be80d9f6
5 changed files with 92 additions and 9 deletions

View File

@ -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. ';

View File

@ -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<SpaceDto> {
@ -64,6 +64,12 @@ export class SpaceEntity extends AbstractEntity<SpaceDto> {
})
public disabled: boolean;
@Column({
nullable: true,
type: Number,
})
public order?: number;
@OneToMany(() => SubspaceEntity, (subspace) => subspace.space, {
nullable: true,
})

View File

@ -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')

View File

@ -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[];
}

View File

@ -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<BaseResponseDto> {
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,