Start

import React, { useMemo, useState, useEffect } from "react"; import { motion } from "framer-motion"; import { Plane, Calendar as CalendarIcon, Users, IndianRupee, Bike, Car, Bus, Train, PlaneTakeoff, MapPin, Hotel, UtensilsCrossed, Wallet, Cog, Search, CreditCard, CheckCircle, } from "lucide-react"; /** * SMART TRAVEL PLANNER – One Page App * ----------------------------------------------------------- * - Fully responsive (mobile-first), clean + subtle 3D (neumorphic) look * - Form: Destination, Start/End date (with native calendar), Travellers, Budget (optional), Transport (dropdown w/ icons) * - Currency selector (defaults to INR), conversion across UI; user can change currency * - Generate Itinerary button → shows results sections: Itinerary, Hotels, Places, Vehicle, Cost, Restaurants * - Booking block with mock live search + payment (demo). Replace adapters below with real APIs when ready. * - NOTE: This demo uses mocked generators so it works without keys. API adapter placeholders are included. * ----------------------------------------------------------- * HOW TO DEPLOY QUICKLY * - Copy this file into a Vite/Next.js React project and set it as a page/component. * - TailwindCSS is assumed. If not installed, you can still get decent styling with the base class names, * or replace classes with your CSS. */ // -------------------- Utility: Date helpers -------------------- const dayDiff = (a, b) => { const ms = Math.max(0, new Date(b).setHours(0, 0, 0, 0) - new Date(a).setHours(0, 0, 0, 0)); return Math.floor(ms / (1000 * 60 * 60 * 24)) + 1; // inclusive }; const enumerateDates = (start, end) => { const days = []; let d = new Date(start); const last = new Date(end); while (d <= last) { days.push(new Date(d)); d.setDate(d.getDate() + 1); } return days; }; // -------------------- Currency: simple store + adapter hooks -------------------- const DEFAULT_CCY = "INR"; // Base: INR; rough sample rates; you should refresh these with a live FX API (see refreshRates function). const DEFAULT_RATES = { INR: 1, USD: 0.012, EUR: 0.011, GBP: 0.0095, JPY: 1.9, AED: 0.044, AUD: 0.018, CAD: 0.017, SGD: 0.016, }; const CURRENCIES = Object.keys(DEFAULT_RATES); function useCurrency() { const [currency, setCurrency] = useState(DEFAULT_CCY); const [rates, setRates] = useState(DEFAULT_RATES); const [refreshing, setRefreshing] = useState(false); async function refreshRates() { // Replace with your live FX API (e.g., exchangerate.host, currencyapi, Fixer, etc.) // Keep base as INR for consistency. try { setRefreshing(true); // const res = await fetch("/api/fx?base=INR"); // const json = await res.json(); // setRates(json.rates); await new Promise((r) => setTimeout(r, 600)); } catch (e) { console.warn("FX refresh failed; using fallback rates.", e); } finally { setRefreshing(false); } } function convertINR(amountInINR, to = currency) { const rate = rates[to] ?? 1; return amountInINR * rate; } return { currency, setCurrency, rates, setRates, convertINR, refreshRates, refreshing }; } // -------------------- Mock Data Generators (deterministic-ish) -------------------- function seededRandom(seed) { // xorshift32-ish for deterministic mocks let x = 0; for (let i = 0; i < seed.length; i++) x ^= seed.charCodeAt(i) << ((i % 4) * 8); return () => { x ^= x << 13; x ^= x >>> 17; x ^= x << 5; return (x >>> 0) / 4294967296; }; } function guessRegionCostFactor(destination) { const d = destination.toLowerCase(); if (/switzerland|iceland|norway|japan|singapore|uae|uk|london|paris|new york|sydney/.test(d)) return 3.0; // expensive if (/india|vietnam|nepal|indonesia|thailand|turkey|mexico|portugal/.test(d)) return 1.0; // budget return 2.0; // mid } function createHotels(dest, count = 6) { const rnd = seededRandom(dest + "hotels"); return Array.from({ length: count }).map((_, i) => { const price = 1500 + Math.floor(rnd() * 7000); return { id: `h${i}`, name: `${dest} Grand ${i + 1}`, rating: (3 + rnd() * 2).toFixed(1), distanceKm: (0.5 + rnd() * 8).toFixed(1), pricePerNightINR: price, amenities: ["Wi‑Fi", "Breakfast", rnd() > 0.5 ? "Pool" : "Gym"], }; }); } function createPlaces(dest, count = 6) { const rnd = seededRandom(dest + "places"); const samples = [ "Historic Fort", "City Museum", "Botanical Garden", "Scenic Viewpoint", "Art District", "Riverside Walk", "Night Market", "National Park", ]; return Array.from({ length: count }).map((_, i) => ({ id: `p${i}`, name: `${dest} ${samples[Math.floor(rnd() * samples.length)]}`, bestTime: rnd() > 0.5 ? "Morning" : "Evening", ticketINR: Math.floor(100 + rnd() * 800), note: rnd() > 0.6 ? "Guided tours available" : "Free entry on Sundays", })); } function createRestaurants(dest, count = 6) { const rnd = seededRandom(dest + "food"); const cuisines = ["Indian", "Italian", "Asian", "Mexican", "Seafood", "Vegan", "BBQ", "Bakery"]; return Array.from({ length: count }).map((_, i) => ({ id: `r${i}`, name: `${dest} ${cuisines[Math.floor(rnd() * cuisines.length)]} Kitchen`, rating: (3 + rnd() * 2).toFixed(1), avgMealINR: Math.floor(250 + rnd() * 1200), popular: rnd() > 0.5 ? "Bestseller" : "Family friendly", })); } function createVehicles(dest, mode = "flight") { const rnd = seededRandom(dest + mode); const rows = []; const n = 5; for (let i = 0; i < n; i++) { const base = 900 + rnd() * 4500; rows.push({ id: `${mode}-${i}`, name: mode === "flight" ? `${dest} Airways ${100 + i}` : mode === "train" ? `${dest} Express ${200 + i}` : mode === "bus" ? `${dest} Coach ${300 + i}` : mode === "car" ? `Self‑Drive Hatchback ${i + 1}` : `Touring Bike ${i + 1}`, depart: `${8 + i}:00`, arrive: `${10 + i}:30`, pricePerPersonINR: Math.floor(base * (mode === "flight" ? 2.2 : mode === "train" ? 0.6 : mode === "bus" ? 0.5 : 0.4)), refundable: rnd() > 0.5, }); } return rows; } function estimateCosts({ destination, startDate, endDate, travellers, mode }) { const nights = Math.max(1, dayDiff(startDate, endDate) - 1); const factor = guessRegionCostFactor(destination); const hotelPerNightINR = 2500 * factor; // per room per night (assume 2 per room) const rooms = Math.ceil(travellers / 2); const hotelTotal = hotelPerNightINR * rooms * nights; const foodPerPersonPerDayINR = 900 * factor; const localTransportPerDayINR = 600 * factor; const sightseeingPerPersonINR = 800 * factor; const vehicleOptions = createVehicles(destination, mode); const transportPerPersonINR = vehicleOptions[1]?.pricePerPersonINR ?? 2000; const persons = travellers; const stayFoodLocal = nights * (foodPerPersonPerDayINR * persons + localTransportPerDayINR); const sightseeing = sightseeingPerPersonINR * persons; const toFrom = transportPerPersonINR * persons; // one‑way approx. (simplified) const subtotal = hotelTotal + stayFoodLocal + sightseeing + toFrom; const contingency = 0.1 * subtotal; const total = Math.round(subtotal + contingency); return { nights, rooms, hotelTotal: Math.round(hotelTotal), stayFoodLocal: Math.round(stayFoodLocal), sightseeing: Math.round(sightseeing), toFrom: Math.round(toFrom), contingency: Math.round(contingency), total }; } // -------------------- Icons for mode -------------------- const modeConfig = { bike: { label: "Bike", Icon: Bike }, car: { label: "Car", Icon: Car }, bus: { label: "Bus", Icon: Bus }, train: { label: "Train", Icon: Train }, flight: { label: "Flight", Icon: PlaneTakeoff }, }; // -------------------- UI Helpers -------------------- function Card({ children, className = "" }) { return ( {children} ); } function SectionTitle({ icon: Icon, title, right }) { return (
{Icon && }

