diff --git a/web/src/components/feats/airline/AirlineHero.tsx b/web/src/components/feats/airline/AirlineHero.tsx index ec2d47e..f3d0c37 100644 --- a/web/src/components/feats/airline/AirlineHero.tsx +++ b/web/src/components/feats/airline/AirlineHero.tsx @@ -2,10 +2,20 @@ import { useState, FormEvent } from 'react'; import { useRouter } from 'next/navigation'; -import { Button, Label, Input, DateInput, RadioGroup, Dropdown, DropdownCounter } from '@/components/ui'; +import { Button, Label, DateInput, Dropdown, DropdownCounter, SelectDropdown, SelectOption } from '@/components/ui'; import { dateToDaysFromToday } from '@/lib/airline-utils'; -type TripType = 'roundtrip' | 'oneway' | 'multicity'; +const CITIES: SelectOption[] = [ + { value: 'JFK', label: 'New York (JFK)', sublabel: 'John F. Kennedy International' }, + { value: 'LAX', label: 'Los Angeles (LAX)', sublabel: 'Los Angeles International' }, + { value: 'ORD', label: 'Chicago (ORD)', sublabel: "O'Hare International" }, + { value: 'MIA', label: 'Miami (MIA)', sublabel: 'Miami International' }, + { value: 'SFO', label: 'San Francisco (SFO)', sublabel: 'San Francisco International' }, + { value: 'SEA', label: 'Seattle (SEA)', sublabel: 'Seattle-Tacoma International' }, + { value: 'ATL', label: 'Atlanta (ATL)', sublabel: 'Hartsfield-Jackson International' }, + { value: 'DFW', label: 'Dallas (DFW)', sublabel: 'Dallas/Fort Worth International' }, +]; + const PlaneIcon = () => ( @@ -22,11 +32,9 @@ const LocationIcon = () => ( export default function AirlineHero() { const router = useRouter(); - const [tripType, setTripType] = useState('roundtrip'); const [origin, setOrigin] = useState(''); const [destination, setDestination] = useState(''); const [departDate, setDepartDate] = useState(''); - const [returnDate, setReturnDate] = useState(''); const [passengers, setPassengers] = useState({ adults: 1, children: 0, infants: 0 }); const handleSearch = (e: FormEvent) => { @@ -40,8 +48,6 @@ export default function AirlineHero() { if (origin) params.set('origin', origin); if (destination) params.set('destination', destination); - if (tripType !== 'roundtrip') params.set('tripType', tripType); - if (returnDate && tripType === 'roundtrip') params.set('returnDate', returnDate); params.set('adults', passengers.adults.toString()); params.set('children', passengers.children.toString()); @@ -66,28 +72,15 @@ export default function AirlineHero() {
-
- -
- -
+
- setOrigin(e.target.value)} - placeholder="Airport or city" + onChange={setOrigin} + options={CITIES} + placeholder="Select origin" icon={} required /> @@ -95,12 +88,12 @@ export default function AirlineHero() {
- setDestination(e.target.value)} - placeholder="Airport or city" + onChange={setDestination} + options={CITIES} + placeholder="Select destination" icon={} required /> @@ -115,20 +108,6 @@ export default function AirlineHero() { required />
- -
- - {tripType === 'roundtrip' ? ( - setReturnDate(e.target.value)} - required - /> - ) : ( - - )} -
diff --git a/web/src/components/ui/SelectDropdown.tsx b/web/src/components/ui/SelectDropdown.tsx new file mode 100644 index 0000000..a36a968 --- /dev/null +++ b/web/src/components/ui/SelectDropdown.tsx @@ -0,0 +1,119 @@ +'use client'; + +import { useState, useRef, useEffect, ReactNode } from 'react'; + +export interface SelectOption { + value: string; + label: string; + sublabel?: string; +} + +interface SelectDropdownProps { + value: string; + onChange: (value: string) => void; + options: SelectOption[]; + placeholder?: string; + icon?: ReactNode; + required?: boolean; + id?: string; +} + +export default function SelectDropdown({ + value, + onChange, + options, + placeholder = 'Select...', + icon, + required, + id, +}: SelectDropdownProps) { + const [open, setOpen] = useState(false); + const [filter, setFilter] = useState(''); + const ref = useRef(null); + const inputRef = useRef(null); + + useEffect(() => { + const handleClick = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) { + setOpen(false); + setFilter(''); + } + }; + document.addEventListener('mousedown', handleClick); + return () => document.removeEventListener('mousedown', handleClick); + }, []); + + const selectedOption = options.find((o) => o.value === value); + const filtered = options.filter( + (o) => + o.label.toLowerCase().includes(filter.toLowerCase()) || + o.value.toLowerCase().includes(filter.toLowerCase()) || + o.sublabel?.toLowerCase().includes(filter.toLowerCase()) + ); + + const handleSelect = (opt: SelectOption) => { + onChange(opt.value); + setOpen(false); + setFilter(''); + }; + + return ( +
+
{ + setOpen(true); + setTimeout(() => inputRef.current?.focus(), 0); + }} + > + {icon && {icon}} + {open ? ( + setFilter(e.target.value)} + placeholder={placeholder} + className="flex-1 bg-transparent outline-none text-sm text-[var(--text-primary)]" + /> + ) : ( + + {selectedOption ? selectedOption.label : placeholder} + + )} + + + +
+ {open && ( +
+ {filtered.length === 0 ? ( +
No results
+ ) : ( + filtered.map((opt) => ( +
handleSelect(opt)} + className={`px-4 py-2 cursor-pointer transition-colors hover:bg-[var(--accent-primary-light)] ${ + opt.value === value ? 'bg-[var(--accent-primary-light)]' : '' + }`} + > +
{opt.label}
+ {opt.sublabel &&
{opt.sublabel}
} +
+ )) + )} +
+ )} + {required && !value && ( + {}} /> + )} +
+ ); +} diff --git a/web/src/components/ui/index.ts b/web/src/components/ui/index.ts index d3734cb..691b896 100644 --- a/web/src/components/ui/index.ts +++ b/web/src/components/ui/index.ts @@ -5,3 +5,5 @@ export { default as DateInput } from './DateInput'; export { default as RadioGroup } from './RadioGroup'; export { default as Dropdown, DropdownCounter } from './Dropdown'; export { default as Navigation } from './Navigation'; +export { default as SelectDropdown } from './SelectDropdown'; +export type { SelectOption } from './SelectDropdown'; diff --git a/web/src/styles/airline.css b/web/src/styles/airline.css index 564e366..0cdc5d5 100644 --- a/web/src/styles/airline.css +++ b/web/src/styles/airline.css @@ -278,6 +278,8 @@ padding: 12px; transition: border-color 0.2s ease; width: 100%; + min-height: 48px; + box-sizing: border-box; } [data-mode="airline"] .input-field:focus {