Files
tripwithbonus/frontend/components/AddressSelector.tsx
2025-05-27 15:07:33 +03:00

139 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import React, { useState } from 'react'
import TextInput from './ui/TextInput'
import axios from 'axios'
import { useRouter } from 'next/navigation'
interface AddressSelectorProps {
is_search?: boolean
}
export default function AddressSelector({ is_search }: AddressSelectorProps) {
const router = useRouter()
const [fromAddress, setFromAddress] = useState('')
const [toAddress, setToAddress] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const API_URL = process.env.NEXT_PUBLIC_API_URL
const getCityName = async (searchText: string): Promise<string> => {
try {
const encodedSearch = encodeURIComponent(searchText)
const url = `${API_URL}/cities/?search=${encodedSearch}&russian_name=${encodedSearch}`
const response = await axios.get(url)
if (response.data && response.data.length > 0) {
return response.data[0].value
}
throw new Error(`Город "${searchText}" не найден`)
} catch (error) {
if (error instanceof Error) {
throw new Error(`Ошибка при поиске города "${searchText}": ${error.message}`)
}
throw error
}
}
const formatAddress = (address: string) => {
return address
.toLowerCase()
.trim()
.replace(/[^a-zа-яё0-9\s-]/gi, '')
.replace(/\s+/g, '-')
}
const getSearchUrl = async (category: 'mover' | 'customer') => {
if (!fromAddress.trim() || !toAddress.trim()) {
return `/search/${category}`
}
const [fromCity, toCity] = await Promise.all([getCityName(fromAddress), getCityName(toAddress)])
const from = formatAddress(fromCity)
const to = formatAddress(toCity)
return `/search/${category}/${from}-${to}`
}
const handleSearch = async (category: 'mover' | 'customer') => {
setError(null)
if (!fromAddress.trim() || !toAddress.trim()) {
router.push(`/search/${category}`)
return
}
setIsLoading(true)
try {
const url = await getSearchUrl(category)
router.push(url)
} catch (err) {
setError(err instanceof Error ? err.message : 'Произошла ошибка при поиске')
} finally {
setIsLoading(false)
}
}
return (
<div className="my-2 w-full rounded-xl bg-white p-4 shadow-lg sm:my-4 sm:p-6">
{is_search && <h2 className="mb-4 text-lg font-bold">Может поищем по городу?</h2>}
<div className="flex flex-col gap-4 sm:flex-row sm:items-end sm:gap-3">
<div className="w-full min-w-0 sm:flex-[3] sm:px-1">
<TextInput
placeholder="Минск"
tooltip="Укажите пункт (Город), откуда необходимо забрать посылку."
label="Забрать посылку из"
value={fromAddress}
handleChange={e => {
setError(null)
setFromAddress(e.target.value)
}}
name="fromAddress"
style="main"
error={error && !fromAddress.trim() ? 'Укажите город отправления' : undefined}
/>
</div>
<div className="w-full min-w-0 sm:flex-[3] sm:px-1">
<TextInput
placeholder="Москва"
label="Доставить посылку в"
tooltip="Укажите пункт (Город), куда необходимо доставить посылку."
value={toAddress}
handleChange={e => {
setError(null)
setToAddress(e.target.value)
}}
name="toAddress"
style="main"
error={error && !toAddress.trim() ? 'Укажите город назначения' : undefined}
/>
</div>
<button
onClick={() => handleSearch('mover')}
disabled={isLoading}
className={`w-full cursor-pointer rounded-2xl p-4 text-center whitespace-nowrap text-white sm:w-auto sm:flex-1 ${
isLoading ? 'bg-orange/50 cursor-not-allowed' : 'bg-orange hover:bg-orange/80'
}`}
>
{isLoading ? 'Поиск...' : 'Найти перевозчика'}
</button>
<button
onClick={() => handleSearch('customer')}
disabled={isLoading}
className={`w-full cursor-pointer rounded-2xl p-4 text-center whitespace-nowrap sm:w-auto sm:flex-1 ${
isLoading
? 'cursor-not-allowed bg-gray-100 text-gray-400'
: 'bg-gray-100 text-gray-800 hover:bg-gray-200'
}`}
>
{isLoading ? 'Поиск...' : 'Найти посылку'}
</button>
</div>
{error && <div className="mt-4 text-center text-sm text-red-500">{error}</div>}
</div>
)
}