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; | |