gpt-engineer-app[bot] commited on
Commit
fce2ccc
·
1 Parent(s): 019c4ee

Restore header on home page

Browse files

The header on the home page was missing after a previous commit. This commit adds it back.

Files changed (1) hide show
  1. src/pages/Calendar.tsx +141 -139
src/pages/Calendar.tsx CHANGED
@@ -1,4 +1,3 @@
1
-
2
  import { useState } from "react";
3
  import conferencesData from "@/data/conferences.yml";
4
  import { Conference } from "@/types/conference";
@@ -6,26 +5,24 @@ import { Calendar as CalendarIcon, Tag } from "lucide-react";
6
  import { Calendar } from "@/components/ui/calendar";
7
  import { parseISO, format, isValid, isSameMonth, isSameYear, isSameDay } from "date-fns";
8
  import { Toggle } from "@/components/ui/toggle";
 
9
 
10
  const CalendarPage = () => {
11
  const [selectedDate, setSelectedDate] = useState<Date | undefined>(new Date());
12
  const [isYearView, setIsYearView] = useState(false);
 
13
 
14
- // Helper function to safely parse dates
15
  const safeParseISO = (dateString: string | undefined | number): Date | null => {
16
  if (!dateString) return null;
17
  if (dateString === 'TBD') return null;
18
 
19
  try {
20
- // If it's a Date object wrapped in a stringified object (from console logs)
21
  if (typeof dateString === 'object') {
22
  return null;
23
  }
24
 
25
- // Convert number to string if needed
26
  const dateStr = typeof dateString === 'number' ? dateString.toString() : dateString;
27
 
28
- // Add leading zeros to single-digit months and days
29
  const normalizedDate = dateStr
30
  .replace(/(\d{4})-(\d{1})-(\d{1,2})/, '$1-0$2-0$3')
31
  .replace(/(\d{4})-(\d{2})-(\d{1})/, '$1-$2-0$1');
@@ -38,38 +35,41 @@ const CalendarPage = () => {
38
  }
39
  };
40
 
41
- // Get events for the current month/year
42
  const getEvents = (date: Date) => {
43
- return conferencesData.filter((conf: Conference) => {
44
- const deadlineDate = safeParseISO(conf.deadline);
45
- const startDate = safeParseISO(conf.start);
46
- const endDate = safeParseISO(conf.end);
47
-
48
- const dateMatches = isYearView ? isSameYear : isSameMonth;
49
-
50
- // Check if deadline is in the selected period
51
- const deadlineInPeriod = deadlineDate && dateMatches(deadlineDate, date);
52
-
53
- // Check if any part of the conference falls in the selected period
54
- let conferenceInPeriod = false;
55
- if (startDate && endDate) {
56
- let currentDate = new Date(startDate);
57
- while (currentDate <= endDate) {
58
- if (dateMatches(currentDate, date)) {
59
- conferenceInPeriod = true;
60
- break;
 
 
 
 
 
 
 
61
  }
62
- currentDate.setDate(currentDate.getDate() + 1);
 
63
  }
64
- } else if (startDate) {
65
- conferenceInPeriod = dateMatches(startDate, date);
66
- }
67
 
68
- return deadlineInPeriod || conferenceInPeriod;
69
- });
70
  };
71
 
72
- // Get all events for day indicators
73
  const getDayEvents = (date: Date) => {
74
  return conferencesData.reduce((acc, conf) => {
75
  const deadlineDate = safeParseISO(conf.deadline);
@@ -94,7 +94,6 @@ const CalendarPage = () => {
94
 
95
  const events = selectedDate ? getEvents(selectedDate) : [];
96
 
97
- // Custom day content renderer
98
  const renderDayContent = (day: Date) => {
99
  const dayEvents = getDayEvents(day);
100
  const hasDeadlines = dayEvents.deadlines.length > 0;
@@ -116,120 +115,123 @@ const CalendarPage = () => {
116
  };
117
 
118
  return (
119
- <div className="min-h-screen bg-neutral-light p-6">
120
- <div className="max-w-7xl mx-auto">
121
- <div className="flex flex-col items-center mb-8">
122
- <h1 className="text-3xl font-bold mb-4">Calendar Overview</h1>
123
- <div className="flex items-center gap-4">
124
- <Toggle
125
- pressed={!isYearView}
126
- onPressedChange={() => setIsYearView(false)}
127
- variant="outline"
128
- >
129
- Month
130
- </Toggle>
131
- <Toggle
132
- pressed={isYearView}
133
- onPressedChange={() => setIsYearView(true)}
134
- variant="outline"
135
- >
136
- Year
137
- </Toggle>
 
 
 
138
  </div>
139
- </div>
140
 
141
- <div className="flex justify-center gap-6 mb-6">
142
- <div className="flex items-center gap-2">
143
- <div className="w-4 h-1 bg-purple-600" />
144
- <span>Conference Dates</span>
145
- </div>
146
- <div className="flex items-center gap-2">
147
- <div className="w-4 h-1 bg-red-500" />
148
- <span>Submission Deadlines</span>
 
149
  </div>
150
- </div>
151
 
152
- <div className="grid grid-cols-1 gap-8">
153
- <div className="mx-auto w-full max-w-4xl">
154
- <Calendar
155
- mode="single"
156
- selected={selectedDate}
157
- onSelect={setSelectedDate}
158
- numberOfMonths={isYearView ? 12 : 1}
159
- className="bg-white rounded-lg p-6 shadow-sm mx-auto w-full"
160
- components={{
161
- Day: ({ date, ...props }) => (
162
- <button {...props} className="w-full h-full p-2">
163
- {renderDayContent(date)}
164
- </button>
165
- ),
166
- }}
167
- classNames={{
168
- months: `grid ${isYearView ? 'grid-cols-3 gap-4' : ''} justify-center`,
169
- month: "space-y-4",
170
- caption: "flex justify-center pt-1 relative items-center mb-4",
171
- caption_label: "text-lg font-semibold",
172
- table: "w-full border-collapse space-y-1",
173
- head_row: "flex",
174
- head_cell: "text-muted-foreground rounded-md w-10 font-normal text-[0.8rem]",
175
- row: "flex w-full mt-2",
176
- cell: `h-10 w-10 text-center text-sm p-0 relative focus-within:relative focus-within:z-20
177
- [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md
178
- last:[&:has([aria-selected])]:rounded-r-md hover:bg-neutral-50`,
179
- day: "h-10 w-10 p-0 font-normal hover:bg-neutral-100 rounded-lg transition-colors",
180
- day_today: "bg-neutral-100 text-primary font-semibold",
181
- nav: "space-x-1 flex items-center",
182
- nav_button: "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100",
183
- nav_button_previous: "absolute left-1",
184
- nav_button_next: "absolute right-1",
185
- }}
186
- />
187
- </div>
188
 
189
- {selectedDate && events.length > 0 && (
190
- <div className="mx-auto w-full max-w-3xl space-y-4">
191
- <h2 className="text-xl font-semibold flex items-center gap-2">
192
- <CalendarIcon className="h-5 w-5" />
193
- Events in {format(selectedDate, isYearView ? 'yyyy' : 'MMMM yyyy')}
194
- </h2>
195
- <div className="space-y-4">
196
- {events.map((conf: Conference) => {
197
- const deadlineDate = safeParseISO(conf.deadline);
198
- const startDate = safeParseISO(conf.start);
199
- const endDate = safeParseISO(conf.end);
200
-
201
- return (
202
- <div key={conf.id} className="bg-white p-4 rounded-lg shadow-sm">
203
- <h3 className="font-semibold text-lg">{conf.title}</h3>
204
- <div className="space-y-1">
205
- {deadlineDate && (
206
- <p className="text-red-500">
207
- Submission Deadline: {format(deadlineDate, 'MMMM d, yyyy')}
208
- </p>
209
- )}
210
- {startDate && (
211
- <p className="text-purple-600">
212
- Conference Date: {format(startDate, 'MMMM d')}
213
- {endDate ? ` - ${format(endDate, 'MMMM d, yyyy')}` :
214
- `, ${format(startDate, 'yyyy')}`}
215
- </p>
216
- )}
217
- </div>
218
- <div className="mt-2 flex flex-wrap gap-2">
219
- {conf.tags.map((tag) => (
220
- <span key={tag} className="inline-flex items-center px-2 py-1 rounded-full
221
- text-xs bg-neutral-100">
222
- <Tag className="h-3 w-3 mr-1" />
223
- {tag}
224
- </span>
225
- ))}
 
226
  </div>
227
- </div>
228
- );
229
- })}
230
  </div>
231
- </div>
232
- )}
233
  </div>
234
  </div>
235
  </div>
 
 
1
  import { useState } from "react";
2
  import conferencesData from "@/data/conferences.yml";
3
  import { Conference } from "@/types/conference";
 
5
  import { Calendar } from "@/components/ui/calendar";
6
  import { parseISO, format, isValid, isSameMonth, isSameYear, isSameDay } from "date-fns";
7
  import { Toggle } from "@/components/ui/toggle";
8
+ import Header from "@/components/Header";
9
 
10
  const CalendarPage = () => {
11
  const [selectedDate, setSelectedDate] = useState<Date | undefined>(new Date());
12
  const [isYearView, setIsYearView] = useState(false);
13
+ const [searchQuery, setSearchQuery] = useState("");
14
 
 
15
  const safeParseISO = (dateString: string | undefined | number): Date | null => {
16
  if (!dateString) return null;
17
  if (dateString === 'TBD') return null;
18
 
19
  try {
 
20
  if (typeof dateString === 'object') {
21
  return null;
22
  }
23
 
 
24
  const dateStr = typeof dateString === 'number' ? dateString.toString() : dateString;
25
 
 
26
  const normalizedDate = dateStr
27
  .replace(/(\d{4})-(\d{1})-(\d{1,2})/, '$1-0$2-0$3')
28
  .replace(/(\d{4})-(\d{2})-(\d{1})/, '$1-$2-0$1');
 
35
  }
36
  };
37
 
 
38
  const getEvents = (date: Date) => {
39
+ return conferencesData
40
+ .filter((conf: Conference) => {
41
+ const matchesSearch = searchQuery === "" ||
42
+ conf.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
43
+ (conf.full_name && conf.full_name.toLowerCase().includes(searchQuery.toLowerCase()));
44
+
45
+ if (!matchesSearch) return false;
46
+
47
+ const deadlineDate = safeParseISO(conf.deadline);
48
+ const startDate = safeParseISO(conf.start);
49
+ const endDate = safeParseISO(conf.end);
50
+
51
+ const dateMatches = isYearView ? isSameYear : isSameMonth;
52
+
53
+ const deadlineInPeriod = deadlineDate && dateMatches(deadlineDate, date);
54
+
55
+ let conferenceInPeriod = false;
56
+ if (startDate && endDate) {
57
+ let currentDate = new Date(startDate);
58
+ while (currentDate <= endDate) {
59
+ if (dateMatches(currentDate, date)) {
60
+ conferenceInPeriod = true;
61
+ break;
62
+ }
63
+ currentDate.setDate(currentDate.getDate() + 1);
64
  }
65
+ } else if (startDate) {
66
+ conferenceInPeriod = dateMatches(startDate, date);
67
  }
 
 
 
68
 
69
+ return deadlineInPeriod || conferenceInPeriod;
70
+ });
71
  };
72
 
 
73
  const getDayEvents = (date: Date) => {
74
  return conferencesData.reduce((acc, conf) => {
75
  const deadlineDate = safeParseISO(conf.deadline);
 
94
 
95
  const events = selectedDate ? getEvents(selectedDate) : [];
96
 
 
97
  const renderDayContent = (day: Date) => {
98
  const dayEvents = getDayEvents(day);
99
  const hasDeadlines = dayEvents.deadlines.length > 0;
 
115
  };
116
 
117
  return (
118
+ <div className="min-h-screen bg-neutral-light">
119
+ <Header onSearch={setSearchQuery} />
120
+ <div className="p-6">
121
+ <div className="max-w-7xl mx-auto">
122
+ <div className="flex flex-col items-center mb-8">
123
+ <h1 className="text-3xl font-bold mb-4">Calendar Overview</h1>
124
+ <div className="flex items-center gap-4">
125
+ <Toggle
126
+ pressed={!isYearView}
127
+ onPressedChange={() => setIsYearView(false)}
128
+ variant="outline"
129
+ >
130
+ Month
131
+ </Toggle>
132
+ <Toggle
133
+ pressed={isYearView}
134
+ onPressedChange={() => setIsYearView(true)}
135
+ variant="outline"
136
+ >
137
+ Year
138
+ </Toggle>
139
+ </div>
140
  </div>
 
141
 
142
+ <div className="flex justify-center gap-6 mb-6">
143
+ <div className="flex items-center gap-2">
144
+ <div className="w-4 h-1 bg-purple-600" />
145
+ <span>Conference Dates</span>
146
+ </div>
147
+ <div className="flex items-center gap-2">
148
+ <div className="w-4 h-1 bg-red-500" />
149
+ <span>Submission Deadlines</span>
150
+ </div>
151
  </div>
 
152
 
153
+ <div className="grid grid-cols-1 gap-8">
154
+ <div className="mx-auto w-full max-w-4xl">
155
+ <Calendar
156
+ mode="single"
157
+ selected={selectedDate}
158
+ onSelect={setSelectedDate}
159
+ numberOfMonths={isYearView ? 12 : 1}
160
+ className="bg-white rounded-lg p-6 shadow-sm mx-auto w-full"
161
+ components={{
162
+ Day: ({ date, ...props }) => (
163
+ <button {...props} className="w-full h-full p-2">
164
+ {renderDayContent(date)}
165
+ </button>
166
+ ),
167
+ }}
168
+ classNames={{
169
+ months: `grid ${isYearView ? 'grid-cols-3 gap-4' : ''} justify-center`,
170
+ month: "space-y-4",
171
+ caption: "flex justify-center pt-1 relative items-center mb-4",
172
+ caption_label: "text-lg font-semibold",
173
+ table: "w-full border-collapse space-y-1",
174
+ head_row: "flex",
175
+ head_cell: "text-muted-foreground rounded-md w-10 font-normal text-[0.8rem]",
176
+ row: "flex w-full mt-2",
177
+ cell: `h-10 w-10 text-center text-sm p-0 relative focus-within:relative focus-within:z-20
178
+ [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md
179
+ last:[&:has([aria-selected])]:rounded-r-md hover:bg-neutral-50`,
180
+ day: "h-10 w-10 p-0 font-normal hover:bg-neutral-100 rounded-lg transition-colors",
181
+ day_today: "bg-neutral-100 text-primary font-semibold",
182
+ nav: "space-x-1 flex items-center",
183
+ nav_button: "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100",
184
+ nav_button_previous: "absolute left-1",
185
+ nav_button_next: "absolute right-1",
186
+ }}
187
+ />
188
+ </div>
189
 
190
+ {selectedDate && events.length > 0 && (
191
+ <div className="mx-auto w-full max-w-3xl space-y-4">
192
+ <h2 className="text-xl font-semibold flex items-center gap-2">
193
+ <CalendarIcon className="h-5 w-5" />
194
+ Events in {format(selectedDate, isYearView ? 'yyyy' : 'MMMM yyyy')}
195
+ </h2>
196
+ <div className="space-y-4">
197
+ {events.map((conf: Conference) => {
198
+ const deadlineDate = safeParseISO(conf.deadline);
199
+ const startDate = safeParseISO(conf.start);
200
+ const endDate = safeParseISO(conf.end);
201
+
202
+ return (
203
+ <div key={conf.id} className="bg-white p-4 rounded-lg shadow-sm">
204
+ <h3 className="font-semibold text-lg">{conf.title}</h3>
205
+ <div className="space-y-1">
206
+ {deadlineDate && (
207
+ <p className="text-red-500">
208
+ Submission Deadline: {format(deadlineDate, 'MMMM d, yyyy')}
209
+ </p>
210
+ )}
211
+ {startDate && (
212
+ <p className="text-purple-600">
213
+ Conference Date: {format(startDate, 'MMMM d')}
214
+ {endDate ? ` - ${format(endDate, 'MMMM d, yyyy')}` :
215
+ `, ${format(startDate, 'yyyy')}`}
216
+ </p>
217
+ )}
218
+ </div>
219
+ <div className="mt-2 flex flex-wrap gap-2">
220
+ {conf.tags.map((tag) => (
221
+ <span key={tag} className="inline-flex items-center px-2 py-1 rounded-full
222
+ text-xs bg-neutral-100">
223
+ <Tag className="h-3 w-3 mr-1" />
224
+ {tag}
225
+ </span>
226
+ ))}
227
+ </div>
228
  </div>
229
+ );
230
+ })}
231
+ </div>
232
  </div>
233
+ )}
234
+ </div>
235
  </div>
236
  </div>
237
  </div>