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/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { AssetsModule } from './assets/assets.module';
import { QueueModule } from './queue/queue.module';
import { StorageModule } from './storage/storage.module';
import { CacheService } from './cache/cache.service';
import { LocationsModule } from './locations/locations.module';
import { ContractsModule } from './contracts/contracts.module';
import { LicensesModule } from './licenses/licenses.module';
import { PurchaseOrdersModule } from './purchase-orders/purchase-orders.module';
Expand Down Expand Up @@ -78,6 +79,8 @@ import { TasksModule } from './tasks/tasks.module';
LicensesModule,
PurchaseOrdersModule,
TasksModule,
],
LocationsModule,
],
controllers: [AppController],
providers: [
Expand Down
18 changes: 18 additions & 0 deletions backend/src/assets/asset.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ export class Asset {
@Column({ nullable: true })
location: string;

@Column({ nullable: true })
city: string;

@Column({ nullable: true })
state: string;

@Column({ nullable: true })
building: string;

@Column({ nullable: true })
room: string;

@Column({ nullable: true })
aisle: string;

@Column({ nullable: true })
shelf: string;

@Column({ nullable: true })
assignedToId: string;

Expand Down
24 changes: 24 additions & 0 deletions backend/src/assets/dtos/create-asset.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,30 @@ export class CreateAssetDto {
@IsString()
location?: string;

@IsOptional()
@IsString()
city?: string;

@IsOptional()
@IsString()
state?: string;

@IsOptional()
@IsString()
building?: string;

@IsOptional()
@IsString()
room?: string;

@IsOptional()
@IsString()
aisle?: string;

@IsOptional()
@IsString()
shelf?: string;

@IsOptional()
@IsString()
assignedToId?: string;
Expand Down
24 changes: 24 additions & 0 deletions backend/src/assets/dtos/update-asset.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,30 @@ export class UpdateAssetDto {
@IsString()
location?: string;

@IsOptional()
@IsString()
city?: string;

@IsOptional()
@IsString()
state?: string;

@IsOptional()
@IsString()
building?: string;

@IsOptional()
@IsString()
room?: string;

@IsOptional()
@IsString()
aisle?: string;

@IsOptional()
@IsString()
shelf?: string;

@IsOptional()
@IsString()
assignedToId?: string;
Expand Down
18 changes: 18 additions & 0 deletions backend/src/assets/entities/asset.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ export class Asset {
@Column({ nullable: true })
location: string;

@Column({ nullable: true })
city: string;

@Column({ nullable: true })
state: string;

@Column({ nullable: true })
building: string;

@Column({ nullable: true })
room: string;

@Column({ nullable: true })
aisle: string;

@Column({ nullable: true })
shelf: string;

@Column({ nullable: true })
assignedToId: string;

Expand Down
27 changes: 27 additions & 0 deletions backend/src/common/interceptors/response.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

export interface ApiResponse<T> {
success: boolean;
data: T;
timestamp: string;
path: string;
}

@Injectable()
export class ResponseInterceptor<T> implements NestInterceptor<T, ApiResponse<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<ApiResponse<T>> {
const ctx = context.switchToHttp();
const request = ctx.getRequest();

return next.handle().pipe(
map(data => ({
success: true,
data,
timestamp: new Date().toISOString(),
path: request.url,
})),
);
}
}
46 changes: 46 additions & 0 deletions backend/src/locations/dtos/create-location.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { IsString, IsOptional, IsBoolean } from 'class-validator';

export class CreateLocationDto {
@IsString()
name: string;

@IsOptional()
@IsString()
address?: string;

@IsOptional()
@IsString()
city?: string;

@IsOptional()
@IsString()
state?: string;

@IsOptional()
@IsString()
country?: string;

@IsOptional()
@IsString()
postalCode?: string;

@IsOptional()
@IsString()
building?: string;

@IsOptional()
@IsString()
floor?: string;

@IsOptional()
@IsString()
room?: string;

@IsOptional()
@IsString()
description?: string;

@IsOptional()
@IsBoolean()
isActive?: boolean;
}
47 changes: 47 additions & 0 deletions backend/src/locations/dtos/update-location.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { IsString, IsOptional, IsBoolean } from 'class-validator';

export class UpdateLocationDto {
@IsOptional()
@IsString()
name?: string;

@IsOptional()
@IsString()
address?: string;

@IsOptional()
@IsString()
city?: string;

@IsOptional()
@IsString()
state?: string;

@IsOptional()
@IsString()
country?: string;

@IsOptional()
@IsString()
postalCode?: string;

@IsOptional()
@IsString()
building?: string;

@IsOptional()
@IsString()
floor?: string;

@IsOptional()
@IsString()
room?: string;

@IsOptional()
@IsString()
description?: string;

@IsOptional()
@IsBoolean()
isActive?: boolean;
}
46 changes: 46 additions & 0 deletions backend/src/locations/entities/location.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';

@Entity('locations')
export class Location {
@PrimaryGeneratedColumn('uuid')
id: string;

@Column()
name: string;

@Column({ nullable: true })
address: string;

@Column({ nullable: true })
city: string;

@Column({ nullable: true })
state: string;

@Column({ nullable: true })
country: string;

@Column({ nullable: true })
postalCode: string;

@Column({ nullable: true })
building: string;

@Column({ nullable: true })
floor: string;

@Column({ nullable: true })
room: string;

@Column({ nullable: true, type: 'text' })
description: string;

@Column({ default: true })
isActive: boolean;

@CreateDateColumn()
createdAt: Date;

@UpdateDateColumn()
updatedAt: Date;
}
37 changes: 37 additions & 0 deletions backend/src/locations/locations.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Controller, Get, Post, Put, Delete, Param, Body, Query, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { LocationsService } from './locations.service';
import { CreateLocationDto } from './dtos/create-location.dto';
import { UpdateLocationDto } from './dtos/update-location.dto';

@Controller('locations')
@UseGuards(AuthGuard('jwt'))
export class LocationsController {
constructor(private readonly locationsService: LocationsService) {}

@Post()
async create(@Body() dto: CreateLocationDto) {
return this.locationsService.create(dto);
}

@Get()
async findAll(@Query() query: { page?: number; limit?: number; isActive?: boolean }) {
return this.locationsService.findAll(query);
}

@Get(':id')
async findOne(@Param('id') id: string) {
return this.locationsService.findById(id);
}

@Put(':id')
async update(@Param('id') id: string, @Body() dto: UpdateLocationDto) {
return this.locationsService.update(id, dto);
}

@Delete(':id')
async remove(@Param('id') id: string) {
await this.locationsService.remove(id);
return { message: 'Location deleted successfully' };
}
}
13 changes: 13 additions & 0 deletions backend/src/locations/locations.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Location } from './entities/location.entity';
import { LocationsService } from './locations.service';
import { LocationsController } from './locations.controller';

@Module({
imports: [TypeOrmModule.forFeature([Location])],
controllers: [LocationsController],
providers: [LocationsService],
exports: [LocationsService],
})
export class LocationsModule {}
48 changes: 48 additions & 0 deletions backend/src/locations/locations.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Location } from './entities/location.entity';
import { CreateLocationDto } from './dtos/create-location.dto';
import { UpdateLocationDto } from './dtos/update-location.dto';

@Injectable()
export class LocationsService {
constructor(
@InjectRepository(Location)
private readonly locationRepository: Repository<Location>,
) {}

async create(dto: CreateLocationDto): Promise<Location> {
const location = this.locationRepository.create(dto);
return this.locationRepository.save(location);
}

async findAll(query: { page?: number; limit?: number; isActive?: boolean } = {}): Promise<{ data: Location[]; total: number }> {
const { page = 1, limit = 20, isActive } = query;
const qb = this.locationRepository.createQueryBuilder('location')
.skip((page - 1) * limit)
.take(limit);

if (isActive !== undefined) qb.andWhere('location.isActive = :isActive', { isActive });

const [data, total] = await qb.getManyAndCount();
return { data, total };
}

async findById(id: string): Promise<Location> {
const location = await this.locationRepository.findOne({ where: { id } });
if (!location) throw new NotFoundException('Location not found');
return location;
}

async update(id: string, dto: UpdateLocationDto): Promise<Location> {
const location = await this.findById(id);
Object.assign(location, dto);
return this.locationRepository.save(location);
}

async remove(id: string): Promise<void> {
const location = await this.findById(id);
await this.locationRepository.softDelete(location.id);
}
}
Loading
Loading