Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ SMTP_PASS=

ASSET_ID_PREFIX=AST
ASSET_ID_START=1000

# File Upload Configuration
UPLOAD_DIR=/var/uploads/assets
2 changes: 2 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { OpsceModule } from './opsce/opsce.module';

@Module({
imports: [
Expand All @@ -22,6 +23,7 @@ import { AppService } from './app.service';
}),
inject: [ConfigService],
}),
OpsceModule,
],
controllers: [AppController],
providers: [AppService],
Expand Down
114 changes: 114 additions & 0 deletions backend/src/opsce/assets/assets.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
Query,
UseGuards,
HttpStatus,
} from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiBearerAuth,
ApiParam,
ApiQuery,
} from '@nestjs/swagger';
import { AssetsService } from './assets.service';
import { CreateAssetDto } from './dto/create-asset.dto';
import { UpdateAssetDto } from './dto/update-asset.dto';
import { PaginationDto } from '../common/dto/pagination.dto';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard } from '../auth/roles.guard';
import { Roles } from '../auth/roles.decorator';
import { UserRole } from '../users/entities/user.entity';

@ApiTags('Assets')
@Controller('assets')
@UseGuards(JwtAuthGuard, RolesGuard)
@ApiBearerAuth('JWT-auth')
export class AssetsController {
constructor(private readonly assetsService: AssetsService) {}

@Post()
@Roles(UserRole.ADMIN)
@ApiOperation({ summary: 'Create a new asset (ADMIN only)' })
@ApiResponse({
status: HttpStatus.CREATED,
description: 'Asset successfully created',
})
@ApiResponse({
status: HttpStatus.BAD_REQUEST,
description: 'Invalid input data',
})
create(@Body() createAssetDto: CreateAssetDto) {
return this.assetsService.create(createAssetDto);
}

@Get()
@ApiOperation({ summary: 'Get all assets with pagination' })
@ApiQuery({ name: 'page', required: false, type: Number })
@ApiQuery({ name: 'limit', required: false, type: Number })
@ApiResponse({
status: HttpStatus.OK,
description: 'Return paginated assets',
})
findAll(@Query() paginationDto: PaginationDto) {
return this.assetsService.findAll(paginationDto);
}

@Get(':id')
@ApiOperation({ summary: 'Get an asset by ID' })
@ApiParam({ name: 'id', description: 'Asset ID' })
@ApiResponse({
status: HttpStatus.OK,
description: 'Return the asset',
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Asset not found',
})
findOne(@Param('id') id: string) {
return this.assetsService.findOne(id);
}

@Patch(':id')
@Roles(UserRole.ADMIN)
@ApiOperation({ summary: 'Update an asset (ADMIN only)' })
@ApiParam({ name: 'id', description: 'Asset ID' })
@ApiResponse({
status: HttpStatus.OK,
description: 'Asset successfully updated',
})
@ApiResponse({
status: HttpStatus.BAD_REQUEST,
description: 'Invalid input data',
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Asset not found',
})
update(@Param('id') id: string, @Body() updateAssetDto: UpdateAssetDto) {
return this.assetsService.update(id, updateAssetDto);
}

@Delete(':id')
@Roles(UserRole.ADMIN)
@ApiOperation({ summary: 'Delete an asset (ADMIN only)' })
@ApiParam({ name: 'id', description: 'Asset ID' })
@ApiResponse({
status: HttpStatus.NO_CONTENT,
description: 'Asset successfully deleted',
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Asset not found',
})
remove(@Param('id') id: string) {
return this.assetsService.remove(id);
}
}
6 changes: 5 additions & 1 deletion backend/src/opsce/assets/assets.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Asset } from './entities/asset.entity';
import { AssetsController } from './assets.controller';
import { AssetsService } from './assets.service';

@Module({
imports: [TypeOrmModule.forFeature([Asset])],
exports: [TypeOrmModule],
controllers: [AssetsController],
providers: [AssetsService],
exports: [AssetsService],
})
export class AssetsModule {}
47 changes: 47 additions & 0 deletions backend/src/opsce/assets/assets.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Asset } from './entities/asset.entity';
import { CreateAssetDto } from './dto/create-asset.dto';
import { UpdateAssetDto } from './dto/update-asset.dto';
import { PaginationDto, PaginatedResponseDto, paginate } from '../common';

@Injectable()
export class AssetsService {
constructor(
@InjectRepository(Asset)
private readonly assetRepository: Repository<Asset>,
) {}

async create(createAssetDto: CreateAssetDto): Promise<Asset> {
const asset = this.assetRepository.create(createAssetDto);
return this.assetRepository.save(asset);
}

async findAll(
paginationDto: PaginationDto,
): Promise<PaginatedResponseDto<Asset>> {
return paginate(this.assetRepository, paginationDto, {
order: { createdAt: 'DESC' },
});
}

async findOne(id: string): Promise<Asset> {
const asset = await this.assetRepository.findOne({ where: { id } });
if (!asset) {
throw new NotFoundException(`Asset with ID ${id} not found`);
}
return asset;
}

async update(id: string, updateAssetDto: UpdateAssetDto): Promise<Asset> {
const asset = await this.findOne(id);
Object.assign(asset, updateAssetDto);
return this.assetRepository.save(asset);
}

async remove(id: string): Promise<void> {
const asset = await this.findOne(id);
await this.assetRepository.remove(asset);
}
}
69 changes: 69 additions & 0 deletions backend/src/opsce/assets/dto/create-asset.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import {
IsString,
IsOptional,
IsEnum,
IsNumber,
IsDateString,
} from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { AssetStatus, AssetCondition } from '../entities/asset.entity';

export class CreateAssetDto {
@ApiProperty({ description: 'Asset name' })
@IsString()
name: string;

@ApiPropertyOptional({ description: 'Asset description' })
@IsOptional()
@IsString()
description?: string;

@ApiProperty({ description: 'Asset category' })
@IsString()
category: string;

@ApiPropertyOptional({ description: 'Serial number' })
@IsOptional()
@IsString()
serialNumber?: string;

@ApiPropertyOptional({ description: 'Asset status', enum: AssetStatus })
@IsOptional()
@IsEnum(AssetStatus)
status?: AssetStatus;

@ApiPropertyOptional({ description: 'Asset condition', enum: AssetCondition })
@IsOptional()
@IsEnum(AssetCondition)
condition?: AssetCondition;

@ApiPropertyOptional({ description: 'Purchase date' })
@IsOptional()
@IsDateString()
purchaseDate?: Date;

@ApiPropertyOptional({ description: 'Purchase value' })
@IsOptional()
@IsNumber()
purchaseValue?: number;

@ApiPropertyOptional({ description: 'Current value' })
@IsOptional()
@IsNumber()
currentValue?: number;

@ApiPropertyOptional({ description: 'User ID assigned to' })
@IsOptional()
@IsString()
assignedToUserId?: string;

@ApiPropertyOptional({ description: 'Department ID' })
@IsOptional()
@IsString()
departmentId?: string;

@ApiPropertyOptional({ description: 'Location ID' })
@IsOptional()
@IsString()
locationId?: string;
}
4 changes: 4 additions & 0 deletions backend/src/opsce/assets/dto/update-asset.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/swagger';
import { CreateAssetDto } from './create-asset.dto';

export class UpdateAssetDto extends PartialType(CreateAssetDto) {}
Loading
Loading