Spaces:
Running
Running
import { useMemo } from "react"; | |
import conferencesData from "@/data/conferences.yml"; | |
import { X, ChevronRight, Filter } from "lucide-react"; | |
import { getAllCountries } from "@/utils/countryExtractor"; | |
import { | |
Popover, | |
PopoverContent, | |
PopoverTrigger, | |
} from "@/components/ui/popover"; | |
import { Button } from "@/components/ui/button"; | |
import { Checkbox } from "@/components/ui/checkbox"; | |
import { Conference } from "@/types/conference"; | |
interface FilterBarProps { | |
selectedTags: Set<string>; | |
selectedCountries: Set<string>; | |
onTagSelect: (tags: Set<string>) => void; | |
onCountrySelect: (countries: Set<string>) => void; | |
} | |
const FilterBar = ({ | |
selectedTags = new Set(), | |
selectedCountries = new Set(), | |
onTagSelect, | |
onCountrySelect | |
}: FilterBarProps) => { | |
const uniqueTags = useMemo(() => { | |
const tags = new Set<string>(); | |
if (Array.isArray(conferencesData)) { | |
conferencesData.forEach(conf => { | |
if (Array.isArray(conf.tags)) { | |
conf.tags.forEach(tag => tags.add(tag)); | |
} | |
}); | |
} | |
return Array.from(tags).map(tag => ({ | |
id: tag, | |
label: tag.split("-").map(word => | |
word.charAt(0).toUpperCase() + word.slice(1) | |
).join(" "), | |
description: `${tag} Conferences` | |
})); | |
}, []); | |
const isTagSelected = (tagId: string) => { | |
return selectedTags?.has(tagId) ?? false; | |
}; | |
const handleTagChange = (tagId: string) => { | |
const newSelectedTags = new Set(selectedTags); | |
if (newSelectedTags.has(tagId)) { | |
newSelectedTags.delete(tagId); | |
} else { | |
newSelectedTags.add(tagId); | |
} | |
onTagSelect(newSelectedTags); | |
}; | |
const clearAllFilters = () => { | |
onTagSelect(new Set()); | |
onCountrySelect(new Set()); | |
}; | |
return ( | |
<div className="bg-white shadow rounded-lg p-4"> | |
<div className="flex flex-col space-y-4"> | |
<div className="flex flex-wrap items-center gap-2"> | |
<Popover> | |
<PopoverTrigger asChild> | |
<Button variant="outline" size="sm" className="h-8 gap-1"> | |
<Filter className="h-4 w-4" /> | |
Filter by Tag | |
</Button> | |
</PopoverTrigger> | |
<PopoverContent className="w-80 p-4" align="start"> | |
<div className="space-y-4"> | |
<div> | |
<div className="flex items-center justify-between mb-4"> | |
<h4 className="text-sm font-medium text-gray-800">Tags</h4> | |
<ChevronRight className="h-4 w-4 text-gray-500" /> | |
</div> | |
<div className="max-h-60 overflow-y-auto space-y-2"> | |
{uniqueTags.map(tag => ( | |
<div key={tag.id} className="flex items-center space-x-2 hover:bg-gray-50 p-1 rounded"> | |
<Checkbox | |
id={`tag-${tag.id}`} | |
checked={isTagSelected(tag.id)} | |
onCheckedChange={() => handleTagChange(tag.id)} | |
/> | |
<label | |
htmlFor={`tag-${tag.id}`} | |
className="text-sm font-medium text-gray-700 cursor-pointer w-full py-1" | |
> | |
{tag.label} | |
</label> | |
</div> | |
))} | |
</div> | |
</div> | |
</div> | |
</PopoverContent> | |
</Popover> | |
{/* Clear all filters button */} | |
{(selectedTags.size > 0 || selectedCountries.size > 0) && ( | |
<Button | |
variant="ghost" | |
size="sm" | |
onClick={clearAllFilters} | |
className="text-neutral-500 hover:text-neutral-700" | |
> | |
Clear all | |
</Button> | |
)} | |
{/* Display selected tags */} | |
{Array.from(selectedTags).map(tag => ( | |
<button | |
key={tag} | |
className="inline-flex items-center px-3 py-1 rounded-full text-sm bg-blue-100 text-blue-800 hover:bg-blue-200 font-medium" | |
onClick={() => handleTagChange(tag)} | |
> | |
{tag.split("-").map(word => | |
word.charAt(0).toUpperCase() + word.slice(1) | |
).join(" ")} | |
<X className="ml-1 h-3 w-3" /> | |
</button> | |
))} | |
</div> | |
</div> | |
</div> | |
); | |
}; | |
export default FilterBar; | |