'use client'; import dynamic from 'next/dynamic'; import { useEffect, useRef, useState } from 'react'; import { Loader2, MapPin, Trash2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { useToast } from '@/components/id/toast-provider'; import { ADDRESS_LABELS, type AddressLabel } from '@/lib/address-catalog'; import { forwardGeocode, reverseGeocode } from '@/lib/geocoding'; import { deleteUserAddress, getApiErrorMessage, upsertUserAddress, UserAddress } from '@/lib/api'; import { cn } from '@/lib/utils'; const AddressMapPicker = dynamic( () => import('@/components/addresses/address-map-picker').then((module) => module.AddressMapPicker), { ssr: false, loading: () => (
Загрузка карты...
) } ); function FieldLabel({ children }: { children: React.ReactNode }) { return ; } export function AddressFormDialog({ open, onOpenChange, label, userId, token, existing, onSaved }: { open: boolean; onOpenChange: (open: boolean) => void; label: AddressLabel; userId: string; token?: string | null; existing: UserAddress | null; onSaved: () => void; }) { const { showToast } = useToast(); const [mode, setMode] = useState<'map' | 'manual'>('map'); const [city, setCity] = useState(''); const [street, setStreet] = useState(''); const [house, setHouse] = useState(''); const [apartment, setApartment] = useState(''); const [comment, setComment] = useState(''); const [fullAddress, setFullAddress] = useState(''); const [latitude, setLatitude] = useState(); const [longitude, setLongitude] = useState(); const [isGeocoding, setIsGeocoding] = useState(false); const [isSaving, setIsSaving] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const geocodeTimer = useRef | null>(null); useEffect(() => { if (!open) return; setMode(existing?.latitude != null && existing?.longitude != null ? 'map' : existing ? 'manual' : 'map'); setCity(existing?.city ?? ''); setStreet(existing?.street ?? ''); setHouse(existing?.house ?? ''); setApartment(existing?.apartment ?? ''); setComment(existing?.comment ?? ''); setFullAddress(existing?.fullAddress ?? ''); setLatitude(existing?.latitude); setLongitude(existing?.longitude); }, [existing, open]); useEffect(() => { return () => { if (geocodeTimer.current) clearTimeout(geocodeTimer.current); }; }, []); async function handleMapPick(lat: number, lng: number) { setLatitude(lat); setLongitude(lng); if (geocodeTimer.current) clearTimeout(geocodeTimer.current); geocodeTimer.current = setTimeout(async () => { setIsGeocoding(true); try { const parsed = await reverseGeocode(lat, lng); if (parsed) { setCity(parsed.city); setStreet(parsed.street); setHouse(parsed.house); setFullAddress(parsed.fullAddress); } } finally { setIsGeocoding(false); } }, 450); } async function locateManualAddress() { const query = fullAddress.trim() || [`г. ${city}`, `${street}, ${house}`].filter(Boolean).join(', '); if (!query.trim()) { showToast('Сначала укажите адрес для поиска'); return; } setIsGeocoding(true); try { const result = await forwardGeocode(query); if (!result) { showToast('Адрес не найден на карте'); return; } setLatitude(result.lat); setLongitude(result.lng); setCity(result.parsed.city || city); setStreet(result.parsed.street || street); setHouse(result.parsed.house || house); setFullAddress(result.parsed.fullAddress); setMode('map'); } finally { setIsGeocoding(false); } } async function saveAddress() { setIsSaving(true); try { await upsertUserAddress( userId, { addressId: existing?.id, label, city: city.trim(), street: street.trim(), house: house.trim(), apartment: apartment.trim() || undefined, comment: comment.trim() || undefined, latitude, longitude, fullAddress: fullAddress.trim() || undefined }, token ); showToast(existing ? 'Адрес обновлён' : 'Адрес добавлен'); onSaved(); onOpenChange(false); } catch (error) { const message = getApiErrorMessage(error, 'Не удалось сохранить адрес'); if (message) showToast(message); } finally { setIsSaving(false); } } async function removeAddress() { if (!existing) return; setIsDeleting(true); try { await deleteUserAddress(userId, existing.id, token); showToast('Адрес удалён'); onSaved(); onOpenChange(false); } catch (error) { const message = getApiErrorMessage(error, 'Не удалось удалить адрес'); if (message) showToast(message); } finally { setIsDeleting(false); } } const title = existing ? `Редактировать: ${ADDRESS_LABELS[label].title}` : `Добавить: ${ADDRESS_LABELS[label].title}`; return ( {title} setMode(value as 'map' | 'manual')}> На карте Вручную {isGeocoding ? (
Определяем адрес...
) : fullAddress ? (
{fullAddress}
) : (

Выберите точку на карте — адрес подставится автоматически

)}
Полный адрес setFullAddress(event.target.value)} />
Город setCity(event.target.value)} />
Улица setStreet(event.target.value)} />
Дом setHouse(event.target.value)} />
Квартира setApartment(event.target.value)} />
Комментарий setComment(event.target.value)} />
{existing ? ( ) : null}
); }