Skip to content

FrontMan-01/Traveloop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 

Repository files navigation

🌍 Traveloop

Personalized Travel Planning Made Easy

Plan smarter. Travel better. Share your journey.

React Node.js PostgreSQL Tailwind CSS


📌 Table of Contents


🧭 Overview

Traveloop is a full-stack, personalized travel planning platform built for the Odoo Hackathon. It transforms the way individuals plan and experience travel by offering an end-to-end tool that combines flexibility, interactivity, and community.

Users can dream, design, and organize multi-city trips — with automatic budget estimation, day-wise itinerary building, packing checklists, trip notes, and the ability to share plans with a community of travelers.

Problem Statement: Design and develop a complete travel planning application backed by a relational database, with dynamic UIs that adapt to each user's trip flow.


✨ Features

# Feature Description
1 Multi-city Itinerary Builder Add city stops, dates, and activities dynamically
2 Auto Budget Estimation Cost breakdown by transport, stay, meals, and activities
3 City & Activity Search Discover destinations and experiences
4 Visual Itinerary View Day-wise timeline with activity blocks and costs
5 Community Sharing Share trip plans publicly or copy others' itineraries
6 Packing Checklist Categorized checklist with packed/unpacked toggle
7 Trip Notes / Journal Per-stop notes for hotel info, reminders, contacts
8 Expense Invoice Downloadable PDF invoice with GST breakdown
9 Admin Analytics Charts for top cities, trip trends, user stats
10 User Profiles Editable profile with trip history

🛠 Tech Stack

Frontend

Technology Purpose
React 18 + Vite UI framework with fast HMR dev server
React Router DOM v6 Client-side routing and navigation
Tailwind CSS Utility-first responsive styling
Axios HTTP client for API calls
Recharts Budget pie, bar, and line charts
React Hook Form Form state management and validation
jsPDF + html2canvas PDF invoice generation and export
Zustand Lightweight global state management

Backend

Technology Purpose
Node.js + Express REST API server
Prisma ORM Type-safe database queries and migrations
PostgreSQL Relational database (required by problem statement)
bcryptjs Secure password hashing
jsonwebtoken JWT-based authentication
cors + dotenv Cross-origin support and environment config
multer File and image uploads

DevOps

Technology Purpose
Vercel Frontend deployment
Railway Backend + PostgreSQL deployment
GitHub Version control and collaboration

🏗 System Architecture

┌─────────────────────────────────────────────────────────────┐
│                    User (Browser / Mobile)                   │
└───────────────────────────┬─────────────────────────────────┘
                            │ HTTP Requests
┌───────────────────────────▼─────────────────────────────────┐
│              Frontend  —  React + Vite + Tailwind            │
│                                                              │
│   Pages          Components        State        API Layer    │
│   (14 screens)   (TripCard,        (Zustand /   (Axios +    │
│                   BudgetChart,      Context)     interceptor) │
│                   SearchBar…)                                │
└───────────────────────────┬─────────────────────────────────┘
                            │ REST API (JSON)
┌───────────────────────────▼─────────────────────────────────┐
│              Backend  —  Node.js + Express                   │
│                                                              │
│   Routes            Auth Middleware    Controllers           │
│   /auth /trips      JWT verify         Business logic        │
│   /stops /budget    bcrypt passwords   Budget calc           │
│   /activities       Role checks        Prisma queries        │
└───────────────────────────┬─────────────────────────────────┘
                            │ SQL via Prisma
┌───────────────────────────▼─────────────────────────────────┐
│              Database  —  PostgreSQL                         │
│                                                              │
│   users · trips · stops · activities · budgets               │
│   notes · checklist_items                                    │
└─────────────────────────────────────────────────────────────┘

🗄 Database Schema

// prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        String   @id @default(uuid())
  firstName String
  lastName  String
  email     String   @unique
  phone     String?
  city      String?
  country   String?
  password  String
  photo     String?
  isAdmin   Boolean  @default(false)
  createdAt DateTime @default(now())

  trips          Trip[]
  checklistItems ChecklistItem[]
}

model Trip {
  id          String   @id @default(uuid())
  name        String
  description String?
  coverPhoto  String?
  startDate   DateTime
  endDate     DateTime
  isPublic    Boolean  @default(false)
  status      String   @default("upcoming")
  createdAt   DateTime @default(now())

  userId  String
  user    User     @relation(fields: [userId], references: [id])
  stops   Stop[]
  notes   Note[]
  budgets Budget[]
}

model Stop {
  id         String   @id @default(uuid())
  cityName   String
  country    String?
  startDate  DateTime
  endDate    DateTime
  orderIndex Int      @default(0)

  tripId     String
  trip       Trip       @relation(fields: [tripId], references: [id], onDelete: Cascade)
  activities Activity[]
  notes      Note[]
}

