Skip to content

Commit f06624b

Browse files
authored
Merge pull request #281 from Mkalbani/feat/asset-sub
implemented Asset Subcategory Module
2 parents 40fb41b + 9565945 commit f06624b

File tree

11 files changed

+161
-22
lines changed

11 files changed

+161
-22
lines changed

backend/package-lock.json

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@nestjs/common": "^10.0.0",
2424
"@nestjs/config": "^3.0.0",
2525
"@nestjs/core": "^10.0.0",
26+
"@nestjs/mapped-types": "^2.1.0",
2627
"@nestjs/platform-express": "^10.0.0",
2728
"@nestjs/typeorm": "^10.0.0",
2829
"class-transformer": "^0.5.1",

backend/src/app.module.ts

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,30 @@ import { AssetCategoriesModule } from './asset-categories/asset-categories.modul
77
import { AssetCategory } from './asset-categories/asset-category.entity';
88
import { DepartmentsModule } from './departments/departments.module';
99
import { Department } from './departments/department.entity';
10+
import { AssetSubcategoriesModule } from './asset-subcategories/asset-subcategories.module';
11+
import { AssetSubcategory } from './asset-subcategories/entities/asset-subcategory.entity';
1012

1113
@Module({
1214
imports: [
1315
ConfigModule.forRoot({
1416
isGlobal: true,
1517
}),
1618
TypeOrmModule.forRootAsync({
17-
imports: [ConfigModule,
18-
// --- ADD THIS CONFIGURATION ---
19-
I18nModule.forRoot({
20-
fallbackLanguage: 'en',
21-
loaderOptions: {
22-
path: path.join(__dirname, '/i18n/'), // Directory for translation files
23-
watch: true, // Watch for changes in translation files
24-
},
25-
resolvers: [
26-
// Order matters: checks query param, then header, then browser settings
27-
new QueryResolver(['lang', 'l']),
28-
new HeaderResolver(['x-custom-lang-header']),
29-
AcceptLanguageResolver, // Standard 'Accept-Language' header
30-
],
31-
}),
32-
// --- END OF CONFIGURATION ---
33-
],],
19+
imports: [ConfigModule],
3420
useFactory: (configService: ConfigService) => ({
3521
type: 'postgres',
3622
host: configService.get('DB_HOST', 'localhost'),
3723
port: configService.get('DB_PORT', 5432),
3824
username: configService.get('DB_USERNAME', 'postgres'),
3925
password: configService.get('DB_PASSWORD', 'password'),
4026
database: configService.get('DB_DATABASE', 'manage_assets'),
41-
entities: [AssetCategory, Department],
27+
entities: [AssetCategory, AssetSubcategory, Department],
4228
synchronize: configService.get('NODE_ENV') !== 'production', // Only for development
4329
}),
4430
inject: [ConfigService],
4531
}),
4632
AssetCategoriesModule,
33+
AssetSubcategoriesModule,
4734
DepartmentsModule,
4835
],
4936
controllers: [AppController],

backend/src/asset-categories/asset-category.entity.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany } from 'typeorm';
2+
import { AssetSubcategory } from '../asset-subcategories/entities/asset-subcategory.entity';
23

