Files
time_to_leave/README.md
T

163 lines
7.8 KiB
Markdown

# ⏱️ TimeToLeave
![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) ![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue)
## 🚀 How It Works
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`.
## 🧱 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, bike routing, and Google Calendar sync. |
## 🛠 Development & Running the Application
### 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.2.4 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
);
```
## 🛡️ Testing & Quality Assurance
The project provides comprehensive scripts for maintaining code quality:
* **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**.
## 📂 File Structure
```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
```
---
*Built for developers who bike to the train and hate missing their connections.*