Back to projects

Community Platform

Ecalendy

A community events platform for Lithuanians in Denmark. Aggregates 6 organizations' Google Calendars into one searchable interface with grid and list views, an interactive map, one-click calendar export, and full Lithuanian language support.

Ecalendy — Lithuanian events calendar with mobile view
Community events from 6 Lithuanian organizations in one place

Status

Live

Organizations

6 integrated

Deploy

Vercel

Cron

Daily 06:00 UTC

Why I built this

As president of the Lithuanian Youth Society in Denmark, I saw that Lithuanian events in Copenhagen were scattered across different organizations' Google Calendars, Facebook pages, and websites. Nobody had a single place to see everything. I built Ecalendy to aggregate all events into one searchable, filterable interface that the entire community can use.

How it works

1

Aggregate from 6 organizations

Fetches events from LYS, Credo choir, Lithuanian Embassy, DLB community, AI Workshops CPH, and Lithuanian Professionals — all via Google Calendar API. AllEvents.in scraper serves as a fallback source.

2

Geocode and enrich

Smart address parsing extracts street names using Danish patterns (gade, vej, stræde, allé), geocodes via Nominatim with rate limiting and caching. Events get keyword-based fallback images when no photo is available.

3

Browse, search, and filter

Desktop shows card grid with holographic shimmer effects; mobile shows a compact list. Filter by organization, language (Lithuanian or English), and month. Real-time text search across titles and descriptions.

4

Export to your calendar

One-click export to Google Calendar via URL intent, or download .ics files for Apple Calendar / Outlook. Lithuanian characters (ąčęėįšųūž) are properly encoded.

Integrated organizations

LYS — Lietuvių Jaunimo Draugija

Lithuanian

Choras Credo

Lithuanian

LR Ambasada Danijoje

Lithuanian

Danijos Lietuvių Bendruomenė

Lithuanian

AI Workshops CPH

English

Lithuanian Professionals in CPH

English

Key features

Grid + list views

Desktop card grid with holographic shimmer; mobile compact list — CSS-based responsive switching

Interactive map

Leaflet map with color-coded organization markers, popups, legend, and unmapped count indicator

Smart geocoding

Multi-strategy address parsing with Danish street patterns, Nominatim fallback, and in-memory caching

Lithuanian localization

Full Lithuanian UI with proper grammatical cases (nominative + genitive months), weekday names, and date formatting

Calendar export

Google Calendar URL intent + ICS download with UTF-8 Lithuanian character support

PWA installable

Add to home screen on iOS/Android, dark theme default, custom geometric logo icon

Tech stack

Frontend

Next.jsReactTypeScriptTailwind CSS

Maps

Leaflet

react-leaflet with dynamic imports for SSR safety, CartoDB dark/light tiles

Data Sources

Google Calendar APICheerio

6 Google Calendars + AllEvents.in scraper as fallback

Infrastructure

Vercel

ISR (1h revalidation), Vercel Cron (daily 06:00 UTC), file-based event cache

Architecture

Multi-source aggregation: fetches 6 Google Calendars in parallel with Promise.allSettled() — one failure doesn't block others. Deduplicates by org-prefixed IDs.

Smart geocoding pipeline: extracts Danish street patterns via regex, appends "Denmark" if missing, falls back to full address. Rate-limited to 1.1s/request for Nominatim compliance. Results cached in-memory + file.

Vercel Cron hits /api/cron/scrape daily at 06:00 UTC, fetching all orgs, geocoding new events, and writing to /tmp cache (1-hour TTL). ISR revalidates the page every hour.

Client-side filtering with useMemo — organization toggles, language tags, month selector, and text search all computed efficiently without API calls.

What I learned

  • -Lithuanian grammatical cases matter in UI — months need both nominative ("Balandis") and genitive ("Balandžio") forms depending on context.
  • -Google Calendar events set to "Private" visibility show as "busy" even with a public calendar — cost me hours of debugging until I found the setting.
  • -Nominatim geocoding needs careful rate limiting (1 request per 1.1s) and smart address parsing — Danish compound street names like "Langebrogade" need special handling.
  • -Building for a real community gives you feedback you can't get from personal projects nobody uses.
Next.jsReactTypeScriptTailwind CSSLeafletGoogle Calendar APICheerioVercel