feat: Equipment-System, Buchungsbearbeitung, Kundenadresse, LexOffice-Fix
- Vintage Modell hinzugefuegt - Equipment Multi-Select (Neue Buchung + Bearbeitung) - Kundenadresse in Formularen - Bearbeiten-Seite fuer Buchungen - Abbau-Zeiten in Formularen und Uebersicht - Vertrag PDF nur bei Privatkunden - LexOffice Kontakt-Erstellung Fix (BUSINESS) - Zurueck-Pfeil auf Touren-Seite
This commit is contained in:
88
lib/distance-calculator.ts
Normal file
88
lib/distance-calculator.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
interface Coordinates {
|
||||
lat: number;
|
||||
lon: number;
|
||||
}
|
||||
|
||||
interface DistanceResult {
|
||||
distance: number;
|
||||
duration: number;
|
||||
}
|
||||
|
||||
export class DistanceCalculator {
|
||||
private static readonly OSRM_API = 'https://router.project-osrm.org/route/v1/driving';
|
||||
|
||||
static async geocodeAddress(address: string): Promise<Coordinates | null> {
|
||||
try {
|
||||
const nominatimUrl = `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(address)}&limit=1`;
|
||||
|
||||
const response = await fetch(nominatimUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'SaveTheMoment-Atlas/1.0',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Geocoding failed: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
console.warn(`No results found for address: ${address}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
lat: parseFloat(data[0].lat),
|
||||
lon: parseFloat(data[0].lon),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Geocoding error:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async calculateDistance(
|
||||
fromAddress: string,
|
||||
toAddress: string
|
||||
): Promise<DistanceResult | null> {
|
||||
try {
|
||||
const fromCoords = await this.geocodeAddress(fromAddress);
|
||||
const toCoords = await this.geocodeAddress(toAddress);
|
||||
|
||||
if (!fromCoords || !toCoords) {
|
||||
console.error('Failed to geocode one or both addresses');
|
||||
return null;
|
||||
}
|
||||
|
||||
const url = `${this.OSRM_API}/${fromCoords.lon},${fromCoords.lat};${toCoords.lon},${toCoords.lat}?overview=false`;
|
||||
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`OSRM API failed: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.routes || data.routes.length === 0) {
|
||||
console.error('No route found');
|
||||
return null;
|
||||
}
|
||||
|
||||
const route = data.routes[0];
|
||||
|
||||
return {
|
||||
distance: Math.round(route.distance / 1000 * 100) / 100,
|
||||
duration: Math.round(route.duration / 60),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Distance calculation error:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static formatAddress(address: string, zip: string, city: string): string {
|
||||
return `${address}, ${zip} ${city}`.trim();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user