Travel Itinerary Generator

Travel Itinerary Generator

Plan your perfect trip and estimate the budget.

Destination: Tokyo, Japan

Dates: N/A

Estimated Total Cost: $0.00

No activities planned yet. Use the Planner tab to add activities.

'; return; } // 3. Render Daily Cards Object.keys(groupedByDay).sort((a, b) => a - b).forEach(day => { const dayNum = parseInt(day); const activities = groupedByDay[day]; const dayCard = document.createElement('div'); dayCard.className = 'tig-daily-card bg-white p-4 mb-4 rounded-lg shadow-md border-l-4 border-blue-400'; let activityListHtml = ''; // Sort activities by time activities.sort((a, b) => { const timeA = a.time || '23:59'; const timeB = b.time || '23:59'; return timeA.localeCompare(timeB); }); activities.forEach(activity => { const color = { 'Meal': 'text-orange-500', 'Sightseeing': 'text-purple-500', 'Activity': 'text-green-500', 'Transport': 'text-blue-500' }[activity.type] || 'text-gray-500'; activityListHtml += `
${activity.time || 'N/A'} ${escapeHtml(activity.description)} (${activity.type})
$${Number(activity.cost).toFixed(2)}
`; }); dayCard.innerHTML = `

Day ${dayNum}

${activityListHtml}
`; logContainer.appendChild(dayCard); }); } // --- Interaction Functions --- function tigAddActivity() { const day = parseInt(document.getElementById('tig-in-day').value); const time = document.getElementById('tig-in-time').value; const cost = parseFloat(document.getElementById('tig-in-cost').value); const type = document.getElementById('tig-in-type').value; const desc = document.getElementById('tig-in-desc').value; if (isNaN(day) || day < 1 || !desc || isNaN(cost)) { alert("Please ensure Day Number (>=1), Description, and valid Cost are entered."); return; } const newActivity = { id: Date.now(), day: day, time: time, type: type, description: desc, cost: cost, notes: '' // Keeping notes for future expansion }; tripData.itinerary.push(newActivity); // Clear inputs (keep day number) document.getElementById('tig-in-time').value = ''; document.getElementById('tig-in-cost').value = '0.00'; document.getElementById('tig-in-desc').value = ''; tigRenderItinerary(); tigSwitchTab('view'); } window.tigDeleteActivity = function(id) { if(confirm("Are you sure you want to remove this activity?")) { tripData.itinerary = tripData.itinerary.filter(a => a.id !== id); tigRenderItinerary(); } } window.tigSwitchTab = function(tabName) { document.getElementById('tab-content-planner').classList.toggle('hidden', tabName !== 'planner'); document.getElementById('tab-content-view').classList.toggle('hidden', tabName !== 'view'); document.getElementById('tab-planner').classList.toggle('active', tabName === 'planner'); document.getElementById('tab-view').classList.toggle('active', tabName === 'view'); // Always re-render when switching (to catch detail updates) tigRenderItinerary(); } // --- Gemini API Generator --- // Function to simulate asynchronous API call with exponential backoff async function fetchWithRetry(url, options, maxRetries = 5) { for (let i = 0; i < maxRetries; i++) { try { const response = await fetch(url, options); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response; } catch (error) { if (i === maxRetries - 1) throw error; const delay = Math.pow(2, i) * 1000 + Math.random() * 1000; await new Promise(resolve => setTimeout(resolve, delay)); } } } async function tigGenerateItinerary() { const destination = document.getElementById('tig-destination').value; const startDay = new Date(document.getElementById('tig-start-date').value); const endDay = new Date(document.getElementById('tig-end-date').value); const duration = tigCalculateDuration(); const days = Math.min(duration, 3); // Limit generation to 3 days for complexity/cost reasons if (!destination || duration < 1) { alert("Please enter a valid Destination and Start/End Dates."); return; } const loadingMsg = document.getElementById('tig-loading-msg'); const generateBtn = document.getElementById('tig-generate-btn'); loadingMsg.classList.remove('hidden'); generateBtn.disabled = true; try { const systemPrompt = "You are a world-class, creative travel planner. Generate a fun and realistic 3-day travel itinerary for the specified destination. Include a unique activity, meal suggestion, and transportation method for each time slot (Morning, Afternoon, Evening). The response MUST be a JSON array matching the provided schema."; const userQuery = `Generate a ${days}-day itinerary for ${destination}. Ensure the itinerary is focused on general tourism and local experiences.`; const apiKey = ""; const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`; const payload = { contents: [{ parts: [{ text: userQuery }] }], systemInstruction: { parts: [{ text: systemPrompt }] }, generationConfig: { responseMimeType: "application/json", responseSchema: { type: "ARRAY", items: { type: "OBJECT", properties: { day: { type: "NUMBER", description: "The day number, starting from 1." }, activities: { type: "ARRAY", items: { type: "OBJECT", properties: { time: { type: "STRING", description: "The time of day (e.g., Morning, 9:00 AM, Lunch)." }, activity: { type: "STRING", description: "A detailed description of the activity/meal." }, costEstimate: { type: "NUMBER", description: "Estimated cost in USD." }, activityType: { type: "STRING", description: "Type: Sightseeing, Meal, Transport, or Activity." } }, required: ["time", "activity", "costEstimate", "activityType"] } } } } } } }; const response = await fetchWithRetry(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const result = await response.json(); const jsonText = result.candidates?.[0]?.content?.parts?.[0]?.text; if (jsonText) { const generatedItinerary = JSON.parse(jsonText); // Clear existing plan tripData.itinerary = []; // Process and merge generated data let currentId = Date.now(); generatedItinerary.forEach(dayPlan => { dayPlan.activities.forEach(activity => { const newActivity = { id: currentId++, day: dayPlan.day, time: activity.time.split('-')[0].trim(), // Simplify time string type: activity.activityType || 'Activity', description: activity.activity, cost: activity.costEstimate || 0, notes: '' }; tripData.itinerary.push(newActivity); }); }); alert(`Successfully generated a ${generatedItinerary.length}-day itinerary for ${destination}!`); tigRenderItinerary(); tigSwitchTab('view'); } else { console.error("Gemini response structure error:", result); alert("Failed to generate itinerary. Please try again or check console for details."); } } catch (error) { console.error("API Error:", error); alert("An error occurred during itinerary generation. Please check your inputs."); } finally { loadingMsg.classList.add('hidden'); generateBtn.disabled = false; } } // --- PDF Download --- window.tigDownloadPDF = function() { const element = document.getElementById('tig-print-area'); const destination = document.getElementById('tig-destination').value || 'Travel_Plan'; document.body.classList.add('tig-generating-pdf'); const opt = { margin: [0.5, 0.5], filename: `${destination.replace(/\s+/g, '_')}_Itinerary.pdf`, image: { type: 'jpeg', quality: 0.98 }, html2canvas: { scale: 2, useCORS: true }, jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' } }; html2pdf().set(opt).from(element).save().then(() => { document.body.classList.remove('tig-generating-pdf'); }); } // Utility: HTML Escape function escapeHtml(text) { if (!text) return ''; return text.replace(/[&<>"']/g, function(m) { switch (m) { case '&': return '&'; case '<': return '<'; case '>': return '>'; case '"': return '"'; case "'": return '''; default: return m; } }); }
Scroll to Top