Odoo X LDCE Hackathon

Project overview

GlobeTrotter

From dream to departure — a collaborative travel planner for multi-city trips, budgets, and group coordination.

GlobeTrotter is a full-stack web application that helps travelers discover destinations, build day-by-day itineraries, track expenses, invite collaborators, and share trips others can copy. It was built for the Odoo Hackathon 2026 and ships as a single repository: Next.js frontend, REST API, authentication, authorization, and a PostgreSQL data layer.

Next.js React TypeScript PostgreSQL Bun


Why GlobeTrotter?

Planning a group trip usually means juggling spreadsheets, chat threads, maps, and expense apps. GlobeTrotter brings discovery, itinerary building, budgeting, collaboration, and sharing into one place.

| Pain point | GlobeTrotter answer | | --- | --- | | Ideas scattered across tools | Curated catalog of cities and activities with search and filters | | Hard to plan multi-city routes | Ordered stops, auto-generated days, drag-and-drop scheduling | | Budget surprises | Category budgets, expense logging, and planned-vs-actual charts | | One person does all the work | Trip roles (owner, editor, viewer) with server-enforced access | | Great trips stay private | Public trips and share links; others can fork into their own copy |


Quick start

git clone https://github.com/uvpatel/odoo-ldce.git
cd odoo-ldce
bun install
cp .env.example .env
# Edit .env — set DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL, NEXT_PUBLIC_APP_URL
bun run db:migrate
bun run db:seed
bun run dev

Open http://localhost:3000. Create an account at /sign-up for local testing — seeded demo users do not include passwords.


Contents


Features

Discover

  • Browse and search a curated catalog of countries, cities, and activities
  • Filter activities by city or category; view duration, average cost, and ratings
  • Save destinations to a personal shortlist
  • Explore public trips for inspiration

Plan

  • Create trips with dates, lifecycle status, visibility, currency, and overall budget
  • Build ordered multi-city routes with arrival and departure dates
  • Organize days with activities, transport, accommodation, meals, or custom entries
  • Reorder stops and itinerary items with drag-and-drop
  • Switch between overview, daily itinerary, and calendar views

Budget

  • Set category budgets (transport, accommodation, activities, food, shopping, other)
  • Log estimated or confirmed expenses and record who paid
  • Compare planned budgets with actual spending via summaries and charts

Collaborate and share

  • Invite members as owner, editor, or viewer
  • Enforce access rules for editing, member management, and budgets
  • Generate revocable public share links with optional expiration
  • Copy a public or shared itinerary into a new personal trip

Accounts and administration

  • Email/password sign-in plus optional Google and GitHub OAuth
  • Password recovery and profile, preference, privacy, and account settings
  • Light, dark, and system themes
  • Admin portal for analytics and user, trip, city, and activity management

Tech stack

| Layer | Technologies | | --- | --- | | Application | Next.js 16 (App Router), React 19, TypeScript | | UI | Tailwind CSS 4, shadcn/ui, Base UI, Lucide & Tabler icons | | Client state | TanStack React Query | | Forms | React Hook Form, Zod | | Database | PostgreSQL (Neon serverless), Drizzle ORM, Drizzle Kit | | Auth | Better Auth with Drizzle adapter | | Interactions | dnd-kit, Motion, Recharts | | Tooling | Bun, ESLint 9 |


Architecture

GlobeTrotter follows a layered design. UI components call feature-level API clients and React Query hooks. Route handlers validate input and establish the session, then delegate to services. Services enforce business rules; repositories hold Drizzle queries.

flowchart LR
    UI[Pages & React components] --> Query[Feature hooks & React Query]
    Query --> API[Next.js route handlers]
    API --> Auth[Better Auth & access checks]
    API --> Service[Domain services]
    Service --> Repo[Repositories]
    Repo --> ORM[Drizzle ORM]
    ORM --> DB[(PostgreSQL / Neon)]