model Activity {
  id          String  @id @default(uuid())
  name        String
  description String?
  type        String?
  cost        Float   @default(0)
  duration    String?
  timeSlot    String?

  stopId String
  stop   Stop   @relation(fields: [stopId], references: [id], onDelete: Cascade)
}

model Budget {
  id          String  @id @default(uuid())
  category    String
  amount      Float
  description String?

  tripId String
  trip   Trip   @relation(fields: [tripId], references: [id], onDelete: Cascade)
}

model Note {
  id        String   @id @default(uuid())
  content   String
  createdAt DateTime @default(now())

  tripId String?
  trip   Trip?   @relation(fields: [tripId], references: [id])
  stopId String?
  stop   Stop?   @relation(fields: [stopId], references: [id])
}

model ChecklistItem {
  id       String  @id @default(uuid())
  name     String
  category String
  isPacked Boolean @default(false)
  tripId   String

  userId String
  user   User   @relation(fields: [userId], references: [id])
}

Relationships summary:

  • One User → many Trips
  • One Trip → many Stops (cities)
  • One Stop → many Activity records
  • One Trip → many Budget items and Notes
  • One User → many ChecklistItems per trip

📁 Project Structure

traveloop/
│
├── client/                             # React frontend
│   ├── public/
│   └── src/
│       ├── pages/
│       │   ├── Login.jsx               # Screen 1
│       │   ├── Register.jsx            # Screen 2
│       │   ├── Dashboard.jsx           # Screen 3
│       │   ├── PlanTrip.jsx            # Screen 4
│       │   ├── ItineraryBuilder.jsx    # Screen 5 ⭐
│       │   ├── MyTrips.jsx             # Screen 6
│       │   ├── Profile.jsx             # Screen 7
│       │   ├── Search.jsx              # Screen 8 (city + activity)
│       │   ├── ItineraryView.jsx       # Screen 9
│       │   ├── Community.jsx           # Screen 10
│       │   ├── Checklist.jsx           # Screen 11
│       │   ├── AdminDashboard.jsx      # Screen 12
│       │   ├── Notes.jsx               # Screen 13
│       │   └── Invoice.jsx             # Screen 14
│       ├── components/
│       │   ├── Navbar.jsx
│       │   ├── TripCard.jsx
│       │   ├── BudgetChart.jsx
│       │   ├── SearchBar.jsx
│       │   └── ProtectedRoute.jsx
│       ├── context/
│       │   └── AuthContext.jsx
│       ├── api/
│       │   ├── index.js                # Axios instance + interceptor
│       │   ├── auth.js
│       │   ├── trips.js
│       │   ├── stops.js
│       │   └── activities.js
│       ├── App.jsx
│       └── main.jsx
│
├── server/                             # Express backend
│   ├── prisma/
│   │   └── schema.prisma
│   ├── src/
│   │   ├── routes/
│   │   │   ├── auth.routes.js
│   │   │   ├── trips.routes.js
│   │   │   ├── stops.routes.js
│   │   │   ├── activities.routes.js
│   │   │   ├── budget.routes.js
│   │   │   ├── checklist.routes.js
│   │   │   ├── notes.routes.js
│   │   │   └── admin.routes.js
│   │   ├── controllers/
│   │   │   ├── auth.controller.js
│   │   │   ├── trips.controller.js
│   │   │   ├── stops.controller.js
│   │   │   └── activities.controller.js
│   │   ├── middleware/
│   │   │   └── auth.middleware.js
│   │   └── app.js
│   ├── .env
│   └── package.json
│
└── README.md

🚀 Getting Started

Prerequisites

  • Node.js v18+
  • Git

1. Clone the Repository

git clone https://github.com/FrontMan-01/traveloop.git
cd traveloop

2. Backend Setup

The backend uses a local SQLite database by default, meaning no database installation is required!

cd backend

# Install dependencies
npm install

# Configure environment variables
cp .env.example .env

# Generate Prisma Client & push schema to local SQLite db
npx prisma generate
npx prisma db push

# Start the server
node app.js
# The API will run on http://localhost:5000

3. Frontend Setup

Open a new terminal window to keep the backend running:

cd frontend

# Install dependencies
npm install

# Start the development server
npm run dev
# The app will run on http://localhost:5173

🔐 Environment Variables

backend/.env

DATABASE_URL="file:./dev.db"
JWT_SECRET="your_super_secret_key_change_this"
PORT=5000

Note: The frontend is pre-configured to connect to the backend running on localhost:5000. No .env is required for the frontend.


📡 API Reference

Authentication

