'use client'; import { useEffect, useId, useRef, useState } from 'react'; import { MapPin } from 'lucide-react'; import { fetchGeoapifyAddressSuggestions, isDeliverableGeoapifyFeature, shouldUseGeoapifyAutocomplete, } from '@/infrastructure/geocoding/geoapify-autocomplete'; import type { GeoapifyFeature } from '@/infrastructure/geocoding/geoapify-types'; export interface DeliveryAddressAutocompleteLabels { placeholder: string; hint: string; fallbackPlaceholder: string; fallbackHint: string; searching: string; noResults: string; outsideGothenburg: string; selectSuggestion: string; } interface DeliveryAddressAutocompleteProps { value: string; onChange: (value: string) => void; onValidationChange?: (isValid: boolean) => void; language: 'sv' | 'en'; labels: DeliveryAddressAutocompleteLabels; inputClassName: string; } function formatSuggestion(feature: GeoapifyFeature): string { return ( feature.properties.formatted ?? [feature.properties.address_line1, feature.properties.address_line2] .filter(Boolean) .join(', ') ); } export default function DeliveryAddressAutocomplete({ value, onChange, onValidationChange, language, labels, inputClassName, }: DeliveryAddressAutocompleteProps) { const listboxId = useId(); const rootRef = useRef(null); const fetchGenerationRef = useRef(0); const [query, setQuery] = useState(value); const [suggestions, setSuggestions] = useState([]); const [isOpen, setIsOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); const [selectedFromList, setSelectedFromList] = useState(false); const [autocompleteActive, setAutocompleteActive] = useState(null); const deactivateAutocomplete = () => { setAutocompleteActive(false); setSuggestions([]); setIsOpen(false); setIsLoading(false); setSelectedFromList(false); setError(''); onValidationChange?.(true); }; useEffect(() => { const active = shouldUseGeoapifyAutocomplete(); setAutocompleteActive(active); if (!active) onValidationChange?.(true); }, [onValidationChange]); useEffect(() => { setQuery(value); if (autocompleteActive) { setSelectedFromList(!!value.trim()); } setError(''); }, [value, autocompleteActive]); useEffect(() => { if (autocompleteActive !== true) return; if (!query.trim()) { setSuggestions([]); setError(''); setSelectedFromList(false); onValidationChange?.(true); return; } if (selectedFromList) { onValidationChange?.(!error); return; } onValidationChange?.(false); const generation = ++fetchGenerationRef.current; const handle = window.setTimeout(async () => { const trimmedQuery = query.trim(); if (trimmedQuery.length < 3) { if (generation === fetchGenerationRef.current) { setSuggestions([]); setIsLoading(false); } return; } setIsLoading(true); try { const result = await fetchGeoapifyAddressSuggestions(trimmedQuery, language); if (generation !== fetchGenerationRef.current) return; if (result.status === 'unavailable') { deactivateAutocomplete(); return; } setSuggestions(result.features); setIsOpen(result.features.length > 0); } finally { if (generation === fetchGenerationRef.current) { setIsLoading(false); } } }, 600); return () => window.clearTimeout(handle); }, [query, language, selectedFromList, error, onValidationChange, autocompleteActive]); useEffect(() => { if (autocompleteActive !== true) return; const handlePointerDown = (event: MouseEvent) => { if (!rootRef.current?.contains(event.target as Node)) { setIsOpen(false); } }; document.addEventListener('mousedown', handlePointerDown); return () => document.removeEventListener('mousedown', handlePointerDown); }, [autocompleteActive]); const applySuggestion = (feature: GeoapifyFeature) => { if (!isDeliverableGeoapifyFeature(feature)) { setError(labels.outsideGothenburg); setSelectedFromList(false); onValidationChange?.(false); return; } const formatted = formatSuggestion(feature); setQuery(formatted); onChange(formatted); setSelectedFromList(true); setSuggestions([]); setIsOpen(false); setError(''); onValidationChange?.(true); }; const handleInputChange = (next: string) => { setQuery(next); onChange(next); if (autocompleteActive) { setSelectedFromList(false); setError(''); onValidationChange?.(!next.trim()); } else { onValidationChange?.(true); } }; if (autocompleteActive === false) { return (