Skip to content

Commit e772ec2

Browse files
Merge pull request #865 from KuchiMercy/feat/issues
Created LocationsModule, added pagination, implemented file upload service, created DepartmentsModule
2 parents 44e0144 + 0ea5743 commit e772ec2

32 files changed

Lines changed: 1524 additions & 19 deletions

backend/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ SMTP_PASS=
2020

2121
ASSET_ID_PREFIX=AST
2222
ASSET_ID_START=1000
23+
24+
# File Upload Configuration
25+
UPLOAD_DIR=/var/uploads/assets

backend/src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
44
import { TypeOrmModule } from '@nestjs/typeorm';
55
import { AppController } from './app.controller';
66
import { AppService } from './app.service';
7+
import { OpsceModule } from './opsce/opsce.module';
78

89
@Module({
910
imports: [
@@ -22,6 +23,7 @@ import { AppService } from './app.service';
2223
}),
2324
inject: [ConfigService],
2425
}),
26+
OpsceModule,
2527
],
2628
controllers: [AppController],
2729
providers: [AppService],
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import {
2+
Controller,
3+
Get,
4+
Post,
5+
Body,
6+
Patch,
7+
Param,
8+
Delete,
9+
Query,
10+
UseGuards,
11+
HttpStatus,
12+
} from '@nestjs/common';
13+
import {
14+
ApiTags,
15+
ApiOperation,
16+
ApiResponse,
17+
ApiBearerAuth,
18+
ApiParam,
19+
ApiQuery,
20+
} from '@nestjs/swagger';
21+
import { AssetsService } from './assets.service';
22+
import { CreateAssetDto } from './dto/create-asset.dto';
23+
import { UpdateAssetDto } from './dto/update-asset.dto';
24+
import { PaginationDto } from '../common/dto/pagination.dto';
25+
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
26+
import { RolesGuard } from '../auth/roles.guard';
27+
import { Roles } from '../auth/roles.decorator';
28+
import { UserRole } from '../users/entities/user.entity';
29+
30+
@ApiTags('Assets')
31+
@Controller('assets')
32+
@UseGuards(JwtAuthGuard, RolesGuard)
33+
@ApiBearerAuth('JWT-auth')
34+
export class AssetsController {
35+
constructor(private readonly assetsService: AssetsService) {}
36+
37+
@Post()
38+
@Roles(UserRole.ADMIN)
39+
@ApiOperation({ summary: 'Create a new asset (ADMIN only)' })
40+
@ApiResponse({
41+
status: HttpStatus.CREATED,
42+
description: 'Asset successfully created',
43+
})
44+
@ApiResponse({
45+
status: HttpStatus.BAD_REQUEST,
46+
description: 'Invalid input data',
47+
})
48+
create(@Body() createAssetDto: CreateAssetDto) {
49+
return this.assetsService.create(createAssetDto);
50+
}
51+
52+
@Get()
53+
@ApiOperation({ summary: 'Get all assets with pagination' })
54+
@ApiQuery({ name: 'page', required: false, type: Number })
55+
@ApiQuery({ name: 'limit', required: false, type: Number })
56+
@ApiResponse({
57+
status: HttpStatus.OK,
58+
description: 'Return paginated assets',
59+
})
60+
findAll(@Query() paginationDto: PaginationDto) {
61+
return this.assetsService.findAll(paginationDto);
62+
}
63+
64+
@Get(':id')
65+
@ApiOperation({ summary: 'Get an asset by ID' })
66+
@ApiParam({ name: 'id', description: 'Asset ID' })
67+
@ApiResponse({
68+
status: HttpStatus.OK,
69+
description: 'Return the asset',
70+
})
71+
@ApiResponse({
72+
status: HttpStatus.NOT_FOUND,
73+
description: 'Asset not found',
74+
})
75+
findOne(@Param('id') id: string) {
76+
return this.assetsService.findOne(id);
77+
}
78+
79+
@Patch(':id')
80+
@Roles(UserRole.ADMIN)
81+
@ApiOperation({ summary: 'Update an asset (ADMIN only)' })
82+
@ApiParam({ name: 'id', description: 'Asset ID' })
83+
@ApiResponse({
84+
status: HttpStatus.OK,
85+
description: 'Asset successfully updated',
86+
})
87+
@ApiResponse({
88+
status: HttpStatus.BAD_REQUEST,
89+
description: 'Invalid input data',
90+
})
91+
@ApiResponse({
92+
status: HttpStatus.NOT_FOUND,
93+
description: 'Asset not found',
94+
})
95+
update(@Param('id') id: string, @Body() updateAssetDto: UpdateAssetDto) {
96+
return this.assetsService.update(id, updateAssetDto);
97+
}
98+
99+
@Delete(':id')
100+
@Roles(UserRole.ADMIN)
101+
@ApiOperation({ summary: 'Delete an asset (ADMIN only)' })
102+
@ApiParam({ name: 'id', description: 'Asset ID' })
103+
@ApiResponse({
104+
status: HttpStatus.NO_CONTENT,
105+
description: 'Asset successfully deleted',
106+
})
107+
@ApiResponse({
108+
status: HttpStatus.NOT_FOUND,
109+
description: 'Asset not found',
110+
})
111+
remove(@Param('id') id: string) {
112+
return this.assetsService.remove(id);
113+
}
114+
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { Module } from '@nestjs/common';
22
import { TypeOrmModule } from '@nestjs/typeorm';
33
import { Asset } from './entities/asset.entity';
4+
import { AssetsController } from './assets.controller';
5+
import { AssetsService } from './assets.service';
46

57
@Module({
68
imports: [TypeOrmModule.forFeature([Asset])],
7-
exports: [TypeOrmModule],
9+
controllers: [AssetsController],
10+
providers: [AssetsService],
11+
exports: [AssetsService],
812
})
913
export class AssetsModule {}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Injectable, NotFoundException } from '@nestjs/common';
2+
import { InjectRepository } from '@nestjs/typeorm';
3+
import { Repository } from 'typeorm';
4+
import { Asset } from './entities/asset.entity';
5+
import { CreateAssetDto } from './dto/create-asset.dto';
6+
import { UpdateAssetDto } from './dto/update-asset.dto';
7+
import { PaginationDto, PaginatedResponseDto, paginate } from '../common';
8+
9+
@Injectable()
10+
export class AssetsService {
11+
constructor(
12+
@InjectRepository(Asset)
13+
private readonly assetRepository: Repository<Asset>,
14+
) {}
15+
16+
async create(createAssetDto: CreateAssetDto): Promise<Asset> {
17+
const asset = this.assetRepository.create(createAssetDto);
18+
return this.assetRepository.save(asset);
19+
}
20+
21+
async findAll(
22+
paginationDto: PaginationDto,
23+
): Promise<PaginatedResponseDto<Asset>> {
24+
return paginate(this.assetRepository, paginationDto, {
25+
order: { createdAt: 'DESC' },
26+
});
27+
}
28+
29+
async findOne(id: string): Promise<Asset> {
30+
const asset = await this.assetRepository.findOne({ where: { id } });
31+
if (!asset) {
32+
throw new NotFoundException(`Asset with ID ${id} not found`);
33+
}
34+
return asset;
35+
}
36+
37+
async update(id: string, updateAssetDto: UpdateAssetDto): Promise<Asset> {
38+
const asset = await this.findOne(id);
39+
Object.assign(asset, updateAssetDto);
40+
return this.assetRepository.save(asset);
41+
}
42+
43+
async remove(id: string): Promise<void> {
44+
const asset = await this.findOne(id);
45+
await this.assetRepository.remove(asset);
46+
}
47+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import {
2+
IsString,
3+
IsOptional,
4+
IsEnum,
5+
IsNumber,
6+
IsDateString,
7+
} from 'class-validator';
8+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
9+
import { AssetStatus, AssetCondition } from '../entities/asset.entity';
10+
11+
export class CreateAssetDto {
12+
@ApiProperty({ description: 'Asset name' })
13+
@IsString()
14+
name: string;
15+
16+
@ApiPropertyOptional({ description: 'Asset description' })
17+
@IsOptional()
18+
@IsString()
19+
description?: string;
20+
21+
@ApiProperty({ description: 'Asset category' })
22+
@IsString()
23+
category: string;
24+
25+
@ApiPropertyOptional({ description: 'Serial number' })
26+
@IsOptional()
27+
@IsString()
28+
serialNumber?: string;
29+
30+
@ApiPropertyOptional({ description: 'Asset status', enum: AssetStatus })
31+
@IsOptional()
32+
@IsEnum(AssetStatus)
33+
status?: AssetStatus;
34+
35+
@ApiPropertyOptional({ description: 'Asset condition', enum: AssetCondition })
36+
@IsOptional()
37+
@IsEnum(AssetCondition)
38+
condition?: AssetCondition;
39+
40+
@ApiPropertyOptional({ description: 'Purchase date' })
41+
@IsOptional()
42+
@IsDateString()
43+
purchaseDate?: Date;
44+
45+
@ApiPropertyOptional({ description: 'Purchase value' })
46+
@IsOptional()
47+
@IsNumber()
48+
purchaseValue?: number;
49+
50+
@ApiPropertyOptional({ description: 'Current value' })
51+
@IsOptional()
52+
@IsNumber()
53+
currentValue?: number;
54+
55+
@ApiPropertyOptional({ description: 'User ID assigned to' })
56+
@IsOptional()
57+
@IsString()
58+
assignedToUserId?: string;
59+
60+
@ApiPropertyOptional({ description: 'Department ID' })
61+
@IsOptional()
62+
@IsString()
63+
departmentId?: string;
64+
65+
@ApiPropertyOptional({ description: 'Location ID' })
66+
@IsOptional()
67+
@IsString()
68+
locationId?: string;
69+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { PartialType } from '@nestjs/swagger';
2+
import { CreateAssetDto } from './create-asset.dto';
3+
4+
export class UpdateAssetDto extends PartialType(CreateAssetDto) {}

0 commit comments

Comments
 (0)