mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 00:52:50 +00:00
@@ -311,6 +311,7 @@ function PassengerCountModal({
|
||||
const [adultCount, setAdultCount] = useState(1);
|
||||
const [childCount, setChildCount] = useState(0);
|
||||
const [departureStationId, setDepartureStationId] = useState('');
|
||||
const [showStationError, setShowStationError] = useState(false);
|
||||
const remaining = tier.availableSeats - tier.bookedSeats;
|
||||
const childFareMinor = Math.round(tier.priceMinor * PKG_CHILD_FARE_RATIO);
|
||||
const totalMinor = (adultCount * tier.priceMinor + childCount * childFareMinor) * priceMultiplier;
|
||||
@@ -378,8 +379,10 @@ function PassengerCountModal({
|
||||
</label>
|
||||
<select
|
||||
value={departureStationId}
|
||||
onChange={(e) => setDepartureStationId(e.target.value)}
|
||||
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"
|
||||
onChange={(e) => { setDepartureStationId(e.target.value); setShowStationError(false); }}
|
||||
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>
|
||||
{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>
|
||||
</div>
|
||||
|
||||
{showStationError && !departureStationId && (
|
||||
<p className="text-xs text-red-500 -mt-2">Please select your boarding station to continue.</p>
|
||||
)}
|
||||
|
||||
<button type="button" onClick={() => {
|
||||
if (!departureStationId) { setShowStationError(true); return; }
|
||||
const station = stations.find(s => s.id === departureStationId);
|
||||
onConfirm(adultCount, childCount, departureStationId, station?.name ?? '');
|
||||
}}
|
||||
@@ -429,7 +437,20 @@ export default function PackageDetailPage() {
|
||||
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);
|
||||
|
||||
|
||||
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