Skip to content

Commit 0116cb0

Browse files
Add SCEvents Landing Page (#2069)
1 parent 6453c76 commit 0116cb0

2 files changed

Lines changed: 183 additions & 3 deletions

File tree

src/APIFunctions/SCEvents.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { ApiResponse } from './ApiResponses';
2+
3+
const SCEVENTS_API_URL = 'http://localhost:8080';
4+
5+
export async function getAllSCEvents() {
6+
let status = new ApiResponse();
7+
8+
try {
9+
const url = new URL('/events/', SCEVENTS_API_URL);
10+
const res = await fetch(url.href, {
11+
method: 'GET',
12+
headers: {
13+
'Content-Type': 'application/json',
14+
},
15+
});
16+
17+
if (res.ok) {
18+
const result = await res.json();
19+
status.responseData = result;
20+
} else {
21+
status.error = true;
22+
}
23+
} catch (err) {
24+
status.error = true;
25+
status.responseData = err;
26+
}
27+
28+
return status;
29+
}

src/Pages/Events/Events.js

Lines changed: 154 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,159 @@
1-
1+
import React, { useEffect, useState } from 'react';
22
import config from '../../config/config.json';
3-
import NotFoundPage from '../NotFoundPage/NotFoundPage.js';
43
import { Redirect } from 'react-router-dom';
4+
import { getAllSCEvents } from '../../APIFunctions/SCEvents';
5+
6+
function CalendarIcon() {
7+
return (
8+
<svg
9+
className="h-4 w-4 flex-shrink-0"
10+
viewBox="0 0 24 24"
11+
fill="none"
12+
stroke="currentColor"
13+
strokeWidth="1.8"
14+
aria-hidden="true"
15+
>
16+
<path strokeLinecap="round" strokeLinejoin="round" d="M8 2v4M16 2v4M3 10h18" />
17+
<rect x="3" y="4" width="18" height="17" rx="2" />
18+
</svg>
19+
);
20+
}
21+
22+
function PinIcon() {
23+
return (
24+
<svg
25+
className="h-4 w-4 flex-shrink-0"
26+
viewBox="0 0 24 24"
27+
fill="none"
28+
stroke="currentColor"
29+
strokeWidth="1.8"
30+
aria-hidden="true"
31+
>
32+
<path
33+
strokeLinecap="round"
34+
strokeLinejoin="round"
35+
d="M12 21s6-5.686 6-11a6 6 0 1 0-12 0c0 5.314 6 11 6 11Z"
36+
/>
37+
<circle cx="12" cy="10" r="2.5" />
38+
</svg>
39+
);
40+
}
41+
42+
function EventCard({ event }) {
43+
return (
44+
<div className="group rounded-2xl border border-white/10 bg-white/5 p-6 shadow-md backdrop-blur-sm transition duration-300 hover:-translate-y-1 hover:border-white/20 hover:bg-white/[0.07]">
45+
<h2 className="mb-4 text-2xl font-bold text-white">
46+
{event.name || 'Untitled Event'}
47+
</h2>
48+
49+
<div className="mb-5 space-y-2 text-sm text-gray-300">
50+
{(event.date || event.time) && (
51+
<div className="flex items-center gap-2">
52+
<span className="text-blue-300">
53+
<CalendarIcon />
54+
</span>
55+
<span>{[event.date, event.time].filter(Boolean).join(' · ')}</span>
56+
</div>
57+
)}
58+
59+
{event.location && (
60+
<div className="flex items-center gap-2">
61+
<span className="text-blue-300">
62+
<PinIcon />
63+
</span>
64+
<span>{event.location}</span>
65+
</div>
66+
)}
67+
</div>
68+
69+
{event.description && (
70+
<p className="text-base leading-7 text-gray-300">
71+
{event.description}
72+
</p>
73+
)}
74+
</div>
75+
);
76+
}
577

678
export default function EventsPage() {
7-
return config.SCEvents.ENABLED ? <h1>Events Page</h1> : <Redirect to="/notfound" />;
79+
const [events, setEvents] = useState([]);
80+
const [isLoading, setIsLoading] = useState(true);
81+
const [hasError, setHasError] = useState(false);
82+
const isSCEventsEnabled = config.SCEvents?.ENABLED;
83+
84+
useEffect(() => {
85+
if (!isSCEventsEnabled) {
86+
return;
87+
}
88+
89+
async function fetchEvents() {
90+
setIsLoading(true);
91+
setHasError(false);
92+
93+
const response = await getAllSCEvents();
94+
95+
if (!response.error) {
96+
setEvents(Array.isArray(response.responseData) ? response.responseData : []);
97+
} else {
98+
setHasError(true);
99+
}
100+
101+
setIsLoading(false);
102+
}
103+
104+
fetchEvents();
105+
}, [isSCEventsEnabled]);
106+
107+
if (!isSCEventsEnabled) {
108+
return <Redirect to="/notfound" />;
109+
}
110+
111+
return (
112+
<div className="relative min-h-screen overflow-hidden bg-gradient-to-r from-gray-800 to-gray-600 text-white">
113+
<div className="pointer-events-none absolute inset-0">
114+
<div className="absolute -top-24 left-[-8rem] h-[22rem] w-[22rem] rounded-full bg-sky-400/10 blur-3xl" />
115+
<div className="absolute right-[-8rem] top-[10rem] h-[24rem] w-[24rem] rounded-full bg-indigo-500/10 blur-3xl" />
116+
</div>
117+
118+
<div className="relative mx-auto max-w-6xl px-6 py-12">
119+
<h1 className="mb-3 text-4xl font-bold text-white md:text-5xl">
120+
SCEvents
121+
</h1>
122+
123+
<div className="mb-6 h-[2px] w-28 rounded-full bg-gradient-to-r from-sky-400 via-blue-400 to-indigo-400" />
124+
125+
<p className="max-w-2xl text-lg text-gray-300 md:text-xl">
126+
Discover upcoming SCE events and activities.
127+
</p>
128+
</div>
129+
130+
<div className="relative mx-auto max-w-6xl px-6 pb-14">
131+
{isLoading && (
132+
<div className="py-16 text-center text-lg text-gray-300">
133+
Loading events...
134+
</div>
135+
)}
136+
137+
{!isLoading && hasError && (
138+
<div className="rounded-2xl border border-red-400/30 bg-red-500/10 p-6 text-center text-lg text-red-100">
139+
Failed to load events. Please make sure SCEvents is running locally.
140+
</div>
141+
)}
142+
143+
{!isLoading && !hasError && events.length === 0 && (
144+
<div className="rounded-2xl border border-white/10 bg-white/5 p-10 text-center text-lg text-gray-300">
145+
No events available right now.
146+
</div>
147+
)}
148+
149+
{!isLoading && !hasError && events.length > 0 && (
150+
<div className="grid grid-cols-1 gap-8 md:grid-cols-2 xl:grid-cols-3">
151+
{events.map((event) => (
152+
<EventCard key={event.id} event={event} />
153+
))}
154+
</div>
155+
)}
156+
</div>
157+
</div>
158+
);
8159
}

0 commit comments

Comments
 (0)