Key patterns

  • Server-rendered layouts protect private route groups; signed-out users are redirected to /signin
  • React Query caches reads; mutations invalidate related query keys
  • Trip access checks are centralized so itineraries, budgets, members, and sharing share one policy

Data model

Schema modules live under src/db/schema:

| Domain | Tables | Purpose | | --- | --- | --- | | Auth | user, session, account, verification | Identities, sessions, OAuth providers, tokens | | Travel | trips, trip_members, trip_stops, trip_days, itinerary_items | Journeys, collaborators, routes, daily plans | | Catalog | countries, cities, activity_categories, activities | Discoverable destinations and activities | | Budget | trip_budgets, expenses | Category allocations and costs | | Social | saved_destinations, trip_shares | Bookmarks and token-based sharing | | User | user_preferences | Locale, currency, timezone, theme, notifications |

Core enums

| Type | Values | | --- | --- | | Trip lifecycle | draft, planned, ongoing, completed, cancelled | | Visibility | private, friends, public | | Trip membership | owner, editor, viewer | | Itinerary item | activity, transport, accommodation, meal, custom | | Expense category | transport, accommodation, activity, food, shopping, other |

SQL migrations and snapshots are committed under drizzle/.


Getting started

Prerequisites

  • Bun 1.3+ (repo pins bun@1.3.14)
  • PostgreSQL — local or hosted (e.g. Neon)
  • Node.js 20+ if your deployment environment requires it

Installation

  1. Clone and install dependencies

    bun install
    cp .env.example .env
    
  2. Configure environment — see Environment variables. At minimum set DATABASE_URL, BETTER_AUTH_URL, and NEXT_PUBLIC_APP_URL. Use a strong BETTER_AUTH_SECRET in production.

  3. Prepare the database

    bun run db:migrate
    bun run db:seed
    

    The seed is idempotent: it adds countries, cities, activity categories, activities, and demo travel data. Seeded user rows (e.g. admin@globetrotter.com) are for relational demo data only — sign up through the app for a working login.

  4. Run the dev server

    bun run dev
    

    Visit http://localhost:3000. Marketing and explore pages are public; dashboard and trip pages require authentication.


Environment variables

| Variable | Required | Description | | --- | --- | --- | | DATABASE_URL | Yes | PostgreSQL connection string | | BETTER_AUTH_SECRET | Production | Auth signing secret (32+ random characters) | | BETTER_AUTH_URL | Yes | Canonical app origin, e.g. http://localhost:3000 | | NEXT_PUBLIC_APP_URL | Yes | Public URL for metadata and links | | GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | No | Google OAuth (both required to enable) | | GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET | No | GitHub OAuth (both required to enable) | | RESEND_API_KEY | For email | Password reset and email-change delivery | | EMAIL_FROM | For email | Verified sender address | | GEMINI_API_KEY | No | Reserved for future AI features |

Never commit .env or production secrets. Auth URLs must match your deployed origin. On Vercel, Better Auth also reads VERCEL_URL.


Scripts

| Command | Description | | --- | --- | | bun run dev | Start the development server | | bun run build | Production build | | bun run start | Serve a production build | | bun run lint | Run ESLint | | bun run db:generate | Generate a migration after schema changes | | bun run db:migrate | Apply pending migrations | | bun run db:seed | Seed catalog and demo data |

scripts/reset-dev-db.ts truncates and reseeds a development database. It is not exposed as an npm script — review the file and use only on disposable databases.


Routes and API

Next.js route groups (e.g. (dashboard)) organize layouts but do not appear in URLs.

Pages

| Area | Routes | | --- | --- | | Marketing | /, /explore, /privacy, /terms | | Auth | /signin, /signup, /sign-in, /sign-up, /forgot-password, /reset-password | | Dashboard | /dashboard, /budget, /reports, /saved | | Discovery | /discover, /discover/cities, /discover/activities, detail pages | | Trips | /trips, /trips/new, /trips/[tripId] (+ itinerary, calendar, budget, members, share, settings) | | Settings | /settings, /settings/profile, /settings/preferences, /settings/privacy, /settings/account | | Sharing | /shared/[token] | | Admin | /admin, /admin/analytics, /admin/users, /admin/trips, /admin/cities, /admin/activities |

