import React, { useState, useEffect, useCallback, useMemo } from 'react'
import { X, ChevronDown, ChevronUp, RotateCcw, Search } from 'lucide-react'
const ENTITY_TYPES = [
{ value: 'startup', label: 'Startups', icon: '๐', color: '#3B82F6' },
{ value: 'sme', label: 'SMEs', icon: '๐ข', color: '#10B981' },
{ value: 'college_ecell', label: 'College E-Cells', icon: '๐', color: '#FBBF24' },
{ value: 'incubator', label: 'Incubators', icon: '๐งช', color: '#A855F7' },
{ value: 'accelerator', label: 'Accelerators', icon: 'โก', color: '#EC4899' },
]
const TOP_SECTORS = [
{ value: 'fintech', label: 'FinTech', icon: '๐ณ', color: '#3B82F6' },
{ value: 'saas_ai', label: 'SaaS / AI', icon: 'โ๏ธ', color: '#6366F1' },
{ value: 'ecommerce', label: 'E-Commerce', icon: '๐', color: '#F59E0B' },
{ value: 'healthcare', label: 'Healthcare', icon: '๐ฅ', color: '#10B981' },
{ value: 'manufacturing', label: 'Manufacturing', icon: '๐ญ', color: '#78716C' },
]
const DPIIT_CATEGORIES = [
{ value: 'cleantech', label: 'CleanTech', icon: '๐ฟ' },
{ value: 'agritech', label: 'AgriTech', icon: '๐พ' },
{ value: 'edtech', label: 'EdTech', icon: '๐' },
{ value: 'healthtech', label: 'HealthTech', icon: '๐' },
{ value: 'deeptech', label: 'DeepTech', icon: '๐ฌ' },
{ value: 'ai_ml', label: 'AI / ML', icon: '๐ค' },
{ value: 'cybersecurity', label: 'Cybersecurity', icon: '๐' },
{ value: 'foodtech', label: 'FoodTech', icon: '๐' },
{ value: 'logistics', label: 'Logistics', icon: '๐' },
{ value: 'mobility', label: 'Mobility', icon: '๐' },
{ value: 'proptech', label: 'PropTech', icon: '๐ ' },
{ value: 'spacetech', label: 'SpaceTech', icon: '๐' },
{ value: 'biotech', label: 'BioTech', icon: '๐งฌ' },
{ value: 'ev', label: 'EV / E-Mobility', icon: '๐' },
{ value: 'gaming', label: 'Gaming', icon: '๐ฎ' },
{ value: 'mediatech', label: 'MediaTech', icon: '๐บ' },
{ value: 'iot', label: 'IoT', icon: '๐ก' },
{ value: 'drone_tech', label: 'Drone Tech', icon: '๐ธ' },
]
const BUSINESS_MODELS = [
{ value: 'lifestyle', label: 'Lifestyle', icon: '๐', desc: 'Self-funded, profitable' },
{ value: 'scalable', label: 'Scalable', icon: '๐', desc: 'VC-funded, high-growth' },
{ value: 'social', label: 'Social Enterprise', icon: '๐', desc: 'Impact-first' },
{ value: 'large_company', label: 'Large Company', icon: '๐ข', desc: 'Corporate spin-off' },
]
const STAGES = [
{ value: 'ideation', label: 'Ideation', color: '#64748B' },
{ value: 'validation', label: 'Validation', color: '#60A5FA' },
{ value: 'early_traction', label: 'Early Traction', color: '#34D399' },
{ value: 'scaling', label: 'Scaling', color: '#FBBF24' },
{ value: 'mature', label: 'Mature', color: '#F87171' },
]
export default function Sidebar({ filters, facets, onFilterChange, onReset, onClose, isMobile }) {
const [sectors, setSectors] = useState([])
const [states, setStates] = useState([])
const [expandedSections, setExpandedSections] = useState({
type: true,
top_sector: false,
dpiit_cat: false,
biz_model: false,
awards: false,
stage: false,
location: false,
unicorn: false,
})
// In-filter search states for all searchable sections
const [searchTerms, setSearchTerms] = useState({
dpiit: '',
sector: '',
stage: '',
location: '',
biz_model: '',
type: '',
})
useEffect(() => {
fetch('/api/entities/sectors').then(r => r.json()).then(setSectors).catch(console.error)
fetch('/api/entities/locations/states').then(r => r.json()).then(setStates).catch(console.error)
}, [])
const toggleSection = (key) => setExpandedSections(prev => ({ ...prev, [key]: !prev[key] }))
const updateSearch = (key, value) => setSearchTerms(prev => ({ ...prev, [key]: value }))
const toggleArrayFilter = useCallback((key, value) => {
const current = filters[key]
const next = current.includes(value) ? current.filter(v => v !== value) : [...current, value]
onFilterChange(key, next)
}, [filters, onFilterChange])
const activeFilterCount = [
filters.entity_type.length, filters.sector.length, filters.stage.length,
filters.dpiit_category.length, filters.business_model.length, filters.unicorn_status.length,
filters.dpiit_only ? 1 : 0, filters.is_women_led ? 1 : 0,
filters.is_rural_impact ? 1 : 0, filters.is_campus_startup ? 1 : 0,
filters.nsa_winner ? 1 : 0, filters.state ? 1 : 0,
].reduce((a, b) => a + b, 0)
// Filtered lists
const filteredDpiitCats = useMemo(() => {
const q = searchTerms.dpiit.toLowerCase()
return q ? DPIIT_CATEGORIES.filter(c =>
c.label.toLowerCase().includes(q) || c.value.includes(q)
) : DPIIT_CATEGORIES
}, [searchTerms.dpiit])
const filteredTypes = useMemo(() => {
const q = searchTerms.type.toLowerCase()
return q ? ENTITY_TYPES.filter(t =>
t.label.toLowerCase().includes(q) || t.value.includes(q)
) : ENTITY_TYPES
}, [searchTerms.type])
const filteredStages = useMemo(() => {
const q = searchTerms.stage.toLowerCase()
return q ? STAGES.filter(s =>
s.label.toLowerCase().includes(q) || s.value.includes(q)
) : STAGES
}, [searchTerms.stage])
const filteredStates = useMemo(() => {
const q = searchTerms.location.toLowerCase()
return q ? states.filter(s =>
s.state.toLowerCase().includes(q)
) : states
}, [searchTerms.location, states])
const filteredBizModels = useMemo(() => {
const q = searchTerms.biz_model.toLowerCase()
return q ? BUSINESS_MODELS.filter(b =>
b.label.toLowerCase().includes(q) || b.value.includes(q)
) : BUSINESS_MODELS
}, [searchTerms.biz_model])
// Helper: count active filters in a section (shows badge on collapsed sections)
const sectionBadge = (count) => count > 0 ? (
{count}
) : null
return (
{/* Mobile drag handle */}
{isMobile && (
)}
{/* Header */}
Filters
{activeFilterCount > 0 && (
{activeFilterCount}
)}
{activeFilterCount > 0 && (
)}
{/* Quick filters bar โ always visible, no scroll needed */}
{/* Quick toggle pills */}
toggleArrayFilter('unicorn_status', 'unicorn')}
icon="๐ฆ" label="Unicorns"
count={facets?.awards?.unicorns}
/>
onFilterChange('is_women_led', !filters.is_women_led)}
icon="๐ฉ" label="Women-led"
count={facets?.awards?.women_led}
/>
onFilterChange('dpiit_only', !filters.dpiit_only)}
icon="๐๏ธ" label="DPIIT"
/>
onFilterChange('nsa_winner', !filters.nsa_winner)}
icon="๐" label="NSA"
count={facets?.awards?.nsa_winners}
/>
{/* Scrollable filter sections */}
{/* Entity Type */}
toggleSection('type')}
badge={sectionBadge(filters.entity_type.length)}
>
{filteredTypes.map(type => {
const count = facets?.entity_type?.[type.value] || 0
const active = filters.entity_type.includes(type.value)
return (
)
})}
{/* Top Sectors */}
toggleSection('top_sector')}
badge={sectionBadge(filters.sector.length)}
>
updateSearch('sector', v)} placeholder="Search sectors..." />
{TOP_SECTORS.filter(s =>
!searchTerms.sector || s.label.toLowerCase().includes(searchTerms.sector.toLowerCase()) || s.value.includes(searchTerms.sector.toLowerCase())
).map(s => {
const active = filters.sector.includes(s.value)
return (
)
})}
{/* Show DPIIT categories matching search */}
{searchTerms.sector && sectors.filter(s => s.category === 'dpiit_category' &&
(s.label.toLowerCase().includes(searchTerms.sector.toLowerCase()) || s.slug.includes(searchTerms.sector.toLowerCase())) &&
!TOP_SECTORS.find(ts => ts.value === s.slug)
).slice(0, 8).map(s => {
const active = filters.sector.includes(s.slug)
return (
)
})}
{/* DPIIT Categories */}
toggleSection('dpiit_cat')}
badge={sectionBadge(filters.dpiit_category.length)}
>
updateSearch('dpiit', v)} placeholder="Search DPIIT categories..." />
{filteredDpiitCats.map(cat => {
const active = filters.dpiit_category.includes(cat.value)
const count = facets?.dpiit_category?.[cat.value] || 0
return (
)
})}
{searchTerms.dpiit && filteredDpiitCats.length === 0 && (
No categories match "{searchTerms.dpiit}"
)}
{/* Business Models */}
toggleSection('biz_model')}
badge={sectionBadge(filters.business_model.length)}
>
{filteredBizModels.map(bm => {
const active = filters.business_model.includes(bm.value)
const count = facets?.business_model?.[bm.value] || 0
return (
)
})}
{/* Awards & Recognition โ merged unicorn + special */}
toggleSection('awards')}
badge={sectionBadge(
(filters.is_women_led ? 1 : 0) + (filters.is_rural_impact ? 1 : 0) +
(filters.is_campus_startup ? 1 : 0) + (filters.nsa_winner ? 1 : 0) +
filters.unicorn_status.length
)}
>
{/* Unicorn status */}
{[
{ value: 'unicorn', label: 'Unicorns', icon: '๐ฆ', count: facets?.awards?.unicorns || 0 },
{ value: 'soonicorn', label: 'Soonicorns', icon: '๐', count: facets?.awards?.soonicorns || 0 },
].map(u => {
const active = filters.unicorn_status.includes(u.value)
return (
)
})}
onFilterChange('is_women_led', !filters.is_women_led)}
icon="๐ฉ" label="Women-led Startups"
count={facets?.awards?.women_led || 0}
activeColor="bg-pink-500/15 ring-pink-500/30 text-pink-400"
/>
onFilterChange('is_rural_impact', !filters.is_rural_impact)}
icon="๐พ" label="Rural Impact"
count={facets?.awards?.rural_impact || 0}
activeColor="bg-green-500/15 ring-green-500/30 text-green-400"
/>
onFilterChange('is_campus_startup', !filters.is_campus_startup)}
icon="๐" label="Campus Startups"
count={facets?.awards?.campus_startup || 0}
activeColor="bg-blue-500/15 ring-blue-500/30 text-blue-400"
/>
onFilterChange('nsa_winner', !filters.nsa_winner)}
icon="๐" label="NSA 5.0 Winners"
count={facets?.awards?.nsa_winners || 0}
activeColor="bg-amber-500/15 ring-amber-500/30 text-amber-400"
/>
{/* Stage */}
toggleSection('stage')}
badge={sectionBadge(filters.stage.length)}
>
{filteredStages.map(stage => {
const count = facets?.stage?.[stage.value] || 0
const active = filters.stage.includes(stage.value)
return (
)
})}
{/* Location */}
toggleSection('location')}
badge={sectionBadge(filters.state ? 1 : 0)}
>
updateSearch('location', v)} placeholder="Search states..." />
{/* Legend โ compact */}
{ENTITY_TYPES.map(t => (
))}
)
}
/* โโ Subcomponents โโ */
function FilterSection({ title, expanded, onToggle, children, badge }) {
return (
{expanded && (
{children}
)}
)
}
function InFilterSearch({ value, onChange, placeholder }) {
return (
onChange(e.target.value)}
placeholder={placeholder}
className="w-full pl-7 pr-3 py-1.5 rounded-lg bg-atlas-surface border border-atlas-border text-xs text-atlas-text placeholder:text-atlas-muted/40 focus:outline-none focus:ring-1 focus:ring-brand-500"
/>
{value && (
)}
)
}
function QuickPill({ active, onClick, icon, label, count }) {
return (
)
}
function ToggleButton({ active, onClick, icon, label, count, activeColor }) {
return (
)
}