gpt-engineer-app[bot] commited on
Commit
64b8b1b
·
1 Parent(s): bf7d92d

Add calendar overview panel

Browse files

Adds a new panel to display an overview of upcoming conferences and their deadlines. This provides users with a convenient way to see all deadlines at a glance.

Files changed (3) hide show
  1. src/App.tsx +3 -1
  2. src/components/Header.tsx +32 -19
  3. src/pages/Calendar.tsx +102 -0
src/App.tsx CHANGED
@@ -1,3 +1,4 @@
 
1
  import { Toaster } from "@/components/ui/toaster";
2
  import { Toaster as Sonner } from "@/components/ui/sonner";
3
  import { TooltipProvider } from "@/components/ui/tooltip";
@@ -5,6 +6,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
5
  import { BrowserRouter, Routes, Route } from "react-router-dom";
6
  import Index from "./pages/Index";
7
  import NotFound from "./pages/NotFound";
 
8
 
9
  const queryClient = new QueryClient();
10
 
@@ -16,7 +18,7 @@ const App = () => (
16
  <BrowserRouter>
17
  <Routes>
18
  <Route path="/" element={<Index />} />
19
- {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
20
  <Route path="*" element={<NotFound />} />
21
  </Routes>
22
  </BrowserRouter>
 
1
+
2
  import { Toaster } from "@/components/ui/toaster";
3
  import { Toaster as Sonner } from "@/components/ui/sonner";
4
  import { TooltipProvider } from "@/components/ui/tooltip";
 
6
  import { BrowserRouter, Routes, Route } from "react-router-dom";
7
  import Index from "./pages/Index";
8
  import NotFound from "./pages/NotFound";
9
+ import Calendar from "./pages/Calendar";
10
 
11
  const queryClient = new QueryClient();
12
 
 
18
  <BrowserRouter>
19
  <Routes>
20
  <Route path="/" element={<Index />} />
21
+ <Route path="/calendar" element={<Calendar />} />
22
  <Route path="*" element={<NotFound />} />
23
  </Routes>
24
  </BrowserRouter>
src/components/Header.tsx CHANGED
@@ -1,4 +1,8 @@
 
1
  import { Search } from "lucide-react";
 
 
 
2
 
3
  interface HeaderProps {
4
  onSearch: (query: string) => void;
@@ -6,26 +10,35 @@ interface HeaderProps {
6
 
7
  const Header = ({ onSearch }: HeaderProps) => {
8
  return (
9
- <header className="w-full py-6 px-4 sm:px-6 lg:px-8 bg-white shadow-sm animate-fade-in">
10
- <div className="max-w-7xl mx-auto">
11
- <div className="flex items-center justify-between">
12
- <div className="flex items-center space-x-3">
13
- <img
14
- src="https://huggingface.co/front/assets/huggingface_logo.svg"
15
- alt="Hugging Face Logo"
16
- className="h-8 w-auto"
17
- />
18
- <div className="h-6 w-px bg-neutral-200 mx-2" />
19
- <h1 className="text-2xl font-bold text-neutral-dark">AI Conference Deadlines</h1>
 
 
 
 
 
20
  </div>
21
- <div className="relative w-full max-w-md ml-4">
22
- <input
23
- type="text"
24
- placeholder="Search conferences..."
25
- onChange={(e) => onSearch(e.target.value)}
26
- className="w-full pl-10 pr-4 py-2 border border-neutral-200 rounded-lg focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary bg-neutral-50"
27
- />
28
- <Search className="absolute left-3 top-2.5 h-5 w-5 text-neutral" />
 
 
 
 
29
  </div>
30
  </div>
31
  </div>
 
1
+
2
  import { Search } from "lucide-react";
3
+ import { Input } from "@/components/ui/input";
4
+ import { Link } from "react-router-dom";
5
+ import { CalendarDays } from "lucide-react";
6
 
7
  interface HeaderProps {
8
  onSearch: (query: string) => void;
 
10
 
11
  const Header = ({ onSearch }: HeaderProps) => {
12
  return (
13
+ <header className="bg-white border-b">
14
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
15
+ <div className="flex items-center justify-between h-16">
16
+ <div className="flex items-center gap-8">
17
+ <Link to="/" className="text-2xl font-bold text-primary">
18
+ AI Deadlines
19
+ </Link>
20
+ <nav className="hidden md:flex space-x-4">
21
+ <Link
22
+ to="/calendar"
23
+ className="text-neutral-600 hover:text-primary flex items-center gap-2"
24
+ >
25
+ <CalendarDays className="h-5 w-5" />
26
+ Calendar
27
+ </Link>
28
+ </nav>
29
  </div>
30
+ <div className="max-w-lg w-full lg:max-w-xs">
31
+ <div className="relative">
32
+ <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
33
+ <Search className="h-5 w-5 text-neutral-400" />
34
+ </div>
35
+ <Input
36
+ type="search"
37
+ placeholder="Search conferences..."
38
+ className="pl-10"
39
+ onChange={(e) => onSearch(e.target.value)}
40
+ />
41
+ </div>
42
  </div>
43
  </div>
44
  </div>
src/pages/Calendar.tsx ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import { useState } from "react";
3
+ import conferencesData from "@/data/conferences.yml";
4
+ import { Conference } from "@/types/conference";
5
+ import { Calendar as CalendarIcon, Tag } from "lucide-react";
6
+ import { Calendar } from "@/components/ui/calendar";
7
+ import { parseISO, format, isValid } from "date-fns";
8
+
9
+ const CalendarPage = () => {
10
+ const [selectedDate, setSelectedDate] = useState<Date | undefined>(new Date());
11
+
12
+ // Get all unique dates (deadlines and conference dates)
13
+ const getDatesWithEvents = () => {
14
+ const dates = new Set<string>();
15
+ conferencesData.forEach((conf: Conference) => {
16
+ if (conf.deadline && conf.deadline !== 'TBD' && isValid(parseISO(conf.deadline))) {
17
+ dates.add(format(parseISO(conf.deadline), 'yyyy-MM-dd'));
18
+ }
19
+ if (conf.start && isValid(parseISO(conf.start))) {
20
+ dates.add(format(parseISO(conf.start), 'yyyy-MM-dd'));
21
+ }
22
+ });
23
+ return Array.from(dates).map(date => parseISO(date));
24
+ };
25
+
26
+ // Get conferences for selected date
27
+ const getConferencesForDate = (date: Date) => {
28
+ const formattedDate = format(date, 'yyyy-MM-dd');
29
+ return conferencesData.filter((conf: Conference) => {
30
+ const deadlineDate = conf.deadline && conf.deadline !== 'TBD' ? format(parseISO(conf.deadline), 'yyyy-MM-dd') : null;
31
+ const startDate = conf.start ? format(parseISO(conf.start), 'yyyy-MM-dd') : null;
32
+ return deadlineDate === formattedDate || startDate === formattedDate;
33
+ });
34
+ };
35
+
36
+ const selectedDateConferences = selectedDate ? getConferencesForDate(selectedDate) : [];
37
+
38
+ return (
39
+ <div className="min-h-screen bg-neutral-light p-6">
40
+ <div className="max-w-7xl mx-auto">
41
+ <h1 className="text-3xl font-bold mb-8">Calendar Overview</h1>
42
+ <div className="grid md:grid-cols-2 gap-8">
43
+ <div>
44
+ <Calendar
45
+ mode="single"
46
+ selected={selectedDate}
47
+ onSelect={setSelectedDate}
48
+ className="bg-white rounded-lg p-4 shadow-sm"
49
+ modifiers={{
50
+ event: getDatesWithEvents()
51
+ }}
52
+ modifiersStyles={{
53
+ event: {
54
+ fontWeight: 'bold',
55
+ color: '#0284C7',
56
+ textDecoration: 'underline'
57
+ }
58
+ }}
59
+ />
60
+ </div>
61
+ <div className="space-y-4">
62
+ {selectedDate && (
63
+ <>
64
+ <h2 className="text-xl font-semibold flex items-center gap-2">
65
+ <CalendarIcon className="h-5 w-5" />
66
+ Events on {format(selectedDate, 'MMMM d, yyyy')}
67
+ </h2>
68
+ {selectedDateConferences.length === 0 ? (
69
+ <p className="text-neutral-600">No events on this date.</p>
70
+ ) : (
71
+ <div className="space-y-4">
72
+ {selectedDateConferences.map((conf: Conference) => (
73
+ <div key={conf.id} className="bg-white p-4 rounded-lg shadow-sm">
74
+ <h3 className="font-semibold text-lg">{conf.title}</h3>
75
+ {conf.deadline && format(parseISO(conf.deadline), 'yyyy-MM-dd') === format(selectedDate, 'yyyy-MM-dd') && (
76
+ <p className="text-red-600">Submission Deadline</p>
77
+ )}
78
+ {conf.start && format(parseISO(conf.start), 'yyyy-MM-dd') === format(selectedDate, 'yyyy-MM-dd') && (
79
+ <p className="text-green-600">Conference Start Date</p>
80
+ )}
81
+ <div className="mt-2 flex flex-wrap gap-2">
82
+ {conf.tags.map((tag) => (
83
+ <span key={tag} className="tag text-sm">
84
+ <Tag className="h-3 w-3 mr-1" />
85
+ {tag}
86
+ </span>
87
+ ))}
88
+ </div>
89
+ </div>
90
+ ))}
91
+ </div>
92
+ )}
93
+ </>
94
+ )}
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ );
100
+ };
101
+
102
+ export default CalendarPage;