diff --git a/apps/edr-passenger-api/src/modules/dashboard/dashboard.service.ts b/apps/edr-passenger-api/src/modules/dashboard/dashboard.service.ts index aed950b73..155391814 100644 --- a/apps/edr-passenger-api/src/modules/dashboard/dashboard.service.ts +++ b/apps/edr-passenger-api/src/modules/dashboard/dashboard.service.ts @@ -13,8 +13,8 @@ export class DashboardService { async getBackofficeStats() { const [totalBookings, totalPackageBookings, totalTickets, totalPassengers, revenueRows, packageRevenueRows] = await Promise.all([ - this.prisma.booking.count(), - this.prisma.booking.count({ where: { packageId: { not: null } } }), + this.prisma.booking.count({ where: { status: { in: ['CONFIRMED', 'BOARDED'] } } }), + this.prisma.booking.count({ where: { status: { in: ['CONFIRMED', 'BOARDED'] }, packageId: { not: null } } }), this.prisma.ticket.count(), this.prisma.passenger.count(), this.prisma.$queryRaw<{ currency: string; total: bigint }[]>` diff --git a/apps/edr-passenger-api/src/modules/reports/reports.service.ts b/apps/edr-passenger-api/src/modules/reports/reports.service.ts index 57c5db895..0da670ec7 100644 --- a/apps/edr-passenger-api/src/modules/reports/reports.service.ts +++ b/apps/edr-passenger-api/src/modules/reports/reports.service.ts @@ -95,13 +95,18 @@ export class ReportsService { where: { departureAt: { gte: dateFrom, lte: dateTo } }, include: { coachAssignments: { include: { coach: { include: { seats: true } } } }, - bookings: { include: { seats: { where: { scheduleId: schedule.id } } } }, + bookings: { + where: { status: { in: ['CONFIRMED', 'BOARDED'] } }, + include: { seats: true }, + }, }, }); const tripData = schedules.map(schedule => { const totalSeats = schedule.coachAssignments.reduce((sum, a) => sum + a.coach.seats.length, 0); - const bookedSeats = schedule.bookings.reduce((sum, b) => sum + b.seats.length, 0); + const bookedSeats = schedule.bookings.reduce( + (sum, b) => sum + b.seats.filter((s: any) => s.scheduleId === schedule.id).length, 0, + ); const occupancyRate = totalSeats > 0 ? (bookedSeats / totalSeats) * 100 : 0; return { scheduleId: schedule.id, departureAt: schedule.departureAt, totalSeats, bookedSeats, occupancyRate: +occupancyRate.toFixed(2) }; }); diff --git a/apps/edr-passenger-api/src/modules/seats/seats.controller.ts b/apps/edr-passenger-api/src/modules/seats/seats.controller.ts index 596c88bb3..a302a01ba 100644 --- a/apps/edr-passenger-api/src/modules/seats/seats.controller.ts +++ b/apps/edr-passenger-api/src/modules/seats/seats.controller.ts @@ -29,6 +29,16 @@ import { PASSENGER_PERMS } from "../../seed/passenger-permissions.registry"; export class SeatsController { constructor(private service: SeatsService) {} + // ── Blocked Seats ───────────────────────────────────────────────────────── + @Get('blocks') + @PassengerStaff([PASSENGER_PERMS.seats.manage, PASSENGER_PERMS.admin]) + @ApiBearerAuth('IAM-auth') + @ApiOperation({ summary: 'List all blocked seats with reason and coach info' }) + @ApiResponse({ status: 200, description: 'Blocked seat records' }) + getBlockedSeats() { + return this.service.getBlockedSeats(); + } + // ── Coach Availability ──────────────────────────────────────────────────── @Get('coaches/:scheduleId') @SetMetadata('isPublic', true) diff --git a/apps/edr-passenger-api/src/modules/seats/seats.service.ts b/apps/edr-passenger-api/src/modules/seats/seats.service.ts index 10368f380..6d5301cfb 100644 --- a/apps/edr-passenger-api/src/modules/seats/seats.service.ts +++ b/apps/edr-passenger-api/src/modules/seats/seats.service.ts @@ -598,6 +598,26 @@ export class SeatsService { await this.prisma.journey.deleteMany({ where: { bookingId } as any }); } + async getBlockedSeats() { + const blocks = await this.prisma.seatBlock.findMany({ + include: { + seat: { include: { coach: { select: { number: true } } } }, + }, + orderBy: { blockedAt: 'desc' }, + }); + return blocks.map(b => ({ + id: b.id, + seatId: b.seatId, + seatNumber: b.seat.seatNumber, + coachNumber: b.seat.coach.number, + scheduleId: b.scheduleId, + reason: b.reason, + blockedBy: b.blockedBy, + blockedAt: b.blockedAt, + unblockAt: b.unblockAt, + })); + } + async getCoachesWithAvailability(scheduleId: string, originStationId?: string, destinationStationId?: string) { const schedule = await this.prisma.trainSchedule.findUnique({ where: { id: scheduleId }, diff --git a/apps/edr-passenger-web/backoffice/src/app/dashboard/page.tsx b/apps/edr-passenger-web/backoffice/src/app/dashboard/page.tsx index 6d1e19649..8c2b38570 100644 --- a/apps/edr-passenger-web/backoffice/src/app/dashboard/page.tsx +++ b/apps/edr-passenger-web/backoffice/src/app/dashboard/page.tsx @@ -170,7 +170,7 @@ function DashboardPageContent() { )} {/* Stat cards */} -
Blocked Seats
++ {blockedSeats.length} +
+Manually blocked
+