Pet Health Dashboard

Pet Health Dashboard

Track your pet's weight, vaccinations, and appointments.

Pet Information

Health Snapshot

Weight Trend

Upcoming Appointments

Vaccination Record

Pet Details

Weight Log

Appointments Log

Vaccinations Log

${latestWeight} lbs

Next Vet Visit

${nextAppointment}

Vaccinations Due

${nextVaccine}

`; }; const renderWeightChart = () => { const sortedWeightLog = [...state.weightLog].sort((a,b) => new Date(a.date) - new Date(b.date)); const labels = sortedWeightLog.map(w => w.date); if (weightChart) weightChart.destroy(); weightChart = new Chart(weightChartCanvas.getContext('2d'), { type: 'line', data: { labels: labels, datasets: [{ label: 'Weight (lbs)', data: sortedWeightLog.map(w => w.weight), borderColor: '#10b981', tension: 0.1 }] }, options: { responsive: true, maintainAspectRatio: false, scales: { x: { type: 'time', time: { unit: 'month' } }, y: { beginAtZero: false, title: { display: true, text: 'Weight (lbs)' } } } } }); }; const renderAppointments = () => { appointmentsList.innerHTML = ''; if(state.appointments.length === 0) { appointmentsList.innerHTML = `

No upcoming appointments.

`; return; } [...state.appointments].sort((a,b) => new Date(a.date) - new Date(b.date)).forEach(a => { appointmentsList.innerHTML += `

${a.reason}

${a.date} at ${a.time}

`; }); }; const renderVaccinations = () => { vaccinationsList.innerHTML = ''; if(state.vaccinations.length === 0) { vaccinationsList.innerHTML = `

No vaccination records.

`; return; } [...state.vaccinations].sort((a,b) => new Date(a.nextDue) - new Date(b.nextDue)).forEach(v => { vaccinationsList.innerHTML += `

${v.name}

Next Due: ${v.nextDue}

`; }); }; const renderConfigRows = () => { // Pet Info petInfoConfig.innerHTML = `
`.replaceAll('class="config-input"', 'class="config-input mt-1 w-full border-gray-300 rounded-md shadow-sm"'); // Weight weightConfig.innerHTML = ''; [...state.weightLog].sort((a,b)=>new Date(b.date)-new Date(a.date)).forEach(w => { weightConfig.innerHTML += `
`.replaceAll('class="config-input"', 'class="config-input w-full border-gray-300 rounded-md shadow-sm"'); }); // Appointments apptConfig.innerHTML = ''; [...state.appointments].sort((a,b)=>new Date(b.date)-new Date(a.date)).forEach(a => { apptConfig.innerHTML += `
`.replaceAll('class="config-input"', 'class="config-input w-full border-gray-300 rounded-md shadow-sm"'); }); // Vaccinations vaxConfig.innerHTML = ''; [...state.vaccinations].sort((a,b)=>new Date(b.dateGiven)-new Date(a.dateGiven)).forEach(v => { vaxConfig.innerHTML += `
`.replaceAll('class="config-input"', 'class="config-input w-full border-gray-300 rounded-md shadow-sm"'); }); addConfigEventListeners(); }; // --- EVENT HANDLERS --- const handleConfigChange = (e) => { const type = e.target.dataset.type; const field = e.target.dataset.field; const value = e.target.value; if (type === 'petInfo') { state.petInfo[field] = value; } else { const id = parseInt(e.target.dataset.id); const entry = state[type].find(item => item.id === id); if (entry) entry[field] = (e.target.type === 'number') ? parseFloat(value) : value; } renderAll(); }; const handleAdd = (type) => { const today = new Date().toISOString().split('T')[0]; const newId = state[type].length > 0 ? Math.max(...state[type].map(item => item.id)) + 1 : 1; if (type === 'weightLog') state.weightLog.push({ id: newId, date: today, weight: 0 }); if (type === 'appointments') state.appointments.push({ id: newId, date: today, time: "12:00", reason: "New Appointment" }); if (type === 'vaccinations') state.vaccinations.push({ id: newId, dateGiven: today, name: "New Vaccine", nextDue: today }); renderAll(); }; const handleRemove = (e) => { const id = parseInt(e.target.dataset.id); const type = e.target.dataset.type; state[type] = state[type].filter(item => item.id !== id); renderAll(); }; const addConfigEventListeners = () => { document.querySelectorAll('.config-input').forEach(input => input.addEventListener('change', handleConfigChange)); document.querySelectorAll('.remove-btn').forEach(button => button.addEventListener('click', handleRemove)); }; const handleDownloadPdf = () => { const { jsPDF } = window.jspdf; const pdfContent = document.getElementById('pdf-content'); document.querySelectorAll('.no-print').forEach(el => el.style.visibility = 'hidden'); Chart.defaults.animation = false; html2canvas(pdfContent, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }).then(canvas => { document.querySelectorAll('.no-print').forEach(el => el.style.visibility = 'visible'); Chart.defaults.animation = true; const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const imgWidth = pdfWidth - 20; const imgHeight = canvas.height * imgWidth / canvas.width; pdf.addImage(imgData, 'PNG', 10, 10, imgWidth, imgHeight); pdf.save(`${state.petInfo.name}-Health-Summary.pdf`); }); }; // --- TABBING LOGIC --- let currentTabIndex = 0; const updateTabButtons = () => { prevTabBtn.disabled = currentTabIndex === 0; nextTabBtn.disabled = currentTabIndex === tabContents.length - 1; }; const switchTab = (index) => { tabButtons.forEach(btn => btn.classList.remove('active')); tabContents.forEach(content => content.classList.remove('active')); tabButtons[index].classList.add('active'); tabContents[index].classList.add('active'); currentTabIndex = index; updateTabButtons(); }; tabButtons.forEach((button, index) => button.addEventListener('click', () => switchTab(index))); prevTabBtn.addEventListener('click', () => { if (currentTabIndex > 0) switchTab(currentTabIndex - 1); }); nextTabBtn.addEventListener('click', () => { if (currentTabIndex < tabContents.length - 1) switchTab(currentTabIndex + 1); }); // --- INITIALIZATION --- if (kpiContainer && addWeightBtn && addApptBtn && addVaxBtn && downloadPdfBtn) { addWeightBtn.addEventListener('click', () => handleAdd('weightLog')); addApptBtn.addEventListener('click', () => handleAdd('appointments')); addVaxBtn.addEventListener('click', () => handleAdd('vaccinations')); downloadPdfBtn.addEventListener('click', handleDownloadPdf); renderAll(); updateTabButtons(); } else { console.error("Essential dashboard elements could not be found."); } });
Scroll to Top