`).join('');
};
// --- EVENT LISTENERS ---
const attachEventListeners = () => {
tabs.forEach(tab => tab.addEventListener('click', () => switchTab(tab.dataset.tab)));
prevBtn.addEventListener('click', navigatePrev);
nextBtn.addEventListener('click', navigateNext);
getPlanBtn.addEventListener('click', handleGetPlan);
downloadPdfBtn.addEventListener('click', handleDownloadPdf);
// Lifestyle inputs
screenTimeSelect.addEventListener('change', (e) => userState.lifestyle.screenTime = e.target.value);
outdoorTimeSelect.addEventListener('change', (e) => userState.lifestyle.outdoorTime = e.target.value);
smokingSelect.addEventListener('change', (e) => userState.lifestyle.smoking = e.target.value);
dietListContainer.addEventListener('change', (e) => {
if (e.target.classList.contains('diet-checkbox')) {
const food = e.target.dataset.food;
if (e.target.checked) {
userState.lifestyle.diet.push(food);
} else {
userState.lifestyle.diet = userState.lifestyle.diet.filter(f => f !== food);
}
}
});
concernsListContainer.addEventListener('click', (e) => {
const card = e.target.closest('.selectable-card');
if (card) {
const concernId = card.dataset.concernId;
card.classList.toggle('selected');
if (card.classList.contains('selected')) {
userState.concerns.push(concernId);
} else {
userState.concerns = userState.concerns.filter(c => c !== concernId);
}
}
});
};
// --- NAVIGATION ---
const switchTab = (targetTab) => {
tabs.forEach(tab => tab.classList.toggle('active', tab.dataset.tab === targetTab));
tabContents.forEach(content => content.classList.toggle('active', content.id === targetTab));
updateNavButtons();
};
const updateNavButtons = () => {
const activeTabIndex = Array.from(tabs).findIndex(tab => tab.classList.contains('active'));
prevBtn.style.visibility = activeTabIndex === 0 ? 'hidden' : 'visible';
nextBtn.style.visibility = activeTabIndex === tabs.length - 1 ? 'hidden' : 'visible';
};
const navigatePrev = () => {
const activeTabIndex = Array.from(tabs).findIndex(tab => tab.classList.contains('active'));
if (activeTabIndex > 0) switchTab(tabs[activeTabIndex - 1].dataset.tab);
};
const navigateNext = () => {
const activeTabIndex = Array.from(tabs).findIndex(tab => tab.classList.contains('active'));
if (activeTabIndex < tabs.length - 1) switchTab(tabs[activeTabIndex + 1].dataset.tab);
};
// --- API & RESULTS LOGIC ---
const handleGetPlan = async () => {
resultsLoader.classList.remove('hidden');
resultsOutput.classList.add('hidden');
getPlanBtn.style.display = 'none';
const prompt = buildPrompt();
const insightsText = await callGeminiApi(prompt);
renderSummary(insightsText);
resultsLoader.classList.add('hidden');
resultsOutput.classList.remove('hidden');
};
const buildPrompt = () => {
const concernsText = userState.concerns.map(id => CONCERNS.find(c => c.id === id).name).join(', ') || 'None reported';
return `
As a health and wellness AI assistant (not a medical professional), create a personalized vision health plan based on the user's profile.
**User's Profile:**
- Daily Screen Time: ${userState.lifestyle.screenTime}
- Daily Outdoor Time: ${userState.lifestyle.outdoorTime}
- Smoker: ${userState.lifestyle.smoking}
- Regularly Eats: ${userState.lifestyle.diet.join(', ') || 'Not specified'}
- Reported Vision Concerns: ${concernsText}
**Task:**
Generate a supportive and actionable plan. Structure your response into the following sections using the exact headings:
### Understanding Your Vision Profile
Briefly summarize how the user's lifestyle factors might relate to their concerns. For example, connect high screen time to eye strain, or smoking to long-term eye health risks.
### Your Personalized Action Plan
Provide 3-5 specific, non-medical tips tailored to the user's inputs.
- If screen time is high and they report eye strain, strongly recommend the 20-20-20 rule.
- If diet is lacking, suggest specific foods to incorporate.
- If outdoor time is low, explain the benefits of natural light for eye health.
- If they smoke, gently mention it's one of the most significant controllable risk factors for eye diseases.
- If they report dry eyes, suggest conscious blinking and staying hydrated.
### General Eye Health Best Practices
Include 2-3 universal tips, such as wearing sunglasses with UV protection and the importance of regular comprehensive eye exams.
**IMPORTANT:** Conclude the entire response with a clear, bolded disclaimer: **Disclaimer: This plan provides general wellness tips and is not a substitute for professional medical advice. Consult with an eye care professional for any vision problems or before making significant lifestyle changes.**
`;
};
const callGeminiApi = async (prompt) => {
const apiKey = ""; // Provided by environment
const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent?key=${apiKey}`;
const payload = { contents: [{ role: "user", parts: [{ text: prompt }] }] };
try {
const response = await fetch(apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) throw new Error(`API Error: ${response.status}`);
const data = await response.json();
return data.candidates[0].content.parts[0].text;
} catch (error) {
console.error("Gemini API Error:", error);
return "## Error\nCould not generate your plan. Please try again later.\n\n**Disclaimer:** This is not medical advice.";
}
};
const renderSummary = (insightsText) => {
document.getElementById('summary-date').textContent = `Plan generated on ${new Date().toLocaleDateString('en-US')}`;
const summaryHtml = `
Your Profile Summary
- Screen Time: ${userState.lifestyle.screenTime} per day
- Outdoor Time: ${userState.lifestyle.outdoorTime} per day
- Smoker: ${userState.lifestyle.smoking}
- Regularly Eats: ${userState.lifestyle.diet.join(', ') || 'None specified'}
- Reported Concerns: ${userState.concerns.map(id => CONCERNS.find(c => c.id === id).name).join(', ') || 'None'}
`;
const insightsHtml = insightsText
.replace(/### (.*?)\n/g, '
$1
')
.replace(/\n\n/g, '
')
.replace(/\n- /g, '
- ')
.replace(/(\n- .*?)+/g, (match) => match.replace(/\n- /g, '
- ') + '
')
.replace(/
/,'')
.replace(/\*\*(.*?)\*\*/g, '$1');
summaryContentContainer.innerHTML = summaryHtml + insightsHtml;
};
// --- PDF GENERATION ---
const handleDownloadPdf = () => {
const content = document.getElementById('pdf-content');
if (!content) return;
html2canvas(content, { scale: 2, useCORS: true, backgroundColor: '#ffffff' })
.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 ratio = canvas.width / canvas.height;
const imgWidth = pdfWidth - 20;
let imgHeight = imgWidth / ratio;
let heightLeft = imgHeight;
let position = 10;
pdf.addImage(imgData, 'PNG', 10, position, imgWidth, imgHeight);
heightLeft -= (pdf.internal.pageSize.getHeight() - 20);
while (heightLeft > 0) {
position = -heightLeft - 10;
pdf.addPage();
pdf.addImage(imgData, 'PNG', 10, position, imgWidth, imgHeight);
heightLeft -= (pdf.internal.pageSize.getHeight() - 20);
}
pdf.save('My-Vision-Health-Plan.pdf');
})
.catch(err => console.error("PDF Generation Error:", err));
};
// --- START THE APP ---
initialize();
});