Skip to content

Commit a7c98c5

Browse files
committed
Add template files for Events tab.
1 parent 4607876 commit a7c98c5

File tree

5 files changed

+377
-0
lines changed

5 files changed

+377
-0
lines changed

docusaurus.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ const config: Config = {
8484
{to: '/blog', label: 'Blog', position: 'left'},
8585
{to: '/research', label: 'Research', position: 'left'},
8686
{to: '/community', label: 'Community', position: 'left'},
87+
{to: '/events', label: 'Events', position: 'left'},
8788
{
8889
type: 'docsVersionDropdown',
8990
position: 'right',

src/components/Events/events.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
export interface Event {
2+
title: string;
3+
date: string;
4+
endDate?: string;
5+
location: string;
6+
description: string;
7+
link?: string;
8+
type: "conference" | "workshop" | "meetup" | "webinar" | "hackathon";
9+
isUpcoming: boolean;
10+
}
11+
12+
// Add your events here
13+
// Events are automatically sorted by date
14+
export const events: Event[] = [
15+
// Example events - replace with actual events
16+
// {
17+
// title: "Lingua Franca Workshop 2025",
18+
// date: "2025-03-15",
19+
// endDate: "2025-03-16",
20+
// location: "UC Berkeley, CA",
21+
// description: "A two-day workshop on reactor-oriented programming with Lingua Franca.",
22+
// link: "https://example.com/workshop",
23+
// type: "workshop",
24+
// isUpcoming: true,
25+
// },
26+
];
27+
28+
// Helper to sort events by date
29+
export const sortEventsByDate = (eventList: Event[], ascending = true): Event[] => {
30+
return [...eventList].sort((a, b) => {
31+
const dateA = new Date(a.date).getTime();
32+
const dateB = new Date(b.date).getTime();
33+
return ascending ? dateA - dateB : dateB - dateA;
34+
});
35+
};
36+
37+
export const upcomingEvents = sortEventsByDate(
38+
events.filter((e) => e.isUpcoming),
39+
true
40+
);
41+
42+
export const pastEvents = sortEventsByDate(
43+
events.filter((e) => !e.isUpcoming),
44+
false
45+
);
46+

src/components/Events/index.tsx

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import clsx from "clsx";
2+
3+
import Translate from "@docusaurus/Translate";
4+
import Layout from "@theme/Layout";
5+
import Heading from "@theme/Heading";
6+
import Link from "@docusaurus/Link";
7+
8+
import { Event, upcomingEvents, pastEvents } from "./events";
9+
import styles from "./styles.module.css";
10+
11+
// Calendar icon
12+
const CalendarIcon = () => (
13+
<svg
14+
width="16"
15+
height="16"
16+
viewBox="0 0 24 24"
17+
fill="none"
18+
stroke="currentColor"
19+
strokeWidth="2"
20+
strokeLinecap="round"
21+
strokeLinejoin="round"
22+
>
23+
<rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
24+
<line x1="16" y1="2" x2="16" y2="6" />
25+
<line x1="8" y1="2" x2="8" y2="6" />
26+
<line x1="3" y1="10" x2="21" y2="10" />
27+
</svg>
28+
);
29+
30+
// Location pin icon
31+
const LocationIcon = () => (
32+
<svg
33+
width="16"
34+
height="16"
35+
viewBox="0 0 24 24"
36+
fill="none"
37+
stroke="currentColor"
38+
strokeWidth="2"
39+
strokeLinecap="round"
40+
strokeLinejoin="round"
41+
>
42+
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" />
43+
<circle cx="12" cy="10" r="3" />
44+
</svg>
45+
);
46+
47+
// Empty calendar icon
48+
const EmptyCalendarIcon = () => (
49+
<svg
50+
width="64"
51+
height="64"
52+
viewBox="0 0 24 24"
53+
fill="none"
54+
stroke="currentColor"
55+
strokeWidth="1.5"
56+
strokeLinecap="round"
57+
strokeLinejoin="round"
58+
>
59+
<rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
60+
<line x1="16" y1="2" x2="16" y2="6" />
61+
<line x1="8" y1="2" x2="8" y2="6" />
62+
<line x1="3" y1="10" x2="21" y2="10" />
63+
</svg>
64+
);
65+
66+
const formatDate = (dateStr: string, endDateStr?: string): string => {
67+
const options: Intl.DateTimeFormatOptions = {
68+
year: "numeric",
69+
month: "long",
70+
day: "numeric",
71+
};
72+
const startDate = new Date(dateStr).toLocaleDateString("en-US", options);
73+
74+
if (endDateStr) {
75+
const endDate = new Date(endDateStr).toLocaleDateString("en-US", options);
76+
return `${startDate} - ${endDate}`;
77+
}
78+
79+
return startDate;
80+
};
81+
82+
const EventCard = ({ event }: { event: Event }) => {
83+
const typeClassName = styles[event.type] || "";
84+
85+
return (
86+
<div className={clsx("card", "margin-bottom--md", styles.eventCard)}>
87+
<div className="card__header">
88+
<div className="row">
89+
<div className="col">
90+
<span className={clsx(styles.eventType, typeClassName)}>
91+
{event.type}
92+
</span>
93+
<Heading as="h3" className="margin-top--sm margin-bottom--none">
94+
{event.link ? (
95+
<Link href={event.link}>{event.title}</Link>
96+
) : (
97+
event.title
98+
)}
99+
</Heading>
100+
</div>
101+
</div>
102+
</div>
103+
<div className="card__body">
104+
<p>{event.description}</p>
105+
<div className={styles.eventMeta}>
106+
<span>
107+
<CalendarIcon /> {formatDate(event.date, event.endDate)}
108+
</span>
109+
<span>
110+
<LocationIcon /> {event.location}
111+
</span>
112+
</div>
113+
</div>
114+
{event.link && (
115+
<div className="card__footer">
116+
<Link className="button button--primary button--sm" href={event.link}>
117+
<Translate>Learn More</Translate>
118+
</Link>
119+
</div>
120+
)}
121+
</div>
122+
);
123+
};
124+
125+
const EmptyState = ({ message }: { message: string }) => (
126+
<div className={styles.emptyState}>
127+
<EmptyCalendarIcon />
128+
<p>{message}</p>
129+
</div>
130+
);
131+
132+
export default function Events(): JSX.Element {
133+
return (
134+
<Layout
135+
title="Events"
136+
description="Lingua Franca events, workshops, and conferences"
137+
>
138+
{/* Hero Section */}
139+
<div className={styles.heroSection}>
140+
<div className="container">
141+
<Heading as="h1" className={styles.heroTitle}>
142+
<Translate>Events</Translate>
143+
</Heading>
144+
<p className={styles.heroSubtitle}>
145+
<Translate>
146+
Join us at conferences, workshops, and meetups to learn more about
147+
Lingua Franca and connect with the community.
148+
</Translate>
149+
</p>
150+
</div>
151+
</div>
152+
153+
{/* Upcoming Events */}
154+
<div className="section">
155+
<div className="container">
156+
<Heading
157+
as="h2"
158+
className={clsx("margin-bottom--lg", "text--center")}
159+
>
160+
<Translate>Upcoming Events</Translate>
161+
</Heading>
162+
{upcomingEvents.length > 0 ? (
163+
<div className="row">
164+
<div className="col col--8 col--offset-2">
165+
{upcomingEvents.map((event, idx) => (
166+
<EventCard key={idx} event={event} />
167+
))}
168+
</div>
169+
</div>
170+
) : (
171+
<EmptyState message="No upcoming events scheduled. Check back soon or follow us on Zulip for announcements!" />
172+
)}
173+
</div>
174+
</div>
175+
176+
{/* Past Events */}
177+
<div className="section sectionAlt">
178+
<div className="container">
179+
<Heading
180+
as="h2"
181+
className={clsx("margin-bottom--lg", "text--center")}
182+
>
183+
<Translate>Past Events</Translate>
184+
</Heading>
185+
{pastEvents.length > 0 ? (
186+
<div className="row">
187+
<div className="col col--8 col--offset-2">
188+
{pastEvents.map((event, idx) => (
189+
<EventCard key={idx} event={event} />
190+
))}
191+
</div>
192+
</div>
193+
) : (
194+
<EmptyState message="No past events to display yet." />
195+
)}
196+
</div>
197+
</div>
198+
</Layout>
199+
);
200+
}
201+
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/**
2+
* Events page styles
3+
*/
4+
5+
.eventCard {
6+
border-left: 4px solid var(--ifm-color-primary);
7+
transition: transform 0.2s ease, box-shadow 0.2s ease;
8+
}
9+
10+
.eventCard:hover {
11+
transform: translateY(-2px);
12+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
13+
}
14+
15+
[data-theme="dark"] .eventCard:hover {
16+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
17+
}
18+
19+
.eventType {
20+
display: inline-block;
21+
padding: 2px 10px;
22+
border-radius: 12px;
23+
font-size: 0.75rem;
24+
font-weight: 600;
25+
text-transform: uppercase;
26+
letter-spacing: 0.5px;
27+
}
28+
29+
.conference {
30+
background-color: #e3f2fd;
31+
color: #1565c0;
32+
}
33+
34+
[data-theme="dark"] .conference {
35+
background-color: #1565c0;
36+
color: #e3f2fd;
37+
}
38+
39+
.workshop {
40+
background-color: #f3e5f5;
41+
color: #7b1fa2;
42+
}
43+
44+
[data-theme="dark"] .workshop {
45+
background-color: #7b1fa2;
46+
color: #f3e5f5;
47+
}
48+
49+
.meetup {
50+
background-color: #e8f5e9;
51+
color: #2e7d32;
52+
}
53+
54+
[data-theme="dark"] .meetup {
55+
background-color: #2e7d32;
56+
color: #e8f5e9;
57+
}
58+
59+
.webinar {
60+
background-color: #fff3e0;
61+
color: #ef6c00;
62+
}
63+
64+
[data-theme="dark"] .webinar {
65+
background-color: #ef6c00;
66+
color: #fff3e0;
67+
}
68+
69+
.hackathon {
70+
background-color: #fce4ec;
71+
color: #c2185b;
72+
}
73+
74+
[data-theme="dark"] .hackathon {
75+
background-color: #c2185b;
76+
color: #fce4ec;
77+
}
78+
79+
.eventMeta {
80+
display: flex;
81+
flex-wrap: wrap;
82+
gap: 16px;
83+
margin-top: 8px;
84+
font-size: 0.9rem;
85+
color: var(--ifm-color-emphasis-700);
86+
}
87+
88+
.eventMeta svg {
89+
margin-right: 4px;
90+
vertical-align: middle;
91+
}
92+
93+
.emptyState {
94+
text-align: center;
95+
padding: 60px 20px;
96+
color: var(--ifm-color-emphasis-600);
97+
}
98+
99+
.emptyState svg {
100+
margin-bottom: 16px;
101+
opacity: 0.5;
102+
}
103+
104+
.heroSection {
105+
background: linear-gradient(135deg, var(--ifm-color-primary-darker) 0%, var(--ifm-color-primary) 100%);
106+
color: white;
107+
padding: 60px 0;
108+
text-align: center;
109+
}
110+
111+
[data-theme="dark"] .heroSection {
112+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
113+
}
114+
115+
.heroTitle {
116+
color: white;
117+
margin-bottom: 16px;
118+
}
119+
120+
.heroSubtitle {
121+
opacity: 0.9;
122+
max-width: 600px;
123+
margin: 0 auto;
124+
font-size: 1.1rem;
125+
}
126+

src/pages/events.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Events from "@site/src/components/Events";
2+
export default Events;
3+

0 commit comments

Comments
 (0)