mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 15:48:11 +00:00
@@ -311,6 +311,7 @@ function PassengerCountModal({
|
|||||||
const [adultCount, setAdultCount] = useState(1);
|
const [adultCount, setAdultCount] = useState(1);
|
||||||
const [childCount, setChildCount] = useState(0);
|
const [childCount, setChildCount] = useState(0);
|
||||||
const [departureStationId, setDepartureStationId] = useState('');
|
const [departureStationId, setDepartureStationId] = useState('');
|
||||||
|
const [showStationError, setShowStationError] = useState(false);
|
||||||
const remaining = tier.availableSeats - tier.bookedSeats;
|
const remaining = tier.availableSeats - tier.bookedSeats;
|
||||||
const childFareMinor = Math.round(tier.priceMinor * PKG_CHILD_FARE_RATIO);
|
const childFareMinor = Math.round(tier.priceMinor * PKG_CHILD_FARE_RATIO);
|
||||||
const totalMinor = (adultCount * tier.priceMinor + childCount * childFareMinor) * priceMultiplier;
|
const totalMinor = (adultCount * tier.priceMinor + childCount * childFareMinor) * priceMultiplier;
|
||||||
@@ -378,8 +379,10 @@ function PassengerCountModal({
|
|||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={departureStationId}
|
value={departureStationId}
|
||||||
onChange={(e) => setDepartureStationId(e.target.value)}
|
onChange={(e) => { setDepartureStationId(e.target.value); setShowStationError(false); }}
|
||||||
className="w-full rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm text-gray-900 dark:text-white px-3 py-2.5 focus:outline-none focus:ring-2 focus:ring-primary/40"
|
className={`w-full rounded-xl border bg-white dark:bg-gray-800 text-sm text-gray-900 dark:text-white px-3 py-2.5 focus:outline-none focus:ring-2 focus:ring-primary/40 ${
|
||||||
|
showStationError && !departureStationId ? 'border-red-400 dark:border-red-500' : 'border-gray-200 dark:border-gray-700'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<option value="">Select your boarding station</option>
|
<option value="">Select your boarding station</option>
|
||||||
{stations.map((s) => (
|
{stations.map((s) => (
|
||||||
@@ -389,7 +392,12 @@ function PassengerCountModal({
|
|||||||
<p className="text-[10px] text-gray-400 mt-1">For informational purposes — pricing remains fixed regardless of boarding point.</p>
|
<p className="text-[10px] text-gray-400 mt-1">For informational purposes — pricing remains fixed regardless of boarding point.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{showStationError && !departureStationId && (
|
||||||
|
<p className="text-xs text-red-500 -mt-2">Please select your boarding station to continue.</p>
|
||||||
|
)}
|
||||||
|
|
||||||
<button type="button" onClick={() => {
|
<button type="button" onClick={() => {
|
||||||
|
if (!departureStationId) { setShowStationError(true); return; }
|
||||||
const station = stations.find(s => s.id === departureStationId);
|
const station = stations.find(s => s.id === departureStationId);
|
||||||
onConfirm(adultCount, childCount, departureStationId, station?.name ?? '');
|
onConfirm(adultCount, childCount, departureStationId, station?.name ?? '');
|
||||||
}}
|
}}
|
||||||
@@ -429,7 +437,20 @@ export default function PackageDetailPage() {
|
|||||||
queryFn: async () => (await apiClient.get(`/stations?pageSize=100`)) as any,
|
queryFn: async () => (await apiClient.get(`/stations?pageSize=100`)) as any,
|
||||||
});
|
});
|
||||||
|
|
||||||
const stations: Station[] = Array.isArray(stationsData) ? stationsData : stationsData?.items || stationsData?.data?.items || [];
|
const allStations: Station[] = Array.isArray(stationsData) ? stationsData : stationsData?.items || stationsData?.data?.items || [];
|
||||||
|
|
||||||
|
// Fetch stops for the outbound schedule to filter stations to only those on the route
|
||||||
|
const outboundScheduleId = pkg?.outboundSchedule?.id;
|
||||||
|
const { data: scheduleStops } = useQuery({
|
||||||
|
queryKey: ["schedule-stops", outboundScheduleId],
|
||||||
|
queryFn: async () => (await apiClient.get(`/schedules/${outboundScheduleId}/stops`)) as any[],
|
||||||
|
enabled: !!outboundScheduleId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only show stations that are actual stops on the route
|
||||||
|
const stations: Station[] = scheduleStops?.length
|
||||||
|
? allStations.filter((s) => scheduleStops.some((stop: any) => stop.stationId === s.id || stop.station?.id === s.id))
|
||||||
|
: allStations;
|
||||||
|
|
||||||
const selectedTier = pkg?.priceTiers?.find((t) => t.id === selectedTierId);
|
const selectedTier = pkg?.priceTiers?.find((t) => t.id === selectedTierId);
|
||||||
|
|
||||||
|
|||||||
54
apps/edr-passenger-web/portal/src/middleware.ts
Normal file
54
apps/edr-passenger-web/portal/src/middleware.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
// Routes that should NOT redirect to home on hard refresh
|
||||||
|
const PRESERVED_ROUTES = [
|
||||||
|
'/booking/',
|
||||||
|
'/login',
|
||||||
|
'/register',
|
||||||
|
'/forgot-password',
|
||||||
|
'/reset-password',
|
||||||
|
'/set-password',
|
||||||
|
'/verify-account',
|
||||||
|
'/fayda-setup',
|
||||||
|
'/profile',
|
||||||
|
'/about',
|
||||||
|
'/contact',
|
||||||
|
'/help',
|
||||||
|
'/guide',
|
||||||
|
'/services',
|
||||||
|
'/go/',
|
||||||
|
];
|
||||||
|
|
||||||
|
export function middleware(request: NextRequest) {
|
||||||
|
const { pathname } = request.nextUrl;
|
||||||
|
|
||||||
|
// Only intercept hard refreshes (no Referer header = direct navigation / refresh)
|
||||||
|
const referer = request.headers.get('referer');
|
||||||
|
const isHardRefresh = !referer;
|
||||||
|
|
||||||
|
// Skip Next.js internals, static files, and API routes
|
||||||
|
if (
|
||||||
|
pathname.startsWith('/_next') ||
|
||||||
|
pathname.startsWith('/api') ||
|
||||||
|
pathname.includes('.') ||
|
||||||
|
pathname === '/'
|
||||||
|
) {
|
||||||
|
return NextResponse.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// On hard refresh of a preserved route, let it through
|
||||||
|
if (PRESERVED_ROUTES.some((r) => pathname.startsWith(r))) {
|
||||||
|
return NextResponse.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// On hard refresh of package detail or packages list, redirect to home
|
||||||
|
if (isHardRefresh && (pathname.startsWith('/packages'))) {
|
||||||
|
return NextResponse.redirect(new URL('/', request.url));
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user