Gas Station Feature Finally Working
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* @ai-summary Desktop stations page with map and list layout
|
||||
*/
|
||||
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
||||
import {
|
||||
Grid,
|
||||
Paper,
|
||||
@@ -11,7 +11,9 @@ import {
|
||||
Box,
|
||||
Alert,
|
||||
useMediaQuery,
|
||||
useTheme
|
||||
useTheme,
|
||||
CircularProgress,
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
import { Station, StationSearchRequest } from '../types/stations.types';
|
||||
import {
|
||||
@@ -24,7 +26,8 @@ import {
|
||||
StationMap,
|
||||
StationsList,
|
||||
SavedStationsList,
|
||||
StationsSearchForm
|
||||
StationsSearchForm,
|
||||
GoogleMapsErrorBoundary
|
||||
} from '../components';
|
||||
|
||||
interface TabPanelProps {
|
||||
@@ -34,14 +37,19 @@ interface TabPanelProps {
|
||||
}
|
||||
|
||||
const TabPanel: React.FC<TabPanelProps> = ({ children, value, index }) => {
|
||||
// Only render content when tab is active to prevent IntersectionObserver errors
|
||||
// on hidden MUI components
|
||||
if (value !== index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
role="tabpanel"
|
||||
hidden={value !== index}
|
||||
id={`stations-tabpanel-${index}`}
|
||||
aria-labelledby={`stations-tab-${index}`}
|
||||
>
|
||||
{value === index && <Box sx={{ padding: 2 }}>{children}</Box>}
|
||||
<Box sx={{ padding: 2 }}>{children}</Box>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -63,11 +71,51 @@ export const StationsPage: React.FC = () => {
|
||||
const [currentLocation, setCurrentLocation] = useState<
|
||||
{ latitude: number; longitude: number } | undefined
|
||||
>();
|
||||
const [isPageReady, setIsPageReady] = useState(false);
|
||||
const [isMapReady, setIsMapReady] = useState(false);
|
||||
|
||||
// Queries and mutations
|
||||
const { mutate: search, isPending: isSearching, error: searchError } = useStationsSearch();
|
||||
const { data: savedStations = [], isLoading: isSavedLoading, error: savedError } = useSavedStations();
|
||||
console.log('[DEBUG StationsPage] useSavedStations result:', { data: savedStations, isLoading: isSavedLoading, error: savedError });
|
||||
const { data: savedStations = [], isLoading: isSavedLoading, error: savedError, isFetching, isSuccess } = useSavedStations();
|
||||
console.log('[DEBUG StationsPage] useSavedStations result:', {
|
||||
data: savedStations,
|
||||
isLoading: isSavedLoading,
|
||||
isFetching,
|
||||
isSuccess,
|
||||
error: savedError
|
||||
});
|
||||
|
||||
// Multi-stage initialization: Wait for auth, data, and DOM
|
||||
useEffect(() => {
|
||||
// Stage 1: Wait for saved stations query to settle (loading complete or error)
|
||||
const isDataReady = !isSavedLoading && !isFetching;
|
||||
|
||||
if (isDataReady && !isPageReady) {
|
||||
console.log('[DEBUG StationsPage] Data ready, waiting for DOM...');
|
||||
// Stage 2: Wait for DOM to be ready
|
||||
const timer = setTimeout(() => {
|
||||
setIsPageReady(true);
|
||||
console.log('[DEBUG StationsPage] Page ready');
|
||||
}, 150);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [isSavedLoading, isFetching, isPageReady]);
|
||||
|
||||
// Stage 3: Wait for page render to complete before allowing map initialization
|
||||
useEffect(() => {
|
||||
if (isPageReady && !isMapReady) {
|
||||
console.log('[DEBUG StationsPage] Page rendered, enabling map...');
|
||||
const timer = setTimeout(() => {
|
||||
setIsMapReady(true);
|
||||
console.log('[DEBUG StationsPage] Map ready');
|
||||
}, 100);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [isPageReady, isMapReady]);
|
||||
|
||||
const { mutate: saveStation } = useSaveStation();
|
||||
const { mutate: deleteStation } = useDeleteStation();
|
||||
@@ -115,6 +163,36 @@ export const StationsPage: React.FC = () => {
|
||||
deleteStation(placeId);
|
||||
};
|
||||
|
||||
// Handle station selection - wrapped in useCallback to prevent infinite renders
|
||||
const handleSelectStation = useCallback((station: Station) => {
|
||||
setMapCenter({
|
||||
lat: station.latitude,
|
||||
lng: station.longitude
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Show comprehensive loading state until everything is ready
|
||||
if (!isPageReady || !isMapReady) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
padding: 4,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
minHeight: '400px',
|
||||
gap: 2
|
||||
}}
|
||||
>
|
||||
<CircularProgress size={48} />
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
Loading stations...
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// If mobile, stack components vertically
|
||||
if (isMobile) {
|
||||
return (
|
||||
@@ -127,13 +205,23 @@ export const StationsPage: React.FC = () => {
|
||||
<Alert severity="error">{(searchError as any).message || 'Search failed'}</Alert>
|
||||
)}
|
||||
|
||||
<StationMap
|
||||
stations={searchResults}
|
||||
savedPlaceIds={savedPlaceIds}
|
||||
currentLocation={currentLocation}
|
||||
center={mapCenter || undefined}
|
||||
height="300px"
|
||||
/>
|
||||
{isMapReady ? (
|
||||
<GoogleMapsErrorBoundary>
|
||||
<StationMap
|
||||
key="mobile-station-map"
|
||||
stations={searchResults}
|
||||
savedPlaceIds={savedPlaceIds}
|
||||
currentLocation={currentLocation}
|
||||
center={mapCenter || undefined}
|
||||
height="300px"
|
||||
readyToRender={true}
|
||||
/>
|
||||
</GoogleMapsErrorBoundary>
|
||||
) : (
|
||||
<Box sx={{ height: '300px', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Tabs
|
||||
value={tabValue}
|
||||
@@ -160,12 +248,9 @@ export const StationsPage: React.FC = () => {
|
||||
<TabPanel value={tabValue} index={1}>
|
||||
<SavedStationsList
|
||||
stations={savedStations}
|
||||
onSelectStation={(station) => {
|
||||
setMapCenter({
|
||||
lat: station.latitude,
|
||||
lng: station.longitude
|
||||
});
|
||||
}}
|
||||
loading={isSavedLoading}
|
||||
error={savedError ? (savedError as any).message : null}
|
||||
onSelectStation={handleSelectStation}
|
||||
onDeleteStation={handleDelete}
|
||||
/>
|
||||
</TabPanel>
|
||||
@@ -179,13 +264,23 @@ export const StationsPage: React.FC = () => {
|
||||
{/* Left: Map (60%) */}
|
||||
<Grid item xs={12} md={6} sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Paper sx={{ flex: 1, display: 'flex', overflow: 'hidden' }}>
|
||||
<StationMap
|
||||
stations={searchResults}
|
||||
savedPlaceIds={savedPlaceIds}
|
||||
currentLocation={currentLocation}
|
||||
center={mapCenter || undefined}
|
||||
height="100%"
|
||||
/>
|
||||
{isMapReady ? (
|
||||
<GoogleMapsErrorBoundary>
|
||||
<StationMap
|
||||
key="desktop-station-map"
|
||||
stations={searchResults}
|
||||
savedPlaceIds={savedPlaceIds}
|
||||
currentLocation={currentLocation}
|
||||
center={mapCenter || undefined}
|
||||
height="100%"
|
||||
readyToRender={true}
|
||||
/>
|
||||
</GoogleMapsErrorBoundary>
|
||||
) : (
|
||||
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
)}
|
||||
</Paper>
|
||||
</Grid>
|
||||
|
||||
@@ -224,24 +319,16 @@ export const StationsPage: React.FC = () => {
|
||||
error={searchError ? (searchError as any).message : null}
|
||||
onSaveStation={handleSave}
|
||||
onDeleteStation={handleDelete}
|
||||
onSelectStation={(station) => {
|
||||
setMapCenter({
|
||||
lat: station.latitude,
|
||||
lng: station.longitude
|
||||
});
|
||||
}}
|
||||
onSelectStation={handleSelectStation}
|
||||
/>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel value={tabValue} index={1}>
|
||||
<SavedStationsList
|
||||
stations={savedStations}
|
||||
onSelectStation={(station) => {
|
||||
setMapCenter({
|
||||
lat: station.latitude,
|
||||
lng: station.longitude
|
||||
});
|
||||
}}
|
||||
loading={isSavedLoading}
|
||||
error={savedError ? (savedError as any).message : null}
|
||||
onSelectStation={handleSelectStation}
|
||||
onDeleteStation={handleDelete}
|
||||
/>
|
||||
</TabPanel>
|
||||
|
||||
Reference in New Issue
Block a user