Files
tripwithbonus/frontend/components/ui/LocationSelect.tsx
2025-05-22 13:28:20 +03:00

135 lines
3.8 KiB
TypeScript

'use client'
import React, { useEffect, useState } from 'react'
import Select from 'react-select'
import { SelectOption } from '@/app/types'
import axios from 'axios'
interface LocationOption extends SelectOption {
value: string // добавляем поле value к базовому интерфейсу SelectOption
}
interface LocationSelectProps {
name: string
value: string
handleChange: (e: {
target: { id: string; value: string; selectedOption?: LocationOption }
}) => void
label: string
placeholder: string
countryId?: string
isCity?: boolean
}
const LocationSelect: React.FC<LocationSelectProps> = ({
name,
value,
handleChange,
label,
placeholder,
countryId,
isCity = false,
}) => {
const [options, setOptions] = useState<LocationOption[]>([])
const [isLoading, setIsLoading] = useState(false)
const [search, setSearch] = useState('')
const API_URL = process.env.NEXT_PUBLIC_API_URL
useEffect(() => {
const fetchOptions = async () => {
setIsLoading(true)
try {
const url = isCity
? `${API_URL}/cities/?${countryId ? `country_id=${countryId}&` : ''}search=${search}`
: `${API_URL}/countries/?search=${search}`
const response = await axios.get(url)
setOptions(response.data)
} catch (error) {
console.error('Error fetching options:', error)
} finally {
setIsLoading(false)
}
}
// Debounce search
const timeoutId = setTimeout(() => {
fetchOptions()
}, 300)
return () => clearTimeout(timeoutId)
}, [search, countryId, isCity, API_URL])
return (
<div>
<label htmlFor={name} className="mb-1 block text-sm font-medium text-gray-700">
{label}
</label>
<Select<LocationOption>
inputId={name}
name={name}
options={options}
value={options.find(opt => opt.value === value)}
onChange={selectedOption => {
handleChange({
target: {
id: name,
value: selectedOption?.value || '',
selectedOption: selectedOption || undefined,
},
})
}}
isLoading={isLoading}
onInputChange={newValue => setSearch(newValue)}
isSearchable
isClearable
placeholder={placeholder}
noOptionsMessage={() => (isLoading ? 'Загрузка...' : 'Нет доступных вариантов')}
classNamePrefix="select"
className="rounded-lg"
styles={{
control: base => ({
...base,
borderRadius: '0.75rem',
backgroundColor: '#F3F4F6',
border: '1px solid #E5E7EB',
padding: '2px',
'&:hover': {
borderColor: '#E5E7EB',
},
'&:focus-within': {
backgroundColor: '#FFFFFF',
borderColor: '#E5E7EB',
boxShadow: '0 0 0 2px rgba(59, 130, 246, 0.5)',
},
}),
menu: base => ({
...base,
position: 'absolute',
width: '100%',
zIndex: 9999,
marginTop: '4px',
borderRadius: '0.75rem',
overflow: 'hidden',
}),
option: (base, state) => ({
...base,
fontSize: '0.875rem',
padding: '8px 12px',
backgroundColor: state.isSelected ? '#EFF6FF' : state.isFocused ? '#F3F4F6' : 'white',
color: state.isSelected ? '#2563EB' : '#1F2937',
cursor: 'pointer',
'&:active': {
backgroundColor: '#DBEAFE',
},
'&:hover': {
backgroundColor: state.isSelected ? '#EFF6FF' : '#F3F4F6',
},
}),
}}
/>
</div>
)
}
export default LocationSelect