← back to Handbag Auth Nextjs
src/components/BrandModelDropdowns.tsx
260 lines
'use client'
import { useState, useEffect, useRef } from 'react'
import { getAllBrands, getModelsByBrand, searchModels, HANDBAG_CATALOG } from '@/data/handbag-catalog'
interface BrandModelDropdownsProps {
selectedBrand: string
selectedModel: string
onBrandChange: (brand: string) => void
onModelChange: (model: string) => void
}
export function BrandModelDropdowns({
selectedBrand,
selectedModel,
onBrandChange,
onModelChange,
}: BrandModelDropdownsProps) {
const [brandSearch, setBrandSearch] = useState('')
const [modelSearch, setModelSearch] = useState('')
const [showBrandDropdown, setShowBrandDropdown] = useState(false)
const [showModelDropdown, setShowModelDropdown] = useState(false)
const brandRef = useRef<HTMLDivElement>(null)
const modelRef = useRef<HTMLDivElement>(null)
const brands = getAllBrands()
const models = selectedBrand ? getModelsByBrand(selectedBrand) : []
// Filter brands based on search
const filteredBrands = brandSearch
? brands.filter(b => b.toLowerCase().includes(brandSearch.toLowerCase()))
: brands
// Filter models based on search
const filteredModels = modelSearch
? models.filter(m => m.toLowerCase().includes(modelSearch.toLowerCase()))
: models
// Close dropdowns when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (brandRef.current && !brandRef.current.contains(event.target as Node)) {
setShowBrandDropdown(false)
}
if (modelRef.current && !modelRef.current.contains(event.target as Node)) {
setShowModelDropdown(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [])
// Get brand tier badge
const getBrandTierBadge = (brand: string) => {
const brandData = HANDBAG_CATALOG.find(b => b.brand === brand)
const tier = brandData?.tier || 'contemporary'
const colors = {
'ultra-luxury': 'bg-purple-100 text-purple-700',
'luxury': 'bg-blue-100 text-blue-700',
'contemporary': 'bg-gray-100 text-gray-700',
}
return (
<span className={`ml-2 px-2 py-0.5 rounded text-xs font-medium ${colors[tier]}`}>
{tier.replace('-', ' ')}
</span>
)
}
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Brand Dropdown */}
<div ref={brandRef} className="relative">
<label className="block text-sm font-medium text-gray-700 mb-1">
Brand
</label>
<div className="relative">
<input
type="text"
value={selectedBrand || brandSearch}
onChange={(e) => {
setBrandSearch(e.target.value)
setShowBrandDropdown(true)
if (!e.target.value) onBrandChange('')
}}
onFocus={() => setShowBrandDropdown(true)}
placeholder="Select or search brand..."
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
style={{ fontSize: '16px' }} // Prevent iOS zoom
/>
<svg
className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 pointer-events-none"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</div>
{/* Brand Dropdown Menu */}
{showBrandDropdown && (
<div className="absolute z-50 mt-1 w-full bg-white border border-gray-300 rounded-lg shadow-lg max-h-80 overflow-y-auto">
{filteredBrands.length === 0 ? (
<div className="px-4 py-3 text-sm text-gray-500">No brands found</div>
) : (
<>
{/* Clear option */}
{selectedBrand && (
<button
onClick={() => {
onBrandChange('')
onModelChange('')
setBrandSearch('')
setShowBrandDropdown(false)
}}
className="w-full px-4 py-2 text-left text-sm text-red-600 hover:bg-red-50 border-b"
>
Clear selection
</button>
)}
{filteredBrands.map((brand) => (
<button
key={brand}
onClick={() => {
onBrandChange(brand)
onModelChange('') // Reset model when brand changes
setBrandSearch('')
setShowBrandDropdown(false)
}}
className={`w-full px-4 py-2 text-left text-sm hover:bg-blue-50 flex items-center justify-between ${
selectedBrand === brand ? 'bg-blue-100 font-semibold' : ''
}`}
>
<span>{brand}</span>
{getBrandTierBadge(brand)}
</button>
))}
</>
)}
</div>
)}
</div>
{/* Model Dropdown */}
<div ref={modelRef} className="relative">
<label className="block text-sm font-medium text-gray-700 mb-1">
Model
</label>
<div className="relative">
<input
type="text"
value={selectedModel || modelSearch}
onChange={(e) => {
setModelSearch(e.target.value)
setShowModelDropdown(true)
if (!e.target.value) onModelChange('')
}}
onFocus={() => setShowModelDropdown(true)}
placeholder={selectedBrand ? 'Select or search model...' : 'Select a brand first'}
disabled={!selectedBrand}
className={`w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 ${
!selectedBrand ? 'bg-gray-100 cursor-not-allowed' : ''
}`}
style={{ fontSize: '16px' }} // Prevent iOS zoom
/>
<svg
className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400 pointer-events-none"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</div>
{/* Model Dropdown Menu */}
{showModelDropdown && selectedBrand && (
<div className="absolute z-50 mt-1 w-full bg-white border border-gray-300 rounded-lg shadow-lg max-h-80 overflow-y-auto">
{filteredModels.length === 0 ? (
<div className="px-4 py-3 text-sm text-gray-500">No models found</div>
) : (
<>
{/* Clear option */}
{selectedModel && (
<button
onClick={() => {
onModelChange('')
setModelSearch('')
setShowModelDropdown(false)
}}
className="w-full px-4 py-2 text-left text-sm text-red-600 hover:bg-red-50 border-b"
>
Clear selection
</button>
)}
{/* Model count info */}
<div className="px-4 py-2 text-xs text-gray-500 border-b bg-gray-50">
{filteredModels.length} models available for {selectedBrand}
</div>
{filteredModels.map((model) => (
<button
key={model}
onClick={() => {
onModelChange(model)
setModelSearch('')
setShowModelDropdown(false)
}}
className={`w-full px-4 py-2 text-left text-sm hover:bg-blue-50 ${
selectedModel === model ? 'bg-blue-100 font-semibold' : ''
}`}
>
{model}
</button>
))}
</>
)}
</div>
)}
</div>
{/* Selection Summary */}
{(selectedBrand || selectedModel) && (
<div className="md:col-span-2 mt-2">
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3 flex items-center justify-between">
<div className="flex items-center gap-2 text-sm">
<svg className="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
<span className="text-blue-900">
{selectedBrand && selectedModel
? `Filtering: ${selectedBrand} ${selectedModel}`
: selectedBrand
? `Showing all ${selectedBrand} bags`
: 'Filter active'}
</span>
</div>
<button
onClick={() => {
onBrandChange('')
onModelChange('')
setBrandSearch('')
setModelSearch('')
}}
className="text-blue-600 hover:text-blue-800 text-sm font-medium"
>
Clear filters
</button>
</div>
</div>
)}
</div>
)
}