-
Coach type
-
{tier.seatClass?.coachType?.name ?? tier.label.trim()}
-
{remaining} seats remaining · from {formatPrice(tier.priceMinor * priceMultiplier, tier.currency)} per adult · 1st child per adult free (no seat)
+
+ Coach type
+
+
+ {tier.seatClass?.coachType?.name ?? tier.label.trim()}
+
+
+ {remaining} seats remaining · from{" "}
+ {formatPrice(tier.priceMinor * priceMultiplier, tier.currency)}{" "}
+ per adult · 1st child per adult free (no seat)
+
{[
- { label: "Adults", sub: `Age 5+ · max ${PKG_MAX_ADULTS}`, value: adultCount, min: 1, max: Math.min(PKG_MAX_ADULTS, remaining), set: (v: number) => { setAdultCount(v); const newMax = Math.min(v * PKG_CHILDREN_PER_ADULT, v + Math.max(0, remaining - v)); setChildCount(c => Math.min(c, newMax)); } },
- { label: "Children", sub: `Under 5 · max ${PKG_CHILDREN_PER_ADULT} per adult · 1st per adult FREE (no seat)`, value: childCount, min: 0, max: Math.min(adultCount * PKG_CHILDREN_PER_ADULT, adultCount + Math.max(0, remaining - adultCount)), set: setChildCount },
+ {
+ label: "Adults",
+ sub: `Age 5+ · max ${PKG_MAX_ADULTS}`,
+ value: adultCount,
+ min: 1,
+ max: Math.min(PKG_MAX_ADULTS, remaining),
+ set: (v: number) => {
+ setAdultCount(v);
+ const newMax = Math.min(
+ v * PKG_CHILDREN_PER_ADULT,
+ v + Math.max(0, remaining - v),
+ );
+ setChildCount((c) => Math.min(c, newMax));
+ },
+ },
+ {
+ label: "Children",
+ sub: `Under 5 · max ${PKG_CHILDREN_PER_ADULT} per adult · 1st per adult FREE (no seat)`,
+ value: childCount,
+ min: 0,
+ max: Math.min(
+ adultCount * PKG_CHILDREN_PER_ADULT,
+ adultCount + Math.max(0, remaining - adultCount),
+ ),
+ set: setChildCount,
+ },
].map(({ label, sub, value, min, max, set }) => (
-
{label}
+
+ {label}
+
{sub}
-
@@ -467,22 +647,64 @@ function PassengerCountModal({
{freeChildren > 0 && (
- {freeChildren} child{freeChildren > 1 ? 'ren' : ''} travel free (no seat)
+
+ {freeChildren} child{freeChildren > 1 ? "ren" : ""} travel
+ free (no seat)
+
ETB 0.00
)}
- Total{priceMultiplier === 2 ? ' (round-trip) from:' : ''}
- {formatPrice(totalMinor, tier.currency)}
+
+ Total{priceMultiplier === 2 ? " (round-trip) from:" : ""}
+
+
+ {formatPrice(totalMinor, tier.currency)}
+
{error && (
)}
+ {/* Nationality */}
+
+
+ Nationality
+
+
+ {natOptions.map((opt) => (
+ {
+ setNationality(opt.value);
+ setShowNationalityError(false);
+ }}
+ className={`py-2.5 px-2 rounded-xl border-2 text-xs font-semibold transition-all ${
+ nationality === opt.value
+ ? "border-primary bg-primary/5 text-primary"
+ : showNationalityError
+ ? "border-red-300 dark:border-red-800 text-gray-600 dark:text-gray-400 hover:border-gray-300"
+ : "border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:border-gray-300"
+ }`}
+ >
+ {opt.label}
+
+ ))}
+
+ {showNationalityError && !nationality && (
+
+ Please select a nationality to continue.
+
+ )}
+
+
{/* Departure Station */}
-
For informational purposes — pricing remains fixed regardless of boarding point.
+
+ For informational purposes — pricing remains fixed regardless of
+ boarding point.
+
{showStationError && !departureStationId && (
-
Please select your boarding station to continue.
+
+ Please select your boarding station to continue.
+
)}
-
{
- if (!departureStationId) { setShowStationError(true); return; }
- const station = stations.find(s => s.id === departureStationId);
- onConfirm(adultCount, childCount, departureStationId, station?.name ?? '');
- }}
+ {
+ if (!nationality) {
+ setShowNationalityError(true);
+ return;
+ }
+ if (!departureStationId) {
+ setShowStationError(true);
+ return;
+ }
+ const station = stations.find(
+ (s) => s.id === departureStationId,
+ );
+ onConfirm(
+ adultCount,
+ childCount,
+ departureStationId,
+ station?.name ?? "",
+ nationality,
+ );
+ }}
disabled={loading || adultCount + childCount < 1}
- className="w-full py-3.5 bg-[rgb(20,113,76)] hover:bg-[rgb(16,89,60)] disabled:opacity-60 text-white font-bold text-sm rounded-xl transition-all shadow-lg flex items-center justify-center gap-2">
- {loading ? <> Loading...> : <>Continue >}
+ className="w-full py-3.5 bg-[rgb(20,113,76)] hover:bg-[rgb(16,89,60)] disabled:opacity-60 text-white font-bold text-sm rounded-xl transition-all shadow-lg flex items-center justify-center gap-2"
+ >
+ {loading ? (
+ <>
+ Loading...
+ >
+ ) : (
+ <>
+ Continue
+ >
+ )}
@@ -528,14 +789,30 @@ export default function PackageDetailPage() {
const params = useParams();
const router = useRouter();
const id = params?.id as string;
- const { clearBooking, setSearchCriteria, setSelectedSchedule, setOutboundSchedule, setInboundSchedule, setPassengers, setPackageContext } = useBookingStore();
+ const {
+ clearBooking,
+ setSearchCriteria,
+ setSelectedSchedule,
+ setOutboundSchedule,
+ setInboundSchedule,
+ setPassengers,
+ setPackageContext,
+ } = useBookingStore();
- const [selectedCoachTypeId, setSelectedCoachTypeId] = useState