A required library could not be loaded. Please check your internet connection and try again.
';
return;
}
const { jsPDF } = jspdf;
// --- DATA & STATE ---
const SYMPTOMS = [
{ id: 'sneezing', name: 'Sneezing', icon: '🤧' },
{ id: 'runny_nose', name: 'Runny or Stuffy Nose', icon: '👃' },
{ id: 'itchy_eyes', name: 'Itchy, Watery Eyes', icon: '👁️' },
{ id: 'sore_throat', name: 'Itchy Throat / Mouth', icon: '😷' },
{ id: 'coughing', name: 'Coughing / Wheezing', icon: '😮💨' },
{ id: 'short_breath', name: 'Shortness of Breath', icon: '🫁' },
{ id: 'skin_rash', name: 'Skin Rash or Hives', icon: '✋' },
{ id: 'eczema', name: 'Aggravated Eczema', icon: '🩹' },
];
let userState = {
profile: {
petType: 'Cat',
petHair: 'Short-haired',
homeType: 'Apartment',
furnitureAccess: 'Yes',
},
symptoms: {},
};
// --- ELEMENT SELECTORS ---
const tabs = document.querySelectorAll('.tab-btn');
const tabContents = document.querySelectorAll('.tab-content');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const symptomListContainer = document.getElementById('symptom-list');
// Profile
const petTypeSelect = document.getElementById('pet-type');
const petHairSelect = document.getElementById('pet-hair');
const homeTypeSelect = document.getElementById('home-type');
// Summary
const getPlanBtn = document.getElementById('getPlanBtn');
const summaryLoader = document.getElementById('summary-loader');
const summaryOutput = document.getElementById('summary-output');
const downloadPdfBtn = document.getElementById('downloadPdfBtn');
// --- INITIALIZATION ---
const initialize = () => {
renderSymptoms();
updateNavButtons();
attachEventListeners();
};
// --- RENDER FUNCTIONS ---
const renderSymptoms = () => {
symptomListContainer.innerHTML = SYMPTOMS.map(s => `
`).join('');
};
// --- EVENT LISTENERS ---
const attachEventListeners = () => {
tabs.forEach(tab => tab.addEventListener('click', () => switchTab(tab.dataset.tab)));
prevBtn.addEventListener('click', navigatePrev);
nextBtn.addEventListener('click', navigateNext);
// Profile inputs
petTypeSelect.addEventListener('change', (e) => userState.profile.petType = e.target.value);
petHairSelect.addEventListener('change', (e) => userState.profile.petHair = e.target.value);
homeTypeSelect.addEventListener('change', (e) => userState.profile.homeType = e.target.value);
document.querySelectorAll('input[name="furniture-access"]').forEach(radio => {
radio.addEventListener('change', (e) => userState.profile.furnitureAccess = e.target.value);
});
// Symptom selection
symptomListContainer.addEventListener('click', (e) => {
const card = e.target.closest('.selectable-card');
if (card) {
const symptomId = card.dataset.symptomId;
const selectDiv = card.querySelector('.mt-4');
const isSelected = card.classList.toggle('selected');
if (isSelected) {
selectDiv.classList.remove('hidden');
userState.symptoms[symptomId] = { severity: '' };
} else {
selectDiv.classList.add('hidden');
selectDiv.querySelector('select').value = '';
delete userState.symptoms[symptomId];
}
}
});
// Severity selection
symptomListContainer.addEventListener('change', (e) => {
if (e.target.classList.contains('severity-select')) {
const symptomId = e.target.closest('.selectable-card').dataset.symptomId;
if (userState.symptoms[symptomId]) {
userState.symptoms[symptomId].severity = e.target.value;
}
}
});
// Main action buttons
getPlanBtn.addEventListener('click', handleGetPlan);
downloadPdfBtn.addEventListener('click', handleDownloadPdf);
};
// --- 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 & SUMMARY LOGIC ---
const handleGetPlan = async () => {
summaryLoader.classList.remove('hidden');
summaryOutput.classList.add('hidden');
getPlanBtn.disabled = true;
const prompt = buildPrompt();
const insights = await callGeminiApi(prompt);
renderSummary(insights);
summaryLoader.classList.add('hidden');
summaryOutput.classList.remove('hidden');
getPlanBtn.disabled = false;
};
const buildPrompt = () => {
const { petType, petHair, homeType, furnitureAccess } = userState.profile;
const selectedSymptoms = Object.entries(userState.symptoms)
.filter(([_, val]) => val.severity)
.map(([id, val]) => {
const symptomName = SYMPTOMS.find(s => s.id === id).name;
return `- ${symptomName} (Severity: ${val.severity})`;
}).join('\n');
return `
As an expert informational guide on living with pet allergies (not a medical professional), create a personalized management plan for a user with the following profile:
**User Profile:**
- Pet Type: ${petType}
- Pet's Coat: ${petHair}
- Home Type: ${homeType}
- Pet is allowed on furniture: ${furnitureAccess}
**Reported Symptoms:**
${selectedSymptoms || 'No specific symptoms with severity provided.'}
**Task:**
Generate a helpful, actionable management plan. Structure your response into the following sections, using the exact headings below:
### Understanding Your Potential Triggers
Briefly explain that pet allergies are often caused by dander, saliva, and urine, not just hair. Mention how these allergens can become airborne and settle on surfaces in a ${homeType}.
### Tiered Management Strategies
Provide a list of strategies organized by effort level.
**1. Low-Effort, High-Impact Changes:**
- Create a pet-free zone, especially the bedroom.
- Wash hands and face after petting the animal.
- Use a high-efficiency particulate air (HEPA) purifier.
**2. Medium-Effort, Regular Habits:**
- Bathe the pet regularly (e.g., weekly), if appropriate for the species.
- Clean the house frequently: vacuum with a HEPA filter vacuum, damp-dust surfaces.
- Wash pet bedding and your own bedding in hot water weekly.
**3. Higher-Effort Solutions:**
- Consider replacing carpet with hard flooring.
- Use allergen-proof covers on mattresses and pillows.
- Discuss options with a veterinarian regarding pet diet or shampoos that may reduce dander.
**IMPORTANT:** Conclude the entire response with a clear, bolded disclaimer: **Disclaimer: This guide provides general information and is not a substitute for professional medical advice. Consult with an allergist or healthcare provider for diagnosis and treatment.**
`;
};
const callGeminiApi = async (prompt) => {
const apiKey = ""; // Provided by the 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 a plan at this time. Please try again later.\n\n**Disclaimer: This guide provides general information and is not a substitute for professional medical advice. Consult with an allergist or healthcare provider for diagnosis and treatment.**";
}
};
const renderSummary = (insights) => {
document.getElementById('summaryDate').textContent = `Plan generated on ${new Date().toLocaleDateString('en-US')}`;
// Profile Summary
const { petType, petHair, homeType, furnitureAccess } = userState.profile;
const profileHtml = `
My Profile
Pet Type: ${petType}
Pet's Coat: ${petHair}
Home Type: ${homeType}
Pet on Furniture: ${furnitureAccess}
`;
document.getElementById('summary-profile').innerHTML = profileHtml;
// Symptoms Table
const symptomsHtml = `
My Symptoms
| Symptom | Severity |
${Object.keys(userState.symptoms).length > 0 ?
Object.entries(userState.symptoms).map(([id, val]) => `
| ${SYMPTOMS.find(s => s.id === id).name} |
${val.severity || 'Not rated'} |
`).join('') :
'| No symptoms with severity were selected. |
'
}
`;
document.getElementById('summary-symptoms').innerHTML = symptomsHtml;
// AI Insights
const insightsHtml = insights
.replace(/### (.*?)\n/g, '
$1
')
.replace(/\n\n/g, '
')
.replace(/\n- /g, '
- ')
.replace(/\*\*(.*?)\*\*/g, '
$1');
document.getElementById('summary-insights').innerHTML = `
${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 canvasHeight = canvas.height;
const canvasWidth = canvas.width;
const ratio = canvasWidth / canvasHeight;
const imgWidth = pdfWidth - 20;
const 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-Pet-Allergy-Plan.pdf');
})
.catch(err => console.error("PDF Generation Error:", err));
};
// --- START THE APP ---
initialize();
});