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, 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,

View File

@@ -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">

View File

@@ -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">

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" ? "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"
}`} }`}