From bd82e95f9fc1c2ce3cd35eca5bdedfb4908119d5 Mon Sep 17 00:00:00 2001 From: Roba Boru Date: Thu, 25 Jun 2026 19:58:47 +0300 Subject: [PATCH] Fix test findings --- .../src/app/booking/confirmation/page.tsx | 163 +++++++++++++----- .../src/app/booking/passengers/page.tsx | 55 ++++-- .../portal/src/app/booking/results/page.tsx | 4 +- .../portal/src/app/booking/review/page.tsx | 47 +++-- .../portal/src/app/booking/search/page.tsx | 16 +- .../portal/src/app/booking/seats/page.tsx | 3 +- 6 files changed, 210 insertions(+), 78 deletions(-) diff --git a/apps/edr-passenger-web/portal/src/app/booking/confirmation/page.tsx b/apps/edr-passenger-web/portal/src/app/booking/confirmation/page.tsx index 5644c9b56..29af2b29b 100644 --- a/apps/edr-passenger-web/portal/src/app/booking/confirmation/page.tsx +++ b/apps/edr-passenger-web/portal/src/app/booking/confirmation/page.tsx @@ -16,6 +16,8 @@ type BookingWithTicket = { pnr?: string | null; status?: string; totalMinor?: number; + createdAt?: string; + paymentMethod?: string; ticket?: { barcodePayload?: string; qrPayload?: string; @@ -24,7 +26,8 @@ type BookingWithTicket = { export default function ConfirmationPage() { const router = useRouter(); - const { bookingId, pnr, selectedSchedule, passengers, clearBooking } = useBookingStore(); + const { bookingId, pnr, selectedSchedule, outboundSchedule, inboundSchedule, searchCriteria, passengers, clearBooking } = useBookingStore(); + const isRoundTrip = searchCriteria?.tripType === 'ROUND_TRIP'; const [copied, setCopied] = useState(false); const [isGeneratingVoucher, setIsGeneratingVoucher] = useState(false); const confirmAttempted = useRef(false); @@ -193,12 +196,7 @@ export default function ConfirmationPage() {
{/* QR Code Section */}
- +

Scan at gate

@@ -208,44 +206,129 @@ export default function ConfirmationPage() {
-

Trip details

+

+ {isRoundTrip ? 'Round trip details' : 'Trip details'} +

-
-
-
-

Train number

-

{selectedSchedule?.trainNumber}

-
-
-

Route

-

{selectedSchedule?.origin} → {selectedSchedule?.destination}

-
- {selectedSchedule?.selectedSeatClassName && ( -
-

Class

-

{selectedSchedule.selectedSeatClassName.replace(/_/g, ' ')}

+ + {/* Outbound journey (round trip) or single journey */} + {(() => { + const schedule = isRoundTrip ? outboundSchedule : selectedSchedule; + if (!schedule) return null; + return ( +
+ {isRoundTrip && ( +

Outbound

+ )} +
+
+
+

Train number

+

{schedule.trainNumber}

+
+
+

Route

+

{schedule.origin} → {schedule.destination}

+
+ {schedule.selectedSeatClassName && ( +
+

Class

+

{schedule.selectedSeatClassName.replace(/_/g, ' ')}

+
+ )} +
+
+
+

Departure

+

+ {schedule.departureTime && format(new Date(schedule.departureTime), 'PPp')} +

+
+
+

Arrival

+

+ {schedule.arrivalTime && format(new Date(schedule.arrivalTime), 'PPp')} +

+
+
+

Duration

+

{schedule.duration}

+
+
- )} -
-
-
-

Departure

-

- {selectedSchedule?.departureTime && format(new Date(selectedSchedule.departureTime), 'PPp')} -

-
-

Arrival

-

- {selectedSchedule?.arrivalTime && format(new Date(selectedSchedule.arrivalTime), 'PPp')} -

-
-
-

Duration

-

{selectedSchedule?.duration}

+ ); + })()} + + {/* Return journey (round trip only) */} + {isRoundTrip && inboundSchedule && ( +
+

Return

+
+
+
+

Train number

+

{inboundSchedule.trainNumber}

+
+
+

Route

+

{inboundSchedule.origin} → {inboundSchedule.destination}

+
+ {inboundSchedule.selectedSeatClassName && ( +
+

Class

+

{inboundSchedule.selectedSeatClassName.replace(/_/g, ' ')}

+
+ )} +
+
+
+

Departure

+

+ {inboundSchedule.departureTime && format(new Date(inboundSchedule.departureTime), 'PPp')} +

+
+
+

Arrival

+

+ {inboundSchedule.arrivalTime && format(new Date(inboundSchedule.arrivalTime), 'PPp')} +

+
+
+

Duration

+

{inboundSchedule.duration}

+
+
-
+ )} +
+
+
+ + {/* Booking date & payment summary */} +
+

Booking details

+
+
+

Booking date

+

+ {format(new Date(_booking?.createdAt || new Date()), 'PPp')} +

+
+
+

Status

+

{_booking?.status || 'CONFIRMED'}

+
+
+

Passengers

+

{passengers.length}

+
+
+

Total paid

+

+ ETB {((_booking?.totalMinor || passengers.reduce((s) => s + (selectedSchedule?.baseFareAdult || 0), 0)) / 100).toFixed(2)} +

diff --git a/apps/edr-passenger-web/portal/src/app/booking/passengers/page.tsx b/apps/edr-passenger-web/portal/src/app/booking/passengers/page.tsx index 0a3e1d65b..272992b73 100644 --- a/apps/edr-passenger-web/portal/src/app/booking/passengers/page.tsx +++ b/apps/edr-passenger-web/portal/src/app/booking/passengers/page.tsx @@ -378,7 +378,7 @@ type FormData = z.infer; export default function PassengersPage() { const router = useRouter(); - const { searchCriteria, setPassengers, setCreateAccount } = useBookingStore(); + const { searchCriteria, passengers: storedPassengers, setPassengers, setCreateAccount } = useBookingStore(); const { user, isAuthenticated, updateUser } = useAuthStore(); const isInitialized = useAuthStore((s) => s.isInitialized); const [faydaEnabled, setFaydaEnabled] = useState(true); @@ -393,22 +393,43 @@ export default function PassengersPage() { resolver: zodResolver(formSchema as any), mode: 'onChange', defaultValues: { - passengers: Array.from({ length: totalPassengers }, () => ({ - name: '', - dateOfBirth: '', - gender: undefined, - nationality: searchCriteria?.nationality || 'ETHIOPIAN', - phone: '', - email: '', - nationalId: '', - passportNumber: '', - passportCountry: '', - passportIssueDate: '', - passportExpiryDate: '', - passportIssuingAuthority: '', - faydaVerified: false, - formExpanded: false, - })), + passengers: Array.from({ length: totalPassengers }, (_, i) => { + const stored = storedPassengers[i]; + if (stored?.name) { + return { + name: stored.name, + dateOfBirth: stored.dateOfBirth || '', + gender: (stored.gender as any) || undefined, + nationality: stored.nationality || searchCriteria?.nationality || 'ETHIOPIAN', + phone: stored.phone || '', + email: stored.email || '', + nationalId: stored.nationalId || '', + passportNumber: stored.passportNumber || '', + passportCountry: stored.passportCountry || '', + passportIssueDate: stored.passportIssueDate || '', + passportExpiryDate: stored.passportExpiryDate || '', + passportIssuingAuthority: stored.passportIssuingAuthority || '', + faydaVerified: stored.faydaVerified || false, + formExpanded: true, + }; + } + return { + name: '', + dateOfBirth: '', + gender: undefined, + nationality: searchCriteria?.nationality || 'ETHIOPIAN', + phone: '', + email: '', + nationalId: '', + passportNumber: '', + passportCountry: '', + passportIssueDate: '', + passportExpiryDate: '', + passportIssuingAuthority: '', + faydaVerified: false, + formExpanded: false, + }; + }), createAccount: false, }, }); diff --git a/apps/edr-passenger-web/portal/src/app/booking/results/page.tsx b/apps/edr-passenger-web/portal/src/app/booking/results/page.tsx index 5669240ad..56c293ac4 100644 --- a/apps/edr-passenger-web/portal/src/app/booking/results/page.tsx +++ b/apps/edr-passenger-web/portal/src/app/booking/results/page.tsx @@ -14,7 +14,9 @@ export default function ResultsPage() { const searchParams = useSearchParams(); const { setSelectedSchedule, setOutboundSchedule, setInboundSchedule } = useBookingStore(); const [selectedCoachTypes, setSelectedCoachTypes] = useState>({}); - const [outboundScheduleData, setOutboundScheduleData] = useState(null); + const [outboundScheduleData, setOutboundScheduleData] = useState( + () => useBookingStore.getState().outboundSchedule, + ); const [classModal, setClassModal] = useState(null); const [promoData, setPromoData] = useState<{ code: string; discount: string; message: string } | null>(null); diff --git a/apps/edr-passenger-web/portal/src/app/booking/review/page.tsx b/apps/edr-passenger-web/portal/src/app/booking/review/page.tsx index f0af1b438..7b94503a0 100644 --- a/apps/edr-passenger-web/portal/src/app/booking/review/page.tsx +++ b/apps/edr-passenger-web/portal/src/app/booking/review/page.tsx @@ -648,24 +648,37 @@ export default function ReviewPage() {

Fare breakdown

-
- {isRoundTrip ? ( - <> -
- Outbound fare - ETB {(outboundBaseFare / 100).toFixed(2)} +
+ {passengers.map((p, i) => { + const outFare = outboundSchedule?.baseFareAdult || 0; + const inFare = inboundSchedule?.baseFareAdult || 0; + const onewayFare = selectedSchedule?.baseFareAdult || 0; + const passengerTotal = isRoundTrip ? outFare + inFare : onewayFare; + return ( +
+
+ + {p.name || `Passenger ${i + 1}`} + + + ETB {(passengerTotal / 100).toFixed(2)} + +
+ {isRoundTrip && ( +
+
+ Outbound + ETB {(outFare / 100).toFixed(2)} +
+
+ Return + ETB {(inFare / 100).toFixed(2)} +
+
+ )}
-
- Return fare - ETB {(inboundBaseFare / 100).toFixed(2)} -
- - ) : ( -
- Base fare - ETB {(baseFare / 100).toFixed(2)} -
- )} + ); + })}
Total ETB {(total / 100).toFixed(2)} diff --git a/apps/edr-passenger-web/portal/src/app/booking/search/page.tsx b/apps/edr-passenger-web/portal/src/app/booking/search/page.tsx index a587bc87c..bb6340d0a 100644 --- a/apps/edr-passenger-web/portal/src/app/booking/search/page.tsx +++ b/apps/edr-passenger-web/portal/src/app/booking/search/page.tsx @@ -65,6 +65,16 @@ const searchSchema = z message: "Return date must be after departure date", path: ["returnDate"], }, + ) + .refine( + (d) => + !d.originStationId || + !d.destinationStationId || + d.originStationId !== d.destinationStationId, + { + message: "Departure and destination cannot be the same station", + path: ["destinationStationId"], + }, ); type SearchForm = z.infer; @@ -583,7 +593,7 @@ export default function SearchPage() { adultCount: 1, childCount: 0, nationality: "ETHIOPIAN", - departureDate: new Date().toISOString().split("T")[0], + departureDate: "", promoCode: "", }, }); @@ -609,6 +619,8 @@ export default function SearchPage() { const adults = searchParams.get("adults"); const children = searchParams.get("children"); const nat = searchParams.get("nationality"); + const tripType = searchParams.get("tripType"); + const returnDate = searchParams.get("returnDate"); if (o) setValue("originStationId", o); if (d) setValue("destinationStationId", d); if (date) setValue("departureDate", date); @@ -616,6 +628,8 @@ export default function SearchPage() { if (children) setValue("childCount", parseInt(children)); if (nat) setValue("nationality", nat as "ETHIOPIAN" | "DJIBOUTIAN" | "OTHER"); + if (tripType) setValue("tripType", tripType as "ONE_WAY" | "ROUND_TRIP"); + if (returnDate) setValue("returnDate", returnDate); }, [searchParams, setValue]); useEffect(() => { diff --git a/apps/edr-passenger-web/portal/src/app/booking/seats/page.tsx b/apps/edr-passenger-web/portal/src/app/booking/seats/page.tsx index e088e0303..ba3fe0159 100644 --- a/apps/edr-passenger-web/portal/src/app/booking/seats/page.tsx +++ b/apps/edr-passenger-web/portal/src/app/booking/seats/page.tsx @@ -133,7 +133,6 @@ export default function SeatsPage() { ? outboundSchedule : selectedSchedule; const coachTypeId = (currentSchedule as any)?.selectedCoachTypeId; - const coachTypeCode = (currentSchedule as any)?.selectedCoachTypeCode; const { data: seatMapData, @@ -142,7 +141,7 @@ export default function SeatsPage() { } = useQuery({ queryKey: ["seatmap", currentSchedule?.id, coachTypeId, currentJourneyType], queryFn: async () => { - const endpoint = `/seats/seatmap/${currentSchedule?.id}?coachTypeId=${coachTypeCode}`; + const endpoint = `/seats/seatmap/${currentSchedule?.id}?coachTypeId=${coachTypeId}`; console.log("🪑 Seatmap Request:", { endpoint, });