update documentation

This commit is contained in:
2026-05-18 15:01:53 +02:00
parent 834025e560
commit 7018443b18
13 changed files with 756 additions and 725 deletions
+110 -144
View File
@@ -1,162 +1,128 @@
# ⏱️ TimeToLeave
# TimeToLeave
TimeToLeave is a departure planner for calendar-driven travel. It imports events with locations, resolves the closest public-transport station, checks live ÖBB HAFAS and Wiener Linien data, and shows when to leave by train or bike.
![TimeToLeave Logo](apps/web/public/timetoleave_logo.png)
> **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 (`.ics` files or Google Calendar), checks real-time train/bus departures (HAFAS & WienerLinien), and provides a live "Leave Status" based on real-time delays.
![Platform](https://img.shields.io/badge/platform-Web_%26_Mobile-blue) ![Next.js](https://img.shields.io/badge/Next.js-16.2-green) ![React Native](https://img.shields.io/badge/React%20Native-0.81-blue) ![Expo](https://img.shields.io/badge/Expo-54-black) ![TypeScript](https://img.shields.io/badge/TypeScript-5-blue)
![Platform](https://img.shields.io/badge/platform-Web_%26_Mobile-blue) ![Next.js](https://img.shields.io/badge/Next.js-16.2-green) ![React Native](https://img.shields.io/badge/React%20Native-0.81-blue) ![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue)
## What It Does
## 🚀 How It Works
1. Imports events from ICS URLs, ICS files, Google Calendar on web, or native device calendars on mobile.
2. Stores events locally in browser `localStorage` or mobile `AsyncStorage`.
3. Uses browser or device geolocation, a saved station, or the default Mödling origin.
4. Geocodes event destinations and resolves nearby stations through HAFAS `LocMatch`.
5. Searches ÖBB HAFAS journeys, enriches train data with the ÖBB GTFS fallback when available, and shows real-time delays and cancellations.
6. Calculates bike and final walking routes through OSRM.
7. Shows a live leave-by countdown and optional browser/mobile notifications.
1. **Sync Your Calendar:** Import your `.ics` file, provide a calendar URL, or connect your Google Calendar via OAuth 2.0. The app extracts your upcoming events and destinations.
2. **Set Your Origin:** Define your home station or let the app use your current geolocation. The app can also find the nearest station to your location via HAFAS LocMatch.
3. **Journey Calculation:** The app queries the HAFAS protocol and WienerLinien APIs to find the best public transport connections to your event destination.
4. **Real-Time Monitoring:** It monitors your train's real-time departure time, accounts for delays, and adds your local travel time (e.g., biking or walking to the station) to calculate a dynamic countdown.
5. **Leave Status:** You get a clear status: `Leave now`, `On time`, `Delayed +X min`, or `Departure missed`.
## Repository Layout
## 🧱 Project Structure
| Path | Purpose |
| --- | --- |
| `apps/web/` | Next.js 16 App Router web UI plus backend proxy routes for HAFAS, geocoding, routing, calendar parsing, Google Calendar, and Wiener Linien. |
| `apps/mobile/` | Expo 54 / React Native 0.81 mobile app with native calendar, location, notification, and local storage integrations. |
| `packages/core/` | Shared types, defaults, HAFAS time parsing, journey parsing/scoring, countdown, formatting, and status utilities. |
| `packages/api-client/` | Shared client for calling the web app's `/api/*` backend routes from web hooks and the mobile app. |
| `docs/` | Architecture, development, API, user, and codebase reference documentation. |
This project uses a monorepo setup (npm workspaces) to manage multiple, interconnected parts:
## Prerequisites
| 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, bike routing, and Google Calendar sync. |
- Node.js 20 or newer
- npm 9 or newer
- For mobile native builds: Expo/EAS prerequisites plus Android Studio or Xcode as needed
## 🛠 Development & Running the Application
## Setup
### Prerequisites
* Node.js (version 20.x or higher)
* npm (version 9.x or higher)
### Installation
1. **Clone the repository:**
```bash
git clone <repository-url>
cd TimeToLeave
```
2. **Install dependencies:**
```bash
npm install
```
3. **Environment variables:**
* For `apps/web` and `apps/mobile`, copy `.env.example` to `.env` in each app directory and update the backend API URL and any required keys.
* Google Calendar integration requires `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `GOOGLE_REDIRECT_URI` environment variables.
### 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.1.0 with Tailwind CSS 4
* **State Management:** React Context (via `EventsProvider` and `ReminderSettingsProvider`)
* **Routing:** Next.js built-in routing for `/` (event list) and `/calendar` views.
* **Calendar Integration:**
* Import `.ics` files or provide calendar URLs
* **Google Calendar sync** via full OAuth 2.0 flow (token exchange, refresh, and status checks)
* Batch edit panel for managing event destinations
* Edit support in the AddEventModal for modifying existing events
* **Dark/Light Theme:** Built-in theme toggle
### Mobile Application (`apps/mobile`)
* **Framework:** React Native 0.81 via Expo 54
* **Navigation:** React Navigation 7 (Native Stack)
* **Theme:** Dark theme by default
* **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.
* **Station Selection:** Async station search with error handling and nearest-station detection via HAFAS LocMatch.
### 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, calendar parsing, and Google Calendar sync.
```typescript
import { ApiClient } from "@timetoleave/api-client";
// Initialize with your backend URL
const api = new ApiClient("http://localhost:3000");
// 1. Sync your calendar (via .ics URL)
const events = await api.fetchCalendar("https://example.com/calendar.ics", 7);
// 2. Sync Google Calendar (after OAuth flow)
const googleEvents = await api.fetchGoogleCalendarEvents();
// 3. Search for a station via the HAFAS LocMatch endpoint
const stationResult = await api.hafasRequest({
svcReqL: [
{
meth: "LocMatch",
req: { searchTxt: "Wien Mitte", maxMatches: 5 },
},
],
});
const stations = stationResult?.svcReqL?.[0]?.res?.locL ?? [];
// 4. Find the nearest station to your current location
const nearestStation = await api.findNearestStation(48.2082, 16.3738);
// 5. Find journeys between stations for a specific date
const journeys = await api.searchJourneys(
stations[0].extId, // From
"dest:extId", // To
new Date() // Date
);
// 6. Get a bike route from your current location to the station
const bikeRoute = await api.getBikeRoute(
48.2082, 16.3738, // From lat/lng
48.1850, 16.3780 // To lat/lng
);
```bash
npm install
cp .env.example .env
```
## 🛡️ Testing & Quality Assurance
The web app reads environment variables from the workspace process. For deployment, configure the same values in the hosting environment.
The project provides comprehensive scripts for maintaining code quality:
Important variables:
* **Linting:** Use `npm run lint` to catch stylistic and structural errors via ESLint 9.
* **Type Checking:** Use `npm run typecheck` to 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**.
| Variable | Purpose |
| --- | --- |
| `HAFAS_URL` | ÖBB HAFAS endpoint. Defaults to `https://fahrplan.oebb.at/bin/mgate.exe`. |
| `NOMINATIM_URL` and `NOMINATIM_USER_AGENT` | Geocoding endpoint and required user agent. |
| `OSRM_URL` | Routing endpoint used for bike and foot profiles. |
| `WIENER_LINIEN_API_URL` | Wiener Linien live data base URL. |
| `OEBB_GTFS_URL` | Optional ÖBB GTFS ZIP used to enrich HAFAS train metadata. |
| `CORS_ALLOWED_ORIGINS` | Comma-separated origins allowed to call `/api/*`. |
| `DEPLOYMENT_URL` | Public base URL used by Google OAuth redirects. |
| `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GOOGLE_REDIRECT_URI` | Required for Google Calendar sync on web. |
| `EXPO_PUBLIC_API_BASE_URL` | Mobile backend URL. Set this for device builds so the app can reach the deployed web backend. |
## 📂 File Structure
## Development
```text
├── 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, Google 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
```
| Command | Description |
| --- | --- |
| `npm run dev` | Start the Next.js web app on `http://localhost:3000`. |
| `npm run dev:mobile` | Start the Expo development server. |
| `npm run build` | Build the web app. |
| `npm run start` | Start the built web app. |
| `npm run test` | Run web Vitest and mobile Jest suites. |
| `npm run lint` | Run ESLint across web, mobile, core, and api-client workspaces. |
| `npm run typecheck` | Run TypeScript checks across all workspaces. |
---
*Built for developers who bike to the train and hate missing their connections.*
## Web App
Current user-facing routes:
| Route | Description |
| --- | --- |
| `/` | Departure desk. Shows the next upcoming event, leave-by status, transport mode selector, train journeys, bike route, final walk, and nearby Wiener Linien departures. |
| `/calendar` | Calendar import and management view with URL, file, and Google Calendar tabs plus batch destination editing. |
The add/edit event UI is a modal component, not a standalone page route.
## Backend Proxy Routes
All backend routes live under `apps/web/src/app/api/` and are protected by strict CORS plus per-IP rate limiting in `apps/web/src/proxy.ts`.
| Endpoint | Methods | Purpose |
| --- | --- | --- |
| `/api/health` | `GET` | Returns `{ ok, ts, version }`. |
| `/api/hafas` | `GET`, `POST` | Convenience journey search or validated HAFAS relay for `TripSearch` and `LocMatch`. |
| `/api/geocode` | `GET` | Forward geocoding through Nominatim. |
| `/api/bike-route` | `GET` | OSRM bicycle route between two coordinates. |
| `/api/walk-route` | `GET` | OSRM foot route between two coordinates. |
| `/api/calendar` | `GET` | Fetch and parse an allowed remote ICS URL. |
| `/api/calendar/parse` | `POST` | Parse uploaded/raw ICS text. |
| `/api/calendar/google` | `GET` | Fetch Google Calendar events using OAuth cookies. |
| `/api/auth/google` | `GET` | Start Google OAuth. |
| `/api/auth/google/callback` | `GET` | Complete Google OAuth and store token cookie. |
| `/api/auth/google/status` | `GET` | Report Google configuration and connection state. |
| `/api/auth/google/disconnect` | `POST` | Delete the Google token cookie. |
| `/api/wienerlinien/stops` | `GET` | Find nearby Wiener Linien stops. |
| `/api/wienerlinien/monitor` | `GET` | Fetch and flatten live stop departures. |
## Mobile App
The mobile app includes event list, add/edit event, event detail, calendar import, and settings screens. It supports:
- Native calendar sync for the next 30 days.
- Calendar-source selection, including CalDAV/DAVx, Apple, Google, Exchange, subscribed, and local calendars when exposed by the device.
- Saved origin station with current-location lookup.
- Train, bike, walking, and Wiener Linien live sections on event detail.
- Local notifications scheduled from stored event/settings data.
- Dark/light theme toggle.
## Documentation
Start with [docs/README.md](docs/README.md), then use:
- [Architecture](docs/ARCHITECTURE.md)
- [Development Guide](docs/DEVELOPMENT.md)
- [Core & API Client Reference](docs/API_REFERENCE.md)
- [User Guide](docs/USER_GUIDE.md)
- [Codebase Function Guide](docs/CODEBASE_FUNCTION_GUIDE.md)
## Important Implementation Notes
- HAFAS date/time values are Vienna-local strings. Use `parseHafasTime()` and `hafasDateTime()` from `@timetoleave/core`; avoid ad hoc `Date` parsing for HAFAS payloads.
- Remote calendar URLs are restricted to known calendar providers and private/reserved hosts are blocked.
- Mobile devices must use a reachable `EXPO_PUBLIC_API_BASE_URL`; same-origin empty base URLs only work in the web app.
- This repo uses Next.js 16. Before changing Next.js routing, middleware/proxy, or framework conventions, read the relevant guide in `node_modules/next/dist/docs/`.