UniCalendar: timetable, deadlines and grades in one place
A calendar and study planner for university students that works offline. It imports the official timetable from an .ics feed and adds what students track themselves: attendance, coursework deadlines, grades and the mark needed on the next assignment, with optional encrypted sync to the user's own Google Drive.
The problem
At university my timetable, deadlines, attendance and marks lived in four different systems, and none of them could answer "what do I need to get on this essay?". UniCalendar pulls the timetable in and builds everything else around it, grouped by module.

Features
- Six calendar views. Stream (a 14-day timeline by module), Radar (upcoming deadlines plotted by how soon they're due), Month, Week, Day and Year, with clash detection and academic terms.
- Timetable import from an
.icsURL or file, with recurring events expanded, and export back to.ics. - Attendance per module, keeping what the university recorded separate from what actually happened.
- Coursework with weights and grades, a Gantt chart where due dates can be dragged, and a grade projection for UK undergraduate and postgraduate classes, US letter grades and GPA, or a generic scale.
- Notes in a rich-text editor per lecture and module, exportable to PDF, plus CSV export of grades and attendance and full JSON backups.
- Keyboard first: a command palette on Ctrl/Cmd+K and shortcuts to switch views, plus a mobile layout with swipe navigation and a study timer.


Architecture
Everything runs in the browser: state lives in a Zustand store saved to local storage, with rich text and attachments in IndexedDB, and a service worker makes it work offline. There is no app database; the only server code is three small Vercel functions for the Google sign-in token exchange and fetching calendar feeds.
From timetable feed to dashboard
- 01ImportFetch the .ics feed directly, falling back to a small serverless proxy
- 02Parseical.js expands recurring events and exceptions into the app's schema
- 03OrganiseEvents, modules, coursework, attendance and notes in one store
- 04PersistLocal storage and IndexedDB, so it works offline
- 05Encrypt and syncAES-256-GCM, uploaded to the user's own Google Drive 1.5 s after the last change
- 06PlanSix views, Gantt chart, grade projection, reminders and exports
Sync and privacy
Sync is optional. Signing in with Google uses the OAuth authorization-code flow with PKCE, with the client secret kept server-side, and the drive.file scope, so the app can only see files it created. Data is encrypted with AES-256-GCM (key derived with PBKDF2-SHA256, 600,000 iterations) before it's uploaded to a folder in the user's own Drive, with a version file to detect conflicts.
Looking back
Sync is push and pull with conflict detection rather than a full merge, and reminders only fire while the app is open. A user-chosen encryption passphrase, and proxying calendar feeds only through my own function, are the next things I'd change.
Source: src/ and api/ in the repository