mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 19:58:11 +00:00
Round trip journeys, feedback items, backoffice documentation
This commit is contained in:
@@ -158,7 +158,34 @@ export class FleetController {
|
||||
@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' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Array of coaches',
|
||||
schema: {
|
||||
example: [
|
||||
{
|
||||
id: '550e8400-e29b-41d4-a716-446655440000',
|
||||
number: 'A-001',
|
||||
sequence: 1,
|
||||
coachTypeId: 'coach-type-uuid',
|
||||
coachType: {
|
||||
id: 'coach-type-uuid',
|
||||
code: 'sleeper',
|
||||
name: 'Sleeper Coach'
|
||||
},
|
||||
arrangement: '2+2',
|
||||
capacity: 60,
|
||||
status: 'ACTIVE',
|
||||
totalSeats: 60,
|
||||
availableSeats: 45,
|
||||
occupiedSeats: 15,
|
||||
blockedSeats: 0,
|
||||
createdAt: '2024-01-15T10:30:00.000Z',
|
||||
updatedAt: '2024-01-15T10:30:00.000Z'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
listCoaches(
|
||||
@Query('status') status?: string,
|
||||
@Query('scheduleId') scheduleId?: string,
|
||||
@@ -173,7 +200,40 @@ export class FleetController {
|
||||
@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: 200,
|
||||
description: 'Coach detail with seats by row',
|
||||
schema: {
|
||||
example: {
|
||||
id: '550e8400-e29b-41d4-a716-446655440000',
|
||||
number: 'A-001',
|
||||
sequence: 1,
|
||||
coachTypeId: 'coach-type-uuid',
|
||||
coachType: {
|
||||
id: 'coach-type-uuid',
|
||||
code: 'sleeper',
|
||||
name: 'Sleeper Coach'
|
||||
},
|
||||
arrangement: '2+2',
|
||||
capacity: 60,
|
||||
status: 'ACTIVE',
|
||||
seats: [
|
||||
{
|
||||
id: 'seat-uuid-1',
|
||||
seatNumber: '1A',
|
||||
status: 'AVAILABLE',
|
||||
class: {
|
||||
id: 'class-uuid',
|
||||
name: 'Economy',
|
||||
baseFareMinor: 5000
|
||||
}
|
||||
}
|
||||
],
|
||||
createdAt: '2024-01-15T10:30:00.000Z',
|
||||
updatedAt: '2024-01-15T10:30:00.000Z'
|
||||
}
|
||||
}
|
||||
})
|
||||
@ApiResponse({ status: 404, description: 'Coach not found' })
|
||||
getCoach(@Param('id') id: string) {
|
||||
return this.service.getCoach(id);
|
||||
@@ -182,7 +242,23 @@ export class FleetController {
|
||||
@Post('coaches')
|
||||
@ApiOperation({ summary: 'Create a coach with auto-generated seat numbers' })
|
||||
@ApiBody({ type: CreateCoachDto })
|
||||
@ApiResponse({ status: 201, description: 'Coach created' })
|
||||
@ApiResponse({
|
||||
status: 201,
|
||||
description: 'Coach created',
|
||||
schema: {
|
||||
example: {
|
||||
id: '550e8400-e29b-41d4-a716-446655440000',
|
||||
number: 'A-001',
|
||||
sequence: 1,
|
||||
coachTypeId: 'coach-type-uuid',
|
||||
arrangement: '2+2',
|
||||
capacity: 60,
|
||||
status: 'ACTIVE',
|
||||
createdAt: '2024-01-15T10:30:00.000Z',
|
||||
updatedAt: '2024-01-15T10:30:00.000Z'
|
||||
}
|
||||
}
|
||||
})
|
||||
@ApiResponse({ status: 400, description: 'Invalid arrangement format' })
|
||||
createCoach(@Body() dto: CreateCoachDto) {
|
||||
return this.service.createCoach(dto);
|
||||
@@ -192,7 +268,23 @@ export class FleetController {
|
||||
@ApiOperation({ summary: 'Update coach properties' })
|
||||
@ApiParam({ name: 'id', description: 'Coach UUID' })
|
||||
@ApiBody({ type: UpdateCoachDto })
|
||||
@ApiResponse({ status: 200, description: 'Coach updated' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Coach updated',
|
||||
schema: {
|
||||
example: {
|
||||
id: '550e8400-e29b-41d4-a716-446655440000',
|
||||
number: 'A-001',
|
||||
sequence: 1,
|
||||
coachTypeId: 'coach-type-uuid',
|
||||
arrangement: '2+2',
|
||||
capacity: 60,
|
||||
status: 'ACTIVE',
|
||||
createdAt: '2024-01-15T10:30:00.000Z',
|
||||
updatedAt: '2024-01-15T10:30:00.000Z'
|
||||
}
|
||||
}
|
||||
})
|
||||
@ApiResponse({ status: 404, description: 'Coach not found' })
|
||||
updateCoach(@Param('id') id: string, @Body() dto: UpdateCoachDto) {
|
||||
return this.service.updateCoach(id, dto);
|
||||
@@ -201,7 +293,7 @@ export class FleetController {
|
||||
@Delete('coaches/:id')
|
||||
@ApiOperation({ summary: 'Delete a coach' })
|
||||
@ApiParam({ name: 'id', description: 'Coach UUID' })
|
||||
@ApiResponse({ status: 200, description: 'Coach deleted' })
|
||||
@ApiResponse({ status: 200, description: 'Coach deleted successfully' })
|
||||
@ApiResponse({ status: 404, description: 'Coach not found' })
|
||||
deleteCoach(@Param('id') id: string) {
|
||||
return this.service.deleteCoach(id);
|
||||
|
||||
@@ -48,19 +48,16 @@ function buildSeats(coachId: string, coachNumber: string, arrangement: string, c
|
||||
const col = cols[ci];
|
||||
let bedPosition = null;
|
||||
|
||||
// Set bedPosition for bed coaches based on seat number cycling
|
||||
// Set bedPosition for bed coaches based on ROW cycling (not seat number)
|
||||
if (isBedCoach) {
|
||||
if (totalCols === 3) {
|
||||
// Economy bed (3 levels): 1L, 2M, 3U, 4L, 5M, 6U...
|
||||
const posMod = ((seatNumber - 1) % 3);
|
||||
if (posMod === 0) bedPosition = 'lower';
|
||||
else if (posMod === 1) bedPosition = 'middle';
|
||||
else if (posMod === 2) bedPosition = 'upper';
|
||||
// Economy bed (3-row cycle): upper, middle, lower
|
||||
if (row % 3 === 1) bedPosition = 'upper';
|
||||
else if (row % 3 === 2) bedPosition = 'middle';
|
||||
else bedPosition = 'lower';
|
||||
} else if (totalCols === 2) {
|
||||
// VIP bed (2 levels): 1L, 2U, 3L, 4U...
|
||||
const posMod = ((seatNumber - 1) % 2);
|
||||
if (posMod === 0) bedPosition = 'lower';
|
||||
else if (posMod === 1) bedPosition = 'upper';
|
||||
// VIP bed (2-row cycle): upper, lower
|
||||
bedPosition = row % 2 === 1 ? 'upper' : 'lower';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,7 +250,7 @@ export class FleetService {
|
||||
return this.prisma.coach.findMany({
|
||||
where,
|
||||
include: { coachType: true },
|
||||
orderBy: { number: 'asc' },
|
||||
orderBy: { sequence: 'asc' },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -263,10 +260,18 @@ export class FleetService {
|
||||
throw new BadRequestException(`Invalid arrangement format "${dto.arrangement}". Use e.g. "2+2"`);
|
||||
}
|
||||
|
||||
// Get the next sequence number for this coach type
|
||||
const lastCoach = await this.prisma.coach.findFirst({
|
||||
where: { coachTypeId: dto.coachTypeId },
|
||||
orderBy: { sequence: 'desc' },
|
||||
});
|
||||
const nextSequence = (lastCoach?.sequence ?? 0) + 1;
|
||||
|
||||
const coach = await this.prisma.coach.create({
|
||||
data: {
|
||||
coachTypeId: dto.coachTypeId,
|
||||
number: dto.number,
|
||||
sequence: nextSequence,
|
||||
arrangement: dto.arrangement,
|
||||
capacity: dto.capacity,
|
||||
status: dto.status || 'ACTIVE',
|
||||
@@ -301,33 +306,6 @@ export class FleetService {
|
||||
async deleteCoach(id: string) {
|
||||
const coach = await this.prisma.coach.findUnique({ where: { id } });
|
||||
if (!coach) throw new NotFoundException('Coach not found');
|
||||
|
||||
// Get all seat IDs for this coach
|
||||
const seats = await this.prisma.seat.findMany({ where: { coachId: id }, select: { id: true } });
|
||||
const seatIds = seats.map(s => s.id);
|
||||
|
||||
// Delete in order of foreign key dependencies
|
||||
if (seatIds.length > 0) {
|
||||
// 1. Delete seat blocks (references seats)
|
||||
await this.prisma.seatBlock.deleteMany({ where: { seatId: { in: seatIds } } });
|
||||
|
||||
// 2. Delete ticket seats (references seats)
|
||||
await this.prisma.ticketSeat.deleteMany({ where: { seatId: { in: seatIds } } });
|
||||
|
||||
// 3. Delete booking seats (references seats)
|
||||
await this.prisma.bookingSeat.deleteMany({ where: { seatId: { in: seatIds } } });
|
||||
|
||||
// 4. Delete journey segments with these seats
|
||||
await this.prisma.journeySegment.deleteMany({ where: { seatId: { in: seatIds } } });
|
||||
}
|
||||
|
||||
// 5. Delete all associated seats
|
||||
await this.prisma.seat.deleteMany({ where: { coachId: id } });
|
||||
|
||||
// 6. Delete coach assignments
|
||||
await this.prisma.coachAssignment.deleteMany({ where: { coachId: id } });
|
||||
|
||||
// 7. Finally delete the coach
|
||||
return this.prisma.coach.delete({ where: { id } });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user