mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 20:05:41 +00:00
Update seatmap for blocked seats
This commit is contained in:
@@ -1879,6 +1879,8 @@ export class BookingsService {
|
|||||||
adultCount: booking.adultCount, childCount: booking.childCount,
|
adultCount: booking.adultCount, childCount: booking.childCount,
|
||||||
displayCurrency: booking.displayCurrency, displayTotalMinor: booking.displayTotalMinor ?? undefined,
|
displayCurrency: booking.displayCurrency, displayTotalMinor: booking.displayTotalMinor ?? undefined,
|
||||||
bookingType: booking.bookingType,
|
bookingType: booking.bookingType,
|
||||||
|
packageId: (booking as any).packageId ?? null,
|
||||||
|
isPackageBooking: !!(booking as any).packageId,
|
||||||
returnLegStatus: (booking as any).returnLegStatus ?? null,
|
returnLegStatus: (booking as any).returnLegStatus ?? null,
|
||||||
outboundBoardedAt: (booking as any).outboundBoardedAt ?? null,
|
outboundBoardedAt: (booking as any).outboundBoardedAt ?? null,
|
||||||
returnBoardedAt: (booking as any).returnBoardedAt ?? null,
|
returnBoardedAt: (booking as any).returnBoardedAt ?? null,
|
||||||
|
|||||||
@@ -427,6 +427,7 @@ function BookingDetailContent() {
|
|||||||
</span>
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
{!booking.isPackageBooking && booking.bookingType !== "PACKAGE" && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h3 className="text-sm font-bold text-gray-900 dark:text-gray-100">
|
<h3 className="text-sm font-bold text-gray-900 dark:text-gray-100">
|
||||||
Fare breakdown
|
Fare breakdown
|
||||||
@@ -481,6 +482,7 @@ function BookingDetailContent() {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="pt-2 border-t-2 border-gray-200 dark:border-gray-700">
|
<div className="pt-2 border-t-2 border-gray-200 dark:border-gray-700">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export default function ReviewPage() {
|
|||||||
const [timeLeft, setTimeLeft] = useState<string>('');
|
const [timeLeft, setTimeLeft] = useState<string>('');
|
||||||
const [seatDetails, setSeatDetails] = useState<Record<string, string>>({});
|
const [seatDetails, setSeatDetails] = useState<Record<string, string>>({});
|
||||||
const [fareBreakdown, setFareBreakdown] = useState<any>(null);
|
const [fareBreakdown, setFareBreakdown] = useState<any>(null);
|
||||||
|
const [returnFareBreakdown, setReturnFareBreakdown] = useState<any>(null);
|
||||||
const [computedTotal, setComputedTotal] = useState<number>(0);
|
const [computedTotal, setComputedTotal] = useState<number>(0);
|
||||||
|
|
||||||
const isRoundTrip = searchCriteria?.tripType === 'ROUND_TRIP';
|
const isRoundTrip = searchCriteria?.tripType === 'ROUND_TRIP';
|
||||||
@@ -173,16 +174,27 @@ const adultPassengerCount = searchCriteria?.adultCount ?? passengers.filter(p =>
|
|||||||
// Prefers displayFareMinor (converted) from the fare breakdown API when available.
|
// Prefers displayFareMinor (converted) from the fare breakdown API when available.
|
||||||
// Falls back to raw ETB seat fares (which are always in minor units).
|
// Falls back to raw ETB seat fares (which are always in minor units).
|
||||||
const getPassengerSeatFare = (p: any, index?: number): number | null => {
|
const getPassengerSeatFare = (p: any, index?: number): number | null => {
|
||||||
|
if (isRoundTrip) {
|
||||||
|
// Seat-specific fares (set during seat selection) cover each leg separately — use them first.
|
||||||
|
if (p.outboundSeatFareMinor != null || p.inboundSeatFareMinor != null) {
|
||||||
|
if (isPackageBooking) return (p.outboundSeatFareMinor ?? 0) * 2;
|
||||||
|
return (p.outboundSeatFareMinor ?? 0) + (p.inboundSeatFareMinor ?? 0);
|
||||||
|
}
|
||||||
|
// No seat-specific fares: fall back to the per-leg fare-breakdown totals for both legs.
|
||||||
|
if (!isPackageBooking && fareBreakdown?.passengers && returnFareBreakdown?.passengers && index != null) {
|
||||||
|
const obLine = fareBreakdown.passengers[index];
|
||||||
|
const retLine = returnFareBreakdown.passengers[index];
|
||||||
|
const obFare = obLine?.displayFareMinor ?? obLine?.fareMinor;
|
||||||
|
const retFare = retLine?.displayFareMinor ?? retLine?.fareMinor;
|
||||||
|
if (obFare != null && retFare != null) return obFare + retFare;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (!isPackageBooking && fareBreakdown?.passengers && index != null) {
|
if (!isPackageBooking && fareBreakdown?.passengers && index != null) {
|
||||||
const line = fareBreakdown.passengers[index];
|
const line = fareBreakdown.passengers[index];
|
||||||
const displayFare = line?.displayFareMinor ?? line?.fareMinor;
|
const displayFare = line?.displayFareMinor ?? line?.fareMinor;
|
||||||
if (displayFare != null) return displayFare;
|
if (displayFare != null) return displayFare;
|
||||||
}
|
}
|
||||||
if (isRoundTrip) {
|
|
||||||
if (p.outboundSeatFareMinor == null && p.inboundSeatFareMinor == null) return null;
|
|
||||||
if (isPackageBooking) return (p.outboundSeatFareMinor ?? 0) * 2;
|
|
||||||
return (p.outboundSeatFareMinor ?? 0) + (p.inboundSeatFareMinor ?? 0);
|
|
||||||
}
|
|
||||||
if (p.seatFareMinor == null) return null;
|
if (p.seatFareMinor == null) return null;
|
||||||
return isPackageBooking ? p.seatFareMinor * 2 : p.seatFareMinor;
|
return isPackageBooking ? p.seatFareMinor * 2 : p.seatFareMinor;
|
||||||
};
|
};
|
||||||
@@ -541,6 +553,22 @@ const adultPassengerCount = searchCriteria?.adultCount ?? passengers.filter(p =>
|
|||||||
|
|
||||||
const result: any = await apiClient.get(`/search/fare-breakdown?${params}`);
|
const result: any = await apiClient.get(`/search/fare-breakdown?${params}`);
|
||||||
setFareBreakdown(result);
|
setFareBreakdown(result);
|
||||||
|
|
||||||
|
// For round-trips, also fetch the return leg's fare breakdown so the review page
|
||||||
|
// can display and send the correct combined total (outbound + return per passenger).
|
||||||
|
if (isRoundTrip && inboundSchedule) {
|
||||||
|
const returnScheduleId = (inboundSchedule as any).id;
|
||||||
|
const returnParams = new URLSearchParams({
|
||||||
|
scheduleId: returnScheduleId,
|
||||||
|
originStationId: searchCriteria.destinationStationId,
|
||||||
|
destinationStationId: searchCriteria.originStationId,
|
||||||
|
passengers: passengersParam,
|
||||||
|
displayCurrency: displayCurrencyCode,
|
||||||
|
...(searchCriteria.promoCode ? { promoCode: searchCriteria.promoCode } : {}),
|
||||||
|
});
|
||||||
|
const returnResult: any = await apiClient.get(`/search/fare-breakdown?${returnParams}`);
|
||||||
|
setReturnFareBreakdown(returnResult);
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
@@ -566,7 +594,11 @@ const adultPassengerCount = searchCriteria?.adultCount ?? passengers.filter(p =>
|
|||||||
const isFreeChild = line?.isFree ?? (isChildPassenger && isFirstChild(passengers, i));
|
const isFreeChild = line?.isFree ?? (isChildPassenger && isFirstChild(passengers, i));
|
||||||
if (isFreeChild) return sum;
|
if (isFreeChild) return sum;
|
||||||
const seatFare = getPassengerSeatFare(p, i);
|
const seatFare = getPassengerSeatFare(p, i);
|
||||||
const displayFare = line?.displayFareMinor ?? line?.fareMinor;
|
// For round-trips the fallback must combine both legs; for one-way it's the single-leg fare.
|
||||||
|
const obFare = line?.displayFareMinor ?? line?.fareMinor;
|
||||||
|
const retLine = returnFareBreakdown?.passengers?.[i];
|
||||||
|
const retFare = retLine?.displayFareMinor ?? retLine?.fareMinor;
|
||||||
|
const displayFare = isRoundTrip && retFare != null ? (obFare ?? 0) + retFare : obFare;
|
||||||
return sum + (seatFare ?? displayFare ?? 0);
|
return sum + (seatFare ?? displayFare ?? 0);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
@@ -586,26 +618,27 @@ const adultPassengerCount = searchCriteria?.adultCount ?? passengers.filter(p =>
|
|||||||
? isPkgFreeChild(i)
|
? isPkgFreeChild(i)
|
||||||
: (line?.isFree ?? (isChild(p) && isFirstChild(passengers, i)));
|
: (line?.isFree ?? (isChild(p) && isFirstChild(passengers, i)));
|
||||||
|
|
||||||
// Per-leg fares for round trips — use converted amounts from fareBreakdown when available
|
// Per-leg fares for round trips — prefer seat-specific fares, then per-leg breakdowns.
|
||||||
const displayFare = line?.displayFareMinor ?? line?.fareMinor;
|
const retLine = returnFareBreakdown?.passengers?.[i];
|
||||||
|
const obBreakdownFare = line?.displayFareMinor ?? line?.fareMinor;
|
||||||
|
const retBreakdownFare = retLine?.displayFareMinor ?? retLine?.fareMinor;
|
||||||
const outboundFare: number | null = isRoundTrip
|
const outboundFare: number | null = isRoundTrip
|
||||||
? (isPackageBooking
|
? (isPackageBooking
|
||||||
? (packageTierPriceMinor ?? null)
|
? (packageTierPriceMinor ?? null)
|
||||||
: (displayFare != null
|
: ((p as any).outboundSeatFareMinor ?? obBreakdownFare ?? null))
|
||||||
? Math.round(displayFare / 2)
|
|
||||||
: ((p as any).outboundSeatFareMinor ?? null)))
|
|
||||||
: null;
|
: null;
|
||||||
const inboundFare: number | null = isRoundTrip
|
const inboundFare: number | null = isRoundTrip
|
||||||
? (isPackageBooking
|
? (isPackageBooking
|
||||||
? (packageTierPriceMinor ?? null)
|
? (packageTierPriceMinor ?? null)
|
||||||
: (displayFare != null
|
: ((p as any).inboundSeatFareMinor ?? retBreakdownFare ?? null))
|
||||||
? Math.round(displayFare / 2)
|
|
||||||
: ((p as any).inboundSeatFareMinor ?? null)))
|
|
||||||
: null;
|
: null;
|
||||||
const seatFare = getPassengerSeatFare(p, i);
|
const seatFare = getPassengerSeatFare(p, i);
|
||||||
|
const combinedDisplayFare = isRoundTrip && retBreakdownFare != null
|
||||||
|
? (obBreakdownFare ?? 0) + retBreakdownFare
|
||||||
|
: obBreakdownFare;
|
||||||
const passengerTotal = isPackageBooking
|
const passengerTotal = isPackageBooking
|
||||||
? (isFreeChild ? 0 : (seatFare ?? (isChildPassenger ? pkgChildFare : pkgAdultFare)))
|
? (isFreeChild ? 0 : (seatFare ?? (isChildPassenger ? pkgChildFare : pkgAdultFare)))
|
||||||
: (isFreeChild ? 0 : (seatFare ?? displayFare ?? 0));
|
: (isFreeChild ? 0 : (seatFare ?? combinedDisplayFare ?? 0));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={i} className="border-b border-gray-100 dark:border-gray-800 pb-2 last:border-0">
|
<div key={i} className="border-b border-gray-100 dark:border-gray-800 pb-2 last:border-0">
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const BedCard = memo(({ bed, isSelected, isAssignedToOther, onToggle }: any) =>
|
|||||||
? "bg-purple-50 border-purple-300 cursor-not-allowed dark:bg-purple-900/20 dark:border-purple-700"
|
? "bg-purple-50 border-purple-300 cursor-not-allowed dark:bg-purple-900/20 dark:border-purple-700"
|
||||||
: bed.status === "AVAILABLE"
|
: bed.status === "AVAILABLE"
|
||||||
? "bg-green-50 border-green-300 hover:bg-green-100 hover:border-green-400 dark:bg-green-900/20 dark:border-green-700"
|
? "bg-green-50 border-green-300 hover:bg-green-100 hover:border-green-400 dark:bg-green-900/20 dark:border-green-700"
|
||||||
: bed.status === "BOOKED"
|
: bed.status === "BOOKED" || bed.status === "BLOCKED"
|
||||||
? "bg-red-50 border-red-300 cursor-not-allowed dark:bg-red-900/20 dark:border-red-700"
|
? "bg-red-50 border-red-300 cursor-not-allowed dark:bg-red-900/20 dark:border-red-700"
|
||||||
: "bg-gray-100 border-gray-300 cursor-not-allowed dark:bg-gray-800 dark:border-gray-700"
|
: "bg-gray-100 border-gray-300 cursor-not-allowed dark:bg-gray-800 dark:border-gray-700"
|
||||||
}`}
|
}`}
|
||||||
|
|||||||
Reference in New Issue
Block a user