nielsr HF staff commited on
Commit
de1321f
·
1 Parent(s): 1f74e1a

Add ability to select multiple categories

Browse files
Files changed (2) hide show
  1. src/components/FilterBar.tsx +17 -10
  2. src/pages/Index.tsx +7 -6
src/components/FilterBar.tsx CHANGED
@@ -1,14 +1,13 @@
1
-
2
  import { useMemo } from "react";
3
  import conferencesData from "@/data/conferences.yml";
4
  import { X } from "lucide-react";
5
 
6
  interface FilterBarProps {
7
- selectedTag: string;
8
- onTagSelect: (tag: string) => void;
9
  }
10
 
11
- const FilterBar = ({ selectedTag, onTagSelect }: FilterBarProps) => {
12
  const uniqueTags = useMemo(() => {
13
  const tags = new Set<string>();
14
  if (Array.isArray(conferencesData)) {
@@ -18,12 +17,12 @@ const FilterBar = ({ selectedTag, onTagSelect }: FilterBarProps) => {
18
  }
19
  });
20
  }
21
- return ["All", ...Array.from(tags)].map(tag => ({
22
  id: tag,
23
  label: tag.split("-").map(word =>
24
  word.charAt(0).toUpperCase() + word.slice(1)
25
  ).join(" "),
26
- description: tag === "All" ? "All Conferences" : `${tag} Conferences`
27
  }));
28
  }, []);
29
 
@@ -35,11 +34,19 @@ const FilterBar = ({ selectedTag, onTagSelect }: FilterBarProps) => {
35
  <button
36
  key={filter.id}
37
  title={filter.description}
38
- onClick={() => onTagSelect(filter.id)}
 
 
 
 
 
 
 
 
39
  className={`
40
  px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200
41
  filter-tag
42
- ${selectedTag === filter.id
43
  ? "bg-primary text-white shadow-sm filter-tag-active"
44
  : "bg-neutral-50 text-neutral-600 hover:bg-neutral-100 hover:text-neutral-900"
45
  }
@@ -49,9 +56,9 @@ const FilterBar = ({ selectedTag, onTagSelect }: FilterBarProps) => {
49
  </button>
50
  ))}
51
 
52
- {selectedTag !== "All" && (
53
  <button
54
- onClick={() => onTagSelect("All")}
55
  className="px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200
56
  bg-red-50 text-red-600 hover:bg-red-100 hover:text-red-700
57
  flex items-center gap-2"
 
 
1
  import { useMemo } from "react";
2
  import conferencesData from "@/data/conferences.yml";
3
  import { X } from "lucide-react";
4
 
5
  interface FilterBarProps {
6
+ selectedTags: Set<string>;
7
+ onTagSelect: (tags: Set<string>) => void;
8
  }
9
 
10
+ const FilterBar = ({ selectedTags, onTagSelect }: FilterBarProps) => {
11
  const uniqueTags = useMemo(() => {
12
  const tags = new Set<string>();
13
  if (Array.isArray(conferencesData)) {
 
17
  }
18
  });
19
  }
20
+ return Array.from(tags).map(tag => ({
21
  id: tag,
22
  label: tag.split("-").map(word =>
23
  word.charAt(0).toUpperCase() + word.slice(1)
24
  ).join(" "),
25
+ description: `${tag} Conferences`
26
  }));
27
  }, []);
28
 
 
34
  <button
35
  key={filter.id}
36
  title={filter.description}
37
+ onClick={() => {
38
+ const newTags = new Set(selectedTags);
39
+ if (newTags.has(filter.id)) {
40
+ newTags.delete(filter.id);
41
+ } else {
42
+ newTags.add(filter.id);
43
+ }
44
+ onTagSelect(newTags);
45
+ }}
46
  className={`
47
  px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200
48
  filter-tag
49
+ ${selectedTags.has(filter.id)
50
  ? "bg-primary text-white shadow-sm filter-tag-active"
51
  : "bg-neutral-50 text-neutral-600 hover:bg-neutral-100 hover:text-neutral-900"
52
  }
 
56
  </button>
57
  ))}
58
 
