mirror of
https://github.com/CompassConnections/Compass.git
synced 2025-12-23 22:18:43 -05:00
125 lines
3.2 KiB
TypeScript
125 lines
3.2 KiB
TypeScript
import clsx from 'clsx'
|
|
import {useEffect, useRef, useState} from 'react'
|
|
import {api} from 'web/lib/api'
|
|
import {countryCodeToFlag} from 'web/lib/util/location'
|
|
import {ProfileRow} from 'common/profiles/profile'
|
|
import {OriginLocation} from "common/filters";
|
|
|
|
function isDigitString(value: string): boolean {
|
|
return /^\d+$/.test(value);
|
|
}
|
|
|
|
export type City = {
|
|
geodb_city_id: string
|
|
city: string | null
|
|
region_code: string
|
|
country: string
|
|
country_code: string
|
|
latitude: number
|
|
longitude: number
|
|
}
|
|
|
|
export function profileToCity(profile: ProfileRow): City {
|
|
return {
|
|
geodb_city_id: profile.geodb_city_id!,
|
|
city: profile.city,
|
|
region_code: profile.region_code!,
|
|
country: profile.country!,
|
|
country_code: '',
|
|
latitude: profile.city_latitude!,
|
|
longitude: profile.city_longitude!,
|
|
}
|
|
}
|
|
|
|
export function originToCity(origin: OriginLocation): City {
|
|
return {
|
|
geodb_city_id: origin.id,
|
|
city: origin.name,
|
|
region_code: '',
|
|
country: '',
|
|
country_code: '',
|
|
latitude: 0,
|
|
longitude: 0,
|
|
}
|
|
}
|
|
|
|
export function CityRow(props: {
|
|
city: City
|
|
onSelect: (city: City) => void
|
|
className?: string
|
|
}) {
|
|
const {city, onSelect, className} = props
|
|
return (
|
|
<button
|
|
key={city.geodb_city_id}
|
|
onClick={() => onSelect(city)}
|
|
className={clsx(
|
|
'group flex cursor-pointer flex-row flex-wrap justify-between gap-x-4',
|
|
className
|
|
)}
|
|
>
|
|
<div className="group-hover:text-ink-950 font-semibold transition-colors">
|
|
{city.city}
|
|
{city.region_code && !isDigitString(city.region_code) ? `, ${city.region_code}` : ''}
|
|
</div>
|
|
<div className="text-ink-400 group-hover:text-ink-700 transition-colors">
|
|
{countryCodeToFlag(city.country_code) || city.country}
|
|
</div>
|
|
</button>
|
|
)
|
|
}
|
|
|
|
export const useCitySearch = () => {
|
|
const [query, setQuery] = useState('')
|
|
const [loading, setLoading] = useState(false)
|
|
const [cities, setCities] = useState<City[]>([])
|
|
|
|
const searchCountRef = useRef(0)
|
|
|
|
useEffect(() => {
|
|
const fetchData = async () => {
|
|
try {
|
|
setLoading(true)
|
|
searchCountRef.current++
|
|
const thisSearchCount = searchCountRef.current
|
|
const response = await api('search-location', {term: query, limit: 8})
|
|
if (response.status !== 'success') {
|
|
throw new Error(response.data)
|
|
}
|
|
if (thisSearchCount == searchCountRef.current) {
|
|
setCities(
|
|
response.data.data.map((city: any) => ({
|
|
geodb_city_id: city.id.toString(),
|
|
city: city.name,
|
|
region_code: city.regionCode,
|
|
country: city.country,
|
|
country_code: city.countryCode,
|
|
latitude: city.latitude,
|
|
longitude: city.longitude,
|
|
}))
|
|
)
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching cities', error)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const debounce = setTimeout(() => {
|
|
if (query.length < 2) {
|
|
setCities([])
|
|
return
|
|
} else {
|
|
fetchData()
|
|
}
|
|
}, 1000)
|
|
|
|
return () => {
|
|
clearTimeout(debounce)
|
|
}
|
|
}, [query])
|
|
|
|
return {query, setQuery, loading, cities}
|
|
}
|