From 93c583edf643c9419f07a24f4cef7c1d17030199 Mon Sep 17 00:00:00 2001 From: Stephanos A Date: Sun, 19 Jul 2026 08:07:59 +0300 Subject: [PATCH] Report number updates --- .../src/modules/reports/reports.service.ts | 51 ++++--- .../src/modules/seats/seats.service.ts | 3 + .../src/app/reports/passengers/page.tsx | 127 ++++++++---------- .../backoffice/src/app/reports/seats/page.tsx | 16 +-- 4 files changed, 92 insertions(+), 105 deletions(-) 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 0da670ec7..3d6345af7 100644 --- a/apps/edr-passenger-api/src/modules/reports/reports.service.ts +++ b/apps/edr-passenger-api/src/modules/reports/reports.service.ts @@ -105,7 +105,7 @@ export class ReportsService { 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.filter((s: any) => s.scheduleId === schedule.id).length, 0, + (sum, b) => sum + b.seats.filter((s: any) => s.leg === 1).length, 0, ); const occupancyRate = totalSeats > 0 ? (bookedSeats / totalSeats) * 100 : 0; return { scheduleId: schedule.id, departureAt: schedule.departureAt, totalSeats, bookedSeats, occupancyRate: +occupancyRate.toFixed(2) }; @@ -217,7 +217,7 @@ export class ReportsService { where: { status: { in: ['CONFIRMED', 'BOARDED'] } }, include: { seats: { - where: { scheduleId }, + where: { leg: 1 }, include: { seat: { include: { coach: { include: { coachType: true } } } }, }, @@ -313,27 +313,46 @@ export class ReportsService { async getPassengerList(scheduleId: string) { const seats = await this.prisma.bookingSeat.findMany({ where: { - scheduleId, - booking: { status: { in: ['CONFIRMED', 'BOARDED'] } }, + leg: 1, + booking: { scheduleId, status: { in: ['CONFIRMED', 'BOARDED'] } }, }, include: { - booking: { select: { bookingRef: true, status: true } }, - seat: { include: { coach: { select: { number: true, coachType: { select: { name: true } } } } } }, + booking: { + select: { + bookingRef: true, + status: true, + originStationId: true, + destinationStationId: true, + }, + }, + seat: { include: { coach: { select: { number: true } } } }, }, - orderBy: [{ seat: { coach: { number: 'asc' } } }], + orderBy: [{ seat: { coach: { number: 'asc' } } }, { seat: { seatNumber: 'asc' } }], }); + + // Resolve station names in one query + const stationIds = [...new Set( + seats.flatMap(bs => [bs.booking.originStationId, bs.booking.destinationStationId]).filter(Boolean) as string[], + )]; + const stations = stationIds.length > 0 + ? await this.prisma.station.findMany({ where: { id: { in: stationIds } }, select: { id: true, name: true } }) + : []; + const stationName = new Map(stations.map(s => [s.id, s.name])); + + const schedule = await this.prisma.trainSchedule.findUnique({ + where: { id: scheduleId }, + select: { departureAt: true }, + }); + return seats.map(bs => ({ bookingRef: bs.booking.bookingRef, - bookingStatus: bs.booking.status, passengerName: bs.passengerName, - passengerCategory: bs.passengerCategory, - idDocumentType: bs.idDocumentType, - idDocumentNumber: bs.idDocumentNumber, - passportNumber: bs.passportNumber, - passportCountry: bs.passportCountry, - seatLabel: bs.seatLabelSnapshot, - coachNumber: bs.seat?.coach?.number ?? null, - coachType: (bs.seat?.coach as any)?.coachType?.name ?? null, + coachSeat: bs.seat?.coach?.number && bs.seatLabelSnapshot + ? `${bs.seat.coach.number}·${bs.seatLabelSnapshot}` + : (bs.seatLabelSnapshot ?? '—'), + origin: bs.booking.originStationId ? (stationName.get(bs.booking.originStationId) ?? '—') : '—', + destination: bs.booking.destinationStationId ? (stationName.get(bs.booking.destinationStationId) ?? '—') : '—', + departureAt: schedule?.departureAt ?? null, })); } 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 6d5301cfb..46bb34b76 100644 --- a/apps/edr-passenger-api/src/modules/seats/seats.service.ts +++ b/apps/edr-passenger-api/src/modules/seats/seats.service.ts @@ -600,6 +600,9 @@ export class SeatsService { async getBlockedSeats() { const blocks = await this.prisma.seatBlock.findMany({ + where: { + NOT: { reason: { startsWith: 'MAINTENANCE:' } }, + }, include: { seat: { include: { coach: { select: { number: true } } } }, }, diff --git a/apps/edr-passenger-web/backoffice/src/app/reports/passengers/page.tsx b/apps/edr-passenger-web/backoffice/src/app/reports/passengers/page.tsx index 8c6958565..fa164dbca 100644 --- a/apps/edr-passenger-web/backoffice/src/app/reports/passengers/page.tsx +++ b/apps/edr-passenger-web/backoffice/src/app/reports/passengers/page.tsx @@ -20,16 +20,11 @@ interface PassengersReport { interface PassengerRow { bookingRef: string; - bookingStatus: string; passengerName: string; - passengerCategory: string; - idDocumentType: string | null; - idDocumentNumber: string | null; - passportNumber: string | null; - passportCountry: string | null; - seatLabel: string | null; - coachNumber: string | null; - coachType: string | null; + coachSeat: string; + origin: string; + destination: string; + departureAt: string | null; } type Tab = 'occupancy' | 'list'; @@ -60,9 +55,7 @@ export default function PassengersReportPage() { const filteredList = listSearch.trim() ? passengerList.filter(p => p.passengerName.toLowerCase().includes(listSearch.toLowerCase()) || - p.bookingRef.toLowerCase().includes(listSearch.toLowerCase()) || - (p.idDocumentNumber ?? '').toLowerCase().includes(listSearch.toLowerCase()) || - (p.passportNumber ?? '').toLowerCase().includes(listSearch.toLowerCase()), + p.bookingRef.toLowerCase().includes(listSearch.toLowerCase()), ) : passengerList; @@ -82,11 +75,10 @@ export default function PassengersReportPage() { const doExportList = () => { if (!passengerList.length) return; - const headers = ['Booking Ref', 'Status', 'Name', 'Category', 'ID Type', 'ID Number', 'Passport', 'Country', 'Seat', 'Coach', 'Class']; - const rows = passengerList.map(p => [ - p.bookingRef, p.bookingStatus, p.passengerName, p.passengerCategory, - p.idDocumentType ?? '', p.idDocumentNumber ?? '', p.passportNumber ?? '', - p.passportCountry ?? '', p.seatLabel ?? '', p.coachNumber ?? '', p.coachType ?? '', + const headers = ['#', 'Name', 'Coach·Seat', 'Origin', 'Destination', 'Date', 'Booking Ref']; + const rows = passengerList.map((p, i) => [ + String(i + 1), p.passengerName, p.coachSeat, p.origin, p.destination, + p.departureAt ? formatDateTime(p.departureAt) : '—', p.bookingRef, ].map(v => `"${String(v).replace(/"/g, '""')}"`)); downloadCsv([headers.join(','), ...rows.map(r => r.join(','))].join('\n'), `passengers-${scheduleId}.csv`); }; @@ -118,9 +110,6 @@ export default function PassengersReportPage() { {data && tab === 'occupancy' && ( Export CSV )} - {passengerList.length > 0 && tab === 'list' && ( - Export CSV - )} {(isLoading || listLoading) &&

Loading…

} {isError &&

Failed to load report.

} @@ -267,62 +256,52 @@ export default function PassengersReportPage() { )} - {/* Passenger List tab */} {tab === 'list' && ( -
- setListSearch(e.target.value)} - /> -
- - - - - - - - - - - - - - - {filteredList.map((p, i) => ( - - - - - - - - - +
+
+ setListSearch(e.target.value)} + /> + {passengerList.length > 0 && ( + Export CSV + )} +
+
+
+
#NameCategoryID / PassportSeatCoachBooking RefStatus
{i + 1}{p.passengerName} - - {p.passengerCategory} - - - {p.idDocumentNumber ?? p.passportNumber ?? '—'} - {p.passportCountry && ({p.passportCountry})} - {p.seatLabel ?? '—'} - {p.coachNumber ?? '—'} - {p.coachType && ({p.coachType})} - {p.bookingRef} - - {p.bookingStatus} - -
+ + + + + + + + + - ))} - {filteredList.length === 0 && ( - - )} - -
#NameCoach · SeatOriginDestinationDateBooking Ref
No passengers found
+ + + {filteredList.map((p, i) => ( + + {i + 1} + {p.passengerName} + {p.coachSeat} + {p.origin} + {p.destination} + {p.departureAt ? formatDateTime(p.departureAt) : '—'} + {p.bookingRef} + + ))} + {filteredList.length === 0 && ( + No passengers found + )} + + +
)} diff --git a/apps/edr-passenger-web/backoffice/src/app/reports/seats/page.tsx b/apps/edr-passenger-web/backoffice/src/app/reports/seats/page.tsx index 196e83c46..f56f26e8e 100644 --- a/apps/edr-passenger-web/backoffice/src/app/reports/seats/page.tsx +++ b/apps/edr-passenger-web/backoffice/src/app/reports/seats/page.tsx @@ -155,7 +155,7 @@ export default function SeatStatusReportPage() { {/* Summary Cards */} -
+
@@ -206,20 +206,6 @@ export default function SeatStatusReportPage() {
- {blockedSeats.length > 0 && ( -
- {blockedSeats.map((b: any) => ( -
- - Seat {b.seatNumber} · Coach {b.coachNumber} - - - {b.reason} - -
- ))} -
- )}