Auto-trigger WALK + BICYCLE distance calculation on POI creation
After creating a POI, automatically trigger WALK and BICYCLE distance calculations (cheap OSRM batch API). TRANSIT is excluded since it uses the expensive OTP backend — users trigger it manually via the calculator button. Failure is non-fatal: the POI is still created and calculation can be retried manually.
This commit is contained in:
parent
8a5d1b3787
commit
2fdafdcb64
1 changed files with 54 additions and 22 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { MapPin, Plus, Trash2, Calculator, Loader2 } from 'lucide-react';
|
import { MapPin, Plus, Trash2, Calculator, Loader2, Crosshair } from 'lucide-react';
|
||||||
import { Button } from './ui/button';
|
import { Button } from './ui/button';
|
||||||
import { Input } from './ui/input';
|
import { Input } from './ui/input';
|
||||||
import type { AuthUser } from '@/auth/types';
|
import type { AuthUser } from '@/auth/types';
|
||||||
|
|
@ -10,9 +10,11 @@ interface POIManagerProps {
|
||||||
user: AuthUser;
|
user: AuthUser;
|
||||||
listingType: 'RENT' | 'BUY';
|
listingType: 'RENT' | 'BUY';
|
||||||
onTaskCreated?: (taskId: string) => void;
|
onTaskCreated?: (taskId: string) => void;
|
||||||
|
pickedLocation?: { lat: number; lng: number } | null;
|
||||||
|
onStartPicking?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function POIManager({ user, listingType, onTaskCreated }: POIManagerProps) {
|
export function POIManager({ user, listingType, onTaskCreated, pickedLocation, onStartPicking }: POIManagerProps) {
|
||||||
const [pois, setPois] = useState<POI[]>([]);
|
const [pois, setPois] = useState<POI[]>([]);
|
||||||
const [isAdding, setIsAdding] = useState(false);
|
const [isAdding, setIsAdding] = useState(false);
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
|
|
@ -26,6 +28,14 @@ export function POIManager({ user, listingType, onTaskCreated }: POIManagerProps
|
||||||
fetchUserPOIs(user).then(setPois).catch(() => {});
|
fetchUserPOIs(user).then(setPois).catch(() => {});
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
|
// When a location is picked from the map, populate lat/lng
|
||||||
|
useEffect(() => {
|
||||||
|
if (pickedLocation) {
|
||||||
|
setLat(String(pickedLocation.lat));
|
||||||
|
setLng(String(pickedLocation.lng));
|
||||||
|
}
|
||||||
|
}, [pickedLocation]);
|
||||||
|
|
||||||
const handleCreate = async () => {
|
const handleCreate = async () => {
|
||||||
if (!name || !lat || !lng) return;
|
if (!name || !lat || !lng) return;
|
||||||
try {
|
try {
|
||||||
|
|
@ -41,6 +51,16 @@ export function POIManager({ user, listingType, onTaskCreated }: POIManagerProps
|
||||||
setAddress('');
|
setAddress('');
|
||||||
setLat('');
|
setLat('');
|
||||||
setLng('');
|
setLng('');
|
||||||
|
// Auto-trigger WALK + BICYCLE distance calculation (cheap OSRM).
|
||||||
|
// TRANSIT (expensive OTP) is excluded — user triggers manually.
|
||||||
|
try {
|
||||||
|
const result = await triggerPOICalculation(
|
||||||
|
user, poi.id, ['WALK', 'BICYCLE'], listingType
|
||||||
|
);
|
||||||
|
onTaskCreated?.(result.task_id);
|
||||||
|
} catch {
|
||||||
|
// Non-fatal: POI created successfully, calculation can be retried manually.
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// silently fail
|
// silently fail
|
||||||
}
|
}
|
||||||
|
|
@ -83,6 +103,7 @@ export function POIManager({ user, listingType, onTaskCreated }: POIManagerProps
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
type="button"
|
||||||
className="h-6 w-6 p-0"
|
className="h-6 w-6 p-0"
|
||||||
onClick={() => handleCalculate(poi.id)}
|
onClick={() => handleCalculate(poi.id)}
|
||||||
disabled={calculating === poi.id}
|
disabled={calculating === poi.id}
|
||||||
|
|
@ -96,6 +117,7 @@ export function POIManager({ user, listingType, onTaskCreated }: POIManagerProps
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
type="button"
|
||||||
className="h-6 w-6 p-0 text-destructive hover:text-destructive"
|
className="h-6 w-6 p-0 text-destructive hover:text-destructive"
|
||||||
onClick={() => handleDelete(poi.id)}
|
onClick={() => handleDelete(poi.id)}
|
||||||
>
|
>
|
||||||
|
|
@ -139,33 +161,42 @@ export function POIManager({ user, listingType, onTaskCreated }: POIManagerProps
|
||||||
onChange={e => setAddress(e.target.value)}
|
onChange={e => setAddress(e.target.value)}
|
||||||
className="h-7 text-xs"
|
className="h-7 text-xs"
|
||||||
/>
|
/>
|
||||||
<div className="grid grid-cols-2 gap-2">
|
{lat && lng ? (
|
||||||
<Input
|
<div className="flex items-center gap-2 p-1.5 bg-muted rounded text-xs text-muted-foreground">
|
||||||
type="number"
|
<MapPin className="h-3 w-3 shrink-0" />
|
||||||
step="any"
|
<span className="truncate">{parseFloat(lat).toFixed(5)}, {parseFloat(lng).toFixed(5)}</span>
|
||||||
placeholder="Latitude"
|
<Button
|
||||||
value={lat}
|
variant="ghost"
|
||||||
onChange={e => setLat(e.target.value)}
|
size="sm"
|
||||||
className="h-7 text-xs"
|
type="button"
|
||||||
/>
|
className="h-5 px-1.5 text-xs ml-auto"
|
||||||
<Input
|
onClick={() => { onStartPicking?.(); }}
|
||||||
type="number"
|
>
|
||||||
step="any"
|
Change
|
||||||
placeholder="Longitude"
|
</Button>
|
||||||
value={lng}
|
</div>
|
||||||
onChange={e => setLng(e.target.value)}
|
) : (
|
||||||
className="h-7 text-xs"
|
<Button
|
||||||
/>
|
variant="outline"
|
||||||
</div>
|
size="sm"
|
||||||
|
type="button"
|
||||||
|
className="w-full h-7 text-xs"
|
||||||
|
onClick={() => onStartPicking?.()}
|
||||||
|
>
|
||||||
|
<Crosshair className="h-3.5 w-3.5 mr-1" />
|
||||||
|
Pick on Map
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button size="sm" className="h-7 text-xs flex-1" onClick={handleCreate}>
|
<Button size="sm" type="button" className="h-7 text-xs flex-1" onClick={handleCreate} disabled={!lat || !lng}>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
type="button"
|
||||||
className="h-7 text-xs"
|
className="h-7 text-xs"
|
||||||
onClick={() => setIsAdding(false)}
|
onClick={() => { setIsAdding(false); setLat(''); setLng(''); }}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -175,6 +206,7 @@ export function POIManager({ user, listingType, onTaskCreated }: POIManagerProps
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
type="button"
|
||||||
className="w-full h-7 text-xs"
|
className="w-full h-7 text-xs"
|
||||||
onClick={() => setIsAdding(true)}
|
onClick={() => setIsAdding(true)}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue