'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 (
);
}