Spaces:
Running
Running
File size: 4,664 Bytes
f46336a 38de65f 6decbb1 255baa6 de1321f 38de65f 6decbb1 38de65f 6decbb1 de1321f 38de65f 6decbb1 de1321f 38de65f 6decbb1 de1321f 38de65f 1685b02 38de65f 255baa6 f46336a 0edd5f0 6decbb1 07ed8c2 7033dcf 1685b02 7033dcf 6decbb1 f46336a 38de65f f46336a 255baa6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
import Header from "@/components/Header";
import FilterBar from "@/components/FilterBar";
import ConferenceCard from "@/components/ConferenceCard";
import conferencesData from "@/data/conferences.yml";
import { Conference } from "@/types/conference";
import { useState, useMemo, useEffect } from "react";
import { Switch } from "@/components/ui/switch"
import { parseISO, isValid, isPast } from "date-fns";
const Index = () => {
const [selectedTags, setSelectedTags] = useState<Set<string>>(new Set());
const [searchQuery, setSearchQuery] = useState("");
const [showPastConferences, setShowPastConferences] = useState(false);
const filteredConferences = useMemo(() => {
if (!Array.isArray(conferencesData)) {
console.error("Conferences data is not an array:", conferencesData);
return [];
}
return conferencesData
.filter((conf: Conference) => {
// Filter by deadline (past/future)
const deadlineDate = conf.deadline && conf.deadline !== 'TBD' ? parseISO(conf.deadline) : null;
const isUpcoming = !deadlineDate || !isValid(deadlineDate) || !isPast(deadlineDate);
if (!showPastConferences && !isUpcoming) return false;
// Filter by tags and search query
const matchesTags = selectedTags.size === 0 ||
(Array.isArray(conf.tags) && conf.tags.some(tag => selectedTags.has(tag)));
const matchesSearch = searchQuery === "" ||
conf.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
(conf.full_name && conf.full_name.toLowerCase().includes(searchQuery.toLowerCase()));
return matchesTags && matchesSearch;
})
.sort((a: Conference, b: Conference) => {
const dateA = a.deadline && a.deadline !== 'TBD' ? parseISO(a.deadline).getTime() : Infinity;
const dateB = b.deadline && b.deadline !== 'TBD' ? parseISO(b.deadline).getTime() : Infinity;
return dateA - dateB;
});
}, [selectedTags, searchQuery, showPastConferences]);
// Update handleTagsChange to handle multiple tags
const handleTagsChange = (newTags: Set<string>) => {
setSelectedTags(newTags);
const searchParams = new URLSearchParams(window.location.search);
if (newTags.size > 0) {
searchParams.set('tags', Array.from(newTags).join(','));
} else {
searchParams.delete('tags');
}
const newUrl = `${window.location.pathname}${searchParams.toString() ? `?${searchParams.toString()}` : ''}`;
window.history.pushState({}, '', newUrl);
};
useEffect(() => {
const handleUrlChange = (event: CustomEvent) => {
const { tag } = event.detail;
// Create new Set with existing tags plus the new one
const newTags = new Set(selectedTags);
if (newTags.has(tag)) {
newTags.delete(tag);
} else {
newTags.add(tag);
}
handleTagsChange(newTags);
};
window.addEventListener('urlchange', handleUrlChange as EventListener);
// Check URL params on mount
const params = new URLSearchParams(window.location.search);
const tagsParam = params.get('tags');
if (tagsParam) {
setSelectedTags(new Set(tagsParam.split(',')));
}
return () => {
window.removeEventListener('urlchange', handleUrlChange as EventListener);
};
}, [selectedTags]); // Add selectedTags as dependency
if (!Array.isArray(conferencesData)) {
return <div>Loading conferences...</div>;
}
return (
<div className="min-h-screen bg-neutral-light">
<Header
onSearch={setSearchQuery}
showEmptyMessage={selectedTags.size > 0 && filteredConferences.length === 0 && !showPastConferences}
/>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="space-y-4 py-4">
<FilterBar
selectedTags={selectedTags}
onTagSelect={handleTagsChange}
/>
<div className="flex items-center gap-2">
<label htmlFor="show-past" className="text-sm text-neutral-600">
Show past conferences
</label>
<Switch
id="show-past"
checked={showPastConferences}
onCheckedChange={setShowPastConferences}
/>
</div>
</div>
</div>
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredConferences.map((conference: Conference) => (
<ConferenceCard key={conference.id} {...conference} />
))}
</div>
</main>
</div>
);
};
export default Index;
|