⏱️ TimeToLeave
TimeToLeave is a smart departure planner that tells you exactly when to leave home to catch your public transport for upcoming appointments. It syncs with your personal calendar, checks real-time train/bus departures (HAFAS & WienerLinien), and provides a live "Leave Status" based on real-time delays.
🚀 How It Works
- Sync Your Calendar: Import your
.icsfile or provide a calendar URL. The app extracts your upcoming events and destinations. - Set Your Origin: Define your home station or let the app use your current geolocation.
- Journey Calculation: The app queries the HAFAS protocol and WienerLinien APIs to find the best public transport connections to your event destination.
- Real-Time Monitoring: It monitors your train's real-time departure time, accounts for delays, and adds your local travel time (e.g., biking to the station) to calculate a dynamic countdown.
- Leave Status: You get a clear status:
Leave now,On time,Delayed +X min, orDeparture missed.
🧱 Project Structure
This project uses a monorepo setup (npm workspaces) to manage multiple, interconnected parts:
| Directory | Description |
|---|---|
apps/web/ |
The main web dashboard built with Next.js 16, React 19, and Tailwind CSS 4. |
apps/mobile/ |
The on-the-go mobile client built with React Native 0.81 and Expo 54. |
packages/core/ |
Shared domain logic, types (Event, Journey, Station), countdown utilities, and status calculators. |
packages/api-client/ |
A lightweight client that handles API proxies for HAFAS requests, calendar parsing, geocoding, and bike routing. |
🛠 Development & Running the Application
Prerequisites
- Node.js (version 20.x or higher)
- npm (version 9.x or higher)
Installation
-
Clone the repository:
git clone <repository-url> cd TimeToLeave -
Install dependencies:
npm install -
Environment variables:
- For
apps/webandapps/mobile, copy.env.exampleto.envin each app directory and update the backend API URL and any required keys.
- For
Available Scripts
| Script | Command | Description |
|---|---|---|
dev |
npm run dev |
Starts the Next.js development server for the Web dashboard. |
dev:mobile |
npm run dev:mobile |
Starts the Expo development server for the Mobile client. |
build |
npm run build |
Builds the production bundle for the Web application. |
test |
npm run test |
Runs Vitest for the Web app and Jest for the Mobile app. |
lint |
npm run lint |
Runs ESLint across both web and mobile clients. |
typecheck |
npm run typecheck |
Runs TypeScript type checking across all workspaces. |
📝 Key Features & Tech Stack
Web Application (apps/web)
- Framework: Next.js 16.2.6 (App Router)
- UI: React 19.2.4 with Tailwind CSS 4
- State Management: Zustand (for events and station selection)
- Routing: Next.js built-in routing for
/add-event,/calendar, and/eventviews.
Mobile Application (apps/mobile)
- Framework: React Native 0.81 via Expo 54
- Navigation: React Navigation 7 (Native Stack)
- Device APIs:
expo-location: For geocoding your current position.expo-calendar: For native calendar event integration.expo-notifications: For native push notifications when it's time to leave.@react-native-async-storage/async-storage: For persisting settings and local state.
Core Logic (packages/core)
- Countdown Utilities: Calculates time-deltas and assigns color codes (Red/Orange/Yellow/Green/Blue) based on urgency.
- HAFAS Time Parsing: Highly accurate timezone-aware parsing for HAFAS timestamps, specifically handling
Europe/Vienna(CET/CEST) and DST transitions. - WienerLinien Support: Native types and handling for Vienna public transport departures.
- Leave Status: Derives human-readable statuses (
Leave now,Delayed +10 min, etc.) by comparing the best non-cancelled journey's real departure time against the current time.
📄 API Client Usage
The @timetoleave/api-client package provides a clean interface to interact with your backend proxy, which handles the heavy lifting of HAFAS protocol communication and calendar parsing.
import { ApiClient } from "@timetoleave/api-client";
// Initialize with your backend URL
const api = new ApiClient("http://localhost:3000");
// 1. Sync your calendar
const events = await api.fetchCalendar("https://example.com/calendar.ics", 7);
// 2. Search for a station by name
const stations = await api.searchStation("Wien Mitte");
// 3. Find journeys between stations for a specific date
const journeys = await api.searchJourneys(
stations[0].extId, // From
"dest:extId", // To
new Date() // Date
);
// 4. Get a bike route from your current location to the station
const bikeRoute = await api.getBikeRoute(
48.2082, 16.3738, // From lat/lng
stations[0].lat, stations[0].lng // To lat/lng
);
🛡️ Testing & Quality Assurance
The project provides comprehensive scripts for maintaining code quality:
- Linting: Use
npm run lintto catch stylistic and structural errors via ESLint 9. - Type Checking: Use
npm run typecheckto ensure strict type safety across the codebase via TypeScript 5. - Testing:
- The web application uses Vitest (v4.1.5) with jsdom and @testing-library/react.
- The mobile application uses Jest (v29.7.0) with jest-expo and react-test-renderer.
📂 File Structure
├── apps/
│ ├── mobile/ # Mobile application using React Native and Expo
│ └── web/ # Web application using Next.js and Tailwind CSS
├── packages/
│ ├── api-client/ # API client for HAFAS, Calendar, and Routing proxies
│ └── core/ # Shared domain types, countdowns, and HAFAS time utilities
├── node_modules/ # Third-party dependencies
└── README.md # The file you're reading now
Built for developers who bike to the train and hate missing their connections.
