Joint Health & Arthritis Symptom Tracker

Joint Health & Arthritis Symptom Tracker

Log Your Symptoms for Today

5
5

Affected Joints (select all that apply)

No entries yet. Go to the "Log New Entry" tab to start tracking.

`; return; } let tableHTML = ``; symptomData.forEach((entry, index) => { tableHTML += ` `; }); tableHTML += `
Date Pain Stiffness Joints Notes Actions
${new Date(entry.date).toLocaleDateString()} ${entry.pain} ${entry.stiffness} ${entry.joints.join(', ') || 'None'} ${entry.notes || '-'}
`; historyTableContainer.innerHTML = tableHTML; }; const renderSymptomChart = () => { if (symptomChart) { symptomChart.destroy(); } if (symptomData.length === 0) { chartCanvas.style.display = 'none'; return; } chartCanvas.style.display = 'block'; // Prepare data for chart (show last 30 entries) const chartData = [...symptomData].reverse().slice(-30); const labels = chartData.map(d => new Date(d.date).toLocaleDateString()); const painData = chartData.map(d => d.pain); const stiffnessData = chartData.map(d => d.stiffness); symptomChart = new Chart(chartCanvas, { type: 'line', data: { labels: labels, datasets: [ { label: 'Pain Level', data: painData, borderColor: '#ef4444', // red-500 backgroundColor: '#ef4444', tension: 0.1 }, { label: 'Stiffness Level', data: stiffnessData, borderColor: '#3b82f6', // blue-500 backgroundColor: '#3b82f6', tension: 0.1 } ] }, options: { responsive: true, scales: { y: { beginAtZero: true, max: 10 } } } }); }; const saveEntry = () => { const selectedJoints = Array.from(document.querySelectorAll('input[name="joint"]:checked')).map(cb => cb.value); const entry = { date: logDateInput.value, pain: painSlider.value, stiffness: stiffnessSlider.value, joints: selectedJoints, notes: notesInput.value }; // Prevent duplicate entries for the same day const existingIndex = symptomData.findIndex(d => d.date === entry.date); if (existingIndex > -1) { if (!confirm('An entry for this date already exists. Do you want to overwrite it?')) { return; } symptomData[existingIndex] = entry; } else { symptomData.push(entry); } localStorage.setItem('symptomTrackerData', JSON.stringify(symptomData)); alert('Entry saved successfully!'); resetForm(); renderDashboard(); }; const deleteEntry = (index) => { if (confirm('Are you sure you want to delete this entry?')) { symptomData.splice(index, 1); localStorage.setItem('symptomTrackerData', JSON.stringify(symptomData)); renderDashboard(); } }; const resetForm = () => { logDateInput.valueAsDate = new Date(); painSlider.value = 5; painValue.textContent = 5; stiffnessSlider.value = 5; stiffnessValue.textContent = 5; document.querySelectorAll('input[name="joint"]:checked').forEach(cb => cb.checked = false); notesInput.value = ''; }; // IV. Tab Navigation const switchTab = (targetTabId) => { tabs.forEach(tab => { const isTarget = tab.dataset.tab === targetTabId; tab.classList.toggle('active', isTarget); tab.classList.toggle('inactive', !isTarget); }); tabContents.forEach(content => { content.classList.toggle('hidden', content.id !== targetTabId); }); updateNavButtons(); }; const updateNavButtons = () => { const activeTab = document.querySelector('.tab-button.active').dataset.tab; prevBtn.disabled = activeTab === 'log-entry'; nextBtn.disabled = activeTab === 'dashboard'; prevBtn.classList.toggle('opacity-50', prevBtn.disabled); nextBtn.classList.toggle('opacity-50', nextBtn.disabled); }; // V. Event Listeners tabs.forEach(tab => { tab.addEventListener('click', () => switchTab(tab.dataset.tab)); }); nextBtn.addEventListener('click', () => { const activeTab = document.querySelector('.tab-button.active').dataset.tab; if (activeTab === 'log-entry') switchTab('dashboard'); }); prevBtn.addEventListener('click', () => { const activeTab = document.querySelector('.tab-button.active').dataset.tab; if (activeTab === 'dashboard') switchTab('log-entry'); }); painSlider.addEventListener('input', (e) => painValue.textContent = e.target.value); stiffnessSlider.addEventListener('input', (e) => stiffnessValue.textContent = e.target.value); saveEntryBtn.addEventListener('click', saveEntry); historyTableContainer.addEventListener('click', (e) => { if (e.target.classList.contains('delete-btn')) { const index = e.target.dataset.index; deleteEntry(index); } }); // VI. PDF Generation pdfDownloadBtn.addEventListener('click', () => { if (symptomData.length === 0) { alert('No data to generate a report.'); return; } const { jsPDF } = jspdf; const doc = new jsPDF(); const pageW = doc.internal.pageSize.getWidth(); const margin = 15; // Header doc.setFillColor(30, 41, 59); doc.rect(0, 0, pageW, 25, 'F'); doc.setFontSize(16); doc.setFont('helvetica', 'bold'); doc.setTextColor(255, 255, 255); doc.text('Joint Health & Arthritis Symptom Report', margin, 16); // Report Date doc.setFontSize(10); doc.setFont('helvetica', 'normal'); doc.setTextColor(150, 150, 150); doc.text(`Report Generated: ${new Date().toLocaleString()}`, margin, 22); // Chart Image if (symptomChart && chartCanvas.style.display !== 'none') { const chartImage = chartCanvas.toDataURL('image/png', 1.0); doc.setFontSize(12); doc.setFont('helvetica', 'bold'); doc.setTextColor(30, 41, 59); doc.text('Symptom Trend Chart', margin, 40); doc.addImage(chartImage, 'PNG', margin, 45, pageW - (margin * 2), (pageW - (margin * 2)) * 0.5); } // Data Table const tableStartY = doc.lastAutoTable ? doc.lastAutoTable.finalY + 15 : 120; doc.setFontSize(12); doc.setFont('helvetica', 'bold'); doc.setTextColor(30, 41, 59); doc.text('Detailed Symptom Log', margin, tableStartY); const tableData = symptomData.map(entry => [ new Date(entry.date).toLocaleDateString(), entry.pain, entry.stiffness, entry.joints.join(', ') || 'N/A', entry.notes || 'N/A' ]); doc.autoTable({ startY: tableStartY + 5, head: [['Date', 'Pain', 'Stiffness', 'Affected Joints', 'Notes']], body: tableData, headStyles: { fillColor: [71, 85, 105] }, // slate-600 theme: 'grid' }); doc.save('Symptom_Tracker_Report.pdf'); }); // Initial Render renderDashboard(); updateNavButtons(); });
Scroll to Top