Academic Calendar
Purpose and Scope
The Academic Calendar feature displays institutional events, holidays, and important dates for JIIT academic sessions. It provides a chronological timeline view with filtering capabilities and Google Calendar synchronization. For information about personal timetable scheduling, see Schedule Generation. For exporting personal schedules to Google Calendar, see Google Calendar Integration.
Sources: src/components/academic-calendar.tsx1-414
Overview
The academic calendar system loads event data from static JSON files organized by academic year and renders them in an interactive timeline. Users can view past and upcoming events, filter by holidays, and sync all events to their Google Calendar account.
System Architecture

Sources: src/components/academic-calendar.tsx20-54 src/utils/calendar-AC.ts21-107
Data Model
CalendarEvent Type
The calendar system expects event data in the following structure:
| Field | Type | Description | Example |
|---|---|---|---|
summary |
string |
Event title (prefix "Holiday -" for holidays) | "Holiday - Republic Day" |
start.date |
string |
ISO date string for event start | "2025-01-26" |
end.date |
string |
ISO date string for event end | "2025-01-27" |
// Type definition from calendar-AC.ts
type CalendarEvent = {
summary: string;
start: { date: string };
end: { date: string };
};
The component automatically detects holidays by checking if summary.startsWith("Holiday -") and applies special styling.
Sources: src/utils/calendar-AC.ts9-13 src/components/academic-calendar.tsx260-261
Component State Management
State Variables

| State Variable | Type | Purpose |
|---|---|---|
isLoading |
boolean |
Controls loading spinner display |
calendarData |
CalendarEvent[] |
Raw event data from JSON |
isDataLoading |
boolean |
Tracks fetch operation status |
selectedYear |
string |
Currently selected academic year ("2425" or "2526") |
visibleEventsCount |
number |
Number of past events to display (starts at 0) |
showHolidaysOnly |
boolean |
Filter state for holiday-only view |
availableYears |
Array<{value: string, label: string}> |
List of selectable years |
eventRefs |
HTMLDivElement[] |
Refs for auto-scroll functionality |
upcomingDividerRef |
HTMLDivElement |
Ref to "Upcoming Events" divider |
Sources: src/components/academic-calendar.tsx7-18
Data Loading Pipeline
Year Selection and Data Fetching

The component uses different filename conventions based on the year:
- Year
2425: Loadscalendar.json(correct spelling) - Other years: Loads
calender.json(alternate spelling)
Sources: src/components/academic-calendar.tsx28-54
Event Processing and Display Logic
Event Categorization
The component processes events through several transformations:

