Coaches, seats, schedules, and pricing related updates

This commit is contained in:
Stephanos A
2026-06-07 17:41:31 +03:00
parent af14535e08
commit bb10e7fdf2
55 changed files with 5119 additions and 2930 deletions

View File

@@ -1,7 +1,7 @@
import { Body, Controller, Delete, Get, Param, Patch, Post, Query, UseGuards } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam, ApiQuery, ApiBody, ApiResponse } from '@nestjs/swagger';
import { FleetService } from './fleet.service';
import { CreateTrainDto, CreateCoachDto, UpdateCoachDto, AssignCoachDto, CreateSeatBatchDto, ListCoachesDto } from './fleet.dto';
import { CreateTrainDto, CreateCoachDto, UpdateCoachDto, AssignCoachDto, ListCoachesDto, CreateCoachTypeDto, UpdateCoachTypeDto, CreateClassDto, UpdateClassDto } from './fleet.dto';
import { JwtGuard } from '../../common/jwt.guard';
@ApiTags('Fleet')
@@ -11,16 +11,128 @@ import { JwtGuard } from '../../common/jwt.guard';
export class FleetController {
constructor(private service: FleetService) {}
// Coach Type Endpoints
@Get('coach-types')
@ApiOperation({ summary: 'List all coach types' })
@ApiResponse({ status: 200, description: 'Array of coach types' })
getCoachTypes() {
return this.service.getCoachTypes();
}
@Post('coach-types')
@ApiOperation({ summary: 'Create a coach type' })
@ApiBody({ type: CreateCoachTypeDto })
@ApiResponse({ status: 201, description: 'Coach type created' })
createCoachType(@Body() dto: CreateCoachTypeDto) {
return this.service.createCoachType(dto);
}
@Patch('coach-types/:id')
@ApiOperation({ summary: 'Update a coach type' })
@ApiParam({ name: 'id', description: 'Coach Type UUID' })
@ApiBody({ type: UpdateCoachTypeDto })
@ApiResponse({ status: 200, description: 'Coach type updated' })
@ApiResponse({ status: 404, description: 'Coach type not found' })
updateCoachType(@Param('id') id: string, @Body() dto: UpdateCoachTypeDto) {
return this.service.updateCoachType(id, dto);
}
@Delete('coach-types/:id')
@ApiOperation({ summary: 'Delete a coach type' })
@ApiParam({ name: 'id', description: 'Coach Type UUID' })
@ApiResponse({ status: 200, description: 'Coach type deleted' })
@ApiResponse({ status: 404, description: 'Coach type not found' })
deleteCoachType(@Param('id') id: string) {
return this.service.deleteCoachType(id);
}
// Class Endpoints
@Get('classes')
@ApiOperation({ summary: 'List all classes' })
@ApiQuery({ name: 'coachTypeId', required: false, description: 'Filter by coach type' })
@ApiResponse({ status: 200, description: 'Array of classes' })
getClasses(@Query('coachTypeId') coachTypeId?: string) {
return this.service.getClasses(coachTypeId);
}
@Post('classes')
@ApiOperation({ summary: 'Create a class' })
@ApiBody({ type: CreateClassDto })
@ApiResponse({ status: 201, description: 'Class created' })
createClass(@Body() dto: CreateClassDto) {
return this.service.createClass(dto);
}
@Patch('classes/:id')
@ApiOperation({ summary: 'Update a class' })
@ApiParam({ name: 'id', description: 'Class UUID' })
@ApiBody({ type: UpdateClassDto })
@ApiResponse({ status: 200, description: 'Class updated' })
@ApiResponse({ status: 404, description: 'Class not found' })
updateClass(@Param('id') id: string, @Body() dto: UpdateClassDto) {
return this.service.updateClass(id, dto);
}
@Delete('classes/:id')
@ApiOperation({ summary: 'Delete a class' })
@ApiParam({ name: 'id', description: 'Class UUID' })
@ApiResponse({ status: 200, description: 'Class deleted' })
@ApiResponse({ status: 404, description: 'Class not found' })
deleteClass(@Param('id') id: string) {
return this.service.deleteClass(id);
}
// Seat Class Endpoints (DEPRECATED - use Classes endpoints instead)
@Get('seat-classes')
@ApiOperation({ summary: 'List all classes (DEPRECATED - use /fleet/classes)' })
@ApiQuery({ name: 'coachTypeId', required: false, description: 'Filter by coach type' })
@ApiResponse({ status: 200, description: 'Array of classes' })
getSeatClasses(@Query('coachTypeId') coachTypeId?: string) {
return this.service.getClasses(coachTypeId);
}
@Post('seat-classes')
@ApiOperation({ summary: 'Create a class (DEPRECATED - use /fleet/classes)' })
@ApiBody({ type: CreateClassDto })
@ApiResponse({ status: 201, description: 'Class created' })
createSeatClass(@Body() dto: CreateClassDto) {
return this.service.createClass(dto);
}
@Patch('seat-classes/:id')
@ApiOperation({ summary: 'Update a class (DEPRECATED - use /fleet/classes)' })
@ApiParam({ name: 'id', description: 'Class UUID' })
@ApiBody({ type: UpdateClassDto })
@ApiResponse({ status: 200, description: 'Class updated' })
@ApiResponse({ status: 404, description: 'Class not found' })
updateSeatClass(@Param('id') id: string, @Body() dto: UpdateClassDto) {
return this.service.updateClass(id, dto);
}
@Delete('seat-classes/:id')
@ApiOperation({ summary: 'Delete a class (DEPRECATED - use /fleet/classes)' })
@ApiParam({ name: 'id', description: 'Class UUID' })
@ApiResponse({ status: 200, description: 'Class deleted' })
@ApiResponse({ status: 404, description: 'Class not found' })
deleteSeatClass(@Param('id') id: string) {
return this.service.deleteClass(id);
}
// Train Endpoints
@Get('trains')
@ApiOperation({ summary: 'List all trains with their recent schedules' })
@ApiResponse({ status: 200, description: 'Array of trains each with up to 5 most recent schedules' })
getTrains() { return this.service.getTrains(); }
@ApiResponse({ status: 200, description: 'Array of trains' })
getTrains() {
return this.service.getTrains();
}
@Post('trains')
@ApiOperation({ summary: 'Create a train service' })
@ApiBody({ type: CreateTrainDto })
@ApiResponse({ status: 201, description: 'Train created' })
createTrain(@Body() dto: CreateTrainDto) { return this.service.createTrain(dto); }
createTrain(@Body() dto: CreateTrainDto) {
return this.service.createTrain(dto);
}
@Patch('trains/:id')
@ApiOperation({ summary: 'Update a train service' })
@@ -28,96 +140,95 @@ export class FleetController {
@ApiBody({ type: CreateTrainDto })
@ApiResponse({ status: 200, description: 'Train updated' })
@ApiResponse({ status: 404, description: 'Train not found' })
updateTrain(@Param('id') id: string, @Body() dto: CreateTrainDto) { return this.service.updateTrain(id, dto); }
@Get('coaches')
@ApiOperation({ summary: 'List coaches filtered by status, mode, seat class, or schedule assignment' })
@ApiQuery({ name: 'isActive', required: false, type: Boolean, description: 'true = active only, false = inactive only, omit = all' })
@ApiQuery({ name: 'mode', required: false, description: 'Filter by mode: seat | bed | convertible' })
@ApiQuery({ name: 'seatClassId', required: false, description: 'Filter by SeatClass UUID' })
@ApiQuery({ name: 'scheduleId', required: false, description: 'Filter to coaches assigned to this TrainSchedule UUID' })
@ApiResponse({ status: 200, description: 'Coaches with seat class info, assignment count, and seat status summary (total/available/held/booked/blocked)' })
listCoaches(
@Query('isActive') isActive?: string,
@Query('mode') mode?: string,
@Query('seatClassId') seatClassId?: string,
@Query('scheduleId') scheduleId?: string,
) {
const dto: ListCoachesDto = {
isActive: isActive === 'true' ? true : isActive === 'false' ? false : undefined,
mode,
seatClassId,
scheduleId,
};
return this.service.listCoaches(dto);
updateTrain(@Param('id') id: string, @Body() dto: CreateTrainDto) {
return this.service.updateTrain(id, dto);
}
@Get('coaches/:id')
@ApiOperation({ summary: 'Get a single coach with full seat layout and arrangement' })
@ApiParam({ name: 'id', description: 'Coach UUID' })
@ApiResponse({
status: 200,
description: `Coach detail including:
- seatClass: seat class info
- seatsByRow: seats grouped by row number, each seat includes label, seatNumber, col, kind (STANDARD/PREMIUM/ACCESSIBLE), status (AVAILABLE/HELD/BOOKED/BLOCKED), isWindow, isAisle, bedPosition (bed mode only), premiumFeeMinor
- seatStatusSummary: total/available/held/booked/blocked counts
- assignments: up to 5 most recent schedule assignments with origin/destination`,
})
@ApiResponse({ status: 404, description: 'Coach not found' })
getCoach(@Param('id') id: string) { return this.service.getCoach(id); }
@Post('coaches')
@ApiOperation({ summary: 'Register a new physical coach and auto-generate its seats from arrangement config' })
@ApiBody({ type: CreateCoachDto })
@ApiResponse({ status: 201, description: 'Coach created with seats auto-generated from mode + arrangement + totalUnits' })
@ApiResponse({ status: 400, description: 'Invalid arrangement format' })
createCoach(@Body() dto: CreateCoachDto) { return this.service.createCoach(dto); }
@Patch('coaches/:id')
@ApiOperation({ summary: 'Update coach properties (label, mode, arrangement, etc.)' })
@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); }
@Delete('trains/:id')
@ApiOperation({ summary: 'Delete a train service' })
@ApiParam({ name: 'id', description: 'Train UUID' })
@ApiResponse({ status: 200, description: 'Train deleted' })
@ApiResponse({ status: 404, description: 'Train not found' })
deleteTrain(@Param('id') id: string) { return this.service.deleteTrain(id); }
deleteTrain(@Param('id') id: string) {
return this.service.deleteTrain(id);
}
// Coach Endpoints
@Get('coaches')
@ApiOperation({ summary: 'List coaches with seat status summary' })
@ApiQuery({ name: 'status', required: false, description: 'Filter by status: ACTIVE, INACTIVE' })
@ApiQuery({ name: 'scheduleId', required: false, description: 'Filter coaches assigned to schedule' })
@ApiResponse({ status: 200, description: 'Array of coaches' })
listCoaches(
@Query('status') status?: string,
@Query('scheduleId') scheduleId?: string,
) {
const dto: ListCoachesDto = {
status,
scheduleId,
};
return this.service.listCoaches(dto);
}
@Get('coaches/:id')
@ApiOperation({ summary: 'Get single coach with seat layout' })
@ApiParam({ name: 'id', description: 'Coach UUID' })
@ApiResponse({ status: 200, description: 'Coach detail with seats by row' })
@ApiResponse({ status: 404, description: 'Coach not found' })
getCoach(@Param('id') id: string) {
return this.service.getCoach(id);
}
@Post('coaches')
@ApiOperation({ summary: 'Create a coach with auto-generated seat numbers' })
@ApiBody({ type: CreateCoachDto })
@ApiResponse({ status: 201, description: 'Coach created' })
@ApiResponse({ status: 400, description: 'Invalid arrangement format' })
createCoach(@Body() dto: CreateCoachDto) {
return this.service.createCoach(dto);
}
@Patch('coaches/:id')
@ApiOperation({ summary: 'Update coach properties' })
@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);
}
@Delete('coaches/:id')
@ApiOperation({ summary: 'Delete a coach' })
@ApiParam({ name: 'id', description: 'Coach UUID' })
@ApiResponse({ status: 200, description: 'Coach deleted' })
@ApiResponse({ status: 404, description: 'Coach not found' })
deleteCoach(@Param('id') id: string) { return this.service.deleteCoach(id); }
deleteCoach(@Param('id') id: string) {
return this.service.deleteCoach(id);
}
@Post('assignments')
@ApiOperation({ summary: 'Assign a physical coach to a train schedule at a given position' })
@ApiOperation({ summary: 'Assign a coach to a schedule' })
@ApiBody({ type: AssignCoachDto })
@ApiResponse({ status: 201, description: 'CoachAssignment created' })
@ApiResponse({ status: 201, description: 'Coach assigned' })
@ApiResponse({ status: 404, description: 'Schedule or coach not found' })
assignCoach(@Body() dto: AssignCoachDto) { return this.service.assignCoach(dto); }
assignCoach(@Body() dto: AssignCoachDto) {
return this.service.assignCoach(dto);
}
@Delete('assignments/:id')
@ApiOperation({ summary: 'Remove a coach assignment from a schedule' })
@ApiParam({ name: 'id', description: 'CoachAssignment UUID' })
@ApiOperation({ summary: 'Remove a coach assignment' })
@ApiParam({ name: 'id', description: 'Assignment UUID' })
@ApiResponse({ status: 200, description: 'Assignment removed' })
@ApiResponse({ status: 404, description: 'Assignment not found' })
removeAssignment(@Param('id') id: string) { return this.service.removeAssignment(id); }
@Post('seats/batch')
@ApiOperation({ summary: 'Batch-generate seats for a coach (rows × cols)' })
@ApiBody({ type: CreateSeatBatchDto })
@ApiResponse({ status: 201, description: 'Returns count of seats created' })
@ApiResponse({ status: 404, description: 'Coach not found' })
createSeatBatch(@Body() dto: CreateSeatBatchDto) { return this.service.createSeatBatch(dto); }
removeAssignment(@Param('id') id: string) {
return this.service.removeAssignment(id);
}
@Get('analytics')
@ApiOperation({ summary: 'Fleet analytics: train count, schedule count, seat occupancy rate' })
@ApiResponse({ status: 200, description: 'Returns totalTrains, totalSchedules, totalSeats, bookedSeats, occupancyRate' })
getAnalytics() { return this.service.getAnalytics(); }
@ApiOperation({ summary: 'Fleet analytics and occupancy metrics' })
@ApiResponse({ status: 200, description: 'Occupancy statistics' })
getAnalytics() {
return this.service.getAnalytics();
}
}