import React, { useState, useEffect } from "react"; const Calendar = ({ CALENDAR_API_KEY, EVENT_CALENDAR_ID }) => { const [currentDate, setCurrentDate] = useState(new Date()); const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [hoveredEvent, setHoveredEvent] = useState(null); const [tooltipPosition, setTooltipPosition] = useState({ x: 0, y: 0 }); const getDaysInMonth = (date) => { const year = date.getFullYear(); const month = date.getMonth(); const firstDay = new Date(year, month, 1); const lastDay = new Date(year, month + 1, 0); const daysInMonth = lastDay.getDate(); const startingDay = firstDay.getDay(); const endingDay = lastDay.getDay(); const days = []; // Add empty slots for days before the first of the month for (let i = 0; i < startingDay; i++) { days.push(null); } // Add all days of the month for (let i = 1; i <= daysInMonth; i++) { days.push(new Date(year, month, i)); } // Add empty slots for remaining days in the last week const remainingDays = 6 - endingDay; for (let i = 0; i < remainingDays; i++) { days.push(null); } return days; }; // Format date to match event dates const formatDate = (date) => { if (!date) return ""; return date.toISOString().split("T")[0]; }; // Get events for a specific day const getEventsForDay = (day) => { if (!day) return []; const dayStr = formatDate(day); return events.filter((event) => { const eventDate = event.start.dateTime ? new Date(event.start.dateTime).toISOString().split("T")[0] : event.start.date; return eventDate === dayStr; }); }; // Format time for display const formatEventTime = (dateTimeStr) => { if (!dateTimeStr) return ""; const date = new Date(dateTimeStr); return date.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", }); }; useEffect(() => { const calendarId = EVENT_CALENDAR_ID; const userTimeZone = "America/Los_Angeles"; const loadGapiAndListEvents = async () => { try { console.log("Starting to load events..."); if (typeof window.gapi === "undefined") { console.log("Loading GAPI script..."); await new Promise((resolve, reject) => { const script = document.createElement("script"); script.src = "https://apis.google.com/js/api.js"; document.body.appendChild(script); script.onload = () => { console.log("GAPI script loaded"); window.gapi.load("client", resolve); }; script.onerror = () => { console.error("Failed to load GAPI script"); reject(new Error("Failed to load the Google API script.")); }; }); } console.log("Initializing GAPI client..."); await window.gapi.client.init({ apiKey: CALENDAR_API_KEY, discoveryDocs: [ "https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest", ], }); // Get first and last day of current month const firstDay = new Date( currentDate.getFullYear(), currentDate.getMonth(), 1, ); const lastDay = new Date( currentDate.getFullYear(), currentDate.getMonth() + 1, 0, ); console.log("Fetching events..."); const response = await window.gapi.client.calendar.events.list({ calendarId: calendarId, timeZone: userTimeZone, singleEvents: true, timeMin: firstDay.toISOString(), timeMax: lastDay.toISOString(), orderBy: "startTime", }); console.log("Response received:", response); if (response.result.items) { setEvents(response.result.items); } } catch (error) { console.error("Detailed Error: ", error); setError(error.message || "Failed to load events"); } finally { setLoading(false); } }; if (!CALENDAR_API_KEY) { setError("API key is missing"); setLoading(false); return; } setLoading(true); loadGapiAndListEvents(); }, [CALENDAR_API_KEY, currentDate]); const monthNames = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ]; const weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; const changeMonth = (increment) => { setCurrentDate( new Date( currentDate.getFullYear(), currentDate.getMonth() + increment, 1, ), ); }; const handleEventMouseEnter = (event, e) => { const target = e.target; setTooltipPosition({ x: e.clientX, y: e.clientY, }); setHoveredEvent({ event, target }); }; const handleEventMouseLeave = () => { setHoveredEvent(null); }; const handleMouseMove = (e) => { if (hoveredEvent) { // Check if the mouse is still over the event element const rect = hoveredEvent.target.getBoundingClientRect(); const isStillHovering = e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom; if (!isStillHovering) { setHoveredEvent(null); } else { setTooltipPosition({ x: e.clientX, y: e.clientY, }); } } }; useEffect(() => { const handleScroll = () => { if (hoveredEvent) { const rect = hoveredEvent.target.getBoundingClientRect(); const mouseX = tooltipPosition.x - 15; // Subtract the offset added to the tooltip const mouseY = tooltipPosition.y - 15; const isStillHovering = mouseX >= rect.left && mouseX <= rect.right && mouseY >= rect.top && mouseY <= rect.bottom; if (!isStillHovering) { setHoveredEvent(null); } } }; window.addEventListener("scroll", handleScroll, true); return () => window.removeEventListener("scroll", handleScroll, true); }, [hoveredEvent, tooltipPosition]); if (!CALENDAR_API_KEY) { return (
{hoveredEvent.event.description}
)}Start: {formatEventTime(hoveredEvent.event.start.dateTime)}
End: {formatEventTime(hoveredEvent.event.end.dateTime)}
> ) : (All day event
)} {hoveredEvent.event.location && (Location: {hoveredEvent.event.location}
)}