{title}

{right}
); } // -------------------- Main Component -------------------- export default function SmartTravelPlanner() { const [destination, setDestination] = useState(""); const today = new Date().toISOString().slice(0, 10); const [startDate, setStartDate] = useState(today); const [endDate, setEndDate] = useState(today); const [travellers, setTravellers] = useState(1); const [budget, setBudget] = useState(""); const [mode, setMode] = useState("flight"); const [generated, setGenerated] = useState(false); const [activeTab, setActiveTab] = useState("Itinerary"); const [booking, setBooking] = useState(null); // {type, item} const { currency, setCurrency, convertINR, refreshRates, refreshing } = useCurrency(); const hotels = useMemo(() => (destination ? createHotels(destination) : []), [destination]); const places = useMemo(() => (destination ? createPlaces(destination) : []), [destination]); const restaurants = useMemo(() => (destination ? createRestaurants(destination) : []), [destination]); const vehicles = useMemo(() => (destination ? createVehicles(destination, mode) : []), [destination, mode]); const costs = useMemo( () => (destination ? estimateCosts({ destination, startDate, endDate, travellers, mode }) : null), [destination, startDate, endDate, travellers, mode] ); // Save last inputs useEffect(() => { const saved = localStorage.getItem("stp_state"); if (saved) { try { const j = JSON.parse(saved); setDestination(j.destination || ""); setStartDate(j.startDate || today); setEndDate(j.endDate || today); setTravellers(j.travellers || 1); setBudget(j.budget || ""); setMode(j.mode || "flight"); setCurrency(j.currency || DEFAULT_CCY); } catch {} } // eslint-disable-next-line }, []); useEffect(() => { const state = { destination, startDate, endDate, travellers, budget, mode, currency }; localStorage.setItem("stp_state", JSON.stringify(state)); }, [destination, startDate, endDate, travellers, budget, mode, currency]); const days = useMemo(() => (destination ? enumerateDates(startDate, endDate) : []), [destination, startDate, endDate]); function handleGenerate() { setGenerated(true); setActiveTab("Itinerary"); } function currencyFmtINRToCurrent(inr) { const value = convertINR(inr); return new Intl.NumberFormat(undefined, { style: "currency", currency }).format(value); } function Badge({ children }) { return {children}; } function ModeDropdown() { return (
{React.createElement(modeConfig[mode].Icon, { className: "w-5 h-5" })}
); } return (
{/* Header */}

Smart Travel Planner

AI‑powered trip planning made easy

{/* Currency */}
{/* Hero + Form */}

Plan your trip

Clean, professional UI • Subtle 3D design • Fully responsive
setDestination(e.target.value)} />
setStartDate(e.target.value)} />
setEndDate(e.target.value)} />
setTravellers(Math.max(1, Number(e.target.value || 1)))} />
setBudget(e.target.value)} />
{Object.entries(modeConfig).map(([key, { Icon }]) => (
{key}
))}
Generate Itinerary
{!destination && (

Enter a destination to continue

)}
{/* Results */} {generated && destination && (
{[ "Itinerary", "Hotels", "Places", "Vehicle", "Cost", "Restaurants", "Booking", ].map((tab) => ( ))}
{/* Tab content */} {activeTab === "Itinerary" && ( {travellers} travellers} />
{days.map((d, i) => (
Day {i + 1}
{d.toDateString()}
Explore {destination} • Suggested: {places[i % places.length]?.name || "City walk"}
))}
)} {activeTab === "Hotels" && (
{hotels.map((h) => (
{h.name}
{h.distanceKm} km from center • ⭐{h.rating}
Amenities: {h.amenities.join(", ")}
{currencyFmtINRToCurrent(h.pricePerNightINR)} / night
))}
)} {activeTab === "Places" && (
{places.map((p) => (
{p.name}
Best time: {p.bestTime}
Ticket: {currencyFmtINRToCurrent(p.ticketINR)}
{p.note}
))}
)} {activeTab === "Vehicle" && (
{vehicles.map((v) => (
{v.name}
Depart {v.depart} • Arrive {v.arrive}
{currencyFmtINRToCurrent(v.pricePerPersonINR)} / person
{v.refundable ? "Refundable" : "Non‑refundable"}
))}
)} {activeTab === "Cost" && costs && ( {costs.nights} nights} />
Hotel ({costs.rooms} room{costs.rooms>1?"s":""})
{currencyFmtINRToCurrent(costs.hotelTotal)}
Food + Local transport
{currencyFmtINRToCurrent(costs.stayFoodLocal)}
Sightseeing
{currencyFmtINRToCurrent(costs.sightseeing)}
To/From ({travellers} ppl)
{currencyFmtINRToCurrent(costs.toFrom)}
Contingency (10%)
{currencyFmtINRToCurrent(costs.contingency)}
Total (all travellers)
{currencyFmtINRToCurrent(costs.total)}
{budget && (
Budget: {currencyFmtINRToCurrent(Number(budget))} • {Number(budget) >= costs.total ? ( Within budget ) : ( Over budget )}
)}
)} {activeTab === "Restaurants" && (
{restaurants.map((r) => (
{r.name}
⭐{r.rating} • {r.popular}
Avg meal: {currencyFmtINRToCurrent(r.avgMealINR)}
))}
)} {activeTab === "Booking" && (
Hotels
Connect to Booking, Expedia, or your OTA provider via API.
Trains / Flights / Buses
Integrate Amadeus, Skyscanner, Kiwi, or rail APIs for live fares.
Payments
Hook this to Razorpay/Stripe/Paytm/etc. in production.
)}
)}
{/* Booking modal (demo) */} {booking && (
Booking preview
Type: {booking.type}
{JSON.stringify(booking.item, null, 2)}
)} {/* Footer */}

Demo data shown for worldwide destinations. Currency defaults to INR and can be changed. Replace the adapter stubs with live providers for real‑time hotels, flights, trains, and payments.

); }

Comments

Popular posts from this blog

Travel Planner

TP

Travel planner 2