mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 00:38:11 +00:00
wagon types added UI with API integrations
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Transform } from 'class-transformer';
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsInt,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
MaxLength,
|
||||
Min,
|
||||
} from 'class-validator';
|
||||
|
||||
const toNumber = ({ value }: { value: unknown }) =>
|
||||
value === '' || value == null ? value : Number(value);
|
||||
|
||||
const toBoolean = ({ value }: { value: unknown }) => {
|
||||
if (typeof value === 'boolean') return value;
|
||||
if (value === 'true') return true;
|
||||
if (value === 'false') return false;
|
||||
return value;
|
||||
};
|
||||
|
||||
const toStringArray = ({ value }: { value: unknown }) => {
|
||||
if (Array.isArray(value)) return value;
|
||||
if (typeof value !== 'string') return [];
|
||||
return value
|
||||
.split(',')
|
||||
.map((entry) => entry.trim())
|
||||
.filter(Boolean);
|
||||
};
|
||||
|
||||
export class CreateWagonTypeDto {
|
||||
@ApiProperty({ maxLength: 32, example: 'FLAT' })
|
||||
@IsString()
|
||||
@MaxLength(32)
|
||||
code!: string;
|
||||
|
||||
@ApiProperty({ maxLength: 100, example: 'Flat wagon' })
|
||||
@IsString()
|
||||
@MaxLength(100)
|
||||
name!: string;
|
||||
|
||||
@ApiProperty({ example: 60 })
|
||||
@Transform(toNumber)
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
capacityTons!: number;
|
||||
|
||||
@ApiProperty({ example: 14.2 })
|
||||
@Transform(toNumber)
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
lengthMeters!: number;
|
||||
|
||||
@ApiPropertyOptional({ example: 45 })
|
||||
@IsOptional()
|
||||
@Transform(toNumber)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
maxWagonsPerTrain?: number;
|
||||
|
||||
@ApiPropertyOptional({ type: [String], example: ['container', 'break-bulk'] })
|
||||
@IsOptional()
|
||||
@Transform(toStringArray)
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
supportedLoadTypes?: string[];
|
||||
|
||||
@ApiPropertyOptional({ default: true })
|
||||
@IsOptional()
|
||||
@Transform(toBoolean)
|
||||
@IsBoolean()
|
||||
isActive?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
|
||||
import { CreateWagonTypeDto } from './create-wagon-type.dto';
|
||||
|
||||
export class UpdateWagonTypeDto extends PartialType(CreateWagonTypeDto) {}
|
||||
@@ -1,5 +1,19 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Param,
|
||||
ParseUUIDPipe,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
||||
import { CreateWagonTypeDto } from './dto/create-wagon-type.dto';
|
||||
import { UpdateWagonTypeDto } from './dto/update-wagon-type.dto';
|
||||
import { WagonTypesService } from './wagon-types.service';
|
||||
import { WagonType } from './entities/wagon-type.entity';
|
||||
|
||||
@@ -8,9 +22,37 @@ import { WagonType } from './entities/wagon-type.entity';
|
||||
export class WagonTypesController {
|
||||
constructor(private readonly wagonTypesService: WagonTypesService) {}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Get all active wagon types' })
|
||||
async findAll(): Promise<WagonType[]> {
|
||||
return this.wagonTypesService.findAll();
|
||||
@Post()
|
||||
@ApiOperation({ summary: 'Create a wagon type' })
|
||||
async create(@Body() dto: CreateWagonTypeDto): Promise<WagonType> {
|
||||
return this.wagonTypesService.create(dto);
|
||||
}
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Get wagon types' })
|
||||
async findAll(@Query() query: Record<string, string | undefined>): Promise<WagonType[]> {
|
||||
return this.wagonTypesService.findAll(query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Get a wagon type by ID' })
|
||||
async findOne(@Param('id', ParseUUIDPipe) id: string): Promise<WagonType> {
|
||||
return this.wagonTypesService.findById(id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@ApiOperation({ summary: 'Update a wagon type' })
|
||||
async update(
|
||||
@Param('id', ParseUUIDPipe) id: string,
|
||||
@Body() dto: UpdateWagonTypeDto,
|
||||
): Promise<WagonType> {
|
||||
return this.wagonTypesService.update(id, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@ApiOperation({ summary: 'Deactivate a wagon type' })
|
||||
async remove(@Param('id', ParseUUIDPipe) id: string): Promise<void> {
|
||||
return this.wagonTypesService.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { ConflictException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { FindOptionsOrder } from 'typeorm';
|
||||
|
||||
import { CreateWagonTypeDto } from './dto/create-wagon-type.dto';
|
||||
import { UpdateWagonTypeDto } from './dto/update-wagon-type.dto';
|
||||
import { WagonType } from './entities/wagon-type.entity';
|
||||
import { WagonTypesRepository } from './wagon-types.repository';
|
||||
|
||||
@@ -7,13 +10,52 @@ import { WagonTypesRepository } from './wagon-types.repository';
|
||||
export class WagonTypesService {
|
||||
constructor(private readonly wagonTypesRepository: WagonTypesRepository) {}
|
||||
|
||||
async findAll(): Promise<WagonType[]> {
|
||||
return this.wagonTypesRepository.findAll({
|
||||
where: { isActive: true },
|
||||
order: { code: 'ASC' },
|
||||
async create(dto: CreateWagonTypeDto): Promise<WagonType> {
|
||||
const code = dto.code.trim().toUpperCase();
|
||||
const existing = await this.wagonTypesRepository.findAll({ where: { code } });
|
||||
if (existing.length > 0) {
|
||||
throw new ConflictException(`Wagon type code "${code}" already exists`);
|
||||
}
|
||||
|
||||
return this.wagonTypesRepository.create({
|
||||
...dto,
|
||||
code,
|
||||
name: dto.name.trim(),
|
||||
supportedLoadTypes: dto.supportedLoadTypes ?? [],
|
||||
isActive: dto.isActive ?? true,
|
||||
});
|
||||
}
|
||||
|
||||
async findAll(query: Record<string, string | undefined> = {}): Promise<WagonType[]> {
|
||||
const isActive =
|
||||
query.isActive === 'all'
|
||||
? undefined
|
||||
: query.isActive === undefined
|
||||
? true
|
||||
: query.isActive === 'true';
|
||||
const sortBy = ['code', 'name', 'capacityTons', 'lengthMeters', 'isActive'].includes(
|
||||
query.sortBy ?? '',
|
||||
)
|
||||
? (query.sortBy as keyof WagonType)
|
||||
: 'code';
|
||||
const sortOrder = query.sortOrder?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
|
||||
|
||||
return this.wagonTypesRepository.findAll({
|
||||
where: isActive === undefined ? {} : { isActive },
|
||||
order: { [sortBy]: sortOrder } as FindOptionsOrder<WagonType>,
|
||||
});
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<WagonType> {
|
||||
const wagonType = await this.wagonTypesRepository.findById(id);
|
||||
|
||||
if (!wagonType) {
|
||||
throw new NotFoundException(`Wagon type ${id} not found`);
|
||||
}
|
||||
|
||||
return wagonType;
|
||||
}
|
||||
|
||||
async findByCode(code: string): Promise<WagonType> {
|
||||
const [wagonType] = await this.wagonTypesRepository.findAll({ where: { code } });
|
||||
|
||||
@@ -23,4 +65,33 @@ export class WagonTypesService {
|
||||
|
||||
return wagonType;
|
||||
}
|
||||
|
||||
async update(id: string, dto: UpdateWagonTypeDto): Promise<WagonType> {
|
||||
const wagonType = await this.findById(id);
|
||||
const nextCode = dto.code?.trim().toUpperCase();
|
||||
|
||||
if (nextCode && nextCode !== wagonType.code) {
|
||||
const existing = await this.wagonTypesRepository.findAll({ where: { code: nextCode } });
|
||||
if (existing.length > 0) {
|
||||
throw new ConflictException(`Wagon type code "${nextCode}" already exists`);
|
||||
}
|
||||
}
|
||||
|
||||
const updated = await this.wagonTypesRepository.update(id, {
|
||||
...dto,
|
||||
...(nextCode ? { code: nextCode } : {}),
|
||||
...(dto.name ? { name: dto.name.trim() } : {}),
|
||||
});
|
||||
|
||||
if (!updated) {
|
||||
throw new NotFoundException(`Wagon type ${id} not found`);
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
await this.findById(id);
|
||||
await this.wagonTypesRepository.update(id, { isActive: false });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user