diff --git a/src/App.js b/src/App.js
index 3758f48..6f415be 100644
--- a/src/App.js
+++ b/src/App.js
@@ -5,6 +5,7 @@ import './App.css';
import 'react-date-range/dist/styles.css';
import 'react-date-range/dist/theme/default.css';
import { formatDateValue, formatRangeLabel } from './utils/dateUtils';
+import { inferLocationLabel } from './utils/locationLabel';
import useSyncProgress from './hooks/useSyncProgress';
import useNotificationSettings from './hooks/useNotificationSettings';
import useConfigManager from './hooks/useConfigManager';
@@ -634,6 +635,31 @@ function App() {
);
}
+ const userLocationWithLabel = useMemo(() => {
+ if (!preferences?.location) {
+ return null;
+ }
+ const label = inferLocationLabel(preferences.location, stores);
+ return label ? { ...preferences.location, label } : { ...preferences.location };
+ }, [preferences?.location, stores]);
+
+ const sharedNotificationProps = {
+ error: notificationError,
+ message: notificationMessage,
+ settings: notificationSettings,
+ capabilities: notificationCapabilities,
+ loading: notificationLoading,
+ dirty: notificationDirty,
+ saving: notificationSaving,
+ onReset: loadNotificationSettings,
+ onSave: saveNotificationSettings,
+ onFieldChange: handleNotificationFieldChange,
+ onSendTest: sendNotificationTest,
+ onCopyLink: copyToClipboard,
+ copyFeedback,
+ ntfyPreviewUrl
+ };
+
const dashboardContent = (
Standort wird geladen...
- Aktueller Standort: {location.lat.toFixed(4)}, {location.lon.toFixed(4)} (
+ Aktueller Standort: {location.lat.toFixed(4)}, {location.lon.toFixed(4)}
+ {location.label && (
+ – {location.label}
+ )}{' '}
+ (
{location.updatedAt ? new Date(location.updatedAt).toLocaleString('de-DE') : 'Zeit unbekannt'})
+ Standort gespeichert:{' '} + {userLocation.label ? ( + {userLocation.label} + ) : ( + 'Koordinaten' + )}{' '} + • {formatCoordinate(userLocation.lat)}, {formatCoordinate(userLocation.lon)} +
++ Aktualisiert:{' '} + {userLocation.updatedAt ? new Date(userLocation.updatedAt).toLocaleString('de-DE') : 'Zeit unbekannt'} +
+Standort wird automatisch angefragt, um Entfernungen berechnen zu können...
- ) : locationPromptError ? ( + ) : combinedLocationError ? ({locationPromptError}
+{combinedLocationError}
diff --git a/src/utils/locationLabel.js b/src/utils/locationLabel.js new file mode 100644 index 0000000..0b9c9d5 --- /dev/null +++ b/src/utils/locationLabel.js @@ -0,0 +1,65 @@ +import { haversineDistanceKm } from './distance'; + +const DEFAULT_MAX_DISTANCE_KM = 60; + +export function inferLocationLabel(location, stores = [], options = {}) { + if ( + !location || + typeof location !== 'object' || + !Number.isFinite(location.lat) || + !Number.isFinite(location.lon) || + !Array.isArray(stores) || + stores.length === 0 + ) { + return null; + } + + const maxDistanceKm = Number.isFinite(options.maxDistanceKm) + ? options.maxDistanceKm + : DEFAULT_MAX_DISTANCE_KM; + + let closest = null; + for (const store of stores) { + const storeLat = Number(store?.location?.lat); + const storeLon = Number(store?.location?.lon); + if (!Number.isFinite(storeLat) || !Number.isFinite(storeLon)) { + continue; + } + const distance = haversineDistanceKm(location.lat, location.lon, storeLat, storeLon); + if (distance === null) { + continue; + } + if (closest && distance >= closest.distance) { + continue; + } + const labelParts = []; + if (store.city) { + labelParts.push(store.city); + } + if (store.region?.name) { + const cityNormalized = store.city?.toLowerCase(); + const regionNormalized = store.region.name.toLowerCase(); + if (!cityNormalized || cityNormalized !== regionNormalized) { + labelParts.push(store.region.name); + } + } + if (labelParts.length === 0 && store.name) { + labelParts.push(store.name); + } + const label = + labelParts.length > 0 ? labelParts.join(' • ') : `Store ${store.id ?? ''}`.trim(); + closest = { + label, + distance + }; + } + + if (!closest) { + return null; + } + + if (maxDistanceKm <= 0 || closest.distance <= maxDistanceKm) { + return closest.label; + } + return null; +}