Method Endpoint Body Auth Description
POST /api/auth/register {firstName, lastName, email, password, phone?, city?, country?} Register new user
POST /api/auth/login {email, password} Login, returns JWT
GET /api/auth/me Get current user profile
PUT /api/auth/me {firstName, lastName, phone, city, country} Update profile

Trips

Method Endpoint Description Auth
GET /api/trips Get all trips for logged-in user
POST /api/trips Create new trip
GET /api/trips/public Get all public/community trips
GET /api/trips/:id Get single trip with all stops & activities
PUT /api/trips/:id Update trip details
DELETE /api/trips/:id Delete trip and all related data
PATCH /api/trips/:id/share Toggle isPublic flag

Stops

Method Endpoint Description Auth
POST /api/stops Add city stop to a trip
PUT /api/stops/:id Update stop city/dates
DELETE /api/stops/:id Remove stop

Activities

Method Endpoint Description Auth
GET /api/activities?stopId=X Get activities for a stop
POST /api/activities Add activity to a stop
PUT /api/activities/:id Update activity
DELETE /api/activities/:id Delete activity

Budget

Method Endpoint Description Auth
GET /api/budget/:tripId Get full budget breakdown for a trip
POST /api/budget Add budget item
DELETE /api/budget/:id Remove budget item

Checklist

Method Endpoint Description Auth
GET /api/checklist/:tripId Get all checklist items for a trip
POST /api/checklist Add item
PATCH /api/checklist/:id/toggle Toggle isPacked status
DELETE /api/checklist/:id Delete item

Notes

Method Endpoint Description Auth
GET /api/notes/:tripId Get all notes for a trip
POST /api/notes Add note (linked to trip or stop)
DELETE /api/notes/:id Delete note

Admin

Method Endpoint Description Auth
GET /api/admin/stats Total users, trips, top cities, activity trends ✅ Admin

Standard response format:

{ "success": true, "data": { ... } }
{ "success": false, "message": "Error description" }

🖥 Screens & Pages

Screen 1 — Login

Route: / Allows existing users to sign in with email and password. Validates credentials against the database, returns a JWT token stored in localStorage, and redirects to the dashboard.

Key elements: Email field, password field, login button, link to register, forgot password link.


Screen 2 — Registration

Route: /register New user sign-up with full profile info. Uses React Hook Form for validation (required fields, email format, password minimum length).

Key elements: First name, last name, email, phone, city, country, optional photo upload, register button.


Screen 3 — Dashboard / Home

Route: /dashboard The central hub after login. Shows a personalized welcome banner, regional destination cards for discovery, and the user's recent trips.

Key elements: Welcome message, 5 regional selection cards (Asia / Europe / Americas / Africa / Oceania), previous trips grid, "Plan a Trip" CTA.


Screen 4 — Plan New Trip

Route: /plan Form to create a new trip. The left side collects trip details; the right side shows suggested destinations the user can click to auto-fill the place field.

Key elements: Trip name, start date, destination search, end date, destination suggestion cards (2×3 grid), save button.


Screen 5 — Itinerary Builder ⭐

Route: /trips/:id/build The most important screen. Users build their trip stop by stop. Each section represents one city with a date range, budget, and activity description. Sections can be added dynamically and reordered.

// Dynamic section state pattern
const [sections, setSections] = useState([
  { id: 1, city: '', startDate: '', endDate: '', budget: '', activities: '' }
]);

const addSection = () =>
  setSections([...sections, { id: Date.now(), city: '', startDate: '',
    endDate: '', budget: '', activities: '' }]);

Key elements: Section blocks with city, date range, budget, activity textarea; "+ Add another Section" button; save/publish button.


Screen 6 — My Trips

Route: /trips Lists all trips owned by the logged-in user, grouped by status.

Key elements: Three sections — Ongoing, Upcoming, Completed. Trip cards showing name, date range, destination count, and View / Edit / Delete actions.


Screen 7 — User Profile

Route: /profile View and edit personal account information. Also shows a history of past trips.

Key elements: Avatar with initials, editable name/email/phone/city/country fields, save button, previous trips list with View buttons.


Screen 8 — Search (City + Activity)

Route: /search A single reusable search page used for both city discovery and activity search. Pass a type prop to switch modes.

Key elements: Search bar with debounce, filter and sort controls, result rows with name + details + "Add to Trip" button.


Screen 9 — Itinerary View

Route: /trips/:id/view Read-only, structured view of the complete trip plan. Grouped by day with two columns per row.

Key elements: Day labels (Day 1, Day 2…), Activity Name column, Cost/Expense column, budget summary panel, toggle between List and Calendar view.


Screen 10 — Community

Route: /community Discover trips shared publicly by other travelers. Enables copying a trip into your own account.

