Traveler's Health Vaccine Schedule Planner
Your Trip Details
Your Personalized Vaccine Timeline
Enter your travel date and select vaccines to generate a timeline.
Customize Vaccine Data
Please select a travel date.
'; return; } const travelDate = new Date(travelDateStr + 'T00:00:00'); let appointments = []; selectedVaccines.forEach(vaccineId => { const vaccine = vaccineData.vaccines.find(v => v.id === vaccineId); if (!vaccine) return; let firstDoseDate = new Date(travelDate); let totalDaysBack = vaccineData.daysBeforeTravel; if (vaccine.intervalDays.length > 0) { totalDaysBack += vaccine.intervalDays[vaccine.intervalDays.length - 1]; } firstDoseDate.setDate(travelDate.getDate() - totalDaysBack); appointments.push({ date: new Date(firstDoseDate), text: `${vaccine.name} (Dose 1 of ${vaccine.doses})` }); let lastDoseDate = firstDoseDate; for (let i = 0; i < vaccine.intervalDays.length; i++) { let nextDoseDate = new Date(lastDoseDate); nextDoseDate.setDate(lastDoseDate.getDate() + vaccine.intervalDays[i] - (i > 0 ? vaccine.intervalDays[i-1] : 0)); appointments.push({ date: new Date(nextDoseDate), text: `${vaccine.name} (Dose ${i + 2} of ${vaccine.doses})` }); lastDoseDate = nextDoseDate; } }); renderTimeline(appointments, travelDate); } function renderTimeline(appointments, travelDate) { scheduleTimeline.innerHTML = ''; if (appointments.length === 0) { scheduleTimeline.innerHTML = 'No vaccines selected. Your schedule is clear!
'; return; } appointments.sort((a, b) => a.date - b.date); const uniqueAppointments = {}; appointments.forEach(appt => { const dateStr = appt.date.toISOString().split('T')[0]; if (!uniqueAppointments[dateStr]) { uniqueAppointments[dateStr] = []; } uniqueAppointments[dateStr].push(appt.text); }); Object.keys(uniqueAppointments).forEach(dateStr => { const date = new Date(dateStr + 'T00:00:00'); const formattedDate = date.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' }); const details = uniqueAppointments[dateStr].join(''); const item = document.createElement('div'); item.className = 'timeline-item'; item.innerHTML = `
${formattedDate}
${details}
`;
scheduleTimeline.appendChild(item);
});
// Add travel date marker
const travelItem = document.createElement('div');
travelItem.className = 'timeline-item';
travelItem.innerHTML = `${travelDate.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' })}
Travel Day!
`;
scheduleTimeline.appendChild(travelItem);
}
function renderConfig() {
let tableHTML = `
| Vaccine | Doses | Intervals (days) |
|---|---|---|
| ${v.name} |
${tableHTML}
`;
}
window.updateConfig = function(element) {
const { id, prop } = element.dataset;
let value = element.value;
const vaccine = vaccineData.vaccines.find(v => v.id === id);
if (vaccine) {
if (prop === 'doses') {
value = parseInt(value, 10);
} else if (prop === 'intervalDays') {
value = value.split(',').map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n));
}
vaccine[prop] = value;
generateSchedule();
}
}
// --- TAB & NAVIGATION ---
window.openTab = function(evt, tabName) {
const tabContents = document.getElementsByClassName("tab-content");
Array.from(tabContents).forEach(tab => tab.style.display = "none");
const tabButtons = document.getElementsByClassName("tab-btn");
Array.from(tabButtons).forEach(btn => btn.classList.remove("active"));
document.getElementById(tabName).style.display = "block";
if (evt) {
evt.currentTarget.classList.add("active");
} else {
const btnToActivate = Array.from(tabButtons).find(btn => btn.getAttribute('onclick').includes(`'${tabName}'`));
if (btnToActivate) btnToActivate.classList.add("active");
}
updateNavButtons();
}
window.navigateTabs = function(direction) {
const tabs = Array.from(document.querySelectorAll('.tab-btn'));
const activeTabIndex = tabs.findIndex(tab => tab.classList.contains('active'));
let newIndex = (direction === 'next')
? (activeTabIndex + 1) % tabs.length
: (activeTabIndex - 1 + tabs.length) % tabs.length;
tabs[newIndex].click();
}
function updateNavButtons() {
const tabs = Array.from(document.querySelectorAll('.tab-btn'));
const activeTabIndex = tabs.findIndex(tab => tab.classList.contains('active'));
document.getElementById('prev-btn').style.visibility = activeTabIndex === 0 ? 'hidden' : 'visible';
document.getElementById('next-btn').style.visibility = activeTabIndex === tabs.length - 1 ? 'hidden' : 'visible';
}
// --- PDF DOWNLOAD ---
if(downloadPdfBtn) {
downloadPdfBtn.addEventListener('click', function() {
const { jsPDF } = window.jspdf;
const contentToDownload = document.getElementById('results-to-download');
if (!contentToDownload || !document.querySelector('.timeline-item')) {
console.warn("Please generate a schedule before downloading.");
return;
}
const originalButtonText = downloadPdfBtn.innerHTML;
downloadPdfBtn.innerHTML = 'Generating...';
downloadPdfBtn.disabled = true;
html2canvas(contentToDownload, { scale: 2, useCORS: true }).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const imgProps = pdf.getImageProperties(imgData);
const imgHeight = (imgProps.height * pdfWidth) / imgProps.width;
pdf.addImage(imgData, 'PNG', 10, 10, pdfWidth - 20, imgHeight > 0 ? imgHeight - 20 : 0);
pdf.save('Vaccine-Schedule.pdf');
}).catch(err => {
console.error("Error generating PDF:", err);
}).finally(() => {
downloadPdfBtn.innerHTML = originalButtonText;
downloadPdfBtn.disabled = false;
});
});
}
// --- INITIALIZATION ---
function initializeTool() {
// Set default travel date to 3 months from now
const travelDateInput = document.getElementById('travel-date');
const defaultTravelDate = new Date();
defaultTravelDate.setMonth(defaultTravelDate.getMonth() + 3);
travelDateInput.value = defaultTravelDate.toISOString().split('T')[0];
populateControls();
generateSchedule();
renderConfig();
updateNavButtons();
}
initializeTool();
});
