traveloop.md

Traveloop.

Multi-city itineraries, budgets, and packing — shipped on a hackathon clock.

--- traveloop.yaml
project"Traveloop"
tagline"Multi-city itineraries, budgets, and packing — shipped on a hackathon clock."
role"hackathon · group of 4 · The Knights"
status"shipped"
timelineOdoo Hackathon · weekend
commits38
stackNext.js 16 · React 19 · TypeScript · Prisma · PostgreSQL · Tailwind · shadcn · NextAuth
infraNeonDB / Postgres 16 · Auth.js v5 · Vercel

01#tl;dr

Traveloop is a full-stack travel-planning workspace — multi-city itineraries, day-grouped activities, per-stop budgets with charts, packing checklists, journals, and a public community feed. Built end-to-end during the Odoo Hackathon with three teammates.

    • What: plan a trip end-to-end — stops, dates, budget, packing, notes — and share it
    • Hard part: shipping a real multi-domain app in a weekend with four people in flight
    • Why it matters: proves the team can carve a Next.js app cleanly and ship under pressure

02#the problem

Existing trip planners pick one lane. Itinerary apps don't track money. Budget apps don't know your trip has stops. Notes apps don't care about any of it. By the time you've stitched together three tools, you've stopped planning and started doing data entry.

Traveloop puts the lanes in one workspace, with a single trip object that owns its stops, activities, budget, packing, and journal. Same source of truth for the dashboard pie chart and the day-by-day itinerary view.

03#architecture

Server Components first, Server Actions for mutations. No separate /api/v1/* REST surface to maintain — every mutation is a typed 'use server' function with Zod validation and a safeAction() error envelope. RBAC enforced at the layout level so non-admins literally 404 on /admin.

fig. 01 · App Router · RSC pages · Server Actions · Prisma · Postgres

04#key decisions

01

Server Actionsvs.separate REST API

Every mutation lives in src/server/actions/* as a typed 'use server' function. No REST surface to keep in sync with the client, no parallel auth checks. Saved us probably a day of plumbing in a weekend build.

02

Zod schemas as single source of truthvs.duplicate client + server validation

Schemas in src/lib/validations/* are reused by React Hook Form on the client AND by safeAction() on the server. One file defines the shape; the form and the mutation can't drift apart.

03

safeAction() error envelopevs.throwing exceptions across boundaries

Server actions return { ok: true, data } | { ok: false, error }. The UI is if (!res.ok) toast.error(res.error) everywhere — one pattern, no try/catch forests in components.

04

RBAC at layout, not middleware

(app)/layout.tsx runs auth() once for the protected group. (app)/admin/page.tsx calls notFound() for non-admins so the route's existence is hidden. No middleware to debug, no edge-runtime gotchas.

05#walkthrough

01.landing
Traveloop landing page — full-bleed beach hero with handwritten headline
02.sign in
Traveloop sign-in screen with feature highlights on the left and login card on the right
03.dashboard
Traveloop dashboard — welcome stats and recent trips
04.budget breakdown
Trip budget view with per-stop pie and spend-vs-budget bar showing over-budget warning

06#the stack

    • frontend: Next.js 16, React 19, TS, Tailwind 4, shadcn/ui, Radix, lucide
    • forms + charts: React Hook Form + Zod, Recharts, sonner toasts
    • api: Server Actions + Route Handlers (image streaming + Auth.js callbacks)
    • auth: Auth.js v5 credentials provider, JWT sessions, Prisma adapter
    • data: Prisma 7 + PostgreSQL 16 — 12 tables, User > Trip > Stop > Activity
    • infra: Vercel + NeonDB serverless Postgres

07#what's next

    • AI-assisted itinerary generation roadmap
    • map view with Leaflet + OpenStreetMap
    • currency conversion in the expense ledger
    • offline-capable PWA shell
    • real-time collaboration on trips (Liveblocks or CRDT)
    • export trip as PDF