59
+ {selectedTags.size > 0 && (
60
  <button
61
+ onClick={() => onTagSelect(new Set())}
62
  className="px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200
63
  bg-red-50 text-red-600 hover:bg-red-100 hover:text-red-700
64
  flex items-center gap-2"
src/pages/Index.tsx CHANGED
@@ -8,7 +8,7 @@ import { Switch } from "@/components/ui/switch"
8
  import { parseISO, isValid, isPast } from "date-fns";
9
 
10
  const Index = () => {
11
- const [selectedTag, setSelectedTag] = useState("All");
12
  const [searchQuery, setSearchQuery] = useState("");
13
  const [showPastConferences, setShowPastConferences] = useState(false);
14
 
@@ -25,20 +25,21 @@ const Index = () => {
25
  const isUpcoming = !deadlineDate || !isValid(deadlineDate) || !isPast(deadlineDate);
26
  if (!showPastConferences && !isUpcoming) return false;
27
 
28
- // Filter by tag and search query
29
- const matchesTag = selectedTag === "All" || (Array.isArray(conf.tags) && conf.tags.includes(selectedTag));
 
30
  const matchesSearch = searchQuery === "" ||
31
  conf.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
32
  (conf.full_name && conf.full_name.toLowerCase().includes(searchQuery.toLowerCase()));
33
 
34
- return matchesTag && matchesSearch;
35
  })
36
  .sort((a: Conference, b: Conference) => {
37
  const dateA = a.deadline && a.deadline !== 'TBD' ? parseISO(a.deadline).getTime() : Infinity;
38
  const dateB = b.deadline && b.deadline !== 'TBD' ? parseISO(b.deadline).getTime() : Infinity;
39
  return dateA - dateB;
40
  });
41
- }, [selectedTag, searchQuery, showPastConferences]);
42
 
43
  if (!Array.isArray(conferencesData)) {
44
  return <div>Loading conferences...</div>;
@@ -49,7 +50,7 @@ const Index = () => {
49
  <Header onSearch={setSearchQuery} />
50
  <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
51
  <div className="space-y-4 py-4">
52
- <FilterBar selectedTag={selectedTag} onTagSelect={setSelectedTag} />
53
  <div className="flex items-center gap-2">
54
  <label htmlFor="show-past" className="text-sm text-neutral-600">
55
  Show past conferences
 
8
  import { parseISO, isValid, isPast } from "date-fns";
9
 
10
  const Index = () => {
11
+ const [selectedTags, setSelectedTags] = useState<Set<string>>(new Set());
12
  const [searchQuery, setSearchQuery] = useState("");
13
  const [showPastConferences, setShowPastConferences] = useState(false);
14
 
 
25
  const isUpcoming = !deadlineDate || !isValid(deadlineDate) || !isPast(deadlineDate);
26
  if (!showPastConferences && !isUpcoming) return false;
27
 
28
+ // Filter by tags and search query
29
+ const matchesTags = selectedTags.size === 0 ||
30
+ (Array.isArray(conf.tags) && conf.tags.some(tag => selectedTags.has(tag)));
31
  const matchesSearch = searchQuery === "" ||
32
  conf.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
33
  (conf.full_name && conf.full_name.toLowerCase().includes(searchQuery.toLowerCase()));
34
 
35
+ return matchesTags && matchesSearch;
36
  })
37
  .sort((a: Conference, b: Conference) => {
38
  const dateA = a.deadline && a.deadline !== 'TBD' ? parseISO(a.deadline).getTime() : Infinity;
39
  const dateB = b.deadline && b.deadline !== 'TBD' ? parseISO(b.deadline).getTime() : Infinity;
40
  return dateA - dateB;
41
  });
42
+ }, [selectedTags, searchQuery, showPastConferences]);
43
 
44
  if (!Array.isArray(conferencesData)) {
45
  return <div>Loading conferences...</div>;
 
50
  <Header onSearch={setSearchQuery} />
51
  <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
52
  <div className="space-y-4 py-4">
53
+ <FilterBar selectedTags={selectedTags} onTagSelect={setSelectedTags} />
54
  <div className="flex items-center gap-2">
55
  <label htmlFor="show-past" className="text-sm text-neutral-600">
56
  Show past conferences