This commit is contained in:
2025-11-10 21:03:29 +01:00
parent f0d864c2aa
commit 6e0f8c7e29
2 changed files with 81 additions and 1 deletions

View File

@@ -90,7 +90,13 @@ function persistWatchTableState(state) {
}
}
const StoreWatchPage = ({ authorizedFetch, knownStores = [], userLocation }) => {
const StoreWatchPage = ({
authorizedFetch,
knownStores = [],
userLocation,
onRequestLocation,
locationLoading = false
}) => {
const [regions, setRegions] = useState([]);
const [selectedRegionId, setSelectedRegionId] = useState(() => {
if (typeof window === 'undefined') {
@@ -114,6 +120,9 @@ const StoreWatchPage = ({ authorizedFetch, knownStores = [], userLocation }) =>
const initialTableState = useMemo(() => readWatchTableState(), []);
const [sorting, setSorting] = useState(initialTableState.sorting);
const [columnFilters, setColumnFilters] = useState(initialTableState.columnFilters);
const [locationPromptTriggered, setLocationPromptTriggered] = useState(false);
const [locationPromptPending, setLocationPromptPending] = useState(false);
const [locationPromptError, setLocationPromptError] = useState('');
useEffect(() => {
if (typeof window === 'undefined') {
@@ -130,6 +139,53 @@ const StoreWatchPage = ({ authorizedFetch, knownStores = [], userLocation }) =>
persistWatchTableState({ sorting, columnFilters });
}, [sorting, columnFilters]);
const requestLocation = useCallback(() => {
if (!onRequestLocation) {
return;
}
setLocationPromptTriggered(true);
if (typeof window === 'undefined' || typeof navigator === 'undefined' || !navigator.geolocation) {
setLocationPromptPending(false);
setLocationPromptError('Standortbestimmung wird von diesem Browser nicht unterstützt.');
return;
}
setLocationPromptPending(true);
setLocationPromptError('');
navigator.geolocation.getCurrentPosition(
async (position) => {
setLocationPromptPending(false);
try {
await onRequestLocation({
lat: position.coords.latitude,
lon: position.coords.longitude
});
} catch {
setLocationPromptError('Standort konnte nicht gespeichert werden.');
}
},
(geoError) => {
setLocationPromptPending(false);
setLocationPromptError(
geoError.code === geoError.PERMISSION_DENIED
? 'Zugriff auf den Standort wurde verweigert. Bitte erlaube den Zugriff im Browser.'
: 'Standort konnte nicht automatisch ermittelt werden.'
);
},
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
}
);
}, [onRequestLocation]);
useEffect(() => {
if (userLocation || locationLoading || !onRequestLocation || locationPromptTriggered) {
return;
}
requestLocation();
}, [userLocation, locationLoading, onRequestLocation, locationPromptTriggered, requestLocation]);
const watchedIds = useMemo(
() => new Set(watchList.map((entry) => String(entry.storeId))),
[watchList]
@@ -808,6 +864,28 @@ const StoreWatchPage = ({ authorizedFetch, knownStores = [], userLocation }) =>
</div>
)}
{!userLocation && !locationLoading && onRequestLocation && (
<div className="mb-6 rounded-lg border border-blue-200 bg-blue-50 p-4 text-sm text-blue-900">
{locationPromptPending ? (
<p>Standort wird automatisch angefragt, um Entfernungen berechnen zu können...</p>
) : locationPromptError ? (
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<p className="flex-1">{locationPromptError}</p>
<button
type="button"
onClick={requestLocation}
className="inline-flex items-center justify-center rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white hover:bg-blue-700 disabled:opacity-60"
disabled={locationPromptPending}
>
Erneut versuchen
</button>
</div>
) : (
<p>Bitte bestätige die Standortabfrage deines Browsers für Entfernungssortierung.</p>
)}
</div>
)}
<div className="mb-6 border border-gray-200 rounded-lg p-4 bg-gray-50">
<div className="flex flex-col md:flex-row md:items-end gap-3">
<div className="flex-1">