mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 09:58:12 +00:00
Updated sms text
This commit is contained in:
@@ -303,7 +303,7 @@ export class NotificationsService {
|
|||||||
const booking = await this.prisma.booking.findUnique({
|
const booking = await this.prisma.booking.findUnique({
|
||||||
where: { id: bookingId },
|
where: { id: bookingId },
|
||||||
include: {
|
include: {
|
||||||
schedule: { include: { originStation: true, destinationStation: true, train: true } },
|
schedule: { include: { originStation: true, destinationStation: true, train: true, stopTimes: { include: { station: true } } } },
|
||||||
seats: { include: { seat: { include: { coach: { include: { coachType: true } } } } } },
|
seats: { include: { seat: { include: { coach: { include: { coachType: true } } } } } },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -353,6 +353,27 @@ export class NotificationsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the user's actual boarding/alighting stations from the booking's originStationId /
|
||||||
|
* destinationStationId via stopTimes, falling back to the schedule's full-route endpoints when
|
||||||
|
* the booking has no segment override (e.g. older records or packages).
|
||||||
|
*/
|
||||||
|
private resolveSegmentStations(booking: any): { originStation: any; destinationStation: any } {
|
||||||
|
const s = booking?.schedule ?? {};
|
||||||
|
const stopTimes: any[] = s.stopTimes ?? [];
|
||||||
|
const findStation = (stationId: string | null | undefined, fallback: any) => {
|
||||||
|
if (stationId && stopTimes.length > 0) {
|
||||||
|
const stop = stopTimes.find((st: any) => st.stationId === stationId);
|
||||||
|
if (stop?.station) return stop.station;
|
||||||
|
}
|
||||||
|
return fallback ?? null;
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
originStation: findStation(booking?.originStationId, s.originStation),
|
||||||
|
destinationStation: findStation(booking?.destinationStationId, s.destinationStation),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the interpolation context for the `booking.created` template. `trainSeatLines` is a
|
* Builds the interpolation context for the `booking.created` template. `trainSeatLines` is a
|
||||||
* pre-joined block of one "Train/Seat: …" line per booked seat (multi-passenger bookings get
|
* pre-joined block of one "Train/Seat: …" line per booked seat (multi-passenger bookings get
|
||||||
@@ -379,12 +400,13 @@ export class NotificationsService {
|
|||||||
// Lead passenger (leg-1 seat). Booking has no contactName; the traveller name lives on the seat.
|
// Lead passenger (leg-1 seat). Booking has no contactName; the traveller name lives on the seat.
|
||||||
const passengerName = seats[0]?.passengerName ?? 'Passenger';
|
const passengerName = seats[0]?.passengerName ?? 'Passenger';
|
||||||
const payLink = `${process.env.PORTAL_URL ?? 'http://localhost:5174'}/booking/detail?ref=${ref}`;
|
const payLink = `${process.env.PORTAL_URL ?? 'http://localhost:5174'}/booking/detail?ref=${ref}`;
|
||||||
|
const { originStation: originSt, destinationStation: destSt } = this.resolveSegmentStations(booking);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
passengerName,
|
passengerName,
|
||||||
bookingRef: ref,
|
bookingRef: ref,
|
||||||
origin: s.originStation?.name ?? '',
|
origin: originSt?.name ?? '',
|
||||||
destination: s.destinationStation?.name ?? '',
|
destination: destSt?.name ?? '',
|
||||||
trainSeatLines,
|
trainSeatLines,
|
||||||
travelDate: fmtDate(s.departureAt),
|
travelDate: fmtDate(s.departureAt),
|
||||||
departureTime: fmtTime(s.departureAt),
|
departureTime: fmtTime(s.departureAt),
|
||||||
@@ -406,7 +428,7 @@ export class NotificationsService {
|
|||||||
const booking = await this.prisma.booking.findUnique({
|
const booking = await this.prisma.booking.findUnique({
|
||||||
where: { id: bookingId },
|
where: { id: bookingId },
|
||||||
include: {
|
include: {
|
||||||
schedule: { include: { originStation: true, destinationStation: true, train: true } },
|
schedule: { include: { originStation: true, destinationStation: true, train: true, stopTimes: { include: { station: true } } } },
|
||||||
seats: { include: { seat: { include: { coach: { include: { coachType: true } } } } } },
|
seats: { include: { seat: { include: { coach: { include: { coachType: true } } } } } },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -489,9 +511,10 @@ export class NotificationsService {
|
|||||||
const s = booking.schedule ?? {};
|
const s = booking.schedule ?? {};
|
||||||
const dep = s.departureAt ? new Date(s.departureAt).toLocaleString('en-GB') : 'TBD';
|
const dep = s.departureAt ? new Date(s.departureAt).toLocaleString('en-GB') : 'TBD';
|
||||||
const passengers = (booking.seats ?? []).map((bs: any) => bs.passengerName).filter(Boolean).join(', ');
|
const passengers = (booking.seats ?? []).map((bs: any) => bs.passengerName).filter(Boolean).join(', ');
|
||||||
|
const { originStation: originSt, destinationStation: destSt } = this.resolveSegmentStations(booking);
|
||||||
return [
|
return [
|
||||||
`Booking ${booking.bookingRef} confirmed.`,
|
`Booking ${booking.bookingRef} confirmed.`,
|
||||||
`${s.originStation?.name ?? ''} -> ${s.destinationStation?.name ?? ''}`,
|
`${originSt?.name ?? ''} -> ${destSt?.name ?? ''}`,
|
||||||
`Train: ${s.train?.name ?? s.train?.number ?? ''}`,
|
`Train: ${s.train?.name ?? s.train?.number ?? ''}`,
|
||||||
`Departs: ${dep}`,
|
`Departs: ${dep}`,
|
||||||
passengers ? `Passengers: ${passengers}` : '',
|
passengers ? `Passengers: ${passengers}` : '',
|
||||||
@@ -504,6 +527,7 @@ export class NotificationsService {
|
|||||||
const s = booking.schedule ?? {};
|
const s = booking.schedule ?? {};
|
||||||
const fmt = (d: any) =>
|
const fmt = (d: any) =>
|
||||||
d ? new Date(d).toLocaleString('en-GB', { dateStyle: 'medium', timeStyle: 'short' }) : 'TBD';
|
d ? new Date(d).toLocaleString('en-GB', { dateStyle: 'medium', timeStyle: 'short' }) : 'TBD';
|
||||||
|
const { originStation: originSt, destinationStation: destSt } = this.resolveSegmentStations(booking);
|
||||||
const seatRows = (booking.seats ?? [])
|
const seatRows = (booking.seats ?? [])
|
||||||
.map((bs: any) => {
|
.map((bs: any) => {
|
||||||
const coach = bs.seat?.coach?.number ?? '-';
|
const coach = bs.seat?.coach?.number ?? '-';
|
||||||
@@ -532,11 +556,11 @@ export class NotificationsService {
|
|||||||
<table style="width:100%;border-collapse:collapse;margin:16px 0;">
|
<table style="width:100%;border-collapse:collapse;margin:16px 0;">
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding:8px 0;color:#666;">From</td>
|
<td style="padding:8px 0;color:#666;">From</td>
|
||||||
<td style="padding:8px 0;text-align:right;"><strong>${s.originStation?.name ?? ''}</strong> (${s.originStation?.code ?? ''})</td>
|
<td style="padding:8px 0;text-align:right;"><strong>${originSt?.name ?? ''}</strong> (${originSt?.code ?? ''})</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding:8px 0;color:#666;">To</td>
|
<td style="padding:8px 0;color:#666;">To</td>
|
||||||
<td style="padding:8px 0;text-align:right;"><strong>${s.destinationStation?.name ?? ''}</strong> (${s.destinationStation?.code ?? ''})</td>
|
<td style="padding:8px 0;text-align:right;"><strong>${destSt?.name ?? ''}</strong> (${destSt?.code ?? ''})</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding:8px 0;color:#666;">Train</td>
|
<td style="padding:8px 0;color:#666;">Train</td>
|
||||||
@@ -612,8 +636,9 @@ export class NotificationsService {
|
|||||||
const fmt = (d: any) =>
|
const fmt = (d: any) =>
|
||||||
d ? new Date(d).toLocaleString('en-GB', { dateStyle: 'medium', timeStyle: 'short' }) : 'TBD';
|
d ? new Date(d).toLocaleString('en-GB', { dateStyle: 'medium', timeStyle: 'short' }) : 'TBD';
|
||||||
const legLabel = leg ? ` (${leg.replace(/_/g, ' ')})` : '';
|
const legLabel = leg ? ` (${leg.replace(/_/g, ' ')})` : '';
|
||||||
const origin = s.originStation?.name ?? '';
|
const { originStation: originSt, destinationStation: destSt } = this.resolveSegmentStations(booking);
|
||||||
const dest = s.destinationStation?.name ?? '';
|
const origin = originSt?.name ?? '';
|
||||||
|
const dest = destSt?.name ?? '';
|
||||||
const train = s.train?.name ?? s.train?.number ?? '';
|
const train = s.train?.name ?? s.train?.number ?? '';
|
||||||
const dep = fmt(s.departureAt);
|
const dep = fmt(s.departureAt);
|
||||||
const arr = fmt(s.arrivalAt);
|
const arr = fmt(s.arrivalAt);
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ export class TicketsService {
|
|||||||
let booking = await this.prisma.booking.findUnique({
|
let booking = await this.prisma.booking.findUnique({
|
||||||
where: { bookingRef },
|
where: { bookingRef },
|
||||||
include: {
|
include: {
|
||||||
schedule: { include: { originStation: true, destinationStation: true, train: true } },
|
schedule: { include: { originStation: true, destinationStation: true, train: true, stopTimes: { include: { station: true } } } },
|
||||||
returnSchedule: { include: { originStation: true, destinationStation: true } },
|
returnSchedule: { include: { originStation: true, destinationStation: true } },
|
||||||
tickets: true,
|
tickets: true,
|
||||||
seats: { include: { seat: { include: { coach: true } } } },
|
seats: { include: { seat: { include: { coach: true } } } },
|
||||||
@@ -400,7 +400,7 @@ export class TicketsService {
|
|||||||
booking = await this.prisma.booking.findUnique({
|
booking = await this.prisma.booking.findUnique({
|
||||||
where: { bookingRef: ticket.bookingRef },
|
where: { bookingRef: ticket.bookingRef },
|
||||||
include: {
|
include: {
|
||||||
schedule: { include: { originStation: true, destinationStation: true, train: true } },
|
schedule: { include: { originStation: true, destinationStation: true, train: true, stopTimes: { include: { station: true } } } },
|
||||||
returnSchedule: { include: { originStation: true, destinationStation: true } },
|
returnSchedule: { include: { originStation: true, destinationStation: true } },
|
||||||
tickets: true,
|
tickets: true,
|
||||||
seats: { include: { seat: { include: { coach: true } } } },
|
seats: { include: { seat: { include: { coach: true } } } },
|
||||||
@@ -453,6 +453,18 @@ export class TicketsService {
|
|||||||
// Send notifications after successful boarding
|
// Send notifications after successful boarding
|
||||||
await this.sendBoardingNotifications(booking, ticket, result.leg || 'OUTBOUND');
|
await this.sendBoardingNotifications(booking, ticket, result.leg || 'OUTBOUND');
|
||||||
|
|
||||||
|
// Resolve user-selected segment rather than the full schedule route
|
||||||
|
const _schedStops = (booking as any).schedule?.stopTimes ?? [];
|
||||||
|
const _resolveStation = (id: string | null | undefined, fallback: any) => {
|
||||||
|
if (id) {
|
||||||
|
const found = _schedStops.find((st: any) => st.stationId === id)?.station;
|
||||||
|
if (found) return found;
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
};
|
||||||
|
const boardingOrigin = _resolveStation((booking as any).originStationId, (booking as any).schedule?.originStation);
|
||||||
|
const boardingDest = _resolveStation((booking as any).destinationStationId, (booking as any).schedule?.destinationStation);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: `Passenger boarded successfully (${result.leg || 'OUTBOUND'} leg)`,
|
message: `Passenger boarded successfully (${result.leg || 'OUTBOUND'} leg)`,
|
||||||
@@ -461,7 +473,7 @@ export class TicketsService {
|
|||||||
ticketNumber: ticket.barcodePayload,
|
ticketNumber: ticket.barcodePayload,
|
||||||
bookingRef: booking.bookingRef,
|
bookingRef: booking.bookingRef,
|
||||||
passengerName: seatInfo?.passengerName || ticket.passengerName || 'N/A',
|
passengerName: seatInfo?.passengerName || ticket.passengerName || 'N/A',
|
||||||
route: `${(booking as any).schedule?.originStation?.name || 'N/A'} → ${(booking as any).schedule?.destinationStation?.name || 'N/A'}`,
|
route: `${boardingOrigin?.name || 'N/A'} → ${boardingDest?.name || 'N/A'}`,
|
||||||
seat: seatNumber,
|
seat: seatNumber,
|
||||||
coach: coachNumber,
|
coach: coachNumber,
|
||||||
trainName: (booking as any).schedule?.train?.name || (booking as any).schedule?.train?.number || 'N/A',
|
trainName: (booking as any).schedule?.train?.name || (booking as any).schedule?.train?.number || 'N/A',
|
||||||
|
|||||||
@@ -81,20 +81,6 @@ export default function AuthCheckPage() {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
<Tooltip content={[
|
|
||||||
'Saved passenger details',
|
|
||||||
'View booking history',
|
|
||||||
'Faster future bookings',
|
|
||||||
]}>
|
|
||||||
<button
|
|
||||||
onClick={() => router.push('/login?redirect=/booking/passengers')}
|
|
||||||
className="w-full flex items-center justify-center gap-2.5 px-5 py-3.5 bg-primary hover:bg-primary/90 active:scale-[0.98] text-white font-semibold rounded-xl transition-all shadow-md shadow-primary/20"
|
|
||||||
>
|
|
||||||
<LogIn className="w-5 h-5 flex-shrink-0" />
|
|
||||||
Sign in
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip content={[
|
<Tooltip content={[
|
||||||
'No account required',
|
'No account required',
|
||||||
'Quick checkout',
|
'Quick checkout',
|
||||||
@@ -102,12 +88,26 @@ export default function AuthCheckPage() {
|
|||||||
]}>
|
]}>
|
||||||
<button
|
<button
|
||||||
onClick={() => router.push('/booking/passengers')}
|
onClick={() => router.push('/booking/passengers')}
|
||||||
className="w-full flex items-center justify-center gap-2.5 px-5 py-3.5 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 active:scale-[0.98] text-gray-800 dark:text-gray-100 font-semibold rounded-xl border border-gray-200 dark:border-gray-700 transition-all shadow-sm"
|
className="w-full flex items-center justify-center gap-2.5 px-5 py-3.5 bg-primary hover:bg-primary/90 active:scale-[0.98] text-white font-semibold rounded-xl transition-all shadow-md shadow-primary/20"
|
||||||
>
|
>
|
||||||
<UserPlus className="w-5 h-5 flex-shrink-0" />
|
<UserPlus className="w-5 h-5 flex-shrink-0" />
|
||||||
Continue as guest
|
Continue as guest
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip content={[
|
||||||
|
'Saved passenger details',
|
||||||
|
'View booking history',
|
||||||
|
'Faster future bookings',
|
||||||
|
]}>
|
||||||
|
<button
|
||||||
|
onClick={() => router.push('/login?redirect=/booking/passengers')}
|
||||||
|
className="w-full flex items-center justify-center gap-2.5 px-5 py-3.5 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 active:scale-[0.98] text-gray-800 dark:text-gray-100 font-semibold rounded-xl border border-gray-200 dark:border-gray-700 transition-all shadow-sm"
|
||||||
|
>
|
||||||
|
<LogIn className="w-5 h-5 flex-shrink-0" />
|
||||||
|
SignIn or Register
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-8 text-center">
|
<div className="mt-8 text-center">
|
||||||
|
|||||||
Reference in New Issue
Block a user