@@ -4,6 +4,22 @@ import PageSite from "@layouts/PageSite.astro";
44---
55
66<PageSite title =" Calendar" description =" Calendar for Team 694, used for meeting schedules and competitions." >
7+ <div id =" eventModal" class =" modal" >
8+ <div class =" modal-content" >
9+ <div class =" modal-header" >
10+ <h2 id =" modalTitle" ></h2 >
11+ <span class =" close" >× </span >
12+ </div >
13+ <div class =" modal-body" >
14+ <p id =" modalTime" ></p >
15+ <p id =" modalDescription" ></p >
16+ <div id =" modalActions" class =" modal-actions" >
17+ <button id =" joinZoomButton" class =" zoom-btn" style =" display: none;" >Join Zoom Meeting</button >
18+ </div >
19+ </div >
20+ </div >
21+ </div >
22+
723 <script >
824 // import fullcalendar
925 import { Calendar } from "@fullcalendar/core";
@@ -20,9 +36,93 @@ import PageSite from "@layouts/PageSite.astro";
2036 }) : "empty";
2137 }
2238
39+ function dayFormat(date: Date | null) {
40+ return date !== null ? date.toLocaleDateString([], {
41+ month: "short",
42+ day: "numeric"
43+ }) : "empty";
44+ }
45+
46+ function showEventModal(event: any) {
47+ const modal = document.getElementById("eventModal");
48+ const modalTitle = document.getElementById("modalTitle");
49+ const modalTime = document.getElementById("modalTime");
50+ const modalDescription = document.getElementById("modalDescription");
51+ const joinZoomButton = document.getElementById("joinZoomButton");
52+
53+ if (!modal || !modalTitle || !modalTime || !modalDescription || !joinZoomButton) return;
54+
55+ modalTitle.textContent = event.title;
56+
57+ const time = timeFormat(event.start) == timeFormat(event.end) ? `All Day` : `${timeFormat(event.start)} - ${timeFormat(event.end)}`;
58+ const day = dayFormat(event.start) == dayFormat(event.end) ? dayFormat(event.start) : `${dayFormat(event.start)} - ${dayFormat(event.end)}`;
59+ modalTime.textContent = `${day}: ${time}`;
60+
61+ const description = event.extendedProps.description || "";
62+ const location = event.extendedProps.location || "";
63+
64+ const zoomRegex = /(https:\/\/[a-z0-9.-]*zoom\.us\/[^\s]+)/i;
65+ const zoomLink = (description.match(zoomRegex) || location.match(zoomRegex) || [])[0];
66+
67+ if (description) {
68+ const cleanDescription = (text: string) => {
69+ let cleaned = text.replace(/<a[^>]*href="[^"]*zoom\.us[^"]*"[^>]*>.*?<\/a>/gi, '');
70+ cleaned = cleaned.replace(/(https:\/\/[a-z0-9.-]*zoom\.us\/[^\s]+)/gi, '');
71+ cleaned = cleaned.replace(/\s+/g, ' ').trim();
72+ return cleaned;
73+ };
74+
75+ const cleanedDescription = cleanDescription(description);
76+
77+ if (cleanedDescription) {
78+ modalDescription.textContent = cleanedDescription;
79+ modalDescription.style.display = "block";
80+ } else {
81+ modalDescription.style.display = "none";
82+ }
83+ } else {
84+ modalDescription.style.display = "none";
85+ }
86+
87+ if (zoomLink) {
88+ joinZoomButton.style.display = "inline-block";
89+ joinZoomButton.onclick = () => {
90+ window.open(zoomLink, "_blank");
91+ closeModal();
92+ };
93+ } else {
94+ joinZoomButton.style.display = "none";
95+ }
96+
97+ modal.style.display = "block";
98+ }
99+
100+ function closeModal() {
101+ const modal = document.getElementById("eventModal");
102+ if (modal) {
103+ modal.style.display = "none";
104+ }
105+ }
106+
23107 document.addEventListener("DOMContentLoaded", function () {
24108 var calendarEl = document.getElementById("calendar");
25109 if (!calendarEl) throw new Error("Calendar element not found");
110+
111+ const modal = document.getElementById("eventModal");
112+ const closeButton = document.querySelector(".close") as HTMLElement;
113+
114+ if (closeButton) {
115+ closeButton.onclick = closeModal // error: Property 'onclick' does not exist on type 'Element'
116+ }
117+
118+ if (modal) {
119+ window.onclick = function(event) {
120+ if (event.target === modal) {
121+ closeModal();
122+ }
123+ };
124+ }
125+
26126 var calendar = new Calendar(calendarEl, {
27127 googleCalendarApiKey: "AIzaSyC8aX6KJLfEyiuSJ76hErHB8voNcoAi4yA",
28128 initialView: window.innerWidth < 768 ? "listWeek" : "dayGridMonth",
@@ -59,16 +159,143 @@ import PageSite from "@layouts/PageSite.astro";
59159 }
60160 ],
61161 eventClick: function ({event, jsEvent}) {
62- alert(`${event.title} (${timeFormat(event.start)} - ${timeFormat(event.end)})`);
162+ // alert(`${event.title} (${timeFormat(event.start)} - ${timeFormat(event.end)})`);
63163
64164 // prevents current tab from navigating
65165 jsEvent.preventDefault();
166+
167+ showEventModal(event);
66168 },
67169 });
68170 calendar.render();
69171 });
70172 </script >
173+ <style >
174+ .modal {
175+ display: none;
176+ position: fixed;
177+ z-index: 1000;
178+ left: 0;
179+ top: 0;
180+ width: 100%;
181+ height: 100%;
182+ overflow: auto;
183+ background-color: rgba(0, 0, 0, 0.5);
184+ animation: fadeIn 0.3s ease-in-out;
185+ }
186+
187+ @keyframes fadeIn {
188+ from { opacity: 0; }
189+ to { opacity: 1; }
190+ }
191+
192+ .modal-content {
193+ background-color: #fefefe;
194+ margin: 10% auto;
195+ padding: 0;
196+ border-radius: 8px;
197+ width: 90%;
198+ max-width: 500px;
199+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
200+ animation: slideIn 0.3s ease-out;
201+ }
202+
203+ @keyframes slideIn {
204+ from {
205+ transform: translateY(-50px);
206+ opacity: 0;
207+ }
208+ to {
209+ transform: translateY(0);
210+ opacity: 1;
211+ }
212+ }
213+
214+ .modal-header {
215+ padding: 20px 20px 10px 20px;
216+ border-bottom: 1px solid #e0e0e0;
217+ display: flex;
218+ justify-content: space-between;
219+ align-items: center;
220+ }
221+
222+ .modal-header h2 {
223+ margin: 0;
224+ color: #333;
225+ font-size: 1.5em;
226+ font-weight: 600;
227+ }
228+
229+ .close {
230+ color: #aaa;
231+ font-size: 28px;
232+ font-weight: bold;
233+ cursor: pointer;
234+ line-height: 1;
235+ transition: color 0.2s ease;
236+ }
237+
238+ .close:hover,
239+ .close:focus {
240+ color: #555;
241+ }
242+
243+ .modal-body {
244+ padding: 20px;
245+ }
246+
247+ .modal-body p {
248+ margin: 0 0 15px 0;
249+ color: #666;
250+ line-height: 1.5;
251+ }
252+
253+ .modal-actions {
254+ margin-top: 20px;
255+ text-align: center;
256+ }
257+
258+ .zoom-btn {
259+ background: linear-gradient(135deg, #2D8CFF, #1E6FDD);
260+ color: white;
261+ border: none;
262+ padding: 12px 24px;
263+ border-radius: 6px;
264+ cursor: pointer;
265+ font-size: 16px;
266+ font-weight: 500;
267+ transition: all 0.2s ease;
268+ box-shadow: 0 2px 8px rgba(45, 140, 255, 0.3);
269+ }
270+
271+ .zoom-btn:hover {
272+ background: linear-gradient(135deg, #1E6FDD, #155ABB);
273+ transform: translateY(-1px);
274+ box-shadow: 0 4px 12px rgba(45, 140, 255, 0.4);
275+ }
276+
277+ .zoom-btn:active {
278+ transform: translateY(0);
279+ }
280+
281+ /* Mobile responsiveness */
282+ @media (max-width: 768px) {
283+ .modal-content {
284+ width: 95%;
285+ margin: 5% auto;
286+ }
287+
288+ .modal-header h2 {
289+ font-size: 1.3em;
290+ }
291+
292+ .modal-body {
293+ padding: 15px;
294+ }
295+ }
296+ </style >
297+
71298 <div class =" container" >
72299 <div id =" calendar" ></div >
73300 </div >
74- </PageSite >
301+ </PageSite >
0 commit comments