Update seatmap for blocked seats

This commit is contained in:
Roba Boru
2026-07-15 21:28:35 +03:00
parent ffa5a114b5
commit f185da2163
4 changed files with 53 additions and 16 deletions

View File

@@ -1879,6 +1879,8 @@ export class BookingsService {
adultCount: booking.adultCount, childCount: booking.childCount,
displayCurrency: booking.displayCurrency, displayTotalMinor: booking.displayTotalMinor ?? undefined,
bookingType: booking.bookingType,
packageId: (booking as any).packageId ?? null,
isPackageBooking: !!(booking as any).packageId,
returnLegStatus: (booking as any).returnLegStatus ?? null,
outboundBoardedAt: (booking as any).outboundBoardedAt ?? null,
returnBoardedAt: (booking as any).returnBoardedAt ?? null,

View File

@@ -427,6 +427,7 @@ function BookingDetailContent() {
</span>
</h2>
{!booking.isPackageBooking && booking.bookingType !== "PACKAGE" && (
<div className="space-y-2">
<h3 className="text-sm font-bold text-gray-900 dark:text-gray-100">
Fare breakdown
@@ -481,6 +482,7 @@ function BookingDetailContent() {
);
})}
</div>
)}
<div className="pt-2 border-t-2 border-gray-200 dark:border-gray-700">
<div className="flex justify-between items-center">

View File

@@ -52,6 +52,7 @@ export default function ReviewPage() {
const [timeLeft, setTimeLeft] = useState<string>('');
const [seatDetails, setSeatDetails] = useState<Record<string, string>>({});
const [fareBreakdown, setFareBreakdown] = useState<any>(null);
const [returnFareBreakdown, setReturnFareBreakdown] = useState<any>(null);
const [computedTotal, setComputedTotal] = useState<number>(0);
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.
// Falls back to raw ETB seat fares (which are always in minor units).
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) {
const line = fareBreakdown.passengers[index];
const displayFare = line?.displayFareMinor ?? line?.fareMinor;
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;
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}`);
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) {
}
})();
@@ -566,7 +594,11 @@ const adultPassengerCount = searchCriteria?.adultCount ?? passengers.filter(p =>
const isFreeChild = line?.isFree ?? (isChildPassenger && isFirstChild(passengers, i));
if (isFreeChild) return sum;
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);
}, 0);
@@ -586,26 +618,27 @@ const adultPassengerCount = searchCriteria?.adultCount ?? passengers.filter(p =>
? isPkgFreeChild(i)
: (line?.isFree ?? (isChild(p) && isFirstChild(passengers, i)));
// Per-leg fares for round trips — use converted amounts from fareBreakdown when available
const displayFare = line?.displayFareMinor ?? line?.fareMinor;
// Per-leg fares for round trips — prefer seat-specific fares, then per-leg breakdowns.
const retLine = returnFareBreakdown?.passengers?.[i];
const obBreakdownFare = line?.displayFareMinor ?? line?.fareMinor;
const retBreakdownFare = retLine?.displayFareMinor ?? retLine?.fareMinor;
const outboundFare: number | null = isRoundTrip
? (isPackageBooking
? (packageTierPriceMinor ?? null)
: (displayFare != null
? Math.round(displayFare / 2)
: ((p as any).outboundSeatFareMinor ?? null)))
: ((p as any).outboundSeatFareMinor ?? obBreakdownFare ?? null))
: null;
const inboundFare: number | null = isRoundTrip
? (isPackageBooking
? (packageTierPriceMinor ?? null)
: (displayFare != null
? Math.round(displayFare / 2)
: ((p as any).inboundSeatFareMinor ?? null)))
: ((p as any).inboundSeatFareMinor ?? retBreakdownFare ?? null))
: null;
const seatFare = getPassengerSeatFare(p, i);
const combinedDisplayFare = isRoundTrip && retBreakdownFare != null
? (obBreakdownFare ?? 0) + retBreakdownFare
: obBreakdownFare;
const passengerTotal = isPackageBooking
? (isFreeChild ? 0 : (seatFare ?? (isChildPassenger ? pkgChildFare : pkgAdultFare)))
: (isFreeChild ? 0 : (seatFare ?? displayFare ?? 0));
: (isFreeChild ? 0 : (seatFare ?? combinedDisplayFare ?? 0));
return (
<div key={i} className="border-b border-gray-100 dark:border-gray-800 pb-2 last:border-0">

View File

@@ -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"
: 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"
: 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-gray-100 border-gray-300 cursor-not-allowed dark:bg-gray-800 dark:border-gray-700"
}`}