Minimal full-stack implementation of the MVP spec:
- FastAPI backend for trip + participant + itinerary APIs
- Next.js + Tailwind frontend for create/join/generate/view flow
- Itinerary engine with preference aggregation, scoring, geo clustering, and 3 plan styles
backend/FastAPI API and itinerary generation logicfrontend/Next.js app router UI
cd backend
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000Optional LLM explanations:
- copy
backend/.env.exampletobackend/.env - set
DATABASE_URLfor Postgres/Supabase (or omit for local SQLiteplanner.db) - set
OPENAI_API_KEY(optional) - set
CORS_ALLOW_ORIGINS(comma-separated origins; defaults tohttp://localhost:3000) - set
GOOGLE_PLACES_API_KEYto pull real places/activities from Google Places (optional but recommended) - set
OPENAI_DESTINATION_RERANK_MODELto control AI destination rerank model (defaults toOPENAI_EXPLANATION_MODEL) - set
FRONTEND_BASE_URLfor share-link generation (defaults tohttp://localhost:3000)
cd backend
alembic upgrade headCreate a new migration after model changes:
cd backend
alembic revision --autogenerate -m "describe change"
alembic upgrade headcd frontend
npm install
cp .env.local.example .env.local
npm run devOpen http://localhost:3000.
POST /trip/create(returnsowner_tokenandjoin_code)POST /trip/{id}/join(requires headerX-Trip-Token)GET /trip/{id}(requires headerX-Trip-Token)POST /trip/{id}/generate_itinerary(requires headerX-Trip-Token)GET /trip/{id}/itinerary(requires headerX-Trip-Token)GET /trip/{id}/draft_slots(requires headerX-Trip-Token)GET /trip/{id}/planning_settings(requires headerX-Trip-Token)PUT /trip/{id}/planning_settings(requires headerX-Trip-Token)POST /trip/{id}/draft_plan(requires headerX-Trip-Token)GET /trip/{id}/draft_plan(requires headerX-Trip-Token)GET /trip/{id}/draft_validation(requires headerX-Trip-Token)POST /trip/{id}/share(requires headerX-Trip-Token)GET /share/{share_token}(public read-only shared plan)GET /analytics/summary
- Persistence now uses SQLAlchemy with
DATABASE_URL(Postgres/Supabase-ready). - If
DATABASE_URLis not set, backend falls back to local SQLite (backend/planner.db). - Activity retrieval uses Google Places when
GOOGLE_PLACES_API_KEYis set; otherwise it falls back to a curated static dataset. - Explanation layer uses OpenAI only if key is provided; otherwise deterministic summary text is returned.
cd backend
python -m pytest -q