# Trip Plan β€” Project Overview A self-hosted, multi-user web tool for collaboratively planning trips. Runs as a single Docker container. ## Core Idea Multiple people log in to the same instance. Anyone can create a trip by picking a name and a date range. The tool generates a day-by-day calendar for that range, and every member of the trip can fill in what happens on each day β€” flights, stays, transports, activities. The trip is visualized on an interactive map with the route drawn between stops, distances per leg, and an overall summary (days, nights, flights, total km). ## Features ### 1. Users & Access (privacy-friendly, Mullvad-style) - **No registration form, no email, no password.** Click "Create account" and you get a random account number (e.g. `7K2M-9QX4-TT8B-3WPF`) β€” that token IS your login. It's shown once; only its hash is stored server-side. - Every account gets an auto-generated, editable **display name** (e.g. `brave-otter`) so members and cost splits stay human-readable. - Trips are shared via a **join code**: every trip has a short code (e.g. `M4TH-8RK2`); anyone with an account can enter it under "Join a trip" to become an editor. The owner can regenerate the code to cut off further joins. ### 2. Trips - Create a trip with **name + start date + end date**. - Editing the date range regenerates the calendar (existing day entries outside the new range are kept but flagged). - Trip list dashboard showing all trips you're a member of. ### 3. Day-by-Day Calendar - Calendar grid generated from the trip's date range (weeks as rows, like a real calendar). - Click any day to open the day editor and add **entries**: | Entry type | Typical fields | |---|---| | πŸ“ Activity / sightseeing | place, time, notes | | πŸ™οΈ Stay (area block / accommodation) | location, from β†’ until dates, hotel name in title, price | | πŸš† Transport | mode (train/bus/ferry/taxi/drive), from β†’ to; auto-created between consecutive stays | | ✈️ Flight | flight no., from/to airports, departure/arrival time, multi-leg segments | | πŸš™ Rental car | pickup/dropoff, included km | | πŸ“ Note | free text | (Legacy `hotel`/`travel`/`immigration` entries are migrated automatically at startup: hotel β†’ stay, travel β†’ transport, immigration β†’ activity with a πŸ›‚ title prefix.) - Every entry can have a **location** (searched via OpenStreetMap geocoding β€” type "Chiang Mai" and pick from suggestions; lat/lng stored automatically). - Entries show as compact chips inside the day cell; multiple entries per day, ordered. ### 4. Map Visualization - Interactive map (Leaflet + OpenStreetMap tiles β€” free, no API key required, unlike Google Maps). - All located entries plotted as markers, numbered in chronological order. - Route polyline connecting the stops in order. - **Distance per leg** (km, great-circle) shown on the route and in a leg-by-leg list. ### 5. Costs & Splitting - Every entry can carry a **price** (flights, stays, car rental, train tickets, activities…), in the trip's currency (one currency per trip, no FX conversion). - Each priced entry records **who paid** and how it's **split**: - `equal` β€” total split equally among selected participants (e.g. rental car 50/50) - `own` β€” price is per person, everyone pays their own (e.g. flights) - `payer` β€” personal expense of the payer - **Cost summary** per trip: total cost, breakdown by type, per-user share vs. paid vs. net balance, and **settle-up suggestions** (who transfers how much to whom). ### 6. Summary - Total days and nights. - Number of flights, stays, transports. - **Total distance in km** across the whole trip. - Countries/locations visited (from entry locations). ## Tech Stack | Layer | Choice | Why | |---|---|---| | Backend | Node.js 20 + Express | Small, ubiquitous, one runtime in the container | | Database | SQLite (better-sqlite3) | Zero-config single file; persisted via Docker volume | | Auth | cookie sessions + bcrypt | Simple and adequate for a self-hosted tool | | Frontend | Vanilla JS SPA (no build step) | Keeps image small and the stack simple | | Map | Leaflet + OpenStreetMap | Free, no API key, offline-friendly tiles options | | Geocoding | Nominatim (proxied server-side) | Free location search, no key | | Packaging | Dockerfile + docker-compose | `docker compose up` and you're running | ## Architecture ``` trip_plan/ β”œβ”€β”€ src/server/ # Express app β”‚ β”œβ”€β”€ index.js # bootstrap, static serving β”‚ β”œβ”€β”€ db.js # SQLite schema + connection β”‚ β”œβ”€β”€ auth.js # session middleware β”‚ β”œβ”€β”€ routes/ # auth, trips, entries, geocode β”‚ └── util/distance.js # haversine km calc β”œβ”€β”€ public/ # SPA served statically β”‚ β”œβ”€β”€ index.html β”‚ β”œβ”€β”€ css/styles.css β”‚ └── js/ # api client, views (trips, calendar, map, summary) β”œβ”€β”€ tests/ # API tests (node:test + supertest) β”œβ”€β”€ data/ # SQLite file (gitignored, volume-mounted) β”œβ”€β”€ docs/ # this file + API.md β”œβ”€β”€ Dockerfile └── docker-compose.yml ``` ### Data Model ``` users (id, username, password_hash, created_at) trips (id, name, start_date, end_date, owner_id, created_at) trip_members (trip_id, user_id, role) -- owner | editor entries (id, trip_id, date, type, title, details, start_time, end_time, location_name, lat, lng, sort_order) ``` Route + km are **derived**: located entries ordered by (date, sort_order) form the route; each consecutive pair is a leg with haversine distance. ### API (REST, JSON, under /api) - `POST /api/auth/account` (create token account) Β· `POST /api/auth/login` Β· `POST /api/auth/logout` Β· `GET/PATCH /api/auth/me` - `GET/POST /api/trips` Β· `GET/PATCH/DELETE /api/trips/:id` - `POST /api/trips/join` (by join code) Β· `POST /api/trips/:id/join-code` (regenerate) - `GET/POST /api/trips/:id/entries` Β· `PATCH/DELETE /api/entries/:id` - `GET /api/trips/:id/route` (legs + km + summary, computed server-side) - `GET /api/geocode?q=...` (Nominatim proxy) ## Running It ```bash # development npm install npm start # http://localhost:3000 # production docker compose up -d # builds image, mounts ./data for the SQLite file ``` ## Non-Goals (for now) - Real-time collaborative editing (last-write-wins is fine for this scale) - Driving-route distances via a routing engine (great-circle km first; OSRM could be added later) - Email invites / password reset (admin resets via CLI) ## Build Plan 1. **Scaffold** β€” repo, package.json, this doc, API contract. βœ… 2. **Swarm build** (parallel agents): backend API + DB Β· frontend SPA (calendar, map, summary) Β· Docker/infra + tests. 3. **Integrate & verify** β€” run tests, boot server, smoke-test in browser, build Docker image. 4. Initial commit.