Code Implementation:
// Sort events chronologically
const sortedEvents = [...calendarData].sort(
(a, b) => new Date(a.start.date).getTime() - new Date(b.start.date).getTime()
);
// Filter for holidays if enabled
const filteredEvents = showHolidaysOnly
? sortedEvents.filter((event) => event.summary.startsWith("Holiday -"))
: sortedEvents;
// Separate past and upcoming
const today = new Date();
const upcomingEvents = filteredEvents.filter((event) => {
const eventDate = new Date(event.start.date);
return eventDate >= today;
});
const pastEvents = filteredEvents
.filter((event) => {
const eventDate = new Date(event.start.date);
return eventDate < today;
})
.reverse(); // Most recent past events first
// Combine for display
const eventsToShow = [
...pastEvents.slice(0, visibleEventsCount).reverse(),
...upcomingEvents,
];
Sources: src/components/academic-calendar.tsx56-83
UI Features
Timeline View
The timeline uses an alternating left-right layout for desktop (responsive single-column for mobile):
| Element | Styling | Purpose |
|---|---|---|
| Central Timeline | Vertical line with bg-[#F0BB78]/30 |
Visual anchor for chronology |
| Event Dots | Circle with bg-[#F0BB78] (holidays: bg-orange-500) |
Mark event position on timeline |
| Event Cards | Even: right-aligned, Odd: left-aligned | Zigzag layout for readability |
| Holiday Badge | Red background with "Holiday" label | Quick visual identification |
| Past Events | Reduced opacity (75%) and muted colors | De-emphasize historical data |
| "Upcoming Events" Divider | Horizontal line with badge | Clear separation point |
Sources: src/components/academic-calendar.tsx253-378
Auto-Scroll Behavior
On year change, the component automatically scrolls to the first upcoming event:
useEffect(() => {
if (eventsToShow.length === 0) return;
// Find first upcoming event index
let targetIndex = eventsToShow.findIndex((event) => {
const eventDate = new Date(event.start.date);
return eventDate >= today;
}) - 1; // Scroll to event just before upcoming
if (targetIndex >= 0 && eventRefs.current[targetIndex]) {
eventRefs.current[targetIndex].scrollIntoView({ behavior: "smooth" });
}
}, [selectedYear]);
Sources: src/components/academic-calendar.tsx85-104
Google Calendar Synchronization
OAuth Flow

Sources: src/components/academic-calendar.tsx106-128 src/utils/calendar-AC.ts21-107
Event Transformation
The addAcademicCalendarEvents function transforms calendar events into Google Calendar API format:
const calendarEvents = events.map(event => ({
summary: event.summary,
start: {
date: event.start.date,
timeZone: 'Asia/Kolkata'
},
end: {
date: event.end.date,
timeZone: 'Asia/Kolkata'
},
description: `Academic Calendar Event for JIIT 2024-25`,
transparency: 'transparent', // All-day event, doesn't block time
visibility: 'public',
colorId: event.summary.toLowerCase().includes('holiday') ? '11' : '1',
}));
Key Properties:
| Property | Value | Purpose |
|---|---|---|
date (not dateTime) |
ISO date string | Creates all-day events |
timeZone |
'Asia/Kolkata' |
Ensures correct date interpretation |
transparency |
'transparent' |
Events don't show as "busy" |
colorId |
'11' (holidays) or '1' (default) |
Visual distinction in calendar |
Sources: src/utils/calendar-AC.ts38-52
Interactive Controls
Year Selector
<select
value={selectedYear}
onChange={(e) => {
setSelectedYear(e.target.value);
setVisibleEventsCount(0); // Reset to show only upcoming events
}}
className="appearance-none bg-white/10 border border-[#F0BB78]/30
rounded-lg px-4 py-2 pr-8 text-[#F0BB78] font-semibold"
>
{availableYears.map((year) => (
<option key={year.value} value={year.value}>
{year.label}
</option>
))}
</select>
Changing the year triggers a complete data reload and resets the visible past events count.
Sources: src/components/academic-calendar.tsx182-202
Holiday Filter Button
Fixed-position button that toggles between all events and holidays only:
<button
onClick={() => {
setShowHolidaysOnly(!showHolidaysOnly);
setVisibleEventsCount(0); // Reset view
}}
className={`fixed bottom-16 left-4 ... ${
showHolidaysOnly
? "bg-red-500/20 border-red-500/50 text-red-300"
: "bg-white/10 border-white/20 text-[#F0BB78]"
}`}
>
<Filter className="w-4 h-4 sm:w-5 sm:h-5" />
<span>{showHolidaysOnly ? "All Events" : "Holidays Only"}</span>
</button>
Sources: src/components/academic-calendar.tsx381-397
Load Previous Events Button
Displays at the top of the timeline when past events exist but are hidden:
{pastEvents.length > visibleEventsCount && (
<button
onClick={() => {
setVisibleEventsCount(pastEvents.length);
// Auto-scroll to "Upcoming Events" divider after loading
setTimeout(() => {
if (upcomingDividerRef.current) {
const rect = upcomingDividerRef.current.getBoundingClientRect();
const scrollTop = window.scrollY + rect.top - 80; // 80px header offset
window.scrollTo({ top: scrollTop, behavior: "smooth" });
}
}, 200);
}}
>
Load All Previous Events ({pastEvents.length - visibleEventsCount} remaining)
</button>
)}
Sources: src/components/academic-calendar.tsx211-236
Error Handling
Data Loading Errors
The component includes basic error handling for year availability fetching:
fetch("/api/available-years")
.then((res) => res.json())
.then((years) => {
setAvailableYears(years);
if (years.length > 0) {
setSelectedYear(years[years.length - 1].value);
}
})
.catch((error) => {
console.error("Failed to fetch available years:", error);
});
If the years API fails, the component falls back to default years set in state initialization:
const [availableYears, setAvailableYears] = useState([
{ value: "2425", label: "2024-25" },
{ value: "2526", label: "2025-26" },
]);
Sources: src/components/academic-calendar.tsx28-43
Google Calendar Sync Errors
The addAcademicCalendarEvents function returns detailed error information:
type GoogleCalendarResponse = {
success: boolean;
message: string;
error?: string;
};
Error scenarios handled:
- OAuth authentication failure
- Individual event creation failure
- Network errors
All errors are caught and returned in the response object, with user-facing alerts displaying the error message.
Sources: src/utils/calendar-AC.ts15-19 src/components/academic-calendar.tsx118-125
Loading States
Initial Load
if (isDataLoading) {
return (
<main>
<div className="bg-white/10 border border-[#F0BB78]/20 rounded-2xl">
<div className="mb-6 flex flex-row items-end gap-2 h-10">
{[0, 1, 2].map((i) => (
<motion.div
key={i}
className="w-4 h-4 sm:w-5 sm:h-5 rounded-full bg-[#F0BB78]"
animate={{ y: [0, -18, 0] }}
transition={{
repeat: Infinity,
duration: 0.9,
delay: i * 0.15,
}}
/>
))}
</div>
<p className="text-lg font-semibold text-[#F0BB78]">
Loading Academic Calendar...
</p>
</div>
</main>
);
}
Displays animated bouncing dots with loading message while fetching calendar data.
Sources: src/components/academic-calendar.tsx130-159
Calendar Sync Loading
<button
onClick={handleAddToCalendar}
disabled={isLoading}
>
<span>{isLoading ? "Adding to Calendar..." : "Add to Calendar"}</span>
</button>
Button text changes and becomes disabled during Google Calendar sync operation.
Sources: src/components/academic-calendar.tsx399-410
Summary
The Academic Calendar system provides a comprehensive view of institutional events through:
- Data Management: Static JSON files organized by year with automatic fetching
- Timeline Visualization: Chronological display with past/upcoming separation
- Filtering: Toggle between all events and holidays-only view
- Progressive Disclosure: Load past events on-demand to avoid clutter
- External Integration: One-click sync to Google Calendar via OAuth 2.0
- Responsive Design: Adapts layout from desktop zigzag to mobile single-column
The component is self-contained with minimal external dependencies, relying primarily on React state management and the Google Calendar API for synchronization functionality.
Sources: src/components/academic-calendar.tsx1-414 src/utils/calendar-AC.ts1-108