Parallel and intercepted routes support trip creation and itinerary detail modals without losing surrounding context.

REST API

Handlers under src/app/api return JSON.

| Resource | Endpoints | Capabilities | | --- | --- | --- | | Auth | /api/auth/[...all] | Registration, sessions, OAuth, account flows | | User | /api/users/me, .../preferences | Profile and preferences | | Dashboard | /api/dashboard | Aggregated trips, budgets, recommendations | | Catalog | /api/cities, /api/activities | Search, list, detail | | Saved | /api/saved-destinations | List, toggle, remove bookmarks | | Trips | /api/trips, /api/trips/[tripId] | CRUD (soft delete) | | Itinerary | .../stops, .../days, .../itinerary | Manage and reorder route & schedule | | Finance | .../budget, .../expenses | Budgets and expenses | | Members | .../members | Invite, change roles, remove | | Sharing | .../share, /api/shared/[token] | Share links and public resolution | | Public | /api/public/trips, .../copy | Browse and fork public trips | | Admin | /api/admin/* | Analytics and admin listings |

Request/response shapes use Zod schemas in src/lib/validation and pagination types in src/types/pagination.ts.


Authentication and authorization

AuthenticationBetter Auth stores users, sessions, linked accounts, and verification tokens in PostgreSQL. Email/password is always enabled; Google and GitHub activate when both client ID and secret are set. Sessions last 7 days, refresh daily, and use a 5-minute cookie cache.

Authorization has two layers:

  1. Platform rolesemployee, manager, hr, admin, super_admin control app-wide access. New registrations default to employee; role and status cannot be set from the client.
  2. Trip rolesowner, editor, viewer scope access to a single trip. Owners manage the trip and members; editors change content and budgets; viewers are read-only. Platform admins can manage any trip.

Protected layouts require an active session. Suspended or inactive users are sent to /unauthorized. Public trips and valid share tokens allow read access without membership; all mutations re-check permissions on the server.


Project structure

.
├── drizzle/                 # Migrations and schema snapshots
├── public/                  # Static assets
├── scripts/                 # Dev database utilities
├── src/
│   ├── app/                 # Pages, layouts, metadata, API routes
│   ├── components/          # Shared UI and app components
│   ├── config/              # Site config, navigation, permissions
│   ├── constants/           # Sidebar and static UI data
│   ├── db/                  # Client, schema, relations, seed
│   ├── features/            # Per-feature API clients, hooks, components
│   ├── hooks/               # Reusable React hooks
│   ├── lib/                 # Auth, validation, formatting, utilities
│   ├── providers/           # React Query and theme providers
│   ├── server/
│   │   ├── repositories/    # Drizzle data access
│   │   └── services/        # Business logic and authorization
│   └── types/               # Shared TypeScript types
├── drizzle.config.ts
├── next.config.ts
└── package.json

The @/* path alias maps to src/*.


Development and deployment

Before opening a PR

bun run lint
bun run build

Schema changes

  1. Edit modules under src/db/schema
  2. Run bun run db:generate and review SQL in drizzle/
  3. Apply with bun run db:migrate on a dev database
  4. Update the seed if new reference data is needed

There is no automated test suite yet — rely on lint, production build, and manual checks including authorization boundaries.

Production deployment

  1. Provision PostgreSQL and set production env vars
  2. Run migrations against the production database
  3. Build and deploy the Next.js app (e.g. Vercel)
  4. Configure OAuth callback URLs and a verified Resend sender when using those features

Do not run scripts/reset-dev-db.ts against production.


Team

Built for the Odoo Hackathon 2026 by Urvil Patel and team.


GlobeTrotter — making group travel planning coherent from discovery through departure.

Build with love by Urvil Patel