34
@Entity('asset_categories')
45
export class AssetCategory {
@@ -17,8 +18,7 @@ export class AssetCategory {
1718
@UpdateDateColumn()
1819
updatedAt: Date;
1920

20-
// Relationship with assets (one-to-many)
21-
// This will be uncommented when the Asset entity is created
22-
// @OneToMany(() => Asset, asset => asset.category)
23-
// assets: Asset[];
21+
// Relationship with subcategories
22+
@OneToMany(() => AssetSubcategory, subcategory => subcategory.parentCategory)
23+
subcategories: AssetSubcategory[];
2424
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Controller, Get, Post, Body, Param, Patch, Delete } from '@nestjs/common';
2+
import { AssetSubcategoriesService } from './asset-subcategories.service';
3+
import { CreateAssetSubcategoryDto } from './dto/create-asset-subcategory.dto';
4+
import { UpdateAssetSubcategoryDto } from './dto/update-asset-subcategory.dto';
5+
6+
@Controller('asset-subcategories')
7+
export class AssetSubcategoriesController {
8+
constructor(private readonly service: AssetSubcategoriesService) {}
9+
10+
@Post()
11+
create(@Body() dto: CreateAssetSubcategoryDto) {
12+
return this.service.create(dto);
13+
}
14+
15+
@Get()
16+
findAll() {
17+
return this.service.findAll();
18+
}
19+
20+
@Get(':id')
21+
findOne(@Param('id') id: string) {
22+
return this.service.findOne(Number(id));
23+
}
24+
25+
@Patch(':id')
26+
update(@Param('id') id: string, @Body() dto: UpdateAssetSubcategoryDto) {
27+
return this.service.update(Number(id), dto);
28+
}
29+
30+
@Delete(':id')
31+
remove(@Param('id') id: string) {
32+
return this.service.remove(Number(id));
33+
}
34+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Module } from '@nestjs/common';
2+
import { TypeOrmModule } from '@nestjs/typeorm';
3+
import { AssetSubcategory } from './entities/asset-subcategory.entity';
4+
import { AssetSubcategoriesService } from './asset-subcategories.service';
5+
import { AssetSubcategoriesController } from './asset-subcategories.controller';
6+
7+
@Module({
8+
imports: [TypeOrmModule.forFeature([AssetSubcategory])],
9+
providers: [AssetSubcategoriesService],
10+
controllers: [AssetSubcategoriesController],
11+
})
12+
export class AssetSubcategoriesModule {}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Injectable, NotFoundException } from '@nestjs/common';
2+
import { InjectRepository } from '@nestjs/typeorm';
3+
import { Repository } from 'typeorm';
4+
import { AssetSubcategory } from './entities/asset-subcategory.entity';
5+
import { CreateAssetSubcategoryDto } from './dto/create-asset-subcategory.dto';
6+
import { UpdateAssetSubcategoryDto } from './dto/update-asset-subcategory.dto';
7+
import { AssetCategory } from '../asset-categories/asset-category.entity';
8+
9+
@Injectable()
10+
export class AssetSubcategoriesService {
11+
constructor(
12+
@InjectRepository(AssetSubcategory)
13+
private readonly subcategoryRepo: Repository<AssetSubcategory>,
14+
@InjectRepository(AssetCategory)
15+
private readonly categoryRepo: Repository<AssetCategory>,
16+
) {}
17+
18+
async create(dto: CreateAssetSubcategoryDto): Promise<AssetSubcategory> {
19+
const parentCategory = await this.categoryRepo.findOne({ where: { id: dto.parentCategoryId } });
20+
if (!parentCategory) throw new NotFoundException('Parent category not found');
21+
const subcategory = this.subcategoryRepo.create({ ...dto, parentCategory });
22+
return this.subcategoryRepo.save(subcategory);
23+
}
24+
25+
findAll(): Promise<AssetSubcategory[]> {
26+
return this.subcategoryRepo.find({ relations: ['parentCategory'] });
27+
}
28+
29+
findOne(id: number): Promise<AssetSubcategory> {
30+
return this.subcategoryRepo.findOne({ where: { id }, relations: ['parentCategory'] });
31+
}
32+
33+
async update(id: number, dto: UpdateAssetSubcategoryDto): Promise<AssetSubcategory> {
34+
const subcategory = await this.subcategoryRepo.findOne({ where: { id } });
35+
if (!subcategory) throw new NotFoundException('Subcategory not found');
36+
if (dto.parentCategoryId) {
37+
const parentCategory = await this.categoryRepo.findOne({ where: { id: dto.parentCategoryId } });
38+
if (!parentCategory) throw new NotFoundException('Parent category not found');
39+
subcategory.parentCategory = parentCategory;
40+
}
41+
if (dto.name) subcategory.name = dto.name;
42+
return this.subcategoryRepo.save(subcategory);
43+
}
44+
45+
async remove(id: number): Promise<void> {
46+
await this.subcategoryRepo.delete(id);
47+
}
48+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { IsInt, IsNotEmpty, IsString } from 'class-validator';
2+
3+
export class CreateAssetSubcategoryDto {
4+
@IsNotEmpty()
5+
@IsString()
6+
name: string;
7+
8+
@IsNotEmpty()
9+
@IsInt()
10+
parentCategoryId: number;
11+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { PartialType } from '@nestjs/mapped-types';
2+
import { CreateAssetSubcategoryDto } from './create-asset-subcategory.dto';
3+
4+
export class UpdateAssetSubcategoryDto extends PartialType(CreateAssetSubcategoryDto) {
5+
name?: string;
6+
parentCategoryId?: number;
7+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
2+
import { AssetCategory } from '../../asset-categories/asset-category.entity';
3+
4+
@Entity('asset_subcategories')
5+
export class AssetSubcategory {
6+
@PrimaryGeneratedColumn()
7+
id: number;
8+
9+
@Column()
10+
name: string;
11+
12+
@ManyToOne(() => AssetCategory, category => category.subcategories, { nullable: false })
13+
parentCategory: AssetCategory;
14+
}

0 commit comments

Comments
 (0)