Key elements: List of public trips with author name, destinations, and dates. "Copy Trip" button to duplicate to user's account. Social share button for link copying.


Screen 11 — Packing Checklist

Route: /trips/:id/checklist Per-trip checklist organized by category. Items persist in the database so users can check things off on the go.

Key elements: Category sections (Clothing, Documents, Electronics, Medicines, Misc). Checkbox per item toggling isPacked. Add item input. "Reset Checklist" button.


Screen 12 — Admin Analytics Dashboard

Route: /admin Admin-only screen showing platform-wide usage statistics with interactive charts.

Key elements (using Recharts):

  • PieChart — budget breakdown by category
  • LineChart — trips created per month
  • BarChart — top 10 most visited cities
  • Data tables: Popular Trips, Popular Activities, User Engagement

Screen 13 — Trip Notes / Journal

Route: /trips/:id/notes Simple note-taking per trip stop. Useful for hotel check-in info, local contacts, and day-specific reminders.

Key elements: Note cards with stop name header, content, and timestamp. "Add Note" button. Delete per card. Sorted newest first.


Screen 14 — Expense Invoice

Route: /trips/:id/invoice A formatted expense invoice for the full trip, downloadable as a PDF.

// PDF export
import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';

const exportPDF = async () => {
  const canvas = await html2canvas(document.getElementById('invoice'));
  const pdf = new jsPDF('p', 'mm', 'a4');
  pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 0, 0, 210, 297);
  pdf.save(`traveloop-invoice-${tripId}.pdf`);
};

Key elements: Trip thumbnail and summary, invoice table (Category / Description / Qty / Unit Cost / Amount), subtotal + 18% GST + grand total, Download Invoice button, Export as PDF button.


📅 Build Timeline

Day Focus Screens
Day 1 Project setup, database schema, auth API, login/register UI 1, 2
Day 2 Trip CRUD API, dashboard, plan new trip, my trips 3, 4, 6
Day 3 Stops + activities API, itinerary builder, itinerary view 5, 9
Day 4 Profile, search, community sharing, checklist, notes 7, 8, 10, 11, 13
Day 5 Admin dashboard, invoice/PDF, polish, deploy, demo 12, 14

Day-wise checklist

Day 1

  • Initialize backend — npm init, install packages, configure Prisma
  • Write full database schema and run npx prisma db push
  • Build /api/auth/register and /api/auth/login with JWT
  • Initialize frontend — Vite + React + Tailwind
  • Build Screen 1: Login
  • Build Screen 2: Register
  • Set up AuthContext, ProtectedRoute, Axios instance
  • Test full auth flow end-to-end ✓

Day 2

  • Build full trips CRUD API routes
  • Build Screen 3: Dashboard with regional cards
  • Build Screen 4: Plan New Trip form
  • Build Screen 6: My Trips with status grouping
  • Connect frontend ↔ backend via Axios ✓

Day 3

  • Build stops and activities API routes
  • Build Screen 5: Itinerary Builder (dynamic sections — most time here)
  • Build Screen 9: Itinerary View (day-wise layout)
  • Test: create trip → add stops → view itinerary ✓

Day 4

  • Build Screen 7: User Profile with edit
  • Build Screen 8: Search (reuse for city + activity)
  • Build Screen 10: Community public feed + copy trip
  • Build Screen 11: Packing Checklist
  • Build Screen 13: Trip Notes ✓

Day 5

  • Build Screen 12: Admin Dashboard (Recharts)
  • Build Screen 14: Invoice with PDF export
  • Add loading spinners and error messages throughout
  • Mobile responsive testing and fixes
  • Deploy backend to Railway, frontend to Vercel
  • Full demo run-through ✓

☁️ Deployment

Backend — Railway

# Install CLI
npm install -g @railway/cli

# Login and initialize
railway login
cd server
railway init

# Add PostgreSQL plugin from Railway dashboard
# Copy the DATABASE_URL into your environment variables

# Deploy
railway up

Set these environment variables in the Railway dashboard:

  • DATABASE_URL — auto-provided by Railway PostgreSQL plugin
  • JWT_SECRET — your secret key
  • CLIENT_URL — your Vercel frontend URL
  • PORT5000

Frontend — Vercel

npm install -g vercel
cd client
vercel --prod

Set in Vercel dashboard under Environment Variables:

  • VITE_API_URL — your Railway backend URL (e.g. https://traveloop-api.railway.app/api)

Update CORS for production

// server/src/app.js
app.use(cors({
  origin: process.env.CLIENT_URL,
  credentials: true
}));

👥 Team

Name Role
Full Stack Lead
Frontend Developer
Backend Developer
UI/UX & Database Design

Built with ❤️ for the Odoo Hackathon

Traveloop — Making travel planning as exciting as the trip itself.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages