fegger c869d4958e Upgrade Next.js to v16 and refactor API client
Bump Next.js from v9 to v16.2.6 and add node-ical to
serverExternalPackages. Remove Geist font dependencies in favor
of system fonts. Fix state updates in geocode hooks to prevent
stale closures. Expose ApiClient methods as public and improve
error handling in fetchWithRetry.
2026-05-10 02:07:47 +02:00
2026-05-09 11:00:21 +02:00
2026-05-09 12:15:23 +02:00
2026-05-09 11:00:21 +02:00
2026-05-09 22:47:17 +02:00
2026-05-09 11:00:21 +02:00
2026-05-09 11:00:21 +02:00
2026-05-09 11:00:21 +02:00

TimeToLeave

Know when to leave the house so you're never late for your next meeting.

TimeToLeave is a web app that tells you when to leave for upcoming events based on real-time Austrian train (ÖBB) schedules — and optionally compares train journeys with bicycle routes.

Features

  • Real-time train journeys — Query the ÖBB HAFAS API for live departure times, delays, and platform information.
  • Calendar import — Import events from any ICS calendar (CalDAV, Google Calendar, etc.).
  • Bicycle routing — Compare train journeys with bicycle routes powered by OSRM, including distance, duration, and turn-by-turn directions.
  • Train vs bicycle comparison — See both travel modes side by side for each event, so you can decide what works best.
  • Calendar month view — Browse events in a month grid, see event indicators on dates, and drill into train/bike details per day.
  • Geolocation support — Use your browser's current location as the origin station.
  • Countdown & status indicators — Visual countdown timers and live status badges so you know exactly when to go.

Architecture

Built with Next.js App Router (v16), React 19, TypeScript, and Tailwind CSS v4.

src/
├── app/                      # Next.js App Router pages & layout
│   ├── api/                  # Server-side API routes
│   │   ├── hafas/            # ÖBB train journey search
│   │   ├── calendar/         # ICS calendar parsing
│   │   ├── geocode/          # Nominatim geocoding
│   │   ├── bike-route/       # OSRM bicycle routing
│   │   └── health/           # Server health check
│   ├── ui/                   # Shared UI components
│   ├── page.tsx              # Home page
│   └── layout.tsx            # Root layout
├── hooks/                    # Custom React hooks
│   ├── useJourneys.ts        # Fetch train journeys per event
│   ├── useBikeRoute.ts       # Fetch bicycle routes
│   ├── useCalendar.ts        # Calendar import & parsing
│   ├── useEventsStore.tsx    # Events state management
│   ├── useGeolocation.ts     # Browser geolocation
│   ├── useOriginStation.ts   # Origin station selection
│   ├── useClock.ts           # Live clock for countdowns
│   └── useServerHealth.ts    # API health monitoring
├── lib/                      # Shared utilities & clients
│   ├── hafas-client.ts       # ÖBB HAFAS API client
│   ├── geocoding-client.ts   # Nominatim geocoding client
│   ├── bike-routing-client.ts# OSRM bicycle routing client
│   ├── calendar-utils.ts     # ICS parsing utilities
│   ├── countdown-utils.ts    # Countdown time calculations
│   ├── formatting.ts         # Date/time formatting helpers
│   └── constants.ts          # App-wide constants
├── types/                    # TypeScript type definitions
└── test/                     # Test utilities

Getting Started

Prerequisites

  • Node.js 20+
  • npm (or yarn / pnpm)

Installation

npm install

Development

npm run dev

The app will be available at http://localhost:3000.

Build & Start

npm run build
npm start

Docker Deployment

Build and run with Docker Compose:

docker compose up --build -d

The app will be available on port 3000 by default. Override the port or any other variable by creating a .env file at the project root:

PORT=8080
HAFAS_URL=https://fahrplan.oebb.at/bin/mgate.exe
NOMINATIM_URL=https://nominatim.openstreetmap.org
NOMINATIM_USER_AGENT=TimeToLeave/2.0
OSRM_URL=https://router.project-osrm.org

The Dockerfile uses a 3-stage build (deps → builder → runner) with output: "standalone", producing a small final image that runs as a non-root user. Stop and remove the container with docker compose down.

Testing

npm test          # Run tests once
npm run test:watch  # Run tests in watch mode

Linting & Type Checking

npm run lint
npm run typecheck

Environment Variables

The following environment variables can be configured (defaults are provided for development):

Variable Description Default
HAFAS_URL ÖBB HAFAS API endpoint https://fahrplan.oebb.at/bin/mgate.exe
NOMINATIM_URL Nominatim geocoding endpoint https://nominatim.openstreetmap.org
NOMINATIM_USER_AGENT User-Agent for Nominatim requests TimeToLeave/2.0
OSRM_URL OSRM routing endpoint https://router.project-osrm.org

Tech Stack

Category Technology
Framework Next.js 16 (App Router)
UI React 19 + Tailwind CSS v4
Language TypeScript 5
Train Data ÖBB HAFAS API
Geocoding Nominatim
Bicycle Routing OSRM
Calendar node-ical
Dates date-fns
Testing Vitest + React Testing Library

How It Works

  1. Add events — Manually enter events or import from an ICS calendar URL.
  2. Pick an origin — Use your current location or search for a departure station.
  3. Get journey info — The app queries ÖBB for real-time train schedules and optionally calculates bicycle routes via OSRM.
  4. Decide when to leave — Each event shows a countdown, the best available train, and (if enabled) a bicycle alternative with estimated travel time.

License

MIT

S
Description
No description provided
Readme 35 MiB
Languages
TypeScript 96.5%
JavaScript 1.1%
Kotlin 0.7%
CSS 0.7%
Ruby 0.4%
Other 0.6%