Updated seat class and coach mangement

This commit is contained in:
Roba Boru
2026-05-19 16:49:22 +03:00
parent 071a57a668
commit a73277ae38
23 changed files with 310 additions and 61 deletions

View File

@@ -1,7 +1,7 @@
import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
import { Body, Controller, Get, Param, Patch, Post, Query, UseGuards } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam, ApiQuery, ApiResponse, ApiBody } from '@nestjs/swagger';
import { FleetService } from './fleet.service';
import { CreateTrainServiceDto, CreateCoachDto, CreateSeatBatchDto } from './fleet.dto';
import { CreateTrainServiceDto, CreateCoachDto, CreateSeatBatchDto, UpdateCoachDto } from './fleet.dto';
import { JwtGuard } from '../../common/jwt.guard';
@ApiTags('Fleet')
@@ -10,9 +10,47 @@ import { JwtGuard } from '../../common/jwt.guard';
@ApiBearerAuth('JWT-auth')
export class FleetController {
constructor(private service: FleetService) {}
@Get('services') @ApiOperation({ summary: 'List train services' }) getServices() { return this.service.getServices(); }
@Post('services') @ApiOperation({ summary: 'Create train service' }) createService(@Body() dto: CreateTrainServiceDto) { return this.service.createService(dto); }
@Post('coaches') @ApiOperation({ summary: 'Add coach to trip' }) createCoach(@Body() dto: CreateCoachDto) { return this.service.createCoach(dto); }
@Post('seats/batch')@ApiOperation({ summary: 'Batch-create seats for coach' }) createSeatBatch(@Body() dto: CreateSeatBatchDto) { return this.service.createSeatBatch(dto); }
@Get('analytics') @ApiOperation({ summary: 'Fleet analytics' }) getAnalytics() { return this.service.getAnalytics(); }
@Get('services')
@ApiOperation({ summary: 'List train services' })
@ApiResponse({ status: 200, description: 'Returns all train services with recent trips' })
getServices() { return this.service.getServices(); }
@Post('services')
@ApiOperation({ summary: 'Create a train service' })
@ApiBody({ type: CreateTrainServiceDto })
@ApiResponse({ status: 201, description: 'Train service created' })
createService(@Body() dto: CreateTrainServiceDto) { return this.service.createService(dto); }
@Get('coaches')
@ApiOperation({ summary: 'List coaches' })
@ApiQuery({ name: 'tripId', required: false, description: 'Filter by trip UUID' })
@ApiResponse({ status: 200, description: 'Returns coaches with seat class and seat count' })
listCoaches(@Query('tripId') tripId?: string) { return this.service.listCoaches(tripId); }
@Post('coaches')
@ApiOperation({ summary: 'Add a coach to a trip' })
@ApiBody({ type: CreateCoachDto })
@ApiResponse({ status: 201, description: 'Coach created' })
createCoach(@Body() dto: CreateCoachDto) { return this.service.createCoach(dto); }
@Patch('coaches/:id')
@ApiOperation({ summary: 'Update a coach' })
@ApiParam({ name: 'id', description: 'Coach UUID' })
@ApiBody({ type: UpdateCoachDto })
@ApiResponse({ status: 200, description: 'Coach updated' })
@ApiResponse({ status: 404, description: 'Coach not found' })
updateCoach(@Param('id') id: string, @Body() dto: UpdateCoachDto) { return this.service.updateCoach(id, dto); }
@Post('seats/batch')
@ApiOperation({ summary: 'Batch-create seats for a coach' })
@ApiBody({ type: CreateSeatBatchDto })
@ApiResponse({ status: 201, description: 'Seats created' })
@ApiResponse({ status: 404, description: 'Coach not found' })
createSeatBatch(@Body() dto: CreateSeatBatchDto) { return this.service.createSeatBatch(dto); }
@Get('analytics')
@ApiOperation({ summary: 'Fleet analytics' })
@ApiResponse({ status: 200, description: 'Returns fleet occupancy analytics' })
getAnalytics() { return this.service.getAnalytics(); }
}

View File

@@ -1,6 +1,5 @@
import { IsString, IsEnum, IsInt } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { ServiceClass } from '@prisma/client';
import { IsString, IsInt, IsOptional } from 'class-validator';
import { ApiProperty, ApiPropertyOptional, PartialType, OmitType } from '@nestjs/swagger';
export class CreateTrainServiceDto {
@ApiProperty({ example: '301' }) @IsString() number: string;
@@ -8,13 +7,15 @@ export class CreateTrainServiceDto {
}
export class CreateCoachDto {
@ApiProperty() @IsString() tripId: string;
@ApiProperty({ example: 'trip-uuid' }) @IsString() tripId: string;
@ApiProperty({ example: 'A' }) @IsString() label: string;
@ApiProperty({ enum: ServiceClass }) @IsEnum(ServiceClass) serviceClass: ServiceClass;
@ApiProperty({ example: 'seat-class-uuid' }) @IsString() seatClassId: string;
}
export class UpdateCoachDto extends PartialType(OmitType(CreateCoachDto, ['tripId'] as const)) {}
export class CreateSeatBatchDto {
@ApiProperty() @IsString() coachId: string;
@ApiProperty({ example: 'coach-uuid' }) @IsString() coachId: string;
@ApiProperty({ example: 10 }) @IsInt() rows: number;
@ApiProperty({ example: ['A', 'B', 'C', 'D'] }) cols: string[];
}

View File

@@ -1,13 +1,30 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../common/prisma.service';
import { CreateTrainServiceDto, CreateCoachDto, CreateSeatBatchDto } from './fleet.dto';
import { CreateTrainServiceDto, CreateCoachDto, CreateSeatBatchDto, UpdateCoachDto } from './fleet.dto';
@Injectable()
export class FleetService {
constructor(private prisma: PrismaService) {}
getServices() { return this.prisma.trainService.findMany({ include: { trips: { take: 5, orderBy: { departureAt: 'desc' } } } }); }
createService(dto: CreateTrainServiceDto) { return this.prisma.trainService.create({ data: dto }); }
createCoach(dto: CreateCoachDto) { return this.prisma.coach.create({ data: dto }); }
listCoaches(tripId?: string) {
return this.prisma.coach.findMany({
where: tripId ? { tripId } : undefined,
include: { seatClass: { select: { id: true, name: true, basePrice: true } }, _count: { select: { seats: true } } },
orderBy: { label: 'asc' },
});
}
createCoach(dto: CreateCoachDto) { return this.prisma.coach.create({ data: dto, include: { seatClass: { select: { id: true, name: true, basePrice: true } } } }); }
async updateCoach(id: string, dto: UpdateCoachDto) {
const coach = await this.prisma.coach.findUnique({ where: { id } });
if (!coach) throw new NotFoundException('Coach not found');
return this.prisma.coach.update({ where: { id }, data: dto, include: { seatClass: { select: { id: true, name: true, basePrice: true } } } });
}
async createSeatBatch(dto: CreateSeatBatchDto) {
const coach = await this.prisma.coach.findUnique({ where: { id: dto.coachId } });
if (!coach) throw new NotFoundException('Coach not found');
@@ -16,6 +33,7 @@ export class FleetService {
await this.prisma.seat.createMany({ data: seats, skipDuplicates: true });
return { created: seats.length };
}
async getAnalytics() {
const [totalServices, totalTrips, totalSeats, bookedSeats] = await Promise.all([
this.prisma.trainService.count(), this.prisma.trip.count(),